for example), or a DocumentFragment
+ * instance, depending on the contents of the `html` string.
+ *
+ * @param {String} html - HTML string to "domify"
+ * @param {Document} doc - The `document` instance to create the Node for
+ * @return {DOMNode} the TextNode, DOM Node, or DocumentFragment instance
+ * @api private
+ */
+
+ function parse(html, doc) {
+ if ('string' != typeof html) throw new TypeError('String expected');
+
+ // default to the global `document` object
+ if (!doc) doc = document;
+
+ // tag name
+ var m = /<([\w:]+)/.exec(html);
+ if (!m) return doc.createTextNode(html);
+
+ html = html.replace(/^\s+|\s+$/g, ''); // Remove leading/trailing whitespace
+
+ var tag = m[1];
+
+ // body support
+ if (tag == 'body') {
+ var el = doc.createElement('html');
+ el.innerHTML = html;
+ return el.removeChild(el.lastChild);
+ }
+
+ // wrap map
+ var wrap = map$1[tag] || map$1._default;
+ var depth = wrap[0];
+ var prefix = wrap[1];
+ var suffix = wrap[2];
+ var el = doc.createElement('div');
+ el.innerHTML = prefix + html + suffix;
+ while (depth--) el = el.lastChild;
+
+ // one element
+ if (el.firstChild == el.lastChild) {
+ return el.removeChild(el.firstChild);
+ }
+
+ // several elements
+ var fragment = doc.createDocumentFragment();
+ while (el.firstChild) {
+ fragment.appendChild(el.removeChild(el.firstChild));
+ }
+
+ return fragment;
+ }
+
+ var proto$1 = typeof Element !== 'undefined' ? Element.prototype : {};
+ var vendor$1 = proto$1.matches
+ || proto$1.matchesSelector
+ || proto$1.webkitMatchesSelector
+ || proto$1.mozMatchesSelector
+ || proto$1.msMatchesSelector
+ || proto$1.oMatchesSelector;
+
+ function query(selector, el) {
+ el = el || document;
+
+ return el.querySelector(selector);
+ }
+
+ function remove(el) {
+ el.parentNode && el.parentNode.removeChild(el);
+ }
+
+ function ensureImported(element, target) {
+
+ if (element.ownerDocument !== target.ownerDocument) {
+ try {
+ // may fail on webkit
+ return target.ownerDocument.importNode(element, true);
+ } catch (e) {
+ // ignore
+ }
+ }
+
+ return element;
+ }
+
+ /**
+ * appendTo utility
+ */
+
+ /**
+ * Append a node to a target element and return the appended node.
+ *
+ * @param {SVGElement} element
+ * @param {SVGElement} node
+ *
+ * @return {SVGElement} the appended node
+ */
+ function appendTo(element, target) {
+ target.appendChild(ensureImported(element, target));
+ return element;
+ }
+
+ /**
+ * append utility
+ */
+
+ /**
+ * Append a node to an element
+ *
+ * @param {SVGElement} element
+ * @param {SVGElement} node
+ *
+ * @return {SVGElement} the element
+ */
+ function append(element, node) {
+ appendTo(node, element);
+ return element;
+ }
+
+ /**
+ * attribute accessor utility
+ */
+
+ var LENGTH_ATTR = 2;
+
+ var CSS_PROPERTIES = {
+ 'alignment-baseline': 1,
+ 'baseline-shift': 1,
+ 'clip': 1,
+ 'clip-path': 1,
+ 'clip-rule': 1,
+ 'color': 1,
+ 'color-interpolation': 1,
+ 'color-interpolation-filters': 1,
+ 'color-profile': 1,
+ 'color-rendering': 1,
+ 'cursor': 1,
+ 'direction': 1,
+ 'display': 1,
+ 'dominant-baseline': 1,
+ 'enable-background': 1,
+ 'fill': 1,
+ 'fill-opacity': 1,
+ 'fill-rule': 1,
+ 'filter': 1,
+ 'flood-color': 1,
+ 'flood-opacity': 1,
+ 'font': 1,
+ 'font-family': 1,
+ 'font-size': LENGTH_ATTR,
+ 'font-size-adjust': 1,
+ 'font-stretch': 1,
+ 'font-style': 1,
+ 'font-variant': 1,
+ 'font-weight': 1,
+ 'glyph-orientation-horizontal': 1,
+ 'glyph-orientation-vertical': 1,
+ 'image-rendering': 1,
+ 'kerning': 1,
+ 'letter-spacing': 1,
+ 'lighting-color': 1,
+ 'marker': 1,
+ 'marker-end': 1,
+ 'marker-mid': 1,
+ 'marker-start': 1,
+ 'mask': 1,
+ 'opacity': 1,
+ 'overflow': 1,
+ 'pointer-events': 1,
+ 'shape-rendering': 1,
+ 'stop-color': 1,
+ 'stop-opacity': 1,
+ 'stroke': 1,
+ 'stroke-dasharray': 1,
+ 'stroke-dashoffset': 1,
+ 'stroke-linecap': 1,
+ 'stroke-linejoin': 1,
+ 'stroke-miterlimit': 1,
+ 'stroke-opacity': 1,
+ 'stroke-width': LENGTH_ATTR,
+ 'text-anchor': 1,
+ 'text-decoration': 1,
+ 'text-rendering': 1,
+ 'unicode-bidi': 1,
+ 'visibility': 1,
+ 'word-spacing': 1,
+ 'writing-mode': 1
+ };
+
+
+ function getAttribute(node, name) {
+ if (CSS_PROPERTIES[name]) {
+ return node.style[name];
+ } else {
+ return node.getAttributeNS(null, name);
+ }
+ }
+
+ function setAttribute(node, name, value) {
+ var hyphenated = name.replace(/([a-z])([A-Z])/g, '$1-$2').toLowerCase();
+
+ var type = CSS_PROPERTIES[hyphenated];
+
+ if (type) {
+ // append pixel unit, unless present
+ if (type === LENGTH_ATTR && typeof value === 'number') {
+ value = String(value) + 'px';
+ }
+
+ node.style[hyphenated] = value;
+ } else {
+ node.setAttributeNS(null, name, value);
+ }
+ }
+
+ function setAttributes(node, attrs) {
+
+ var names = Object.keys(attrs), i, name;
+
+ for (i = 0, name; (name = names[i]); i++) {
+ setAttribute(node, name, attrs[name]);
+ }
+ }
+
+ /**
+ * Gets or sets raw attributes on a node.
+ *
+ * @param {SVGElement} node
+ * @param {Object} [attrs]
+ * @param {String} [name]
+ * @param {String} [value]
+ *
+ * @return {String}
+ */
+ function attr$1(node, name, value) {
+ if (typeof name === 'string') {
+ if (value !== undefined) {
+ setAttribute(node, name, value);
+ } else {
+ return getAttribute(node, name);
+ }
+ } else {
+ setAttributes(node, name);
+ }
+
+ return node;
+ }
+
+ /**
+ * Clear utility
+ */
+ function index(arr, obj) {
+ if (arr.indexOf) {
+ return arr.indexOf(obj);
+ }
+
+
+ for (var i = 0; i < arr.length; ++i) {
+ if (arr[i] === obj) {
+ return i;
+ }
+ }
+
+ return -1;
+ }
+
+ var re$1 = /\s+/;
+
+ var toString$1 = Object.prototype.toString;
+
+ function defined(o) {
+ return typeof o !== 'undefined';
+ }
+
+ /**
+ * Wrap `el` in a `ClassList`.
+ *
+ * @param {Element} el
+ * @return {ClassList}
+ * @api public
+ */
+
+ function classes$1(el) {
+ return new ClassList$1(el);
+ }
+
+ function ClassList$1(el) {
+ if (!el || !el.nodeType) {
+ throw new Error('A DOM element reference is required');
+ }
+ this.el = el;
+ this.list = el.classList;
+ }
+
+ /**
+ * Add class `name` if not already present.
+ *
+ * @param {String} name
+ * @return {ClassList}
+ * @api public
+ */
+
+ ClassList$1.prototype.add = function(name) {
+
+ // classList
+ if (this.list) {
+ this.list.add(name);
+ return this;
+ }
+
+ // fallback
+ var arr = this.array();
+ var i = index(arr, name);
+ if (!~i) {
+ arr.push(name);
+ }
+
+ if (defined(this.el.className.baseVal)) {
+ this.el.className.baseVal = arr.join(' ');
+ } else {
+ this.el.className = arr.join(' ');
+ }
+
+ return this;
+ };
+
+ /**
+ * Remove class `name` when present, or
+ * pass a regular expression to remove
+ * any which match.
+ *
+ * @param {String|RegExp} name
+ * @return {ClassList}
+ * @api public
+ */
+
+ ClassList$1.prototype.remove = function(name) {
+ if ('[object RegExp]' === toString$1.call(name)) {
+ return this.removeMatching(name);
+ }
+
+ // classList
+ if (this.list) {
+ this.list.remove(name);
+ return this;
+ }
+
+ // fallback
+ var arr = this.array();
+ var i = index(arr, name);
+ if (~i) {
+ arr.splice(i, 1);
+ }
+ this.el.className.baseVal = arr.join(' ');
+ return this;
+ };
+
+ /**
+ * Remove all classes matching `re`.
+ *
+ * @param {RegExp} re
+ * @return {ClassList}
+ * @api private
+ */
+
+ ClassList$1.prototype.removeMatching = function(re) {
+ var arr = this.array();
+ for (var i = 0; i < arr.length; i++) {
+ if (re.test(arr[i])) {
+ this.remove(arr[i]);
+ }
+ }
+ return this;
+ };
+
+ /**
+ * Toggle class `name`, can force state via `force`.
+ *
+ * For browsers that support classList, but do not support `force` yet,
+ * the mistake will be detected and corrected.
+ *
+ * @param {String} name
+ * @param {Boolean} force
+ * @return {ClassList}
+ * @api public
+ */
+
+ ClassList$1.prototype.toggle = function(name, force) {
+ // classList
+ if (this.list) {
+ if (defined(force)) {
+ if (force !== this.list.toggle(name, force)) {
+ this.list.toggle(name); // toggle again to correct
+ }
+ } else {
+ this.list.toggle(name);
+ }
+ return this;
+ }
+
+ // fallback
+ if (defined(force)) {
+ if (!force) {
+ this.remove(name);
+ } else {
+ this.add(name);
+ }
+ } else {
+ if (this.has(name)) {
+ this.remove(name);
+ } else {
+ this.add(name);
+ }
+ }
+
+ return this;
+ };
+
+ /**
+ * Return an array of classes.
+ *
+ * @return {Array}
+ * @api public
+ */
+
+ ClassList$1.prototype.array = function() {
+ var className = this.el.getAttribute('class') || '';
+ var str = className.replace(/^\s+|\s+$/g, '');
+ var arr = str.split(re$1);
+ if ('' === arr[0]) {
+ arr.shift();
+ }
+ return arr;
+ };
+
+ /**
+ * Check if class `name` is present.
+ *
+ * @param {String} name
+ * @return {ClassList}
+ * @api public
+ */
+
+ ClassList$1.prototype.has =
+ ClassList$1.prototype.contains = function(name) {
+ return (
+ this.list ?
+ this.list.contains(name) :
+ !! ~index(this.array(), name)
+ );
+ };
+
+ function remove$1(element) {
+ var parent = element.parentNode;
+
+ if (parent) {
+ parent.removeChild(element);
+ }
+
+ return element;
+ }
+
+ /**
+ * Clear utility
+ */
+
+ /**
+ * Removes all children from the given element
+ *
+ * @param {DOMElement} element
+ * @return {DOMElement} the element (for chaining)
+ */
+ function clear$1(element) {
+ var child;
+
+ while ((child = element.firstChild)) {
+ remove$1(child);
+ }
+
+ return element;
+ }
+
+ var ns = {
+ svg: 'http://www.w3.org/2000/svg'
+ };
+
+ /**
+ * DOM parsing utility
+ */
+
+ var SVG_START = '
' + svg + ' ';
+ }
+
+ return parseDocument(svg);
+ }
+
+ function parseDocument(svg) {
+
+ var parser;
+
+ // parse
+ parser = new DOMParser();
+ parser.async = false;
+
+ return parser.parseFromString(svg, 'text/xml');
+ }
+
+ /**
+ * Create utility for SVG elements
+ */
+
+
+ /**
+ * Create a specific type from name or SVG markup.
+ *
+ * @param {String} name the name or markup of the element
+ * @param {Object} [attrs] attributes to set on the element
+ *
+ * @returns {SVGElement}
+ */
+ function create(name, attrs) {
+ var element;
+
+ if (name.charAt(0) === '<') {
+ element = parse$1(name).firstChild;
+ element = document.importNode(element, true);
+ } else {
+ element = document.createElementNS(ns.svg, name);
+ }
+
+ if (attrs) {
+ attr$1(element, attrs);
+ }
+
+ return element;
+ }
+
+ /**
+ * Geometry helpers
+ */
+
+ // fake node used to instantiate svg geometry elements
+ var node = create('svg');
+
+ function extend(object, props) {
+ var i, k, keys = Object.keys(props);
+
+ for (i = 0; (k = keys[i]); i++) {
+ object[k] = props[k];
+ }
+
+ return object;
+ }
+
+ function createMatrix(a, b, c, d, e, f) {
+ var matrix = node.createSVGMatrix();
+
+ switch (arguments.length) {
+ case 0:
+ return matrix;
+ case 6:
+ a = {
+ a: a,
+ b: b,
+ c: c,
+ d: d,
+ e: e,
+ f: f
+ };
+ break;
+ }
+
+ return extend(matrix, a);
+ }
+
+ function createTransform(matrix) {
+ if (matrix) {
+ return node.createSVGTransformFromMatrix(matrix);
+ } else {
+ return node.createSVGTransform();
+ }
+ }
+
+ /**
+ * Serialization util
+ */
+
+ var TEXT_ENTITIES = /([&<>]{1})/g;
+ var ATTR_ENTITIES = /([\n\r"]{1})/g;
+
+ var ENTITY_REPLACEMENT = {
+ '&': '&',
+ '<': '<',
+ '>': '>',
+ '"': '\''
+ };
+
+ function escape(str, pattern) {
+
+ function replaceFn(match, entity) {
+ return ENTITY_REPLACEMENT[entity] || entity;
+ }
+
+ return str.replace(pattern, replaceFn);
+ }
+
+ function serialize(node, output) {
+
+ var i, len, attrMap, attrNode, childNodes;
+
+ switch (node.nodeType) {
+ // TEXT
+ case 3:
+ // replace special XML characters
+ output.push(escape(node.textContent, TEXT_ENTITIES));
+ break;
+
+ // ELEMENT
+ case 1:
+ output.push('<', node.tagName);
+
+ if (node.hasAttributes()) {
+ attrMap = node.attributes;
+ for (i = 0, len = attrMap.length; i < len; ++i) {
+ attrNode = attrMap.item(i);
+ output.push(' ', attrNode.name, '="', escape(attrNode.value, ATTR_ENTITIES), '"');
+ }
+ }
+
+ if (node.hasChildNodes()) {
+ output.push('>');
+ childNodes = node.childNodes;
+ for (i = 0, len = childNodes.length; i < len; ++i) {
+ serialize(childNodes.item(i), output);
+ }
+ output.push('', node.tagName, '>');
+ } else {
+ output.push('/>');
+ }
+ break;
+
+ // COMMENT
+ case 8:
+ output.push('');
+ break;
+
+ // CDATA
+ case 4:
+ output.push('');
+ break;
+
+ default:
+ throw new Error('unable to handle node ' + node.nodeType);
+ }
+
+ return output;
+ }
+
+ /**
+ * innerHTML like functionality for SVG elements.
+ * based on innerSVG (https://code.google.com/p/innersvg)
+ */
+
+
+ function set(element, svg) {
+
+ var node,
+ documentElement = parse$1(svg).documentElement;
+
+ // clear element contents
+ clear$1(element);
+
+ if (!svg) {
+ return;
+ }
+
+ // import + append each node
+ node = documentElement.firstChild;
+
+ while (node) {
+ appendTo(node, element);
+ node = node.nextSibling;
+ }
+ }
+
+ function get(element) {
+ var child = element.firstChild,
+ output = [];
+
+ while (child) {
+ serialize(child, output);
+ child = child.nextSibling;
+ }
+
+ return output.join('');
+ }
+
+ function innerSVG(element, svg) {
+
+ if (svg !== undefined) {
+
+ try {
+ set(element, svg);
+ } catch (e) {
+ throw new Error('error parsing SVG: ' + e.message);
+ }
+
+ return element;
+ } else {
+ return get(element);
+ }
+ }
+
+ /**
+ * transform accessor utility
+ */
+
+ function wrapMatrix(transformList, transform) {
+ if (transform instanceof SVGMatrix) {
+ return transformList.createSVGTransformFromMatrix(transform);
+ } else {
+ return transform;
+ }
+ }
+
+ function setTransforms(transformList, transforms) {
+ var i, t;
+
+ transformList.clear();
+
+ for (i = 0; (t = transforms[i]); i++) {
+ transformList.appendItem(wrapMatrix(transformList, t));
+ }
+
+ transformList.consolidate();
+ }
+
+ function transform(node, transforms) {
+ var transformList = node.transform.baseVal;
+
+ if (arguments.length === 1) {
+ return transformList.consolidate();
+ } else {
+ if (transforms.length) {
+ setTransforms(transformList, transforms);
+ } else {
+ transformList.initialize(wrapMatrix(transformList, transforms));
+ }
+ }
+ }
+
+ var CLASS_PATTERN = /^class /;
+
+ function isClass(fn) {
+ return CLASS_PATTERN.test(fn.toString());
+ }
+
+ function isArray$1(obj) {
+ return Object.prototype.toString.call(obj) === '[object Array]';
+ }
+
+ function annotate() {
+ var args = Array.prototype.slice.call(arguments);
+
+ if (args.length === 1 && isArray$1(args[0])) {
+ args = args[0];
+ }
+
+ var fn = args.pop();
+
+ fn.$inject = args;
+
+ return fn;
+ }
+
+ // Current limitations:
+ // - can't put into "function arg" comments
+ // function /* (no parenthesis like this) */ (){}
+ // function abc( /* xx (no parenthesis like this) */ a, b) {}
+ //
+ // Just put the comment before function or inside:
+ // /* (((this is fine))) */ function(a, b) {}
+ // function abc(a) { /* (((this is fine))) */}
+ //
+ // - can't reliably auto-annotate constructor; we'll match the
+ // first constructor(...) pattern found which may be the one
+ // of a nested class, too.
+
+ var CONSTRUCTOR_ARGS = /constructor\s*[^(]*\(\s*([^)]*)\)/m;
+ var FN_ARGS = /^function\s*[^(]*\(\s*([^)]*)\)/m;
+ var FN_ARG = /\/\*([^*]*)\*\//m;
+
+ function parse$2(fn) {
+
+ if (typeof fn !== 'function') {
+ throw new Error('Cannot annotate "' + fn + '". Expected a function!');
+ }
+
+ var match = fn.toString().match(isClass(fn) ? CONSTRUCTOR_ARGS : FN_ARGS);
+
+ // may parse class without constructor
+ if (!match) {
+ return [];
+ }
+
+ return match[1] && match[1].split(',').map(function (arg) {
+ match = arg.match(FN_ARG);
+ return match ? match[1].trim() : arg.trim();
+ }) || [];
+ }
+
+ function Module() {
+ var providers = [];
+
+ this.factory = function (name, factory) {
+ providers.push([name, 'factory', factory]);
+ return this;
+ };
+
+ this.value = function (name, value) {
+ providers.push([name, 'value', value]);
+ return this;
+ };
+
+ this.type = function (name, type) {
+ providers.push([name, 'type', type]);
+ return this;
+ };
+
+ this.forEach = function (iterator) {
+ providers.forEach(iterator);
+ };
+ }
+
+ var _typeof = typeof Symbol === "function" && typeof Symbol.iterator === "symbol" ? function (obj) { return typeof obj; } : function (obj) { return obj && typeof Symbol === "function" && obj.constructor === Symbol && obj !== Symbol.prototype ? "symbol" : typeof obj; };
+
+ function _toConsumableArray$1(arr) { if (Array.isArray(arr)) { for (var i = 0, arr2 = Array(arr.length); i < arr.length; i++) { arr2[i] = arr[i]; } return arr2; } else { return Array.from(arr); } }
+
+ function Injector(modules, parent) {
+ parent = parent || {
+ get: function get(name, strict) {
+ currentlyResolving.push(name);
+
+ if (strict === false) {
+ return null;
+ } else {
+ throw error('No provider for "' + name + '"!');
+ }
+ }
+ };
+
+ var currentlyResolving = [];
+ var providers = this._providers = Object.create(parent._providers || null);
+ var instances = this._instances = Object.create(null);
+
+ var self = instances.injector = this;
+
+ var error = function error(msg) {
+ var stack = currentlyResolving.join(' -> ');
+ currentlyResolving.length = 0;
+ return new Error(stack ? msg + ' (Resolving: ' + stack + ')' : msg);
+ };
+
+ /**
+ * Return a named service.
+ *
+ * @param {String} name
+ * @param {Boolean} [strict=true] if false, resolve missing services to null
+ *
+ * @return {Object}
+ */
+ var get = function get(name, strict) {
+ if (!providers[name] && name.indexOf('.') !== -1) {
+ var parts = name.split('.');
+ var pivot = get(parts.shift());
+
+ while (parts.length) {
+ pivot = pivot[parts.shift()];
+ }
+
+ return pivot;
+ }
+
+ if (hasProp(instances, name)) {
+ return instances[name];
+ }
+
+ if (hasProp(providers, name)) {
+ if (currentlyResolving.indexOf(name) !== -1) {
+ currentlyResolving.push(name);
+ throw error('Cannot resolve circular dependency!');
+ }
+
+ currentlyResolving.push(name);
+ instances[name] = providers[name][0](providers[name][1]);
+ currentlyResolving.pop();
+
+ return instances[name];
+ }
+
+ return parent.get(name, strict);
+ };
+
+ var fnDef = function fnDef(fn) {
+ var locals = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {};
+
+ if (typeof fn !== 'function') {
+ if (isArray$1(fn)) {
+ fn = annotate(fn.slice());
+ } else {
+ throw new Error('Cannot invoke "' + fn + '". Expected a function!');
+ }
+ }
+
+ var inject = fn.$inject || parse$2(fn);
+ var dependencies = inject.map(function (dep) {
+ if (hasProp(locals, dep)) {
+ return locals[dep];
+ } else {
+ return get(dep);
+ }
+ });
+
+ return {
+ fn: fn,
+ dependencies: dependencies
+ };
+ };
+
+ var instantiate = function instantiate(Type) {
+ var _fnDef = fnDef(Type),
+ dependencies = _fnDef.dependencies,
+ fn = _fnDef.fn;
+
+ return new (Function.prototype.bind.apply(fn, [null].concat(_toConsumableArray$1(dependencies))))();
+ };
+
+ var invoke = function invoke(func, context, locals) {
+ var _fnDef2 = fnDef(func, locals),
+ dependencies = _fnDef2.dependencies,
+ fn = _fnDef2.fn;
+
+ return fn.call.apply(fn, [context].concat(_toConsumableArray$1(dependencies)));
+ };
+
+ var createPrivateInjectorFactory = function createPrivateInjectorFactory(privateChildInjector) {
+ return annotate(function (key) {
+ return privateChildInjector.get(key);
+ });
+ };
+
+ var createChild = function createChild(modules, forceNewInstances) {
+ if (forceNewInstances && forceNewInstances.length) {
+ var fromParentModule = Object.create(null);
+ var matchedScopes = Object.create(null);
+
+ var privateInjectorsCache = [];
+ var privateChildInjectors = [];
+ var privateChildFactories = [];
+
+ var provider;
+ var cacheIdx;
+ var privateChildInjector;
+ var privateChildInjectorFactory;
+ for (var name in providers) {
+ provider = providers[name];
+
+ if (forceNewInstances.indexOf(name) !== -1) {
+ if (provider[2] === 'private') {
+ cacheIdx = privateInjectorsCache.indexOf(provider[3]);
+ if (cacheIdx === -1) {
+ privateChildInjector = provider[3].createChild([], forceNewInstances);
+ privateChildInjectorFactory = createPrivateInjectorFactory(privateChildInjector);
+ privateInjectorsCache.push(provider[3]);
+ privateChildInjectors.push(privateChildInjector);
+ privateChildFactories.push(privateChildInjectorFactory);
+ fromParentModule[name] = [privateChildInjectorFactory, name, 'private', privateChildInjector];
+ } else {
+ fromParentModule[name] = [privateChildFactories[cacheIdx], name, 'private', privateChildInjectors[cacheIdx]];
+ }
+ } else {
+ fromParentModule[name] = [provider[2], provider[1]];
+ }
+ matchedScopes[name] = true;
+ }
+
+ if ((provider[2] === 'factory' || provider[2] === 'type') && provider[1].$scope) {
+ /* jshint -W083 */
+ forceNewInstances.forEach(function (scope) {
+ if (provider[1].$scope.indexOf(scope) !== -1) {
+ fromParentModule[name] = [provider[2], provider[1]];
+ matchedScopes[scope] = true;
+ }
+ });
+ }
+ }
+
+ forceNewInstances.forEach(function (scope) {
+ if (!matchedScopes[scope]) {
+ throw new Error('No provider for "' + scope + '". Cannot use provider from the parent!');
+ }
+ });
+
+ modules.unshift(fromParentModule);
+ }
+
+ return new Injector(modules, self);
+ };
+
+ var factoryMap = {
+ factory: invoke,
+ type: instantiate,
+ value: function value(_value) {
+ return _value;
+ }
+ };
+
+ modules.forEach(function (module) {
+
+ function arrayUnwrap(type, value) {
+ if (type !== 'value' && isArray$1(value)) {
+ value = annotate(value.slice());
+ }
+
+ return value;
+ }
+
+ // TODO(vojta): handle wrong inputs (modules)
+ if (module instanceof Module) {
+ module.forEach(function (provider) {
+ var name = provider[0];
+ var type = provider[1];
+ var value = provider[2];
+
+ providers[name] = [factoryMap[type], arrayUnwrap(type, value), type];
+ });
+ } else if ((typeof module === 'undefined' ? 'undefined' : _typeof(module)) === 'object') {
+ if (module.__exports__) {
+ var clonedModule = Object.keys(module).reduce(function (m, key) {
+ if (key.substring(0, 2) !== '__') {
+ m[key] = module[key];
+ }
+ return m;
+ }, Object.create(null));
+
+ var privateInjector = new Injector((module.__modules__ || []).concat([clonedModule]), self);
+ var getFromPrivateInjector = annotate(function (key) {
+ return privateInjector.get(key);
+ });
+ module.__exports__.forEach(function (key) {
+ providers[key] = [getFromPrivateInjector, key, 'private', privateInjector];
+ });
+ } else {
+ Object.keys(module).forEach(function (name) {
+ if (module[name][2] === 'private') {
+ providers[name] = module[name];
+ return;
+ }
+
+ var type = module[name][0];
+ var value = module[name][1];
+
+ providers[name] = [factoryMap[type], arrayUnwrap(type, value), type];
+ });
+ }
+ }
+ });
+
+ // public API
+ this.get = get;
+ this.invoke = invoke;
+ this.instantiate = instantiate;
+ this.createChild = createChild;
+ }
+
+ // helpers /////////////////
+
+ function hasProp(obj, prop) {
+ return Object.hasOwnProperty.call(obj, prop);
+ }
+
+ var DEFAULT_RENDER_PRIORITY = 1000;
+
+ /**
+ * The base implementation of shape and connection renderers.
+ *
+ * @param {EventBus} eventBus
+ * @param {Number} [renderPriority=1000]
+ */
+ function BaseRenderer(eventBus, renderPriority) {
+ var self = this;
+
+ renderPriority = renderPriority || DEFAULT_RENDER_PRIORITY;
+
+ eventBus.on([ 'render.shape', 'render.connection' ], renderPriority, function(evt, context) {
+ var type = evt.type,
+ element = context.element,
+ visuals = context.gfx;
+
+ if (self.canRender(element)) {
+ if (type === 'render.shape') {
+ return self.drawShape(visuals, element);
+ } else {
+ return self.drawConnection(visuals, element);
+ }
+ }
+ });
+
+ eventBus.on([ 'render.getShapePath', 'render.getConnectionPath'], renderPriority, function(evt, element) {
+ if (self.canRender(element)) {
+ if (evt.type === 'render.getShapePath') {
+ return self.getShapePath(element);
+ } else {
+ return self.getConnectionPath(element);
+ }
+ }
+ });
+ }
+
+ /**
+ * Should check whether *this* renderer can render
+ * the element/connection.
+ *
+ * @param {element} element
+ *
+ * @returns {Boolean}
+ */
+ BaseRenderer.prototype.canRender = function() {};
+
+ /**
+ * Provides the shape's snap svg element to be drawn on the `canvas`.
+ *
+ * @param {djs.Graphics} visuals
+ * @param {Shape} shape
+ *
+ * @returns {Snap.svg} [returns a Snap.svg paper element ]
+ */
+ BaseRenderer.prototype.drawShape = function() {};
+
+ /**
+ * Provides the shape's snap svg element to be drawn on the `canvas`.
+ *
+ * @param {djs.Graphics} visuals
+ * @param {Connection} connection
+ *
+ * @returns {Snap.svg} [returns a Snap.svg paper element ]
+ */
+ BaseRenderer.prototype.drawConnection = function() {};
+
+ /**
+ * Gets the SVG path of a shape that represents it's visual bounds.
+ *
+ * @param {Shape} shape
+ *
+ * @return {string} svg path
+ */
+ BaseRenderer.prototype.getShapePath = function() {};
+
+ /**
+ * Gets the SVG path of a connection that represents it's visual bounds.
+ *
+ * @param {Connection} connection
+ *
+ * @return {string} svg path
+ */
+ BaseRenderer.prototype.getConnectionPath = function() {};
+
+ function componentsToPath(elements) {
+ return elements.join(',').replace(/,?([A-z]),?/g, '$1');
+ }
+
+ function toSVGPoints(points) {
+ var result = '';
+
+ for (var i = 0, p; (p = points[i]); i++) {
+ result += p.x + ',' + p.y + ' ';
+ }
+
+ return result;
+ }
+
+ function createLine(points, attrs) {
+
+ var line = create('polyline');
+ attr$1(line, { points: toSVGPoints(points) });
+
+ if (attrs) {
+ attr$1(line, attrs);
+ }
+
+ return line;
+ }
+
+ function updateLine(gfx, points) {
+ attr$1(gfx, { points: toSVGPoints(points) });
+
+ return gfx;
+ }
+
+ // apply default renderer with lowest possible priority
+ // so that it only kicks in if noone else could render
+ var DEFAULT_RENDER_PRIORITY$1 = 1;
+
+ /**
+ * The default renderer used for shapes and connections.
+ *
+ * @param {EventBus} eventBus
+ * @param {Styles} styles
+ */
+ function DefaultRenderer(eventBus, styles) {
+ //
+ BaseRenderer.call(this, eventBus, DEFAULT_RENDER_PRIORITY$1);
+
+ this.CONNECTION_STYLE = styles.style([ 'no-fill' ], { strokeWidth: 5, stroke: 'fuchsia' });
+ this.SHAPE_STYLE = styles.style({ fill: 'white', stroke: 'fuchsia', strokeWidth: 2 });
+ }
+
+ inherits_browser(DefaultRenderer, BaseRenderer);
+
+
+ DefaultRenderer.prototype.canRender = function() {
+ return true;
+ };
+
+ DefaultRenderer.prototype.drawShape = function drawShape(visuals, element) {
+
+ var rect = create('rect');
+ attr$1(rect, {
+ x: 0,
+ y: 0,
+ width: element.width || 0,
+ height: element.height || 0
+ });
+ attr$1(rect, this.SHAPE_STYLE);
+
+ append(visuals, rect);
+
+ return rect;
+ };
+
+ DefaultRenderer.prototype.drawConnection = function drawConnection(visuals, connection) {
+
+ var line = createLine(connection.waypoints, this.CONNECTION_STYLE);
+ append(visuals, line);
+
+ return line;
+ };
+
+ DefaultRenderer.prototype.getShapePath = function getShapePath(shape) {
+
+ var x = shape.x,
+ y = shape.y,
+ width = shape.width,
+ height = shape.height;
+
+ var shapePath = [
+ ['M', x, y],
+ ['l', width, 0],
+ ['l', 0, height],
+ ['l', -width, 0],
+ ['z']
+ ];
+
+ return componentsToPath(shapePath);
+ };
+
+ DefaultRenderer.prototype.getConnectionPath = function getConnectionPath(connection) {
+ var waypoints = connection.waypoints;
+
+ var idx, point, connectionPath = [];
+
+ for (idx = 0; (point = waypoints[idx]); idx++) {
+
+ // take invisible docking into account
+ // when creating the path
+ point = point.original || point;
+
+ connectionPath.push([ idx === 0 ? 'M' : 'L', point.x, point.y ]);
+ }
+
+ return componentsToPath(connectionPath);
+ };
+
+
+ DefaultRenderer.$inject = [ 'eventBus', 'styles' ];
+
+ /**
+ * A component that manages shape styles
+ */
+ function Styles() {
+
+ var defaultTraits = {
+
+ 'no-fill': {
+ fill: 'none'
+ },
+ 'no-border': {
+ strokeOpacity: 0.0
+ },
+ 'no-events': {
+ pointerEvents: 'none'
+ }
+ };
+
+ var self = this;
+
+ /**
+ * Builds a style definition from a className, a list of traits and an object of additional attributes.
+ *
+ * @param {String} className
+ * @param {Array
} traits
+ * @param {Object} additionalAttrs
+ *
+ * @return {Object} the style defintion
+ */
+ this.cls = function(className, traits, additionalAttrs) {
+ var attrs = this.style(traits, additionalAttrs);
+
+ return assign(attrs, { 'class': className });
+ };
+
+ /**
+ * Builds a style definition from a list of traits and an object of additional attributes.
+ *
+ * @param {Array} traits
+ * @param {Object} additionalAttrs
+ *
+ * @return {Object} the style defintion
+ */
+ this.style = function(traits, additionalAttrs) {
+
+ if (!isArray(traits) && !additionalAttrs) {
+ additionalAttrs = traits;
+ traits = [];
+ }
+
+ var attrs = reduce(traits, function(attrs, t) {
+ return assign(attrs, defaultTraits[t] || {});
+ }, {});
+
+ return additionalAttrs ? assign(attrs, additionalAttrs) : attrs;
+ };
+
+ this.computeStyle = function(custom, traits, defaultStyles) {
+ if (!isArray(traits)) {
+ defaultStyles = traits;
+ traits = [];
+ }
+
+ return self.style(traits || [], assign({}, defaultStyles, custom || {}));
+ };
+ }
+
+ var DrawModule = {
+ __init__: [ 'defaultRenderer' ],
+ defaultRenderer: [ 'type', DefaultRenderer ],
+ styles: [ 'type', Styles ]
+ };
+
+ /**
+ * Failsafe remove an element from a collection
+ *
+ * @param {Array} [collection]
+ * @param {Object} [element]
+ *
+ * @return {Number} the previous index of the element
+ */
+ function remove$2(collection, element) {
+
+ if (!collection || !element) {
+ return -1;
+ }
+
+ var idx = collection.indexOf(element);
+
+ if (idx !== -1) {
+ collection.splice(idx, 1);
+ }
+
+ return idx;
+ }
+
+ /**
+ * Fail save add an element to the given connection, ensuring
+ * it does not yet exist.
+ *
+ * @param {Array} collection
+ * @param {Object} element
+ * @param {Number} idx
+ */
+ function add(collection, element, idx) {
+
+ if (!collection || !element) {
+ return;
+ }
+
+ if (typeof idx !== 'number') {
+ idx = -1;
+ }
+
+ var currentIdx = collection.indexOf(element);
+
+ if (currentIdx !== -1) {
+
+ if (currentIdx === idx) {
+ // nothing to do, position has not changed
+ return;
+ } else {
+
+ if (idx !== -1) {
+ // remove from current position
+ collection.splice(currentIdx, 1);
+ } else {
+ // already exists in collection
+ return;
+ }
+ }
+ }
+
+ if (idx !== -1) {
+ // insert at specified position
+ collection.splice(idx, 0, element);
+ } else {
+ // push to end
+ collection.push(element);
+ }
+ }
+
+ /**
+ * Returns the surrounding bbox for all elements in
+ * the array or the element primitive.
+ *
+ * @param {Array|djs.model.Shape} elements
+ * @param {Boolean} stopRecursion
+ */
+ function getBBox(elements, stopRecursion) {
+
+ stopRecursion = !!stopRecursion;
+ if (!isArray(elements)) {
+ elements = [elements];
+ }
+
+ var minX,
+ minY,
+ maxX,
+ maxY;
+
+ forEach(elements, function(element) {
+
+ // If element is a connection the bbox must be computed first
+ var bbox = element;
+ if (element.waypoints && !stopRecursion) {
+ bbox = getBBox(element.waypoints, true);
+ }
+
+ var x = bbox.x,
+ y = bbox.y,
+ height = bbox.height || 0,
+ width = bbox.width || 0;
+
+ if (x < minX || minX === undefined) {
+ minX = x;
+ }
+ if (y < minY || minY === undefined) {
+ minY = y;
+ }
+
+ if ((x + width) > maxX || maxX === undefined) {
+ maxX = x + width;
+ }
+ if ((y + height) > maxY || maxY === undefined) {
+ maxY = y + height;
+ }
+ });
+
+ return {
+ x: minX,
+ y: minY,
+ height: maxY - minY,
+ width: maxX - minX
+ };
+ }
+
+
+ function getType(element) {
+
+ if ('waypoints' in element) {
+ return 'connection';
+ }
+
+ if ('x' in element) {
+ return 'shape';
+ }
+
+ return 'root';
+ }
+
+ function round(number, resolution) {
+ return Math.round(number * resolution) / resolution;
+ }
+
+ function ensurePx(number) {
+ return isNumber(number) ? number + 'px' : number;
+ }
+
+ /**
+ * Creates a HTML container element for a SVG element with
+ * the given configuration
+ *
+ * @param {Object} options
+ * @return {HTMLElement} the container element
+ */
+ function createContainer(options) {
+
+ options = assign({}, { width: '100%', height: '100%' }, options);
+
+ var container = options.container || document.body;
+
+ // create a around the svg element with the respective size
+ // this way we can always get the correct container size
+ // (this is impossible for
elements at the moment)
+ var parent = document.createElement('div');
+ parent.setAttribute('class', 'djs-container');
+
+ assign(parent.style, {
+ position: 'relative',
+ overflow: 'hidden',
+ width: ensurePx(options.width),
+ height: ensurePx(options.height)
+ });
+
+ container.appendChild(parent);
+
+ return parent;
+ }
+
+ function createGroup(parent, cls, childIndex) {
+ var group = create('g');
+ classes$1(group).add(cls);
+
+ var index = childIndex !== undefined ? childIndex : parent.childNodes.length - 1;
+
+ parent.insertBefore(group, parent.childNodes[index]);
+
+ return group;
+ }
+
+ var BASE_LAYER = 'base';
+
+
+ var REQUIRED_MODEL_ATTRS = {
+ shape: [ 'x', 'y', 'width', 'height' ],
+ connection: [ 'waypoints' ]
+ };
+
+ /**
+ * The main drawing canvas.
+ *
+ * @class
+ * @constructor
+ *
+ * @emits Canvas#canvas.init
+ *
+ * @param {Object} config
+ * @param {EventBus} eventBus
+ * @param {GraphicsFactory} graphicsFactory
+ * @param {ElementRegistry} elementRegistry
+ */
+ function Canvas(config, eventBus, graphicsFactory, elementRegistry) {
+
+ this._eventBus = eventBus;
+ this._elementRegistry = elementRegistry;
+ this._graphicsFactory = graphicsFactory;
+
+ this._init(config || {});
+ }
+
+ Canvas.$inject = [
+ 'config.canvas',
+ 'eventBus',
+ 'graphicsFactory',
+ 'elementRegistry'
+ ];
+
+
+ Canvas.prototype._init = function(config) {
+
+ var eventBus = this._eventBus;
+
+ // Creates a element that is wrapped into a .
+ // 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 = create('svg');
+ attr$1(svg, { width: '100%', height: '100%' });
+
+ 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 = debounce(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 = 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 = 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$$1) {
+ var container;
+
+ if (!element.id) {
+ element = this._elementRegistry.get(element);
+ }
+
+ // we need to access all
+ container = this._elementRegistry._elements[element.id];
+
+ if (!container) {
+ return;
+ }
+
+ forEach([ container.gfx, container.secondaryGfx ], function(gfx) {
+ if (gfx) {
+ // invoke either addClass or removeClass based on mode
+ if (add$$1) {
+ classes$1(gfx).add(marker);
+ } else {
+ classes$1(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$$1 });
+ };
+
+
+ /**
+ * 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 classes$1(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 = every(requiredAttrs, function(attr$$1) {
+ return typeof element[attr$$1] !== 'undefined';
+ });
+
+ if (!valid) {
+ throw new Error(
+ 'must supply { ' + requiredAttrs.join(', ') + ' } with ' + type);
+ }
+ };
+
+ Canvas.prototype._setParent = function(element, parent, parentIndex) {
+ 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
+ remove$2(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$$1,
+ 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$$1 = transform(viewport);
+ matrix = transform$$1 ? transform$$1.matrix : 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);
+
+ 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 = 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 = 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');
+ };
+
+ 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
+ attr$1(gfx, ELEMENT_ID, id);
+
+ if (secondaryGfx) {
+ attr$1(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
+ attr$1(container.gfx, ELEMENT_ID, '');
+
+ if (container.secondaryGfx) {
+ attr$1(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 && attr$1(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');
+ }
+ };
+
+ /**
+ * An empty collection stub. Use {@link RefsCollection.extend} to extend a
+ * collection with ref semantics.
+ *
+ * @class RefsCollection
+ */
+
+ /**
+ * Extends a collection with {@link Refs} aware methods
+ *
+ * @memberof RefsCollection
+ * @static
+ *
+ * @param {Array} collection
+ * @param {Refs} refs instance
+ * @param {Object} property represented by the collection
+ * @param {Object} target object the collection is attached to
+ *
+ * @return {RefsCollection} the extended array
+ */
+ function extend$1(collection, refs, property, target) {
+
+ var inverseProperty = property.inverse;
+
+ /**
+ * Removes the given element from the array and returns it.
+ *
+ * @method RefsCollection#remove
+ *
+ * @param {Object} element the element to remove
+ */
+ Object.defineProperty(collection, 'remove', {
+ value: function(element) {
+ var idx = this.indexOf(element);
+ if (idx !== -1) {
+ this.splice(idx, 1);
+
+ // unset inverse
+ refs.unset(element, inverseProperty, target);
+ }
+
+ return element;
+ }
+ });
+
+ /**
+ * Returns true if the collection contains the given element
+ *
+ * @method RefsCollection#contains
+ *
+ * @param {Object} element the element to check for
+ */
+ Object.defineProperty(collection, 'contains', {
+ value: function(element) {
+ return this.indexOf(element) !== -1;
+ }
+ });
+
+ /**
+ * Adds an element to the array, unless it exists already (set semantics).
+ *
+ * @method RefsCollection#add
+ *
+ * @param {Object} element the element to add
+ * @param {Number} optional index to add element to
+ * (possibly moving other elements around)
+ */
+ Object.defineProperty(collection, 'add', {
+ value: function(element, idx) {
+
+ var currentIdx = this.indexOf(element);
+
+ if (typeof idx === 'undefined') {
+
+ if (currentIdx !== -1) {
+ // element already in collection (!)
+ return;
+ }
+
+ // add to end of array, as no idx is specified
+ idx = this.length;
+ }
+
+ // handle already in collection
+ if (currentIdx !== -1) {
+
+ // remove element from currentIdx
+ this.splice(currentIdx, 1);
+ }
+
+ // add element at idx
+ this.splice(idx, 0, element);
+
+ if (currentIdx === -1) {
+ // set inverse, unless element was
+ // in collection already
+ refs.set(element, inverseProperty, target);
+ }
+ }
+ });
+
+ // a simple marker, identifying this element
+ // as being a refs collection
+ Object.defineProperty(collection, '__refs_collection', {
+ value: true
+ });
+
+ return collection;
+ }
+
+
+ function isExtended(collection) {
+ return collection.__refs_collection === true;
+ }
+
+ var extend_1 = extend$1;
+
+ var isExtended_1 = isExtended;
+
+ var collection = {
+ extend: extend_1,
+ isExtended: isExtended_1
+ };
+
+ function hasOwnProperty(e, property) {
+ return Object.prototype.hasOwnProperty.call(e, property.name || property);
+ }
+
+ function defineCollectionProperty(ref, property, target) {
+
+ var collection$$1 = collection.extend(target[property.name] || [], ref, property, target);
+
+ Object.defineProperty(target, property.name, {
+ enumerable: property.enumerable,
+ value: collection$$1
+ });
+
+ if (collection$$1.length) {
+
+ collection$$1.forEach(function(o) {
+ ref.set(o, property.inverse, target);
+ });
+ }
+ }
+
+
+ function defineProperty(ref, property, target) {
+
+ var inverseProperty = property.inverse;
+
+ var _value = target[property.name];
+
+ Object.defineProperty(target, property.name, {
+ configurable: property.configurable,
+ enumerable: property.enumerable,
+
+ get: function() {
+ return _value;
+ },
+
+ set: function(value) {
+
+ // return if we already performed all changes
+ if (value === _value) {
+ return;
+ }
+
+ var old = _value;
+
+ // temporary set null
+ _value = null;
+
+ if (old) {
+ ref.unset(old, inverseProperty, target);
+ }
+
+ // set new value
+ _value = value;
+
+ // set inverse value
+ ref.set(_value, inverseProperty, target);
+ }
+ });
+
+ }
+
+ /**
+ * Creates a new references object defining two inversly related
+ * attribute descriptors a and b.
+ *
+ *
+ * When bound to an object using {@link Refs#bind} the references
+ * get activated and ensure that add and remove operations are applied
+ * reversely, too.
+ *
+ *
+ *
+ * For attributes represented as collections {@link Refs} provides the
+ * {@link RefsCollection#add}, {@link RefsCollection#remove} and {@link RefsCollection#contains} extensions
+ * that must be used to properly hook into the inverse change mechanism.
+ *
+ *
+ * @class Refs
+ *
+ * @classdesc A bi-directional reference between two attributes.
+ *
+ * @param {Refs.AttributeDescriptor} a property descriptor
+ * @param {Refs.AttributeDescriptor} b property descriptor
+ *
+ * @example
+ *
+ * var refs = Refs({ name: 'wheels', collection: true, enumerable: true }, { name: 'car' });
+ *
+ * var car = { name: 'toyota' };
+ * var wheels = [{ pos: 'front-left' }, { pos: 'front-right' }];
+ *
+ * refs.bind(car, 'wheels');
+ *
+ * car.wheels // []
+ * car.wheels.add(wheels[0]);
+ * car.wheels.add(wheels[1]);
+ *
+ * car.wheels // [{ pos: 'front-left' }, { pos: 'front-right' }]
+ *
+ * wheels[0].car // { name: 'toyota' };
+ * car.wheels.remove(wheels[0]);
+ *
+ * wheels[0].car // undefined
+ */
+ function Refs(a, b) {
+
+ if (!(this instanceof Refs)) {
+ return new Refs(a, b);
+ }
+
+ // link
+ a.inverse = b;
+ b.inverse = a;
+
+ this.props = {};
+ this.props[a.name] = a;
+ this.props[b.name] = b;
+ }
+
+ /**
+ * Binds one side of a bi-directional reference to a
+ * target object.
+ *
+ * @memberOf Refs
+ *
+ * @param {Object} target
+ * @param {String} property
+ */
+ Refs.prototype.bind = function(target, property) {
+ if (typeof property === 'string') {
+ if (!this.props[property]) {
+ throw new Error('no property <' + property + '> in ref');
+ }
+ property = this.props[property];
+ }
+
+ if (property.collection) {
+ defineCollectionProperty(this, property, target);
+ } else {
+ defineProperty(this, property, target);
+ }
+ };
+
+ Refs.prototype.ensureRefsCollection = function(target, property) {
+
+ var collection$$1 = target[property.name];
+
+ if (!collection.isExtended(collection$$1)) {
+ defineCollectionProperty(this, property, target);
+ }
+
+ return collection$$1;
+ };
+
+ Refs.prototype.ensureBound = function(target, property) {
+ if (!hasOwnProperty(target, property)) {
+ this.bind(target, property);
+ }
+ };
+
+ Refs.prototype.unset = function(target, property, value) {
+
+ if (target) {
+ this.ensureBound(target, property);
+
+ if (property.collection) {
+ this.ensureRefsCollection(target, property).remove(value);
+ } else {
+ target[property.name] = undefined;
+ }
+ }
+ };
+
+ Refs.prototype.set = function(target, property, value) {
+
+ if (target) {
+ this.ensureBound(target, property);
+
+ if (property.collection) {
+ this.ensureRefsCollection(target, property).add(value);
+ } else {
+ target[property.name] = value;
+ }
+ }
+ };
+
+ var refs = Refs;
+
+ var objectRefs = refs;
+
+ var Collection = collection;
+ objectRefs.Collection = Collection;
+
+ var parentRefs = new objectRefs({ name: 'children', enumerable: true, collection: true }, { name: 'parent' }),
+ labelRefs = new objectRefs({ name: 'labels', enumerable: true, collection: true }, { name: 'labelTarget' }),
+ attacherRefs = new objectRefs({ name: 'attachers', collection: true }, { name: 'host' }),
+ outgoingRefs = new objectRefs({ name: 'outgoing', collection: true }, { name: 'source' }),
+ incomingRefs = new objectRefs({ name: 'incoming', collection: true }, { name: 'target' });
+
+ /**
+ * @namespace djs.model
+ */
+
+ /**
+ * @memberOf djs.model
+ */
+
+ /**
+ * The basic graphical representation
+ *
+ * @class
+ *
+ * @abstract
+ */
+ function Base() {
+
+ /**
+ * The object that backs up the shape
+ *
+ * @name Base#businessObject
+ * @type Object
+ */
+ Object.defineProperty(this, 'businessObject', {
+ writable: true
+ });
+
+
+ /**
+ * Single label support, will mapped to multi label array
+ *
+ * @name Base#label
+ * @type Object
+ */
+ Object.defineProperty(this, 'label', {
+ get: function() {
+ return this.labels[0];
+ },
+ set: function(newLabel) {
+
+ var label = this.label,
+ labels = this.labels;
+
+ if (!newLabel && label) {
+ labels.remove(label);
+ } else {
+ labels.add(newLabel, 0);
+ }
+ }
+ });
+
+ /**
+ * The parent shape
+ *
+ * @name Base#parent
+ * @type Shape
+ */
+ parentRefs.bind(this, 'parent');
+
+ /**
+ * The list of labels
+ *
+ * @name Base#labels
+ * @type Label
+ */
+ labelRefs.bind(this, 'labels');
+
+ /**
+ * The list of outgoing connections
+ *
+ * @name Base#outgoing
+ * @type Array
+ */
+ outgoingRefs.bind(this, 'outgoing');
+
+ /**
+ * The list of incoming connections
+ *
+ * @name Base#incoming
+ * @type Array
+ */
+ incomingRefs.bind(this, 'incoming');
+ }
+
+
+ /**
+ * A graphical object
+ *
+ * @class
+ * @constructor
+ *
+ * @extends Base
+ */
+ function Shape() {
+ Base.call(this);
+
+ /**
+ * The list of children
+ *
+ * @name Shape#children
+ * @type Array
+ */
+ parentRefs.bind(this, 'children');
+
+ /**
+ * @name Shape#host
+ * @type Shape
+ */
+ attacherRefs.bind(this, 'host');
+
+ /**
+ * @name Shape#attachers
+ * @type Shape
+ */
+ attacherRefs.bind(this, 'attachers');
+ }
+
+ inherits_browser(Shape, Base);
+
+
+ /**
+ * A root graphical object
+ *
+ * @class
+ * @constructor
+ *
+ * @extends Shape
+ */
+ function Root() {
+ Shape.call(this);
+ }
+
+ inherits_browser(Root, Shape);
+
+
+ /**
+ * A label for an element
+ *
+ * @class
+ * @constructor
+ *
+ * @extends Shape
+ */
+ function Label() {
+ Shape.call(this);
+
+ /**
+ * The labeled element
+ *
+ * @name Label#labelTarget
+ * @type Base
+ */
+ labelRefs.bind(this, 'labelTarget');
+ }
+
+ inherits_browser(Label, Shape);
+
+
+ /**
+ * A connection between two elements
+ *
+ * @class
+ * @constructor
+ *
+ * @extends Base
+ */
+ function Connection() {
+ Base.call(this);
+
+ /**
+ * The element this connection originates from
+ *
+ * @name Connection#source
+ * @type Base
+ */
+ outgoingRefs.bind(this, 'source');
+
+ /**
+ * The element this connection points to
+ *
+ * @name Connection#target
+ * @type Base
+ */
+ incomingRefs.bind(this, 'target');
+ }
+
+ inherits_browser(Connection, Base);
+
+
+ var types = {
+ connection: Connection,
+ shape: Shape,
+ label: Label,
+ root: Root
+ };
+
+ /**
+ * Creates a new model element of the specified type
+ *
+ * @method create
+ *
+ * @example
+ *
+ * var shape1 = Model.create('shape', { x: 10, y: 10, width: 100, height: 100 });
+ * var shape2 = Model.create('shape', { x: 210, y: 210, width: 100, height: 100 });
+ *
+ * var connection = Model.create('connection', { waypoints: [ { x: 110, y: 55 }, {x: 210, y: 55 } ] });
+ *
+ * @param {String} type lower-cased model name
+ * @param {Object} attrs attributes to initialize the new model instance with
+ *
+ * @return {Base} the new model instance
+ */
+ function create$1(type, attrs) {
+ var Type = types[type];
+ if (!Type) {
+ throw new Error('unknown type: <' + type + '>');
+ }
+ return assign(new Type(), attrs);
+ }
+
+ /**
+ * 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 = assign({}, attrs || {});
+
+ if (!attrs.id) {
+ attrs.id = type + '_' + (this._uid++);
+ }
+
+ return create$1(type, attrs);
+ };
+
+ var FN_REF = '__fn';
+
+ var DEFAULT_PRIORITY = 1000;
+
+ var slice$1 = Array.prototype.slice;
+
+ /**
+ * A general purpose event bus.
+ *
+ * This component is used to communicate across a diagram instance.
+ * Other parts of a diagram can use it to listen to and broadcast events.
+ *
+ *
+ * ## Registering for Events
+ *
+ * The event bus provides the {@link EventBus#on} and {@link EventBus#once}
+ * methods to register for events. {@link EventBus#off} can be used to
+ * remove event registrations. Listeners receive an instance of {@link Event}
+ * as the first argument. It allows them to hook into the event execution.
+ *
+ * ```javascript
+ *
+ * // listen for event
+ * eventBus.on('foo', function(event) {
+ *
+ * // access event type
+ * event.type; // 'foo'
+ *
+ * // stop propagation to other listeners
+ * event.stopPropagation();
+ *
+ * // prevent event default
+ * event.preventDefault();
+ * });
+ *
+ * // listen for event with custom payload
+ * eventBus.on('bar', function(event, payload) {
+ * console.log(payload);
+ * });
+ *
+ * // listen for event returning value
+ * eventBus.on('foobar', function(event) {
+ *
+ * // stop event propagation + prevent default
+ * return false;
+ *
+ * // stop event propagation + return custom result
+ * return {
+ * complex: 'listening result'
+ * };
+ * });
+ *
+ *
+ * // listen with custom priority (default=1000, higher is better)
+ * eventBus.on('priorityfoo', 1500, function(event) {
+ * console.log('invoked first!');
+ * });
+ *
+ *
+ * // listen for event and pass the context (`this`)
+ * eventBus.on('foobar', function(event) {
+ * this.foo();
+ * }, this);
+ * ```
+ *
+ *
+ * ## Emitting Events
+ *
+ * Events can be emitted via the event bus using {@link EventBus#fire}.
+ *
+ * ```javascript
+ *
+ * // false indicates that the default action
+ * // was prevented by listeners
+ * if (eventBus.fire('foo') === false) {
+ * console.log('default has been prevented!');
+ * };
+ *
+ *
+ * // custom args + return value listener
+ * eventBus.on('sum', function(event, a, b) {
+ * return a + b;
+ * });
+ *
+ * // you can pass custom arguments + retrieve result values.
+ * var sum = eventBus.fire('sum', 1, 2);
+ * console.log(sum); // 3
+ * ```
+ */
+ function EventBus() {
+ this._listeners = {};
+
+ // cleanup on destroy on lowest priority to allow
+ // message passing until the bitter end
+ this.on('diagram.destroy', 1, this._destroy, this);
+ }
+
+
+ /**
+ * Register an event listener for events with the given name.
+ *
+ * The callback will be invoked with `event, ...additionalArguments`
+ * that have been passed to {@link EventBus#fire}.
+ *
+ * Returning false from a listener will prevent the events default action
+ * (if any is specified). To stop an event from being processed further in
+ * other listeners execute {@link Event#stopPropagation}.
+ *
+ * Returning anything but `undefined` from a listener will stop the listener propagation.
+ *
+ * @param {String|Array} events
+ * @param {Number} [priority=1000] the priority in which this listener is called, larger is higher
+ * @param {Function} callback
+ * @param {Object} [that] Pass context (`this`) to the callback
+ */
+ EventBus.prototype.on = function(events, priority, callback, that) {
+
+ events = isArray(events) ? events : [ events ];
+
+ if (isFunction(priority)) {
+ that = callback;
+ callback = priority;
+ priority = DEFAULT_PRIORITY;
+ }
+
+ if (!isNumber(priority)) {
+ throw new Error('priority must be a number');
+ }
+
+ var actualCallback = callback;
+
+ if (that) {
+ actualCallback = bind(callback, that);
+
+ // make sure we remember and are able to remove
+ // bound callbacks via {@link #off} using the original
+ // callback
+ actualCallback[FN_REF] = callback[FN_REF] || callback;
+ }
+
+ var self = this,
+ listener = { priority: priority, callback: actualCallback };
+
+ events.forEach(function(e) {
+ self._addListener(e, listener);
+ });
+ };
+
+
+ /**
+ * Register an event listener that is executed only once.
+ *
+ * @param {String} event the event name to register for
+ * @param {Function} callback the callback to execute
+ * @param {Object} [that] Pass context (`this`) to the callback
+ */
+ EventBus.prototype.once = function(event, priority, callback, that) {
+ var self = this;
+
+ if (isFunction(priority)) {
+ that = callback;
+ callback = priority;
+ priority = DEFAULT_PRIORITY;
+ }
+
+ if (!isNumber(priority)) {
+ throw new Error('priority must be a number');
+ }
+
+ function wrappedCallback() {
+ self.off(event, wrappedCallback);
+ return callback.apply(that, arguments);
+ }
+
+ // make sure we remember and are able to remove
+ // bound callbacks via {@link #off} using the original
+ // callback
+ wrappedCallback[FN_REF] = callback;
+
+ this.on(event, priority, wrappedCallback);
+ };
+
+
+ /**
+ * Removes event listeners by event and callback.
+ *
+ * If no callback is given, all listeners for a given event name are being removed.
+ *
+ * @param {String} event
+ * @param {Function} [callback]
+ */
+ EventBus.prototype.off = function(event, callback) {
+ var listeners = this._getListeners(event),
+ listener,
+ listenerCallback,
+ idx;
+
+ if (callback) {
+
+ // move through listeners from back to front
+ // and remove matching listeners
+ for (idx = listeners.length - 1; (listener = listeners[idx]); idx--) {
+ listenerCallback = listener.callback;
+
+ if (listenerCallback === callback || listenerCallback[FN_REF] === callback) {
+ listeners.splice(idx, 1);
+ }
+ }
+ } else {
+ // clear listeners
+ listeners.length = 0;
+ }
+ };
+
+
+ /**
+ * Create an EventBus event.
+ *
+ * @param {Object} data
+ *
+ * @return {Object} event, recognized by the eventBus
+ */
+ EventBus.prototype.createEvent = function(data) {
+ var event = new InternalEvent();
+
+ event.init(data);
+
+ return event;
+ };
+
+
+ /**
+ * Fires a named event.
+ *
+ * @example
+ *
+ * // fire event by name
+ * events.fire('foo');
+ *
+ * // fire event object with nested type
+ * var event = { type: 'foo' };
+ * events.fire(event);
+ *
+ * // fire event with explicit type
+ * var event = { x: 10, y: 20 };
+ * events.fire('element.moved', event);
+ *
+ * // pass additional arguments to the event
+ * events.on('foo', function(event, bar) {
+ * alert(bar);
+ * });
+ *
+ * events.fire({ type: 'foo' }, 'I am bar!');
+ *
+ * @param {String} [name] the optional event name
+ * @param {Object} [event] the event object
+ * @param {...Object} additional arguments to be passed to the callback functions
+ *
+ * @return {Boolean} the events return value, if specified or false if the
+ * default action was prevented by listeners
+ */
+ EventBus.prototype.fire = function(type, data) {
+
+ var event,
+ listeners,
+ returnValue,
+ args;
+
+ args = slice$1.call(arguments);
+
+ if (typeof type === 'object') {
+ event = type;
+ type = event.type;
+ }
+
+ if (!type) {
+ throw new Error('no event type specified');
+ }
+
+ listeners = this._listeners[type];
+
+ if (!listeners) {
+ return;
+ }
+
+ // we make sure we fire instances of our home made
+ // events here. We wrap them only once, though
+ if (data instanceof InternalEvent) {
+ // we are fine, we alread have an event
+ event = data;
+ } else {
+ event = this.createEvent(data);
+ }
+
+ // ensure we pass the event as the first parameter
+ args[0] = event;
+
+ // original event type (in case we delegate)
+ var originalType = event.type;
+
+ // update event type before delegation
+ if (type !== originalType) {
+ event.type = type;
+ }
+
+ try {
+ returnValue = this._invokeListeners(event, args, listeners);
+ } finally {
+ // reset event type after delegation
+ if (type !== originalType) {
+ event.type = originalType;
+ }
+ }
+
+ // set the return value to false if the event default
+ // got prevented and no other return value exists
+ if (returnValue === undefined && event.defaultPrevented) {
+ returnValue = false;
+ }
+
+ return returnValue;
+ };
+
+
+ EventBus.prototype.handleError = function(error) {
+ return this.fire('error', { error: error }) === false;
+ };
+
+
+ EventBus.prototype._destroy = function() {
+ this._listeners = {};
+ };
+
+ EventBus.prototype._invokeListeners = function(event, args, listeners) {
+
+ var idx,
+ listener,
+ returnValue;
+
+ for (idx = 0; (listener = listeners[idx]); idx++) {
+
+ // handle stopped propagation
+ if (event.cancelBubble) {
+ break;
+ }
+
+ returnValue = this._invokeListener(event, args, listener);
+ }
+
+ return returnValue;
+ };
+
+ EventBus.prototype._invokeListener = function(event, args, listener) {
+
+ var returnValue;
+
+ try {
+ // returning false prevents the default action
+ returnValue = invokeFunction(listener.callback, args);
+
+ // stop propagation on return value
+ if (returnValue !== undefined) {
+ event.returnValue = returnValue;
+ event.stopPropagation();
+ }
+
+ // prevent default on return false
+ if (returnValue === false) {
+ event.preventDefault();
+ }
+ } catch (e) {
+ if (!this.handleError(e)) {
+ console.error('unhandled error in event listener');
+ console.error(e.stack);
+
+ throw e;
+ }
+ }
+
+ return returnValue;
+ };
+
+ /*
+ * Add new listener with a certain priority to the list
+ * of listeners (for the given event).
+ *
+ * The semantics of listener registration / listener execution are
+ * first register, first serve: New listeners will always be inserted
+ * after existing listeners with the same priority.
+ *
+ * Example: Inserting two listeners with priority 1000 and 1300
+ *
+ * * before: [ 1500, 1500, 1000, 1000 ]
+ * * after: [ 1500, 1500, (new=1300), 1000, 1000, (new=1000) ]
+ *
+ * @param {String} event
+ * @param {Object} listener { priority, callback }
+ */
+ EventBus.prototype._addListener = function(event, newListener) {
+
+ var listeners = this._getListeners(event),
+ existingListener,
+ idx;
+
+ // ensure we order listeners by priority from
+ // 0 (high) to n > 0 (low)
+ for (idx = 0; (existingListener = listeners[idx]); idx++) {
+ if (existingListener.priority < newListener.priority) {
+
+ // prepend newListener at before existingListener
+ listeners.splice(idx, 0, newListener);
+ return;
+ }
+ }
+
+ listeners.push(newListener);
+ };
+
+
+ EventBus.prototype._getListeners = function(name) {
+ var listeners = this._listeners[name];
+
+ if (!listeners) {
+ this._listeners[name] = listeners = [];
+ }
+
+ return listeners;
+ };
+
+
+ /**
+ * A event that is emitted via the event bus.
+ */
+ function InternalEvent() { }
+
+ InternalEvent.prototype.stopPropagation = function() {
+ this.cancelBubble = true;
+ };
+
+ InternalEvent.prototype.preventDefault = function() {
+ this.defaultPrevented = true;
+ };
+
+ InternalEvent.prototype.init = function(data) {
+ assign(this, data || {});
+ };
+
+
+ /**
+ * Invoke function. Be fast...
+ *
+ * @param {Function} fn
+ * @param {Array} args
+ *
+ * @return {Any}
+ */
+ function invokeFunction(fn, args) {
+ return fn.apply(null, args);
+ }
+
+ /**
+ * SVGs for elements are generated by the {@link GraphicsFactory}.
+ *
+ * This utility gives quick access to the important semantic
+ * parts of an element.
+ */
+
+ /**
+ * Returns the visual part of a diagram element
+ *
+ * @param {Snap} gfx
+ *
+ * @return {Snap}
+ */
+ function getVisual(gfx) {
+ return query('.djs-visual', gfx);
+ }
+
+ /**
+ * Returns the children for a given diagram element.
+ *
+ * @param {Snap} gfx
+ * @return {Snap}
+ */
+ function getChildren(gfx) {
+ return gfx.parentNode.childNodes[1];
+ }
+
+ /**
+ * @param {} element
+ * @param {Number} x
+ * @param {Number} y
+ * @param {Number} angle
+ * @param {Number} amount
+ */
+ function transform$1(gfx, x, y, angle, amount) {
+ var translate = createTransform();
+ translate.setTranslate(x, y);
+
+ var rotate = createTransform();
+ rotate.setRotate(angle, 0, 0);
+
+ var scale = createTransform();
+ scale.setScale(amount || 1, amount || 1);
+
+ transform(gfx, [ translate, rotate, scale ]);
+ }
+
+
+ /**
+ * @param {SVGElement} element
+ * @param {Number} x
+ * @param {Number} y
+ */
+ function translate(gfx, x, y) {
+ var translate = createTransform();
+ translate.setTranslate(x, y);
+
+ transform(gfx, translate);
+ }
+
+
+ /**
+ * @param {SVGElement} element
+ * @param {Number} angle
+ */
+ function rotate(gfx, angle) {
+ var rotate = createTransform();
+ rotate.setRotate(angle, 0, 0);
+
+ transform(gfx, rotate);
+ }
+
+ /**
+ * A factory that creates graphical elements
+ *
+ * @param {EventBus} eventBus
+ * @param {ElementRegistry} elementRegistry
+ */
+ function GraphicsFactory(eventBus, elementRegistry) {
+ this._eventBus = eventBus;
+ this._elementRegistry = elementRegistry;
+ }
+
+ GraphicsFactory.$inject = [ 'eventBus' , 'elementRegistry' ];
+
+
+ GraphicsFactory.prototype._getChildren = function(element) {
+
+ var gfx = this._elementRegistry.getGraphics(element);
+
+ var childrenGfx;
+
+ // root element
+ if (!element.parent) {
+ childrenGfx = gfx;
+ } else {
+ childrenGfx = getChildren(gfx);
+ if (!childrenGfx) {
+ childrenGfx = create('g');
+ classes$1(childrenGfx).add('djs-children');
+
+ append(gfx.parentNode, childrenGfx);
+ }
+ }
+
+ return childrenGfx;
+ };
+
+ /**
+ * Clears the graphical representation of the element and returns the
+ * cleared visual (the element).
+ */
+ GraphicsFactory.prototype._clear = function(gfx) {
+ var visual = getVisual(gfx);
+
+ clear(visual);
+
+ return visual;
+ };
+
+ /**
+ * Creates a gfx container for shapes and connections
+ *
+ * The layout is as follows:
+ *
+ *
+ *
+ *
+ *
+ *
+ *
+ *
+ *
+ *
+ *
+ *
+ *
+ * @param {Object} parent
+ * @param {String} type the type of the element, i.e. shape | connection
+ * @param {Number} [parentIndex] position to create container in parent
+ */
+ GraphicsFactory.prototype._createContainer = function(type, childrenGfx, parentIndex) {
+ var outerGfx = create('g');
+ classes$1(outerGfx).add('djs-group');
+
+ // insert node at position
+ if (typeof parentIndex !== 'undefined') {
+ prependTo(outerGfx, childrenGfx, childrenGfx.childNodes[parentIndex]);
+ } else {
+ append(childrenGfx, outerGfx);
+ }
+
+ var gfx = create('g');
+ classes$1(gfx).add('djs-element');
+ classes$1(gfx).add('djs-' + type);
+
+ append(outerGfx, gfx);
+
+ // create visual
+ var visual = create('g');
+ classes$1(visual).add('djs-visual');
+
+ append(gfx, visual);
+
+ return gfx;
+ };
+
+ GraphicsFactory.prototype.create = function(type, element, parentIndex) {
+ var childrenGfx = this._getChildren(element.parent);
+ return this._createContainer(type, childrenGfx, parentIndex);
+ };
+
+ GraphicsFactory.prototype.updateContainments = function(elements) {
+
+ var self = this,
+ elementRegistry = this._elementRegistry,
+ parents;
+
+ parents = reduce(elements, function(map$$1, e) {
+
+ if (e.parent) {
+ map$$1[e.parent.id] = e.parent;
+ }
+
+ return map$$1;
+ }, {});
+
+ // update all parents of changed and reorganized their children
+ // in the correct order (as indicated in our model)
+ forEach(parents, function(parent) {
+
+ var children = parent.children;
+
+ if (!children) {
+ return;
+ }
+
+ var childGfx = self._getChildren(parent);
+
+ forEach(children.slice().reverse(), function(c) {
+ var gfx = elementRegistry.getGraphics(c);
+
+ prependTo(gfx.parentNode, childGfx);
+ });
+ });
+ };
+
+ GraphicsFactory.prototype.drawShape = function(visual, element) {
+ var eventBus = this._eventBus;
+
+ return eventBus.fire('render.shape', { gfx: visual, element: element });
+ };
+
+ GraphicsFactory.prototype.getShapePath = function(element) {
+ var eventBus = this._eventBus;
+
+ return eventBus.fire('render.getShapePath', element);
+ };
+
+ GraphicsFactory.prototype.drawConnection = function(visual, element) {
+ var eventBus = this._eventBus;
+
+ return eventBus.fire('render.connection', { gfx: visual, element: element });
+ };
+
+ GraphicsFactory.prototype.getConnectionPath = function(waypoints) {
+ var eventBus = this._eventBus;
+
+ return eventBus.fire('render.getConnectionPath', waypoints);
+ };
+
+ GraphicsFactory.prototype.update = function(type, element, gfx) {
+ // Do not update root element
+ if (!element.parent) {
+ return;
+ }
+
+ var visual = this._clear(gfx);
+
+ // redraw
+ if (type === 'shape') {
+ this.drawShape(visual, element);
+
+ // update positioning
+ translate(gfx, element.x, element.y);
+ } else
+ if (type === 'connection') {
+ this.drawConnection(visual, element);
+ } else {
+ throw new Error('unknown type: ' + type);
+ }
+
+ if (element.hidden) {
+ attr$1(gfx, 'display', 'none');
+ } else {
+ attr$1(gfx, 'display', 'block');
+ }
+ };
+
+ GraphicsFactory.prototype.remove = function(element) {
+ var gfx = this._elementRegistry.getGraphics(element);
+
+ // remove
+ remove$1(gfx.parentNode);
+ };
+
+
+ // helpers //////////////////////
+
+ function prependTo(newNode, parentNode, siblingNode) {
+ parentNode.insertBefore(newNode, siblingNode || parentNode.firstChild);
+ }
+
+ var CoreModule = {
+ __depends__: [ DrawModule ],
+ __init__: [ 'canvas' ],
+ canvas: [ 'type', Canvas ],
+ elementRegistry: [ 'type', ElementRegistry ],
+ elementFactory: [ 'type', ElementFactory ],
+ eventBus: [ 'type', EventBus ],
+ graphicsFactory: [ 'type', GraphicsFactory ]
+ };
+
+ /**
+ * Bootstrap an injector from a list of modules, instantiating a number of default components
+ *
+ * @ignore
+ * @param {Array} bootstrapModules
+ *
+ * @return {didi.Injector} a injector to use to access the components
+ */
+ function bootstrap(bootstrapModules) {
+
+ var modules = [],
+ components = [];
+
+ function hasModule(m) {
+ return modules.indexOf(m) >= 0;
+ }
+
+ function addModule(m) {
+ modules.push(m);
+ }
+
+ function visit(m) {
+ if (hasModule(m)) {
+ return;
+ }
+
+ (m.__depends__ || []).forEach(visit);
+
+ if (hasModule(m)) {
+ return;
+ }
+
+ addModule(m);
+
+ (m.__init__ || []).forEach(function(c) {
+ components.push(c);
+ });
+ }
+
+ bootstrapModules.forEach(visit);
+
+ var injector = new Injector(modules);
+
+ components.forEach(function(c) {
+
+ try {
+ // eagerly resolve component (fn or string)
+ injector[typeof c === 'string' ? 'get' : 'invoke'](c);
+ } catch (e) {
+ console.error('Failed to instantiate component');
+ console.error(e.stack);
+
+ throw e;
+ }
+ });
+
+ return injector;
+ }
+
+ /**
+ * Creates an injector from passed options.
+ *
+ * @ignore
+ * @param {Object} options
+ * @return {didi.Injector}
+ */
+ function createInjector(options) {
+
+ options = options || {};
+
+ var configModule = {
+ 'config': ['value', options]
+ };
+
+ var modules = [ configModule, CoreModule ].concat(options.modules || []);
+
+ return bootstrap(modules);
+ }
+
+
+ /**
+ * The main diagram-js entry point that bootstraps the diagram with the given
+ * configuration.
+ *
+ * To register extensions with the diagram, pass them as Array to the constructor.
+ *
+ * @class djs.Diagram
+ * @memberOf djs
+ * @constructor
+ *
+ * @example
+ *
+ * Creating a plug-in that logs whenever a shape is added to the canvas.
+ *
+ * // plug-in implemenentation
+ * function MyLoggingPlugin(eventBus) {
+ * eventBus.on('shape.added', function(event) {
+ * console.log('shape ', event.shape, ' was added to the diagram');
+ * });
+ * }
+ *
+ * // export as module
+ * export default {
+ * __init__: [ 'myLoggingPlugin' ],
+ * myLoggingPlugin: [ 'type', MyLoggingPlugin ]
+ * };
+ *
+ *
+ * // instantiate the diagram with the new plug-in
+ *
+ * import MyLoggingModule from 'path-to-my-logging-plugin';
+ *
+ * var diagram = new Diagram({
+ * modules: [
+ * MyLoggingModule
+ * ]
+ * });
+ *
+ * diagram.invoke([ 'canvas', function(canvas) {
+ * // add shape to drawing canvas
+ * canvas.addShape({ x: 10, y: 10 });
+ * });
+ *
+ * // 'shape ... was added to the diagram' logged to console
+ *
+ * @param {Object} options
+ * @param {Array} [options.modules] external modules to instantiate with the diagram
+ * @param {didi.Injector} [injector] an (optional) injector to bootstrap the diagram with
+ */
+ function Diagram(options, injector) {
+
+ // create injector unless explicitly specified
+ this.injector = injector = injector || createInjector(options);
+
+ // API
+
+ /**
+ * Resolves a diagram service
+ *
+ * @method Diagram#get
+ *
+ * @param {String} name the name of the diagram service to be retrieved
+ * @param {Boolean} [strict=true] if false, resolve missing services to null
+ */
+ this.get = injector.get;
+
+ /**
+ * Executes a function into which diagram services are injected
+ *
+ * @method Diagram#invoke
+ *
+ * @param {Function|Object[]} fn the function to resolve
+ * @param {Object} locals a number of locals to use to resolve certain dependencies
+ */
+ this.invoke = injector.invoke;
+
+ // init
+
+ // indicate via event
+
+
+ /**
+ * An event indicating that all plug-ins are loaded.
+ *
+ * Use this event to fire other events to interested plug-ins
+ *
+ * @memberOf Diagram
+ *
+ * @event diagram.init
+ *
+ * @example
+ *
+ * eventBus.on('diagram.init', function() {
+ * eventBus.fire('my-custom-event', { foo: 'BAR' });
+ * });
+ *
+ * @type {Object}
+ */
+ this.get('eventBus').fire('diagram.init');
+ }
+
+
+ /**
+ * Destroys the diagram
+ *
+ * @method Diagram#destroy
+ */
+ Diagram.prototype.destroy = function() {
+ this.get('eventBus').fire('diagram.destroy');
+ };
+
+ /**
+ * Clear the diagram, removing all contents.
+ */
+ Diagram.prototype.clear = function() {
+ this.get('eventBus').fire('diagram.clear');
+ };
+
+ var Diagram$1 = /*#__PURE__*/Object.freeze({
+ default: Diagram
+ });
+
+ var require$$0 = ( Diagram$1 && Diagram ) || Diagram$1;
+
+ var diagramJs = require$$0;
+
+ /**
+ * Moddle base element.
+ */
+ function Base$1() { }
+
+ Base$1.prototype.get = function(name) {
+ return this.$model.properties.get(this, name);
+ };
+
+ Base$1.prototype.set = function(name, value) {
+ this.$model.properties.set(this, name, value);
+ };
+
+ /**
+ * A model element factory.
+ *
+ * @param {Moddle} model
+ * @param {Properties} properties
+ */
+ function Factory(model, properties) {
+ this.model = model;
+ this.properties = properties;
+ }
+
+
+ Factory.prototype.createType = function(descriptor) {
+
+ var model = this.model;
+
+ var props = this.properties,
+ prototype = Object.create(Base$1.prototype);
+
+ // initialize default values
+ forEach(descriptor.properties, function(p) {
+ if (!p.isMany && p.default !== undefined) {
+ prototype[p.name] = p.default;
+ }
+ });
+
+ props.defineModel(prototype, model);
+ props.defineDescriptor(prototype, descriptor);
+
+ var name = descriptor.ns.name;
+
+ /**
+ * The new type constructor
+ */
+ function ModdleElement(attrs) {
+ props.define(this, '$type', { value: name, enumerable: true });
+ props.define(this, '$attrs', { value: {} });
+ props.define(this, '$parent', { writable: true });
+
+ forEach(attrs, bind(function(val, key) {
+ this.set(key, val);
+ }, this));
+ }
+
+ ModdleElement.prototype = prototype;
+
+ ModdleElement.hasType = prototype.$instanceOf = this.model.hasType;
+
+ // static links
+ props.defineModel(ModdleElement, model);
+ props.defineDescriptor(ModdleElement, descriptor);
+
+ return ModdleElement;
+ };
+
+ /**
+ * Built-in moddle types
+ */
+ var BUILTINS = {
+ String: true,
+ Boolean: true,
+ Integer: true,
+ Real: true,
+ Element: true
+ };
+
+ /**
+ * Converters for built in types from string representations
+ */
+ var TYPE_CONVERTERS = {
+ String: function(s) { return s; },
+ Boolean: function(s) { return s === 'true'; },
+ Integer: function(s) { return parseInt(s, 10); },
+ Real: function(s) { return parseFloat(s, 10); }
+ };
+
+ /**
+ * Convert a type to its real representation
+ */
+ function coerceType(type, value) {
+
+ var converter = TYPE_CONVERTERS[type];
+
+ if (converter) {
+ return converter(value);
+ } else {
+ return value;
+ }
+ }
+
+ /**
+ * Return whether the given type is built-in
+ */
+ function isBuiltIn(type) {
+ return !!BUILTINS[type];
+ }
+
+ /**
+ * Return whether the given type is simple
+ */
+ function isSimple(type) {
+ return !!TYPE_CONVERTERS[type];
+ }
+
+ /**
+ * Parses a namespaced attribute name of the form (ns:)localName to an object,
+ * given a default prefix to assume in case no explicit namespace is given.
+ *
+ * @param {String} name
+ * @param {String} [defaultPrefix] the default prefix to take, if none is present.
+ *
+ * @return {Object} the parsed name
+ */
+ function parseName(name, defaultPrefix) {
+ var parts = name.split(/:/),
+ localName, prefix;
+
+ // no prefix (i.e. only local name)
+ if (parts.length === 1) {
+ localName = name;
+ prefix = defaultPrefix;
+ } else
+ // prefix + local name
+ if (parts.length === 2) {
+ localName = parts[1];
+ prefix = parts[0];
+ } else {
+ throw new Error('expected or , got ' + name);
+ }
+
+ name = (prefix ? prefix + ':' : '') + localName;
+
+ return {
+ name: name,
+ prefix: prefix,
+ localName: localName
+ };
+ }
+
+ /**
+ * A utility to build element descriptors.
+ */
+ function DescriptorBuilder(nameNs) {
+ this.ns = nameNs;
+ this.name = nameNs.name;
+ this.allTypes = [];
+ this.allTypesByName = {};
+ this.properties = [];
+ this.propertiesByName = {};
+ }
+
+
+ DescriptorBuilder.prototype.build = function() {
+ return pick(this, [
+ 'ns',
+ 'name',
+ 'allTypes',
+ 'allTypesByName',
+ 'properties',
+ 'propertiesByName',
+ 'bodyProperty',
+ 'idProperty'
+ ]);
+ };
+
+ /**
+ * Add property at given index.
+ *
+ * @param {Object} p
+ * @param {Number} [idx]
+ * @param {Boolean} [validate=true]
+ */
+ DescriptorBuilder.prototype.addProperty = function(p, idx, validate) {
+
+ if (typeof idx === 'boolean') {
+ validate = idx;
+ idx = undefined;
+ }
+
+ this.addNamedProperty(p, validate !== false);
+
+ var properties = this.properties;
+
+ if (idx !== undefined) {
+ properties.splice(idx, 0, p);
+ } else {
+ properties.push(p);
+ }
+ };
+
+
+ DescriptorBuilder.prototype.replaceProperty = function(oldProperty, newProperty, replace) {
+ var oldNameNs = oldProperty.ns;
+
+ var props = this.properties,
+ propertiesByName = this.propertiesByName,
+ rename = oldProperty.name !== newProperty.name;
+
+ if (oldProperty.isId) {
+ if (!newProperty.isId) {
+ throw new Error(
+ 'property <' + newProperty.ns.name + '> must be id property ' +
+ 'to refine <' + oldProperty.ns.name + '>');
+ }
+
+ this.setIdProperty(newProperty, false);
+ }
+
+ if (oldProperty.isBody) {
+
+ if (!newProperty.isBody) {
+ throw new Error(
+ 'property <' + newProperty.ns.name + '> must be body property ' +
+ 'to refine <' + oldProperty.ns.name + '>');
+ }
+
+ // TODO: Check compatibility
+ this.setBodyProperty(newProperty, false);
+ }
+
+ // validate existence and get location of old property
+ var idx = props.indexOf(oldProperty);
+ if (idx === -1) {
+ throw new Error('property <' + oldNameNs.name + '> not found in property list');
+ }
+
+ // remove old property
+ props.splice(idx, 1);
+
+ // replacing the named property is intentional
+ //
+ // * validate only if this is a "rename" operation
+ // * add at specific index unless we "replace"
+ //
+ this.addProperty(newProperty, replace ? undefined : idx, rename);
+
+ // make new property available under old name
+ propertiesByName[oldNameNs.name] = propertiesByName[oldNameNs.localName] = newProperty;
+ };
+
+
+ DescriptorBuilder.prototype.redefineProperty = function(p, targetPropertyName, replace) {
+
+ var nsPrefix = p.ns.prefix;
+ var parts = targetPropertyName.split('#');
+
+ var name = parseName(parts[0], nsPrefix);
+ var attrName = parseName(parts[1], name.prefix).name;
+
+ var redefinedProperty = this.propertiesByName[attrName];
+ if (!redefinedProperty) {
+ throw new Error('refined property <' + attrName + '> not found');
+ } else {
+ this.replaceProperty(redefinedProperty, p, replace);
+ }
+
+ delete p.redefines;
+ };
+
+ DescriptorBuilder.prototype.addNamedProperty = function(p, validate) {
+ var ns = p.ns,
+ propsByName = this.propertiesByName;
+
+ if (validate) {
+ this.assertNotDefined(p, ns.name);
+ this.assertNotDefined(p, ns.localName);
+ }
+
+ propsByName[ns.name] = propsByName[ns.localName] = p;
+ };
+
+ DescriptorBuilder.prototype.removeNamedProperty = function(p) {
+ var ns = p.ns,
+ propsByName = this.propertiesByName;
+
+ delete propsByName[ns.name];
+ delete propsByName[ns.localName];
+ };
+
+ DescriptorBuilder.prototype.setBodyProperty = function(p, validate) {
+
+ if (validate && this.bodyProperty) {
+ throw new Error(
+ 'body property defined multiple times ' +
+ '(<' + this.bodyProperty.ns.name + '>, <' + p.ns.name + '>)');
+ }
+
+ this.bodyProperty = p;
+ };
+
+ DescriptorBuilder.prototype.setIdProperty = function(p, validate) {
+
+ if (validate && this.idProperty) {
+ throw new Error(
+ 'id property defined multiple times ' +
+ '(<' + this.idProperty.ns.name + '>, <' + p.ns.name + '>)');
+ }
+
+ this.idProperty = p;
+ };
+
+ DescriptorBuilder.prototype.assertNotDefined = function(p, name) {
+ var propertyName = p.name,
+ definedProperty = this.propertiesByName[propertyName];
+
+ if (definedProperty) {
+ throw new Error(
+ 'property <' + propertyName + '> already defined; ' +
+ 'override of <' + definedProperty.definedBy.ns.name + '#' + definedProperty.ns.name + '> by ' +
+ '<' + p.definedBy.ns.name + '#' + p.ns.name + '> not allowed without redefines');
+ }
+ };
+
+ DescriptorBuilder.prototype.hasProperty = function(name) {
+ return this.propertiesByName[name];
+ };
+
+ DescriptorBuilder.prototype.addTrait = function(t, inherited) {
+
+ var typesByName = this.allTypesByName,
+ types = this.allTypes;
+
+ var typeName = t.name;
+
+ if (typeName in typesByName) {
+ return;
+ }
+
+ forEach(t.properties, bind(function(p) {
+
+ // clone property to allow extensions
+ p = assign({}, p, {
+ name: p.ns.localName,
+ inherited: inherited
+ });
+
+ Object.defineProperty(p, 'definedBy', {
+ value: t
+ });
+
+ var replaces = p.replaces,
+ redefines = p.redefines;
+
+ // add replace/redefine support
+ if (replaces || redefines) {
+ this.redefineProperty(p, replaces || redefines, replaces);
+ } else {
+ if (p.isBody) {
+ this.setBodyProperty(p);
+ }
+ if (p.isId) {
+ this.setIdProperty(p);
+ }
+ this.addProperty(p);
+ }
+ }, this));
+
+ types.push(t);
+ typesByName[typeName] = t;
+ };
+
+ /**
+ * A registry of Moddle packages.
+ *
+ * @param {Array} packages
+ * @param {Properties} properties
+ */
+ function Registry(packages, properties) {
+ this.packageMap = {};
+ this.typeMap = {};
+
+ this.packages = [];
+
+ this.properties = properties;
+
+ forEach(packages, bind(this.registerPackage, this));
+ }
+
+
+ Registry.prototype.getPackage = function(uriOrPrefix) {
+ return this.packageMap[uriOrPrefix];
+ };
+
+ Registry.prototype.getPackages = function() {
+ return this.packages;
+ };
+
+
+ Registry.prototype.registerPackage = function(pkg) {
+
+ // copy package
+ pkg = assign({}, pkg);
+
+ var pkgMap = this.packageMap;
+
+ ensureAvailable(pkgMap, pkg, 'prefix');
+ ensureAvailable(pkgMap, pkg, 'uri');
+
+ // register types
+ forEach(pkg.types, bind(function(descriptor) {
+ this.registerType(descriptor, pkg);
+ }, this));
+
+ pkgMap[pkg.uri] = pkgMap[pkg.prefix] = pkg;
+ this.packages.push(pkg);
+ };
+
+
+ /**
+ * Register a type from a specific package with us
+ */
+ Registry.prototype.registerType = function(type, pkg) {
+
+ type = assign({}, type, {
+ superClass: (type.superClass || []).slice(),
+ extends: (type.extends || []).slice(),
+ properties: (type.properties || []).slice(),
+ meta: assign((type.meta || {}))
+ });
+
+ var ns = parseName(type.name, pkg.prefix),
+ name = ns.name,
+ propertiesByName = {};
+
+ // parse properties
+ forEach(type.properties, bind(function(p) {
+
+ // namespace property names
+ var propertyNs = parseName(p.name, ns.prefix),
+ propertyName = propertyNs.name;
+
+ // namespace property types
+ if (!isBuiltIn(p.type)) {
+ p.type = parseName(p.type, propertyNs.prefix).name;
+ }
+
+ assign(p, {
+ ns: propertyNs,
+ name: propertyName
+ });
+
+ propertiesByName[propertyName] = p;
+ }, this));
+
+ // update ns + name
+ assign(type, {
+ ns: ns,
+ name: name,
+ propertiesByName: propertiesByName
+ });
+
+ forEach(type.extends, bind(function(extendsName) {
+ var extended = this.typeMap[extendsName];
+
+ extended.traits = extended.traits || [];
+ extended.traits.push(name);
+ }, this));
+
+ // link to package
+ this.definePackage(type, pkg);
+
+ // register
+ this.typeMap[name] = type;
+ };
+
+
+ /**
+ * Traverse the type hierarchy from bottom to top,
+ * calling iterator with (type, inherited) for all elements in
+ * the inheritance chain.
+ *
+ * @param {Object} nsName
+ * @param {Function} iterator
+ * @param {Boolean} [trait=false]
+ */
+ Registry.prototype.mapTypes = function(nsName, iterator, trait) {
+
+ var type = isBuiltIn(nsName.name) ? { name: nsName.name } : this.typeMap[nsName.name];
+
+ var self = this;
+
+ /**
+ * Traverse the selected trait.
+ *
+ * @param {String} cls
+ */
+ function traverseTrait(cls) {
+ return traverseSuper(cls, true);
+ }
+
+ /**
+ * Traverse the selected super type or trait
+ *
+ * @param {String} cls
+ * @param {Boolean} [trait=false]
+ */
+ function traverseSuper(cls, trait) {
+ var parentNs = parseName(cls, isBuiltIn(cls) ? '' : nsName.prefix);
+ self.mapTypes(parentNs, iterator, trait);
+ }
+
+ if (!type) {
+ throw new Error('unknown type <' + nsName.name + '>');
+ }
+
+ forEach(type.superClass, trait ? traverseTrait : traverseSuper);
+
+ // call iterator with (type, inherited=!trait)
+ iterator(type, !trait);
+
+ forEach(type.traits, traverseTrait);
+ };
+
+
+ /**
+ * Returns the effective descriptor for a type.
+ *
+ * @param {String} type the namespaced name (ns:localName) of the type
+ *
+ * @return {Descriptor} the resulting effective descriptor
+ */
+ Registry.prototype.getEffectiveDescriptor = function(name) {
+
+ var nsName = parseName(name);
+
+ var builder = new DescriptorBuilder(nsName);
+
+ this.mapTypes(nsName, function(type, inherited) {
+ builder.addTrait(type, inherited);
+ });
+
+ var descriptor = builder.build();
+
+ // define package link
+ this.definePackage(descriptor, descriptor.allTypes[descriptor.allTypes.length - 1].$pkg);
+
+ return descriptor;
+ };
+
+
+ Registry.prototype.definePackage = function(target, pkg) {
+ this.properties.define(target, '$pkg', { value: pkg });
+ };
+
+
+
+ ///////// helpers ////////////////////////////
+
+ function ensureAvailable(packageMap, pkg, identifierKey) {
+
+ var value = pkg[identifierKey];
+
+ if (value in packageMap) {
+ throw new Error('package with ' + identifierKey + ' <' + value + '> already defined');
+ }
+ }
+
+ /**
+ * A utility that gets and sets properties of model elements.
+ *
+ * @param {Model} model
+ */
+ function Properties(model) {
+ this.model = model;
+ }
+
+
+ /**
+ * Sets a named property on the target element.
+ * If the value is undefined, the property gets deleted.
+ *
+ * @param {Object} target
+ * @param {String} name
+ * @param {Object} value
+ */
+ Properties.prototype.set = function(target, name, value) {
+
+ var property = this.model.getPropertyDescriptor(target, name);
+
+ var propertyName = property && property.name;
+
+ if (isUndefined$1(value)) {
+ // unset the property, if the specified value is undefined;
+ // delete from $attrs (for extensions) or the target itself
+ if (property) {
+ delete target[propertyName];
+ } else {
+ delete target.$attrs[name];
+ }
+ } else {
+ // set the property, defining well defined properties on the fly
+ // or simply updating them in target.$attrs (for extensions)
+ if (property) {
+ if (propertyName in target) {
+ target[propertyName] = value;
+ } else {
+ defineProperty$1(target, property, value);
+ }
+ } else {
+ target.$attrs[name] = value;
+ }
+ }
+ };
+
+ /**
+ * Returns the named property of the given element
+ *
+ * @param {Object} target
+ * @param {String} name
+ *
+ * @return {Object}
+ */
+ Properties.prototype.get = function(target, name) {
+
+ var property = this.model.getPropertyDescriptor(target, name);
+
+ if (!property) {
+ return target.$attrs[name];
+ }
+
+ var propertyName = property.name;
+
+ // check if access to collection property and lazily initialize it
+ if (!target[propertyName] && property.isMany) {
+ defineProperty$1(target, property, []);
+ }
+
+ return target[propertyName];
+ };
+
+
+ /**
+ * Define a property on the target element
+ *
+ * @param {Object} target
+ * @param {String} name
+ * @param {Object} options
+ */
+ Properties.prototype.define = function(target, name, options) {
+ Object.defineProperty(target, name, options);
+ };
+
+
+ /**
+ * Define the descriptor for an element
+ */
+ Properties.prototype.defineDescriptor = function(target, descriptor) {
+ this.define(target, '$descriptor', { value: descriptor });
+ };
+
+ /**
+ * Define the model for an element
+ */
+ Properties.prototype.defineModel = function(target, model) {
+ this.define(target, '$model', { value: model });
+ };
+
+
+ function isUndefined$1(val) {
+ return typeof val === 'undefined';
+ }
+
+ function defineProperty$1(target, property, value) {
+ Object.defineProperty(target, property.name, {
+ enumerable: !property.isReference,
+ writable: true,
+ value: value,
+ configurable: true
+ });
+ }
+
+ //// Moddle implementation /////////////////////////////////////////////////
+
+ /**
+ * @class Moddle
+ *
+ * A model that can be used to create elements of a specific type.
+ *
+ * @example
+ *
+ * var Moddle = require('moddle');
+ *
+ * var pkg = {
+ * name: 'mypackage',
+ * prefix: 'my',
+ * types: [
+ * { name: 'Root' }
+ * ]
+ * };
+ *
+ * var moddle = new Moddle([pkg]);
+ *
+ * @param {Array} packages the packages to contain
+ */
+ function Moddle(packages) {
+
+ this.properties = new Properties(this);
+
+ this.factory = new Factory(this, this.properties);
+ this.registry = new Registry(packages, this.properties);
+
+ this.typeCache = {};
+ }
+
+
+ /**
+ * Create an instance of the specified type.
+ *
+ * @method Moddle#create
+ *
+ * @example
+ *
+ * var foo = moddle.create('my:Foo');
+ * var bar = moddle.create('my:Bar', { id: 'BAR_1' });
+ *
+ * @param {String|Object} descriptor the type descriptor or name know to the model
+ * @param {Object} attrs a number of attributes to initialize the model instance with
+ * @return {Object} model instance
+ */
+ Moddle.prototype.create = function(descriptor, attrs) {
+ var Type = this.getType(descriptor);
+
+ if (!Type) {
+ throw new Error('unknown type <' + descriptor + '>');
+ }
+
+ return new Type(attrs);
+ };
+
+
+ /**
+ * Returns the type representing a given descriptor
+ *
+ * @method Moddle#getType
+ *
+ * @example
+ *
+ * var Foo = moddle.getType('my:Foo');
+ * var foo = new Foo({ 'id' : 'FOO_1' });
+ *
+ * @param {String|Object} descriptor the type descriptor or name know to the model
+ * @return {Object} the type representing the descriptor
+ */
+ Moddle.prototype.getType = function(descriptor) {
+
+ var cache = this.typeCache;
+
+ var name = isString(descriptor) ? descriptor : descriptor.ns.name;
+
+ var type = cache[name];
+
+ if (!type) {
+ descriptor = this.registry.getEffectiveDescriptor(name);
+ type = cache[name] = this.factory.createType(descriptor);
+ }
+
+ return type;
+ };
+
+
+ /**
+ * Creates an any-element type to be used within model instances.
+ *
+ * This can be used to create custom elements that lie outside the meta-model.
+ * The created element contains all the meta-data required to serialize it
+ * as part of meta-model elements.
+ *
+ * @method Moddle#createAny
+ *
+ * @example
+ *
+ * var foo = moddle.createAny('vendor:Foo', 'http://vendor', {
+ * value: 'bar'
+ * });
+ *
+ * var container = moddle.create('my:Container', 'http://my', {
+ * any: [ foo ]
+ * });
+ *
+ * // go ahead and serialize the stuff
+ *
+ *
+ * @param {String} name the name of the element
+ * @param {String} nsUri the namespace uri of the element
+ * @param {Object} [properties] a map of properties to initialize the instance with
+ * @return {Object} the any type instance
+ */
+ Moddle.prototype.createAny = function(name, nsUri, properties) {
+
+ var nameNs = parseName(name);
+
+ var element = {
+ $type: name,
+ $instanceOf: function(type) {
+ return type === this.$type;
+ }
+ };
+
+ var descriptor = {
+ name: name,
+ isGeneric: true,
+ ns: {
+ prefix: nameNs.prefix,
+ localName: nameNs.localName,
+ uri: nsUri
+ }
+ };
+
+ this.properties.defineDescriptor(element, descriptor);
+ this.properties.defineModel(element, this);
+ this.properties.define(element, '$parent', { enumerable: false, writable: true });
+
+ forEach(properties, function(a, key) {
+ if (isObject(a) && a.value !== undefined) {
+ element[a.name] = a.value;
+ } else {
+ element[key] = a;
+ }
+ });
+
+ return element;
+ };
+
+ /**
+ * Returns a registered package by uri or prefix
+ *
+ * @return {Object} the package
+ */
+ Moddle.prototype.getPackage = function(uriOrPrefix) {
+ return this.registry.getPackage(uriOrPrefix);
+ };
+
+ /**
+ * Returns a snapshot of all known packages
+ *
+ * @return {Object} the package
+ */
+ Moddle.prototype.getPackages = function() {
+ return this.registry.getPackages();
+ };
+
+ /**
+ * Returns the descriptor for an element
+ */
+ Moddle.prototype.getElementDescriptor = function(element) {
+ return element.$descriptor;
+ };
+
+ /**
+ * Returns true if the given descriptor or instance
+ * represents the given type.
+ *
+ * May be applied to this, if element is omitted.
+ */
+ Moddle.prototype.hasType = function(element, type) {
+ if (type === undefined) {
+ type = element;
+ element = this;
+ }
+
+ var descriptor = element.$model.getElementDescriptor(element);
+
+ return (type in descriptor.allTypesByName);
+ };
+
+ /**
+ * Returns the descriptor of an elements named property
+ */
+ Moddle.prototype.getPropertyDescriptor = function(element, property) {
+ return this.getElementDescriptor(element).propertiesByName[property];
+ };
+
+ /**
+ * Returns a mapped type's descriptor
+ */
+ Moddle.prototype.getTypeDescriptor = function(type) {
+ return this.registry.typeMap[type];
+ };
+
+ var tinyStack = createCommonjsModule(function (module, exports) {
+ /**
+ * Tiny stack for browser or server
+ *
+ * @copyright 2015 Jason Mulligan
+ * @license BSD-3-Clause
+ * @version 1.0.0
+ */
+
+ ( function ( global ) {
+
+ /**
+ * TinyStack
+ *
+ * @constructor
+ */
+ function TinyStack () {
+ this.data = [null];
+ this.top = 0;
+ }
+
+ /**
+ * Clears the stack
+ *
+ * @method clear
+ * @memberOf TinyStack
+ * @return {Object} {@link TinyStack}
+ */
+ TinyStack.prototype.clear = function clear () {
+ this.data = [null];
+ this.top = 0;
+
+ return this;
+ };
+
+ /**
+ * Gets the size of the stack
+ *
+ * @method length
+ * @memberOf TinyStack
+ * @return {Number} Size of stack
+ */
+ TinyStack.prototype.length = function length () {
+ return this.top;
+ };
+
+ /**
+ * Tests if this stack is empty
+ *
+ * @method empty
+ * @memberOf TinyStack
+ * @return {Boolean} Stack is empty
+ */
+ TinyStack.prototype.empty = function empty () {
+ return this.top === 0;
+ };
+
+ /**
+ * Gets the item at the top of the stack
+ *
+ * @method peek
+ * @memberOf TinyStack
+ * @return {Mixed} Item at the top of the stack
+ */
+ TinyStack.prototype.peek = function peek () {
+ return this.data[this.top];
+ };
+
+ /**
+ * Gets & removes the item at the top of the stack
+ *
+ * @method pop
+ * @memberOf TinyStack
+ * @return {Mixed} Item at the top of the stack
+ */
+ TinyStack.prototype.pop = function pop () {
+ if ( this.top > 0 ) {
+ this.top--;
+
+ return this.data.pop();
+ }
+ else {
+ return undefined;
+ }
+ };
+
+ /**
+ * Pushes an item onto the stack
+ *
+ * @method push
+ * @memberOf TinyStack
+ * @return {Object} {@link TinyStack}
+ */
+ TinyStack.prototype.push = function push ( arg ) {
+ this.data[++this.top] = arg;
+
+ return this;
+ };
+
+ /**
+ * Returns the 1-based position where an object is on this stack
+ *
+ * If the object o occurs as an item in this stack, this method returns the
+ * distance from the top of the stack of the occurrence nearest the top of
+ * the stack; the topmost item on the stack is considered to be at distance 1.
+ * The equals method is used to compare o to the items in this stack.
+ *
+ * @method push
+ * @memberOf TinyStack
+ * @return {Number} 1-based position
+ */
+ TinyStack.prototype.search = function search ( arg ) {
+ var index = this.data.indexOf(arg);
+
+ return index === -1 ? -1 : this.data.length - index;
+ };
+
+ /**
+ * TinyStack factory
+ *
+ * @method factory
+ * @return {Object} {@link TinyStack}
+ */
+ function factory () {
+ return new TinyStack();
+ }
+
+ // Node, AMD & window supported
+ {
+ module.exports = factory;
+ }
+ } )( commonjsGlobal );
+ });
+
+ var fromCharCode = String.fromCharCode;
+
+ var hasOwnProperty$1 = Object.prototype.hasOwnProperty;
+
+ var ENTITY_PATTERN = /(\d+);|([0-9a-f]+);|&(\w+);/ig;
+
+ var ENTITY_MAPPING = {
+ 'amp': '&',
+ 'apos': '\'',
+ 'gt': '>',
+ 'lt': '<',
+ 'quot': '"'
+ };
+
+ // map UPPERCASE variants of supported special chars
+ Object.keys(ENTITY_MAPPING).forEach(function(k) {
+ ENTITY_MAPPING[k.toUpperCase()] = ENTITY_MAPPING[k];
+ });
+
+
+ function replaceEntities(_, d, x, z) {
+
+ // reserved names, i.e.
+ if (z) {
+ if (hasOwnProperty$1.call(ENTITY_MAPPING, z)) {
+ return ENTITY_MAPPING[z];
+ } else {
+ // fall back to original value
+ return '&' + z + ';';
+ }
+ }
+
+ // decimal encoded char
+ if (d) {
+ return fromCharCode(d);
+ }
+
+ // hex encoded char
+ return fromCharCode(parseInt(x, 16));
+ }
+
+
+ /**
+ * A basic entity decoder that can decode a minimal
+ * sub-set of reserved names (&) as well as
+ * hex (ય) and decimal (ӏ) encoded characters.
+ *
+ * @param {string} str
+ *
+ * @return {string} decoded string
+ */
+ function decodeEntities(s) {
+ if (s.length > 3 && s.indexOf('&') !== -1) {
+ return s.replace(ENTITY_PATTERN, replaceEntities);
+ }
+
+ return s;
+ }
+
+ var XSI_URI = 'http://www.w3.org/2001/XMLSchema-instance';
+ var XSI_PREFIX = 'xsi';
+ var XSI_TYPE = 'xsi:type';
+
+ function error(msg) {
+ return new Error(msg);
+ }
+
+ function missingNamespaceForPrefix(prefix) {
+ return 'missing namespace for prefix <' + prefix + '>';
+ }
+
+ function getter(getFn) {
+ return {
+ 'get': getFn,
+ 'enumerable': true
+ };
+ }
+
+ function cloneNsMatrix(nsMatrix) {
+ var clone = {}, key;
+ for (key in nsMatrix) {
+ clone[key] = nsMatrix[key];
+ }
+ return clone;
+ }
+
+ function uriPrefix(prefix) {
+ return prefix + '$uri';
+ }
+
+ function buildNsMatrix(nsUriToPrefix) {
+ var nsMatrix = {},
+ uri,
+ prefix;
+
+ for (uri in nsUriToPrefix) {
+ prefix = nsUriToPrefix[uri];
+ nsMatrix[prefix] = prefix;
+ nsMatrix[uriPrefix(prefix)] = uri;
+ }
+
+ return nsMatrix;
+ }
+
+ function noopGetContext() {
+ return { 'line': 0, 'column': 0 };
+ }
+
+ function throwFunc(err) {
+ throw err;
+ }
+
+ /**
+ * Creates a new parser with the given options.
+ *
+ * @constructor
+ *
+ * @param {!Object=} options
+ */
+ function Parser(options) {
+
+ if (!this) {
+ return new Parser(options);
+ }
+
+ var proxy = options && options['proxy'];
+
+ var onText,
+ onOpenTag,
+ onCloseTag,
+ onCDATA,
+ onError = throwFunc,
+ onWarning,
+ onComment,
+ onQuestion,
+ onAttention;
+
+ var getContext = noopGetContext;
+
+ /**
+ * Do we need to parse the current elements attributes for namespaces?
+ *
+ * @type {boolean}
+ */
+ var maybeNS = false;
+
+ /**
+ * Do we process namespaces at all?
+ *
+ * @type {boolean}
+ */
+ var isNamespace = false;
+
+ /**
+ * The caught error returned on parse end
+ *
+ * @type {Error}
+ */
+ var returnError = null;
+
+ /**
+ * Should we stop parsing?
+ *
+ * @type {boolean}
+ */
+ var parseStop = false;
+
+ /**
+ * A map of { uri: prefix } used by the parser.
+ *
+ * This map will ensure we can normalize prefixes during processing;
+ * for each uri, only one prefix will be exposed to the handlers.
+ *
+ * @type {!Object}}
+ */
+ var nsUriToPrefix;
+
+ /**
+ * Handle parse error.
+ *
+ * @param {string|Error} err
+ */
+ function handleError(err) {
+ if (!(err instanceof Error)) {
+ err = error(err);
+ }
+
+ returnError = err;
+
+ onError(err, getContext);
+ }
+
+ /**
+ * Handle parse error.
+ *
+ * @param {string|Error} err
+ */
+ function handleWarning(err) {
+
+ if (!onWarning) {
+ return;
+ }
+
+ if (!(err instanceof Error)) {
+ err = error(err);
+ }
+
+ onWarning(err, getContext);
+ }
+
+ /**
+ * Register parse listener.
+ *
+ * @param {string} name
+ * @param {Function} cb
+ *
+ * @return {Parser}
+ */
+ this['on'] = function(name, cb) {
+
+ if (typeof cb !== 'function') {
+ throw error('required args ');
+ }
+
+ switch (name) {
+ case 'openTag': onOpenTag = cb; break;
+ case 'text': onText = cb; break;
+ case 'closeTag': onCloseTag = cb; break;
+ case 'error': onError = cb; break;
+ case 'warn': onWarning = cb; break;
+ case 'cdata': onCDATA = cb; break;
+ case 'attention': onAttention = cb; break; //
+ case 'question': onQuestion = cb; break; // .... ?>
+ case 'comment': onComment = cb; break;
+ default:
+ throw error('unsupported event: ' + name);
+ }
+
+ return this;
+ };
+
+ /**
+ * Set the namespace to prefix mapping.
+ *
+ * @example
+ *
+ * parser.ns({
+ * 'http://foo': 'foo',
+ * 'http://bar': 'bar'
+ * });
+ *
+ * @param {!Object} nsMap
+ *
+ * @return {Parser}
+ */
+ this['ns'] = function(nsMap) {
+
+ if (typeof nsMap === 'undefined') {
+ nsMap = {};
+ }
+
+ if (typeof nsMap !== 'object') {
+ throw error('required args ');
+ }
+
+ var _nsUriToPrefix = {}, k;
+
+ for (k in nsMap) {
+ _nsUriToPrefix[k] = nsMap[k];
+ }
+
+ // FORCE default mapping for schema instance
+ _nsUriToPrefix[XSI_URI] = XSI_PREFIX;
+
+ isNamespace = true;
+ nsUriToPrefix = _nsUriToPrefix;
+
+ return this;
+ };
+
+ /**
+ * Parse xml string.
+ *
+ * @param {string} xml
+ *
+ * @return {Error} returnError, if not thrown
+ */
+ this['parse'] = function(xml) {
+ if (typeof xml !== 'string') {
+ throw error('required args ');
+ }
+
+ returnError = null;
+
+ parse(xml);
+
+ getContext = noopGetContext;
+ parseStop = false;
+
+ return returnError;
+ };
+
+ /**
+ * Stop parsing.
+ */
+ this['stop'] = function() {
+ parseStop = true;
+ };
+
+ /**
+ * Parse string, invoking configured listeners on element.
+ *
+ * @param {string} xml
+ */
+ function parse(xml) {
+ var nsMatrixStack = isNamespace ? [] : null,
+ nsMatrix = isNamespace ? buildNsMatrix(nsUriToPrefix) : null,
+ _nsMatrix,
+ nodeStack = [],
+ anonymousNsCount = 0,
+ tagStart = false,
+ tagEnd = false,
+ i = 0, j = 0,
+ x, y, q, w,
+ xmlns,
+ elementName,
+ _elementName,
+ elementProxy
+ ;
+
+ var attrsString = '',
+ attrsStart = 0,
+ cachedAttrs // false = parsed with errors, null = needs parsing
+ ;
+
+ /**
+ * Parse attributes on demand and returns the parsed attributes.
+ *
+ * Return semantics: (1) `false` on attribute parse error,
+ * (2) object hash on extracted attrs.
+ *
+ * @return {boolean|Object}
+ */
+ function getAttrs() {
+ if (cachedAttrs !== null) {
+ return cachedAttrs;
+ }
+
+ var nsUri,
+ nsUriPrefix,
+ nsName,
+ defaultAlias = isNamespace && nsMatrix['xmlns'],
+ attrList = isNamespace && maybeNS ? [] : null,
+ i = attrsStart,
+ s = attrsString,
+ l = s.length,
+ hasNewMatrix,
+ newalias,
+ value,
+ alias,
+ name,
+ attrs = {},
+ seenAttrs = {},
+ skipAttr,
+ w,
+ j;
+
+ parseAttr:
+ for (; i < l; i++) {
+ skipAttr = false;
+ w = s.charCodeAt(i);
+
+ if (w === 32 || (w < 14 && w > 8)) { // WHITESPACE={ \f\n\r\t\v}
+ continue;
+ }
+
+ // wait for non whitespace character
+ if (w < 65 || w > 122 || (w > 90 && w < 97)) {
+ if (w !== 95 && w !== 58) { // char 95"_" 58":"
+ handleWarning('illegal first char attribute name');
+ skipAttr = true;
+ }
+ }
+
+ // parse attribute name
+ for (j = i + 1; j < l; j++) {
+ w = s.charCodeAt(j);
+
+ if (
+ w > 96 && w < 123 ||
+ w > 64 && w < 91 ||
+ w > 47 && w < 59 ||
+ w === 46 || // '.'
+ w === 45 || // '-'
+ w === 95 // '_'
+ ) {
+ continue;
+ }
+
+ // unexpected whitespace
+ if (w === 32 || (w < 14 && w > 8)) { // WHITESPACE
+ handleWarning('missing attribute value');
+ i = j;
+
+ continue parseAttr;
+ }
+
+ // expected "="
+ if (w === 61) { // "=" == 61
+ break;
+ }
+
+ handleWarning('illegal attribute name char');
+ skipAttr = true;
+ }
+
+ name = s.substring(i, j);
+
+ if (name === 'xmlns:xmlns') {
+ handleWarning('illegal declaration of xmlns');
+ skipAttr = true;
+ }
+
+ w = s.charCodeAt(j + 1);
+
+ if (w === 34) { // '"'
+ j = s.indexOf('"', i = j + 2);
+
+ if (j === -1) {
+ j = s.indexOf('\'', i);
+
+ if (j !== -1) {
+ handleWarning('attribute value quote missmatch');
+ skipAttr = true;
+ }
+ }
+
+ } else if (w === 39) { // "'"
+ j = s.indexOf('\'', i = j + 2);
+
+ if (j === -1) {
+ j = s.indexOf('"', i);
+
+ if (j !== -1) {
+ handleWarning('attribute value quote missmatch');
+ skipAttr = true;
+ }
+ }
+
+ } else {
+ handleWarning('missing attribute value quotes');
+ skipAttr = true;
+
+ // skip to next space
+ for (j = j + 1; j < l; j++) {
+ w = s.charCodeAt(j + 1);
+
+ if (w === 32 || (w < 14 && w > 8)) { // WHITESPACE
+ break;
+ }
+ }
+
+ }
+
+ if (j === -1) {
+ handleWarning('missing closing quotes');
+
+ j = l;
+ skipAttr = true;
+ }
+
+ if (!skipAttr) {
+ value = s.substring(i, j);
+ }
+
+ i = j;
+
+ // ensure SPACE follows attribute
+ // skip illegal content otherwise
+ // example a="b"c
+ for (; j + 1 < l; j++) {
+ w = s.charCodeAt(j + 1);
+
+ if (w === 32 || (w < 14 && w > 8)) { // WHITESPACE
+ break;
+ }
+
+ // FIRST ILLEGAL CHAR
+ if (i === j) {
+ handleWarning('illegal character after attribute end');
+ skipAttr = true;
+ }
+ }
+
+ // advance cursor to next attribute
+ i = j + 1;
+
+ if (skipAttr) {
+ continue parseAttr;
+ }
+
+ // check attribute re-declaration
+ if (name in seenAttrs) {
+ handleWarning('attribute <' + name + '> already defined');
+ continue;
+ }
+
+ seenAttrs[name] = true;
+
+ if (!isNamespace) {
+ attrs[name] = value;
+ continue;
+ }
+
+ // try to extract namespace information
+ if (maybeNS) {
+ newalias = (
+ name === 'xmlns'
+ ? 'xmlns'
+ : (name.charCodeAt(0) === 120 && name.substr(0, 6) === 'xmlns:')
+ ? name.substr(6)
+ : null
+ );
+
+ // handle xmlns(:alias) assignment
+ if (newalias !== null) {
+ nsUri = decodeEntities(value);
+ nsUriPrefix = uriPrefix(newalias);
+
+ alias = nsUriToPrefix[nsUri];
+
+ if (!alias) {
+ // no prefix defined or prefix collision
+ if (
+ (newalias === 'xmlns') ||
+ (nsUriPrefix in nsMatrix && nsMatrix[nsUriPrefix] !== nsUri)
+ ) {
+ // alocate free ns prefix
+ do {
+ alias = 'ns' + (anonymousNsCount++);
+ } while (typeof nsMatrix[alias] !== 'undefined');
+ } else {
+ alias = newalias;
+ }
+
+ nsUriToPrefix[nsUri] = alias;
+ }
+
+ if (nsMatrix[newalias] !== alias) {
+ if (!hasNewMatrix) {
+ nsMatrix = cloneNsMatrix(nsMatrix);
+ hasNewMatrix = true;
+ }
+
+ nsMatrix[newalias] = alias;
+ if (newalias === 'xmlns') {
+ nsMatrix[uriPrefix(alias)] = nsUri;
+ defaultAlias = alias;
+ }
+
+ nsMatrix[nsUriPrefix] = nsUri;
+ }
+
+ // expose xmlns(:asd)="..." in attributes
+ attrs[name] = value;
+ continue;
+ }
+
+ // collect attributes until all namespace
+ // declarations are processed
+ attrList.push(name, value);
+ continue;
+
+ } /** end if (maybeNs) */
+
+ // handle attributes on element without
+ // namespace declarations
+ w = name.indexOf(':');
+ if (w === -1) {
+ attrs[name] = value;
+ continue;
+ }
+
+ // normalize ns attribute name
+ if (!(nsName = nsMatrix[name.substring(0, w)])) {
+ handleWarning(missingNamespaceForPrefix(name.substring(0, w)));
+ continue;
+ }
+
+ name = defaultAlias === nsName
+ ? name.substr(w + 1)
+ : nsName + name.substr(w);
+ // end: normalize ns attribute name
+
+ // normalize xsi:type ns attribute value
+ if (name === XSI_TYPE) {
+ w = value.indexOf(':');
+
+ if (w !== -1) {
+ nsName = value.substring(0, w);
+ // handle default prefixes, i.e. xs:String gracefully
+ nsName = nsMatrix[nsName] || nsName;
+ value = nsName + value.substring(w);
+ } else {
+ value = defaultAlias + ':' + value;
+ }
+ }
+ // end: normalize xsi:type ns attribute value
+
+ attrs[name] = value;
+ }
+
+
+ // handle deferred, possibly namespaced attributes
+ if (maybeNS) {
+
+ // normalize captured attributes
+ for (i = 0, l = attrList.length; i < l; i++) {
+
+ name = attrList[i++];
+ value = attrList[i];
+
+ w = name.indexOf(':');
+
+ if (w !== -1) {
+ // normalize ns attribute name
+ if (!(nsName = nsMatrix[name.substring(0, w)])) {
+ handleWarning(missingNamespaceForPrefix(name.substring(0, w)));
+ continue;
+ }
+
+ name = defaultAlias === nsName
+ ? name.substr(w + 1)
+ : nsName + name.substr(w);
+ // end: normalize ns attribute name
+
+ // normalize xsi:type ns attribute value
+ if (name === XSI_TYPE) {
+ w = value.indexOf(':');
+
+ if (w !== -1) {
+ nsName = value.substring(0, w);
+ // handle default prefixes, i.e. xs:String gracefully
+ nsName = nsMatrix[nsName] || nsName;
+ value = nsName + value.substring(w);
+ } else {
+ value = defaultAlias + ':' + value;
+ }
+ }
+ // end: normalize xsi:type ns attribute value
+ }
+
+ attrs[name] = value;
+ }
+ // end: normalize captured attributes
+ }
+
+ return cachedAttrs = attrs;
+ }
+
+ /**
+ * Extract the parse context { line, column, part }
+ * from the current parser position.
+ *
+ * @return {Object} parse context
+ */
+ function getParseContext() {
+ var splitsRe = /(\r\n|\r|\n)/g;
+
+ var line = 0;
+ var column = 0;
+ var startOfLine = 0;
+ var endOfLine = j;
+ var match;
+ var data;
+
+ while (i >= startOfLine) {
+
+ match = splitsRe.exec(xml);
+
+ if (!match) {
+ break;
+ }
+
+ // end of line = (break idx + break chars)
+ endOfLine = match[0].length + match.index;
+
+ if (endOfLine > i) {
+ break;
+ }
+
+ // advance to next line
+ line += 1;
+
+ startOfLine = endOfLine;
+ }
+
+ // EOF errors
+ if (i == -1) {
+ column = endOfLine;
+ data = '';
+ } else {
+ column = i - startOfLine;
+ data = (j == -1 ? xml.substring(i) : xml.substring(i, j + 1));
+ }
+
+ return {
+ 'data': data,
+ 'line': line,
+ 'column': column
+ };
+ }
+
+ getContext = getParseContext;
+
+
+ if (proxy) {
+ elementProxy = Object.create({}, {
+ 'name': getter(function() {
+ return elementName;
+ }),
+ 'originalName': getter(function() {
+ return _elementName;
+ }),
+ 'attrs': getter(getAttrs),
+ 'ns': getter(function() {
+ return nsMatrix;
+ })
+ });
+ }
+
+ // actual parse logic
+ while (j !== -1) {
+
+ if (xml.charCodeAt(j) === 60) { // "<"
+ i = j;
+ } else {
+ i = xml.indexOf('<', j);
+ }
+
+ // parse end
+ if (i === -1) {
+ if (nodeStack.length) {
+ return handleError('unexpected end of file');
+ }
+
+ if (j === 0) {
+ return handleError('missing start tag');
+ }
+
+ return;
+ }
+
+ if (j !== i) {
+ if (onText) {
+ onText(xml.substring(j, i), decodeEntities, getContext);
+ if (parseStop) {
+ return;
+ }
+ }
+ }
+
+ w = xml.charCodeAt(i+1);
+
+ // parse comments + CDATA
+ if (w === 33) { // "!"
+ w = xml.charCodeAt(i+2);
+ if (w === 91 && xml.substr(i + 3, 6) === 'CDATA[') { // 91 == "["
+ j = xml.indexOf(']]>', i);
+ if (j === -1) {
+ return handleError('unclosed cdata');
+ }
+
+ if (onCDATA) {
+ onCDATA(xml.substring(i + 9, j), getContext);
+ if (parseStop) {
+ return;
+ }
+ }
+
+ j += 3;
+ continue;
+ }
+
+
+ if (w === 45 && xml.charCodeAt(i + 3) === 45) { // 45 == "-"
+ j = xml.indexOf('-->', i);
+ if (j === -1) {
+ return handleError('unclosed comment');
+ }
+
+
+ if (onComment) {
+ onComment(xml.substring(i + 4, j), decodeEntities, getContext);
+ if (parseStop) {
+ return;
+ }
+ }
+
+ j += 3;
+ continue;
+ }
+
+ j = xml.indexOf('>', i + 1);
+ if (j === -1) {
+ return handleError('unclosed tag');
+ }
+
+ if (onAttention) {
+ onAttention(xml.substring(i, j + 1), decodeEntities, getContext);
+ if (parseStop) {
+ return;
+ }
+ }
+
+ j += 1;
+ continue;
+ }
+
+ if (w === 63) { // "?"
+ j = xml.indexOf('?>', i);
+ if (j === -1) {
+ return handleError('unclosed question');
+ }
+
+ if (onQuestion) {
+ onQuestion(xml.substring(i, j + 2), getContext);
+ if (parseStop) {
+ return;
+ }
+ }
+
+ j += 2;
+ continue;
+ }
+
+ j = xml.indexOf('>', i + 1);
+
+ if (j == -1) {
+ return handleError('unclosed tag');
+ }
+
+ // don't process attributes;
+ // there are none
+ cachedAttrs = {};
+
+ // if (xml.charCodeAt(i+1) === 47) { // close tag match
+ x = elementName = nodeStack.pop();
+ q = i + 2 + x.length;
+
+ if (xml.substring(i + 2, q) !== x) {
+ return handleError('closing tag mismatch');
+ }
+
+ // verify chars in close tag
+ for (; q < j; q++) {
+ w = xml.charCodeAt(q);
+
+ if (w === 32 || (w > 8 && w < 14)) { // \f\n\r\t\v space
+ continue;
+ }
+
+ return handleError('close tag');
+ }
+
+ } else {
+ if (xml.charCodeAt(j - 1) === 47) { // .../>
+ x = elementName = xml.substring(i + 1, j - 1);
+
+ tagStart = true;
+ tagEnd = true;
+
+ } else {
+ x = elementName = xml.substring(i + 1, j);
+
+ tagStart = true;
+ tagEnd = false;
+ }
+
+ if (!(w > 96 && w < 123 || w > 64 && w < 91 || w === 95 || w === 58)) { // char 95"_" 58":"
+ return handleError('illegal first char nodeName');
+ }
+
+ for (q = 1, y = x.length; q < y; q++) {
+ w = x.charCodeAt(q);
+
+ if (w > 96 && w < 123 || w > 64 && w < 91 || w > 47 && w < 59 || w === 45 || w === 95) {
+ continue;
+ }
+
+ if (w === 32 || (w < 14 && w > 8)) { // \f\n\r\t\v space
+ elementName = x.substring(0, q);
+ // maybe there are attributes
+ cachedAttrs = null;
+ break;
+ }
+
+ return handleError('invalid nodeName');
+ }
+
+ if (!tagEnd) {
+ nodeStack.push(elementName);
+ }
+ }
+
+ if (isNamespace) {
+
+ _nsMatrix = nsMatrix;
+
+ if (tagStart) {
+ // remember old namespace
+ // unless we're self-closing
+ if (!tagEnd) {
+ nsMatrixStack.push(_nsMatrix);
+ }
+
+ if (cachedAttrs === null) {
+ // quick check, whether there may be namespace
+ // declarations on the node; if that is the case
+ // we need to eagerly parse the node attributes
+ if ((maybeNS = x.indexOf('xmlns', q) !== -1)) {
+ attrsStart = q;
+ attrsString = x;
+
+ getAttrs();
+
+ maybeNS = false;
+ }
+ }
+ }
+
+ _elementName = elementName;
+
+ w = elementName.indexOf(':');
+ if (w !== -1) {
+ xmlns = nsMatrix[elementName.substring(0, w)];
+
+ // prefix given; namespace must exist
+ if (!xmlns) {
+ return handleError('missing namespace on <' + _elementName + '>');
+ }
+
+ elementName = elementName.substr(w + 1);
+ } else {
+ xmlns = nsMatrix['xmlns'];
+
+ // if no default namespace is defined,
+ // we'll import the element as anonymous.
+ //
+ // it is up to users to correct that to the document defined
+ // targetNamespace, or whatever their undersanding of the
+ // XML spec mandates.
+ }
+
+ // adjust namespace prefixs as configured
+ if (xmlns) {
+ elementName = xmlns + ':' + elementName;
+ }
+
+ }
+
+ if (tagStart) {
+ attrsStart = q;
+ attrsString = x;
+
+ if (onOpenTag) {
+ if (proxy) {
+ onOpenTag(elementProxy, decodeEntities, tagEnd, getContext);
+ } else {
+ onOpenTag(elementName, getAttrs, decodeEntities, tagEnd, getContext);
+ }
+
+ if (parseStop) {
+ return;
+ }
+ }
+
+ }
+
+ if (tagEnd) {
+
+ if (onCloseTag) {
+ onCloseTag(proxy ? elementProxy : elementName, decodeEntities, tagStart, getContext);
+
+ if (parseStop) {
+ return;
+ }
+ }
+
+ // restore old namespace
+ if (isNamespace) {
+ if (!tagStart) {
+ nsMatrix = nsMatrixStack.pop();
+ } else {
+ nsMatrix = _nsMatrix;
+ }
+ }
+ }
+
+ j += 1;
+ }
+ } /** end parse */
+
+ }
+
+ function hasLowerCaseAlias(pkg) {
+ return pkg.xml && pkg.xml.tagAlias === 'lowerCase';
+ }
+
+ var DEFAULT_NS_MAP = {
+ 'xsi': 'http://www.w3.org/2001/XMLSchema-instance'
+ };
+
+ var XSI_TYPE$1 = 'xsi:type';
+
+ function serializeFormat(element) {
+ return element.xml && element.xml.serialize;
+ }
+
+ function serializeAsType(element) {
+ return serializeFormat(element) === XSI_TYPE$1;
+ }
+
+ function serializeAsProperty(element) {
+ return serializeFormat(element) === 'property';
+ }
+
+ function capitalize(str) {
+ return str.charAt(0).toUpperCase() + str.slice(1);
+ }
+
+ function aliasToName(aliasNs, pkg) {
+
+ if (!hasLowerCaseAlias(pkg)) {
+ return aliasNs.name;
+ }
+
+ return aliasNs.prefix + ':' + capitalize(aliasNs.localName);
+ }
+
+ function prefixedToName(nameNs, pkg) {
+
+ var name = nameNs.name,
+ localName = nameNs.localName;
+
+ var typePrefix = pkg.xml && pkg.xml.typePrefix;
+
+ if (typePrefix && localName.indexOf(typePrefix) === 0) {
+ return nameNs.prefix + ':' + localName.slice(typePrefix.length);
+ } else {
+ return name;
+ }
+ }
+
+ function normalizeXsiTypeName(name, model) {
+
+ var nameNs = parseName(name);
+ var pkg = model.getPackage(nameNs.prefix);
+
+ return prefixedToName(nameNs, pkg);
+ }
+
+ function error$1(message) {
+ return new Error(message);
+ }
+
+ /**
+ * Get the moddle descriptor for a given instance or type.
+ *
+ * @param {ModdleElement|Function} element
+ *
+ * @return {Object} the moddle descriptor
+ */
+ function getModdleDescriptor(element) {
+ return element.$descriptor;
+ }
+
+ function defer(fn) {
+ setTimeout(fn, 0);
+ }
+
+ /**
+ * A parse context.
+ *
+ * @class
+ *
+ * @param {Object} options
+ * @param {ElementHandler} options.rootHandler the root handler for parsing a document
+ * @param {boolean} [options.lax=false] whether or not to ignore invalid elements
+ */
+ function Context(options) {
+
+ /**
+ * @property {ElementHandler} rootHandler
+ */
+
+ /**
+ * @property {Boolean} lax
+ */
+
+ assign(this, options);
+
+ this.elementsById = {};
+ this.references = [];
+ this.warnings = [];
+
+ /**
+ * Add an unresolved reference.
+ *
+ * @param {Object} reference
+ */
+ this.addReference = function(reference) {
+ this.references.push(reference);
+ };
+
+ /**
+ * Add a processed element.
+ *
+ * @param {ModdleElement} element
+ */
+ this.addElement = function(element) {
+
+ if (!element) {
+ throw error$1('expected element');
+ }
+
+ var elementsById = this.elementsById;
+
+ var descriptor = getModdleDescriptor(element);
+
+ var idProperty = descriptor.idProperty,
+ id;
+
+ if (idProperty) {
+ id = element.get(idProperty.name);
+
+ if (id) {
+
+ if (elementsById[id]) {
+ throw error$1('duplicate ID <' + id + '>');
+ }
+
+ elementsById[id] = element;
+ }
+ }
+ };
+
+ /**
+ * Add an import warning.
+ *
+ * @param {Object} warning
+ * @param {String} warning.message
+ * @param {Error} [warning.error]
+ */
+ this.addWarning = function(warning) {
+ this.warnings.push(warning);
+ };
+ }
+
+ function BaseHandler() {}
+
+ BaseHandler.prototype.handleEnd = function() {};
+ BaseHandler.prototype.handleText = function() {};
+ BaseHandler.prototype.handleNode = function() {};
+
+
+ /**
+ * A simple pass through handler that does nothing except for
+ * ignoring all input it receives.
+ *
+ * This is used to ignore unknown elements and
+ * attributes.
+ */
+ function NoopHandler() { }
+
+ NoopHandler.prototype = Object.create(BaseHandler.prototype);
+
+ NoopHandler.prototype.handleNode = function() {
+ return this;
+ };
+
+ function BodyHandler() {}
+
+ BodyHandler.prototype = Object.create(BaseHandler.prototype);
+
+ BodyHandler.prototype.handleText = function(text) {
+ this.body = (this.body || '') + text;
+ };
+
+ function ReferenceHandler(property, context) {
+ this.property = property;
+ this.context = context;
+ }
+
+ ReferenceHandler.prototype = Object.create(BodyHandler.prototype);
+
+ ReferenceHandler.prototype.handleNode = function(node) {
+
+ if (this.element) {
+ throw error$1('expected no sub nodes');
+ } else {
+ this.element = this.createReference(node);
+ }
+
+ return this;
+ };
+
+ ReferenceHandler.prototype.handleEnd = function() {
+ this.element.id = this.body;
+ };
+
+ ReferenceHandler.prototype.createReference = function(node) {
+ return {
+ property: this.property.ns.name,
+ id: ''
+ };
+ };
+
+ function ValueHandler(propertyDesc, element) {
+ this.element = element;
+ this.propertyDesc = propertyDesc;
+ }
+
+ ValueHandler.prototype = Object.create(BodyHandler.prototype);
+
+ ValueHandler.prototype.handleEnd = function() {
+
+ var value = this.body || '',
+ element = this.element,
+ propertyDesc = this.propertyDesc;
+
+ value = coerceType(propertyDesc.type, value);
+
+ if (propertyDesc.isMany) {
+ element.get(propertyDesc.name).push(value);
+ } else {
+ element.set(propertyDesc.name, value);
+ }
+ };
+
+
+ function BaseElementHandler() {}
+
+ BaseElementHandler.prototype = Object.create(BodyHandler.prototype);
+
+ BaseElementHandler.prototype.handleNode = function(node) {
+ var parser = this,
+ element = this.element;
+
+ if (!element) {
+ element = this.element = this.createElement(node);
+
+ this.context.addElement(element);
+ } else {
+ parser = this.handleChild(node);
+ }
+
+ return parser;
+ };
+
+ /**
+ * @class Reader.ElementHandler
+ *
+ */
+ function ElementHandler(model, typeName, context) {
+ this.model = model;
+ this.type = model.getType(typeName);
+ this.context = context;
+ }
+
+ ElementHandler.prototype = Object.create(BaseElementHandler.prototype);
+
+ ElementHandler.prototype.addReference = function(reference) {
+ this.context.addReference(reference);
+ };
+
+ ElementHandler.prototype.handleEnd = function() {
+
+ var value = this.body,
+ element = this.element,
+ descriptor = getModdleDescriptor(element),
+ bodyProperty = descriptor.bodyProperty;
+
+ if (bodyProperty && value !== undefined) {
+ value = coerceType(bodyProperty.type, value);
+ element.set(bodyProperty.name, value);
+ }
+ };
+
+ /**
+ * Create an instance of the model from the given node.
+ *
+ * @param {Element} node the xml node
+ */
+ ElementHandler.prototype.createElement = function(node) {
+ var attributes = node.attributes,
+ Type = this.type,
+ descriptor = getModdleDescriptor(Type),
+ context = this.context,
+ instance = new Type({}),
+ model = this.model,
+ propNameNs;
+
+ forEach(attributes, function(value, name) {
+
+ var prop = descriptor.propertiesByName[name],
+ values$$1;
+
+ if (prop && prop.isReference) {
+
+ if (!prop.isMany) {
+ context.addReference({
+ element: instance,
+ property: prop.ns.name,
+ id: value
+ });
+ } else {
+ // IDREFS: parse references as whitespace-separated list
+ values$$1 = value.split(' ');
+
+ forEach(values$$1, function(v) {
+ context.addReference({
+ element: instance,
+ property: prop.ns.name,
+ id: v
+ });
+ });
+ }
+
+ } else {
+ if (prop) {
+ value = coerceType(prop.type, value);
+ } else
+ if (name !== 'xmlns') {
+ propNameNs = parseName(name, descriptor.ns.prefix);
+
+ // check whether attribute is defined in a well-known namespace
+ // if that is the case we emit a warning to indicate potential misuse
+ if (model.getPackage(propNameNs.prefix)) {
+
+ context.addWarning({
+ message: 'unknown attribute <' + name + '>',
+ element: instance,
+ property: name,
+ value: value
+ });
+ }
+ }
+
+ instance.set(name, value);
+ }
+ });
+
+ return instance;
+ };
+
+ ElementHandler.prototype.getPropertyForNode = function(node) {
+
+ var name = node.name;
+ var nameNs = parseName(name);
+
+ var type = this.type,
+ model = this.model,
+ descriptor = getModdleDescriptor(type);
+
+ var propertyName = nameNs.name,
+ property = descriptor.propertiesByName[propertyName],
+ elementTypeName,
+ elementType;
+
+ // search for properties by name first
+
+ if (property) {
+
+ if (serializeAsType(property)) {
+ elementTypeName = node.attributes[XSI_TYPE$1];
+
+ // xsi type is optional, if it does not exists the
+ // default type is assumed
+ if (elementTypeName) {
+
+ // take possible type prefixes from XML
+ // into account, i.e.: xsi:type="t{ActualType}"
+ elementTypeName = normalizeXsiTypeName(elementTypeName, model);
+
+ elementType = model.getType(elementTypeName);
+
+ return assign({}, property, {
+ effectiveType: getModdleDescriptor(elementType).name
+ });
+ }
+ }
+
+ // search for properties by name first
+ return property;
+ }
+
+ var pkg = model.getPackage(nameNs.prefix);
+
+ if (pkg) {
+ elementTypeName = aliasToName(nameNs, pkg);
+ elementType = model.getType(elementTypeName);
+
+ // search for collection members later
+ property = find(descriptor.properties, function(p) {
+ return !p.isVirtual && !p.isReference && !p.isAttribute && elementType.hasType(p.type);
+ });
+
+ if (property) {
+ return assign({}, property, {
+ effectiveType: getModdleDescriptor(elementType).name
+ });
+ }
+ } else {
+ // parse unknown element (maybe extension)
+ property = find(descriptor.properties, function(p) {
+ return !p.isReference && !p.isAttribute && p.type === 'Element';
+ });
+
+ if (property) {
+ return property;
+ }
+ }
+
+ throw error$1('unrecognized element <' + nameNs.name + '>');
+ };
+
+ ElementHandler.prototype.toString = function() {
+ return 'ElementDescriptor[' + getModdleDescriptor(this.type).name + ']';
+ };
+
+ ElementHandler.prototype.valueHandler = function(propertyDesc, element) {
+ return new ValueHandler(propertyDesc, element);
+ };
+
+ ElementHandler.prototype.referenceHandler = function(propertyDesc) {
+ return new ReferenceHandler(propertyDesc, this.context);
+ };
+
+ ElementHandler.prototype.handler = function(type) {
+ if (type === 'Element') {
+ return new GenericElementHandler(this.model, type, this.context);
+ } else {
+ return new ElementHandler(this.model, type, this.context);
+ }
+ };
+
+ /**
+ * Handle the child element parsing
+ *
+ * @param {Element} node the xml node
+ */
+ ElementHandler.prototype.handleChild = function(node) {
+ var propertyDesc, type, element, childHandler;
+
+ propertyDesc = this.getPropertyForNode(node);
+ element = this.element;
+
+ type = propertyDesc.effectiveType || propertyDesc.type;
+
+ if (isSimple(type)) {
+ return this.valueHandler(propertyDesc, element);
+ }
+
+ if (propertyDesc.isReference) {
+ childHandler = this.referenceHandler(propertyDesc).handleNode(node);
+ } else {
+ childHandler = this.handler(type).handleNode(node);
+ }
+
+ var newElement = childHandler.element;
+
+ // child handles may decide to skip elements
+ // by not returning anything
+ if (newElement !== undefined) {
+
+ if (propertyDesc.isMany) {
+ element.get(propertyDesc.name).push(newElement);
+ } else {
+ element.set(propertyDesc.name, newElement);
+ }
+
+ if (propertyDesc.isReference) {
+ assign(newElement, {
+ element: element
+ });
+
+ this.context.addReference(newElement);
+ } else {
+ // establish child -> parent relationship
+ newElement.$parent = element;
+ }
+ }
+
+ return childHandler;
+ };
+
+ /**
+ * An element handler that performs special validation
+ * to ensure the node it gets initialized with matches
+ * the handlers type (namespace wise).
+ *
+ * @param {Moddle} model
+ * @param {String} typeName
+ * @param {Context} context
+ */
+ function RootElementHandler(model, typeName, context) {
+ ElementHandler.call(this, model, typeName, context);
+ }
+
+ RootElementHandler.prototype = Object.create(ElementHandler.prototype);
+
+ RootElementHandler.prototype.createElement = function(node) {
+
+ var name = node.name,
+ nameNs = parseName(name),
+ model = this.model,
+ type = this.type,
+ pkg = model.getPackage(nameNs.prefix),
+ typeName = pkg && aliasToName(nameNs, pkg) || name;
+
+ // verify the correct namespace if we parse
+ // the first element in the handler tree
+ //
+ // this ensures we don't mistakenly import wrong namespace elements
+ if (!type.hasType(typeName)) {
+ throw error$1('unexpected element <' + node.originalName + '>');
+ }
+
+ return ElementHandler.prototype.createElement.call(this, node);
+ };
+
+
+ function GenericElementHandler(model, typeName, context) {
+ this.model = model;
+ this.context = context;
+ }
+
+ GenericElementHandler.prototype = Object.create(BaseElementHandler.prototype);
+
+ GenericElementHandler.prototype.createElement = function(node) {
+
+ var name = node.name,
+ ns = parseName(name),
+ prefix = ns.prefix,
+ uri = node.ns[prefix + '$uri'],
+ attributes = node.attributes;
+
+ return this.model.createAny(name, uri, attributes);
+ };
+
+ GenericElementHandler.prototype.handleChild = function(node) {
+
+ var handler = new GenericElementHandler(this.model, 'Element', this.context).handleNode(node),
+ element = this.element;
+
+ var newElement = handler.element,
+ children;
+
+ if (newElement !== undefined) {
+ children = element.$children = element.$children || [];
+ children.push(newElement);
+
+ // establish child -> parent relationship
+ newElement.$parent = element;
+ }
+
+ return handler;
+ };
+
+ GenericElementHandler.prototype.handleText = function(text) {
+ this.body = this.body || '' + text;
+ };
+
+ GenericElementHandler.prototype.handleEnd = function() {
+ if (this.body) {
+ this.element.$body = this.body;
+ }
+ };
+
+ /**
+ * A reader for a meta-model
+ *
+ * @param {Object} options
+ * @param {Model} options.model used to read xml files
+ * @param {Boolean} options.lax whether to make parse errors warnings
+ */
+ function Reader(options) {
+
+ if (options instanceof Moddle) {
+ options = {
+ model: options
+ };
+ }
+
+ assign(this, { lax: false }, options);
+ }
+
+
+ /**
+ * Parse the given XML into a moddle document tree.
+ *
+ * @param {String} xml
+ * @param {ElementHandler|Object} options or rootHandler
+ * @param {Function} done
+ */
+ Reader.prototype.fromXML = function(xml, options, done) {
+
+ var rootHandler = options.rootHandler;
+
+ if (options instanceof ElementHandler) {
+ // root handler passed via (xml, { rootHandler: ElementHandler }, ...)
+ rootHandler = options;
+ options = {};
+ } else {
+ if (typeof options === 'string') {
+ // rootHandler passed via (xml, 'someString', ...)
+ rootHandler = this.handler(options);
+ options = {};
+ } else if (typeof rootHandler === 'string') {
+ // rootHandler passed via (xml, { rootHandler: 'someString' }, ...)
+ rootHandler = this.handler(rootHandler);
+ }
+ }
+
+ var model = this.model,
+ lax = this.lax;
+
+ var context = new Context(assign({}, options, { rootHandler: rootHandler })),
+ parser = new Parser({ proxy: true }),
+ stack = new tinyStack();
+
+ rootHandler.context = context;
+
+ // push root handler
+ stack.push(rootHandler);
+
+
+ /**
+ * Handle error.
+ *
+ * @param {Error} err
+ * @param {Function} getContext
+ * @param {boolean} lax
+ *
+ * @return {boolean} true if handled
+ */
+ function handleError(err, getContext, lax) {
+
+ var ctx = getContext();
+
+ var line = ctx.line,
+ column = ctx.column,
+ data = ctx.data;
+
+ // we receive the full context data here,
+ // for elements trim down the information
+ // to the tag name, only
+ if (data.charAt(0) === '<' && data.indexOf(' ') !== -1) {
+ data = data.slice(0, data.indexOf(' ')) + '>';
+ }
+
+ var message =
+ 'unparsable content ' + (data ? data + ' ' : '') + 'detected\n\t' +
+ 'line: ' + line + '\n\t' +
+ 'column: ' + column + '\n\t' +
+ 'nested error: ' + err.message;
+
+ if (lax) {
+ context.addWarning({
+ message: message,
+ error: err
+ });
+
+ console.warn('could not parse node');
+ console.warn(err);
+
+ return true;
+ } else {
+ console.error('could not parse document');
+ console.error(err);
+
+ throw error$1(message);
+ }
+ }
+
+ function handleWarning(err, getContext) {
+ // just like handling errors in mode
+ return handleError(err, getContext, true);
+ }
+
+ /**
+ * Resolve collected references on parse end.
+ */
+ function resolveReferences() {
+
+ var elementsById = context.elementsById;
+ var references = context.references;
+
+ var i, r;
+
+ for (i = 0; (r = references[i]); i++) {
+ var element = r.element;
+ var reference = elementsById[r.id];
+ var property = getModdleDescriptor(element).propertiesByName[r.property];
+
+ if (!reference) {
+ context.addWarning({
+ message: 'unresolved reference <' + r.id + '>',
+ element: r.element,
+ property: r.property,
+ value: r.id
+ });
+ }
+
+ if (property.isMany) {
+ var collection = element.get(property.name),
+ idx = collection.indexOf(r);
+
+ // we replace an existing place holder (idx != -1) or
+ // append to the collection instead
+ if (idx === -1) {
+ idx = collection.length;
+ }
+
+ if (!reference) {
+ // remove unresolvable reference
+ collection.splice(idx, 1);
+ } else {
+ // add or update reference in collection
+ collection[idx] = reference;
+ }
+ } else {
+ element.set(property.name, reference);
+ }
+ }
+ }
+
+ function handleClose() {
+ stack.pop().handleEnd();
+ }
+
+ var PREAMBLE_START_PATTERN = /^<\?xml /i;
+
+ var ENCODING_PATTERN = / encoding="([^"]+)"/i;
+
+ var UTF_8_PATTERN = /^utf-8$/i;
+
+ function handleQuestion(question) {
+
+ if (!PREAMBLE_START_PATTERN.test(question)) {
+ return;
+ }
+
+ var match = ENCODING_PATTERN.exec(question);
+ var encoding = match && match[1];
+
+ if (!encoding || UTF_8_PATTERN.test(encoding)) {
+ return;
+ }
+
+ context.addWarning({
+ message:
+ 'unsupported document encoding <' + encoding + '>, ' +
+ 'falling back to UTF-8'
+ });
+ }
+
+ function handleOpen(node, getContext) {
+ var handler = stack.peek();
+
+ try {
+ stack.push(handler.handleNode(node));
+ } catch (err) {
+
+ if (handleError(err, getContext, lax)) {
+ stack.push(new NoopHandler());
+ }
+ }
+ }
+
+ function handleCData(text) {
+ stack.peek().handleText(text);
+ }
+
+ function handleText(text) {
+ // strip whitespace only nodes, i.e. before
+ // sections and in between tags
+ text = text.trim();
+
+ if (!text) {
+ return;
+ }
+
+ stack.peek().handleText(text);
+ }
+
+ var uriMap = model.getPackages().reduce(function(uriMap, p) {
+ uriMap[p.uri] = p.prefix;
+
+ return uriMap;
+ }, {});
+
+ parser
+ .ns(uriMap)
+ .on('openTag', function(obj, decodeStr, selfClosing, getContext) {
+
+ // gracefully handle unparsable attributes (attrs=false)
+ var attrs = obj.attrs || {};
+
+ var decodedAttrs = Object.keys(attrs).reduce(function(d, key) {
+ var value = decodeStr(attrs[key]);
+
+ d[key] = value;
+
+ return d;
+ }, {});
+
+ var node = {
+ name: obj.name,
+ originalName: obj.originalName,
+ attributes: decodedAttrs,
+ ns: obj.ns
+ };
+
+ handleOpen(node, getContext);
+ })
+ .on('question', handleQuestion)
+ .on('closeTag', handleClose)
+ .on('cdata', handleCData)
+ .on('text', function(text, decodeEntities$$1) {
+ handleText(decodeEntities$$1(text));
+ })
+ .on('error', handleError)
+ .on('warn', handleWarning);
+
+ // deferred parse XML to make loading really ascnchronous
+ // this ensures the execution environment (node or browser)
+ // is kept responsive and that certain optimization strategies
+ // can kick in
+ defer(function() {
+ var err;
+
+ try {
+ parser.parse(xml);
+
+ resolveReferences();
+ } catch (e) {
+ err = e;
+ }
+
+ var element = rootHandler.element;
+
+ // handle the situation that we could not extract
+ // the desired root element from the document
+ if (!err && !element) {
+ err = error$1('failed to parse document as <' + rootHandler.type.$descriptor.name + '>');
+ }
+
+ done(err, err ? undefined : element, context);
+ });
+ };
+
+ Reader.prototype.handler = function(name) {
+ return new RootElementHandler(this.model, name);
+ };
+
+ var XML_PREAMBLE = '\n';
+
+ var ESCAPE_ATTR_CHARS = /<|>|'|"|&|\n\r|\n/g;
+ var ESCAPE_CHARS = /<|>|&/g;
+
+
+ function Namespaces(parent) {
+
+ var prefixMap = {};
+ var uriMap = {};
+ var used = {};
+
+ var wellknown = [];
+ var custom = [];
+
+ // API
+
+ this.byUri = function(uri) {
+ return uriMap[uri] || (
+ parent && parent.byUri(uri)
+ );
+ };
+
+ this.add = function(ns, isWellknown) {
+
+ uriMap[ns.uri] = ns;
+
+ if (isWellknown) {
+ wellknown.push(ns);
+ } else {
+ custom.push(ns);
+ }
+
+ this.mapPrefix(ns.prefix, ns.uri);
+ };
+
+ this.uriByPrefix = function(prefix) {
+ return prefixMap[prefix || 'xmlns'];
+ };
+
+ this.mapPrefix = function(prefix, uri) {
+ prefixMap[prefix || 'xmlns'] = uri;
+ };
+
+ this.logUsed = function(ns) {
+ var uri = ns.uri;
+
+ used[uri] = this.byUri(uri);
+ };
+
+ this.getUsed = function(ns) {
+
+ function isUsed(ns) {
+ return used[ns.uri];
+ }
+
+ var allNs = [].concat(wellknown, custom);
+
+ return allNs.filter(isUsed);
+ };
+
+ }
+
+ function lower(string) {
+ return string.charAt(0).toLowerCase() + string.slice(1);
+ }
+
+ function nameToAlias(name, pkg) {
+ if (hasLowerCaseAlias(pkg)) {
+ return lower(name);
+ } else {
+ return name;
+ }
+ }
+
+ function inherits(ctor, superCtor) {
+ ctor.super_ = superCtor;
+ ctor.prototype = Object.create(superCtor.prototype, {
+ constructor: {
+ value: ctor,
+ enumerable: false,
+ writable: true,
+ configurable: true
+ }
+ });
+ }
+
+ function nsName(ns) {
+ if (isString(ns)) {
+ return ns;
+ } else {
+ return (ns.prefix ? ns.prefix + ':' : '') + ns.localName;
+ }
+ }
+
+ function getNsAttrs(namespaces) {
+
+ return map(namespaces.getUsed(), function(ns) {
+ var name = 'xmlns' + (ns.prefix ? ':' + ns.prefix : '');
+ return { name: name, value: ns.uri };
+ });
+
+ }
+
+ function getElementNs(ns, descriptor) {
+ if (descriptor.isGeneric) {
+ return assign({ localName: descriptor.ns.localName }, ns);
+ } else {
+ return assign({ localName: nameToAlias(descriptor.ns.localName, descriptor.$pkg) }, ns);
+ }
+ }
+
+ function getPropertyNs(ns, descriptor) {
+ return assign({ localName: descriptor.ns.localName }, ns);
+ }
+
+ function getSerializableProperties(element) {
+ var descriptor = element.$descriptor;
+
+ return filter(descriptor.properties, function(p) {
+ var name = p.name;
+
+ if (p.isVirtual) {
+ return false;
+ }
+
+ // do not serialize defaults
+ if (!element.hasOwnProperty(name)) {
+ return false;
+ }
+
+ var value = element[name];
+
+ // do not serialize default equals
+ if (value === p.default) {
+ return false;
+ }
+
+ // do not serialize null properties
+ if (value === null) {
+ return false;
+ }
+
+ return p.isMany ? value.length : true;
+ });
+ }
+
+ var ESCAPE_ATTR_MAP = {
+ '\n': '#10',
+ '\n\r': '#10',
+ '"': '#34',
+ '\'': '#39',
+ '<': '#60',
+ '>': '#62',
+ '&': '#38'
+ };
+
+ var ESCAPE_MAP = {
+ '<': 'lt',
+ '>': 'gt',
+ '&': 'amp'
+ };
+
+ function escape$1(str, charPattern, replaceMap) {
+
+ // ensure we are handling strings here
+ str = isString(str) ? str : '' + str;
+
+ return str.replace(charPattern, function(s) {
+ return '&' + replaceMap[s] + ';';
+ });
+ }
+
+ /**
+ * Escape a string attribute to not contain any bad values (line breaks, '"', ...)
+ *
+ * @param {String} str the string to escape
+ * @return {String} the escaped string
+ */
+ function escapeAttr(str) {
+ return escape$1(str, ESCAPE_ATTR_CHARS, ESCAPE_ATTR_MAP);
+ }
+
+ function escapeBody(str) {
+ return escape$1(str, ESCAPE_CHARS, ESCAPE_MAP);
+ }
+
+ function filterAttributes(props) {
+ return filter(props, function(p) { return p.isAttr; });
+ }
+
+ function filterContained(props) {
+ return filter(props, function(p) { return !p.isAttr; });
+ }
+
+
+ function ReferenceSerializer(tagName) {
+ this.tagName = tagName;
+ }
+
+ ReferenceSerializer.prototype.build = function(element) {
+ this.element = element;
+ return this;
+ };
+
+ ReferenceSerializer.prototype.serializeTo = function(writer) {
+ writer
+ .appendIndent()
+ .append('<' + this.tagName + '>' + this.element.id + '' + this.tagName + '>')
+ .appendNewLine();
+ };
+
+ function BodySerializer() {}
+
+ BodySerializer.prototype.serializeValue =
+ BodySerializer.prototype.serializeTo = function(writer) {
+ writer.append(
+ this.escape
+ ? escapeBody(this.value)
+ : this.value
+ );
+ };
+
+ BodySerializer.prototype.build = function(prop, value) {
+ this.value = value;
+
+ if (prop.type === 'String' && value.search(ESCAPE_CHARS) !== -1) {
+ this.escape = true;
+ }
+
+ return this;
+ };
+
+ function ValueSerializer(tagName) {
+ this.tagName = tagName;
+ }
+
+ inherits(ValueSerializer, BodySerializer);
+
+ ValueSerializer.prototype.serializeTo = function(writer) {
+
+ writer
+ .appendIndent()
+ .append('<' + this.tagName + '>');
+
+ this.serializeValue(writer);
+
+ writer
+ .append('' + this.tagName + '>')
+ .appendNewLine();
+ };
+
+ function ElementSerializer(parent, propertyDescriptor) {
+ this.body = [];
+ this.attrs = [];
+
+ this.parent = parent;
+ this.propertyDescriptor = propertyDescriptor;
+ }
+
+ ElementSerializer.prototype.build = function(element) {
+ this.element = element;
+
+ var elementDescriptor = element.$descriptor,
+ propertyDescriptor = this.propertyDescriptor;
+
+ var otherAttrs,
+ properties;
+
+ var isGeneric = elementDescriptor.isGeneric;
+
+ if (isGeneric) {
+ otherAttrs = this.parseGeneric(element);
+ } else {
+ otherAttrs = this.parseNsAttributes(element);
+ }
+
+ if (propertyDescriptor) {
+ this.ns = this.nsPropertyTagName(propertyDescriptor);
+ } else {
+ this.ns = this.nsTagName(elementDescriptor);
+ }
+
+ // compute tag name
+ this.tagName = this.addTagName(this.ns);
+
+ if (!isGeneric) {
+ properties = getSerializableProperties(element);
+
+ this.parseAttributes(filterAttributes(properties));
+ this.parseContainments(filterContained(properties));
+ }
+
+ this.parseGenericAttributes(element, otherAttrs);
+
+ return this;
+ };
+
+ ElementSerializer.prototype.nsTagName = function(descriptor) {
+ var effectiveNs = this.logNamespaceUsed(descriptor.ns);
+ return getElementNs(effectiveNs, descriptor);
+ };
+
+ ElementSerializer.prototype.nsPropertyTagName = function(descriptor) {
+ var effectiveNs = this.logNamespaceUsed(descriptor.ns);
+ return getPropertyNs(effectiveNs, descriptor);
+ };
+
+ ElementSerializer.prototype.isLocalNs = function(ns) {
+ return ns.uri === this.ns.uri;
+ };
+
+ /**
+ * Get the actual ns attribute name for the given element.
+ *
+ * @param {Object} element
+ * @param {Boolean} [element.inherited=false]
+ *
+ * @return {Object} nsName
+ */
+ ElementSerializer.prototype.nsAttributeName = function(element) {
+
+ var ns;
+
+ if (isString(element)) {
+ ns = parseName(element);
+ } else {
+ ns = element.ns;
+ }
+
+ // return just local name for inherited attributes
+ if (element.inherited) {
+ return { localName: ns.localName };
+ }
+
+ // parse + log effective ns
+ var effectiveNs = this.logNamespaceUsed(ns);
+
+ // LOG ACTUAL namespace use
+ this.getNamespaces().logUsed(effectiveNs);
+
+ // strip prefix if same namespace like parent
+ if (this.isLocalNs(effectiveNs)) {
+ return { localName: ns.localName };
+ } else {
+ return assign({ localName: ns.localName }, effectiveNs);
+ }
+ };
+
+ ElementSerializer.prototype.parseGeneric = function(element) {
+
+ var self = this,
+ body = this.body;
+
+ var attributes = [];
+
+ forEach(element, function(val, key) {
+
+ var nonNsAttr;
+
+ if (key === '$body') {
+ body.push(new BodySerializer().build({ type: 'String' }, val));
+ } else
+ if (key === '$children') {
+ forEach(val, function(child) {
+ body.push(new ElementSerializer(self).build(child));
+ });
+ } else
+ if (key.indexOf('$') !== 0) {
+ nonNsAttr = self.parseNsAttribute(element, key, val);
+
+ if (nonNsAttr) {
+ attributes.push({ name: key, value: val });
+ }
+ }
+ });
+
+ return attributes;
+ };
+
+ ElementSerializer.prototype.parseNsAttribute = function(element, name, value) {
+ var model = element.$model;
+
+ var nameNs = parseName(name);
+
+ var ns;
+
+ // parse xmlns:foo="http://foo.bar"
+ if (nameNs.prefix === 'xmlns') {
+ ns = { prefix: nameNs.localName, uri: value };
+ }
+
+ // parse xmlns="http://foo.bar"
+ if (!nameNs.prefix && nameNs.localName === 'xmlns') {
+ ns = { uri: value };
+ }
+
+ if (!ns) {
+ return {
+ name: name,
+ value: value
+ };
+ }
+
+ if (model && model.getPackage(value)) {
+ // register well known namespace
+ this.logNamespace(ns, true, true);
+ } else {
+ // log custom namespace directly as used
+ var actualNs = this.logNamespaceUsed(ns, true);
+
+ this.getNamespaces().logUsed(actualNs);
+ }
+ };
+
+
+ /**
+ * Parse namespaces and return a list of left over generic attributes
+ *
+ * @param {Object} element
+ * @return {Array}
+ */
+ ElementSerializer.prototype.parseNsAttributes = function(element, attrs) {
+ var self = this;
+
+ var genericAttrs = element.$attrs;
+
+ var attributes = [];
+
+ // parse namespace attributes first
+ // and log them. push non namespace attributes to a list
+ // and process them later
+ forEach(genericAttrs, function(value, name) {
+
+ var nonNsAttr = self.parseNsAttribute(element, name, value);
+
+ if (nonNsAttr) {
+ attributes.push(nonNsAttr);
+ }
+ });
+
+ return attributes;
+ };
+
+ ElementSerializer.prototype.parseGenericAttributes = function(element, attributes) {
+
+ var self = this;
+
+ forEach(attributes, function(attr) {
+
+ // do not serialize xsi:type attribute
+ // it is set manually based on the actual implementation type
+ if (attr.name === XSI_TYPE$1) {
+ return;
+ }
+
+ try {
+ self.addAttribute(self.nsAttributeName(attr.name), attr.value);
+ } catch (e) {
+ console.warn(
+ 'missing namespace information for ',
+ attr.name, '=', attr.value, 'on', element,
+ e);
+ }
+ });
+ };
+
+ ElementSerializer.prototype.parseContainments = function(properties) {
+
+ var self = this,
+ body = this.body,
+ element = this.element;
+
+ forEach(properties, function(p) {
+ var value = element.get(p.name),
+ isReference = p.isReference,
+ isMany = p.isMany;
+
+ if (!isMany) {
+ value = [ value ];
+ }
+
+ if (p.isBody) {
+ body.push(new BodySerializer().build(p, value[0]));
+ } else
+ if (isSimple(p.type)) {
+ forEach(value, function(v) {
+ body.push(new ValueSerializer(self.addTagName(self.nsPropertyTagName(p))).build(p, v));
+ });
+ } else
+ if (isReference) {
+ forEach(value, function(v) {
+ body.push(new ReferenceSerializer(self.addTagName(self.nsPropertyTagName(p))).build(v));
+ });
+ } else {
+ // allow serialization via type
+ // rather than element name
+ var asType = serializeAsType(p),
+ asProperty = serializeAsProperty(p);
+
+ forEach(value, function(v) {
+ var serializer;
+
+ if (asType) {
+ serializer = new TypeSerializer(self, p);
+ } else
+ if (asProperty) {
+ serializer = new ElementSerializer(self, p);
+ } else {
+ serializer = new ElementSerializer(self);
+ }
+
+ body.push(serializer.build(v));
+ });
+ }
+ });
+ };
+
+ ElementSerializer.prototype.getNamespaces = function(local) {
+
+ var namespaces = this.namespaces,
+ parent = this.parent,
+ parentNamespaces;
+
+ if (!namespaces) {
+ parentNamespaces = parent && parent.getNamespaces();
+
+ if (local || !parentNamespaces) {
+ this.namespaces = namespaces = new Namespaces(parentNamespaces);
+ } else {
+ namespaces = parentNamespaces;
+ }
+ }
+
+ return namespaces;
+ };
+
+ ElementSerializer.prototype.logNamespace = function(ns, wellknown, local) {
+ var namespaces = this.getNamespaces(local);
+
+ var nsUri = ns.uri,
+ nsPrefix = ns.prefix;
+
+ var existing = namespaces.byUri(nsUri);
+
+ if (!existing) {
+ namespaces.add(ns, wellknown);
+ }
+
+ namespaces.mapPrefix(nsPrefix, nsUri);
+
+ return ns;
+ };
+
+ ElementSerializer.prototype.logNamespaceUsed = function(ns, local) {
+ var element = this.element,
+ model = element.$model,
+ namespaces = this.getNamespaces(local);
+
+ // ns may be
+ //
+ // * prefix only
+ // * prefix:uri
+ // * localName only
+
+ var prefix = ns.prefix,
+ uri = ns.uri,
+ newPrefix, idx,
+ wellknownUri;
+
+ // handle anonymous namespaces (elementForm=unqualified), cf. #23
+ if (!prefix && !uri) {
+ return { localName: ns.localName };
+ }
+
+ wellknownUri = DEFAULT_NS_MAP[prefix] || model && (model.getPackage(prefix) || {}).uri;
+
+ uri = uri || wellknownUri || namespaces.uriByPrefix(prefix);
+
+ if (!uri) {
+ throw new Error('no namespace uri given for prefix <' + prefix + '>');
+ }
+
+ ns = namespaces.byUri(uri);
+
+ if (!ns) {
+ newPrefix = prefix;
+ idx = 1;
+
+ // find a prefix that is not mapped yet
+ while (namespaces.uriByPrefix(newPrefix)) {
+ newPrefix = prefix + '_' + idx++;
+ }
+
+ ns = this.logNamespace({ prefix: newPrefix, uri: uri }, wellknownUri === uri);
+ }
+
+ if (prefix) {
+ namespaces.mapPrefix(prefix, uri);
+ }
+
+ return ns;
+ };
+
+ ElementSerializer.prototype.parseAttributes = function(properties) {
+ var self = this,
+ element = this.element;
+
+ forEach(properties, function(p) {
+
+ var value = element.get(p.name);
+
+ if (p.isReference) {
+
+ if (!p.isMany) {
+ value = value.id;
+ }
+ else {
+ var values$$1 = [];
+ forEach(value, function(v) {
+ values$$1.push(v.id);
+ });
+ // IDREFS is a whitespace-separated list of references.
+ value = values$$1.join(' ');
+ }
+
+ }
+
+ self.addAttribute(self.nsAttributeName(p), value);
+ });
+ };
+
+ ElementSerializer.prototype.addTagName = function(nsTagName) {
+ var actualNs = this.logNamespaceUsed(nsTagName);
+
+ this.getNamespaces().logUsed(actualNs);
+
+ return nsName(nsTagName);
+ };
+
+ ElementSerializer.prototype.addAttribute = function(name, value) {
+ var attrs = this.attrs;
+
+ if (isString(value)) {
+ value = escapeAttr(value);
+ }
+
+ attrs.push({ name: name, value: value });
+ };
+
+ ElementSerializer.prototype.serializeAttributes = function(writer) {
+ var attrs = this.attrs,
+ namespaces = this.namespaces;
+
+ if (namespaces) {
+ attrs = getNsAttrs(namespaces).concat(attrs);
+ }
+
+ forEach(attrs, function(a) {
+ writer
+ .append(' ')
+ .append(nsName(a.name)).append('="').append(a.value).append('"');
+ });
+ };
+
+ ElementSerializer.prototype.serializeTo = function(writer) {
+ var firstBody = this.body[0],
+ indent = firstBody && firstBody.constructor !== BodySerializer;
+
+ writer
+ .appendIndent()
+ .append('<' + this.tagName);
+
+ this.serializeAttributes(writer);
+
+ writer.append(firstBody ? '>' : ' />');
+
+ if (firstBody) {
+
+ if (indent) {
+ writer
+ .appendNewLine()
+ .indent();
+ }
+
+ forEach(this.body, function(b) {
+ b.serializeTo(writer);
+ });
+
+ if (indent) {
+ writer
+ .unindent()
+ .appendIndent();
+ }
+
+ writer.append('' + this.tagName + '>');
+ }
+
+ writer.appendNewLine();
+ };
+
+ /**
+ * A serializer for types that handles serialization of data types
+ */
+ function TypeSerializer(parent, propertyDescriptor) {
+ ElementSerializer.call(this, parent, propertyDescriptor);
+ }
+
+ inherits(TypeSerializer, ElementSerializer);
+
+ TypeSerializer.prototype.parseNsAttributes = function(element) {
+
+ // extracted attributes
+ var attributes = ElementSerializer.prototype.parseNsAttributes.call(this, element);
+
+ var descriptor = element.$descriptor;
+
+ // only serialize xsi:type if necessary
+ if (descriptor.name === this.propertyDescriptor.type) {
+ return attributes;
+ }
+
+ var typeNs = this.typeNs = this.nsTagName(descriptor);
+ this.getNamespaces().logUsed(this.typeNs);
+
+ // add xsi:type attribute to represent the elements
+ // actual type
+
+ var pkg = element.$model.getPackage(typeNs.uri),
+ typePrefix = (pkg.xml && pkg.xml.typePrefix) || '';
+
+ this.addAttribute(
+ this.nsAttributeName(XSI_TYPE$1),
+ (typeNs.prefix ? typeNs.prefix + ':' : '') + typePrefix + descriptor.ns.localName
+ );
+
+ return attributes;
+ };
+
+ TypeSerializer.prototype.isLocalNs = function(ns) {
+ return ns.uri === (this.typeNs || this.ns).uri;
+ };
+
+ function SavingWriter() {
+ this.value = '';
+
+ this.write = function(str) {
+ this.value += str;
+ };
+ }
+
+ function FormatingWriter(out, format) {
+
+ var indent = [''];
+
+ this.append = function(str) {
+ out.write(str);
+
+ return this;
+ };
+
+ this.appendNewLine = function() {
+ if (format) {
+ out.write('\n');
+ }
+
+ return this;
+ };
+
+ this.appendIndent = function() {
+ if (format) {
+ out.write(indent.join(' '));
+ }
+
+ return this;
+ };
+
+ this.indent = function() {
+ indent.push('');
+ return this;
+ };
+
+ this.unindent = function() {
+ indent.pop();
+ return this;
+ };
+ }
+
+ /**
+ * A writer for meta-model backed document trees
+ *
+ * @param {Object} options output options to pass into the writer
+ */
+ function Writer(options) {
+
+ options = assign({ format: false, preamble: true }, options || {});
+
+ function toXML(tree, writer) {
+ var internalWriter = writer || new SavingWriter();
+ var formatingWriter = new FormatingWriter(internalWriter, options.format);
+
+ if (options.preamble) {
+ formatingWriter.append(XML_PREAMBLE);
+ }
+
+ new ElementSerializer().build(tree).serializeTo(formatingWriter);
+
+ if (!writer) {
+ return internalWriter.value;
+ }
+ }
+
+ return {
+ toXML: toXML
+ };
+ }
+
+ /**
+ * A sub class of {@link Moddle} with support for import and export of BPMN 2.0 xml files.
+ *
+ * @class BpmnModdle
+ * @extends Moddle
+ *
+ * @param {Object|Array} packages to use for instantiating the model
+ * @param {Object} [options] additional options to pass over
+ */
+ function BpmnModdle(packages, options) {
+ Moddle.call(this, packages, options);
+ }
+
+ BpmnModdle.prototype = Object.create(Moddle.prototype);
+
+
+ /**
+ * Instantiates a BPMN model tree from a given xml string.
+ *
+ * @param {String} xmlStr
+ * @param {String} [typeName='bpmn:Definitions'] name of the root element
+ * @param {Object} [options] options to pass to the underlying reader
+ * @param {Function} done callback that is invoked with (err, result, parseContext)
+ * once the import completes
+ */
+ BpmnModdle.prototype.fromXML = function(xmlStr, typeName, options, done) {
+
+ if (!isString(typeName)) {
+ done = options;
+ options = typeName;
+ typeName = 'bpmn:Definitions';
+ }
+
+ if (isFunction(options)) {
+ done = options;
+ options = {};
+ }
+
+ var reader = new Reader(assign({ model: this, lax: true }, options));
+ var rootHandler = reader.handler(typeName);
+
+ reader.fromXML(xmlStr, rootHandler, done);
+ };
+
+
+ /**
+ * Serializes a BPMN 2.0 object tree to XML.
+ *
+ * @param {String} element the root element, typically an instance of `bpmn:Definitions`
+ * @param {Object} [options] to pass to the underlying writer
+ * @param {Function} done callback invoked with (err, xmlStr) once the import completes
+ */
+ BpmnModdle.prototype.toXML = function(element, options, done) {
+
+ if (isFunction(options)) {
+ done = options;
+ options = {};
+ }
+
+ var writer = new Writer(options);
+
+ var result;
+ var err;
+
+ try {
+ result = writer.toXML(element);
+ } catch (e) {
+ err = e;
+ }
+
+ return done(err, result);
+ };
+
+ var name = "BPMN20";
+ var uri = "http://www.omg.org/spec/BPMN/20100524/MODEL";
+ var associations = [];
+ var types$1 = [{"name":"Interface","superClass":["RootElement"],"properties":[{"name":"name","isAttr":true,"type":"String"},{"name":"operations","type":"Operation","isMany":true},{"name":"implementationRef","type":"String","isAttr":true}]},{"name":"Operation","superClass":["BaseElement"],"properties":[{"name":"name","isAttr":true,"type":"String"},{"name":"inMessageRef","type":"Message","isReference":true},{"name":"outMessageRef","type":"Message","isReference":true},{"name":"errorRef","type":"Error","isMany":true,"isReference":true},{"name":"implementationRef","type":"String","isAttr":true}]},{"name":"EndPoint","superClass":["RootElement"]},{"name":"Auditing","superClass":["BaseElement"]},{"name":"GlobalTask","superClass":["CallableElement"],"properties":[{"name":"resources","type":"ResourceRole","isMany":true}]},{"name":"Monitoring","superClass":["BaseElement"]},{"name":"Performer","superClass":["ResourceRole"]},{"name":"Process","superClass":["FlowElementsContainer","CallableElement"],"properties":[{"name":"processType","type":"ProcessType","isAttr":true},{"name":"isClosed","isAttr":true,"type":"Boolean"},{"name":"auditing","type":"Auditing"},{"name":"monitoring","type":"Monitoring"},{"name":"properties","type":"Property","isMany":true},{"name":"laneSets","type":"LaneSet","isMany":true,"replaces":"FlowElementsContainer#laneSets"},{"name":"flowElements","type":"FlowElement","isMany":true,"replaces":"FlowElementsContainer#flowElements"},{"name":"artifacts","type":"Artifact","isMany":true},{"name":"resources","type":"ResourceRole","isMany":true},{"name":"correlationSubscriptions","type":"CorrelationSubscription","isMany":true},{"name":"supports","type":"Process","isMany":true,"isReference":true},{"name":"definitionalCollaborationRef","type":"Collaboration","isAttr":true,"isReference":true},{"name":"isExecutable","isAttr":true,"type":"Boolean"}]},{"name":"LaneSet","superClass":["BaseElement"],"properties":[{"name":"lanes","type":"Lane","isMany":true},{"name":"name","isAttr":true,"type":"String"}]},{"name":"Lane","superClass":["BaseElement"],"properties":[{"name":"name","isAttr":true,"type":"String"},{"name":"partitionElementRef","type":"BaseElement","isAttr":true,"isReference":true},{"name":"partitionElement","type":"BaseElement"},{"name":"flowNodeRef","type":"FlowNode","isMany":true,"isReference":true},{"name":"childLaneSet","type":"LaneSet","xml":{"serialize":"xsi:type"}}]},{"name":"GlobalManualTask","superClass":["GlobalTask"]},{"name":"ManualTask","superClass":["Task"]},{"name":"UserTask","superClass":["Task"],"properties":[{"name":"renderings","type":"Rendering","isMany":true},{"name":"implementation","isAttr":true,"type":"String"}]},{"name":"Rendering","superClass":["BaseElement"]},{"name":"HumanPerformer","superClass":["Performer"]},{"name":"PotentialOwner","superClass":["HumanPerformer"]},{"name":"GlobalUserTask","superClass":["GlobalTask"],"properties":[{"name":"implementation","isAttr":true,"type":"String"},{"name":"renderings","type":"Rendering","isMany":true}]},{"name":"Gateway","isAbstract":true,"superClass":["FlowNode"],"properties":[{"name":"gatewayDirection","type":"GatewayDirection","default":"Unspecified","isAttr":true}]},{"name":"EventBasedGateway","superClass":["Gateway"],"properties":[{"name":"instantiate","default":false,"isAttr":true,"type":"Boolean"},{"name":"eventGatewayType","type":"EventBasedGatewayType","isAttr":true,"default":"Exclusive"}]},{"name":"ComplexGateway","superClass":["Gateway"],"properties":[{"name":"activationCondition","type":"Expression","xml":{"serialize":"xsi:type"}},{"name":"default","type":"SequenceFlow","isAttr":true,"isReference":true}]},{"name":"ExclusiveGateway","superClass":["Gateway"],"properties":[{"name":"default","type":"SequenceFlow","isAttr":true,"isReference":true}]},{"name":"InclusiveGateway","superClass":["Gateway"],"properties":[{"name":"default","type":"SequenceFlow","isAttr":true,"isReference":true}]},{"name":"ParallelGateway","superClass":["Gateway"]},{"name":"RootElement","isAbstract":true,"superClass":["BaseElement"]},{"name":"Relationship","superClass":["BaseElement"],"properties":[{"name":"type","isAttr":true,"type":"String"},{"name":"direction","type":"RelationshipDirection","isAttr":true},{"name":"source","isMany":true,"isReference":true,"type":"Element"},{"name":"target","isMany":true,"isReference":true,"type":"Element"}]},{"name":"BaseElement","isAbstract":true,"properties":[{"name":"id","isAttr":true,"type":"String","isId":true},{"name":"documentation","type":"Documentation","isMany":true},{"name":"extensionDefinitions","type":"ExtensionDefinition","isMany":true,"isReference":true},{"name":"extensionElements","type":"ExtensionElements"}]},{"name":"Extension","properties":[{"name":"mustUnderstand","default":false,"isAttr":true,"type":"Boolean"},{"name":"definition","type":"ExtensionDefinition","isAttr":true,"isReference":true}]},{"name":"ExtensionDefinition","properties":[{"name":"name","isAttr":true,"type":"String"},{"name":"extensionAttributeDefinitions","type":"ExtensionAttributeDefinition","isMany":true}]},{"name":"ExtensionAttributeDefinition","properties":[{"name":"name","isAttr":true,"type":"String"},{"name":"type","isAttr":true,"type":"String"},{"name":"isReference","default":false,"isAttr":true,"type":"Boolean"},{"name":"extensionDefinition","type":"ExtensionDefinition","isAttr":true,"isReference":true}]},{"name":"ExtensionElements","properties":[{"name":"valueRef","isAttr":true,"isReference":true,"type":"Element"},{"name":"values","type":"Element","isMany":true},{"name":"extensionAttributeDefinition","type":"ExtensionAttributeDefinition","isAttr":true,"isReference":true}]},{"name":"Documentation","superClass":["BaseElement"],"properties":[{"name":"text","type":"String","isBody":true},{"name":"textFormat","default":"text/plain","isAttr":true,"type":"String"}]},{"name":"Event","isAbstract":true,"superClass":["FlowNode","InteractionNode"],"properties":[{"name":"properties","type":"Property","isMany":true}]},{"name":"IntermediateCatchEvent","superClass":["CatchEvent"]},{"name":"IntermediateThrowEvent","superClass":["ThrowEvent"]},{"name":"EndEvent","superClass":["ThrowEvent"]},{"name":"StartEvent","superClass":["CatchEvent"],"properties":[{"name":"isInterrupting","default":true,"isAttr":true,"type":"Boolean"}]},{"name":"ThrowEvent","isAbstract":true,"superClass":["Event"],"properties":[{"name":"dataInputs","type":"DataInput","isMany":true},{"name":"dataInputAssociations","type":"DataInputAssociation","isMany":true},{"name":"inputSet","type":"InputSet"},{"name":"eventDefinitions","type":"EventDefinition","isMany":true},{"name":"eventDefinitionRef","type":"EventDefinition","isMany":true,"isReference":true}]},{"name":"CatchEvent","isAbstract":true,"superClass":["Event"],"properties":[{"name":"parallelMultiple","isAttr":true,"type":"Boolean","default":false},{"name":"dataOutputs","type":"DataOutput","isMany":true},{"name":"dataOutputAssociations","type":"DataOutputAssociation","isMany":true},{"name":"outputSet","type":"OutputSet"},{"name":"eventDefinitions","type":"EventDefinition","isMany":true},{"name":"eventDefinitionRef","type":"EventDefinition","isMany":true,"isReference":true}]},{"name":"BoundaryEvent","superClass":["CatchEvent"],"properties":[{"name":"cancelActivity","default":true,"isAttr":true,"type":"Boolean"},{"name":"attachedToRef","type":"Activity","isAttr":true,"isReference":true}]},{"name":"EventDefinition","isAbstract":true,"superClass":["RootElement"]},{"name":"CancelEventDefinition","superClass":["EventDefinition"]},{"name":"ErrorEventDefinition","superClass":["EventDefinition"],"properties":[{"name":"errorRef","type":"Error","isAttr":true,"isReference":true}]},{"name":"TerminateEventDefinition","superClass":["EventDefinition"]},{"name":"EscalationEventDefinition","superClass":["EventDefinition"],"properties":[{"name":"escalationRef","type":"Escalation","isAttr":true,"isReference":true}]},{"name":"Escalation","properties":[{"name":"structureRef","type":"ItemDefinition","isAttr":true,"isReference":true},{"name":"name","isAttr":true,"type":"String"},{"name":"escalationCode","isAttr":true,"type":"String"}],"superClass":["RootElement"]},{"name":"CompensateEventDefinition","superClass":["EventDefinition"],"properties":[{"name":"waitForCompletion","isAttr":true,"type":"Boolean","default":true},{"name":"activityRef","type":"Activity","isAttr":true,"isReference":true}]},{"name":"TimerEventDefinition","superClass":["EventDefinition"],"properties":[{"name":"timeDate","type":"Expression","xml":{"serialize":"xsi:type"}},{"name":"timeCycle","type":"Expression","xml":{"serialize":"xsi:type"}},{"name":"timeDuration","type":"Expression","xml":{"serialize":"xsi:type"}}]},{"name":"LinkEventDefinition","superClass":["EventDefinition"],"properties":[{"name":"name","isAttr":true,"type":"String"},{"name":"target","type":"LinkEventDefinition","isAttr":true,"isReference":true},{"name":"source","type":"LinkEventDefinition","isMany":true,"isReference":true}]},{"name":"MessageEventDefinition","superClass":["EventDefinition"],"properties":[{"name":"messageRef","type":"Message","isAttr":true,"isReference":true},{"name":"operationRef","type":"Operation","isAttr":true,"isReference":true}]},{"name":"ConditionalEventDefinition","superClass":["EventDefinition"],"properties":[{"name":"condition","type":"Expression","xml":{"serialize":"xsi:type"}}]},{"name":"SignalEventDefinition","superClass":["EventDefinition"],"properties":[{"name":"signalRef","type":"Signal","isAttr":true,"isReference":true}]},{"name":"Signal","superClass":["RootElement"],"properties":[{"name":"structureRef","type":"ItemDefinition","isAttr":true,"isReference":true},{"name":"name","isAttr":true,"type":"String"}]},{"name":"ImplicitThrowEvent","superClass":["ThrowEvent"]},{"name":"DataState","superClass":["BaseElement"],"properties":[{"name":"name","isAttr":true,"type":"String"}]},{"name":"ItemAwareElement","superClass":["BaseElement"],"properties":[{"name":"itemSubjectRef","type":"ItemDefinition","isAttr":true,"isReference":true},{"name":"dataState","type":"DataState"}]},{"name":"DataAssociation","superClass":["BaseElement"],"properties":[{"name":"assignment","type":"Assignment","isMany":true},{"name":"sourceRef","type":"ItemAwareElement","isMany":true,"isReference":true},{"name":"targetRef","type":"ItemAwareElement","isReference":true},{"name":"transformation","type":"FormalExpression","xml":{"serialize":"property"}}]},{"name":"DataInput","superClass":["ItemAwareElement"],"properties":[{"name":"name","isAttr":true,"type":"String"},{"name":"isCollection","default":false,"isAttr":true,"type":"Boolean"},{"name":"inputSetRef","type":"InputSet","isVirtual":true,"isMany":true,"isReference":true},{"name":"inputSetWithOptional","type":"InputSet","isVirtual":true,"isMany":true,"isReference":true},{"name":"inputSetWithWhileExecuting","type":"InputSet","isVirtual":true,"isMany":true,"isReference":true}]},{"name":"DataOutput","superClass":["ItemAwareElement"],"properties":[{"name":"name","isAttr":true,"type":"String"},{"name":"isCollection","default":false,"isAttr":true,"type":"Boolean"},{"name":"outputSetRef","type":"OutputSet","isVirtual":true,"isMany":true,"isReference":true},{"name":"outputSetWithOptional","type":"OutputSet","isVirtual":true,"isMany":true,"isReference":true},{"name":"outputSetWithWhileExecuting","type":"OutputSet","isVirtual":true,"isMany":true,"isReference":true}]},{"name":"InputSet","superClass":["BaseElement"],"properties":[{"name":"name","isAttr":true,"type":"String"},{"name":"dataInputRefs","type":"DataInput","isMany":true,"isReference":true},{"name":"optionalInputRefs","type":"DataInput","isMany":true,"isReference":true},{"name":"whileExecutingInputRefs","type":"DataInput","isMany":true,"isReference":true},{"name":"outputSetRefs","type":"OutputSet","isMany":true,"isReference":true}]},{"name":"OutputSet","superClass":["BaseElement"],"properties":[{"name":"dataOutputRefs","type":"DataOutput","isMany":true,"isReference":true},{"name":"name","isAttr":true,"type":"String"},{"name":"inputSetRefs","type":"InputSet","isMany":true,"isReference":true},{"name":"optionalOutputRefs","type":"DataOutput","isMany":true,"isReference":true},{"name":"whileExecutingOutputRefs","type":"DataOutput","isMany":true,"isReference":true}]},{"name":"Property","superClass":["ItemAwareElement"],"properties":[{"name":"name","isAttr":true,"type":"String"}]},{"name":"DataInputAssociation","superClass":["DataAssociation"]},{"name":"DataOutputAssociation","superClass":["DataAssociation"]},{"name":"InputOutputSpecification","superClass":["BaseElement"],"properties":[{"name":"dataInputs","type":"DataInput","isMany":true},{"name":"dataOutputs","type":"DataOutput","isMany":true},{"name":"inputSets","type":"InputSet","isMany":true},{"name":"outputSets","type":"OutputSet","isMany":true}]},{"name":"DataObject","superClass":["FlowElement","ItemAwareElement"],"properties":[{"name":"isCollection","default":false,"isAttr":true,"type":"Boolean"}]},{"name":"InputOutputBinding","properties":[{"name":"inputDataRef","type":"InputSet","isAttr":true,"isReference":true},{"name":"outputDataRef","type":"OutputSet","isAttr":true,"isReference":true},{"name":"operationRef","type":"Operation","isAttr":true,"isReference":true}]},{"name":"Assignment","superClass":["BaseElement"],"properties":[{"name":"from","type":"Expression","xml":{"serialize":"xsi:type"}},{"name":"to","type":"Expression","xml":{"serialize":"xsi:type"}}]},{"name":"DataStore","superClass":["RootElement","ItemAwareElement"],"properties":[{"name":"name","isAttr":true,"type":"String"},{"name":"capacity","isAttr":true,"type":"Integer"},{"name":"isUnlimited","default":true,"isAttr":true,"type":"Boolean"}]},{"name":"DataStoreReference","superClass":["ItemAwareElement","FlowElement"],"properties":[{"name":"dataStoreRef","type":"DataStore","isAttr":true,"isReference":true}]},{"name":"DataObjectReference","superClass":["ItemAwareElement","FlowElement"],"properties":[{"name":"dataObjectRef","type":"DataObject","isAttr":true,"isReference":true}]},{"name":"ConversationLink","superClass":["BaseElement"],"properties":[{"name":"sourceRef","type":"InteractionNode","isAttr":true,"isReference":true},{"name":"targetRef","type":"InteractionNode","isAttr":true,"isReference":true},{"name":"name","isAttr":true,"type":"String"}]},{"name":"ConversationAssociation","superClass":["BaseElement"],"properties":[{"name":"innerConversationNodeRef","type":"ConversationNode","isAttr":true,"isReference":true},{"name":"outerConversationNodeRef","type":"ConversationNode","isAttr":true,"isReference":true}]},{"name":"CallConversation","superClass":["ConversationNode"],"properties":[{"name":"calledCollaborationRef","type":"Collaboration","isAttr":true,"isReference":true},{"name":"participantAssociations","type":"ParticipantAssociation","isMany":true}]},{"name":"Conversation","superClass":["ConversationNode"]},{"name":"SubConversation","superClass":["ConversationNode"],"properties":[{"name":"conversationNodes","type":"ConversationNode","isMany":true}]},{"name":"ConversationNode","isAbstract":true,"superClass":["InteractionNode","BaseElement"],"properties":[{"name":"name","isAttr":true,"type":"String"},{"name":"participantRefs","type":"Participant","isMany":true,"isReference":true},{"name":"messageFlowRefs","type":"MessageFlow","isMany":true,"isReference":true},{"name":"correlationKeys","type":"CorrelationKey","isMany":true}]},{"name":"GlobalConversation","superClass":["Collaboration"]},{"name":"PartnerEntity","superClass":["RootElement"],"properties":[{"name":"name","isAttr":true,"type":"String"},{"name":"participantRef","type":"Participant","isMany":true,"isReference":true}]},{"name":"PartnerRole","superClass":["RootElement"],"properties":[{"name":"name","isAttr":true,"type":"String"},{"name":"participantRef","type":"Participant","isMany":true,"isReference":true}]},{"name":"CorrelationProperty","superClass":["RootElement"],"properties":[{"name":"correlationPropertyRetrievalExpression","type":"CorrelationPropertyRetrievalExpression","isMany":true},{"name":"name","isAttr":true,"type":"String"},{"name":"type","type":"ItemDefinition","isAttr":true,"isReference":true}]},{"name":"Error","superClass":["RootElement"],"properties":[{"name":"structureRef","type":"ItemDefinition","isAttr":true,"isReference":true},{"name":"name","isAttr":true,"type":"String"},{"name":"errorCode","isAttr":true,"type":"String"}]},{"name":"CorrelationKey","superClass":["BaseElement"],"properties":[{"name":"correlationPropertyRef","type":"CorrelationProperty","isMany":true,"isReference":true},{"name":"name","isAttr":true,"type":"String"}]},{"name":"Expression","superClass":["BaseElement"],"isAbstract":false,"properties":[{"name":"body","type":"String","isBody":true}]},{"name":"FormalExpression","superClass":["Expression"],"properties":[{"name":"language","isAttr":true,"type":"String"},{"name":"evaluatesToTypeRef","type":"ItemDefinition","isAttr":true,"isReference":true}]},{"name":"Message","superClass":["RootElement"],"properties":[{"name":"name","isAttr":true,"type":"String"},{"name":"itemRef","type":"ItemDefinition","isAttr":true,"isReference":true}]},{"name":"ItemDefinition","superClass":["RootElement"],"properties":[{"name":"itemKind","type":"ItemKind","isAttr":true},{"name":"structureRef","type":"String","isAttr":true},{"name":"isCollection","default":false,"isAttr":true,"type":"Boolean"},{"name":"import","type":"Import","isAttr":true,"isReference":true}]},{"name":"FlowElement","isAbstract":true,"superClass":["BaseElement"],"properties":[{"name":"name","isAttr":true,"type":"String"},{"name":"auditing","type":"Auditing"},{"name":"monitoring","type":"Monitoring"},{"name":"categoryValueRef","type":"CategoryValue","isMany":true,"isReference":true}]},{"name":"SequenceFlow","superClass":["FlowElement"],"properties":[{"name":"isImmediate","isAttr":true,"type":"Boolean"},{"name":"conditionExpression","type":"Expression","xml":{"serialize":"xsi:type"}},{"name":"sourceRef","type":"FlowNode","isAttr":true,"isReference":true},{"name":"targetRef","type":"FlowNode","isAttr":true,"isReference":true}]},{"name":"FlowElementsContainer","isAbstract":true,"superClass":["BaseElement"],"properties":[{"name":"laneSets","type":"LaneSet","isMany":true},{"name":"flowElements","type":"FlowElement","isMany":true}]},{"name":"CallableElement","isAbstract":true,"superClass":["RootElement"],"properties":[{"name":"name","isAttr":true,"type":"String"},{"name":"ioSpecification","type":"InputOutputSpecification","xml":{"serialize":"property"}},{"name":"supportedInterfaceRef","type":"Interface","isMany":true,"isReference":true},{"name":"ioBinding","type":"InputOutputBinding","isMany":true,"xml":{"serialize":"property"}}]},{"name":"FlowNode","isAbstract":true,"superClass":["FlowElement"],"properties":[{"name":"incoming","type":"SequenceFlow","isMany":true,"isReference":true},{"name":"outgoing","type":"SequenceFlow","isMany":true,"isReference":true},{"name":"lanes","type":"Lane","isVirtual":true,"isMany":true,"isReference":true}]},{"name":"CorrelationPropertyRetrievalExpression","superClass":["BaseElement"],"properties":[{"name":"messagePath","type":"FormalExpression"},{"name":"messageRef","type":"Message","isAttr":true,"isReference":true}]},{"name":"CorrelationPropertyBinding","superClass":["BaseElement"],"properties":[{"name":"dataPath","type":"FormalExpression"},{"name":"correlationPropertyRef","type":"CorrelationProperty","isAttr":true,"isReference":true}]},{"name":"Resource","superClass":["RootElement"],"properties":[{"name":"name","isAttr":true,"type":"String"},{"name":"resourceParameters","type":"ResourceParameter","isMany":true}]},{"name":"ResourceParameter","superClass":["BaseElement"],"properties":[{"name":"name","isAttr":true,"type":"String"},{"name":"isRequired","isAttr":true,"type":"Boolean"},{"name":"type","type":"ItemDefinition","isAttr":true,"isReference":true}]},{"name":"CorrelationSubscription","superClass":["BaseElement"],"properties":[{"name":"correlationKeyRef","type":"CorrelationKey","isAttr":true,"isReference":true},{"name":"correlationPropertyBinding","type":"CorrelationPropertyBinding","isMany":true}]},{"name":"MessageFlow","superClass":["BaseElement"],"properties":[{"name":"name","isAttr":true,"type":"String"},{"name":"sourceRef","type":"InteractionNode","isAttr":true,"isReference":true},{"name":"targetRef","type":"InteractionNode","isAttr":true,"isReference":true},{"name":"messageRef","type":"Message","isAttr":true,"isReference":true}]},{"name":"MessageFlowAssociation","superClass":["BaseElement"],"properties":[{"name":"innerMessageFlowRef","type":"MessageFlow","isAttr":true,"isReference":true},{"name":"outerMessageFlowRef","type":"MessageFlow","isAttr":true,"isReference":true}]},{"name":"InteractionNode","isAbstract":true,"properties":[{"name":"incomingConversationLinks","type":"ConversationLink","isVirtual":true,"isMany":true,"isReference":true},{"name":"outgoingConversationLinks","type":"ConversationLink","isVirtual":true,"isMany":true,"isReference":true}]},{"name":"Participant","superClass":["InteractionNode","BaseElement"],"properties":[{"name":"name","isAttr":true,"type":"String"},{"name":"interfaceRef","type":"Interface","isMany":true,"isReference":true},{"name":"participantMultiplicity","type":"ParticipantMultiplicity"},{"name":"endPointRefs","type":"EndPoint","isMany":true,"isReference":true},{"name":"processRef","type":"Process","isAttr":true,"isReference":true}]},{"name":"ParticipantAssociation","superClass":["BaseElement"],"properties":[{"name":"innerParticipantRef","type":"Participant","isAttr":true,"isReference":true},{"name":"outerParticipantRef","type":"Participant","isAttr":true,"isReference":true}]},{"name":"ParticipantMultiplicity","properties":[{"name":"minimum","default":0,"isAttr":true,"type":"Integer"},{"name":"maximum","default":1,"isAttr":true,"type":"Integer"}],"superClass":["BaseElement"]},{"name":"Collaboration","superClass":["RootElement"],"properties":[{"name":"name","isAttr":true,"type":"String"},{"name":"isClosed","isAttr":true,"type":"Boolean"},{"name":"participants","type":"Participant","isMany":true},{"name":"messageFlows","type":"MessageFlow","isMany":true},{"name":"artifacts","type":"Artifact","isMany":true},{"name":"conversations","type":"ConversationNode","isMany":true},{"name":"conversationAssociations","type":"ConversationAssociation"},{"name":"participantAssociations","type":"ParticipantAssociation","isMany":true},{"name":"messageFlowAssociations","type":"MessageFlowAssociation","isMany":true},{"name":"correlationKeys","type":"CorrelationKey","isMany":true},{"name":"choreographyRef","type":"Choreography","isMany":true,"isReference":true},{"name":"conversationLinks","type":"ConversationLink","isMany":true}]},{"name":"ChoreographyActivity","isAbstract":true,"superClass":["FlowNode"],"properties":[{"name":"participantRefs","type":"Participant","isMany":true,"isReference":true},{"name":"initiatingParticipantRef","type":"Participant","isAttr":true,"isReference":true},{"name":"correlationKeys","type":"CorrelationKey","isMany":true},{"name":"loopType","type":"ChoreographyLoopType","default":"None","isAttr":true}]},{"name":"CallChoreography","superClass":["ChoreographyActivity"],"properties":[{"name":"calledChoreographyRef","type":"Choreography","isAttr":true,"isReference":true},{"name":"participantAssociations","type":"ParticipantAssociation","isMany":true}]},{"name":"SubChoreography","superClass":["ChoreographyActivity","FlowElementsContainer"],"properties":[{"name":"artifacts","type":"Artifact","isMany":true}]},{"name":"ChoreographyTask","superClass":["ChoreographyActivity"],"properties":[{"name":"messageFlowRef","type":"MessageFlow","isMany":true,"isReference":true}]},{"name":"Choreography","superClass":["FlowElementsContainer","Collaboration"]},{"name":"GlobalChoreographyTask","superClass":["Choreography"],"properties":[{"name":"initiatingParticipantRef","type":"Participant","isAttr":true,"isReference":true}]},{"name":"TextAnnotation","superClass":["Artifact"],"properties":[{"name":"text","type":"String"},{"name":"textFormat","default":"text/plain","isAttr":true,"type":"String"}]},{"name":"Group","superClass":["Artifact"],"properties":[{"name":"categoryValueRef","type":"CategoryValue","isAttr":true,"isReference":true}]},{"name":"Association","superClass":["Artifact"],"properties":[{"name":"associationDirection","type":"AssociationDirection","isAttr":true},{"name":"sourceRef","type":"BaseElement","isAttr":true,"isReference":true},{"name":"targetRef","type":"BaseElement","isAttr":true,"isReference":true}]},{"name":"Category","superClass":["RootElement"],"properties":[{"name":"categoryValue","type":"CategoryValue","isMany":true},{"name":"name","isAttr":true,"type":"String"}]},{"name":"Artifact","isAbstract":true,"superClass":["BaseElement"]},{"name":"CategoryValue","superClass":["BaseElement"],"properties":[{"name":"categorizedFlowElements","type":"FlowElement","isVirtual":true,"isMany":true,"isReference":true},{"name":"value","isAttr":true,"type":"String"}]},{"name":"Activity","isAbstract":true,"superClass":["FlowNode"],"properties":[{"name":"isForCompensation","default":false,"isAttr":true,"type":"Boolean"},{"name":"default","type":"SequenceFlow","isAttr":true,"isReference":true},{"name":"ioSpecification","type":"InputOutputSpecification","xml":{"serialize":"property"}},{"name":"boundaryEventRefs","type":"BoundaryEvent","isMany":true,"isReference":true},{"name":"properties","type":"Property","isMany":true},{"name":"dataInputAssociations","type":"DataInputAssociation","isMany":true},{"name":"dataOutputAssociations","type":"DataOutputAssociation","isMany":true},{"name":"startQuantity","default":1,"isAttr":true,"type":"Integer"},{"name":"resources","type":"ResourceRole","isMany":true},{"name":"completionQuantity","default":1,"isAttr":true,"type":"Integer"},{"name":"loopCharacteristics","type":"LoopCharacteristics"}]},{"name":"ServiceTask","superClass":["Task"],"properties":[{"name":"implementation","isAttr":true,"type":"String"},{"name":"operationRef","type":"Operation","isAttr":true,"isReference":true}]},{"name":"SubProcess","superClass":["Activity","FlowElementsContainer","InteractionNode"],"properties":[{"name":"triggeredByEvent","default":false,"isAttr":true,"type":"Boolean"},{"name":"artifacts","type":"Artifact","isMany":true}]},{"name":"LoopCharacteristics","isAbstract":true,"superClass":["BaseElement"]},{"name":"MultiInstanceLoopCharacteristics","superClass":["LoopCharacteristics"],"properties":[{"name":"isSequential","default":false,"isAttr":true,"type":"Boolean"},{"name":"behavior","type":"MultiInstanceBehavior","default":"All","isAttr":true},{"name":"loopCardinality","type":"Expression","xml":{"serialize":"xsi:type"}},{"name":"loopDataInputRef","type":"ItemAwareElement","isReference":true},{"name":"loopDataOutputRef","type":"ItemAwareElement","isReference":true},{"name":"inputDataItem","type":"DataInput","xml":{"serialize":"property"}},{"name":"outputDataItem","type":"DataOutput","xml":{"serialize":"property"}},{"name":"complexBehaviorDefinition","type":"ComplexBehaviorDefinition","isMany":true},{"name":"completionCondition","type":"Expression","xml":{"serialize":"xsi:type"}},{"name":"oneBehaviorEventRef","type":"EventDefinition","isAttr":true,"isReference":true},{"name":"noneBehaviorEventRef","type":"EventDefinition","isAttr":true,"isReference":true}]},{"name":"StandardLoopCharacteristics","superClass":["LoopCharacteristics"],"properties":[{"name":"testBefore","default":false,"isAttr":true,"type":"Boolean"},{"name":"loopCondition","type":"Expression","xml":{"serialize":"xsi:type"}},{"name":"loopMaximum","type":"Integer","isAttr":true}]},{"name":"CallActivity","superClass":["Activity"],"properties":[{"name":"calledElement","type":"String","isAttr":true}]},{"name":"Task","superClass":["Activity","InteractionNode"]},{"name":"SendTask","superClass":["Task"],"properties":[{"name":"implementation","isAttr":true,"type":"String"},{"name":"operationRef","type":"Operation","isAttr":true,"isReference":true},{"name":"messageRef","type":"Message","isAttr":true,"isReference":true}]},{"name":"ReceiveTask","superClass":["Task"],"properties":[{"name":"implementation","isAttr":true,"type":"String"},{"name":"instantiate","default":false,"isAttr":true,"type":"Boolean"},{"name":"operationRef","type":"Operation","isAttr":true,"isReference":true},{"name":"messageRef","type":"Message","isAttr":true,"isReference":true}]},{"name":"ScriptTask","superClass":["Task"],"properties":[{"name":"scriptFormat","isAttr":true,"type":"String"},{"name":"script","type":"String"}]},{"name":"BusinessRuleTask","superClass":["Task"],"properties":[{"name":"implementation","isAttr":true,"type":"String"}]},{"name":"AdHocSubProcess","superClass":["SubProcess"],"properties":[{"name":"completionCondition","type":"Expression","xml":{"serialize":"xsi:type"}},{"name":"ordering","type":"AdHocOrdering","isAttr":true},{"name":"cancelRemainingInstances","default":true,"isAttr":true,"type":"Boolean"}]},{"name":"Transaction","superClass":["SubProcess"],"properties":[{"name":"protocol","isAttr":true,"type":"String"},{"name":"method","isAttr":true,"type":"String"}]},{"name":"GlobalScriptTask","superClass":["GlobalTask"],"properties":[{"name":"scriptLanguage","isAttr":true,"type":"String"},{"name":"script","isAttr":true,"type":"String"}]},{"name":"GlobalBusinessRuleTask","superClass":["GlobalTask"],"properties":[{"name":"implementation","isAttr":true,"type":"String"}]},{"name":"ComplexBehaviorDefinition","superClass":["BaseElement"],"properties":[{"name":"condition","type":"FormalExpression"},{"name":"event","type":"ImplicitThrowEvent"}]},{"name":"ResourceRole","superClass":["BaseElement"],"properties":[{"name":"resourceRef","type":"Resource","isReference":true},{"name":"resourceParameterBindings","type":"ResourceParameterBinding","isMany":true},{"name":"resourceAssignmentExpression","type":"ResourceAssignmentExpression"},{"name":"name","isAttr":true,"type":"String"}]},{"name":"ResourceParameterBinding","properties":[{"name":"expression","type":"Expression","xml":{"serialize":"xsi:type"}},{"name":"parameterRef","type":"ResourceParameter","isAttr":true,"isReference":true}],"superClass":["BaseElement"]},{"name":"ResourceAssignmentExpression","properties":[{"name":"expression","type":"Expression","xml":{"serialize":"xsi:type"}}],"superClass":["BaseElement"]},{"name":"Import","properties":[{"name":"importType","isAttr":true,"type":"String"},{"name":"location","isAttr":true,"type":"String"},{"name":"namespace","isAttr":true,"type":"String"}]},{"name":"Definitions","superClass":["BaseElement"],"properties":[{"name":"name","isAttr":true,"type":"String"},{"name":"targetNamespace","isAttr":true,"type":"String"},{"name":"expressionLanguage","default":"http://www.w3.org/1999/XPath","isAttr":true,"type":"String"},{"name":"typeLanguage","default":"http://www.w3.org/2001/XMLSchema","isAttr":true,"type":"String"},{"name":"imports","type":"Import","isMany":true},{"name":"extensions","type":"Extension","isMany":true},{"name":"rootElements","type":"RootElement","isMany":true},{"name":"diagrams","isMany":true,"type":"bpmndi:BPMNDiagram"},{"name":"exporter","isAttr":true,"type":"String"},{"name":"relationships","type":"Relationship","isMany":true},{"name":"exporterVersion","isAttr":true,"type":"String"}]}];
+ var enumerations = [{"name":"ProcessType","literalValues":[{"name":"None"},{"name":"Public"},{"name":"Private"}]},{"name":"GatewayDirection","literalValues":[{"name":"Unspecified"},{"name":"Converging"},{"name":"Diverging"},{"name":"Mixed"}]},{"name":"EventBasedGatewayType","literalValues":[{"name":"Parallel"},{"name":"Exclusive"}]},{"name":"RelationshipDirection","literalValues":[{"name":"None"},{"name":"Forward"},{"name":"Backward"},{"name":"Both"}]},{"name":"ItemKind","literalValues":[{"name":"Physical"},{"name":"Information"}]},{"name":"ChoreographyLoopType","literalValues":[{"name":"None"},{"name":"Standard"},{"name":"MultiInstanceSequential"},{"name":"MultiInstanceParallel"}]},{"name":"AssociationDirection","literalValues":[{"name":"None"},{"name":"One"},{"name":"Both"}]},{"name":"MultiInstanceBehavior","literalValues":[{"name":"None"},{"name":"One"},{"name":"All"},{"name":"Complex"}]},{"name":"AdHocOrdering","literalValues":[{"name":"Parallel"},{"name":"Sequential"}]}];
+ var prefix$1 = "bpmn";
+ var xml = {"tagAlias":"lowerCase","typePrefix":"t"};
+ var BpmnPackage = {
+ name: name,
+ uri: uri,
+ associations: associations,
+ types: types$1,
+ enumerations: enumerations,
+ prefix: prefix$1,
+ xml: xml
+ };
+
+ var name$1 = "BPMNDI";
+ var uri$1 = "http://www.omg.org/spec/BPMN/20100524/DI";
+ var types$2 = [{"name":"BPMNDiagram","properties":[{"name":"plane","type":"BPMNPlane","redefines":"di:Diagram#rootElement"},{"name":"labelStyle","type":"BPMNLabelStyle","isMany":true}],"superClass":["di:Diagram"]},{"name":"BPMNPlane","properties":[{"name":"bpmnElement","isAttr":true,"isReference":true,"type":"bpmn:BaseElement","redefines":"di:DiagramElement#modelElement"}],"superClass":["di:Plane"]},{"name":"BPMNShape","properties":[{"name":"bpmnElement","isAttr":true,"isReference":true,"type":"bpmn:BaseElement","redefines":"di:DiagramElement#modelElement"},{"name":"isHorizontal","isAttr":true,"type":"Boolean"},{"name":"isExpanded","isAttr":true,"type":"Boolean"},{"name":"isMarkerVisible","isAttr":true,"type":"Boolean"},{"name":"label","type":"BPMNLabel"},{"name":"isMessageVisible","isAttr":true,"type":"Boolean"},{"name":"participantBandKind","type":"ParticipantBandKind","isAttr":true},{"name":"choreographyActivityShape","type":"BPMNShape","isAttr":true,"isReference":true}],"superClass":["di:LabeledShape"]},{"name":"BPMNEdge","properties":[{"name":"label","type":"BPMNLabel"},{"name":"bpmnElement","isAttr":true,"isReference":true,"type":"bpmn:BaseElement","redefines":"di:DiagramElement#modelElement"},{"name":"sourceElement","isAttr":true,"isReference":true,"type":"di:DiagramElement","redefines":"di:Edge#source"},{"name":"targetElement","isAttr":true,"isReference":true,"type":"di:DiagramElement","redefines":"di:Edge#target"},{"name":"messageVisibleKind","type":"MessageVisibleKind","isAttr":true,"default":"initiating"}],"superClass":["di:LabeledEdge"]},{"name":"BPMNLabel","properties":[{"name":"labelStyle","type":"BPMNLabelStyle","isAttr":true,"isReference":true,"redefines":"di:DiagramElement#style"}],"superClass":["di:Label"]},{"name":"BPMNLabelStyle","properties":[{"name":"font","type":"dc:Font"}],"superClass":["di:Style"]}];
+ var enumerations$1 = [{"name":"ParticipantBandKind","literalValues":[{"name":"top_initiating"},{"name":"middle_initiating"},{"name":"bottom_initiating"},{"name":"top_non_initiating"},{"name":"middle_non_initiating"},{"name":"bottom_non_initiating"}]},{"name":"MessageVisibleKind","literalValues":[{"name":"initiating"},{"name":"non_initiating"}]}];
+ var associations$1 = [];
+ var prefix$2 = "bpmndi";
+ var BpmnDiPackage = {
+ name: name$1,
+ uri: uri$1,
+ types: types$2,
+ enumerations: enumerations$1,
+ associations: associations$1,
+ prefix: prefix$2
+ };
+
+ var name$2 = "DC";
+ var uri$2 = "http://www.omg.org/spec/DD/20100524/DC";
+ var types$3 = [{"name":"Boolean"},{"name":"Integer"},{"name":"Real"},{"name":"String"},{"name":"Font","properties":[{"name":"name","type":"String","isAttr":true},{"name":"size","type":"Real","isAttr":true},{"name":"isBold","type":"Boolean","isAttr":true},{"name":"isItalic","type":"Boolean","isAttr":true},{"name":"isUnderline","type":"Boolean","isAttr":true},{"name":"isStrikeThrough","type":"Boolean","isAttr":true}]},{"name":"Point","properties":[{"name":"x","type":"Real","default":"0","isAttr":true},{"name":"y","type":"Real","default":"0","isAttr":true}]},{"name":"Bounds","properties":[{"name":"x","type":"Real","default":"0","isAttr":true},{"name":"y","type":"Real","default":"0","isAttr":true},{"name":"width","type":"Real","isAttr":true},{"name":"height","type":"Real","isAttr":true}]}];
+ var prefix$3 = "dc";
+ var associations$2 = [];
+ var DcPackage = {
+ name: name$2,
+ uri: uri$2,
+ types: types$3,
+ prefix: prefix$3,
+ associations: associations$2
+ };
+
+ var name$3 = "DI";
+ var uri$3 = "http://www.omg.org/spec/DD/20100524/DI";
+ var types$4 = [{"name":"DiagramElement","isAbstract":true,"properties":[{"name":"id","type":"String","isAttr":true,"isId":true},{"name":"extension","type":"Extension"},{"name":"owningDiagram","type":"Diagram","isReadOnly":true,"isVirtual":true,"isReference":true},{"name":"owningElement","type":"DiagramElement","isReadOnly":true,"isVirtual":true,"isReference":true},{"name":"modelElement","isReadOnly":true,"isVirtual":true,"isReference":true,"type":"Element"},{"name":"style","type":"Style","isReadOnly":true,"isVirtual":true,"isReference":true},{"name":"ownedElement","type":"DiagramElement","isReadOnly":true,"isVirtual":true,"isMany":true}]},{"name":"Node","isAbstract":true,"superClass":["DiagramElement"]},{"name":"Edge","isAbstract":true,"superClass":["DiagramElement"],"properties":[{"name":"source","type":"DiagramElement","isReadOnly":true,"isVirtual":true,"isReference":true},{"name":"target","type":"DiagramElement","isReadOnly":true,"isVirtual":true,"isReference":true},{"name":"waypoint","isUnique":false,"isMany":true,"type":"dc:Point","xml":{"serialize":"xsi:type"}}]},{"name":"Diagram","isAbstract":true,"properties":[{"name":"id","type":"String","isAttr":true,"isId":true},{"name":"rootElement","type":"DiagramElement","isReadOnly":true,"isVirtual":true},{"name":"name","isAttr":true,"type":"String"},{"name":"documentation","isAttr":true,"type":"String"},{"name":"resolution","isAttr":true,"type":"Real"},{"name":"ownedStyle","type":"Style","isReadOnly":true,"isVirtual":true,"isMany":true}]},{"name":"Shape","isAbstract":true,"superClass":["Node"],"properties":[{"name":"bounds","type":"dc:Bounds"}]},{"name":"Plane","isAbstract":true,"superClass":["Node"],"properties":[{"name":"planeElement","type":"DiagramElement","subsettedProperty":"DiagramElement-ownedElement","isMany":true}]},{"name":"LabeledEdge","isAbstract":true,"superClass":["Edge"],"properties":[{"name":"ownedLabel","type":"Label","isReadOnly":true,"subsettedProperty":"DiagramElement-ownedElement","isVirtual":true,"isMany":true}]},{"name":"LabeledShape","isAbstract":true,"superClass":["Shape"],"properties":[{"name":"ownedLabel","type":"Label","isReadOnly":true,"subsettedProperty":"DiagramElement-ownedElement","isVirtual":true,"isMany":true}]},{"name":"Label","isAbstract":true,"superClass":["Node"],"properties":[{"name":"bounds","type":"dc:Bounds"}]},{"name":"Style","isAbstract":true,"properties":[{"name":"id","type":"String","isAttr":true,"isId":true}]},{"name":"Extension","properties":[{"name":"values","type":"Element","isMany":true}]}];
+ var associations$3 = [];
+ var prefix$4 = "di";
+ var xml$1 = {"tagAlias":"lowerCase"};
+ var DiPackage = {
+ name: name$3,
+ uri: uri$3,
+ types: types$4,
+ associations: associations$3,
+ prefix: prefix$4,
+ xml: xml$1
+ };
+
+ var name$4 = "bpmn.io colors for BPMN";
+ var uri$4 = "http://bpmn.io/schema/bpmn/biocolor/1.0";
+ var prefix$5 = "bioc";
+ var types$5 = [{"name":"ColoredShape","extends":["bpmndi:BPMNShape"],"properties":[{"name":"stroke","isAttr":true,"type":"String"},{"name":"fill","isAttr":true,"type":"String"}]},{"name":"ColoredEdge","extends":["bpmndi:BPMNEdge"],"properties":[{"name":"stroke","isAttr":true,"type":"String"},{"name":"fill","isAttr":true,"type":"String"}]}];
+ var enumerations$2 = [];
+ var associations$4 = [];
+ var BiocPackage = {
+ name: name$4,
+ uri: uri$4,
+ prefix: prefix$5,
+ types: types$5,
+ enumerations: enumerations$2,
+ associations: associations$4
+ };
+
+ var packages = {
+ bpmn: BpmnPackage,
+ bpmndi: BpmnDiPackage,
+ dc: DcPackage,
+ di: DiPackage,
+ bioc: BiocPackage
+ };
+
+ function BpmnModdle$1(additionalPackages, options) {
+ var pks = assign({}, packages, additionalPackages);
+
+ return new BpmnModdle(pks, options);
+ }
+
+ function elementToString(e) {
+ if (!e) {
+ return '';
+ }
+
+ return '<' + e.$type + (e.id ? ' id="' + e.id : '') + '" />';
+ }
+
+ var diRefs = new objectRefs(
+ { name: 'bpmnElement', enumerable: true },
+ { name: 'di', configurable: true }
+ );
+
+ /**
+ * Returns true if an element has the given meta-model type
+ *
+ * @param {ModdleElement} element
+ * @param {String} type
+ *
+ * @return {Boolean}
+ */
+ function is(element, type) {
+ return element.$instanceOf(type);
+ }
+
+
+ /**
+ * Find a suitable display candidate for definitions where the DI does not
+ * correctly specify one.
+ */
+ function findDisplayCandidate(definitions) {
+ return find(definitions.rootElements, function(e) {
+ return is(e, 'bpmn:Process') || is(e, 'bpmn:Collaboration');
+ });
+ }
+
+
+ function BpmnTreeWalker(handler, translate) {
+
+ // list of containers already walked
+ var handledElements = {};
+
+ // list of elements to handle deferred to ensure
+ // prerequisites are drawn
+ var deferred = [];
+
+ // Helpers //////////////////////
+
+ function contextual(fn, ctx) {
+ return function(e) {
+ fn(e, ctx);
+ };
+ }
+
+ function handled(element) {
+ handledElements[element.id] = element;
+ }
+
+ function isHandled(element) {
+ return handledElements[element.id];
+ }
+
+ function visit(element, ctx) {
+
+ var gfx = element.gfx;
+
+ // avoid multiple rendering of elements
+ if (gfx) {
+ throw new Error(
+ translate('already rendered {element}', { element: elementToString(element) })
+ );
+ }
+
+ // call handler
+ return handler.element(element, ctx);
+ }
+
+ function visitRoot(element, diagram) {
+ return handler.root(element, diagram);
+ }
+
+ function visitIfDi(element, ctx) {
+
+ try {
+ var gfx = element.di && visit(element, ctx);
+
+ handled(element);
+
+ return gfx;
+ } catch (e) {
+ logError(e.message, { element: element, error: e });
+
+ console.error(translate('failed to import {element}', { element: elementToString(element) }));
+ console.error(e);
+ }
+ }
+
+ function logError(message, context) {
+ handler.error(message, context);
+ }
+
+ // DI handling //////////////////////
+
+ function registerDi(di) {
+ var bpmnElement = di.bpmnElement;
+
+ if (bpmnElement) {
+ if (bpmnElement.di) {
+ logError(
+ translate('multiple DI elements defined for {element}', {
+ element: elementToString(bpmnElement)
+ }),
+ { element: bpmnElement }
+ );
+ } else {
+ diRefs.bind(bpmnElement, 'di');
+ bpmnElement.di = di;
+ }
+ } else {
+ logError(
+ translate('no bpmnElement referenced in {element}', {
+ element: elementToString(di)
+ }),
+ { element: di }
+ );
+ }
+ }
+
+ function handleDiagram(diagram) {
+ handlePlane(diagram.plane);
+ }
+
+ function handlePlane(plane) {
+ registerDi(plane);
+
+ forEach(plane.planeElement, handlePlaneElement);
+ }
+
+ function handlePlaneElement(planeElement) {
+ registerDi(planeElement);
+ }
+
+
+ // Semantic handling //////////////////////
+
+ /**
+ * Handle definitions and return the rendered diagram (if any)
+ *
+ * @param {ModdleElement} definitions to walk and import
+ * @param {ModdleElement} [diagram] specific diagram to import and display
+ *
+ * @throws {Error} if no diagram to display could be found
+ */
+ function handleDefinitions(definitions, diagram) {
+ // make sure we walk the correct bpmnElement
+
+ var diagrams = definitions.diagrams;
+
+ if (diagram && diagrams.indexOf(diagram) === -1) {
+ throw new Error(translate('diagram not part of bpmn:Definitions'));
+ }
+
+ if (!diagram && diagrams && diagrams.length) {
+ diagram = diagrams[0];
+ }
+
+ // no diagram -> nothing to import
+ if (!diagram) {
+ throw new Error(translate('no diagram to display'));
+ }
+
+ // load DI from selected diagram only
+ handleDiagram(diagram);
+
+
+ var plane = diagram.plane;
+
+ if (!plane) {
+ throw new Error(translate(
+ 'no plane for {element}',
+ { element: elementToString(diagram) }
+ ));
+ }
+
+ var rootElement = plane.bpmnElement;
+
+ // ensure we default to a suitable display candidate (process or collaboration),
+ // even if non is specified in DI
+ if (!rootElement) {
+ rootElement = findDisplayCandidate(definitions);
+
+ if (!rootElement) {
+ throw new Error(translate('no process or collaboration to display'));
+ } else {
+
+ logError(
+ translate('correcting missing bpmnElement on {plane} to {rootElement}', {
+ plane: elementToString(plane),
+ rootElement: elementToString(rootElement)
+ })
+ );
+
+ // correct DI on the fly
+ plane.bpmnElement = rootElement;
+ registerDi(plane);
+ }
+ }
+
+
+ var ctx = visitRoot(rootElement, plane);
+
+ if (is(rootElement, 'bpmn:Process')) {
+ handleProcess(rootElement, ctx);
+ } else if (is(rootElement, 'bpmn:Collaboration')) {
+ handleCollaboration(rootElement, ctx);
+
+ // force drawing of everything not yet drawn that is part of the target DI
+ handleUnhandledProcesses(definitions.rootElements, ctx);
+ } else {
+ throw new Error(
+ translate('unsupported bpmnElement for {plane}: {rootElement}', {
+ plane: elementToString(plane),
+ rootElement: elementToString(rootElement)
+ })
+ );
+ }
+
+ // handle all deferred elements
+ handleDeferred(deferred);
+ }
+
+ function handleDeferred() {
+
+ var fn;
+
+ // drain deferred until empty
+ while (deferred.length) {
+ fn = deferred.shift();
+
+ fn();
+ }
+ }
+
+ function handleProcess(process, context) {
+ handleFlowElementsContainer(process, context);
+ handleIoSpecification(process.ioSpecification, context);
+
+ handleArtifacts(process.artifacts, context);
+
+ // log process handled
+ handled(process);
+ }
+
+ function handleUnhandledProcesses(rootElements) {
+
+ // walk through all processes that have not yet been drawn and draw them
+ // if they contain lanes with DI information.
+ // we do this to pass the free-floating lane test cases in the MIWG test suite
+ var processes = filter(rootElements, function(e) {
+ return !isHandled(e) && is(e, 'bpmn:Process') && e.laneSets;
+ });
+
+ processes.forEach(contextual(handleProcess));
+ }
+
+ function handleMessageFlow(messageFlow, context) {
+ visitIfDi(messageFlow, context);
+ }
+
+ function handleMessageFlows(messageFlows, context) {
+ forEach(messageFlows, contextual(handleMessageFlow, context));
+ }
+
+ function handleDataAssociation(association, context) {
+ visitIfDi(association, context);
+ }
+
+ function handleDataInput(dataInput, context) {
+ visitIfDi(dataInput, context);
+ }
+
+ function handleDataOutput(dataOutput, context) {
+ visitIfDi(dataOutput, context);
+ }
+
+ function handleArtifact(artifact, context) {
+
+ // bpmn:TextAnnotation
+ // bpmn:Group
+ // bpmn:Association
+
+ visitIfDi(artifact, context);
+ }
+
+ function handleArtifacts(artifacts, context) {
+
+ forEach(artifacts, function(e) {
+ if (is(e, 'bpmn:Association')) {
+ deferred.push(function() {
+ handleArtifact(e, context);
+ });
+ } else {
+ handleArtifact(e, context);
+ }
+ });
+ }
+
+ function handleIoSpecification(ioSpecification, context) {
+
+ if (!ioSpecification) {
+ return;
+ }
+
+ forEach(ioSpecification.dataInputs, contextual(handleDataInput, context));
+ forEach(ioSpecification.dataOutputs, contextual(handleDataOutput, context));
+ }
+
+ function handleSubProcess(subProcess, context) {
+ handleFlowElementsContainer(subProcess, context);
+ handleArtifacts(subProcess.artifacts, context);
+ }
+
+ function handleFlowNode(flowNode, context) {
+ var childCtx = visitIfDi(flowNode, context);
+
+ if (is(flowNode, 'bpmn:SubProcess')) {
+ handleSubProcess(flowNode, childCtx || context);
+ }
+
+ if (is(flowNode, 'bpmn:Activity')) {
+ handleIoSpecification(flowNode.ioSpecification, context);
+ }
+
+ // defer handling of associations
+ // affected types:
+ //
+ // * bpmn:Activity
+ // * bpmn:ThrowEvent
+ // * bpmn:CatchEvent
+ //
+ deferred.push(function() {
+ forEach(flowNode.dataInputAssociations, contextual(handleDataAssociation, context));
+ forEach(flowNode.dataOutputAssociations, contextual(handleDataAssociation, context));
+ });
+ }
+
+ function handleSequenceFlow(sequenceFlow, context) {
+ visitIfDi(sequenceFlow, context);
+ }
+
+ function handleDataElement(dataObject, context) {
+ visitIfDi(dataObject, context);
+ }
+
+ function handleBoundaryEvent(dataObject, context) {
+ visitIfDi(dataObject, context);
+ }
+
+ function handleLane(lane, context) {
+
+ deferred.push(function() {
+
+ var newContext = visitIfDi(lane, context);
+
+ if (lane.childLaneSet) {
+ handleLaneSet(lane.childLaneSet, newContext || context);
+ }
+
+ wireFlowNodeRefs(lane);
+ });
+ }
+
+ function handleLaneSet(laneSet, context) {
+ forEach(laneSet.lanes, contextual(handleLane, context));
+ }
+
+ function handleLaneSets(laneSets, context) {
+ forEach(laneSets, contextual(handleLaneSet, context));
+ }
+
+ function handleFlowElementsContainer(container, context) {
+ handleFlowElements(container.flowElements, context);
+
+ if (container.laneSets) {
+ handleLaneSets(container.laneSets, context);
+ }
+ }
+
+ function handleFlowElements(flowElements, context) {
+ forEach(flowElements, function(e) {
+ if (is(e, 'bpmn:SequenceFlow')) {
+ deferred.push(function() {
+ handleSequenceFlow(e, context);
+ });
+ } else if (is(e, 'bpmn:BoundaryEvent')) {
+ deferred.unshift(function() {
+ handleBoundaryEvent(e, context);
+ });
+ } else if (is(e, 'bpmn:FlowNode')) {
+ handleFlowNode(e, context);
+ } else if (is(e, 'bpmn:DataObject')) {
+ // SKIP (assume correct referencing via DataObjectReference)
+ } else if (is(e, 'bpmn:DataStoreReference')) {
+ handleDataElement(e, context);
+ } else if (is(e, 'bpmn:DataObjectReference')) {
+ handleDataElement(e, context);
+ } else {
+ logError(
+ translate('unrecognized flowElement {element} in context {context}', {
+ element: elementToString(e),
+ context: (context ? elementToString(context.businessObject) : 'null')
+ }),
+ { element: e, context: context }
+ );
+ }
+ });
+ }
+
+ function handleParticipant(participant, context) {
+ var newCtx = visitIfDi(participant, context);
+
+ var process = participant.processRef;
+ if (process) {
+ handleProcess(process, newCtx || context);
+ }
+ }
+
+ function handleCollaboration(collaboration) {
+
+ forEach(collaboration.participants, contextual(handleParticipant));
+
+ handleArtifacts(collaboration.artifacts);
+
+ // handle message flows latest in the process
+ deferred.push(function() {
+ handleMessageFlows(collaboration.messageFlows);
+ });
+ }
+
+
+ function wireFlowNodeRefs(lane) {
+ // wire the virtual flowNodeRefs <-> relationship
+ forEach(lane.flowNodeRef, function(flowNode) {
+ var lanes = flowNode.get('lanes');
+
+ if (lanes) {
+ lanes.push(lane);
+ }
+ });
+ }
+
+ // API //////////////////////
+
+ return {
+ handleDeferred: handleDeferred,
+ handleDefinitions: handleDefinitions,
+ handleSubProcess: handleSubProcess,
+ registerDi: registerDi
+ };
+ }
+
+ /**
+ * Import the definitions into a diagram.
+ *
+ * Errors and warnings are reported through the specified callback.
+ *
+ * @param {Diagram} diagram
+ * @param {ModdleElement} definitions
+ * @param {Function} done the callback, invoked with (err, [ warning ]) once the import is done
+ */
+ function importBpmnDiagram(diagram, definitions, done) {
+
+ var importer,
+ eventBus,
+ translate;
+
+ var error,
+ warnings = [];
+
+ /**
+ * Walk the diagram semantically, importing (=drawing)
+ * all elements you encounter.
+ *
+ * @param {ModdleElement} definitions
+ */
+ function render(definitions) {
+
+ var visitor = {
+
+ root: function(element) {
+ return importer.add(element);
+ },
+
+ element: function(element, parentShape) {
+ return importer.add(element, parentShape);
+ },
+
+ error: function(message, context) {
+ warnings.push({ message: message, context: context });
+ }
+ };
+
+ var walker = new BpmnTreeWalker(visitor, translate);
+
+ // traverse BPMN 2.0 document model,
+ // starting at definitions
+ walker.handleDefinitions(definitions);
+ }
+
+ try {
+ importer = diagram.get('bpmnImporter');
+ eventBus = diagram.get('eventBus');
+ translate = diagram.get('translate');
+
+ eventBus.fire('import.render.start', { definitions: definitions });
+
+ render(definitions);
+
+ eventBus.fire('import.render.complete', {
+ error: error,
+ warnings: warnings
+ });
+ } catch (e) {
+ error = e;
+ }
+
+ done(error, warnings);
+ }
+
+ /**
+ * Is an element of the given BPMN type?
+ *
+ * @param {djs.model.Base|ModdleElement} element
+ * @param {String} type
+ *
+ * @return {Boolean}
+ */
+ function is$1(element, type) {
+ var bo = getBusinessObject(element);
+
+ return bo && (typeof bo.$instanceOf === 'function') && bo.$instanceOf(type);
+ }
+
+
+ /**
+ * Return the business object for a given element.
+ *
+ * @param {djs.model.Base|ModdleElement} element
+ *
+ * @return {ModdleElement}
+ */
+ function getBusinessObject(element) {
+ return (element && element.businessObject) || element;
+ }
+
+ function isExpanded(element) {
+
+ if (is$1(element, 'bpmn:CallActivity')) {
+ return false;
+ }
+
+ if (is$1(element, 'bpmn:SubProcess')) {
+ return !!getBusinessObject(element).di.isExpanded;
+ }
+
+ if (is$1(element, 'bpmn:Participant')) {
+ return !!getBusinessObject(element).processRef;
+ }
+
+ return true;
+ }
+
+ function isEventSubProcess(element) {
+ return element && !!getBusinessObject(element).triggeredByEvent;
+ }
+
+ // element utils //////////////////////
+
+ /**
+ * Checks if eventDefinition of the given element matches with semantic type.
+ *
+ * @return {boolean} true if element is of the given semantic type
+ */
+ function isTypedEvent(event, eventDefinitionType, filter$$1) {
+
+ function matches(definition, filter$$1) {
+ return every(filter$$1, function(val, key) {
+
+ // we want a == conversion here, to be able to catch
+ // undefined == false and friends
+ /* jshint -W116 */
+ return definition[key] == val;
+ });
+ }
+
+ return some(event.eventDefinitions, function(definition) {
+ return definition.$type === eventDefinitionType && matches(event, filter$$1);
+ });
+ }
+
+ function isThrowEvent(event) {
+ return (event.$type === 'bpmn:IntermediateThrowEvent') || (event.$type === 'bpmn:EndEvent');
+ }
+
+ function isCollection(element) {
+ var dataObject = element.dataObjectRef;
+
+ return element.isCollection || (dataObject && dataObject.isCollection);
+ }
+
+ function getDi(element) {
+ return element.businessObject.di;
+ }
+
+ function getSemantic(element) {
+ return element.businessObject;
+ }
+
+
+ // color access //////////////////////
+
+ function getFillColor(element, defaultColor) {
+ return getDi(element).get('bioc:fill') || defaultColor || 'white';
+ }
+
+ function getStrokeColor(element, defaultColor) {
+ return getDi(element).get('bioc:stroke') || defaultColor || 'black';
+ }
+
+
+ // cropping path customizations //////////////////////
+
+ function getCirclePath(shape) {
+
+ var cx = shape.x + shape.width / 2,
+ cy = shape.y + shape.height / 2,
+ radius = shape.width / 2;
+
+ var circlePath = [
+ ['M', cx, cy],
+ ['m', 0, -radius],
+ ['a', radius, radius, 0, 1, 1, 0, 2 * radius],
+ ['a', radius, radius, 0, 1, 1, 0, -2 * radius],
+ ['z']
+ ];
+
+ return componentsToPath(circlePath);
+ }
+
+ function getRoundRectPath(shape, borderRadius) {
+
+ var x = shape.x,
+ y = shape.y,
+ width = shape.width,
+ height = shape.height;
+
+ var roundRectPath = [
+ ['M', x + borderRadius, y],
+ ['l', width - borderRadius * 2, 0],
+ ['a', borderRadius, borderRadius, 0, 0, 1, borderRadius, borderRadius],
+ ['l', 0, height - borderRadius * 2],
+ ['a', borderRadius, borderRadius, 0, 0, 1, -borderRadius, borderRadius],
+ ['l', borderRadius * 2 - width, 0],
+ ['a', borderRadius, borderRadius, 0, 0, 1, -borderRadius, -borderRadius],
+ ['l', 0, borderRadius * 2 - height],
+ ['a', borderRadius, borderRadius, 0, 0, 1, borderRadius, -borderRadius],
+ ['z']
+ ];
+
+ return componentsToPath(roundRectPath);
+ }
+
+ function getDiamondPath(shape) {
+
+ var width = shape.width,
+ height = shape.height,
+ x = shape.x,
+ y = shape.y,
+ halfWidth = width / 2,
+ halfHeight = height / 2;
+
+ var diamondPath = [
+ ['M', x + halfWidth, y],
+ ['l', halfWidth, halfHeight],
+ ['l', -halfWidth, halfHeight],
+ ['l', -halfWidth, -halfHeight],
+ ['z']
+ ];
+
+ return componentsToPath(diamondPath);
+ }
+
+ function getRectPath(shape) {
+ var x = shape.x,
+ y = shape.y,
+ width = shape.width,
+ height = shape.height;
+
+ var rectPath = [
+ ['M', x, y],
+ ['l', width, 0],
+ ['l', 0, height],
+ ['l', -width, 0],
+ ['z']
+ ];
+
+ return componentsToPath(rectPath);
+ }
+
+ var hat_1 = createCommonjsModule(function (module) {
+ var hat = module.exports = function (bits, base) {
+ if (!base) base = 16;
+ if (bits === undefined) bits = 128;
+ if (bits <= 0) return '0';
+
+ var digits = Math.log(Math.pow(2, bits)) / Math.log(base);
+ for (var i = 2; digits === Infinity; i *= 2) {
+ digits = Math.log(Math.pow(2, bits / i)) / Math.log(base) * i;
+ }
+
+ var rem = digits - Math.floor(digits);
+
+ var res = '';
+
+ for (var i = 0; i < Math.floor(digits); i++) {
+ var x = Math.floor(Math.random() * base).toString(base);
+ res = x + res;
+ }
+
+ if (rem) {
+ var b = Math.pow(base, rem);
+ var x = Math.floor(Math.random() * b).toString(base);
+ res = x + res;
+ }
+
+ var parsed = parseInt(res, base);
+ if (parsed !== Infinity && parsed >= Math.pow(2, bits)) {
+ return hat(bits, base)
+ }
+ else return res;
+ };
+
+ hat.rack = function (bits, base, expandBy) {
+ var fn = function (data) {
+ var iters = 0;
+ do {
+ if (iters ++ > 10) {
+ if (expandBy) bits += expandBy;
+ else throw new Error('too many ID collisions, use more bits')
+ }
+
+ var id = hat(bits, base);
+ } while (Object.hasOwnProperty.call(hats, id));
+
+ hats[id] = data;
+ return id;
+ };
+ var hats = fn.hats = {};
+
+ fn.get = function (id) {
+ return fn.hats[id];
+ };
+
+ fn.set = function (id, value) {
+ fn.hats[id] = value;
+ return fn;
+ };
+
+ fn.bits = bits || 128;
+ fn.base = base || 16;
+ return fn;
+ };
+ });
+
+ /**
+ * Create a new id generator / cache instance.
+ *
+ * You may optionally provide a seed that is used internally.
+ *
+ * @param {Seed} seed
+ */
+ function Ids(seed) {
+
+ if (!(this instanceof Ids)) {
+ return new Ids(seed);
+ }
+
+ seed = seed || [ 128, 36, 1 ];
+ this._seed = seed.length ? hat_1.rack(seed[0], seed[1], seed[2]) : seed;
+ }
+
+ var ids = Ids;
+
+ /**
+ * Generate a next id.
+ *
+ * @param {Object} [element] element to bind the id to
+ *
+ * @return {String} id
+ */
+ Ids.prototype.next = function(element) {
+ return this._seed(element || true);
+ };
+
+ /**
+ * Generate a next id with a given prefix.
+ *
+ * @param {Object} [element] element to bind the id to
+ *
+ * @return {String} id
+ */
+ Ids.prototype.nextPrefixed = function(prefix, element) {
+ var id;
+
+ do {
+ id = prefix + this.next(true);
+ } while (this.assigned(id));
+
+ // claim {prefix}{random}
+ this.claim(id, element);
+
+ // return
+ return id;
+ };
+
+ /**
+ * Manually claim an existing id.
+ *
+ * @param {String} id
+ * @param {String} [element] element the id is claimed by
+ */
+ Ids.prototype.claim = function(id, element) {
+ this._seed.set(id, element || true);
+ };
+
+ /**
+ * Returns true if the given id has already been assigned.
+ *
+ * @param {String} id
+ * @return {Boolean}
+ */
+ Ids.prototype.assigned = function(id) {
+ return this._seed.get(id) || false;
+ };
+
+ /**
+ * Unclaim an id.
+ *
+ * @param {String} id the id to unclaim
+ */
+ Ids.prototype.unclaim = function(id) {
+ delete this._seed.hats[id];
+ };
+
+
+ /**
+ * Clear all claimed ids.
+ */
+ Ids.prototype.clear = function() {
+
+ var hats = this._seed.hats,
+ id;
+
+ for (id in hats) {
+ this.unclaim(id);
+ }
+ };
+
+ var RENDERER_IDS = new ids();
+
+ var TASK_BORDER_RADIUS = 10;
+ var INNER_OUTER_DIST = 3;
+
+ var DEFAULT_FILL_OPACITY = .95,
+ HIGH_FILL_OPACITY = .35;
+
+
+ function BpmnRenderer(
+ config, eventBus, styles, pathMap,
+ canvas, textRenderer, priority) {
+
+ BaseRenderer.call(this, eventBus, priority);
+
+ var defaultFillColor = config && config.defaultFillColor,
+ defaultStrokeColor = config && config.defaultStrokeColor;
+
+ var rendererId = RENDERER_IDS.next();
+
+ var markers = {};
+
+ var computeStyle = styles.computeStyle;
+
+ function addMarker(id, options) {
+ var attrs = assign({
+ fill: 'black',
+ strokeWidth: 1,
+ strokeLinecap: 'round',
+ strokeDasharray: 'none'
+ }, options.attrs);
+
+ var ref = options.ref || { x: 0, y: 0 };
+
+ var scale$$1 = options.scale || 1;
+
+ // fix for safari / chrome / firefox bug not correctly
+ // resetting stroke dash array
+ if (attrs.strokeDasharray === 'none') {
+ attrs.strokeDasharray = [10000, 1];
+ }
+
+ var marker = create('marker');
+
+ attr$1(options.element, attrs);
+
+ append(marker, options.element);
+
+ attr$1(marker, {
+ id: id,
+ viewBox: '0 0 20 20',
+ refX: ref.x,
+ refY: ref.y,
+ markerWidth: 20 * scale$$1,
+ markerHeight: 20 * scale$$1,
+ orient: 'auto'
+ });
+
+ var defs = query('defs', canvas._svg);
+
+ if (!defs) {
+ defs = create('defs');
+
+ append(canvas._svg, defs);
+ }
+
+ append(defs, marker);
+
+ markers[id] = marker;
+ }
+
+ function marker(type, fill, stroke) {
+ var id = type + '-' + fill + '-' + stroke + '-' + rendererId;
+
+ if (!markers[id]) {
+ createMarker(type, fill, stroke);
+ }
+
+ return 'url(#' + id + ')';
+ }
+
+ function createMarker(type, fill, stroke) {
+ var id = type + '-' + fill + '-' + stroke + '-' + rendererId;
+
+ if (type === 'sequenceflow-end') {
+ var sequenceflowEnd = create('path');
+ attr$1(sequenceflowEnd, { d: 'M 1 5 L 11 10 L 1 15 Z' });
+
+ addMarker(id, {
+ element: sequenceflowEnd,
+ ref: { x: 11, y: 10 },
+ scale: 0.5,
+ attrs: {
+ fill: stroke,
+ stroke: stroke
+ }
+ });
+ }
+
+ if (type === 'messageflow-start') {
+ var messageflowStart = create('circle');
+ attr$1(messageflowStart, { cx: 6, cy: 6, r: 3.5 });
+
+ addMarker(id, {
+ element: messageflowStart,
+ attrs: {
+ fill: fill,
+ stroke: stroke
+ },
+ ref: { x: 6, y: 6 }
+ });
+ }
+
+ if (type === 'messageflow-end') {
+ var messageflowEnd = create('path');
+ attr$1(messageflowEnd, { d: 'm 1 5 l 0 -3 l 7 3 l -7 3 z' });
+
+ addMarker(id, {
+ element: messageflowEnd,
+ attrs: {
+ fill: fill,
+ stroke: stroke,
+ strokeLinecap: 'butt'
+ },
+ ref: { x: 8.5, y: 5 }
+ });
+ }
+
+ if (type === 'association-start') {
+ var associationStart = create('path');
+ attr$1(associationStart, { d: 'M 11 5 L 1 10 L 11 15' });
+
+ addMarker(id, {
+ element: associationStart,
+ attrs: {
+ fill: 'none',
+ stroke: stroke,
+ strokeWidth: 1.5
+ },
+ ref: { x: 1, y: 10 },
+ scale: 0.5
+ });
+ }
+
+ if (type === 'association-end') {
+ var associationEnd = create('path');
+ attr$1(associationEnd, { d: 'M 1 5 L 11 10 L 1 15' });
+
+ addMarker(id, {
+ element: associationEnd,
+ attrs: {
+ fill: 'none',
+ stroke: stroke,
+ strokeWidth: 1.5
+ },
+ ref: { x: 12, y: 10 },
+ scale: 0.5
+ });
+ }
+
+ if (type === 'conditional-flow-marker') {
+ var conditionalflowMarker = create('path');
+ attr$1(conditionalflowMarker, { d: 'M 0 10 L 8 6 L 16 10 L 8 14 Z' });
+
+ addMarker(id, {
+ element: conditionalflowMarker,
+ attrs: {
+ fill: fill,
+ stroke: stroke
+ },
+ ref: { x: -1, y: 10 },
+ scale: 0.5
+ });
+ }
+
+ if (type === 'conditional-default-flow-marker') {
+ var conditionaldefaultflowMarker = create('path');
+ attr$1(conditionaldefaultflowMarker, { d: 'M 6 4 L 10 16' });
+
+ addMarker(id, {
+ element: conditionaldefaultflowMarker,
+ attrs: {
+ stroke: stroke
+ },
+ ref: { x: 0, y: 10 },
+ scale: 0.5
+ });
+ }
+ }
+
+ function drawCircle(parentGfx, width, height, offset, attrs) {
+
+ if (isObject(offset)) {
+ attrs = offset;
+ offset = 0;
+ }
+
+ offset = offset || 0;
+
+ attrs = computeStyle(attrs, {
+ stroke: 'black',
+ strokeWidth: 2,
+ fill: 'white'
+ });
+
+ if (attrs.fill === 'none') {
+ delete attrs.fillOpacity;
+ }
+
+ var cx = width / 2,
+ cy = height / 2;
+
+ var circle = create('circle');
+ attr$1(circle, {
+ cx: cx,
+ cy: cy,
+ r: Math.round((width + height) / 4 - offset)
+ });
+ attr$1(circle, attrs);
+
+ append(parentGfx, circle);
+
+ return circle;
+ }
+
+ function drawRect(parentGfx, width, height, r, offset, attrs) {
+
+ if (isObject(offset)) {
+ attrs = offset;
+ offset = 0;
+ }
+
+ offset = offset || 0;
+
+ attrs = computeStyle(attrs, {
+ stroke: 'black',
+ strokeWidth: 2,
+ fill: 'white'
+ });
+
+ var rect = create('rect');
+ attr$1(rect, {
+ x: offset,
+ y: offset,
+ width: width - offset * 2,
+ height: height - offset * 2,
+ rx: r,
+ ry: r
+ });
+ attr$1(rect, attrs);
+
+ append(parentGfx, rect);
+
+ return rect;
+ }
+
+ function drawDiamond(parentGfx, width, height, attrs) {
+
+ var x_2 = width / 2;
+ var y_2 = height / 2;
+
+ var points = [{ x: x_2, y: 0 }, { x: width, y: y_2 }, { x: x_2, y: height }, { x: 0, y: y_2 }];
+
+ var pointsString = points.map(function(point) {
+ return point.x + ',' + point.y;
+ }).join(' ');
+
+ attrs = computeStyle(attrs, {
+ stroke: 'black',
+ strokeWidth: 2,
+ fill: 'white'
+ });
+
+ var polygon = create('polygon');
+ attr$1(polygon, {
+ points: pointsString
+ });
+ attr$1(polygon, attrs);
+
+ append(parentGfx, polygon);
+
+ return polygon;
+ }
+
+ function drawLine(parentGfx, waypoints, attrs) {
+ attrs = computeStyle(attrs, [ 'no-fill' ], {
+ stroke: 'black',
+ strokeWidth: 2,
+ fill: 'none'
+ });
+
+ var line = createLine(waypoints, attrs);
+
+ append(parentGfx, line);
+
+ return line;
+ }
+
+ function drawPath(parentGfx, d, attrs) {
+
+ attrs = computeStyle(attrs, [ 'no-fill' ], {
+ strokeWidth: 2,
+ stroke: 'black'
+ });
+
+ var path = create('path');
+ attr$1(path, { d: d });
+ attr$1(path, attrs);
+
+ append(parentGfx, path);
+
+ return path;
+ }
+
+ function drawMarker(type, parentGfx, path, attrs) {
+ return drawPath(parentGfx, path, assign({ 'data-marker': type }, attrs));
+ }
+
+ function as(type) {
+ return function(parentGfx, element) {
+ return handlers[type](parentGfx, element);
+ };
+ }
+
+ function renderer(type) {
+ return handlers[type];
+ }
+
+ function renderEventContent(element, parentGfx) {
+
+ var event = getSemantic(element);
+ var isThrowing = isThrowEvent(event);
+
+ if (isTypedEvent(event, 'bpmn:MessageEventDefinition')) {
+ return renderer('bpmn:MessageEventDefinition')(parentGfx, element, isThrowing);
+ }
+
+ if (isTypedEvent(event, 'bpmn:TimerEventDefinition')) {
+ return renderer('bpmn:TimerEventDefinition')(parentGfx, element, isThrowing);
+ }
+
+ if (isTypedEvent(event, 'bpmn:ConditionalEventDefinition')) {
+ return renderer('bpmn:ConditionalEventDefinition')(parentGfx, element);
+ }
+
+ if (isTypedEvent(event, 'bpmn:SignalEventDefinition')) {
+ return renderer('bpmn:SignalEventDefinition')(parentGfx, element, isThrowing);
+ }
+
+ if (isTypedEvent(event, 'bpmn:CancelEventDefinition') &&
+ isTypedEvent(event, 'bpmn:TerminateEventDefinition', { parallelMultiple: false })) {
+ return renderer('bpmn:MultipleEventDefinition')(parentGfx, element, isThrowing);
+ }
+
+ if (isTypedEvent(event, 'bpmn:CancelEventDefinition') &&
+ isTypedEvent(event, 'bpmn:TerminateEventDefinition', { parallelMultiple: true })) {
+ return renderer('bpmn:ParallelMultipleEventDefinition')(parentGfx, element, isThrowing);
+ }
+
+ if (isTypedEvent(event, 'bpmn:EscalationEventDefinition')) {
+ return renderer('bpmn:EscalationEventDefinition')(parentGfx, element, isThrowing);
+ }
+
+ if (isTypedEvent(event, 'bpmn:LinkEventDefinition')) {
+ return renderer('bpmn:LinkEventDefinition')(parentGfx, element, isThrowing);
+ }
+
+ if (isTypedEvent(event, 'bpmn:ErrorEventDefinition')) {
+ return renderer('bpmn:ErrorEventDefinition')(parentGfx, element, isThrowing);
+ }
+
+ if (isTypedEvent(event, 'bpmn:CancelEventDefinition')) {
+ return renderer('bpmn:CancelEventDefinition')(parentGfx, element, isThrowing);
+ }
+
+ if (isTypedEvent(event, 'bpmn:CompensateEventDefinition')) {
+ return renderer('bpmn:CompensateEventDefinition')(parentGfx, element, isThrowing);
+ }
+
+ if (isTypedEvent(event, 'bpmn:TerminateEventDefinition')) {
+ return renderer('bpmn:TerminateEventDefinition')(parentGfx, element, isThrowing);
+ }
+
+ return null;
+ }
+
+ function renderLabel(parentGfx, label, options) {
+
+ options = assign({
+ size: {
+ width: 100
+ }
+ }, options);
+
+ var text = textRenderer.createText(label || '', options);
+
+ classes$1(text).add('djs-label');
+
+ append(parentGfx, text);
+
+ return text;
+ }
+
+ function renderEmbeddedLabel(parentGfx, element, align) {
+ var semantic = getSemantic(element);
+
+ return renderLabel(parentGfx, semantic.name, {
+ box: element,
+ align: align,
+ padding: 5,
+ style: {
+ fill: getStrokeColor(element, defaultStrokeColor)
+ }
+ });
+ }
+
+ function renderExternalLabel(parentGfx, element) {
+ var semantic = getSemantic(element);
+ var box = {
+ width: 90,
+ height: 30,
+ x: element.width / 2 + element.x,
+ y: element.height / 2 + element.y
+ };
+
+ return renderLabel(parentGfx, semantic.name, {
+ box: box,
+ fitBox: true,
+ style: assign(
+ {},
+ textRenderer.getExternalStyle(),
+ {
+ fill: getStrokeColor(element, defaultStrokeColor)
+ }
+ )
+ });
+ }
+
+ function renderLaneLabel(parentGfx, text, element) {
+ var textBox = renderLabel(parentGfx, text, {
+ box: {
+ height: 30,
+ width: element.height
+ },
+ align: 'center-middle',
+ style: {
+ fill: getStrokeColor(element, defaultStrokeColor)
+ }
+ });
+
+ var top = -1 * element.height;
+
+ transform$1(textBox, 0, -top, 270);
+ }
+
+ function createPathFromConnection(connection) {
+ var waypoints = connection.waypoints;
+
+ var pathData = 'm ' + waypoints[0].x + ',' + waypoints[0].y;
+ for (var i = 1; i < waypoints.length; i++) {
+ pathData += 'L' + waypoints[i].x + ',' + waypoints[i].y + ' ';
+ }
+ return pathData;
+ }
+
+ var handlers = this.handlers = {
+ 'bpmn:Event': function(parentGfx, element, attrs) {
+
+ if (!('fillOpacity' in attrs)) {
+ attrs.fillOpacity = DEFAULT_FILL_OPACITY;
+ }
+
+ return drawCircle(parentGfx, element.width, element.height, attrs);
+ },
+ 'bpmn:StartEvent': function(parentGfx, element) {
+ var attrs = {
+ fill: getFillColor(element, defaultFillColor),
+ stroke: getStrokeColor(element, defaultStrokeColor)
+ };
+
+ var semantic = getSemantic(element);
+
+ if (!semantic.isInterrupting) {
+ attrs = {
+ strokeDasharray: '6',
+ strokeLinecap: 'round',
+ fill: getFillColor(element, defaultFillColor),
+ stroke: getStrokeColor(element, defaultStrokeColor)
+ };
+ }
+
+ var circle = renderer('bpmn:Event')(parentGfx, element, attrs);
+
+ renderEventContent(element, parentGfx);
+
+ return circle;
+ },
+ 'bpmn:MessageEventDefinition': function(parentGfx, element, isThrowing) {
+ var pathData = pathMap.getScaledPath('EVENT_MESSAGE', {
+ xScaleFactor: 0.9,
+ yScaleFactor: 0.9,
+ containerWidth: element.width,
+ containerHeight: element.height,
+ position: {
+ mx: 0.235,
+ my: 0.315
+ }
+ });
+
+ var fill = isThrowing ? getStrokeColor(element, defaultStrokeColor) : getFillColor(element, defaultFillColor);
+ var stroke = isThrowing ? getFillColor(element, defaultFillColor) : getStrokeColor(element, defaultStrokeColor);
+
+ var messagePath = drawPath(parentGfx, pathData, {
+ strokeWidth: 1,
+ fill: fill,
+ stroke: stroke
+ });
+
+ return messagePath;
+ },
+ 'bpmn:TimerEventDefinition': function(parentGfx, element) {
+ var circle = drawCircle(parentGfx, element.width, element.height, 0.2 * element.height, {
+ strokeWidth: 2,
+ fill: getFillColor(element, defaultFillColor),
+ stroke: getStrokeColor(element, defaultStrokeColor)
+ });
+
+ var pathData = pathMap.getScaledPath('EVENT_TIMER_WH', {
+ xScaleFactor: 0.75,
+ yScaleFactor: 0.75,
+ containerWidth: element.width,
+ containerHeight: element.height,
+ position: {
+ mx: 0.5,
+ my: 0.5
+ }
+ });
+
+ drawPath(parentGfx, pathData, {
+ strokeWidth: 2,
+ strokeLinecap: 'square',
+ stroke: getStrokeColor(element, defaultStrokeColor)
+ });
+
+ for (var i = 0;i < 12; i++) {
+
+ var linePathData = pathMap.getScaledPath('EVENT_TIMER_LINE', {
+ xScaleFactor: 0.75,
+ yScaleFactor: 0.75,
+ containerWidth: element.width,
+ containerHeight: element.height,
+ position: {
+ mx: 0.5,
+ my: 0.5
+ }
+ });
+
+ var width = element.width / 2;
+ var height = element.height / 2;
+
+ drawPath(parentGfx, linePathData, {
+ strokeWidth: 1,
+ strokeLinecap: 'square',
+ transform: 'rotate(' + (i * 30) + ',' + height + ',' + width + ')',
+ stroke: getStrokeColor(element, defaultStrokeColor)
+ });
+ }
+
+ return circle;
+ },
+ 'bpmn:EscalationEventDefinition': function(parentGfx, event, isThrowing) {
+ var pathData = pathMap.getScaledPath('EVENT_ESCALATION', {
+ xScaleFactor: 1,
+ yScaleFactor: 1,
+ containerWidth: event.width,
+ containerHeight: event.height,
+ position: {
+ mx: 0.5,
+ my: 0.2
+ }
+ });
+
+ var fill = isThrowing ? getStrokeColor(event, defaultStrokeColor) : 'none';
+
+ return drawPath(parentGfx, pathData, {
+ strokeWidth: 1,
+ fill: fill,
+ stroke: getStrokeColor(event, defaultStrokeColor)
+ });
+ },
+ 'bpmn:ConditionalEventDefinition': function(parentGfx, event) {
+ var pathData = pathMap.getScaledPath('EVENT_CONDITIONAL', {
+ xScaleFactor: 1,
+ yScaleFactor: 1,
+ containerWidth: event.width,
+ containerHeight: event.height,
+ position: {
+ mx: 0.5,
+ my: 0.222
+ }
+ });
+
+ return drawPath(parentGfx, pathData, {
+ strokeWidth: 1,
+ stroke: getStrokeColor(event, defaultStrokeColor)
+ });
+ },
+ 'bpmn:LinkEventDefinition': function(parentGfx, event, isThrowing) {
+ var pathData = pathMap.getScaledPath('EVENT_LINK', {
+ xScaleFactor: 1,
+ yScaleFactor: 1,
+ containerWidth: event.width,
+ containerHeight: event.height,
+ position: {
+ mx: 0.57,
+ my: 0.263
+ }
+ });
+
+ var fill = isThrowing ? getStrokeColor(event, defaultStrokeColor) : 'none';
+
+ return drawPath(parentGfx, pathData, {
+ strokeWidth: 1,
+ fill: fill,
+ stroke: getStrokeColor(event, defaultStrokeColor)
+ });
+ },
+ 'bpmn:ErrorEventDefinition': function(parentGfx, event, isThrowing) {
+ var pathData = pathMap.getScaledPath('EVENT_ERROR', {
+ xScaleFactor: 1.1,
+ yScaleFactor: 1.1,
+ containerWidth: event.width,
+ containerHeight: event.height,
+ position: {
+ mx: 0.2,
+ my: 0.722
+ }
+ });
+
+ var fill = isThrowing ? getStrokeColor(event, defaultStrokeColor) : 'none';
+
+ return drawPath(parentGfx, pathData, {
+ strokeWidth: 1,
+ fill: fill,
+ stroke: getStrokeColor(event, defaultStrokeColor)
+ });
+ },
+ 'bpmn:CancelEventDefinition': function(parentGfx, event, isThrowing) {
+ var pathData = pathMap.getScaledPath('EVENT_CANCEL_45', {
+ xScaleFactor: 1.0,
+ yScaleFactor: 1.0,
+ containerWidth: event.width,
+ containerHeight: event.height,
+ position: {
+ mx: 0.638,
+ my: -0.055
+ }
+ });
+
+ var fill = isThrowing ? getStrokeColor(event, defaultStrokeColor) : 'none';
+
+ var path = drawPath(parentGfx, pathData, {
+ strokeWidth: 1,
+ fill: fill,
+ stroke: getStrokeColor(event, defaultStrokeColor)
+ });
+
+ rotate(path, 45);
+
+ return path;
+ },
+ 'bpmn:CompensateEventDefinition': function(parentGfx, event, isThrowing) {
+ var pathData = pathMap.getScaledPath('EVENT_COMPENSATION', {
+ xScaleFactor: 1,
+ yScaleFactor: 1,
+ containerWidth: event.width,
+ containerHeight: event.height,
+ position: {
+ mx: 0.22,
+ my: 0.5
+ }
+ });
+
+ var fill = isThrowing ? getStrokeColor(event, defaultStrokeColor) : 'none';
+
+ return drawPath(parentGfx, pathData, {
+ strokeWidth: 1,
+ fill: fill,
+ stroke: getStrokeColor(event, defaultStrokeColor)
+ });
+ },
+ 'bpmn:SignalEventDefinition': function(parentGfx, event, isThrowing) {
+ var pathData = pathMap.getScaledPath('EVENT_SIGNAL', {
+ xScaleFactor: 0.9,
+ yScaleFactor: 0.9,
+ containerWidth: event.width,
+ containerHeight: event.height,
+ position: {
+ mx: 0.5,
+ my: 0.2
+ }
+ });
+
+ var fill = isThrowing ? getStrokeColor(event, defaultStrokeColor) : 'none';
+
+ return drawPath(parentGfx, pathData, {
+ strokeWidth: 1,
+ fill: fill,
+ stroke: getStrokeColor(event, defaultStrokeColor)
+ });
+ },
+ 'bpmn:MultipleEventDefinition': function(parentGfx, event, isThrowing) {
+ var pathData = pathMap.getScaledPath('EVENT_MULTIPLE', {
+ xScaleFactor: 1.1,
+ yScaleFactor: 1.1,
+ containerWidth: event.width,
+ containerHeight: event.height,
+ position: {
+ mx: 0.222,
+ my: 0.36
+ }
+ });
+
+ var fill = isThrowing ? getStrokeColor(event, defaultStrokeColor) : 'none';
+
+ return drawPath(parentGfx, pathData, {
+ strokeWidth: 1,
+ fill: fill
+ });
+ },
+ 'bpmn:ParallelMultipleEventDefinition': function(parentGfx, event) {
+ var pathData = pathMap.getScaledPath('EVENT_PARALLEL_MULTIPLE', {
+ xScaleFactor: 1.2,
+ yScaleFactor: 1.2,
+ containerWidth: event.width,
+ containerHeight: event.height,
+ position: {
+ mx: 0.458,
+ my: 0.194
+ }
+ });
+
+ return drawPath(parentGfx, pathData, {
+ strokeWidth: 1,
+ fill: getStrokeColor(event, defaultStrokeColor),
+ stroke: getStrokeColor(event, defaultStrokeColor)
+ });
+ },
+ 'bpmn:EndEvent': function(parentGfx, element) {
+ var circle = renderer('bpmn:Event')(parentGfx, element, {
+ strokeWidth: 4,
+ fill: getFillColor(element, defaultFillColor),
+ stroke: getStrokeColor(element, defaultStrokeColor)
+ });
+
+ renderEventContent(element, parentGfx, true);
+
+ return circle;
+ },
+ 'bpmn:TerminateEventDefinition': function(parentGfx, element) {
+ var circle = drawCircle(parentGfx, element.width, element.height, 8, {
+ strokeWidth: 4,
+ fill: getStrokeColor(element, defaultStrokeColor),
+ stroke: getStrokeColor(element, defaultStrokeColor)
+ });
+
+ return circle;
+ },
+ 'bpmn:IntermediateEvent': function(parentGfx, element) {
+ var outer = renderer('bpmn:Event')(parentGfx, element, {
+ strokeWidth: 1,
+ fill: getFillColor(element, defaultFillColor),
+ stroke: getStrokeColor(element, defaultStrokeColor)
+ });
+
+ /* inner */
+ drawCircle(parentGfx, element.width, element.height, INNER_OUTER_DIST, {
+ strokeWidth: 1,
+ fill: getFillColor(element, 'none'),
+ stroke: getStrokeColor(element, defaultStrokeColor)
+ });
+
+ renderEventContent(element, parentGfx);
+
+ return outer;
+ },
+ 'bpmn:IntermediateCatchEvent': as('bpmn:IntermediateEvent'),
+ 'bpmn:IntermediateThrowEvent': as('bpmn:IntermediateEvent'),
+
+ 'bpmn:Activity': function(parentGfx, element, attrs) {
+
+ attrs = attrs || {};
+
+ if (!('fillOpacity' in attrs)) {
+ attrs.fillOpacity = DEFAULT_FILL_OPACITY;
+ }
+
+ return drawRect(parentGfx, element.width, element.height, TASK_BORDER_RADIUS, attrs);
+ },
+
+ 'bpmn:Task': function(parentGfx, element) {
+ var attrs = {
+ fill: getFillColor(element, defaultFillColor),
+ stroke: getStrokeColor(element, defaultStrokeColor)
+ };
+
+ var rect = renderer('bpmn:Activity')(parentGfx, element, attrs);
+
+ renderEmbeddedLabel(parentGfx, element, 'center-middle');
+ attachTaskMarkers(parentGfx, element);
+
+ return rect;
+ },
+ 'bpmn:ServiceTask': function(parentGfx, element) {
+ var task = renderer('bpmn:Task')(parentGfx, element);
+
+ var pathDataBG = pathMap.getScaledPath('TASK_TYPE_SERVICE', {
+ abspos: {
+ x: 12,
+ y: 18
+ }
+ });
+
+ /* service bg */ drawPath(parentGfx, pathDataBG, {
+ strokeWidth: 1,
+ fill: getFillColor(element, defaultFillColor),
+ stroke: getStrokeColor(element, defaultStrokeColor)
+ });
+
+ var fillPathData = pathMap.getScaledPath('TASK_TYPE_SERVICE_FILL', {
+ abspos: {
+ x: 17.2,
+ y: 18
+ }
+ });
+
+ /* service fill */ drawPath(parentGfx, fillPathData, {
+ strokeWidth: 0,
+ fill: getFillColor(element, defaultFillColor)
+ });
+
+ var pathData = pathMap.getScaledPath('TASK_TYPE_SERVICE', {
+ abspos: {
+ x: 17,
+ y: 22
+ }
+ });
+
+ /* service */ drawPath(parentGfx, pathData, {
+ strokeWidth: 1,
+ fill: getFillColor(element, defaultFillColor),
+ stroke: getStrokeColor(element, defaultStrokeColor)
+ });
+
+ return task;
+ },
+ 'bpmn:UserTask': function(parentGfx, element) {
+ var task = renderer('bpmn:Task')(parentGfx, element);
+
+ var x = 15;
+ var y = 12;
+
+ var pathData = pathMap.getScaledPath('TASK_TYPE_USER_1', {
+ abspos: {
+ x: x,
+ y: y
+ }
+ });
+
+ /* user path */ drawPath(parentGfx, pathData, {
+ strokeWidth: 0.5,
+ fill: getFillColor(element, defaultFillColor),
+ stroke: getStrokeColor(element, defaultStrokeColor)
+ });
+
+ var pathData2 = pathMap.getScaledPath('TASK_TYPE_USER_2', {
+ abspos: {
+ x: x,
+ y: y
+ }
+ });
+
+ /* user2 path */ drawPath(parentGfx, pathData2, {
+ strokeWidth: 0.5,
+ fill: getFillColor(element, defaultFillColor),
+ stroke: getStrokeColor(element, defaultStrokeColor)
+ });
+
+ var pathData3 = pathMap.getScaledPath('TASK_TYPE_USER_3', {
+ abspos: {
+ x: x,
+ y: y
+ }
+ });
+
+ /* user3 path */ drawPath(parentGfx, pathData3, {
+ strokeWidth: 0.5,
+ fill: getStrokeColor(element, defaultStrokeColor),
+ stroke: getStrokeColor(element, defaultStrokeColor)
+ });
+
+ return task;
+ },
+ 'bpmn:ManualTask': function(parentGfx, element) {
+ var task = renderer('bpmn:Task')(parentGfx, element);
+
+ var pathData = pathMap.getScaledPath('TASK_TYPE_MANUAL', {
+ abspos: {
+ x: 17,
+ y: 15
+ }
+ });
+
+ /* manual path */ drawPath(parentGfx, pathData, {
+ strokeWidth: 0.5, // 0.25,
+ fill: getFillColor(element, defaultFillColor),
+ stroke: getStrokeColor(element, defaultStrokeColor)
+ });
+
+ return task;
+ },
+ 'bpmn:SendTask': function(parentGfx, element) {
+ var task = renderer('bpmn:Task')(parentGfx, element);
+
+ var pathData = pathMap.getScaledPath('TASK_TYPE_SEND', {
+ xScaleFactor: 1,
+ yScaleFactor: 1,
+ containerWidth: 21,
+ containerHeight: 14,
+ position: {
+ mx: 0.285,
+ my: 0.357
+ }
+ });
+
+ /* send path */ drawPath(parentGfx, pathData, {
+ strokeWidth: 1,
+ fill: getStrokeColor(element, defaultStrokeColor),
+ stroke: getFillColor(element, defaultFillColor)
+ });
+
+ return task;
+ },
+ 'bpmn:ReceiveTask' : function(parentGfx, element) {
+ var semantic = getSemantic(element);
+
+ var task = renderer('bpmn:Task')(parentGfx, element);
+ var pathData;
+
+ if (semantic.instantiate) {
+ drawCircle(parentGfx, 28, 28, 20 * 0.22, { strokeWidth: 1 });
+
+ pathData = pathMap.getScaledPath('TASK_TYPE_INSTANTIATING_SEND', {
+ abspos: {
+ x: 7.77,
+ y: 9.52
+ }
+ });
+ } else {
+
+ pathData = pathMap.getScaledPath('TASK_TYPE_SEND', {
+ xScaleFactor: 0.9,
+ yScaleFactor: 0.9,
+ containerWidth: 21,
+ containerHeight: 14,
+ position: {
+ mx: 0.3,
+ my: 0.4
+ }
+ });
+ }
+
+ /* receive path */ drawPath(parentGfx, pathData, {
+ strokeWidth: 1,
+ fill: getFillColor(element, defaultFillColor),
+ stroke: getStrokeColor(element, defaultStrokeColor)
+ });
+
+ return task;
+ },
+ 'bpmn:ScriptTask': function(parentGfx, element) {
+ var task = renderer('bpmn:Task')(parentGfx, element);
+
+ var pathData = pathMap.getScaledPath('TASK_TYPE_SCRIPT', {
+ abspos: {
+ x: 15,
+ y: 20
+ }
+ });
+
+ /* script path */ drawPath(parentGfx, pathData, {
+ strokeWidth: 1,
+ stroke: getStrokeColor(element, defaultStrokeColor)
+ });
+
+ return task;
+ },
+ 'bpmn:BusinessRuleTask': function(parentGfx, element) {
+ var task = renderer('bpmn:Task')(parentGfx, element);
+
+ var headerPathData = pathMap.getScaledPath('TASK_TYPE_BUSINESS_RULE_HEADER', {
+ abspos: {
+ x: 8,
+ y: 8
+ }
+ });
+
+ var businessHeaderPath = drawPath(parentGfx, headerPathData);
+ attr$1(businessHeaderPath, {
+ strokeWidth: 1,
+ fill: getFillColor(element, '#aaaaaa'),
+ stroke: getStrokeColor(element, defaultStrokeColor)
+ });
+
+ var headerData = pathMap.getScaledPath('TASK_TYPE_BUSINESS_RULE_MAIN', {
+ abspos: {
+ x: 8,
+ y: 8
+ }
+ });
+
+ var businessPath = drawPath(parentGfx, headerData);
+ attr$1(businessPath, {
+ strokeWidth: 1,
+ stroke: getStrokeColor(element, defaultStrokeColor)
+ });
+
+ return task;
+ },
+ 'bpmn:SubProcess': function(parentGfx, element, attrs) {
+ attrs = assign({
+ fill: getFillColor(element, defaultFillColor),
+ stroke: getStrokeColor(element, defaultStrokeColor)
+ }, attrs);
+
+ var rect = renderer('bpmn:Activity')(parentGfx, element, attrs);
+
+ var expanded = isExpanded(element);
+
+ if (isEventSubProcess(element)) {
+ attr$1(rect, {
+ strokeDasharray: '1,2'
+ });
+ }
+
+ renderEmbeddedLabel(parentGfx, element, expanded ? 'center-top' : 'center-middle');
+
+ if (expanded) {
+ attachTaskMarkers(parentGfx, element);
+ } else {
+ attachTaskMarkers(parentGfx, element, ['SubProcessMarker']);
+ }
+
+ return rect;
+ },
+ 'bpmn:AdHocSubProcess': function(parentGfx, element) {
+ return renderer('bpmn:SubProcess')(parentGfx, element);
+ },
+ 'bpmn:Transaction': function(parentGfx, element) {
+ var outer = renderer('bpmn:SubProcess')(parentGfx, element);
+
+ var innerAttrs = styles.style([ 'no-fill', 'no-events' ], {
+ stroke: getStrokeColor(element, defaultStrokeColor)
+ });
+
+ /* inner path */ drawRect(parentGfx, element.width, element.height, TASK_BORDER_RADIUS - 2, INNER_OUTER_DIST, innerAttrs);
+
+ return outer;
+ },
+ 'bpmn:CallActivity': function(parentGfx, element) {
+ return renderer('bpmn:SubProcess')(parentGfx, element, {
+ strokeWidth: 5
+ });
+ },
+ 'bpmn:Participant': function(parentGfx, element) {
+
+ var attrs = {
+ fillOpacity: DEFAULT_FILL_OPACITY,
+ fill: getFillColor(element, defaultFillColor),
+ stroke: getStrokeColor(element, defaultStrokeColor)
+ };
+
+ var lane = renderer('bpmn:Lane')(parentGfx, element, attrs);
+
+ var expandedPool = isExpanded(element);
+
+ if (expandedPool) {
+ drawLine(parentGfx, [
+ { x: 30, y: 0 },
+ { x: 30, y: element.height }
+ ], {
+ stroke: getStrokeColor(element, defaultStrokeColor)
+ });
+ var text = getSemantic(element).name;
+ renderLaneLabel(parentGfx, text, element);
+ } else {
+ // Collapsed pool draw text inline
+ var text2 = getSemantic(element).name;
+ renderLabel(parentGfx, text2, {
+ box: element, align: 'center-middle',
+ style: {
+ fill: getStrokeColor(element, defaultStrokeColor)
+ }
+ });
+ }
+
+ var participantMultiplicity = !!(getSemantic(element).participantMultiplicity);
+
+ if (participantMultiplicity) {
+ renderer('ParticipantMultiplicityMarker')(parentGfx, element);
+ }
+
+ return lane;
+ },
+ 'bpmn:Lane': function(parentGfx, element, attrs) {
+ var rect = drawRect(parentGfx, element.width, element.height, 0, assign({
+ fill: getFillColor(element, defaultFillColor),
+ fillOpacity: HIGH_FILL_OPACITY,
+ stroke: getStrokeColor(element, defaultStrokeColor)
+ }, attrs));
+
+ var semantic = getSemantic(element);
+
+ if (semantic.$type === 'bpmn:Lane') {
+ var text = semantic.name;
+ renderLaneLabel(parentGfx, text, element);
+ }
+
+ return rect;
+ },
+ 'bpmn:InclusiveGateway': function(parentGfx, element) {
+ var diamond = renderer('bpmn:Gateway')(parentGfx, element);
+
+ /* circle path */
+ drawCircle(parentGfx, element.width, element.height, element.height * 0.24, {
+ strokeWidth: 2.5,
+ fill: getFillColor(element, defaultFillColor),
+ stroke: getStrokeColor(element, defaultStrokeColor)
+ });
+
+ return diamond;
+ },
+ 'bpmn:ExclusiveGateway': function(parentGfx, element) {
+ var diamond = renderer('bpmn:Gateway')(parentGfx, element);
+
+ var pathData = pathMap.getScaledPath('GATEWAY_EXCLUSIVE', {
+ xScaleFactor: 0.4,
+ yScaleFactor: 0.4,
+ containerWidth: element.width,
+ containerHeight: element.height,
+ position: {
+ mx: 0.32,
+ my: 0.3
+ }
+ });
+
+ if ((getDi(element).isMarkerVisible)) {
+ drawPath(parentGfx, pathData, {
+ strokeWidth: 1,
+ fill: getStrokeColor(element, defaultStrokeColor),
+ stroke: getStrokeColor(element, defaultStrokeColor)
+ });
+ }
+
+ return diamond;
+ },
+ 'bpmn:ComplexGateway': function(parentGfx, element) {
+ var diamond = renderer('bpmn:Gateway')(parentGfx, element);
+
+ var pathData = pathMap.getScaledPath('GATEWAY_COMPLEX', {
+ xScaleFactor: 0.5,
+ yScaleFactor:0.5,
+ containerWidth: element.width,
+ containerHeight: element.height,
+ position: {
+ mx: 0.46,
+ my: 0.26
+ }
+ });
+
+ /* complex path */ drawPath(parentGfx, pathData, {
+ strokeWidth: 1,
+ fill: getStrokeColor(element, defaultStrokeColor),
+ stroke: getStrokeColor(element, defaultStrokeColor)
+ });
+
+ return diamond;
+ },
+ 'bpmn:ParallelGateway': function(parentGfx, element) {
+ var diamond = renderer('bpmn:Gateway')(parentGfx, element);
+
+ var pathData = pathMap.getScaledPath('GATEWAY_PARALLEL', {
+ xScaleFactor: 0.6,
+ yScaleFactor:0.6,
+ containerWidth: element.width,
+ containerHeight: element.height,
+ position: {
+ mx: 0.46,
+ my: 0.2
+ }
+ });
+
+ /* parallel path */ drawPath(parentGfx, pathData, {
+ strokeWidth: 1,
+ fill: getStrokeColor(element, defaultStrokeColor),
+ stroke: getStrokeColor(element, defaultStrokeColor)
+ });
+
+ return diamond;
+ },
+ 'bpmn:EventBasedGateway': function(parentGfx, element) {
+
+ var semantic = getSemantic(element);
+
+ var diamond = renderer('bpmn:Gateway')(parentGfx, element);
+
+ /* outer circle path */ drawCircle(parentGfx, element.width, element.height, element.height * 0.20, {
+ strokeWidth: 1,
+ fill: 'none',
+ stroke: getStrokeColor(element, defaultStrokeColor)
+ });
+
+ var type = semantic.eventGatewayType;
+ var instantiate = !!semantic.instantiate;
+
+ function drawEvent() {
+
+ var pathData = pathMap.getScaledPath('GATEWAY_EVENT_BASED', {
+ xScaleFactor: 0.18,
+ yScaleFactor: 0.18,
+ containerWidth: element.width,
+ containerHeight: element.height,
+ position: {
+ mx: 0.36,
+ my: 0.44
+ }
+ });
+
+ var attrs = {
+ strokeWidth: 2,
+ fill: getFillColor(element, 'none'),
+ stroke: getStrokeColor(element, defaultStrokeColor)
+ };
+
+ /* event path */ drawPath(parentGfx, pathData, attrs);
+ }
+
+ if (type === 'Parallel') {
+
+ var pathData = pathMap.getScaledPath('GATEWAY_PARALLEL', {
+ xScaleFactor: 0.4,
+ yScaleFactor:0.4,
+ containerWidth: element.width,
+ containerHeight: element.height,
+ position: {
+ mx: 0.474,
+ my: 0.296
+ }
+ });
+
+ var parallelPath = drawPath(parentGfx, pathData);
+ attr$1(parallelPath, {
+ strokeWidth: 1,
+ fill: 'none'
+ });
+ } else if (type === 'Exclusive') {
+
+ if (!instantiate) {
+ var innerCircle = drawCircle(parentGfx, element.width, element.height, element.height * 0.26);
+ attr$1(innerCircle, {
+ strokeWidth: 1,
+ fill: 'none',
+ stroke: getStrokeColor(element, defaultStrokeColor)
+ });
+ }
+
+ drawEvent();
+ }
+
+
+ return diamond;
+ },
+ 'bpmn:Gateway': function(parentGfx, element) {
+ var attrs = {
+ fill: getFillColor(element, defaultFillColor),
+ fillOpacity: DEFAULT_FILL_OPACITY,
+ stroke: getStrokeColor(element, defaultStrokeColor)
+ };
+
+ return drawDiamond(parentGfx, element.width, element.height, attrs);
+ },
+ 'bpmn:SequenceFlow': function(parentGfx, element) {
+ var pathData = createPathFromConnection(element);
+
+ var fill = getFillColor(element, defaultFillColor),
+ stroke = getStrokeColor(element, defaultStrokeColor);
+
+ var attrs = {
+ strokeLinejoin: 'round',
+ markerEnd: marker('sequenceflow-end', fill, stroke),
+ stroke: getStrokeColor(element, defaultStrokeColor)
+ };
+
+ var path = drawPath(parentGfx, pathData, attrs);
+
+ var sequenceFlow = getSemantic(element);
+
+ var source;
+
+ if (element.source) {
+ source = element.source.businessObject;
+
+ // conditional flow marker
+ if (sequenceFlow.conditionExpression && source.$instanceOf('bpmn:Activity')) {
+ attr$1(path, {
+ markerStart: marker('conditional-flow-marker', fill, stroke)
+ });
+ }
+
+ // default marker
+ if (source.default && (source.$instanceOf('bpmn:Gateway') || source.$instanceOf('bpmn:Activity')) &&
+ source.default === sequenceFlow) {
+ attr$1(path, {
+ markerStart: marker('conditional-default-flow-marker', fill, stroke)
+ });
+ }
+ }
+
+ return path;
+ },
+ 'bpmn:Association': function(parentGfx, element, attrs) {
+
+ var semantic = getSemantic(element);
+
+ var fill = getFillColor(element, defaultFillColor),
+ stroke = getStrokeColor(element, defaultStrokeColor);
+
+ attrs = assign({
+ strokeDasharray: '0.5, 5',
+ strokeLinecap: 'round',
+ strokeLinejoin: 'round',
+ stroke: getStrokeColor(element, defaultStrokeColor)
+ }, attrs || {});
+
+ if (semantic.associationDirection === 'One' ||
+ semantic.associationDirection === 'Both') {
+ attrs.markerEnd = marker('association-end', fill, stroke);
+ }
+
+ if (semantic.associationDirection === 'Both') {
+ attrs.markerStart = marker('association-start', fill, stroke);
+ }
+
+ return drawLine(parentGfx, element.waypoints, attrs);
+ },
+ 'bpmn:DataInputAssociation': function(parentGfx, element) {
+ var fill = getFillColor(element, defaultFillColor),
+ stroke = getStrokeColor(element, defaultStrokeColor);
+
+ return renderer('bpmn:Association')(parentGfx, element, {
+ markerEnd: marker('association-end', fill, stroke)
+ });
+ },
+ 'bpmn:DataOutputAssociation': function(parentGfx, element) {
+ var fill = getFillColor(element, defaultFillColor),
+ stroke = getStrokeColor(element, defaultStrokeColor);
+
+ return renderer('bpmn:Association')(parentGfx, element, {
+ markerEnd: marker('association-end', fill, stroke)
+ });
+ },
+ 'bpmn:MessageFlow': function(parentGfx, element) {
+
+ var semantic = getSemantic(element),
+ di = getDi(element);
+
+ var fill = getFillColor(element, defaultFillColor),
+ stroke = getStrokeColor(element, defaultStrokeColor);
+
+ var pathData = createPathFromConnection(element);
+
+ var attrs = {
+ markerEnd: marker('messageflow-end', fill, stroke),
+ markerStart: marker('messageflow-start', fill, stroke),
+ strokeDasharray: '10, 12',
+ strokeLinecap: 'round',
+ strokeLinejoin: 'round',
+ strokeWidth: '1.5px',
+ stroke: getStrokeColor(element, defaultStrokeColor)
+ };
+
+ var path = drawPath(parentGfx, pathData, attrs);
+
+ if (semantic.messageRef) {
+ var midPoint = path.getPointAtLength(path.getTotalLength() / 2);
+
+ var markerPathData = pathMap.getScaledPath('MESSAGE_FLOW_MARKER', {
+ abspos: {
+ x: midPoint.x,
+ y: midPoint.y
+ }
+ });
+
+ var messageAttrs = { strokeWidth: 1 };
+
+ if (di.messageVisibleKind === 'initiating') {
+ messageAttrs.fill = 'white';
+ messageAttrs.stroke = 'black';
+ } else {
+ messageAttrs.fill = '#888';
+ messageAttrs.stroke = 'white';
+ }
+
+ drawPath(parentGfx, markerPathData, messageAttrs);
+ }
+
+ return path;
+ },
+ 'bpmn:DataObject': function(parentGfx, element) {
+ var pathData = pathMap.getScaledPath('DATA_OBJECT_PATH', {
+ xScaleFactor: 1,
+ yScaleFactor: 1,
+ containerWidth: element.width,
+ containerHeight: element.height,
+ position: {
+ mx: 0.474,
+ my: 0.296
+ }
+ });
+
+ var elementObject = drawPath(parentGfx, pathData, {
+ fill: getFillColor(element, defaultFillColor),
+ fillOpacity: DEFAULT_FILL_OPACITY,
+ stroke: getStrokeColor(element, defaultStrokeColor)
+ });
+
+ var semantic = getSemantic(element);
+
+ if (isCollection(semantic)) {
+ renderDataItemCollection(parentGfx, element);
+ }
+
+ return elementObject;
+ },
+ 'bpmn:DataObjectReference': as('bpmn:DataObject'),
+ 'bpmn:DataInput': function(parentGfx, element) {
+
+ var arrowPathData = pathMap.getRawPath('DATA_ARROW');
+
+ // page
+ var elementObject = renderer('bpmn:DataObject')(parentGfx, element);
+
+ /* input arrow path */ drawPath(parentGfx, arrowPathData, { strokeWidth: 1 });
+
+ return elementObject;
+ },
+ 'bpmn:DataOutput': function(parentGfx, element) {
+ var arrowPathData = pathMap.getRawPath('DATA_ARROW');
+
+ // page
+ var elementObject = renderer('bpmn:DataObject')(parentGfx, element);
+
+ /* output arrow path */ drawPath(parentGfx, arrowPathData, {
+ strokeWidth: 1,
+ fill: 'black'
+ });
+
+ return elementObject;
+ },
+ 'bpmn:DataStoreReference': function(parentGfx, element) {
+ var DATA_STORE_PATH = pathMap.getScaledPath('DATA_STORE', {
+ xScaleFactor: 1,
+ yScaleFactor: 1,
+ containerWidth: element.width,
+ containerHeight: element.height,
+ position: {
+ mx: 0,
+ my: 0.133
+ }
+ });
+
+ var elementStore = drawPath(parentGfx, DATA_STORE_PATH, {
+ strokeWidth: 2,
+ fill: getFillColor(element, defaultFillColor),
+ fillOpacity: DEFAULT_FILL_OPACITY,
+ stroke: getStrokeColor(element, defaultStrokeColor)
+ });
+
+ return elementStore;
+ },
+ 'bpmn:BoundaryEvent': function(parentGfx, element) {
+
+ var semantic = getSemantic(element),
+ cancel = semantic.cancelActivity;
+
+ var attrs = {
+ strokeWidth: 1,
+ fill: getFillColor(element, defaultFillColor),
+ stroke: getStrokeColor(element, defaultStrokeColor)
+ };
+
+ if (!cancel) {
+ attrs.strokeDasharray = '6';
+ attrs.strokeLinecap = 'round';
+ }
+
+ // apply fillOpacity
+ var outerAttrs = assign({}, attrs, {
+ fillOpacity: 1
+ });
+
+ // apply no-fill
+ var innerAttrs = assign({}, attrs, {
+ fill: 'none'
+ });
+
+ var outer = renderer('bpmn:Event')(parentGfx, element, outerAttrs);
+
+ /* inner path */ drawCircle(parentGfx, element.width, element.height, INNER_OUTER_DIST, innerAttrs);
+
+ renderEventContent(element, parentGfx);
+
+ return outer;
+ },
+ 'bpmn:Group': function(parentGfx, element) {
+ return drawRect(parentGfx, element.width, element.height, TASK_BORDER_RADIUS, {
+ strokeWidth: 1,
+ strokeDasharray: '8,3,1,3',
+ fill: 'none',
+ pointerEvents: 'none'
+ });
+ },
+ 'label': function(parentGfx, element) {
+ return renderExternalLabel(parentGfx, element);
+ },
+ 'bpmn:TextAnnotation': function(parentGfx, element) {
+ var style = {
+ 'fill': 'none',
+ 'stroke': 'none'
+ };
+
+ var textElement = drawRect(parentGfx, element.width, element.height, 0, 0, style);
+
+ var textPathData = pathMap.getScaledPath('TEXT_ANNOTATION', {
+ xScaleFactor: 1,
+ yScaleFactor: 1,
+ containerWidth: element.width,
+ containerHeight: element.height,
+ position: {
+ mx: 0.0,
+ my: 0.0
+ }
+ });
+
+ drawPath(parentGfx, textPathData, {
+ stroke: getStrokeColor(element, defaultStrokeColor)
+ });
+
+ var text = getSemantic(element).text || '';
+ renderLabel(parentGfx, text, {
+ box: element,
+ align: 'left-top',
+ padding: 5,
+ style: {
+ fill: getStrokeColor(element, defaultStrokeColor)
+ }
+ });
+
+ return textElement;
+ },
+ 'ParticipantMultiplicityMarker': function(parentGfx, element) {
+ var markerPath = pathMap.getScaledPath('MARKER_PARALLEL', {
+ xScaleFactor: 1,
+ yScaleFactor: 1,
+ containerWidth: element.width,
+ containerHeight: element.height,
+ position: {
+ mx: ((element.width / 2) / element.width),
+ my: (element.height - 15) / element.height
+ }
+ });
+
+ drawMarker('participant-multiplicity', parentGfx, markerPath, {
+ strokeWidth: 1,
+ fill: getFillColor(element, defaultFillColor),
+ stroke: getStrokeColor(element, defaultStrokeColor)
+ });
+ },
+ 'SubProcessMarker': function(parentGfx, element) {
+ var markerRect = drawRect(parentGfx, 14, 14, 0, {
+ strokeWidth: 1,
+ fill: getFillColor(element, defaultFillColor),
+ stroke: getStrokeColor(element, defaultStrokeColor)
+ });
+
+ // Process marker is placed in the middle of the box
+ // therefore fixed values can be used here
+ translate(markerRect, element.width / 2 - 7.5, element.height - 20);
+
+ var markerPath = pathMap.getScaledPath('MARKER_SUB_PROCESS', {
+ xScaleFactor: 1.5,
+ yScaleFactor: 1.5,
+ containerWidth: element.width,
+ containerHeight: element.height,
+ position: {
+ mx: (element.width / 2 - 7.5) / element.width,
+ my: (element.height - 20) / element.height
+ }
+ });
+
+ drawMarker('sub-process', parentGfx, markerPath, {
+ fill: getFillColor(element, defaultFillColor),
+ stroke: getStrokeColor(element, defaultStrokeColor)
+ });
+ },
+ 'ParallelMarker': function(parentGfx, element, position) {
+ var markerPath = pathMap.getScaledPath('MARKER_PARALLEL', {
+ xScaleFactor: 1,
+ yScaleFactor: 1,
+ containerWidth: element.width,
+ containerHeight: element.height,
+ position: {
+ mx: ((element.width / 2 + position.parallel) / element.width),
+ my: (element.height - 20) / element.height
+ }
+ });
+
+ drawMarker('parallel', parentGfx, markerPath, {
+ fill: getFillColor(element, defaultFillColor),
+ stroke: getStrokeColor(element, defaultStrokeColor)
+ });
+ },
+ 'SequentialMarker': function(parentGfx, element, position) {
+ var markerPath = pathMap.getScaledPath('MARKER_SEQUENTIAL', {
+ xScaleFactor: 1,
+ yScaleFactor: 1,
+ containerWidth: element.width,
+ containerHeight: element.height,
+ position: {
+ mx: ((element.width / 2 + position.seq) / element.width),
+ my: (element.height - 19) / element.height
+ }
+ });
+
+ drawMarker('sequential', parentGfx, markerPath, {
+ fill: getFillColor(element, defaultFillColor),
+ stroke: getStrokeColor(element, defaultStrokeColor)
+ });
+ },
+ 'CompensationMarker': function(parentGfx, element, position) {
+ var markerMath = pathMap.getScaledPath('MARKER_COMPENSATION', {
+ xScaleFactor: 1,
+ yScaleFactor: 1,
+ containerWidth: element.width,
+ containerHeight: element.height,
+ position: {
+ mx: ((element.width / 2 + position.compensation) / element.width),
+ my: (element.height - 13) / element.height
+ }
+ });
+
+ drawMarker('compensation', parentGfx, markerMath, {
+ strokeWidth: 1,
+ fill: getFillColor(element, defaultFillColor),
+ stroke: getStrokeColor(element, defaultStrokeColor)
+ });
+ },
+ 'LoopMarker': function(parentGfx, element, position) {
+ var markerPath = pathMap.getScaledPath('MARKER_LOOP', {
+ xScaleFactor: 1,
+ yScaleFactor: 1,
+ containerWidth: element.width,
+ containerHeight: element.height,
+ position: {
+ mx: ((element.width / 2 + position.loop) / element.width),
+ my: (element.height - 7) / element.height
+ }
+ });
+
+ drawMarker('loop', parentGfx, markerPath, {
+ strokeWidth: 1,
+ fill: getFillColor(element, defaultFillColor),
+ stroke: getStrokeColor(element, defaultStrokeColor),
+ strokeLinecap: 'round',
+ strokeMiterlimit: 0.5
+ });
+ },
+ 'AdhocMarker': function(parentGfx, element, position) {
+ var markerPath = pathMap.getScaledPath('MARKER_ADHOC', {
+ xScaleFactor: 1,
+ yScaleFactor: 1,
+ containerWidth: element.width,
+ containerHeight: element.height,
+ position: {
+ mx: ((element.width / 2 + position.adhoc) / element.width),
+ my: (element.height - 15) / element.height
+ }
+ });
+
+ drawMarker('adhoc', parentGfx, markerPath, {
+ strokeWidth: 1,
+ fill: getStrokeColor(element, defaultStrokeColor),
+ stroke: getStrokeColor(element, defaultStrokeColor)
+ });
+ }
+ };
+
+ function attachTaskMarkers(parentGfx, element, taskMarkers) {
+ var obj = getSemantic(element);
+
+ var subprocess = taskMarkers && taskMarkers.indexOf('SubProcessMarker') !== -1;
+ var position;
+
+ if (subprocess) {
+ position = {
+ seq: -21,
+ parallel: -22,
+ compensation: -42,
+ loop: -18,
+ adhoc: 10
+ };
+ } else {
+ position = {
+ seq: -3,
+ parallel: -6,
+ compensation: -27,
+ loop: 0,
+ adhoc: 10
+ };
+ }
+
+ forEach(taskMarkers, function(marker) {
+ renderer(marker)(parentGfx, element, position);
+ });
+
+ if (obj.isForCompensation) {
+ renderer('CompensationMarker')(parentGfx, element, position);
+ }
+
+ if (obj.$type === 'bpmn:AdHocSubProcess') {
+ renderer('AdhocMarker')(parentGfx, element, position);
+ }
+
+ var loopCharacteristics = obj.loopCharacteristics,
+ isSequential = loopCharacteristics && loopCharacteristics.isSequential;
+
+ if (loopCharacteristics) {
+
+ if (isSequential === undefined) {
+ renderer('LoopMarker')(parentGfx, element, position);
+ }
+
+ if (isSequential === false) {
+ renderer('ParallelMarker')(parentGfx, element, position);
+ }
+
+ if (isSequential === true) {
+ renderer('SequentialMarker')(parentGfx, element, position);
+ }
+ }
+ }
+
+ function renderDataItemCollection(parentGfx, element) {
+
+ var yPosition = (element.height - 16) / element.height;
+
+ var pathData = pathMap.getScaledPath('DATA_OBJECT_COLLECTION_PATH', {
+ xScaleFactor: 1,
+ yScaleFactor: 1,
+ containerWidth: element.width,
+ containerHeight: element.height,
+ position: {
+ mx: 0.451,
+ my: yPosition
+ }
+ });
+
+ /* collection path */ drawPath(parentGfx, pathData, {
+ strokeWidth: 2
+ });
+ }
+
+
+ // extension API, use at your own risk
+ this._drawPath = drawPath;
+
+ }
+
+
+ inherits_browser(BpmnRenderer, BaseRenderer);
+
+ BpmnRenderer.$inject = [
+ 'config.bpmnRenderer',
+ 'eventBus',
+ 'styles',
+ 'pathMap',
+ 'canvas',
+ 'textRenderer'
+ ];
+
+
+ BpmnRenderer.prototype.canRender = function(element) {
+ return is$1(element, 'bpmn:BaseElement');
+ };
+
+ BpmnRenderer.prototype.drawShape = function(parentGfx, element) {
+ var type = element.type;
+ var h = this.handlers[type];
+
+ /* jshint -W040 */
+ return h(parentGfx, element);
+ };
+
+ BpmnRenderer.prototype.drawConnection = function(parentGfx, element) {
+ var type = element.type;
+ var h = this.handlers[type];
+
+ /* jshint -W040 */
+ return h(parentGfx, element);
+ };
+
+ BpmnRenderer.prototype.getShapePath = function(element) {
+
+ if (is$1(element, 'bpmn:Event')) {
+ return getCirclePath(element);
+ }
+
+ if (is$1(element, 'bpmn:Activity')) {
+ return getRoundRectPath(element, TASK_BORDER_RADIUS);
+ }
+
+ if (is$1(element, 'bpmn:Gateway')) {
+ return getDiamondPath(element);
+ }
+
+ return getRectPath(element);
+ };
+
+ var DEFAULT_BOX_PADDING = 0;
+
+ var DEFAULT_LABEL_SIZE = {
+ width: 150,
+ height: 50
+ };
+
+
+ function parseAlign(align) {
+
+ var parts = align.split('-');
+
+ return {
+ horizontal: parts[0] || 'center',
+ vertical: parts[1] || 'top'
+ };
+ }
+
+ function parsePadding(padding) {
+
+ if (isObject(padding)) {
+ return assign({ top: 0, left: 0, right: 0, bottom: 0 }, padding);
+ } else {
+ return {
+ top: padding,
+ left: padding,
+ right: padding,
+ bottom: padding
+ };
+ }
+ }
+
+ function getTextBBox(text, fakeText) {
+
+ fakeText.textContent = text;
+
+ var textBBox;
+
+ try {
+ var bbox,
+ emptyLine = text === '';
+
+ // add dummy text, when line is empty to
+ // determine correct height
+ fakeText.textContent = emptyLine ? 'dummy' : text;
+
+ textBBox = fakeText.getBBox();
+
+ // take text rendering related horizontal
+ // padding into account
+ bbox = {
+ width: textBBox.width + textBBox.x * 2,
+ height: textBBox.height
+ };
+
+ if (emptyLine) {
+ // correct width
+ bbox.width = 0;
+ }
+
+ return bbox;
+ } catch (e) {
+ return { width: 0, height: 0 };
+ }
+ }
+
+
+ /**
+ * Layout the next line and return the layouted element.
+ *
+ * Alters the lines passed.
+ *
+ * @param {Array} lines
+ * @return {Object} the line descriptor, an object { width, height, text }
+ */
+ function layoutNext(lines, maxWidth, fakeText) {
+
+ var originalLine = lines.shift(),
+ fitLine = originalLine;
+
+ var textBBox;
+
+ for (;;) {
+ textBBox = getTextBBox(fitLine, fakeText);
+
+ textBBox.width = fitLine ? textBBox.width : 0;
+
+ // try to fit
+ if (fitLine === ' ' || fitLine === '' || textBBox.width < Math.round(maxWidth) || fitLine.length < 2) {
+ return fit(lines, fitLine, originalLine, textBBox);
+ }
+
+ fitLine = shortenLine(fitLine, textBBox.width, maxWidth);
+ }
+ }
+
+ function fit(lines, fitLine, originalLine, textBBox) {
+ if (fitLine.length < originalLine.length) {
+ var remainder = originalLine.slice(fitLine.length).trim();
+
+ lines.unshift(remainder);
+ }
+
+ return {
+ width: textBBox.width,
+ height: textBBox.height,
+ text: fitLine
+ };
+ }
+
+
+ /**
+ * Shortens a line based on spacing and hyphens.
+ * Returns the shortened result on success.
+ *
+ * @param {String} line
+ * @param {Number} maxLength the maximum characters of the string
+ * @return {String} the shortened string
+ */
+ function semanticShorten(line, maxLength) {
+ var parts = line.split(/(\s|-)/g),
+ part,
+ shortenedParts = [],
+ length = 0;
+
+ // try to shorten via spaces + hyphens
+ if (parts.length > 1) {
+ while ((part = parts.shift())) {
+ if (part.length + length < maxLength) {
+ shortenedParts.push(part);
+ length += part.length;
+ } else {
+ // remove previous part, too if hyphen does not fit anymore
+ if (part === '-') {
+ shortenedParts.pop();
+ }
+
+ break;
+ }
+ }
+ }
+
+ return shortenedParts.join('');
+ }
+
+
+ function shortenLine(line, width, maxWidth) {
+ var length = Math.max(line.length * (maxWidth / width), 1);
+
+ // try to shorten semantically (i.e. based on spaces and hyphens)
+ var shortenedLine = semanticShorten(line, length);
+
+ if (!shortenedLine) {
+
+ // force shorten by cutting the long word
+ shortenedLine = line.slice(0, Math.max(Math.round(length - 1), 1));
+ }
+
+ return shortenedLine;
+ }
+
+
+ function getHelperSvg() {
+ var helperSvg = document.getElementById('helper-svg');
+
+ if (!helperSvg) {
+ helperSvg = create('svg');
+
+ attr$1(helperSvg, {
+ id: 'helper-svg',
+ width: 0,
+ height: 0,
+ style: 'visibility: hidden; position: fixed'
+ });
+
+ document.body.appendChild(helperSvg);
+ }
+
+ return helperSvg;
+ }
+
+
+ /**
+ * Creates a new label utility
+ *
+ * @param {Object} config
+ * @param {Dimensions} config.size
+ * @param {Number} config.padding
+ * @param {Object} config.style
+ * @param {String} config.align
+ */
+ function Text(config) {
+
+ this._config = assign({}, {
+ size: DEFAULT_LABEL_SIZE,
+ padding: DEFAULT_BOX_PADDING,
+ style: {},
+ align: 'center-top'
+ }, config || {});
+ }
+
+ /**
+ * Returns the layouted text as an SVG element.
+ *
+ * @param {String} text
+ * @param {Object} options
+ *
+ * @return {SVGElement}
+ */
+ Text.prototype.createText = function(text, options) {
+ return this.layoutText(text, options).element;
+ };
+
+ /**
+ * Returns a labels layouted dimensions.
+ *
+ * @param {String} text to layout
+ * @param {Object} options
+ *
+ * @return {Dimensions}
+ */
+ Text.prototype.getDimensions = function(text, options) {
+ return this.layoutText(text, options).dimensions;
+ };
+
+ /**
+ * Creates and returns a label and its bounding box.
+ *
+ * @method Text#createText
+ *
+ * @param {String} text the text to render on the label
+ * @param {Object} options
+ * @param {String} options.align how to align in the bounding box.
+ * Any of { 'center-middle', 'center-top' },
+ * defaults to 'center-top'.
+ * @param {String} options.style style to be applied to the text
+ * @param {boolean} options.fitBox indicates if box will be recalculated to
+ * fit text
+ *
+ * @return {Object} { element, dimensions }
+ */
+ Text.prototype.layoutText = function(text, options) {
+ var box = assign({}, this._config.size, options.box),
+ style = assign({}, this._config.style, options.style),
+ align = parseAlign(options.align || this._config.align),
+ padding = parsePadding(options.padding !== undefined ? options.padding : this._config.padding),
+ fitBox = options.fitBox || false;
+
+ var lineHeight = getLineHeight(style);
+
+ var lines = text.split(/\r?\n/g),
+ layouted = [];
+
+ var maxWidth = box.width - padding.left - padding.right;
+
+ // ensure correct rendering by attaching helper text node to invisible SVG
+ var helperText = create('text');
+ attr$1(helperText, { x: 0, y: 0 });
+ attr$1(helperText, style);
+
+ var helperSvg = getHelperSvg();
+
+ append(helperSvg, helperText);
+
+ while (lines.length) {
+ layouted.push(layoutNext(lines, maxWidth, helperText));
+ }
+
+ var totalHeight = reduce(layouted, function(sum, line, idx) {
+ return sum + (lineHeight || line.height);
+ }, 0);
+
+ var maxLineWidth = reduce(layouted, function(sum, line, idx) {
+ return line.width > sum ? line.width : sum;
+ }, 0);
+
+ // the y position of the next line
+ var y;
+
+ switch (align.vertical) {
+ case 'middle':
+ y = (box.height - totalHeight) / 2;
+ break;
+
+ default:
+ y = padding.top;
+ }
+
+ // magic number initial offset
+ y -= (lineHeight || layouted[0].height) / 4;
+
+
+ var textElement = create('text');
+
+ attr$1(textElement, style);
+
+ // layout each line taking into account that parent
+ // shape might resize to fit text size
+ forEach(layouted, function(line) {
+
+ var x;
+
+ y += (lineHeight || line.height);
+
+ switch (align.horizontal) {
+ case 'left':
+ x = padding.left;
+ break;
+
+ case 'right':
+ x = ((fitBox ? maxLineWidth : maxWidth)
+ - padding.right - line.width);
+ break;
+
+ default:
+ // aka center
+ x = Math.max((((fitBox ? maxLineWidth : maxWidth)
+ - line.width) / 2 + padding.left), 0);
+ }
+
+ var tspan = create('tspan');
+ attr$1(tspan, { x: x, y: y });
+
+ tspan.textContent = line.text;
+
+ append(textElement, tspan);
+ });
+
+ remove$1(helperText);
+
+ var dimensions = {
+ width: maxLineWidth,
+ height: totalHeight
+ };
+
+ return {
+ dimensions: dimensions,
+ element: textElement
+ };
+ };
+
+
+ function getLineHeight(style) {
+ if ('fontSize' in style && 'lineHeight' in style) {
+ return style.lineHeight * parseInt(style.fontSize, 10);
+ }
+ }
+
+ var DEFAULT_FONT_SIZE = 12;
+ var LINE_HEIGHT_RATIO = 1.2;
+
+
+ function TextRenderer(config) {
+
+ var defaultStyle = assign({
+ fontFamily: 'sans-serif',
+ fontSize: DEFAULT_FONT_SIZE,
+ fontWeight: 'normal',
+ lineHeight: LINE_HEIGHT_RATIO
+ }, config && config.defaultStyle || {});
+
+ var fontSize = parseInt(defaultStyle.fontSize, 10) - 1;
+
+ var externalStyle = assign({}, defaultStyle, {
+ fontSize: fontSize
+ }, config && config.externalStyle || {});
+
+ var textUtil = new Text({
+ style: defaultStyle
+ });
+
+ /**
+ * Get the new bounds of an externally rendered,
+ * layouted label.
+ *
+ * @param {Bounds} bounds
+ * @param {String} text
+ *
+ * @return {Bounds}
+ */
+ this.getLayoutedBounds = function(bounds, text) {
+
+ var layoutedDimensions = textUtil.getDimensions(text, {
+ box: {
+ width: 90,
+ height: 30,
+ x: bounds.width / 2 + bounds.x,
+ y: bounds.height / 2 + bounds.y
+ },
+ style: externalStyle
+ });
+
+ // resize label shape to fit label text
+ return {
+ x: Math.round(bounds.x + bounds.width / 2 - layoutedDimensions.width / 2),
+ y: Math.round(bounds.y),
+ width: Math.ceil(layoutedDimensions.width),
+ height: Math.ceil(layoutedDimensions.height)
+ };
+
+ };
+
+ /**
+ * Create a layouted text element.
+ *
+ * @param {String} text
+ * @param {Object} [options]
+ *
+ * @return {SVGElement} rendered text
+ */
+ this.createText = function(text, options) {
+ return textUtil.createText(text, options || {});
+ };
+
+ /**
+ * Get default text style.
+ */
+ this.getDefaultStyle = function() {
+ return defaultStyle;
+ };
+
+ /**
+ * Get the external text style.
+ */
+ this.getExternalStyle = function() {
+ return externalStyle;
+ };
+
+ }
+
+ TextRenderer.$inject = [
+ 'config.textRenderer'
+ ];
+
+ /**
+ * Map containing SVG paths needed by BpmnRenderer.
+ */
+
+ function PathMap() {
+
+ /**
+ * Contains a map of path elements
+ *
+ * Path definition
+ * A parameterized path is defined like this:
+ *
+ * 'GATEWAY_PARALLEL': {
+ * d: 'm {mx},{my} {e.x0},0 0,{e.x1} {e.x1},0 0,{e.y0} -{e.x1},0 0,{e.y1} ' +
+ '-{e.x0},0 0,-{e.y1} -{e.x1},0 0,-{e.y0} {e.x1},0 z',
+ * height: 17.5,
+ * width: 17.5,
+ * heightElements: [2.5, 7.5],
+ * widthElements: [2.5, 7.5]
+ * }
+ *
+ * It's important to specify a correct height and width for the path as the scaling
+ * is based on the ratio between the specified height and width in this object and the
+ * height and width that is set as scale target (Note x,y coordinates will be scaled with
+ * individual ratios).
+ * The 'heightElements ' and 'widthElements ' array must contain the values that will be scaled.
+ * The scaling is based on the computed ratios.
+ * Coordinates on the y axis should be in the heightElement 's array, they will be scaled using
+ * the computed ratio coefficient.
+ * In the parameterized path the scaled values can be accessed through the 'e' object in {} brackets.
+ *
+ * The values for the y axis can be accessed in the path string using {e.y0}, {e.y1}, ....
+ * The values for the x axis can be accessed in the path string using {e.x0}, {e.x1}, ....
+ *
+ * The numbers x0, x1 respectively y0, y1, ... map to the corresponding array index.
+ *
+ */
+ this.pathMap = {
+ 'EVENT_MESSAGE': {
+ d: 'm {mx},{my} l 0,{e.y1} l {e.x1},0 l 0,-{e.y1} z l {e.x0},{e.y0} l {e.x0},-{e.y0}',
+ height: 36,
+ width: 36,
+ heightElements: [6, 14],
+ widthElements: [10.5, 21]
+ },
+ 'EVENT_SIGNAL': {
+ d: 'M {mx},{my} l {e.x0},{e.y0} l -{e.x1},0 Z',
+ height: 36,
+ width: 36,
+ heightElements: [18],
+ widthElements: [10, 20]
+ },
+ 'EVENT_ESCALATION': {
+ d: 'M {mx},{my} l {e.x0},{e.y0} l -{e.x0},-{e.y1} l -{e.x0},{e.y1} Z',
+ height: 36,
+ width: 36,
+ heightElements: [20, 7],
+ widthElements: [8]
+ },
+ 'EVENT_CONDITIONAL': {
+ d: 'M {e.x0},{e.y0} l {e.x1},0 l 0,{e.y2} l -{e.x1},0 Z ' +
+ 'M {e.x2},{e.y3} l {e.x0},0 ' +
+ 'M {e.x2},{e.y4} l {e.x0},0 ' +
+ 'M {e.x2},{e.y5} l {e.x0},0 ' +
+ 'M {e.x2},{e.y6} l {e.x0},0 ' +
+ 'M {e.x2},{e.y7} l {e.x0},0 ' +
+ 'M {e.x2},{e.y8} l {e.x0},0 ',
+ height: 36,
+ width: 36,
+ heightElements: [8.5, 14.5, 18, 11.5, 14.5, 17.5, 20.5, 23.5, 26.5],
+ widthElements: [10.5, 14.5, 12.5]
+ },
+ 'EVENT_LINK': {
+ d: 'm {mx},{my} 0,{e.y0} -{e.x1},0 0,{e.y1} {e.x1},0 0,{e.y0} {e.x0},-{e.y2} -{e.x0},-{e.y2} z',
+ height: 36,
+ width: 36,
+ heightElements: [4.4375, 6.75, 7.8125],
+ widthElements: [9.84375, 13.5]
+ },
+ 'EVENT_ERROR': {
+ d: 'm {mx},{my} {e.x0},-{e.y0} {e.x1},-{e.y1} {e.x2},{e.y2} {e.x3},-{e.y3} -{e.x4},{e.y4} -{e.x5},-{e.y5} z',
+ height: 36,
+ width: 36,
+ heightElements: [0.023, 8.737, 8.151, 16.564, 10.591, 8.714],
+ widthElements: [0.085, 6.672, 6.97, 4.273, 5.337, 6.636]
+ },
+ 'EVENT_CANCEL_45': {
+ d: 'm {mx},{my} -{e.x1},0 0,{e.x0} {e.x1},0 0,{e.y1} {e.x0},0 ' +
+ '0,-{e.y1} {e.x1},0 0,-{e.y0} -{e.x1},0 0,-{e.y1} -{e.x0},0 z',
+ height: 36,
+ width: 36,
+ heightElements: [4.75, 8.5],
+ widthElements: [4.75, 8.5]
+ },
+ 'EVENT_COMPENSATION': {
+ d: 'm {mx},{my} {e.x0},-{e.y0} 0,{e.y1} z m {e.x1},-{e.y2} {e.x2},-{e.y3} 0,{e.y1} -{e.x2},-{e.y3} z',
+ height: 36,
+ width: 36,
+ heightElements: [6.5, 13, 0.4, 6.1],
+ widthElements: [9, 9.3, 8.7]
+ },
+ 'EVENT_TIMER_WH': {
+ d: 'M {mx},{my} l {e.x0},-{e.y0} m -{e.x0},{e.y0} l {e.x1},{e.y1} ',
+ height: 36,
+ width: 36,
+ heightElements: [10, 2],
+ widthElements: [3, 7]
+ },
+ 'EVENT_TIMER_LINE': {
+ d: 'M {mx},{my} ' +
+ 'm {e.x0},{e.y0} l -{e.x1},{e.y1} ',
+ height: 36,
+ width: 36,
+ heightElements: [10, 3],
+ widthElements: [0, 0]
+ },
+ 'EVENT_MULTIPLE': {
+ d:'m {mx},{my} {e.x1},-{e.y0} {e.x1},{e.y0} -{e.x0},{e.y1} -{e.x2},0 z',
+ height: 36,
+ width: 36,
+ heightElements: [6.28099, 12.56199],
+ widthElements: [3.1405, 9.42149, 12.56198]
+ },
+ 'EVENT_PARALLEL_MULTIPLE': {
+ d:'m {mx},{my} {e.x0},0 0,{e.y1} {e.x1},0 0,{e.y0} -{e.x1},0 0,{e.y1} ' +
+ '-{e.x0},0 0,-{e.y1} -{e.x1},0 0,-{e.y0} {e.x1},0 z',
+ height: 36,
+ width: 36,
+ heightElements: [2.56228, 7.68683],
+ widthElements: [2.56228, 7.68683]
+ },
+ 'GATEWAY_EXCLUSIVE': {
+ d:'m {mx},{my} {e.x0},{e.y0} {e.x1},{e.y0} {e.x2},0 {e.x4},{e.y2} ' +
+ '{e.x4},{e.y1} {e.x2},0 {e.x1},{e.y3} {e.x0},{e.y3} ' +
+ '{e.x3},0 {e.x5},{e.y1} {e.x5},{e.y2} {e.x3},0 z',
+ height: 17.5,
+ width: 17.5,
+ heightElements: [8.5, 6.5312, -6.5312, -8.5],
+ widthElements: [6.5, -6.5, 3, -3, 5, -5]
+ },
+ 'GATEWAY_PARALLEL': {
+ d:'m {mx},{my} 0,{e.y1} -{e.x1},0 0,{e.y0} {e.x1},0 0,{e.y1} {e.x0},0 ' +
+ '0,-{e.y1} {e.x1},0 0,-{e.y0} -{e.x1},0 0,-{e.y1} -{e.x0},0 z',
+ height: 30,
+ width: 30,
+ heightElements: [5, 12.5],
+ widthElements: [5, 12.5]
+ },
+ 'GATEWAY_EVENT_BASED': {
+ d:'m {mx},{my} {e.x0},{e.y0} {e.x0},{e.y1} {e.x1},{e.y2} {e.x2},0 z',
+ height: 11,
+ width: 11,
+ heightElements: [-6, 6, 12, -12],
+ widthElements: [9, -3, -12]
+ },
+ 'GATEWAY_COMPLEX': {
+ d:'m {mx},{my} 0,{e.y0} -{e.x0},-{e.y1} -{e.x1},{e.y2} {e.x0},{e.y1} -{e.x2},0 0,{e.y3} ' +
+ '{e.x2},0 -{e.x0},{e.y1} l {e.x1},{e.y2} {e.x0},-{e.y1} 0,{e.y0} {e.x3},0 0,-{e.y0} {e.x0},{e.y1} ' +
+ '{e.x1},-{e.y2} -{e.x0},-{e.y1} {e.x2},0 0,-{e.y3} -{e.x2},0 {e.x0},-{e.y1} -{e.x1},-{e.y2} ' +
+ '-{e.x0},{e.y1} 0,-{e.y0} -{e.x3},0 z',
+ height: 17.125,
+ width: 17.125,
+ heightElements: [4.875, 3.4375, 2.125, 3],
+ widthElements: [3.4375, 2.125, 4.875, 3]
+ },
+ 'DATA_OBJECT_PATH': {
+ d:'m 0,0 {e.x1},0 {e.x0},{e.y0} 0,{e.y1} -{e.x2},0 0,-{e.y2} {e.x1},0 0,{e.y0} {e.x0},0',
+ height: 61,
+ width: 51,
+ heightElements: [10, 50, 60],
+ widthElements: [10, 40, 50, 60]
+ },
+ 'DATA_OBJECT_COLLECTION_PATH': {
+ d:'m {mx}, {my} ' +
+ 'm 0 15 l 0 -15 ' +
+ 'm 4 15 l 0 -15 ' +
+ 'm 4 15 l 0 -15 ',
+ height: 61,
+ width: 51,
+ heightElements: [12],
+ widthElements: [1, 6, 12, 15]
+ },
+ 'DATA_ARROW': {
+ d:'m 5,9 9,0 0,-3 5,5 -5,5 0,-3 -9,0 z',
+ height: 61,
+ width: 51,
+ heightElements: [],
+ widthElements: []
+ },
+ 'DATA_STORE': {
+ d:'m {mx},{my} ' +
+ 'l 0,{e.y2} ' +
+ 'c {e.x0},{e.y1} {e.x1},{e.y1} {e.x2},0 ' +
+ 'l 0,-{e.y2} ' +
+ 'c -{e.x0},-{e.y1} -{e.x1},-{e.y1} -{e.x2},0' +
+ 'c {e.x0},{e.y1} {e.x1},{e.y1} {e.x2},0 ' +
+ 'm -{e.x2},{e.y0}' +
+ 'c {e.x0},{e.y1} {e.x1},{e.y1} {e.x2},0' +
+ 'm -{e.x2},{e.y0}' +
+ 'c {e.x0},{e.y1} {e.x1},{e.y1} {e.x2},0',
+ height: 61,
+ width: 61,
+ heightElements: [7, 10, 45],
+ widthElements: [2, 58, 60]
+ },
+ 'TEXT_ANNOTATION': {
+ d: 'm {mx}, {my} m 10,0 l -10,0 l 0,{e.y0} l 10,0',
+ height: 30,
+ width: 10,
+ heightElements: [30],
+ widthElements: [10]
+ },
+ 'MARKER_SUB_PROCESS': {
+ d: 'm{mx},{my} m 7,2 l 0,10 m -5,-5 l 10,0',
+ height: 10,
+ width: 10,
+ heightElements: [],
+ widthElements: []
+ },
+ 'MARKER_PARALLEL': {
+ d: 'm{mx},{my} m 3,2 l 0,10 m 3,-10 l 0,10 m 3,-10 l 0,10',
+ height: 10,
+ width: 10,
+ heightElements: [],
+ widthElements: []
+ },
+ 'MARKER_SEQUENTIAL': {
+ d: 'm{mx},{my} m 0,3 l 10,0 m -10,3 l 10,0 m -10,3 l 10,0',
+ height: 10,
+ width: 10,
+ heightElements: [],
+ widthElements: []
+ },
+ 'MARKER_COMPENSATION': {
+ d: 'm {mx},{my} 7,-5 0,10 z m 7.1,-0.3 6.9,-4.7 0,10 -6.9,-4.7 z',
+ height: 10,
+ width: 21,
+ heightElements: [],
+ widthElements: []
+ },
+ 'MARKER_LOOP': {
+ d: 'm {mx},{my} c 3.526979,0 6.386161,-2.829858 6.386161,-6.320661 0,-3.490806 -2.859182,-6.320661 ' +
+ '-6.386161,-6.320661 -3.526978,0 -6.38616,2.829855 -6.38616,6.320661 0,1.745402 ' +
+ '0.714797,3.325567 1.870463,4.469381 0.577834,0.571908 1.265885,1.034728 2.029916,1.35457 ' +
+ 'l -0.718163,-3.909793 m 0.718163,3.909793 -3.885211,0.802902',
+ height: 13.9,
+ width: 13.7,
+ heightElements: [],
+ widthElements: []
+ },
+ 'MARKER_ADHOC': {
+ d: 'm {mx},{my} m 0.84461,2.64411 c 1.05533,-1.23780996 2.64337,-2.07882 4.29653,-1.97997996 2.05163,0.0805 ' +
+ '3.85579,1.15803 5.76082,1.79107 1.06385,0.34139996 2.24454,0.1438 3.18759,-0.43767 0.61743,-0.33642 ' +
+ '1.2775,-0.64078 1.7542,-1.17511 0,0.56023 0,1.12046 0,1.6807 -0.98706,0.96237996 -2.29792,1.62393996 ' +
+ '-3.6918,1.66181996 -1.24459,0.0927 -2.46671,-0.2491 -3.59505,-0.74812 -1.35789,-0.55965 ' +
+ '-2.75133,-1.33436996 -4.27027,-1.18121996 -1.37741,0.14601 -2.41842,1.13685996 -3.44288,1.96782996 z',
+ height: 4,
+ width: 15,
+ heightElements: [],
+ widthElements: []
+ },
+ 'TASK_TYPE_SEND': {
+ d: 'm {mx},{my} l 0,{e.y1} l {e.x1},0 l 0,-{e.y1} z l {e.x0},{e.y0} l {e.x0},-{e.y0}',
+ height: 14,
+ width: 21,
+ heightElements: [6, 14],
+ widthElements: [10.5, 21]
+ },
+ 'TASK_TYPE_SCRIPT': {
+ d: 'm {mx},{my} c 9.966553,-6.27276 -8.000926,-7.91932 2.968968,-14.938 l -8.802728,0 ' +
+ 'c -10.969894,7.01868 6.997585,8.66524 -2.968967,14.938 z ' +
+ 'm -7,-12 l 5,0 ' +
+ 'm -4.5,3 l 4.5,0 ' +
+ 'm -3,3 l 5,0' +
+ 'm -4,3 l 5,0',
+ height: 15,
+ width: 12.6,
+ heightElements: [6, 14],
+ widthElements: [10.5, 21]
+ },
+ 'TASK_TYPE_USER_1': {
+ d: 'm {mx},{my} c 0.909,-0.845 1.594,-2.049 1.594,-3.385 0,-2.554 -1.805,-4.62199999 ' +
+ '-4.357,-4.62199999 -2.55199998,0 -4.28799998,2.06799999 -4.28799998,4.62199999 0,1.348 ' +
+ '0.974,2.562 1.89599998,3.405 -0.52899998,0.187 -5.669,2.097 -5.794,4.7560005 v 6.718 ' +
+ 'h 17 v -6.718 c 0,-2.2980005 -5.5279996,-4.5950005 -6.0509996,-4.7760005 z' +
+ 'm -8,6 l 0,5.5 m 11,0 l 0,-5'
+ },
+ 'TASK_TYPE_USER_2': {
+ d: 'm {mx},{my} m 2.162,1.009 c 0,2.4470005 -2.158,4.4310005 -4.821,4.4310005 ' +
+ '-2.66499998,0 -4.822,-1.981 -4.822,-4.4310005 '
+ },
+ 'TASK_TYPE_USER_3': {
+ d: 'm {mx},{my} m -6.9,-3.80 c 0,0 2.25099998,-2.358 4.27399998,-1.177 2.024,1.181 4.221,1.537 ' +
+ '4.124,0.965 -0.098,-0.57 -0.117,-3.79099999 -4.191,-4.13599999 -3.57499998,0.001 ' +
+ '-4.20799998,3.36699999 -4.20699998,4.34799999 z'
+ },
+ 'TASK_TYPE_MANUAL': {
+ d: 'm {mx},{my} c 0.234,-0.01 5.604,0.008 8.029,0.004 0.808,0 1.271,-0.172 1.417,-0.752 0.227,-0.898 ' +
+ '-0.334,-1.314 -1.338,-1.316 -2.467,-0.01 -7.886,-0.004 -8.108,-0.004 -0.014,-0.079 0.016,-0.533 0,-0.61 ' +
+ '0.195,-0.042 8.507,0.006 9.616,0.002 0.877,-0.007 1.35,-0.438 1.353,-1.208 0.003,-0.768 -0.479,-1.09 ' +
+ '-1.35,-1.091 -2.968,-0.002 -9.619,-0.013 -9.619,-0.013 v -0.591 c 0,0 5.052,-0.016 7.225,-0.016 ' +
+ '0.888,-0.002 1.354,-0.416 1.351,-1.193 -0.006,-0.761 -0.492,-1.196 -1.361,-1.196 -3.473,-0.005 ' +
+ '-10.86,-0.003 -11.0829995,-0.003 -0.022,-0.047 -0.045,-0.094 -0.069,-0.139 0.3939995,-0.319 ' +
+ '2.0409995,-1.626 2.4149995,-2.017 0.469,-0.4870005 0.519,-1.1650005 0.162,-1.6040005 -0.414,-0.511 ' +
+ '-0.973,-0.5 -1.48,-0.236 -1.4609995,0.764 -6.5999995,3.6430005 -7.7329995,4.2710005 -0.9,0.499 ' +
+ '-1.516,1.253 -1.882,2.19 -0.37000002,0.95 -0.17,2.01 -0.166,2.979 0.004,0.718 -0.27300002,1.345 ' +
+ '-0.055,2.063 0.629,2.087 2.425,3.312 4.859,3.318 4.6179995,0.014 9.2379995,-0.139 13.8569995,-0.158 ' +
+ '0.755,-0.004 1.171,-0.301 1.182,-1.033 0.012,-0.754 -0.423,-0.969 -1.183,-0.973 -1.778,-0.01 ' +
+ '-5.824,-0.004 -6.04,-0.004 10e-4,-0.084 0.003,-0.586 10e-4,-0.67 z'
+ },
+ 'TASK_TYPE_INSTANTIATING_SEND': {
+ d: 'm {mx},{my} l 0,8.4 l 12.6,0 l 0,-8.4 z l 6.3,3.6 l 6.3,-3.6'
+ },
+ 'TASK_TYPE_SERVICE': {
+ d: 'm {mx},{my} v -1.71335 c 0.352326,-0.0705 0.703932,-0.17838 1.047628,-0.32133 ' +
+ '0.344416,-0.14465 0.665822,-0.32133 0.966377,-0.52145 l 1.19431,1.18005 1.567487,-1.57688 ' +
+ '-1.195028,-1.18014 c 0.403376,-0.61394 0.683079,-1.29908 0.825447,-2.01824 l 1.622133,-0.01 ' +
+ 'v -2.2196 l -1.636514,0.01 c -0.07333,-0.35153 -0.178319,-0.70024 -0.323564,-1.04372 ' +
+ '-0.145244,-0.34406 -0.321407,-0.6644 -0.522735,-0.96217 l 1.131035,-1.13631 -1.583305,-1.56293 ' +
+ '-1.129598,1.13589 c -0.614052,-0.40108 -1.302883,-0.68093 -2.022633,-0.82247 l 0.0093,-1.61852 ' +
+ 'h -2.241173 l 0.0042,1.63124 c -0.353763,0.0736 -0.705369,0.17977 -1.049785,0.32371 -0.344415,0.14437 ' +
+ '-0.665102,0.32092 -0.9635006,0.52046 l -1.1698628,-1.15823 -1.5667691,1.5792 1.1684265,1.15669 ' +
+ 'c -0.4026573,0.61283 -0.68308,1.29797 -0.8247287,2.01713 l -1.6588041,0.003 v 2.22174 ' +
+ 'l 1.6724648,-0.006 c 0.073327,0.35077 0.1797598,0.70243 0.3242851,1.04472 0.1452428,0.34448 ' +
+ '0.3214064,0.6644 0.5227339,0.96066 l -1.1993431,1.19723 1.5840256,1.56011 1.1964668,-1.19348 ' +
+ 'c 0.6140517,0.40346 1.3028827,0.68232 2.0233517,0.82331 l 7.19e-4,1.69892 h 2.226848 z ' +
+ 'm 0.221462,-3.9957 c -1.788948,0.7502 -3.8576,-0.0928 -4.6097055,-1.87438 -0.7521065,-1.78321 ' +
+ '0.090598,-3.84627 1.8802645,-4.59604 1.78823,-0.74936 3.856881,0.0929 4.608987,1.87437 ' +
+ '0.752106,1.78165 -0.0906,3.84612 -1.879546,4.59605 z'
+ },
+ 'TASK_TYPE_SERVICE_FILL': {
+ d: 'm {mx},{my} c -1.788948,0.7502 -3.8576,-0.0928 -4.6097055,-1.87438 -0.7521065,-1.78321 ' +
+ '0.090598,-3.84627 1.8802645,-4.59604 1.78823,-0.74936 3.856881,0.0929 4.608987,1.87437 ' +
+ '0.752106,1.78165 -0.0906,3.84612 -1.879546,4.59605 z'
+ },
+ 'TASK_TYPE_BUSINESS_RULE_HEADER': {
+ d: 'm {mx},{my} 0,4 20,0 0,-4 z'
+ },
+ 'TASK_TYPE_BUSINESS_RULE_MAIN': {
+ d: 'm {mx},{my} 0,12 20,0 0,-12 z' +
+ 'm 0,8 l 20,0 ' +
+ 'm -13,-4 l 0,8'
+ },
+ 'MESSAGE_FLOW_MARKER': {
+ d: 'm {mx},{my} m -10.5 ,-7 l 0,14 l 21,0 l 0,-14 z l 10.5,6 l 10.5,-6'
+ }
+ };
+
+ this.getRawPath = function getRawPath(pathId) {
+ return this.pathMap[pathId].d;
+ };
+
+ /**
+ * Scales the path to the given height and width.
+ * Use case
+ * Use case is to scale the content of elements (event, gateways) based
+ * on the element bounding box's size.
+ *
+ * Why not transform
+ * Scaling a path with transform() will also scale the stroke and IE does not support
+ * the option 'non-scaling-stroke' to prevent this.
+ * Also there are use cases where only some parts of a path should be
+ * scaled.
+ *
+ * @param {String} pathId The ID of the path.
+ * @param {Object} param
+ * Example param object scales the path to 60% size of the container (data.width, data.height).
+ *
+ * {
+ * xScaleFactor: 0.6,
+ * yScaleFactor:0.6,
+ * containerWidth: data.width,
+ * containerHeight: data.height,
+ * position: {
+ * mx: 0.46,
+ * my: 0.2,
+ * }
+ * }
+ *
+ *
+ * targetpathwidth = xScaleFactor * containerWidth
+ * targetpathheight = yScaleFactor * containerHeight
+ * Position is used to set the starting coordinate of the path. M is computed:
+ *
+ * position.x * containerWidth
+ * position.y * containerHeight
+ *
+ * Center of the container position: {
+ * mx: 0.5,
+ * my: 0.5,
+ * }
+ * Upper left corner of the container
+ * position: {
+ * mx: 0.0,
+ * my: 0.0,
+ * }
+ *
+ *
+ *
+ *
+ */
+ this.getScaledPath = function getScaledPath(pathId, param) {
+ var rawPath = this.pathMap[pathId];
+
+ // positioning
+ // compute the start point of the path
+ var mx, my;
+
+ if (param.abspos) {
+ mx = param.abspos.x;
+ my = param.abspos.y;
+ } else {
+ mx = param.containerWidth * param.position.mx;
+ my = param.containerHeight * param.position.my;
+ }
+
+ var coordinates = {}; // map for the scaled coordinates
+ if (param.position) {
+
+ // path
+ var heightRatio = (param.containerHeight / rawPath.height) * param.yScaleFactor;
+ var widthRatio = (param.containerWidth / rawPath.width) * param.xScaleFactor;
+
+
+ // Apply height ratio
+ for (var heightIndex = 0; heightIndex < rawPath.heightElements.length; heightIndex++) {
+ coordinates['y' + heightIndex] = rawPath.heightElements[heightIndex] * heightRatio;
+ }
+
+ // Apply width ratio
+ for (var widthIndex = 0; widthIndex < rawPath.widthElements.length; widthIndex++) {
+ coordinates['x' + widthIndex] = rawPath.widthElements[widthIndex] * widthRatio;
+ }
+ }
+
+ // Apply value to raw path
+ var path = format(
+ rawPath.d, {
+ mx: mx,
+ my: my,
+ e: coordinates
+ }
+ );
+ return path;
+ };
+ }
+
+ // helpers //////////////////////
+
+ // copied from https://github.com/adobe-webplatform/Snap.svg/blob/master/src/svg.js
+ var tokenRegex = /\{([^}]+)\}/g,
+ objNotationRegex = /(?:(?:^|\.)(.+?)(?=\[|\.|$|\()|\[('|")(.+?)\2\])(\(\))?/g; // matches .xxxxx or ["xxxxx"] to run over object properties
+
+ function replacer(all, key, obj) {
+ var res = obj;
+ key.replace(objNotationRegex, function(all, name, quote, quotedName, isFunc) {
+ name = name || quotedName;
+ if (res) {
+ if (name in res) {
+ res = res[name];
+ }
+ typeof res == 'function' && isFunc && (res = res());
+ }
+ });
+ res = (res == null || res == obj ? all : res) + '';
+
+ return res;
+ }
+
+ function format(str, obj) {
+ return String(str).replace(tokenRegex, function(all, key) {
+ return replacer(all, key, obj);
+ });
+ }
+
+ var DrawModule$1 = {
+ __init__: [ 'bpmnRenderer' ],
+ bpmnRenderer: [ 'type', BpmnRenderer ],
+ textRenderer: [ 'type', TextRenderer ],
+ pathMap: [ 'type', PathMap ]
+ };
+
+ /**
+ * A simple translation stub to be used for multi-language support
+ * in diagrams. Can be easily replaced with a more sophisticated
+ * solution.
+ *
+ * @example
+ *
+ * // use it inside any diagram component by injecting `translate`.
+ *
+ * function MyService(translate) {
+ * alert(translate('HELLO {you}', { you: 'You!' }));
+ * }
+ *
+ * @param {String} template to interpolate
+ * @param {Object} [replacements] a map with substitutes
+ *
+ * @return {String} the translated string
+ */
+ function translate$1(template, replacements) {
+
+ replacements = replacements || {};
+
+ return template.replace(/{([^}]+)}/g, function(_, key) {
+ return replacements[key] || '{' + key + '}';
+ });
+ }
+
+ var translate$2 = {
+ translate: [ 'value', translate$1 ]
+ };
+
+ var DEFAULT_LABEL_SIZE$1 = {
+ width: 90,
+ height: 20
+ };
+
+ var FLOW_LABEL_INDENT = 15;
+
+
+ /**
+ * Returns true if the given semantic has an external label
+ *
+ * @param {BpmnElement} semantic
+ * @return {Boolean} true if has label
+ */
+ function isLabelExternal(semantic) {
+ return is$1(semantic, 'bpmn:Event') ||
+ is$1(semantic, 'bpmn:Gateway') ||
+ is$1(semantic, 'bpmn:DataStoreReference') ||
+ is$1(semantic, 'bpmn:DataObjectReference') ||
+ is$1(semantic, 'bpmn:SequenceFlow') ||
+ is$1(semantic, 'bpmn:MessageFlow');
+ }
+
+ /**
+ * Get the position for sequence flow labels
+ *
+ * @param {Array} waypoints
+ * @return {Point} the label position
+ */
+ function getFlowLabelPosition(waypoints) {
+
+ // get the waypoints mid
+ var mid = waypoints.length / 2 - 1;
+
+ var first = waypoints[Math.floor(mid)];
+ var second = waypoints[Math.ceil(mid + 0.01)];
+
+ // get position
+ var position = getWaypointsMid(waypoints);
+
+ // calculate angle
+ var angle = Math.atan((second.y - first.y) / (second.x - first.x));
+
+ var x = position.x,
+ y = position.y;
+
+ if (Math.abs(angle) < Math.PI / 2) {
+ y -= FLOW_LABEL_INDENT;
+ } else {
+ x += FLOW_LABEL_INDENT;
+ }
+
+ return { x: x, y: y };
+ }
+
+
+ /**
+ * Get the middle of a number of waypoints
+ *
+ * @param {Array} waypoints
+ * @return {Point} the mid point
+ */
+ function getWaypointsMid(waypoints) {
+
+ var mid = waypoints.length / 2 - 1;
+
+ var first = waypoints[Math.floor(mid)];
+ var second = waypoints[Math.ceil(mid + 0.01)];
+
+ return {
+ x: first.x + (second.x - first.x) / 2,
+ y: first.y + (second.y - first.y) / 2
+ };
+ }
+
+
+ function getExternalLabelMid(element) {
+
+ if (element.waypoints) {
+ return getFlowLabelPosition(element.waypoints);
+ } else {
+ return {
+ x: element.x + element.width / 2,
+ y: element.y + element.height + DEFAULT_LABEL_SIZE$1.height / 2
+ };
+ }
+ }
+
+
+ /**
+ * Returns the bounds of an elements label, parsed from the elements DI or
+ * generated from its bounds.
+ *
+ * @param {BpmnElement} semantic
+ * @param {djs.model.Base} element
+ */
+ function getExternalLabelBounds(semantic, element) {
+
+ var mid,
+ size$$1,
+ bounds,
+ di = semantic.di,
+ label = di.label;
+
+ if (label && label.bounds) {
+ bounds = label.bounds;
+
+ size$$1 = {
+ width: Math.max(DEFAULT_LABEL_SIZE$1.width, bounds.width),
+ height: bounds.height
+ };
+
+ mid = {
+ x: bounds.x + bounds.width / 2,
+ y: bounds.y + bounds.height / 2
+ };
+ } else {
+
+ mid = getExternalLabelMid(element);
+
+ size$$1 = DEFAULT_LABEL_SIZE$1;
+ }
+
+ return assign({
+ x: mid.x - size$$1.width / 2,
+ y: mid.y - size$$1.height / 2
+ }, size$$1);
+ }
+
+ /**
+ * Computes the distance between two points
+ *
+ * @param {Point} p
+ * @param {Point} q
+ *
+ * @return {Number} distance
+ */
+
+ /**
+ * This file contains portions that got extraced from Snap.svg (licensed Apache-2.0).
+ *
+ * @see https://github.com/adobe-webplatform/Snap.svg/blob/master/src/path.js
+ */
+
+ /* eslint no-fallthrough: "off" */
+
+ var math = Math,
+ PI = math.PI;
+
+ function roundPoint(point) {
+
+ return {
+ x: Math.round(point.x),
+ y: Math.round(point.y)
+ };
+ }
+
+
+ /**
+ * Get the mid of the given bounds or point.
+ *
+ * @param {Bounds|Point} bounds
+ *
+ * @return {Point}
+ */
+ function getMid(bounds) {
+ return roundPoint({
+ x: bounds.x + (bounds.width || 0) / 2,
+ y: bounds.y + (bounds.height || 0) / 2
+ });
+ }
+
+ function elementData(semantic, attrs) {
+ return assign({
+ id: semantic.id,
+ type: semantic.$type,
+ businessObject: semantic
+ }, attrs);
+ }
+
+ function collectWaypoints(waypoints) {
+ return map(waypoints, function(p) {
+ return { x: p.x, y: p.y };
+ });
+ }
+
+ function notYetDrawn(translate, semantic, refSemantic, property) {
+ return new Error(translate('element {element} referenced by {referenced}#{property} not yet drawn', {
+ element: elementToString(refSemantic),
+ referenced: elementToString(semantic),
+ property: property
+ }));
+ }
+
+
+ /**
+ * An importer that adds bpmn elements to the canvas
+ *
+ * @param {EventBus} eventBus
+ * @param {Canvas} canvas
+ * @param {ElementFactory} elementFactory
+ * @param {ElementRegistry} elementRegistry
+ * @param {Function} translate
+ * @param {TextRenderer} textRenderer
+ */
+ function BpmnImporter(
+ eventBus, canvas, elementFactory,
+ elementRegistry, translate, textRenderer) {
+
+ this._eventBus = eventBus;
+ this._canvas = canvas;
+ this._elementFactory = elementFactory;
+ this._elementRegistry = elementRegistry;
+ this._translate = translate;
+ this._textRenderer = textRenderer;
+ }
+
+ BpmnImporter.$inject = [
+ 'eventBus',
+ 'canvas',
+ 'elementFactory',
+ 'elementRegistry',
+ 'translate',
+ 'textRenderer'
+ ];
+
+
+ /**
+ * Add bpmn element (semantic) to the canvas onto the
+ * specified parent shape.
+ */
+ BpmnImporter.prototype.add = function(semantic, parentElement) {
+
+ var di = semantic.di,
+ element,
+ translate = this._translate,
+ hidden;
+
+ var parentIndex;
+
+ // ROOT ELEMENT
+ // handle the special case that we deal with a
+ // invisible root element (process or collaboration)
+ if (is$1(di, 'bpmndi:BPMNPlane')) {
+
+ // add a virtual element (not being drawn)
+ element = this._elementFactory.createRoot(elementData(semantic));
+
+ this._canvas.setRootElement(element);
+ }
+
+ // SHAPE
+ else if (is$1(di, 'bpmndi:BPMNShape')) {
+
+ var collapsed = !isExpanded(semantic);
+ hidden = parentElement && (parentElement.hidden || parentElement.collapsed);
+
+ var bounds = semantic.di.bounds;
+
+ element = this._elementFactory.createShape(elementData(semantic, {
+ collapsed: collapsed,
+ hidden: hidden,
+ x: Math.round(bounds.x),
+ y: Math.round(bounds.y),
+ width: Math.round(bounds.width),
+ height: Math.round(bounds.height)
+ }));
+
+ if (is$1(semantic, 'bpmn:BoundaryEvent')) {
+ this._attachBoundary(semantic, element);
+ }
+
+ // insert lanes behind other flow nodes (cf. #727)
+ if (is$1(semantic, 'bpmn:Lane')) {
+ parentIndex = 0;
+ }
+
+ if (is$1(semantic, 'bpmn:DataStoreReference')) {
+
+ // check wether data store is inside our outside of its semantic parent
+ if (!isPointInsideBBox$1(parentElement, getMid(bounds))) {
+ parentElement = this._canvas.getRootElement();
+ }
+ }
+
+ this._canvas.addShape(element, parentElement, parentIndex);
+ }
+
+ // CONNECTION
+ else if (is$1(di, 'bpmndi:BPMNEdge')) {
+
+ var source = this._getSource(semantic),
+ target = this._getTarget(semantic);
+
+ hidden = parentElement && (parentElement.hidden || parentElement.collapsed);
+
+ element = this._elementFactory.createConnection(elementData(semantic, {
+ hidden: hidden,
+ source: source,
+ target: target,
+ waypoints: collectWaypoints(semantic.di.waypoint)
+ }));
+
+ if (is$1(semantic, 'bpmn:DataAssociation')) {
+
+ // render always on top; this ensures DataAssociations
+ // are rendered correctly across different "hacks" people
+ // love to model such as cross participant / sub process
+ // associations
+ parentElement = null;
+ }
+
+ // insert sequence flows behind other flow nodes (cf. #727)
+ if (is$1(semantic, 'bpmn:SequenceFlow')) {
+ parentIndex = 0;
+ }
+
+ this._canvas.addConnection(element, parentElement, parentIndex);
+ } else {
+ throw new Error(translate('unknown di {di} for element {semantic}', {
+ di: elementToString(di),
+ semantic: elementToString(semantic)
+ }));
+ }
+ // (optional) LABEL
+ if (isLabelExternal(semantic) && semantic.name) {
+ this.addLabel(semantic, element);
+ }
+
+
+ this._eventBus.fire('bpmnElement.added', { element: element });
+
+ return element;
+ };
+
+
+ /**
+ * Attach the boundary element to the given host
+ *
+ * @param {ModdleElement} boundarySemantic
+ * @param {djs.model.Base} boundaryElement
+ */
+ BpmnImporter.prototype._attachBoundary = function(boundarySemantic, boundaryElement) {
+ var translate = this._translate;
+ var hostSemantic = boundarySemantic.attachedToRef;
+
+ if (!hostSemantic) {
+ throw new Error(translate('missing {semantic}#attachedToRef', {
+ semantic: elementToString(boundarySemantic)
+ }));
+ }
+
+ var host = this._elementRegistry.get(hostSemantic.id),
+ attachers = host && host.attachers;
+
+ if (!host) {
+ throw notYetDrawn(translate, boundarySemantic, hostSemantic, 'attachedToRef');
+ }
+
+ // wire element.host <> host.attachers
+ boundaryElement.host = host;
+
+ if (!attachers) {
+ host.attachers = attachers = [];
+ }
+
+ if (attachers.indexOf(boundaryElement) === -1) {
+ attachers.push(boundaryElement);
+ }
+ };
+
+
+ /**
+ * add label for an element
+ */
+ BpmnImporter.prototype.addLabel = function(semantic, element) {
+ var bounds,
+ text,
+ label;
+
+ bounds = getExternalLabelBounds(semantic, element);
+
+ text = semantic.name;
+
+ if (text) {
+ // get corrected bounds from actual layouted text
+ bounds = this._textRenderer.getLayoutedBounds(bounds, text);
+ }
+
+ label = this._elementFactory.createLabel(elementData(semantic, {
+ id: semantic.id + '_label',
+ labelTarget: element,
+ type: 'label',
+ hidden: element.hidden || !semantic.name,
+ x: Math.round(bounds.x),
+ y: Math.round(bounds.y),
+ width: Math.round(bounds.width),
+ height: Math.round(bounds.height)
+ }));
+
+ return this._canvas.addShape(label, element.parent);
+ };
+
+ /**
+ * Return the drawn connection end based on the given side.
+ *
+ * @throws {Error} if the end is not yet drawn
+ */
+ BpmnImporter.prototype._getEnd = function(semantic, side) {
+
+ var element,
+ refSemantic,
+ type = semantic.$type,
+ translate = this._translate;
+
+ refSemantic = semantic[side + 'Ref'];
+
+ // handle mysterious isMany DataAssociation#sourceRef
+ if (side === 'source' && type === 'bpmn:DataInputAssociation') {
+ refSemantic = refSemantic && refSemantic[0];
+ }
+
+ // fix source / target for DataInputAssociation / DataOutputAssociation
+ if (side === 'source' && type === 'bpmn:DataOutputAssociation' ||
+ side === 'target' && type === 'bpmn:DataInputAssociation') {
+
+ refSemantic = semantic.$parent;
+ }
+
+ element = refSemantic && this._getElement(refSemantic);
+
+ if (element) {
+ return element;
+ }
+
+ if (refSemantic) {
+ throw notYetDrawn(translate, semantic, refSemantic, side + 'Ref');
+ } else {
+ throw new Error(translate('{semantic}#{side} Ref not specified', {
+ semantic: elementToString(semantic),
+ side: side
+ }));
+ }
+ };
+
+ BpmnImporter.prototype._getSource = function(semantic) {
+ return this._getEnd(semantic, 'source');
+ };
+
+ BpmnImporter.prototype._getTarget = function(semantic) {
+ return this._getEnd(semantic, 'target');
+ };
+
+
+ BpmnImporter.prototype._getElement = function(semantic) {
+ return this._elementRegistry.get(semantic.id);
+ };
+
+
+ // helpers ////////////////////
+
+ function isPointInsideBBox$1(bbox, point) {
+ var x = point.x,
+ y = point.y;
+
+ return x >= bbox.x &&
+ x <= bbox.x + bbox.width &&
+ y >= bbox.y &&
+ y <= bbox.y + bbox.height;
+ }
+
+ var ImportModule = {
+ __depends__: [
+ translate$2
+ ],
+ bpmnImporter: [ 'type', BpmnImporter ]
+ };
+
+ var CoreModule$1 = {
+ __depends__: [
+ DrawModule$1,
+ ImportModule
+ ]
+ };
+
+ function getOriginal(event) {
+ return event.originalEvent || event.srcEvent;
+ }
+
+
+ function toPoint(event) {
+
+ if (event.pointers && event.pointers.length) {
+ event = event.pointers[0];
+ }
+
+ if (event.touches && event.touches.length) {
+ event = event.touches[0];
+ }
+
+ return event ? {
+ x: event.clientX,
+ y: event.clientY
+ } : null;
+ }
+
+ function isMac() {
+ return (/mac/i).test(navigator.platform);
+ }
+
+ function isPrimaryButton(event) {
+ // button === 0 -> left áka primary mouse button
+ return !(getOriginal(event) || event).button;
+ }
+
+ function hasPrimaryModifier(event) {
+ var originalEvent = getOriginal(event) || event;
+
+ if (!isPrimaryButton(event)) {
+ return false;
+ }
+
+ // Use alt as primary modifier key for mac OS
+ if (isMac()) {
+ return originalEvent.metaKey;
+ } else {
+ return originalEvent.ctrlKey;
+ }
+ }
+
+ function allowAll(e) { return true; }
+
+ var LOW_PRIORITY = 500;
+
+ /**
+ * A plugin that provides interaction events for diagram elements.
+ *
+ * It emits the following events:
+ *
+ * * element.hover
+ * * element.out
+ * * element.click
+ * * element.dblclick
+ * * element.mousedown
+ * * element.contextmenu
+ *
+ * Each event is a tuple { element, gfx, originalEvent }.
+ *
+ * Canceling the event via Event#preventDefault()
+ * prevents the original DOM operation.
+ *
+ * @param {EventBus} eventBus
+ */
+ function InteractionEvents(eventBus, elementRegistry, styles) {
+
+ var HIT_STYLE = styles.cls('djs-hit', [ 'no-fill', 'no-border' ], {
+ stroke: 'white',
+ strokeWidth: 15
+ });
+
+ /**
+ * Fire an interaction event.
+ *
+ * @param {String} type local event name, e.g. element.click.
+ * @param {DOMEvent} event native event
+ * @param {djs.model.Base} [element] the diagram element to emit the event on;
+ * defaults to the event target
+ */
+ function fire(type, event, element) {
+
+ if (isIgnored(type, event)) {
+ return;
+ }
+
+ var target, gfx, returnValue;
+
+ if (!element) {
+ target = event.delegateTarget || event.target;
+
+ if (target) {
+ gfx = target;
+ element = elementRegistry.get(gfx);
+ }
+ } else {
+ gfx = elementRegistry.getGraphics(element);
+ }
+
+ if (!gfx || !element) {
+ return;
+ }
+
+ returnValue = eventBus.fire(type, {
+ element: element,
+ gfx: gfx,
+ originalEvent: event
+ });
+
+ if (returnValue === false) {
+ event.stopPropagation();
+ event.preventDefault();
+ }
+ }
+
+ // TODO(nikku): document this
+ var handlers = {};
+
+ function mouseHandler(localEventName) {
+ return handlers[localEventName];
+ }
+
+ function isIgnored(localEventName, event) {
+
+ var filter$$1 = ignoredFilters[localEventName] || isPrimaryButton;
+
+ // only react on left mouse button interactions
+ // except for interaction events that are enabled
+ // for secundary mouse button
+ return !filter$$1(event);
+ }
+
+ var bindings = {
+ mouseover: 'element.hover',
+ mouseout: 'element.out',
+ click: 'element.click',
+ dblclick: 'element.dblclick',
+ mousedown: 'element.mousedown',
+ mouseup: 'element.mouseup',
+ contextmenu: 'element.contextmenu'
+ };
+
+ var ignoredFilters = {
+ 'element.contextmenu': allowAll
+ };
+
+
+ // manual event trigger
+
+ /**
+ * Trigger an interaction event (based on a native dom event)
+ * on the target shape or connection.
+ *
+ * @param {String} eventName the name of the triggered DOM event
+ * @param {MouseEvent} event
+ * @param {djs.model.Base} targetElement
+ */
+ function triggerMouseEvent(eventName, event, targetElement) {
+
+ // i.e. element.mousedown...
+ var localEventName = bindings[eventName];
+
+ if (!localEventName) {
+ throw new Error('unmapped DOM event name <' + eventName + '>');
+ }
+
+ return fire(localEventName, event, targetElement);
+ }
+
+
+ var elementSelector = 'svg, .djs-element';
+
+ // event registration
+
+ function registerEvent(node, event, localEvent, ignoredFilter) {
+
+ var handler = handlers[localEvent] = function(event) {
+ fire(localEvent, event);
+ };
+
+ if (ignoredFilter) {
+ ignoredFilters[localEvent] = ignoredFilter;
+ }
+
+ handler.$delegate = delegateEvents.bind(node, elementSelector, event, handler);
+ }
+
+ function unregisterEvent(node, event, localEvent) {
+
+ var handler = mouseHandler(localEvent);
+
+ if (!handler) {
+ return;
+ }
+
+ delegateEvents.unbind(node, event, handler.$delegate);
+ }
+
+ function registerEvents(svg) {
+ forEach(bindings, function(val, key) {
+ registerEvent(svg, key, val);
+ });
+ }
+
+ function unregisterEvents(svg) {
+ forEach(bindings, function(val, key) {
+ unregisterEvent(svg, key, val);
+ });
+ }
+
+ eventBus.on('canvas.destroy', function(event) {
+ unregisterEvents(event.svg);
+ });
+
+ eventBus.on('canvas.init', function(event) {
+ registerEvents(event.svg);
+ });
+
+
+ eventBus.on([ 'shape.added', 'connection.added' ], function(event) {
+ var element = event.element,
+ gfx = event.gfx,
+ hit;
+
+ if (element.waypoints) {
+ hit = createLine(element.waypoints);
+ } else {
+ hit = create('rect');
+ attr$1(hit, {
+ x: 0,
+ y: 0,
+ width: element.width,
+ height: element.height
+ });
+ }
+
+ attr$1(hit, HIT_STYLE);
+
+ append(gfx, hit);
+ });
+
+ // Update djs-hit on change.
+ // A low priortity is necessary, because djs-hit of labels has to be updated
+ // after the label bounds have been updated in the renderer.
+ eventBus.on('shape.changed', LOW_PRIORITY, function(event) {
+
+ var element = event.element,
+ gfx = event.gfx,
+ hit = query('.djs-hit', gfx);
+
+ attr$1(hit, {
+ width: element.width,
+ height: element.height
+ });
+ });
+
+ eventBus.on('connection.changed', function(event) {
+
+ var element = event.element,
+ gfx = event.gfx,
+ hit = query('.djs-hit', gfx);
+
+ updateLine(hit, element.waypoints);
+ });
+
+
+ // API
+
+ this.fire = fire;
+
+ this.triggerMouseEvent = triggerMouseEvent;
+
+ this.mouseHandler = mouseHandler;
+
+ this.registerEvent = registerEvent;
+ this.unregisterEvent = unregisterEvent;
+ }
+
+
+ InteractionEvents.$inject = [
+ 'eventBus',
+ 'elementRegistry',
+ 'styles'
+ ];
+
+
+ /**
+ * An event indicating that the mouse hovered over an element
+ *
+ * @event element.hover
+ *
+ * @type {Object}
+ * @property {djs.model.Base} element
+ * @property {SVGElement} gfx
+ * @property {Event} originalEvent
+ */
+
+ /**
+ * An event indicating that the mouse has left an element
+ *
+ * @event element.out
+ *
+ * @type {Object}
+ * @property {djs.model.Base} element
+ * @property {SVGElement} gfx
+ * @property {Event} originalEvent
+ */
+
+ /**
+ * An event indicating that the mouse has clicked an element
+ *
+ * @event element.click
+ *
+ * @type {Object}
+ * @property {djs.model.Base} element
+ * @property {SVGElement} gfx
+ * @property {Event} originalEvent
+ */
+
+ /**
+ * An event indicating that the mouse has double clicked an element
+ *
+ * @event element.dblclick
+ *
+ * @type {Object}
+ * @property {djs.model.Base} element
+ * @property {SVGElement} gfx
+ * @property {Event} originalEvent
+ */
+
+ /**
+ * An event indicating that the mouse has gone down on an element.
+ *
+ * @event element.mousedown
+ *
+ * @type {Object}
+ * @property {djs.model.Base} element
+ * @property {SVGElement} gfx
+ * @property {Event} originalEvent
+ */
+
+ /**
+ * An event indicating that the mouse has gone up on an element.
+ *
+ * @event element.mouseup
+ *
+ * @type {Object}
+ * @property {djs.model.Base} element
+ * @property {SVGElement} gfx
+ * @property {Event} originalEvent
+ */
+
+ /**
+ * An event indicating that the context menu action is triggered
+ * via mouse or touch controls.
+ *
+ * @event element.contextmenu
+ *
+ * @type {Object}
+ * @property {djs.model.Base} element
+ * @property {SVGElement} gfx
+ * @property {Event} originalEvent
+ */
+
+ var InteractionEventsModule = {
+ __init__: [ 'interactionEvents' ],
+ interactionEvents: [ 'type', InteractionEvents ]
+ };
+
+ var LOW_PRIORITY$1 = 500;
+
+
+ /**
+ * @class
+ *
+ * A plugin that adds an outline to shapes and connections that may be activated and styled
+ * via CSS classes.
+ *
+ * @param {EventBus} eventBus
+ * @param {Styles} styles
+ * @param {ElementRegistry} elementRegistry
+ */
+ function Outline(eventBus, styles, elementRegistry) {
+
+ this.offset = 6;
+
+ var OUTLINE_STYLE = styles.cls('djs-outline', [ 'no-fill' ]);
+
+ var self = this;
+
+ function createOutline(gfx, bounds) {
+ var outline = create('rect');
+
+ attr$1(outline, assign({
+ x: 10,
+ y: 10,
+ width: 100,
+ height: 100
+ }, OUTLINE_STYLE));
+
+ append(gfx, outline);
+
+ return outline;
+ }
+
+ // A low priortity is necessary, because outlines of labels have to be updated
+ // after the label bounds have been updated in the renderer.
+ eventBus.on([ 'shape.added', 'shape.changed' ], LOW_PRIORITY$1, function(event) {
+ var element = event.element,
+ gfx = event.gfx;
+
+ var outline = query('.djs-outline', gfx);
+
+ if (!outline) {
+ outline = createOutline(gfx, element);
+ }
+
+ self.updateShapeOutline(outline, element);
+ });
+
+ eventBus.on([ 'connection.added', 'connection.changed' ], function(event) {
+ var element = event.element,
+ gfx = event.gfx;
+
+ var outline = query('.djs-outline', gfx);
+
+ if (!outline) {
+ outline = createOutline(gfx, element);
+ }
+
+ self.updateConnectionOutline(outline, element);
+ });
+ }
+
+
+ /**
+ * Updates the outline of a shape respecting the dimension of the
+ * element and an outline offset.
+ *
+ * @param {SVGElement} outline
+ * @param {djs.model.Base} element
+ */
+ Outline.prototype.updateShapeOutline = function(outline, element) {
+
+ attr$1(outline, {
+ x: -this.offset,
+ y: -this.offset,
+ width: element.width + this.offset * 2,
+ height: element.height + this.offset * 2
+ });
+
+ };
+
+
+ /**
+ * Updates the outline of a connection respecting the bounding box of
+ * the connection and an outline offset.
+ *
+ * @param {SVGElement} outline
+ * @param {djs.model.Base} element
+ */
+ Outline.prototype.updateConnectionOutline = function(outline, connection) {
+
+ var bbox = getBBox(connection);
+
+ attr$1(outline, {
+ x: bbox.x - this.offset,
+ y: bbox.y - this.offset,
+ width: bbox.width + this.offset * 2,
+ height: bbox.height + this.offset * 2
+ });
+
+ };
+
+
+ Outline.$inject = ['eventBus', 'styles', 'elementRegistry'];
+
+ var OutlineModule = {
+ __init__: [ 'outline' ],
+ outline: [ 'type', Outline ]
+ };
+
+ /**
+ * A service that offers the current selection in a diagram.
+ * Offers the api to control the selection, too.
+ *
+ * @class
+ *
+ * @param {EventBus} eventBus the event bus
+ */
+ function Selection(eventBus) {
+
+ this._eventBus = eventBus;
+
+ this._selectedElements = [];
+
+ var self = this;
+
+ eventBus.on([ 'shape.remove', 'connection.remove' ], function(e) {
+ var element = e.element;
+ self.deselect(element);
+ });
+
+ eventBus.on([ 'diagram.clear' ], function(e) {
+ self.select(null);
+ });
+ }
+
+ Selection.$inject = [ 'eventBus' ];
+
+
+ Selection.prototype.deselect = function(element) {
+ var selectedElements = this._selectedElements;
+
+ var idx = selectedElements.indexOf(element);
+
+ if (idx !== -1) {
+ var oldSelection = selectedElements.slice();
+
+ selectedElements.splice(idx, 1);
+
+ this._eventBus.fire('selection.changed', { oldSelection: oldSelection, newSelection: selectedElements });
+ }
+ };
+
+
+ Selection.prototype.get = function() {
+ return this._selectedElements;
+ };
+
+ Selection.prototype.isSelected = function(element) {
+ return this._selectedElements.indexOf(element) !== -1;
+ };
+
+
+ /**
+ * This method selects one or more elements on the diagram.
+ *
+ * By passing an additional add parameter you can decide whether or not the element(s)
+ * should be added to the already existing selection or not.
+ *
+ * @method Selection#select
+ *
+ * @param {Object|Object[]} elements element or array of elements to be selected
+ * @param {boolean} [add] whether the element(s) should be appended to the current selection, defaults to false
+ */
+ Selection.prototype.select = function(elements, add) {
+ var selectedElements = this._selectedElements,
+ oldSelection = selectedElements.slice();
+
+ if (!isArray(elements)) {
+ elements = elements ? [ elements ] : [];
+ }
+
+ // selection may be cleared by passing an empty array or null
+ // to the method
+ if (add) {
+ forEach(elements, function(element) {
+ if (selectedElements.indexOf(element) !== -1) {
+ // already selected
+ return;
+ } else {
+ selectedElements.push(element);
+ }
+ });
+ } else {
+ this._selectedElements = selectedElements = elements.slice();
+ }
+
+ this._eventBus.fire('selection.changed', { oldSelection: oldSelection, newSelection: selectedElements });
+ };
+
+ var MARKER_HOVER = 'hover',
+ MARKER_SELECTED = 'selected';
+
+
+ /**
+ * A plugin that adds a visible selection UI to shapes and connections
+ * by appending the hover
and selected
classes to them.
+ *
+ * @class
+ *
+ * Makes elements selectable, too.
+ *
+ * @param {EventBus} events
+ * @param {SelectionService} selection
+ * @param {Canvas} canvas
+ */
+ function SelectionVisuals(events, canvas, selection, styles) {
+
+ this._multiSelectionBox = null;
+
+ function addMarker(e, cls) {
+ canvas.addMarker(e, cls);
+ }
+
+ function removeMarker(e, cls) {
+ canvas.removeMarker(e, cls);
+ }
+
+ events.on('element.hover', function(event) {
+ addMarker(event.element, MARKER_HOVER);
+ });
+
+ events.on('element.out', function(event) {
+ removeMarker(event.element, MARKER_HOVER);
+ });
+
+ events.on('selection.changed', function(event) {
+
+ function deselect(s) {
+ removeMarker(s, MARKER_SELECTED);
+ }
+
+ function select(s) {
+ addMarker(s, MARKER_SELECTED);
+ }
+
+ var oldSelection = event.oldSelection,
+ newSelection = event.newSelection;
+
+ forEach(oldSelection, function(e) {
+ if (newSelection.indexOf(e) === -1) {
+ deselect(e);
+ }
+ });
+
+ forEach(newSelection, function(e) {
+ if (oldSelection.indexOf(e) === -1) {
+ select(e);
+ }
+ });
+ });
+ }
+
+ SelectionVisuals.$inject = [
+ 'eventBus',
+ 'canvas',
+ 'selection',
+ 'styles'
+ ];
+
+ function SelectionBehavior(
+ eventBus, selection, canvas,
+ elementRegistry) {
+
+ eventBus.on('create.end', 500, function(e) {
+
+ // select the created shape after a
+ // successful create operation
+ if (e.context.canExecute) {
+ selection.select(e.context.shape);
+ }
+ });
+
+ eventBus.on('connect.end', 500, function(e) {
+
+ // select the connect end target
+ // after a connect operation
+ if (e.context.canExecute && e.context.target) {
+ selection.select(e.context.target);
+ }
+ });
+
+ eventBus.on('shape.move.end', 500, function(e) {
+ var previousSelection = e.previousSelection || [];
+
+ var shape = elementRegistry.get(e.context.shape.id);
+
+ // make sure at least the main moved element is being
+ // selected after a move operation
+ var inSelection = find(previousSelection, function(selectedShape) {
+ return shape.id === selectedShape.id;
+ });
+
+ if (!inSelection) {
+ selection.select(shape);
+ }
+ });
+
+ // Shift + click selection
+ eventBus.on('element.click', function(event) {
+
+ var element = event.element;
+
+ // do not select the root element
+ // or connections
+ if (element === canvas.getRootElement()) {
+ element = null;
+ }
+
+ var isSelected = selection.isSelected(element),
+ isMultiSelect = selection.get().length > 1;
+
+ // mouse-event: SELECTION_KEY
+ var add = hasPrimaryModifier(event);
+
+ // select OR deselect element in multi selection
+ if (isSelected && isMultiSelect) {
+ if (add) {
+ return selection.deselect(element);
+ } else {
+ return selection.select(element);
+ }
+ } else
+ if (!isSelected) {
+ selection.select(element, add);
+ } else {
+ selection.deselect(element);
+ }
+ });
+ }
+
+ SelectionBehavior.$inject = [
+ 'eventBus',
+ 'selection',
+ 'canvas',
+ 'elementRegistry'
+ ];
+
+ var SelectionModule = {
+ __init__: [ 'selectionVisuals', 'selectionBehavior' ],
+ __depends__: [
+ InteractionEventsModule,
+ OutlineModule
+ ],
+ selection: [ 'type', Selection ],
+ selectionVisuals: [ 'type', SelectionVisuals ],
+ selectionBehavior: [ 'type', SelectionBehavior ]
+ };
+
+ /**
+ * Util that provides unique IDs.
+ *
+ * @class djs.util.IdGenerator
+ * @constructor
+ * @memberOf djs.util
+ *
+ * The ids can be customized via a given prefix and contain a random value to avoid collisions.
+ *
+ * @param {String} prefix a prefix to prepend to generated ids (for better readability)
+ */
+ function IdGenerator(prefix) {
+
+ this._counter = 0;
+ this._prefix = (prefix ? prefix + '-' : '') + Math.floor(Math.random() * 1000000000) + '-';
+ }
+
+ /**
+ * Returns a next unique ID.
+ *
+ * @method djs.util.IdGenerator#next
+ *
+ * @returns {String} the id
+ */
+ IdGenerator.prototype.next = function() {
+ return this._prefix + (++this._counter);
+ };
+
+ // document wide unique overlay ids
+ var ids$1 = new IdGenerator('ov');
+
+ var LOW_PRIORITY$2 = 500;
+
+
+ function createRoot(parent) {
+ var root = domify('
');
+ parent.insertBefore(root, parent.firstChild);
+
+ return root;
+ }
+
+
+ function setPosition(el, x, y) {
+ assign(el.style, { left: x + 'px', top: y + 'px' });
+ }
+
+ function setVisible(el, visible) {
+ el.style.display = visible === false ? 'none' : '';
+ }
+
+ function setTransform(el, transform) {
+
+ el.style['transform-origin'] = 'top left';
+
+ [ '', '-ms-', '-webkit-' ].forEach(function(prefix) {
+ el.style[prefix + 'transform'] = transform;
+ });
+ }
+
+ function isDef(o) {
+ return typeof o !== 'undefined';
+ }
+
+ /**
+ * A service that allows users to attach overlays to diagram elements.
+ *
+ * The overlay service will take care of overlay positioning during updates.
+ *
+ * @example
+ *
+ * // add a pink badge on the top left of the shape
+ * overlays.add(someShape, {
+ * position: {
+ * top: -5,
+ * left: -5
+ * },
+ * html: '0
'
+ * });
+ *
+ * // or add via shape id
+ *
+ * overlays.add('some-element-id', {
+ * position: {
+ * top: -5,
+ * left: -5
+ * }
+ * html: '0
'
+ * });
+ *
+ * // or add with optional type
+ *
+ * overlays.add(someShape, 'badge', {
+ * position: {
+ * top: -5,
+ * left: -5
+ * }
+ * html: '0
'
+ * });
+ *
+ *
+ * // remove an overlay
+ *
+ * var id = overlays.add(...);
+ * overlays.remove(id);
+ *
+ *
+ * You may configure overlay defaults during tool by providing a `config` module
+ * with `overlays.defaults` as an entry:
+ *
+ * {
+ * overlays: {
+ * defaults: {
+ * show: {
+ * minZoom: 0.7,
+ * maxZoom: 5.0
+ * },
+ * scale: {
+ * min: 1
+ * }
+ * }
+ * }
+ *
+ * @param {Object} config
+ * @param {EventBus} eventBus
+ * @param {Canvas} canvas
+ * @param {ElementRegistry} elementRegistry
+ */
+ function Overlays(config, eventBus, canvas, elementRegistry) {
+
+ this._eventBus = eventBus;
+ this._canvas = canvas;
+ this._elementRegistry = elementRegistry;
+
+ this._ids = ids$1;
+
+ this._overlayDefaults = assign({
+ // no show constraints
+ show: null,
+
+ // always scale
+ scale: true
+ }, config && config.defaults);
+
+ /**
+ * Mapping overlayId -> overlay
+ */
+ this._overlays = {};
+
+ /**
+ * Mapping elementId -> overlay container
+ */
+ this._overlayContainers = [];
+
+ // root html element for all overlays
+ this._overlayRoot = createRoot(canvas.getContainer());
+
+ this._init();
+ }
+
+
+ Overlays.$inject = [
+ 'config.overlays',
+ 'eventBus',
+ 'canvas',
+ 'elementRegistry'
+ ];
+
+
+ /**
+ * Returns the overlay with the specified id or a list of overlays
+ * for an element with a given type.
+ *
+ * @example
+ *
+ * // return the single overlay with the given id
+ * overlays.get('some-id');
+ *
+ * // return all overlays for the shape
+ * overlays.get({ element: someShape });
+ *
+ * // return all overlays on shape with type 'badge'
+ * overlays.get({ element: someShape, type: 'badge' });
+ *
+ * // shape can also be specified as id
+ * overlays.get({ element: 'element-id', type: 'badge' });
+ *
+ *
+ * @param {Object} search
+ * @param {String} [search.id]
+ * @param {String|djs.model.Base} [search.element]
+ * @param {String} [search.type]
+ *
+ * @return {Object|Array} the overlay(s)
+ */
+ Overlays.prototype.get = function(search) {
+
+ if (isString(search)) {
+ search = { id: search };
+ }
+
+ if (isString(search.element)) {
+ search.element = this._elementRegistry.get(search.element);
+ }
+
+ if (search.element) {
+ var container = this._getOverlayContainer(search.element, true);
+
+ // return a list of overlays when searching by element (+type)
+ if (container) {
+ return search.type ? filter(container.overlays, matchPattern({ type: search.type })) : container.overlays.slice();
+ } else {
+ return [];
+ }
+ } else
+ if (search.type) {
+ return filter(this._overlays, matchPattern({ type: search.type }));
+ } else {
+ // return single element when searching by id
+ return search.id ? this._overlays[search.id] : null;
+ }
+ };
+
+ /**
+ * Adds a HTML overlay to an element.
+ *
+ * @param {String|djs.model.Base} element attach overlay to this shape
+ * @param {String} [type] optional type to assign to the overlay
+ * @param {Object} overlay the overlay configuration
+ *
+ * @param {String|DOMElement} overlay.html html element to use as an overlay
+ * @param {Object} [overlay.show] show configuration
+ * @param {Number} [overlay.show.minZoom] minimal zoom level to show the overlay
+ * @param {Number} [overlay.show.maxZoom] maximum zoom level to show the overlay
+ * @param {Object} overlay.position where to attach the overlay
+ * @param {Number} [overlay.position.left] relative to element bbox left attachment
+ * @param {Number} [overlay.position.top] relative to element bbox top attachment
+ * @param {Number} [overlay.position.bottom] relative to element bbox bottom attachment
+ * @param {Number} [overlay.position.right] relative to element bbox right attachment
+ * @param {Boolean|Object} [overlay.scale=true] false to preserve the same size regardless of
+ * diagram zoom
+ * @param {Number} [overlay.scale.min]
+ * @param {Number} [overlay.scale.max]
+ *
+ * @return {String} id that may be used to reference the overlay for update or removal
+ */
+ Overlays.prototype.add = function(element, type, overlay) {
+
+ if (isObject(type)) {
+ overlay = type;
+ type = null;
+ }
+
+ if (!element.id) {
+ element = this._elementRegistry.get(element);
+ }
+
+ if (!overlay.position) {
+ throw new Error('must specifiy overlay position');
+ }
+
+ if (!overlay.html) {
+ throw new Error('must specifiy overlay html');
+ }
+
+ if (!element) {
+ throw new Error('invalid element specified');
+ }
+
+ var id = this._ids.next();
+
+ overlay = assign({}, this._overlayDefaults, overlay, {
+ id: id,
+ type: type,
+ element: element,
+ html: overlay.html
+ });
+
+ this._addOverlay(overlay);
+
+ return id;
+ };
+
+
+ /**
+ * Remove an overlay with the given id or all overlays matching the given filter.
+ *
+ * @see Overlays#get for filter options.
+ *
+ * @param {String} [id]
+ * @param {Object} [filter]
+ */
+ Overlays.prototype.remove = function(filter$$1) {
+
+ var overlays = this.get(filter$$1) || [];
+
+ if (!isArray(overlays)) {
+ overlays = [ overlays ];
+ }
+
+ var self = this;
+
+ forEach(overlays, function(overlay) {
+
+ var container = self._getOverlayContainer(overlay.element, true);
+
+ if (overlay) {
+ remove(overlay.html);
+ remove(overlay.htmlContainer);
+
+ delete overlay.htmlContainer;
+ delete overlay.element;
+
+ delete self._overlays[overlay.id];
+ }
+
+ if (container) {
+ var idx = container.overlays.indexOf(overlay);
+ if (idx !== -1) {
+ container.overlays.splice(idx, 1);
+ }
+ }
+ });
+
+ };
+
+
+ Overlays.prototype.show = function() {
+ setVisible(this._overlayRoot);
+ };
+
+
+ Overlays.prototype.hide = function() {
+ setVisible(this._overlayRoot, false);
+ };
+
+ Overlays.prototype.clear = function() {
+ this._overlays = {};
+
+ this._overlayContainers = [];
+
+ clear(this._overlayRoot);
+ };
+
+ Overlays.prototype._updateOverlayContainer = function(container) {
+ var element = container.element,
+ html = container.html;
+
+ // update container left,top according to the elements x,y coordinates
+ // this ensures we can attach child elements relative to this container
+
+ var x = element.x,
+ y = element.y;
+
+ if (element.waypoints) {
+ var bbox = getBBox(element);
+ x = bbox.x;
+ y = bbox.y;
+ }
+
+ setPosition(html, x, y);
+
+ attr(container.html, 'data-container-id', element.id);
+ };
+
+
+ Overlays.prototype._updateOverlay = function(overlay) {
+
+ var position = overlay.position,
+ htmlContainer = overlay.htmlContainer,
+ element = overlay.element;
+
+ // update overlay html relative to shape because
+ // it is already positioned on the element
+
+ // update relative
+ var left = position.left,
+ top = position.top;
+
+ if (position.right !== undefined) {
+
+ var width;
+
+ if (element.waypoints) {
+ width = getBBox(element).width;
+ } else {
+ width = element.width;
+ }
+
+ left = position.right * -1 + width;
+ }
+
+ if (position.bottom !== undefined) {
+
+ var height;
+
+ if (element.waypoints) {
+ height = getBBox(element).height;
+ } else {
+ height = element.height;
+ }
+
+ top = position.bottom * -1 + height;
+ }
+
+ setPosition(htmlContainer, left || 0, top || 0);
+ };
+
+
+ Overlays.prototype._createOverlayContainer = function(element) {
+ var html = domify('
');
+
+ this._overlayRoot.appendChild(html);
+
+ var container = {
+ html: html,
+ element: element,
+ overlays: []
+ };
+
+ this._updateOverlayContainer(container);
+
+ this._overlayContainers.push(container);
+
+ return container;
+ };
+
+
+ Overlays.prototype._updateRoot = function(viewbox) {
+ var scale = viewbox.scale || 1;
+
+ var matrix = 'matrix(' +
+ [
+ scale,
+ 0,
+ 0,
+ scale,
+ -1 * viewbox.x * scale,
+ -1 * viewbox.y * scale
+ ].join(',') +
+ ')';
+
+ setTransform(this._overlayRoot, matrix);
+ };
+
+
+ Overlays.prototype._getOverlayContainer = function(element, raw) {
+ var container = find(this._overlayContainers, function(c) {
+ return c.element === element;
+ });
+
+
+ if (!container && !raw) {
+ return this._createOverlayContainer(element);
+ }
+
+ return container;
+ };
+
+
+ Overlays.prototype._addOverlay = function(overlay) {
+
+ var id = overlay.id,
+ element = overlay.element,
+ html = overlay.html,
+ htmlContainer,
+ overlayContainer;
+
+ // unwrap jquery (for those who need it)
+ if (html.get && html.constructor.prototype.jquery) {
+ html = html.get(0);
+ }
+
+ // create proper html elements from
+ // overlay HTML strings
+ if (isString(html)) {
+ html = domify(html);
+ }
+
+ overlayContainer = this._getOverlayContainer(element);
+
+ htmlContainer = domify('');
+
+ htmlContainer.appendChild(html);
+
+ if (overlay.type) {
+ classes(htmlContainer).add('djs-overlay-' + overlay.type);
+ }
+
+ overlay.htmlContainer = htmlContainer;
+
+ overlayContainer.overlays.push(overlay);
+ overlayContainer.html.appendChild(htmlContainer);
+
+ this._overlays[id] = overlay;
+
+ this._updateOverlay(overlay);
+ this._updateOverlayVisibilty(overlay, this._canvas.viewbox());
+ };
+
+
+ Overlays.prototype._updateOverlayVisibilty = function(overlay, viewbox) {
+ var show = overlay.show,
+ minZoom = show && show.minZoom,
+ maxZoom = show && show.maxZoom,
+ htmlContainer = overlay.htmlContainer,
+ visible = true;
+
+ if (show) {
+ if (
+ (isDef(minZoom) && minZoom > viewbox.scale) ||
+ (isDef(maxZoom) && maxZoom < viewbox.scale)
+ ) {
+ visible = false;
+ }
+
+ setVisible(htmlContainer, visible);
+ }
+
+ this._updateOverlayScale(overlay, viewbox);
+ };
+
+
+ Overlays.prototype._updateOverlayScale = function(overlay, viewbox) {
+ var shouldScale = overlay.scale,
+ minScale,
+ maxScale,
+ htmlContainer = overlay.htmlContainer;
+
+ var scale, transform = '';
+
+ if (shouldScale !== true) {
+
+ if (shouldScale === false) {
+ minScale = 1;
+ maxScale = 1;
+ } else {
+ minScale = shouldScale.min;
+ maxScale = shouldScale.max;
+ }
+
+ if (isDef(minScale) && viewbox.scale < minScale) {
+ scale = (1 / viewbox.scale || 1) * minScale;
+ }
+
+ if (isDef(maxScale) && viewbox.scale > maxScale) {
+ scale = (1 / viewbox.scale || 1) * maxScale;
+ }
+ }
+
+ if (isDef(scale)) {
+ transform = 'scale(' + scale + ',' + scale + ')';
+ }
+
+ setTransform(htmlContainer, transform);
+ };
+
+
+ Overlays.prototype._updateOverlaysVisibilty = function(viewbox) {
+
+ var self = this;
+
+ forEach(this._overlays, function(overlay) {
+ self._updateOverlayVisibilty(overlay, viewbox);
+ });
+ };
+
+
+ Overlays.prototype._init = function() {
+
+ var eventBus = this._eventBus;
+
+ var self = this;
+
+
+ // scroll/zoom integration
+
+ function updateViewbox(viewbox) {
+ self._updateRoot(viewbox);
+ self._updateOverlaysVisibilty(viewbox);
+
+ self.show();
+ }
+
+ eventBus.on('canvas.viewbox.changing', function(event) {
+ self.hide();
+ });
+
+ eventBus.on('canvas.viewbox.changed', function(event) {
+ updateViewbox(event.viewbox);
+ });
+
+
+ // remove integration
+
+ eventBus.on([ 'shape.remove', 'connection.remove' ], function(e) {
+ var element = e.element;
+ var overlays = self.get({ element: element });
+
+ forEach(overlays, function(o) {
+ self.remove(o.id);
+ });
+
+ var container = self._getOverlayContainer(element);
+
+ if (container) {
+ remove(container.html);
+ var i = self._overlayContainers.indexOf(container);
+ if (i !== -1) {
+ self._overlayContainers.splice(i, 1);
+ }
+ }
+ });
+
+
+ // move integration
+
+ eventBus.on('element.changed', LOW_PRIORITY$2, function(e) {
+ var element = e.element;
+
+ var container = self._getOverlayContainer(element, true);
+
+ if (container) {
+ forEach(container.overlays, function(overlay) {
+ self._updateOverlay(overlay);
+ });
+
+ self._updateOverlayContainer(container);
+ }
+ });
+
+
+ // marker integration, simply add them on the overlays as classes, too.
+
+ eventBus.on('element.marker.update', function(e) {
+ var container = self._getOverlayContainer(e.element, true);
+ if (container) {
+ classes(container.html)[e.add ? 'add' : 'remove'](e.marker);
+ }
+ });
+
+
+ // clear overlays with diagram
+
+ eventBus.on('diagram.clear', this.clear, this);
+ };
+
+ var OveraysModule = {
+ __init__: [ 'overlays' ],
+ overlays: [ 'type', Overlays ]
+ };
+
+ /**
+ * This file must not be changed or exchanged.
+ *
+ * @see http://bpmn.io/license for more information.
+ */
+
+
+ // inlined ../../resources/logo.svg
+ var BPMNIO_LOGO_SVG = '
';
+
+ var BPMNIO_LOGO_URL = 'data:image/svg+xml,' + encodeURIComponent(BPMNIO_LOGO_SVG);
+
+ var BPMNIO_IMG = '
';
+
+ function css(attrs) {
+ return attrs.join(';');
+ }
+
+ var LIGHTBOX_STYLES = css([
+ 'z-index: 1001',
+ 'position: fixed',
+ 'top: 0',
+ 'left: 0',
+ 'right: 0',
+ 'bottom: 0'
+ ]);
+
+ var BACKDROP_STYLES = css([
+ 'width: 100%',
+ 'height: 100%',
+ 'background: rgba(0,0,0,0.2)'
+ ]);
+
+ var NOTICE_STYLES = css([
+ 'position: absolute',
+ 'left: 50%',
+ 'top: 40%',
+ 'margin: 0 -130px',
+ 'width: 260px',
+ 'padding: 10px',
+ 'background: white',
+ 'border: solid 1px #AAA',
+ 'border-radius: 3px',
+ 'font-family: Helvetica, Arial, sans-serif',
+ 'font-size: 14px',
+ 'line-height: 1.2em'
+ ]);
+
+ var LIGHTBOX_MARKUP =
+ '
';
+
+
+ var lightbox;
+
+ function open() {
+
+ if (!lightbox) {
+ lightbox = domify(LIGHTBOX_MARKUP);
+
+ delegateEvents.bind(lightbox, '.backdrop', 'click', function(event) {
+ document.body.removeChild(lightbox);
+ });
+ }
+
+ document.body.appendChild(lightbox);
+ }
+
+ /**
+ * The code in the
area
+ * must not be changed.
+ *
+ * @see http://bpmn.io/license for more information.
+ */
+
+
+ function checkValidationError(err) {
+
+ // check if we can help the user by indicating wrong BPMN 2.0 xml
+ // (in case he or the exporting tool did not get that right)
+
+ var pattern = /unparsable content <([^>]+)> detected([\s\S]*)$/;
+ var match = pattern.exec(err.message);
+
+ if (match) {
+ err.message =
+ 'unparsable content <' + match[1] + '> detected; ' +
+ 'this may indicate an invalid BPMN 2.0 diagram file' + match[2];
+ }
+
+ return err;
+ }
+
+ var DEFAULT_OPTIONS = {
+ width: '100%',
+ height: '100%',
+ position: 'relative'
+ };
+
+
+ /**
+ * Ensure the passed argument is a proper unit (defaulting to px)
+ */
+ function ensureUnit(val) {
+ return val + (isNumber(val) ? 'px' : '');
+ }
+
+ /**
+ * A viewer for BPMN 2.0 diagrams.
+ *
+ * Have a look at {@link NavigatedViewer} or {@link Modeler} for bundles that include
+ * additional features.
+ *
+ *
+ * ## Extending the Viewer
+ *
+ * In order to extend the viewer pass extension modules to bootstrap via the
+ * `additionalModules` option. An extension module is an object that exposes
+ * named services.
+ *
+ * The following example depicts the integration of a simple
+ * logging component that integrates with interaction events:
+ *
+ *
+ * ```javascript
+ *
+ * // logging component
+ * function InteractionLogger(eventBus) {
+ * eventBus.on('element.hover', function(event) {
+ * console.log()
+ * })
+ * }
+ *
+ * InteractionLogger.$inject = [ 'eventBus' ]; // minification save
+ *
+ * // extension module
+ * var extensionModule = {
+ * __init__: [ 'interactionLogger' ],
+ * interactionLogger: [ 'type', InteractionLogger ]
+ * };
+ *
+ * // extend the viewer
+ * var bpmnViewer = new Viewer({ additionalModules: [ extensionModule ] });
+ * bpmnViewer.importXML(...);
+ * ```
+ *
+ * @param {Object} [options] configuration options to pass to the viewer
+ * @param {DOMElement} [options.container] the container to render the viewer in, defaults to body.
+ * @param {String|Number} [options.width] the width of the viewer
+ * @param {String|Number} [options.height] the height of the viewer
+ * @param {Object} [options.moddleExtensions] extension packages to provide
+ * @param {Array
} [options.modules] a list of modules to override the default modules
+ * @param {Array} [options.additionalModules] a list of modules to use with the default modules
+ */
+ function Viewer(options) {
+
+ options = assign({}, DEFAULT_OPTIONS, options);
+
+ this._moddle = this._createModdle(options);
+
+ this._container = this._createContainer(options);
+
+ /* */
+
+ addProjectLogo(this._container);
+
+ /* */
+
+ this._init(this._container, this._moddle, options);
+ }
+
+ inherits_browser(Viewer, diagramJs);
+
+
+ /**
+ * Parse and render a BPMN 2.0 diagram.
+ *
+ * Once finished the viewer reports back the result to the
+ * provided callback function with (err, warnings).
+ *
+ * ## Life-Cycle Events
+ *
+ * During import the viewer will fire life-cycle events:
+ *
+ * * import.parse.start (about to read model from xml)
+ * * import.parse.complete (model read; may have worked or not)
+ * * import.render.start (graphical import start)
+ * * import.render.complete (graphical import finished)
+ * * import.done (everything done)
+ *
+ * You can use these events to hook into the life-cycle.
+ *
+ * @param {String} xml the BPMN 2.0 xml
+ * @param {Function} [done] invoked with (err, warnings=[])
+ */
+ Viewer.prototype.importXML = function(xml, done) {
+
+ // done is optional
+ done = done || function() {};
+
+ var self = this;
+
+ // hook in pre-parse listeners +
+ // allow xml manipulation
+ xml = this._emit('import.parse.start', { xml: xml }) || xml;
+
+ this._moddle.fromXML(xml, 'bpmn:Definitions', function(err, definitions, context) {
+
+ // hook in post parse listeners +
+ // allow definitions manipulation
+ definitions = self._emit('import.parse.complete', {
+ error: err,
+ definitions: definitions,
+ context: context
+ }) || definitions;
+
+ var parseWarnings = context.warnings;
+
+ if (err) {
+ err = checkValidationError(err);
+
+ self._emit('import.done', { error: err, warnings: parseWarnings });
+
+ return done(err, parseWarnings);
+ }
+
+ self.importDefinitions(definitions, function(err, importWarnings) {
+ var allWarnings = [].concat(parseWarnings, importWarnings || []);
+
+ self._emit('import.done', { error: err, warnings: allWarnings });
+
+ done(err, allWarnings);
+ });
+ });
+ };
+
+ /**
+ * Export the currently displayed BPMN 2.0 diagram as
+ * a BPMN 2.0 XML document.
+ *
+ * @param {Object} [options] export options
+ * @param {Boolean} [options.format=false] output formated XML
+ * @param {Boolean} [options.preamble=true] output preamble
+ *
+ * @param {Function} done invoked with (err, xml)
+ */
+ Viewer.prototype.saveXML = function(options, done) {
+
+ if (!done) {
+ done = options;
+ options = {};
+ }
+
+ var definitions = this._definitions;
+
+ if (!definitions) {
+ return done(new Error('no definitions loaded'));
+ }
+
+ this._moddle.toXML(definitions, options, done);
+ };
+
+ /**
+ * Export the currently displayed BPMN 2.0 diagram as
+ * an SVG image.
+ *
+ * @param {Object} [options]
+ * @param {Function} done invoked with (err, svgStr)
+ */
+ Viewer.prototype.saveSVG = function(options, done) {
+
+ if (!done) {
+ done = options;
+ options = {};
+ }
+
+ var svg, err;
+
+ try {
+ var canvas = this.get('canvas');
+
+ var contentNode = canvas.getDefaultLayer(),
+ defsNode = query('defs', canvas._svg);
+
+ var contents = innerSVG(contentNode),
+ defs = defsNode ? '' + innerSVG(defsNode) + ' ' : '';
+
+ var bbox = contentNode.getBBox();
+
+ svg =
+ '\n' +
+ '\n' +
+ '\n' +
+ '' +
+ defs + contents +
+ ' ';
+ } catch (e) {
+ err = e;
+ }
+
+ done(err, svg);
+ };
+
+ /**
+ * Get a named diagram service.
+ *
+ * @example
+ *
+ * var elementRegistry = viewer.get('elementRegistry');
+ * var startEventShape = elementRegistry.get('StartEvent_1');
+ *
+ * @param {String} name
+ *
+ * @return {Object} diagram service instance
+ *
+ * @method Viewer#get
+ */
+
+ /**
+ * Invoke a function in the context of this viewer.
+ *
+ * @example
+ *
+ * viewer.invoke(function(elementRegistry) {
+ * var startEventShape = elementRegistry.get('StartEvent_1');
+ * });
+ *
+ * @param {Function} fn to be invoked
+ *
+ * @return {Object} the functions return value
+ *
+ * @method Viewer#invoke
+ */
+
+ /**
+ * Remove all drawn elements from the viewer.
+ *
+ * After calling this method the viewer can still
+ * be reused for opening another diagram.
+ *
+ * @method Viewer#clear
+ */
+
+ Viewer.prototype.importDefinitions = function(definitions, done) {
+
+ // catch synchronous exceptions during #clear()
+ try {
+ if (this._definitions) {
+ // clear existing rendered diagram
+ this.clear();
+ }
+
+ // update definitions
+ this._definitions = definitions;
+ } catch (e) {
+ return done(e);
+ }
+
+ // perform graphical import
+ return importBpmnDiagram(this, definitions, done);
+ };
+
+ Viewer.prototype.getModules = function() {
+ return this._modules;
+ };
+
+ /**
+ * Destroy the viewer instance and remove all its
+ * remainders from the document tree.
+ */
+ Viewer.prototype.destroy = function() {
+
+ // diagram destroy
+ diagramJs.prototype.destroy.call(this);
+
+ // dom detach
+ remove(this._container);
+ };
+
+ /**
+ * Register an event listener
+ *
+ * Remove a previously added listener via {@link #off(event, callback)}.
+ *
+ * @param {String} event
+ * @param {Number} [priority]
+ * @param {Function} callback
+ * @param {Object} [that]
+ */
+ Viewer.prototype.on = function(event$$1, priority, callback, target) {
+ return this.get('eventBus').on(event$$1, priority, callback, target);
+ };
+
+ /**
+ * De-register an event listener
+ *
+ * @param {String} event
+ * @param {Function} callback
+ */
+ Viewer.prototype.off = function(event$$1, callback) {
+ this.get('eventBus').off(event$$1, callback);
+ };
+
+ Viewer.prototype.attachTo = function(parentNode) {
+
+ if (!parentNode) {
+ throw new Error('parentNode required');
+ }
+
+ // ensure we detach from the
+ // previous, old parent
+ this.detach();
+
+ // unwrap jQuery if provided
+ if (parentNode.get && parentNode.constructor.prototype.jquery) {
+ parentNode = parentNode.get(0);
+ }
+
+ if (typeof parentNode === 'string') {
+ parentNode = query(parentNode);
+ }
+
+ parentNode.appendChild(this._container);
+
+ this._emit('attach', {});
+
+ this.get('canvas').resized();
+ };
+
+ Viewer.prototype.getDefinitions = function() {
+ return this._definitions;
+ };
+
+ Viewer.prototype.detach = function() {
+
+ var container = this._container,
+ parentNode = container.parentNode;
+
+ if (!parentNode) {
+ return;
+ }
+
+ this._emit('detach', {});
+
+ parentNode.removeChild(container);
+ };
+
+ Viewer.prototype._init = function(container, moddle, options) {
+
+ var baseModules = options.modules || this.getModules(),
+ additionalModules = options.additionalModules || [],
+ staticModules = [
+ {
+ bpmnjs: [ 'value', this ],
+ moddle: [ 'value', moddle ]
+ }
+ ];
+
+ var diagramModules = [].concat(staticModules, baseModules, additionalModules);
+
+ var diagramOptions = assign(omit(options, [ 'additionalModules' ]), {
+ canvas: assign({}, options.canvas, { container: container }),
+ modules: diagramModules
+ });
+
+ // invoke diagram constructor
+ diagramJs.call(this, diagramOptions);
+
+ if (options && options.container) {
+ this.attachTo(options.container);
+ }
+ };
+
+ /**
+ * Emit an event on the underlying {@link EventBus}
+ *
+ * @param {String} type
+ * @param {Object} event
+ *
+ * @return {Object} event processing result (if any)
+ */
+ Viewer.prototype._emit = function(type, event$$1) {
+ return this.get('eventBus').fire(type, event$$1);
+ };
+
+ Viewer.prototype._createContainer = function(options) {
+
+ var container = domify('
');
+
+ assign(container.style, {
+ width: ensureUnit(options.width),
+ height: ensureUnit(options.height),
+ position: options.position
+ });
+
+ return container;
+ };
+
+ Viewer.prototype._createModdle = function(options) {
+ var moddleOptions = assign({}, this._moddleExtensions, options.moddleExtensions);
+
+ return new BpmnModdle$1(moddleOptions);
+ };
+
+ // modules the viewer is composed of
+ Viewer.prototype._modules = [
+ CoreModule$1,
+ translate$2,
+ SelectionModule,
+ OveraysModule
+ ];
+
+ // default moddle extensions the viewer is composed of
+ Viewer.prototype._moddleExtensions = {};
+
+ /**
+ * Adds the project logo to the diagram container as
+ * required by the bpmn.io license.
+ *
+ * @see http://bpmn.io/license
+ *
+ * @param {Element} container
+ */
+ function addProjectLogo(container) {
+ var img = BPMNIO_IMG;
+
+ var linkMarkup =
+ '' +
+ img +
+ ' ';
+
+ var linkElement = domify(linkMarkup);
+
+ container.appendChild(linkElement);
+
+ componentEvent.bind(linkElement, 'click', function(event$$1) {
+ open();
+
+ event$$1.preventDefault();
+ });
+ }
+
+ /* */
+
+ var CURSOR_CLS_PATTERN = /^djs-cursor-.*$/;
+
+
+ function set$1(mode) {
+ var classes$$1 = classes(document.body);
+
+ classes$$1.removeMatching(CURSOR_CLS_PATTERN);
+
+ if (mode) {
+ classes$$1.add('djs-cursor-' + mode);
+ }
+ }
+
+ function unset() {
+ set$1(null);
+ }
+
+ var TRAP_PRIORITY = 5000;
+
+ /**
+ * Installs a click trap that prevents a ghost click following a dragging operation.
+ *
+ * @return {Function} a function to immediately remove the installed trap.
+ */
+ function install(eventBus, eventName) {
+
+ eventName = eventName || 'element.click';
+
+ function trap() {
+ return false;
+ }
+
+ eventBus.once(eventName, TRAP_PRIORITY, trap);
+
+ return function() {
+ eventBus.off(eventName, trap);
+ };
+ }
+
+ function delta(a, b) {
+ return {
+ x: a.x - b.x,
+ y: a.y - b.y
+ };
+ }
+
+ function length(point) {
+ return Math.sqrt(Math.pow(point.x, 2) + Math.pow(point.y, 2));
+ }
+
+ var THRESHOLD = 15;
+
+
+ function MoveCanvas(eventBus, canvas) {
+
+ var context;
+
+ function handleMove(event$$1) {
+
+ var start = context.start,
+ position = toPoint(event$$1),
+ delta$$1 = delta(position, start);
+
+ if (!context.dragging && length(delta$$1) > THRESHOLD) {
+ context.dragging = true;
+
+ install(eventBus);
+
+ set$1('grab');
+ }
+
+ if (context.dragging) {
+
+ var lastPosition = context.last || context.start;
+
+ delta$$1 = delta(position, lastPosition);
+
+ canvas.scroll({
+ dx: delta$$1.x,
+ dy: delta$$1.y
+ });
+
+ context.last = position;
+ }
+
+ // prevent select
+ event$$1.preventDefault();
+ }
+
+
+ function handleEnd(event$$1) {
+ componentEvent.unbind(document, 'mousemove', handleMove);
+ componentEvent.unbind(document, 'mouseup', handleEnd);
+
+ context = null;
+
+ unset();
+ }
+
+ function handleStart(event$$1) {
+ // event is already handled by '.djs-draggable'
+ if (closest(event$$1.target, '.djs-draggable')) {
+ return;
+ }
+
+
+ // reject non-left left mouse button or modifier key
+ if (event$$1.button || event$$1.ctrlKey || event$$1.shiftKey || event$$1.altKey) {
+ return;
+ }
+
+ context = {
+ start: toPoint(event$$1)
+ };
+
+ componentEvent.bind(document, 'mousemove', handleMove);
+ componentEvent.bind(document, 'mouseup', handleEnd);
+
+ // we've handled the event
+ return true;
+ }
+
+ // listen for move on element mouse down;
+ // allow others to hook into the event before us though
+ // (dragging / element moving will do this)
+ eventBus.on('element.mousedown', 500, function(e) {
+ return handleStart(e.originalEvent);
+ });
+
+ }
+
+
+ MoveCanvas.$inject = [ 'eventBus', 'canvas' ];
+
+ var MoveCanvasModule = {
+ __init__: [ 'moveCanvas' ],
+ moveCanvas: [ 'type', MoveCanvas ]
+ };
+
+ /**
+ * Get the logarithm of x with base 10
+ * @param {Integer} value
+ */
+ function log10(x) {
+ return Math.log(x) / Math.log(10);
+ }
+
+ /**
+ * Get step size for given range and number of steps.
+ *
+ * @param {Object} range - Range.
+ * @param {number} range.min - Range minimum.
+ * @param {number} range.max - Range maximum.
+ */
+ function getStepSize(range, steps) {
+
+ var minLinearRange = log10(range.min),
+ maxLinearRange = log10(range.max);
+
+ var absoluteLinearRange = Math.abs(minLinearRange) + Math.abs(maxLinearRange);
+
+ return absoluteLinearRange / steps;
+ }
+
+ function cap(range, scale) {
+ return Math.max(range.min, Math.min(range.max, scale));
+ }
+
+ var sign = Math.sign || function(n) {
+ return n >= 0 ? 1 : -1;
+ };
+
+ var RANGE = { min: 0.2, max: 4 },
+ NUM_STEPS = 10;
+
+ var DELTA_THRESHOLD = 0.1;
+
+ var DEFAULT_SCALE = 0.75;
+
+ /**
+ * An implementation of zooming and scrolling within the
+ * {@link Canvas} via the mouse wheel.
+ *
+ * Mouse wheel zooming / scrolling may be disabled using
+ * the {@link toggle(enabled)} method.
+ *
+ * @param {Object} [config]
+ * @param {Boolean} [config.enabled=true] default enabled state
+ * @param {Number} [config.scale=.75] scroll sensivity
+ * @param {EventBus} eventBus
+ * @param {Canvas} canvas
+ */
+ function ZoomScroll(config, eventBus, canvas) {
+
+ config = config || {};
+
+ this._enabled = false;
+
+ this._canvas = canvas;
+ this._container = canvas._container;
+
+ this._handleWheel = bind(this._handleWheel, this);
+
+ this._totalDelta = 0;
+ this._scale = config.scale || DEFAULT_SCALE;
+
+ var self = this;
+
+ eventBus.on('canvas.init', function(e) {
+ self._init(config.enabled !== false);
+ });
+ }
+
+ ZoomScroll.$inject = [
+ 'config.zoomScroll',
+ 'eventBus',
+ 'canvas'
+ ];
+
+ ZoomScroll.prototype.scroll = function scroll(delta$$1) {
+ this._canvas.scroll(delta$$1);
+ };
+
+
+ ZoomScroll.prototype.reset = function reset() {
+ this._canvas.zoom('fit-viewport');
+ };
+
+ /**
+ * Zoom depending on delta.
+ *
+ * @param {number} delta - Zoom delta.
+ * @param {Object} position - Zoom position.
+ */
+ ZoomScroll.prototype.zoom = function zoom(delta$$1, position) {
+
+ // zoom with half the step size of stepZoom
+ var stepSize = getStepSize(RANGE, NUM_STEPS * 2);
+
+ // add until threshold reached
+ this._totalDelta += delta$$1;
+
+ if (Math.abs(this._totalDelta) > DELTA_THRESHOLD) {
+ this._zoom(delta$$1, position, stepSize);
+
+ // reset
+ this._totalDelta = 0;
+ }
+ };
+
+
+ ZoomScroll.prototype._handleWheel = function handleWheel(event$$1) {
+ // event is already handled by '.djs-scrollable'
+ if (closest(event$$1.target, '.djs-scrollable', true)) {
+ return;
+ }
+
+ var element = this._container;
+
+ event$$1.preventDefault();
+
+ // pinch to zoom is mapped to wheel + ctrlKey = true
+ // in modern browsers (!)
+
+ var isZoom = event$$1.ctrlKey;
+
+ var isHorizontalScroll = event$$1.shiftKey;
+
+ var factor = -1 * this._scale,
+ delta$$1;
+
+ if (isZoom) {
+ factor *= event$$1.deltaMode === 0 ? 0.020 : 0.32;
+ } else {
+ factor *= event$$1.deltaMode === 0 ? 1.0 : 16.0;
+ }
+
+ if (isZoom) {
+ var elementRect = element.getBoundingClientRect();
+
+ var offset = {
+ x: event$$1.clientX - elementRect.left,
+ y: event$$1.clientY - elementRect.top
+ };
+
+ delta$$1 = (
+ Math.sqrt(
+ Math.pow(event$$1.deltaY, 2) +
+ Math.pow(event$$1.deltaX, 2)
+ ) * sign(event$$1.deltaY) * factor
+ );
+
+ // zoom in relative to diagram {x,y} coordinates
+ this.zoom(delta$$1, offset);
+ } else {
+
+ if (isHorizontalScroll) {
+ delta$$1 = {
+ dx: factor * event$$1.deltaY,
+ dy: 0
+ };
+ } else {
+ delta$$1 = {
+ dx: factor * event$$1.deltaX,
+ dy: factor * event$$1.deltaY
+ };
+ }
+
+ this.scroll(delta$$1);
+ }
+ };
+
+ /**
+ * Zoom with fixed step size.
+ *
+ * @param {number} delta - Zoom delta (1 for zooming in, -1 for out).
+ * @param {Object} position - Zoom position.
+ */
+ ZoomScroll.prototype.stepZoom = function stepZoom(delta$$1, position) {
+
+ var stepSize = getStepSize(RANGE, NUM_STEPS);
+
+ this._zoom(delta$$1, position, stepSize);
+ };
+
+
+ /**
+ * Zoom in/out given a step size.
+ *
+ * @param {number} delta - Zoom delta. Can be positive or negative.
+ * @param {Object} position - Zoom position.
+ * @param {number} stepSize - Step size.
+ */
+ ZoomScroll.prototype._zoom = function(delta$$1, position, stepSize) {
+ var canvas = this._canvas;
+
+ var direction = delta$$1 > 0 ? 1 : -1;
+
+ var currentLinearZoomLevel = log10(canvas.zoom());
+
+ // snap to a proximate zoom step
+ var newLinearZoomLevel = Math.round(currentLinearZoomLevel / stepSize) * stepSize;
+
+ // increase or decrease one zoom step in the given direction
+ newLinearZoomLevel += stepSize * direction;
+
+ // calculate the absolute logarithmic zoom level based on the linear zoom level
+ // (e.g. 2 for an absolute x2 zoom)
+ var newLogZoomLevel = Math.pow(10, newLinearZoomLevel);
+
+ canvas.zoom(cap(RANGE, newLogZoomLevel), position);
+ };
+
+
+ /**
+ * Toggle the zoom scroll ability via mouse wheel.
+ *
+ * @param {Boolean} [newEnabled] new enabled state
+ */
+ ZoomScroll.prototype.toggle = function toggle(newEnabled) {
+
+ var element = this._container;
+ var handleWheel = this._handleWheel;
+
+ var oldEnabled = this._enabled;
+
+ if (typeof newEnabled === 'undefined') {
+ newEnabled = !oldEnabled;
+ }
+
+ // only react on actual changes
+ if (oldEnabled !== newEnabled) {
+
+ // add or remove wheel listener based on
+ // changed enabled state
+ componentEvent[newEnabled ? 'bind' : 'unbind'](element, 'wheel', handleWheel, false);
+ }
+
+ this._enabled = newEnabled;
+
+ return newEnabled;
+ };
+
+
+ ZoomScroll.prototype._init = function(newEnabled) {
+ this.toggle(newEnabled);
+ };
+
+ var ZoomScrollModule = {
+ __init__: [ 'zoomScroll' ],
+ zoomScroll: [ 'type', ZoomScroll ]
+ };
+
+ /**
+ * A viewer that includes mouse navigation facilities
+ *
+ * @param {Object} options
+ */
+ function NavigatedViewer(options) {
+ Viewer.call(this, options);
+ }
+
+ inherits_browser(NavigatedViewer, Viewer);
+
+ NavigatedViewer.prototype._navigationModules = [
+ MoveCanvasModule,
+ ZoomScrollModule
+ ];
+
+ NavigatedViewer.prototype._modules = [].concat(
+ NavigatedViewer.prototype._modules,
+ NavigatedViewer.prototype._navigationModules);
+
+ return NavigatedViewer;
+
+})));
diff --git a/src/main/resources/templates/body.html b/src/main/resources/templates/body.html
index 5218234..e875ee1 100644
--- a/src/main/resources/templates/body.html
+++ b/src/main/resources/templates/body.html
@@ -2,7 +2,7 @@
-
+