.
+ // This way we are always able to correctly figure out the size of the svg element
+ // by querying the parent node.
+ //
+ // (It is not possible to get the size of a svg element cross browser @ 2014-04-01)
+ //
+ //
+ //
+ //
+ // html container
+
+ var container = this._container = createContainer(config);
+ var svg = this._svg = (0, _tinySvg.create)('svg');
+ (0, _tinySvg.attr)(svg, {
+ width: '100%',
+ height: '100%'
+ });
+ (0, _tinySvg.append)(container, svg);
+ var viewport = this._viewport = createGroup(svg, 'viewport');
+ this._layers = {}; // debounce canvas.viewbox.changed events
+ // for smoother diagram interaction
+
+ if (config.deferUpdate !== false) {
+ this._viewboxChanged = (0, _minDash.debounce)((0, _minDash.bind)(this._viewboxChanged, this), 300);
+ }
+
+ eventBus.on('diagram.init', function () {
+ /**
+ * An event indicating that the canvas is ready to be drawn on.
+ *
+ * @memberOf Canvas
+ *
+ * @event canvas.init
+ *
+ * @type {Object}
+ * @property {SVGElement} svg the created svg element
+ * @property {SVGElement} viewport the direct parent of diagram elements and shapes
+ */
+ eventBus.fire('canvas.init', {
+ svg: svg,
+ viewport: viewport
+ });
+ }, this); // reset viewbox on shape changes to
+ // recompute the viewbox
+
+ eventBus.on(['shape.added', 'connection.added', 'shape.removed', 'connection.removed', 'elements.changed'], function () {
+ delete this._cachedViewbox;
+ }, this);
+ eventBus.on('diagram.destroy', 500, this._destroy, this);
+ eventBus.on('diagram.clear', 500, this._clear, this);
+};
+
+Canvas.prototype._destroy = function (emit) {
+ this._eventBus.fire('canvas.destroy', {
+ svg: this._svg,
+ viewport: this._viewport
+ });
+
+ var parent = this._container.parentNode;
+
+ if (parent) {
+ parent.removeChild(this._container);
+ }
+
+ delete this._svg;
+ delete this._container;
+ delete this._layers;
+ delete this._rootElement;
+ delete this._viewport;
+};
+
+Canvas.prototype._clear = function () {
+ var self = this;
+
+ var allElements = this._elementRegistry.getAll(); // remove all elements
+
+
+ allElements.forEach(function (element) {
+ var type = (0, _Elements.getType)(element);
+
+ if (type === 'root') {
+ self.setRootElement(null, true);
+ } else {
+ self._removeElement(element, type);
+ }
+ }); // force recomputation of view box
+
+ delete this._cachedViewbox;
+};
+/**
+ * Returns the default layer on which
+ * all elements are drawn.
+ *
+ * @returns {SVGElement}
+ */
+
+
+Canvas.prototype.getDefaultLayer = function () {
+ return this.getLayer(BASE_LAYER, 0);
+};
+/**
+ * Returns a layer that is used to draw elements
+ * or annotations on it.
+ *
+ * Non-existing layers retrieved through this method
+ * will be created. During creation, the optional index
+ * may be used to create layers below or above existing layers.
+ * A layer with a certain index is always created above all
+ * existing layers with the same index.
+ *
+ * @param {string} name
+ * @param {number} index
+ *
+ * @returns {SVGElement}
+ */
+
+
+Canvas.prototype.getLayer = function (name, index) {
+ if (!name) {
+ throw new Error('must specify a name');
+ }
+
+ var layer = this._layers[name];
+
+ if (!layer) {
+ layer = this._layers[name] = this._createLayer(name, index);
+ } // throw an error if layer creation / retrival is
+ // requested on different index
+
+
+ if (typeof index !== 'undefined' && layer.index !== index) {
+ throw new Error('layer <' + name + '> already created at index <' + index + '>');
+ }
+
+ return layer.group;
+};
+/**
+ * Creates a given layer and returns it.
+ *
+ * @param {string} name
+ * @param {number} [index=0]
+ *
+ * @return {Object} layer descriptor with { index, group: SVGGroup }
+ */
+
+
+Canvas.prototype._createLayer = function (name, index) {
+ if (!index) {
+ index = 0;
+ }
+
+ var childIndex = (0, _minDash.reduce)(this._layers, function (childIndex, layer) {
+ if (index >= layer.index) {
+ childIndex++;
+ }
+
+ return childIndex;
+ }, 0);
+ return {
+ group: createGroup(this._viewport, 'layer-' + name, childIndex),
+ index: index
+ };
+};
+/**
+ * Returns the html element that encloses the
+ * drawing canvas.
+ *
+ * @return {DOMNode}
+ */
+
+
+Canvas.prototype.getContainer = function () {
+ return this._container;
+}; // markers //////////////////////
+
+
+Canvas.prototype._updateMarker = function (element, marker, add) {
+ var container;
+
+ if (!element.id) {
+ element = this._elementRegistry.get(element);
+ } // we need to access all
+
+
+ container = this._elementRegistry._elements[element.id];
+
+ if (!container) {
+ return;
+ }
+
+ (0, _minDash.forEach)([container.gfx, container.secondaryGfx], function (gfx) {
+ if (gfx) {
+ // invoke either addClass or removeClass based on mode
+ if (add) {
+ (0, _tinySvg.classes)(gfx).add(marker);
+ } else {
+ (0, _tinySvg.classes)(gfx).remove(marker);
+ }
+ }
+ });
+ /**
+ * An event indicating that a marker has been updated for an element
+ *
+ * @event element.marker.update
+ * @type {Object}
+ * @property {djs.model.Element} element the shape
+ * @property {Object} gfx the graphical representation of the shape
+ * @property {string} marker
+ * @property {boolean} add true if the marker was added, false if it got removed
+ */
+
+ this._eventBus.fire('element.marker.update', {
+ element: element,
+ gfx: container.gfx,
+ marker: marker,
+ add: !!add
+ });
+};
+/**
+ * Adds a marker to an element (basically a css class).
+ *
+ * Fires the element.marker.update event, making it possible to
+ * integrate extension into the marker life-cycle, too.
+ *
+ * @example
+ * canvas.addMarker('foo', 'some-marker');
+ *
+ * var fooGfx = canvas.getGraphics('foo');
+ *
+ * fooGfx; //
...
+ *
+ * @param {string|djs.model.Base} element
+ * @param {string} marker
+ */
+
+
+Canvas.prototype.addMarker = function (element, marker) {
+ this._updateMarker(element, marker, true);
+};
+/**
+ * Remove a marker from an element.
+ *
+ * Fires the element.marker.update event, making it possible to
+ * integrate extension into the marker life-cycle, too.
+ *
+ * @param {string|djs.model.Base} element
+ * @param {string} marker
+ */
+
+
+Canvas.prototype.removeMarker = function (element, marker) {
+ this._updateMarker(element, marker, false);
+};
+/**
+ * Check the existence of a marker on element.
+ *
+ * @param {string|djs.model.Base} element
+ * @param {string} marker
+ */
+
+
+Canvas.prototype.hasMarker = function (element, marker) {
+ if (!element.id) {
+ element = this._elementRegistry.get(element);
+ }
+
+ var gfx = this.getGraphics(element);
+ return (0, _tinySvg.classes)(gfx).has(marker);
+};
+/**
+ * Toggles a marker on an element.
+ *
+ * Fires the element.marker.update event, making it possible to
+ * integrate extension into the marker life-cycle, too.
+ *
+ * @param {string|djs.model.Base} element
+ * @param {string} marker
+ */
+
+
+Canvas.prototype.toggleMarker = function (element, marker) {
+ if (this.hasMarker(element, marker)) {
+ this.removeMarker(element, marker);
+ } else {
+ this.addMarker(element, marker);
+ }
+};
+
+Canvas.prototype.getRootElement = function () {
+ if (!this._rootElement) {
+ this.setRootElement({
+ id: '__implicitroot',
+ children: []
+ });
+ }
+
+ return this._rootElement;
+}; // root element handling //////////////////////
+
+/**
+ * Sets a given element as the new root element for the canvas
+ * and returns the new root element.
+ *
+ * @param {Object|djs.model.Root} element
+ * @param {boolean} [override] whether to override the current root element, if any
+ *
+ * @return {Object|djs.model.Root} new root element
+ */
+
+
+Canvas.prototype.setRootElement = function (element, override) {
+ if (element) {
+ this._ensureValid('root', element);
+ }
+
+ var currentRoot = this._rootElement,
+ elementRegistry = this._elementRegistry,
+ eventBus = this._eventBus;
+
+ if (currentRoot) {
+ if (!override) {
+ throw new Error('rootElement already set, need to specify override');
+ } // simulate element remove event sequence
+
+
+ eventBus.fire('root.remove', {
+ element: currentRoot
+ });
+ eventBus.fire('root.removed', {
+ element: currentRoot
+ });
+ elementRegistry.remove(currentRoot);
+ }
+
+ if (element) {
+ var gfx = this.getDefaultLayer(); // resemble element add event sequence
+
+ eventBus.fire('root.add', {
+ element: element
+ });
+ elementRegistry.add(element, gfx, this._svg);
+ eventBus.fire('root.added', {
+ element: element,
+ gfx: gfx
+ });
+ }
+
+ this._rootElement = element;
+ return element;
+}; // add functionality //////////////////////
+
+
+Canvas.prototype._ensureValid = function (type, element) {
+ if (!element.id) {
+ throw new Error('element must have an id');
+ }
+
+ if (this._elementRegistry.get(element.id)) {
+ throw new Error('element with id ' + element.id + ' already exists');
+ }
+
+ var requiredAttrs = REQUIRED_MODEL_ATTRS[type];
+ var valid = (0, _minDash.every)(requiredAttrs, function (attr) {
+ return typeof element[attr] !== 'undefined';
+ });
+
+ if (!valid) {
+ throw new Error('must supply { ' + requiredAttrs.join(', ') + ' } with ' + type);
+ }
+};
+
+Canvas.prototype._setParent = function (element, parent, parentIndex) {
+ (0, _Collections.add)(parent.children, element, parentIndex);
+ element.parent = parent;
+};
+/**
+ * Adds an element to the canvas.
+ *
+ * This wires the parent <-> child relationship between the element and
+ * a explicitly specified parent or an implicit root element.
+ *
+ * During add it emits the events
+ *
+ * * <{type}.add> (element, parent)
+ * * <{type}.added> (element, gfx)
+ *
+ * Extensions may hook into these events to perform their magic.
+ *
+ * @param {string} type
+ * @param {Object|djs.model.Base} element
+ * @param {Object|djs.model.Base} [parent]
+ * @param {number} [parentIndex]
+ *
+ * @return {Object|djs.model.Base} the added element
+ */
+
+
+Canvas.prototype._addElement = function (type, element, parent, parentIndex) {
+ parent = parent || this.getRootElement();
+ var eventBus = this._eventBus,
+ graphicsFactory = this._graphicsFactory;
+
+ this._ensureValid(type, element);
+
+ eventBus.fire(type + '.add', {
+ element: element,
+ parent: parent
+ });
+
+ this._setParent(element, parent, parentIndex); // create graphics
+
+
+ var gfx = graphicsFactory.create(type, element, parentIndex);
+
+ this._elementRegistry.add(element, gfx); // update its visual
+
+
+ graphicsFactory.update(type, element, gfx);
+ eventBus.fire(type + '.added', {
+ element: element,
+ gfx: gfx
+ });
+ return element;
+};
+/**
+ * Adds a shape to the canvas
+ *
+ * @param {Object|djs.model.Shape} shape to add to the diagram
+ * @param {djs.model.Base} [parent]
+ * @param {number} [parentIndex]
+ *
+ * @return {djs.model.Shape} the added shape
+ */
+
+
+Canvas.prototype.addShape = function (shape, parent, parentIndex) {
+ return this._addElement('shape', shape, parent, parentIndex);
+};
+/**
+ * Adds a connection to the canvas
+ *
+ * @param {Object|djs.model.Connection} connection to add to the diagram
+ * @param {djs.model.Base} [parent]
+ * @param {number} [parentIndex]
+ *
+ * @return {djs.model.Connection} the added connection
+ */
+
+
+Canvas.prototype.addConnection = function (connection, parent, parentIndex) {
+ return this._addElement('connection', connection, parent, parentIndex);
+};
+/**
+ * Internal remove element
+ */
+
+
+Canvas.prototype._removeElement = function (element, type) {
+ var elementRegistry = this._elementRegistry,
+ graphicsFactory = this._graphicsFactory,
+ eventBus = this._eventBus;
+ element = elementRegistry.get(element.id || element);
+
+ if (!element) {
+ // element was removed already
+ return;
+ }
+
+ eventBus.fire(type + '.remove', {
+ element: element
+ });
+ graphicsFactory.remove(element); // unset parent <-> child relationship
+
+ (0, _Collections.remove)(element.parent && element.parent.children, element);
+ element.parent = null;
+ eventBus.fire(type + '.removed', {
+ element: element
+ });
+ elementRegistry.remove(element);
+ return element;
+};
+/**
+ * Removes a shape from the canvas
+ *
+ * @param {string|djs.model.Shape} shape or shape id to be removed
+ *
+ * @return {djs.model.Shape} the removed shape
+ */
+
+
+Canvas.prototype.removeShape = function (shape) {
+ /**
+ * An event indicating that a shape is about to be removed from the canvas.
+ *
+ * @memberOf Canvas
+ *
+ * @event shape.remove
+ * @type {Object}
+ * @property {djs.model.Shape} element the shape descriptor
+ * @property {Object} gfx the graphical representation of the shape
+ */
+
+ /**
+ * An event indicating that a shape has been removed from the canvas.
+ *
+ * @memberOf Canvas
+ *
+ * @event shape.removed
+ * @type {Object}
+ * @property {djs.model.Shape} element the shape descriptor
+ * @property {Object} gfx the graphical representation of the shape
+ */
+ return this._removeElement(shape, 'shape');
+};
+/**
+ * Removes a connection from the canvas
+ *
+ * @param {string|djs.model.Connection} connection or connection id to be removed
+ *
+ * @return {djs.model.Connection} the removed connection
+ */
+
+
+Canvas.prototype.removeConnection = function (connection) {
+ /**
+ * An event indicating that a connection is about to be removed from the canvas.
+ *
+ * @memberOf Canvas
+ *
+ * @event connection.remove
+ * @type {Object}
+ * @property {djs.model.Connection} element the connection descriptor
+ * @property {Object} gfx the graphical representation of the connection
+ */
+
+ /**
+ * An event indicating that a connection has been removed from the canvas.
+ *
+ * @memberOf Canvas
+ *
+ * @event connection.removed
+ * @type {Object}
+ * @property {djs.model.Connection} element the connection descriptor
+ * @property {Object} gfx the graphical representation of the connection
+ */
+ return this._removeElement(connection, 'connection');
+};
+/**
+ * Return the graphical object underlaying a certain diagram element
+ *
+ * @param {string|djs.model.Base} element descriptor of the element
+ * @param {boolean} [secondary=false] whether to return the secondary connected element
+ *
+ * @return {SVGElement}
+ */
+
+
+Canvas.prototype.getGraphics = function (element, secondary) {
+ return this._elementRegistry.getGraphics(element, secondary);
+};
+/**
+ * Perform a viewbox update via a given change function.
+ *
+ * @param {Function} changeFn
+ */
+
+
+Canvas.prototype._changeViewbox = function (changeFn) {
+ // notify others of the upcoming viewbox change
+ this._eventBus.fire('canvas.viewbox.changing'); // perform actual change
+
+
+ changeFn.apply(this); // reset the cached viewbox so that
+ // a new get operation on viewbox or zoom
+ // triggers a viewbox re-computation
+
+ this._cachedViewbox = null; // notify others of the change; this step
+ // may or may not be debounced
+
+ this._viewboxChanged();
+};
+
+Canvas.prototype._viewboxChanged = function () {
+ this._eventBus.fire('canvas.viewbox.changed', {
+ viewbox: this.viewbox()
+ });
+};
+/**
+ * Gets or sets the view box of the canvas, i.e. the
+ * area that is currently displayed.
+ *
+ * The getter may return a cached viewbox (if it is currently
+ * changing). To force a recomputation, pass `false` as the first argument.
+ *
+ * @example
+ *
+ * canvas.viewbox({ x: 100, y: 100, width: 500, height: 500 })
+ *
+ * // sets the visible area of the diagram to (100|100) -> (600|100)
+ * // and and scales it according to the diagram width
+ *
+ * var viewbox = canvas.viewbox(); // pass `false` to force recomputing the box.
+ *
+ * console.log(viewbox);
+ * // {
+ * // inner: Dimensions,
+ * // outer: Dimensions,
+ * // scale,
+ * // x, y,
+ * // width, height
+ * // }
+ *
+ * // if the current diagram is zoomed and scrolled, you may reset it to the
+ * // default zoom via this method, too:
+ *
+ * var zoomedAndScrolledViewbox = canvas.viewbox();
+ *
+ * canvas.viewbox({
+ * x: 0,
+ * y: 0,
+ * width: zoomedAndScrolledViewbox.outer.width,
+ * height: zoomedAndScrolledViewbox.outer.height
+ * });
+ *
+ * @param {Object} [box] the new view box to set
+ * @param {number} box.x the top left X coordinate of the canvas visible in view box
+ * @param {number} box.y the top left Y coordinate of the canvas visible in view box
+ * @param {number} box.width the visible width
+ * @param {number} box.height
+ *
+ * @return {Object} the current view box
+ */
+
+
+Canvas.prototype.viewbox = function (box) {
+ if (box === undefined && this._cachedViewbox) {
+ return this._cachedViewbox;
+ }
+
+ var viewport = this._viewport,
+ innerBox,
+ outerBox = this.getSize(),
+ matrix,
+ transform,
+ scale,
+ x,
+ y;
+
+ if (!box) {
+ // compute the inner box based on the
+ // diagrams default layer. This allows us to exclude
+ // external components, such as overlays
+ innerBox = this.getDefaultLayer().getBBox();
+ transform = (0, _tinySvg.transform)(viewport);
+ matrix = transform ? transform.matrix : (0, _tinySvg.createMatrix)();
+ scale = round(matrix.a, 1000);
+ x = round(-matrix.e || 0, 1000);
+ y = round(-matrix.f || 0, 1000);
+ box = this._cachedViewbox = {
+ x: x ? x / scale : 0,
+ y: y ? y / scale : 0,
+ width: outerBox.width / scale,
+ height: outerBox.height / scale,
+ scale: scale,
+ inner: {
+ width: innerBox.width,
+ height: innerBox.height,
+ x: innerBox.x,
+ y: innerBox.y
+ },
+ outer: outerBox
+ };
+ return box;
+ } else {
+ this._changeViewbox(function () {
+ scale = Math.min(outerBox.width / box.width, outerBox.height / box.height);
+
+ var matrix = this._svg.createSVGMatrix().scale(scale).translate(-box.x, -box.y);
+
+ (0, _tinySvg.transform)(viewport, matrix);
+ });
+ }
+
+ return box;
+};
+/**
+ * Gets or sets the scroll of the canvas.
+ *
+ * @param {Object} [delta] the new scroll to apply.
+ *
+ * @param {number} [delta.dx]
+ * @param {number} [delta.dy]
+ */
+
+
+Canvas.prototype.scroll = function (delta) {
+ var node = this._viewport;
+ var matrix = node.getCTM();
+
+ if (delta) {
+ this._changeViewbox(function () {
+ delta = (0, _minDash.assign)({
+ dx: 0,
+ dy: 0
+ }, delta || {});
+ matrix = this._svg.createSVGMatrix().translate(delta.dx, delta.dy).multiply(matrix);
+ setCTM(node, matrix);
+ });
+ }
+
+ return {
+ x: matrix.e,
+ y: matrix.f
+ };
+};
+/**
+ * Gets or sets the current zoom of the canvas, optionally zooming
+ * to the specified position.
+ *
+ * The getter may return a cached zoom level. Call it with `false` as
+ * the first argument to force recomputation of the current level.
+ *
+ * @param {string|number} [newScale] the new zoom level, either a number, i.e. 0.9,
+ * or `fit-viewport` to adjust the size to fit the current viewport
+ * @param {string|Point} [center] the reference point { x: .., y: ..} to zoom to, 'auto' to zoom into mid or null
+ *
+ * @return {number} the current scale
+ */
+
+
+Canvas.prototype.zoom = function (newScale, center) {
+ if (!newScale) {
+ return this.viewbox(newScale).scale;
+ }
+
+ if (newScale === 'fit-viewport') {
+ return this._fitViewport(center);
+ }
+
+ var outer, matrix;
+
+ this._changeViewbox(function () {
+ if (typeof center !== 'object') {
+ outer = this.viewbox().outer;
+ center = {
+ x: outer.width / 2,
+ y: outer.height / 2
+ };
+ }
+
+ matrix = this._setZoom(newScale, center);
+ });
+
+ return round(matrix.a, 1000);
+};
+
+function setCTM(node, m) {
+ var mstr = 'matrix(' + m.a + ',' + m.b + ',' + m.c + ',' + m.d + ',' + m.e + ',' + m.f + ')';
+ node.setAttribute('transform', mstr);
+}
+
+Canvas.prototype._fitViewport = function (center) {
+ var vbox = this.viewbox(),
+ outer = vbox.outer,
+ inner = vbox.inner,
+ newScale,
+ newViewbox; // display the complete diagram without zooming in.
+ // instead of relying on internal zoom, we perform a
+ // hard reset on the canvas viewbox to realize this
+ //
+ // if diagram does not need to be zoomed in, we focus it around
+ // the diagram origin instead
+
+ if (inner.x >= 0 && inner.y >= 0 && inner.x + inner.width <= outer.width && inner.y + inner.height <= outer.height && !center) {
+ newViewbox = {
+ x: 0,
+ y: 0,
+ width: Math.max(inner.width + inner.x, outer.width),
+ height: Math.max(inner.height + inner.y, outer.height)
+ };
+ } else {
+ newScale = Math.min(1, outer.width / inner.width, outer.height / inner.height);
+ newViewbox = {
+ x: inner.x + (center ? inner.width / 2 - outer.width / newScale / 2 : 0),
+ y: inner.y + (center ? inner.height / 2 - outer.height / newScale / 2 : 0),
+ width: outer.width / newScale,
+ height: outer.height / newScale
+ };
+ }
+
+ this.viewbox(newViewbox);
+ return this.viewbox(false).scale;
+};
+
+Canvas.prototype._setZoom = function (scale, center) {
+ var svg = this._svg,
+ viewport = this._viewport;
+ var matrix = svg.createSVGMatrix();
+ var point = svg.createSVGPoint();
+ var centerPoint, originalPoint, currentMatrix, scaleMatrix, newMatrix;
+ currentMatrix = viewport.getCTM();
+ var currentScale = currentMatrix.a;
+
+ if (center) {
+ centerPoint = (0, _minDash.assign)(point, center); // revert applied viewport transformations
+
+ originalPoint = centerPoint.matrixTransform(currentMatrix.inverse()); // create scale matrix
+
+ scaleMatrix = matrix.translate(originalPoint.x, originalPoint.y).scale(1 / currentScale * scale).translate(-originalPoint.x, -originalPoint.y);
+ newMatrix = currentMatrix.multiply(scaleMatrix);
+ } else {
+ newMatrix = matrix.scale(scale);
+ }
+
+ setCTM(this._viewport, newMatrix);
+ return newMatrix;
+};
+/**
+ * Returns the size of the canvas
+ *
+ * @return {Dimensions}
+ */
+
+
+Canvas.prototype.getSize = function () {
+ return {
+ width: this._container.clientWidth,
+ height: this._container.clientHeight
+ };
+};
+/**
+ * Return the absolute bounding box for the given element
+ *
+ * The absolute bounding box may be used to display overlays in the
+ * callers (browser) coordinate system rather than the zoomed in/out
+ * canvas coordinates.
+ *
+ * @param {ElementDescriptor} element
+ * @return {Bounds} the absolute bounding box
+ */
+
+
+Canvas.prototype.getAbsoluteBBox = function (element) {
+ var vbox = this.viewbox();
+ var bbox; // connection
+ // use svg bbox
+
+ if (element.waypoints) {
+ var gfx = this.getGraphics(element);
+ bbox = gfx.getBBox();
+ } // shapes
+ // use data
+ else {
+ bbox = element;
+ }
+
+ var x = bbox.x * vbox.scale - vbox.x * vbox.scale;
+ var y = bbox.y * vbox.scale - vbox.y * vbox.scale;
+ var width = bbox.width * vbox.scale;
+ var height = bbox.height * vbox.scale;
+ return {
+ x: x,
+ y: y,
+ width: width,
+ height: height
+ };
+};
+/**
+ * Fires an event in order other modules can react to the
+ * canvas resizing
+ */
+
+
+Canvas.prototype.resized = function () {
+ // force recomputation of view box
+ delete this._cachedViewbox;
+
+ this._eventBus.fire('canvas.resized');
+};
+
+},{"../util/Collections":313,"../util/Elements":315,"min-dash":555,"tiny-svg":567}],149:[function(require,module,exports){
+"use strict";
+
+Object.defineProperty(exports, "__esModule", {
+ value: true
+});
+exports.default = ElementFactory;
+
+var _model = require("../model");
+
+var _minDash = require("min-dash");
+
+/**
+ * A factory for diagram-js shapes
+ */
+function ElementFactory() {
+ this._uid = 12;
+}
+
+ElementFactory.prototype.createRoot = function (attrs) {
+ return this.create('root', attrs);
+};
+
+ElementFactory.prototype.createLabel = function (attrs) {
+ return this.create('label', attrs);
+};
+
+ElementFactory.prototype.createShape = function (attrs) {
+ return this.create('shape', attrs);
+};
+
+ElementFactory.prototype.createConnection = function (attrs) {
+ return this.create('connection', attrs);
+};
+/**
+ * Create a model element with the given type and
+ * a number of pre-set attributes.
+ *
+ * @param {string} type
+ * @param {Object} attrs
+ * @return {djs.model.Base} the newly created model instance
+ */
+
+
+ElementFactory.prototype.create = function (type, attrs) {
+ attrs = (0, _minDash.assign)({}, attrs || {});
+
+ if (!attrs.id) {
+ attrs.id = type + '_' + this._uid++;
+ }
+
+ return (0, _model.create)(type, attrs);
+};
+
+},{"../model":302,"min-dash":555}],150:[function(require,module,exports){
+"use strict";
+
+Object.defineProperty(exports, "__esModule", {
+ value: true
+});
+exports.default = ElementRegistry;
+
+var _tinySvg = require("tiny-svg");
+
+var ELEMENT_ID = 'data-element-id';
+
+/**
+ * @class
+ *
+ * A registry that keeps track of all shapes in the diagram.
+ */
+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
+
+
+ (0, _tinySvg.attr)(gfx, ELEMENT_ID, id);
+
+ if (secondaryGfx) {
+ (0, _tinySvg.attr)(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
+ (0, _tinySvg.attr)(container.gfx, ELEMENT_ID, '');
+
+ if (container.secondaryGfx) {
+ (0, _tinySvg.attr)(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 && (0, _tinySvg.attr)(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 the first element that satisfies the provided testing function.
+ *
+ * @param {Function} fn
+ *
+ * @return {djs.model.Base}
+ */
+
+
+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;
+ }
+ }
+};
+/**
+ * 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); //