/*!
 * bpmn-js - bpmn-modeler v4.0.4
 *
 * Copyright (c) 2014-present, camunda Services GmbH
 *
 * Released under the bpmn.io license
 * http://bpmn.io/license
 *
 * Source Code: https://github.com/bpmn-io/bpmn-js
 *
 * Date: 2019-08-05
 */
(function (global, factory) {
	typeof exports === 'object' && typeof module !== 'undefined' ? module.exports = factory() :
	typeof define === 'function' && define.amd ? define(factory) :
	(global = global || self, global.BpmnJS = factory());
}(this, function () { 'use strict';

	var commonjsGlobal = typeof globalThis !== 'undefined' ? globalThis : typeof window !== 'undefined' ? window : typeof global !== 'undefined' ? global : typeof self !== 'undefined' ? self : {};

	function createCommonjsModule(fn, module) {
		return module = { exports: {} }, fn(module, module.exports), module.exports;
	}

	var inherits_browser = createCommonjsModule(function (module) {
	if (typeof Object.create === 'function') {
	  // implementation from standard node.js 'util' module
	  module.exports = function inherits(ctor, superCtor) {
	    ctor.super_ = superCtor;
	    ctor.prototype = Object.create(superCtor.prototype, {
	      constructor: {
	        value: ctor,
	        enumerable: false,
	        writable: true,
	        configurable: true
	      }
	    });
	  };
	} else {
	  // old school shim for old browsers
	  module.exports = function inherits(ctor, superCtor) {
	    ctor.super_ = superCtor;
	    var TempCtor = function () {};
	    TempCtor.prototype = superCtor.prototype;
	    ctor.prototype = new TempCtor();
	    ctor.prototype.constructor = ctor;
	  };
	}
	});

	function createCommonjsModule$1(fn, module) {
		return module = { exports: {} }, fn(module, module.exports), module.exports;
	}

	var hat_1 = createCommonjsModule$1(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;
	}
	/**
	 * 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);
	  }
	};

	/**
	 * Flatten array, one level deep.
	 *
	 * @param {Array<?>} arr
	 *
	 * @return {Array<?>}
	 */
	function flatten(arr) {
	  return Array.prototype.concat.apply([], arr);
	}

	var nativeToString = Object.prototype.toString;
	var nativeHasOwnProperty = Object.prototype.hasOwnProperty;
	function isUndefined(obj) {
	  return obj === undefined;
	}
	function isDefined(obj) {
	  return obj !== undefined;
	}
	function isArray(obj) {
	  return nativeToString.call(obj) === '[object Array]';
	}
	function isObject(obj) {
	  return nativeToString.call(obj) === '[object Object]';
	}
	function isNumber(obj) {
	  return nativeToString.call(obj) === '[object Number]';
	}
	function isFunction(obj) {
	  var tag = nativeToString.call(obj);
	  return tag === '[object Function]' || tag === '[object AsyncFunction]' || tag === '[object GeneratorFunction]' || tag === '[object AsyncGeneratorFunction]' || tag === '[object Proxy]';
	}
	function isString(obj) {
	  return nativeToString.call(obj) === '[object String]';
	}
	/**
	 * Ensure collection is an array.
	 *
	 * @param {Object} obj
	 */

	function ensureArray(obj) {
	  if (isArray(obj)) {
	    return;
	  }

	  throw new Error('must supply array');
	}
	/**
	 * Return true, if target owns a property with the given key.
	 *
	 * @param {Object} target
	 * @param {String} key
	 *
	 * @return {Boolean}
	 */

	function has(target, key) {
	  return nativeHasOwnProperty.call(target, key);
	}

	/**
	 * Find element in collection.
	 *
	 * @param  {Array|Object} collection
	 * @param  {Function|Object} matcher
	 *
	 * @return {Object}
	 */

	function find(collection, matcher) {
	  matcher = toMatcher(matcher);
	  var match;
	  forEach(collection, function (val, key) {
	    if (matcher(val, key)) {
	      match = val;
	      return false;
	    }
	  });
	  return match;
	}
	/**
	 * Find element index in collection.
	 *
	 * @param  {Array|Object} collection
	 * @param  {Function} matcher
	 *
	 * @return {Object}
	 */

	function findIndex(collection, matcher) {
	  matcher = toMatcher(matcher);
	  var idx = isArray(collection) ? -1 : undefined;
	  forEach(collection, function (val, key) {
	    if (matcher(val, key)) {
	      idx = key;
	      return false;
	    }
	  });
	  return idx;
	}
	/**
	 * Find element in collection.
	 *
	 * @param  {Array|Object} collection
	 * @param  {Function} matcher
	 *
	 * @return {Array} result
	 */

	function filter(collection, matcher) {
	  var result = [];
	  forEach(collection, function (val, key) {
	    if (matcher(val, key)) {
	      result.push(val);
	    }
	  });
	  return result;
	}
	/**
	 * Iterate over collection; returning something
	 * (non-undefined) will stop iteration.
	 *
	 * @param  {Array|Object} collection
	 * @param  {Function} iterator
	 *
	 * @return {Object} return result that stopped the iteration
	 */

	function forEach(collection, iterator) {
	  var val, result;

	  if (isUndefined(collection)) {
	    return;
	  }

	  var convertKey = isArray(collection) ? toNum : identity;

	  for (var key in collection) {
	    if (has(collection, key)) {
	      val = collection[key];
	      result = iterator(val, convertKey(key));

	      if (result === false) {
	        return val;
	      }
	    }
	  }
	}
	/**
	 * Return collection without element.
	 *
	 * @param  {Array} arr
	 * @param  {Function} matcher
	 *
	 * @return {Array}
	 */

	function without(arr, matcher) {
	  if (isUndefined(arr)) {
	    return [];
	  }

	  ensureArray(arr);
	  matcher = toMatcher(matcher);
	  return arr.filter(function (el, idx) {
	    return !matcher(el, idx);
	  });
	}
	/**
	 * Reduce collection, returning a single result.
	 *
	 * @param  {Object|Array} collection
	 * @param  {Function} iterator
	 * @param  {Any} result
	 *
	 * @return {Any} result returned from last iterator
	 */

	function reduce(collection, iterator, result) {
	  forEach(collection, function (value, idx) {
	    result = iterator(result, value, idx);
	  });
	  return result;
	}
	/**
	 * Return true if every element in the collection
	 * matches the criteria.
	 *
	 * @param  {Object|Array} collection
	 * @param  {Function} matcher
	 *
	 * @return {Boolean}
	 */

	function every(collection, matcher) {
	  return reduce(collection, function (matches, val, key) {
	    return matches && matcher(val, key);
	  }, true);
	}
	/**
	 * Return true if some elements in the collection
	 * match the criteria.
	 *
	 * @param  {Object|Array} collection
	 * @param  {Function} matcher
	 *
	 * @return {Boolean}
	 */

	function some(collection, matcher) {
	  return !!find(collection, matcher);
	}
	/**
	 * Transform a collection into another collection
	 * by piping each member through the given fn.
	 *
	 * @param  {Object|Array}   collection
	 * @param  {Function} fn
	 *
	 * @return {Array} transformed collection
	 */

	function map(collection, fn) {
	  var result = [];
	  forEach(collection, function (val, key) {
	    result.push(fn(val, key));
	  });
	  return result;
	}
	/**
	 * Get the collections keys.
	 *
	 * @param  {Object|Array} collection
	 *
	 * @return {Array}
	 */

	function keys(collection) {
	  return collection && Object.keys(collection) || [];
	}
	/**
	 * Shorthand for `keys(o).length`.
	 *
	 * @param  {Object|Array} collection
	 *
	 * @return {Number}
	 */

	function size(collection) {
	  return keys(collection).length;
	}
	/**
	 * Get the values in the collection.
	 *
	 * @param  {Object|Array} collection
	 *
	 * @return {Array}
	 */

	function values(collection) {
	  return map(collection, function (val) {
	    return val;
	  });
	}
	/**
	 * Group collection members by attribute.
	 *
	 * @param  {Object|Array} collection
	 * @param  {Function} extractor
	 *
	 * @return {Object} map with { attrValue => [ a, b, c ] }
	 */

	function groupBy(collection, extractor) {
	  var grouped = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : {};
	  extractor = toExtractor(extractor);
	  forEach(collection, function (val) {
	    var discriminator = extractor(val) || '_';
	    var group = grouped[discriminator];

	    if (!group) {
	      group = grouped[discriminator] = [];
	    }

	    group.push(val);
	  });
	  return grouped;
	}
	function uniqueBy(extractor) {
	  extractor = toExtractor(extractor);
	  var grouped = {};

	  for (var _len = arguments.length, collections = new Array(_len > 1 ? _len - 1 : 0), _key = 1; _key < _len; _key++) {
	    collections[_key - 1] = arguments[_key];
	  }

	  forEach(collections, function (c) {
	    return groupBy(c, extractor, grouped);
	  });
	  var result = map(grouped, function (val, key) {
	    return val[0];
	  });
	  return result;
	}
	var unionBy = uniqueBy;
	/**
	 * Sort collection by criteria.
	 *
	 * @param  {Object|Array} collection
	 * @param  {String|Function} extractor
	 *
	 * @return {Array}
	 */

	function sortBy(collection, extractor) {
	  extractor = toExtractor(extractor);
	  var sorted = [];
	  forEach(collection, function (value, key) {
	    var disc = extractor(value, key);
	    var entry = {
	      d: disc,
	      v: value
	    };

	    for (var idx = 0; idx < sorted.length; idx++) {
	      var d = sorted[idx].d;

	      if (disc < d) {
	        sorted.splice(idx, 0, entry);
	        return;
	      }
	    } // not inserted, append (!)


	    sorted.push(entry);
	  });
	  return map(sorted, function (e) {
	    return e.v;
	  });
	}
	/**
	 * Create an object pattern matcher.
	 *
	 * @example
	 *
	 * const matcher = matchPattern({ id: 1 });
	 *
	 * var element = find(elements, matcher);
	 *
	 * @param  {Object} pattern
	 *
	 * @return {Function} matcherFn
	 */

	function matchPattern(pattern) {
	  return function (el) {
	    return every(pattern, function (val, key) {
	      return el[key] === val;
	    });
	  };
	}

	function toExtractor(extractor) {
	  return isFunction(extractor) ? extractor : function (e) {
	    return e[extractor];
	  };
	}

	function toMatcher(matcher) {
	  return isFunction(matcher) ? matcher : function (e) {
	    return e === matcher;
	  };
	}

	function identity(arg) {
	  return arg;
	}

	function toNum(arg) {
	  return Number(arg);
	}

	/**
	 * Debounce fn, calling it only once if
	 * the given time elapsed between calls.
	 *
	 * @param  {Function} fn
	 * @param  {Number} timeout
	 *
	 * @return {Function} debounced function
	 */
	function debounce(fn, timeout) {
	  var timer;
	  var lastArgs;
	  var lastThis;
	  var lastNow;

	  function fire() {
	    var now = Date.now();
	    var scheduledDiff = lastNow + timeout - now;

	    if (scheduledDiff > 0) {
	      return schedule(scheduledDiff);
	    }

	    fn.apply(lastThis, lastArgs);
	    timer = lastNow = lastArgs = lastThis = undefined;
	  }

	  function schedule(timeout) {
	    timer = setTimeout(fire, timeout);
	  }

	  return function () {
	    lastNow = Date.now();

	    for (var _len = arguments.length, args = new Array(_len), _key = 0; _key < _len; _key++) {
	      args[_key] = arguments[_key];
	    }

	    lastArgs = args;
	    lastThis = this; // ensure an execution is scheduled

	    if (!timer) {
	      schedule(timeout);
	    }
	  };
	}
	/**
	 * Bind function against target <this>.
	 *
	 * @param  {Function} fn
	 * @param  {Object}   target
	 *
	 * @return {Function} bound function
	 */

	function bind(fn, target) {
	  return fn.bind(target);
	}

	function _extends() {
	  _extends = Object.assign || function (target) {
	    for (var i = 1; i < arguments.length; i++) {
	      var source = arguments[i];

	      for (var key in source) {
	        if (Object.prototype.hasOwnProperty.call(source, key)) {
	          target[key] = source[key];
	        }
	      }
	    }

	    return target;
	  };

	  return _extends.apply(this, arguments);
	}

	/**
	 * Convenience wrapper for `Object.assign`.
	 *
	 * @param {Object} target
	 * @param {...Object} others
	 *
	 * @return {Object} the target
	 */

	function assign(target) {
	  for (var _len = arguments.length, others = new Array(_len > 1 ? _len - 1 : 0), _key = 1; _key < _len; _key++) {
	    others[_key - 1] = arguments[_key];
	  }

	  return _extends.apply(void 0, [target].concat(others));
	}
	/**
	 * Pick given properties from the target object.
	 *
	 * @param {Object} target
	 * @param {Array} properties
	 *
	 * @return {Object} target
	 */

	function pick(target, properties) {
	  var result = {};
	  var obj = Object(target);
	  forEach(properties, function (prop) {
	    if (prop in obj) {
	      result[prop] = target[prop];
	    }
	  });
	  return result;
	}
	/**
	 * Pick all target properties, excluding the given ones.
	 *
	 * @param {Object} target
	 * @param {Array} properties
	 *
	 * @return {Object} target
	 */

	function omit(target, properties) {
	  var result = {};
	  var obj = Object(target);
	  forEach(obj, function (prop, key) {
	    if (properties.indexOf(key) === -1) {
	      result[key] = prop;
	    }
	  });
	  return result;
	}

	/**
	 * Set attribute `name` to `val`, or get attr `name`.
	 *
	 * @param {Element} el
	 * @param {String} name
	 * @param {String} [val]
	 * @api public
	 */
	function attr(el, name, val) {
	  // get
	  if (arguments.length == 2) {
	    return el.getAttribute(name);
	  }

	  // remove
	  if (val === null) {
	    return el.removeAttribute(name);
	  }

	  // set
	  el.setAttribute(name, val);

	  return el;
	}

	var indexOf = [].indexOf;

	var indexof = function(arr, obj){
	  if (indexOf) return arr.indexOf(obj);
	  for (var i = 0; i < arr.length; ++i) {
	    if (arr[i] === obj) return i;
	  }
	  return -1;
	};

	/**
	 * Taken from https://github.com/component/classes
	 *
	 * Without the component bits.
	 */

	/**
	 * Whitespace regexp.
	 */

	var re = /\s+/;

	/**
	 * toString reference.
	 */

	var toString = Object.prototype.toString;

	/**
	 * Wrap `el` in a `ClassList`.
	 *
	 * @param {Element} el
	 * @return {ClassList}
	 * @api public
	 */

	function classes(el) {
	  return new ClassList(el);
	}

	/**
	 * Initialize a new ClassList for `el`.
	 *
	 * @param {Element} el
	 * @api private
	 */

	function ClassList(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.prototype.add = function (name) {
	  // classList
	  if (this.list) {
	    this.list.add(name);
	    return this;
	  }

	  // fallback
	  var arr = this.array();
	  var i = indexof(arr, name);
	  if (!~i) arr.push(name);
	  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.prototype.remove = function (name) {
	  if ('[object RegExp]' == toString.call(name)) {
	    return this.removeMatching(name);
	  }

	  // classList
	  if (this.list) {
	    this.list.remove(name);
	    return this;
	  }

	  // fallback
	  var arr = this.array();
	  var i = indexof(arr, name);
	  if (~i) arr.splice(i, 1);
	  this.el.className = arr.join(' ');
	  return this;
	};

	/**
	 * Remove all classes matching `re`.
	 *
	 * @param {RegExp} re
	 * @return {ClassList}
	 * @api private
	 */

	ClassList.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.prototype.toggle = function (name, force) {
	  // classList
	  if (this.list) {
	    if ('undefined' !== typeof 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 ('undefined' !== typeof 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.prototype.array = function () {
	  var className = this.el.getAttribute('class') || '';
	  var str = className.replace(/^\s+|\s+$/g, '');
	  var arr = str.split(re);
	  if ('' === arr[0]) arr.shift();
	  return arr;
	};

	/**
	 * Check if class `name` is present.
	 *
	 * @param {String} name
	 * @return {ClassList}
	 * @api public
	 */

	ClassList.prototype.has = ClassList.prototype.contains = function (name) {
	  return this.list ? this.list.contains(name) : !!~indexof(this.array(), name);
	};

	/**
	 * Remove all children from the given element.
	 */
	function clear(el) {

	  var c;

	  while (el.childNodes.length) {
	    c = el.childNodes[0];
	    el.removeChild(c);
	  }

	  return el;
	}

	/**
	 * Element prototype.
	 */

	var proto = Element.prototype;

	/**
	 * Vendor function.
	 */

	var vendor = proto.matchesSelector
	  || proto.webkitMatchesSelector
	  || proto.mozMatchesSelector
	  || proto.msMatchesSelector
	  || proto.oMatchesSelector;

	/**
	 * Expose `match()`.
	 */

	var matchesSelector = match;

	/**
	 * Match `el` to `selector`.
	 *
	 * @param {Element} el
	 * @param {String} selector
	 * @return {Boolean}
	 * @api public
	 */

	function match(el, selector) {
	  if (vendor) return vendor.call(el, selector);
	  var nodes = el.parentNode.querySelectorAll(selector);
	  for (var i = 0; i < nodes.length; ++i) {
	    if (nodes[i] == el) return true;
	  }
	  return false;
	}

	var closest = function (element, selector, checkYoSelf) {
	  var parent = checkYoSelf ? element : element.parentNode;

	  while (parent && parent !== document) {
	    if (matchesSelector(parent, selector)) return parent;
	    parent = parent.parentNode;
	  }
	};

	var bind$1 = window.addEventListener ? 'addEventListener' : 'attachEvent',
	    unbind = window.removeEventListener ? 'removeEventListener' : 'detachEvent',
	    prefix = bind$1 !== 'addEventListener' ? 'on' : '';

	/**
	 * Bind `el` event `type` to `fn`.
	 *
	 * @param {Element} el
	 * @param {String} type
	 * @param {Function} fn
	 * @param {Boolean} capture
	 * @return {Function}
	 * @api public
	 */

	var bind_1 = function(el, type, fn, capture){
	  el[bind$1](prefix + type, fn, capture || false);
	  return fn;
	};

	/**
	 * Unbind `el` event `type`'s callback `fn`.
	 *
	 * @param {Element} el
	 * @param {String} type
	 * @param {Function} fn
	 * @param {Boolean} capture
	 * @return {Function}
	 * @api public
	 */

	var unbind_1 = function(el, type, fn, capture){
	  el[unbind](prefix + type, fn, capture || false);
	  return fn;
	};

	var componentEvent = {
		bind: bind_1,
		unbind: unbind_1
	};

	/**
	 * Module dependencies.
	 */



	/**
	 * Delegate event `type` to `selector`
	 * and invoke `fn(e)`. A callback function
	 * is returned which may be passed to `.unbind()`.
	 *
	 * @param {Element} el
	 * @param {String} selector
	 * @param {String} type
	 * @param {Function} fn
	 * @param {Boolean} capture
	 * @return {Function}
	 * @api public
	 */

	// Some events don't bubble, so we want to bind to the capture phase instead
	// when delegating.
	var forceCaptureEvents = ['focus', 'blur'];

	var bind$1$1 = function(el, selector, type, fn, capture){
	  if (forceCaptureEvents.indexOf(type) !== -1) capture = true;

	  return componentEvent.bind(el, type, function(e){
	    var target = e.target || e.srcElement;
	    e.delegateTarget = closest(target, selector, true);
	    if (e.delegateTarget) fn.call(el, e);
	  }, capture);
	};

	/**
	 * Unbind event `type`'s callback `fn`.
	 *
	 * @param {Element} el
	 * @param {String} type
	 * @param {Function} fn
	 * @param {Boolean} capture
	 * @api public
	 */

	var unbind$1 = function(el, type, fn, capture){
	  if (forceCaptureEvents.indexOf(type) !== -1) capture = true;

	  componentEvent.unbind(el, type, fn, capture);
	};

	var delegateEvents = {
		bind: bind$1$1,
		unbind: unbind$1
	};

	/**
	 * Expose `parse`.
	 */

	var domify = parse;

	/**
	 * Tests for browser support.
	 */

	var innerHTMLBug = false;
	var bugTestDiv;
	if (typeof document !== 'undefined') {
	  bugTestDiv = document.createElement('div');
	  // Setup
	  bugTestDiv.innerHTML = '  <link/><table></table><a href="/a">a</a><input type="checkbox"/>';
	  // Make sure that link elements get serialized correctly by innerHTML
	  // This requires a wrapper element in IE
	  innerHTMLBug = !bugTestDiv.getElementsByTagName('link').length;
	  bugTestDiv = undefined;
	}

	/**
	 * Wrap map from jquery.
	 */

	var map$1 = {
	  legend: [1, '<fieldset>', '</fieldset>'],
	  tr: [2, '<table><tbody>', '</tbody></table>'],
	  col: [2, '<table><tbody></tbody><colgroup>', '</colgroup></table>'],
	  // for script/link/style tags to work in IE6-8, you have to wrap
	  // in a div with a non-whitespace character in front, ha!
	  _default: innerHTMLBug ? [1, 'X<div>', '</div>'] : [0, '', '']
	};

	map$1.td =
	map$1.th = [3, '<table><tbody><tr>', '</tr></tbody></table>'];

	map$1.option =
	map$1.optgroup = [1, '<select multiple="multiple">', '</select>'];

	map$1.thead =
	map$1.tbody =
	map$1.colgroup =
	map$1.caption =
	map$1.tfoot = [1, '<table>', '</table>'];

	map$1.polyline =
	map$1.ellipse =
	map$1.polygon =
	map$1.circle =
	map$1.text =
	map$1.line =
	map$1.path =
	map$1.rect =
	map$1.g = [1, '<svg xmlns="http://www.w3.org/2000/svg" version="1.1">','</svg>'];

	/**
	 * Parse `html` and return a DOM Node instance, which could be a TextNode,
	 * HTML DOM Node of some kind (<div> 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;

	var matchesSelector$1 = match$1;

	/**
	 * Match `el` to `selector`.
	 *
	 * @param {Element} el
	 * @param {String} selector
	 * @return {Boolean}
	 * @api public
	 */

	function match$1(el, selector) {
	  if (!el || el.nodeType !== 1) return false;
	  if (vendor$1) return vendor$1.call(el, selector);
	  var nodes = el.parentNode.querySelectorAll(selector);
	  for (var i = 0; i < nodes.length; i++) {
	    if (nodes[i] == el) return true;
	  }
	  return false;
	}

	function query(selector, el) {
	  el = el || document;

	  return el.querySelector(selector);
	}

	function all(selector, el) {
	  el = el || document;

	  return el.querySelectorAll(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} target
	 *
	 * @return {SVGElement} the appended node
	 */
	function appendTo(element, target) {
	  return target.appendChild(ensureImported(element, target));
	}

	/**
	 * append utility
	 */

	/**
	 * Append a node to an element
	 *
	 * @param  {SVGElement} element
	 * @param  {SVGElement} node
	 *
	 * @return {SVGElement} the element
	 */
	function append(target, node) {
	  appendTo(node, target);
	  return target;
	}

	/**
	 * 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;
	}

	function clone(element) {
	  return element.cloneNode(true);
	}

	var ns = {
	  svg: 'http://www.w3.org/2000/svg'
	};

	/**
	 * DOM parsing utility
	 */

	var SVG_START = '<svg xmlns="' + ns.svg + '"';

	function parse$1(svg) {

	  var unwrap = false;

	  // ensure we import a valid svg document
	  if (svg.substring(0, 4) === '<svg') {
	    if (svg.indexOf(ns.svg) === -1) {
	      svg = SVG_START + svg.substring(4);
	    }
	  } else {
	    // namespace svg
	    svg = SVG_START + '>' + svg + '</svg>';
	    unwrap = true;
	  }

	  var parsed = parseDocument(svg);

	  if (!unwrap) {
	    return parsed;
	  }

	  var fragment = document.createDocumentFragment();

	  var parent = parsed.firstChild;

	  while (parent.firstChild) {
	    fragment.appendChild(parent.firstChild);
	  }

	  return fragment;
	}

	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;
	}

	/**
	 * Create matrix via args.
	 *
	 * @example
	 *
	 * createMatrix({ a: 1, b: 1 });
	 * createMatrix();
	 * createMatrix(1, 2, 0, 0, 30, 20);
	 *
	 * @return {SVGMatrix}
	 */
	function createMatrix(a, b, c, d, e, f) {
	  var matrix = node.createSVGMatrix();

	  switch (arguments.length) {
	  case 0:
	    return matrix;
	  case 1:
	    return extend(matrix, a);
	  case 6:
	    return extend(matrix, {
	      a: a,
	      b: b,
	      c: c,
	      d: d,
	      e: e,
	      f: f
	    });
	  }
	}

	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 = {
	  '&': '&amp;',
	  '<': '&lt;',
	  '>': '&gt;',
	  '"': '\''
	};

	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('<!--', escape(node.nodeValue, TEXT_ENTITIES), '-->');
	    break;

	  // CDATA
	  case 4:
	    output.push('<![CDATA[', node.nodeValue, ']]>');
	    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 parsed = parse$1(svg);

	  // clear element contents
	  clear$1(element);

	  if (!svg) {
	    return;
	  }

	  if (!isFragment(parsed)) {
	    // extract <svg> from parsed document
	    parsed = parsed.documentElement;
	  }

	  var nodes = slice(parsed.childNodes);

	  // import + append each node
	  for (var i = 0; i < nodes.length; i++) {
	    appendTo(nodes[i], element);
	  }

	}

	function get(element) {
	  var child = element.firstChild,
	      output = [];

	  while (child) {
	    serialize(child, output);
	    child = child.nextSibling;
	  }

	  return output.join('');
	}

	function isFragment(node) {
	  return node.nodeName === '#document-fragment';
	}

	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);
	  }
	}


	function slice(arr) {
	  return Array.prototype.slice.call(arr);
	}

	/**
	 * transform accessor utility
	 */

	function wrapMatrix(transformList, transform) {
	  if (transform instanceof SVGMatrix) {
	    return transformList.createSVGTransformFromMatrix(transform);
	  }

	  return transform;
	}


	function setTransforms(transformList, transforms) {
	  var i, t;

	  transformList.clear();

	  for (i = 0; (t = transforms[i]); i++) {
	    transformList.appendItem(wrapMatrix(transformList, t));
	  }
	}

	/**
	 * Get or set the transforms on the given node.
	 *
	 * @param {SVGElement} node
	 * @param  {SVGTransform|SVGMatrix|Array<SVGTransform|SVGMatrix>} [transforms]
	 *
	 * @return {SVGTransform} the consolidated transform
	 */
	function transform(node, transforms) {
	  var transformList = node.transform.baseVal;

	  if (transforms) {

	    if (!Array.isArray(transforms)) {
	      transforms = [ transforms ];
	    }

	    setTransforms(transformList, transforms);
	  }

	  return transformList.consolidate();
	}

	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(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(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(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;
	}

	/**
	 * Adds an element to a collection and returns true if the
	 * element was added.
	 *
	 * @param {Array<Object>} elements
	 * @param {Object} e
	 * @param {Boolean} unique
	 */
	function add(elements, e, unique) {
	  var canAdd = !unique || elements.indexOf(e) === -1;

	  if (canAdd) {
	    elements.push(e);
	  }

	  return canAdd;
	}


	/**
	 * Iterate over each element in a collection, calling the iterator function `fn`
	 * with (element, index, recursionDepth).
	 *
	 * Recurse into all elements that are returned by `fn`.
	 *
	 * @param  {Object|Array<Object>} elements
	 * @param  {Function} fn iterator function called with (element, index, recursionDepth)
	 * @param  {Number} [depth] maximum recursion depth
	 */
	function eachElement(elements, fn, depth) {

	  depth = depth || 0;

	  if (!isArray(elements)) {
	    elements = [ elements ];
	  }

	  forEach(elements, function(s, i) {
	    var filter = fn(s, i, depth);

	    if (isArray(filter) && filter.length) {
	      eachElement(filter, fn, depth + 1);
	    }
	  });
	}


	/**
	 * Collects self + child elements up to a given depth from a list of elements.
	 *
	 * @param  {djs.model.Base|Array<djs.model.Base>} elements the elements to select the children from
	 * @param  {Boolean} unique whether to return a unique result set (no duplicates)
	 * @param  {Number} maxDepth the depth to search through or -1 for infinite
	 *
	 * @return {Array<djs.model.Base>} found elements
	 */
	function selfAndChildren(elements, unique, maxDepth) {
	  var result = [],
	      processedChildren = [];

	  eachElement(elements, function(element, i, depth) {
	    add(result, element, unique);

	    var children = element.children;

	    // max traversal depth not reached yet
	    if (maxDepth === -1 || depth < maxDepth) {

	      // children exist && children not yet processed
	      if (children && add(processedChildren, children, unique)) {
	        return children;
	      }
	    }
	  });

	  return result;
	}


	/**
	 * Return self + ALL children for a number of elements
	 *
	 * @param  {Array<djs.model.Base>} elements to query
	 * @param  {Boolean} allowDuplicates to allow duplicates in the result set
	 *
	 * @return {Array<djs.model.Base>} the collected elements
	 */
	function selfAndAllChildren(elements, allowDuplicates) {
	  return selfAndChildren(elements, !allowDuplicates, -1);
	}


	/**
	 * Gets the the closure for all selected elements,
	 * their enclosed children and connections.
	 *
	 * @param {Array<djs.model.Base>} elements
	 * @param {Boolean} [isTopLevel=true]
	 * @param {Object} [existingClosure]
	 *
	 * @return {Object} newClosure
	 */
	function getClosure(elements, isTopLevel, closure) {

	  if (isUndefined(isTopLevel)) {
	    isTopLevel = true;
	  }

	  if (isObject(isTopLevel)) {
	    closure = isTopLevel;
	    isTopLevel = true;
	  }


	  closure = closure || {};

	  var allShapes = copyObject(closure.allShapes),
	      allConnections = copyObject(closure.allConnections),
	      enclosedElements = copyObject(closure.enclosedElements),
	      enclosedConnections = copyObject(closure.enclosedConnections);

	  var topLevel = copyObject(
	    closure.topLevel,
	    isTopLevel && groupBy(elements, function(e) { return e.id; })
	  );


	  function handleConnection(c) {
	    if (topLevel[c.source.id] && topLevel[c.target.id]) {
	      topLevel[c.id] = [ c ];
	    }

	    // not enclosed as a child, but maybe logically
	    // (connecting two moved elements?)
	    if (allShapes[c.source.id] && allShapes[c.target.id]) {
	      enclosedConnections[c.id] = enclosedElements[c.id] = c;
	    }

	    allConnections[c.id] = c;
	  }

	  function handleElement(element) {

	    enclosedElements[element.id] = element;

	    if (element.waypoints) {
	      // remember connection
	      enclosedConnections[element.id] = allConnections[element.id] = element;
	    } else {
	      // remember shape
	      allShapes[element.id] = element;

	      // remember all connections
	      forEach(element.incoming, handleConnection);

	      forEach(element.outgoing, handleConnection);

	      // recurse into children
	      return element.children;
	    }
	  }

	  eachElement(elements, handleElement);

	  return {
	    allShapes: allShapes,
	    allConnections: allConnections,
	    topLevel: topLevel,
	    enclosedConnections: enclosedConnections,
	    enclosedElements: enclosedElements
	  };
	}

	/**
	 * Returns the surrounding bbox for all elements in
	 * the array or the element primitive.
	 *
	 * @param {Array<djs.model.Shape>|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
	  };
	}


	/**
	 * Returns all elements that are enclosed from the bounding box.
	 *
	 *   * If bbox.(width|height) is not specified the method returns
	 *     all elements with element.x/y > bbox.x/y
	 *   * If only bbox.x or bbox.y is specified, method return all elements with
	 *     e.x > bbox.x or e.y > bbox.y
	 *
	 * @param {Array<djs.model.Shape>} elements List of Elements to search through
	 * @param {djs.model.Shape} bbox the enclosing bbox.
	 *
	 * @return {Array<djs.model.Shape>} enclosed elements
	 */
	function getEnclosedElements(elements, bbox) {

	  var filteredElements = {};

	  forEach(elements, function(element) {

	    var e = element;

	    if (e.waypoints) {
	      e = getBBox(e);
	    }

	    if (!isNumber(bbox.y) && (e.x > bbox.x)) {
	      filteredElements[element.id] = element;
	    }
	    if (!isNumber(bbox.x) && (e.y > bbox.y)) {
	      filteredElements[element.id] = element;
	    }
	    if (e.x > bbox.x && e.y > bbox.y) {
	      if (isNumber(bbox.width) && isNumber(bbox.height) &&
	          e.width + e.x < bbox.width + bbox.x &&
	          e.height + e.y < bbox.height + bbox.y) {

	        filteredElements[element.id] = element;
	      } else if (!isNumber(bbox.width) || !isNumber(bbox.height)) {
	        filteredElements[element.id] = element;
	      }
	    }
	  });

	  return filteredElements;
	}


	function getType(element) {

	  if ('waypoints' in element) {
	    return 'connection';
	  }

	  if ('x' in element) {
	    return 'shape';
	  }

	  return 'root';
	}

	function isFrameElement(element) {

	  return !!(element && element.isFrame);
	}

	// helpers ///////////////////////////////

	function copyObject(src1, src2) {
	  return assign({}, src1 || {}, src2 || {});
	}

	// 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 });
	  this.FRAME_STYLE = styles.style([ 'no-fill' ], { stroke: 'fuchsia', strokeDasharray: 4, 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
	  });

	  if (isFrameElement(element)) {
	    attr$1(rect, this.FRAME_STYLE);
	  } else {
	    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<String>} 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<String>} 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<Object>} [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<Object>} collection
	 * @param {Object} element
	 * @param {Number} idx
	 */
	function add$1(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);
	  }
	}


	/**
	 * Fail save get the index of an element in a collection.
	 *
	 * @param {Array<Object>} collection
	 * @param {Object} element
	 *
	 * @return {Number} the index or -1 if collection or element do
	 *                  not exist or the element is not contained.
	 */
	function indexOf$1(collection, element) {

	  if (!collection || !element) {
	    return -1;
	  }

	  return collection.indexOf(element);
	}

	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 <div> around the svg element with the respective size
	  // this way we can always get the correct container size
	  // (this is impossible for <svg> 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;

	  // must ensure second argument is node or _null_
	  // cf. https://developer.mozilla.org/en-US/docs/Web/API/Node/insertBefore
	  parent.insertBefore(group, parent.childNodes[index] || null);

	  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 <svg> element that is wrapped into a <div>.
	  // 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)
	  //
	  // <div class="djs-container" style="width: {desired-width}, height: {desired-height}">
	  //   <svg width="100%" height="100%">
	  //    ...
	  //   </svg>
	  // </div>

	  // 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) {
	  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) {
	        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 });
	};


	/**
	 * 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; // <g class="... some-marker"> ... </g>
	 *
	 * @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) {
	    return typeof element[attr] !== 'undefined';
	  });

	  if (!valid) {
	    throw new Error(
	      'must supply { ' + requiredAttrs.join(', ') + ' } with ' + type);
	  }
	};

	Canvas.prototype._setParent = function(element, parent, parentIndex) {
	  add$1(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<djs.model.Base>}
	 */
	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<djs.model.Base>}
	 */
	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); // <g ...>
	 *
	 * elementRegistry.getGraphics(rootElement, true); // <svg ...>
	 *
	 *
	 * @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<Object>} collection
	 * @param  {Refs} refs instance
	 * @param  {Object} property represented by the collection
	 * @param  {Object} target object the collection is attached to
	 *
	 * @return {RefsCollection<Object>} 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.
	 *
	 * <p>
	 *   When bound to an object using {@link Refs#bind} the references
	 *   get activated and ensure that add and remove operations are applied
	 *   reversely, too.
	 * </p>
	 *
	 * <p>
	 *   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.
	 * </p>
	 *
	 * @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<Connection>
	   */
	  outgoingRefs.bind(this, 'outgoing');

	  /**
	   * The list of incoming connections
	   *
	   * @name Base#incoming
	   * @type Array<Connection>
	   */
	  incomingRefs.bind(this, 'incoming');
	}


	/**
	 * A graphical object
	 *
	 * @class
	 * @constructor
	 *
	 * @extends Base
	 */
	function Shape() {
	  Base.call(this);

	  /**
	   * Indicates frame shapes
	   *
	   * @name Shape#isFrame
	   * @type Boolean
	   */

	  /**
	   * The list of children
	   *
	   * @name Shape#children
	   * @type Array<Base>
	   */
	  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<String>} 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;

	  events.forEach(function(e) {
	    self._addListener(e, {
	      priority: priority,
	      callback: actualCallback,
	      next: null
	    });
	  });
	};


	/**
	 * Register an event listener that is executed only once.
	 *
	 * @param {String} event the event name to register for
	 * @param {Number} [priority=1000] the priority in which this listener is called, larger is higher
	 * @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() {
	    var result = callback.apply(that, arguments);

	    self.off(event, wrappedCallback);

	    return result;
	  }

	  // 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|Array<String>} events
	 * @param {Function} [callback]
	 */
	EventBus.prototype.off = function(events, callback) {

	  events = isArray(events) ? events : [ events ];

	  var self = this;

	  events.forEach(function(event) {
	    self._removeListener(event, callback);
	  });

	};


	/**
	 * 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,
	      firstListener,
	      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');
	  }

	  firstListener = this._listeners[type];

	  if (!firstListener) {
	    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, firstListener);
	  } 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, listener) {

	  var returnValue;

	  while (listener) {

	    // handle stopped propagation
	    if (event.cancelBubble) {
	      break;
	    }

	    returnValue = this._invokeListener(event, args, listener);

	    listener = listener.next;
	  }

	  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 listener = this._getListeners(event),
	      previousListener;

	  // no prior listeners
	  if (!listener) {
	    this._setListeners(event, newListener);

	    return;
	  }

	  // ensure we order listeners by priority from
	  // 0 (high) to n > 0 (low)
	  while (listener) {

	    if (listener.priority < newListener.priority) {

	      newListener.next = listener;

	      if (previousListener) {
	        previousListener.next = newListener;
	      } else {
	        this._setListeners(event, newListener);
	      }

	      return;
	    }

	    previousListener = listener;
	    listener = listener.next;
	  }

	  // add new listener to back
	  previousListener.next = newListener;
	};


	EventBus.prototype._getListeners = function(name) {
	  return this._listeners[name];
	};

	EventBus.prototype._setListeners = function(name, listener) {
	  this._listeners[name] = listener;
	};

	EventBus.prototype._removeListener = function(event, callback) {

	  var listener = this._getListeners(event),
	      nextListener,
	      previousListener,
	      listenerCallback;

	  if (!callback) {
	    // clear listeners
	    this._setListeners(event, null);

	    return;
	  }

	  while (listener) {

	    nextListener = listener.next;

	    listenerCallback = listener.callback;

	    if (listenerCallback === callback || listenerCallback[FN_REF] === callback) {
	      if (previousListener) {
	        previousListener.next = nextListener;
	      } else {
	        // new first listener
	        this._setListeners(event, nextListener);
	      }
	    }

	    previousListener = listener;
	    listener = nextListener;
	  }
	};

	/**
	 * 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<Object>} 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<SVGElement>} gfx
	 *
	 * @return {Snap<SVGElement>}
	 */
	function getVisual(gfx) {
	  return query('.djs-visual', gfx);
	}

	/**
	 * Returns the children for a given diagram element.
	 *
	 * @param {Snap<SVGElement>} gfx
	 * @return {Snap<SVGElement>}
	 */
	function getChildren(gfx) {
	  return gfx.parentNode.childNodes[1];
	}

	/**
	 * @param {<SVGElement>} 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 <g class="djs-visual" /> 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:
	 *
	 * <g class="djs-group">
	 *
	 *   <!-- the gfx -->
	 *   <g class="djs-element djs-(shape|connection|frame)">
	 *     <g class="djs-visual">
	 *       <!-- the renderer draws in here -->
	 *     </g>
	 *
	 *     <!-- extensions (overlays, click box, ...) goes here
	 *   </g>
	 *
	 *   <!-- the gfx child nodes -->
	 *   <g class="djs-children"></g>
	 * </g>
	 *
	 * @param {String} type the type of the element, i.e. shape | connection
	 * @param {SVGElement} [childrenGfx]
	 * @param {Number} [parentIndex] position to create container in parent
	 * @param {Boolean} [isFrame] is frame element
	 *
	 * @return {SVGElement}
	 */
	GraphicsFactory.prototype._createContainer = function(
	    type, childrenGfx, parentIndex, isFrame
	) {
	  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);

	  if (isFrame) {
	    classes$1(gfx).add('djs-frame');
	  }

	  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, isFrameElement(element));
	};

	GraphicsFactory.prototype.updateContainments = function(elements) {

	  var self = this,
	      elementRegistry = this._elementRegistry,
	      parents;

	  parents = reduce(elements, function(map, e) {

	    if (e.parent) {
	      map[e.parent.id] = e.parent;
	    }

	    return map;
	  }, {});

	  // 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) {
	  var node = siblingNode || parentNode.firstChild;

	  // do not prepend node to itself to prevent IE from crashing
	  // https://github.com/bpmn-io/bpmn-js/issues/746
	  if (newNode === node) {
	    return;
	  }

	  parentNode.insertBefore(newNode, node);
	}

	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<didi.Module>} 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<didi.Module> to the constructor.
	 *
	 * @class djs.Diagram
	 * @memberOf djs
	 * @constructor
	 *
	 * @example
	 *
	 * <caption>Creating a plug-in that logs whenever a shape is added to the canvas.</caption>
	 *
	 * // 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<didi.Module>} [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');
	};

	/**
	 * 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 <prefix:localName> or <localName>, 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<Package>} 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<Package>} 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 fromCharCode = String.fromCharCode;

	var hasOwnProperty$1 = Object.prototype.hasOwnProperty;

	var ENTITY_PATTERN = /&#(\d+);|&#x([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. &nbsp;
	  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 (&amp;) as well as
	 * hex (&#xaaf;) and decimal (&#1231;) 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';

	var NON_WHITESPACE_OUTSIDE_ROOT_NODE = 'non-whitespace outside of root node';

	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<string, ?>=} 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<string, string>}}
	   */
	  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 <name, cb>');
	    }

	    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; // <!XXXXX zzzz="eeee">
	    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<string, string>} nsMap
	   *
	   * @return {Parser}
	   */
	  this['ns'] = function(nsMap) {

	    if (typeof nsMap === 'undefined') {
	      nsMap = {};
	    }

	    if (typeof nsMap !== 'object') {
	      throw error('required args <nsMap={}>');
	    }

	    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 <xml=string>');
	    }

	    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 = xml.substring(j);
	      } else
	      // start errors
	      if (j === 0) {
	        console.log(i - startOfLine);
	        data = xml.substring(j, i);
	      }
	      // other errors
	      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');
	        }

	        if (j < xml.length) {
	          if (xml.substring(j).trim()) {
	            handleWarning(NON_WHITESPACE_OUTSIDE_ROOT_NODE);
	          }
	        }

	        return;
	      }

	      // parse text
	      if (j !== i) {

	        if (nodeStack.length) {
	          if (onText) {
	            onText(xml.substring(j, i), decodeEntities, getContext);

	            if (parseStop) {
	              return;
	            }
	          }
	        } else {
	          if (xml.substring(j, i).trim()) {
	            handleWarning(NON_WHITESPACE_OUTSIDE_ROOT_NODE);

	            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) { // </...
	      if (w === 47) { // </...
	        tagStart = false;
	        tagEnd = true;

	        if (!nodeStack.length) {
	          return handleError('missing open tag');
	        }

	        // verify open <-> 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 || w == 46) {
	            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) {
	        // for QName validation as per http://www.w3.org/TR/REC-xml/#NT-NameChar
	        if (!/^([a-z][\w-.]*:)?[a-z_][\w-.]*$/i.test(id)) {
	          throw new Error('illegal ID <' + 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.handleText = function(text) {

	  var element = this.element,
	      descriptor = getModdleDescriptor(element),
	      bodyProperty = descriptor.bodyProperty;

	  if (!bodyProperty) {
	    throw error$1('unexpected body text <' + text + '>');
	  }

	  BodyHandler.prototype.handleText.call(this, text);
	};

	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;

	    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 = value.split(' ');

	        forEach(values, 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.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 = createStack();

	  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
	      });

	      return true;
	    } else {
	      throw error$1(message);
	    }
	  }

	  function handleWarning(err, getContext) {
	    // just like handling errors in <lax=true> 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, getContext) {

	    try {
	      stack.peek().handleText(text);
	    } catch (err) {
	      handleWarning(err, getContext);
	    }
	  }

	  function handleText(text, getContext) {
	    // strip whitespace only nodes, i.e. before
	    // <!CDATA[ ... ]> sections and in between tags
	    text = text.trim();

	    if (!text) {
	      return;
	    }

	    handleCData(text, getContext);
	  }

	  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, getContext) {
	      handleText(decodeEntities(text), getContext);
	    })
	    .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);
	};


	// helpers //////////////////////////

	function createStack() {
	  var stack = [];

	  Object.defineProperty(stack, 'peek', {
	    value: function() {
	      return this[this.length - 1];
	    }
	  });

	  return stack;
	}

	var XML_PREAMBLE = '<?xml version="1.0" encoding="UTF-8"?>\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<Object>}
	 */
	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 = [];
	        forEach(value, function(v) {
	          values.push(v.id);
	        });
	        // IDREFS is a whitespace-separated list of references.
	        value = values.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: "participantRef",
					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: "participantRef",
					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: [
				"Collaboration",
				"FlowElementsContainer"
			]
		},
		{
			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 '<null>';
	  }

	  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);

	      // 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();
	  }

	  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, ctx) {

	    // 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, ctx));
	  }

	  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')) ; 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  {djs.Diagram} diagram
	 * @param  {ModdleElement<Definitions>} definitions
	 * @param  {ModdleElement<BPMNDiagram>} [bpmnDiagram] the diagram to be rendered
	 * (if not provided, the first one will be rendered)
	 * @param  {Function} done the callback, invoked with (err, [ warning ]) once the import is done
	 */
	function importBpmnDiagram(diagram, definitions, bpmnDiagram, done) {

	  if (isFunction(bpmnDiagram)) {
	    done = bpmnDiagram;
	    bpmnDiagram = null;
	  }

	  var importer,
	      eventBus,
	      translate;

	  var error,
	      warnings = [];

	  /**
	   * Walk the diagram semantically, importing (=drawing)
	   * all elements you encounter.
	   *
	   * @param {ModdleElement<Definitions>} definitions
	   * @param {ModdleElement<BPMNDiagram>} bpmnDiagram
	   */
	  function render(definitions, bpmnDiagram) {

	    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, bpmnDiagram);
	  }

	  try {
	    importer = diagram.get('bpmnImporter');
	    eventBus = diagram.get('eventBus');
	    translate = diagram.get('translate');

	    eventBus.fire('import.render.start', { definitions: definitions });

	    render(definitions, bpmnDiagram);

	    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 isInterrupting(element) {
	  return element && getBusinessObject(element).isInterrupting !== false;
	}

	function isEventSubProcess(element) {
	  return element && !!getBusinessObject(element).triggeredByEvent;
	}

	function hasEventDefinition(element, eventType) {
	  var bo = getBusinessObject(element),
	      hasEventDefinition = false;

	  if (bo.eventDefinitions) {
	    forEach(bo.eventDefinitions, function(event) {
	      if (is$1(event, eventType)) {
	        hasEventDefinition = true;
	      }
	    });
	  }

	  return hasEventDefinition;
	}

	function hasErrorEventDefinition(element) {
	  return hasEventDefinition(element, 'bpmn:ErrorEventDefinition');
	}

	function hasEscalationEventDefinition(element) {
	  return hasEventDefinition(element, 'bpmn:EscalationEventDefinition');
	}

	function hasCompensateEventDefinition(element) {
	  return hasEventDefinition(element, 'bpmn:CompensateEventDefinition');
	}

	function getLabelAttr(semantic) {
	  if (
	    is$1(semantic, 'bpmn:FlowElement') ||
	    is$1(semantic, 'bpmn:Participant') ||
	    is$1(semantic, 'bpmn:Lane') ||
	    is$1(semantic, 'bpmn:SequenceFlow') ||
	    is$1(semantic, 'bpmn:MessageFlow') ||
	    is$1(semantic, 'bpmn:DataInput') ||
	    is$1(semantic, 'bpmn:DataOutput')
	  ) {
	    return 'name';
	  }

	  if (is$1(semantic, 'bpmn:TextAnnotation')) {
	    return 'text';
	  }

	  if (is$1(semantic, 'bpmn:Group')) {
	    return 'categoryValueRef';
	  }
	}

	function getCategoryValue(semantic) {
	  var categoryValueRef = semantic['categoryValueRef'];

	  if (!categoryValueRef) {
	    return '';
	  }


	  return categoryValueRef.value || '';
	}

	function getLabel(element) {
	  var semantic = element.businessObject,
	      attr = getLabelAttr(semantic);

	  if (attr) {

	    if (attr === 'categoryValueRef') {

	      return getCategoryValue(semantic);
	    }

	    return semantic[attr] || '';
	  }
	}


	function setLabel(element, text, isExternal) {
	  var semantic = element.businessObject,
	      attr = getLabelAttr(semantic);

	  if (attr) {

	    if (attr === 'categoryValueRef') {
	      semantic['categoryValueRef'].value = text;
	    } else {
	      semantic[attr] = text;
	    }

	  }

	  return element;
	}

	// 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) {

	  function matches(definition, filter) {
	    return every(filter, 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);
	  });
	}

	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 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 = 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,
	      markerHeight: 20 * scale,
	      orient: 'auto'
	    });

	    var defs = query('defs', canvas._svg);

	    if (!defs) {
	      defs = create('defs');

	      append(canvas._svg, defs);
	    }

	    append(defs, marker);

	    markers[id] = marker;
	  }

	  function colorEscape(str) {
	    return str.replace(/[()\s,#]+/g, '_');
	  }

	  function marker(type, fill, stroke) {
	    var id = type + '-' + colorEscape(fill) + '-' + colorEscape(stroke) + '-' + rendererId;

	    if (!markers[id]) {
	      createMarker(id, type, fill, stroke);
	    }

	    return 'url(#' + id + ')';
	  }

	  function createMarker(id, type, fill, stroke) {

	    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 (event.eventDefinitions && event.eventDefinitions.length>1) {
	      if (event.parallelMultiple) {
	        return renderer('bpmn:ParallelMultipleEventDefinition')(parentGfx, element, isThrowing);
	      }
	      else {
	        return renderer('bpmn:MultipleEventDefinition')(parentGfx, element, isThrowing);
	      }
	    }

	    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: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 box = {
	      width: 90,
	      height: 30,
	      x: element.width / 2 + element.x,
	      y: element.height / 2 + element.y
	    };

	    return renderLabel(parentGfx, getLabel(element), {
	      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);

	      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) {

	      var group = drawRect(parentGfx, element.width, element.height, TASK_BORDER_RADIUS, {
	        strokeWidth: 1,
	        strokeDasharray: '8,3,1,3',
	        fill: 'none',
	        pointerEvents: 'none'
	      });

	      return group;
	    },
	    '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<String>} 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));
	  }

	  if (align.vertical === 'middle') {
	    padding.top = padding.bottom = 0;
	  }

	  var totalHeight = reduce(layouted, function(sum, line, idx) {
	    return sum + (lineHeight || line.height);
	  }, 0) + padding.top + padding.bottom;

	  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 = padding.top;

	  if (align.vertical === 'middle') {
	    y += (box.height - totalHeight) / 2;
	  }

	  // 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;

	var MIN_TEXT_ANNOTATION_HEIGHT = 30;


	function TextRenderer(config) {

	  var defaultStyle = assign({
	    fontFamily: 'Arial, 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.getExternalLabelBounds = 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)
	    };

	  };

	  /**
	   * Get the new bounds of text annotation.
	   *
	   * @param  {Bounds} bounds
	   * @param  {String} text
	   *
	   * @return {Bounds}
	   */
	  this.getTextAnnotationBounds = function(bounds, text) {

	    var layoutedDimensions = textUtil.getDimensions(text, {
	      box: bounds,
	      style: defaultStyle,
	      align: 'left-top',
	      padding: 5
	    });

	    return {
	      x: bounds.x,
	      y: bounds.y,
	      width: bounds.width,
	      height: Math.max(MIN_TEXT_ANNOTATION_HEIGHT, Math.round(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
	   *
	   * <h1>Path definition</h1>
	   * A parameterized path is defined like this:
	   * <pre>
	   * '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]
	   * }
	   * </pre>
	   * <p>It's important to specify a correct <b>height and width</b> 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).</p>
	   * <p>The '<b>heightElements</b>' and '<b>widthElements</b>' 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 <b>heightElement</b>'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.
	   *   <ul>
	   *    <li>The values for the y axis can be accessed in the path string using {e.y0}, {e.y1}, ....</li>
	   *    <li>The values for the x axis can be accessed in the path string using {e.x0}, {e.x1}, ....</li>
	   *   </ul>
	   *   The numbers x0, x1 respectively y0, y1, ... map to the corresponding array index.
	   * </p>
	   */
	  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.
	   * <h1>Use case</h1>
	   * <p>Use case is to scale the content of elements (event, gateways) based
	   * on the element bounding box's size.
	   * </p>
	   * <h1>Why not transform</h1>
	   * <p>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.</p>
	   *
	   * @param {String} pathId The ID of the path.
	   * @param {Object} param <p>
	   *   Example param object scales the path to 60% size of the container (data.width, data.height).
	   *   <pre>
	   *   {
	   *     xScaleFactor: 0.6,
	   *     yScaleFactor:0.6,
	   *     containerWidth: data.width,
	   *     containerHeight: data.height,
	   *     position: {
	   *       mx: 0.46,
	   *       my: 0.2,
	   *     }
	   *   }
	   *   </pre>
	   *   <ul>
	   *    <li>targetpathwidth = xScaleFactor * containerWidth</li>
	   *    <li>targetpathheight = yScaleFactor * containerHeight</li>
	   *    <li>Position is used to set the starting coordinate of the path. M is computed:
	    *    <ul>
	    *      <li>position.x * containerWidth</li>
	    *      <li>position.y * containerHeight</li>
	    *    </ul>
	    *    Center of the container <pre> position: {
	   *       mx: 0.5,
	   *       my: 0.5,
	   *     }</pre>
	   *     Upper left corner of the container
	   *     <pre> position: {
	   *       mx: 0.0,
	   *       my: 0.0,
	   *     }</pre>
	   *    </li>
	   *   </ul>
	   * </p>
	   *
	   */
	  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:DataInput') ||
	         is$1(semantic, 'bpmn:DataOutput') ||
	         is$1(semantic, 'bpmn:SequenceFlow') ||
	         is$1(semantic, 'bpmn:MessageFlow') ||
	         is$1(semantic, 'bpmn:Group');
	}

	/**
	 * Returns true if the given element has an external label
	 *
	 * @param {djs.model.shape} element
	 * @return {Boolean} true if has label
	 */
	function hasExternalLabel(element) {
	  return isLabel(element.label);
	}

	/**
	 * Get the position for sequence flow labels
	 *
	 * @param  {Array<Point>} 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<Point>} 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 if (is$1(element, 'bpmn:Group')) {
	    return {
	      x: element.x + element.width / 2,
	      y: element.y + DEFAULT_LABEL_SIZE$1.height / 2
	    };
	  } 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,
	      bounds,
	      di = semantic.di,
	      label = di.label;

	  if (label && label.bounds) {
	    bounds = label.bounds;

	    size = {
	      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 = DEFAULT_LABEL_SIZE$1;
	  }

	  return assign({
	    x: mid.x - size.width / 2,
	    y: mid.y - size.height / 2
	  }, size);
	}

	function isLabel(element) {
	  return element && !!element.labelTarget;
	}

	/**
	 * Computes the distance between two points
	 *
	 * @param  {Point}  p
	 * @param  {Point}  q
	 *
	 * @return {Number}  distance
	 */
	function pointDistance(a, b) {
	  if (!a || !b) {
	    return -1;
	  }

	  return Math.sqrt(
	    Math.pow(a.x - b.x, 2) +
	    Math.pow(a.y - b.y, 2)
	  );
	}


	/**
	 * Returns true if the point r is on the line between p and q
	 *
	 * @param  {Point}  p
	 * @param  {Point}  q
	 * @param  {Point}  r
	 * @param  {Number} [accuracy=5] accuracy for points on line check (lower is better)
	 *
	 * @return {Boolean}
	 */
	function pointsOnLine(p, q, r, accuracy) {

	  if (typeof accuracy === 'undefined') {
	    accuracy = 5;
	  }

	  if (!p || !q || !r) {
	    return false;
	  }

	  var val = (q.x - p.x) * (r.y - p.y) - (q.y - p.y) * (r.x - p.x),
	      dist = pointDistance(p, q);

	  // @see http://stackoverflow.com/a/907491/412190
	  return Math.abs(val / dist) <= accuracy;
	}


	var ALIGNED_THRESHOLD = 2;

	/**
	 * Returns whether two points are in a horizontal or vertical line.
	 *
	 * @param {Point} a
	 * @param {Point} b
	 *
	 * @return {String|Boolean} returns false if the points are not
	 *                          aligned or 'h|v' if they are aligned
	 *                          horizontally / vertically.
	 */
	function pointsAligned(a, b) {
	  if (Math.abs(a.x - b.x) <= ALIGNED_THRESHOLD) {
	    return 'v';
	  }

	  if (Math.abs(a.y - b.y) <= ALIGNED_THRESHOLD) {
	    return 'h';
	  }

	  return false;
	}


	/**
	 * Returns true if the point p is inside the rectangle rect
	 *
	 * @param  {Point}  p
	 * @param  {Rect}   rect
	 * @param  {Number} tolerance
	 *
	 * @return {Boolean}
	 */
	function pointInRect(p, rect, tolerance) {
	  tolerance = tolerance || 0;

	  return p.x > rect.x - tolerance &&
	         p.y > rect.y - tolerance &&
	         p.x < rect.x + rect.width + tolerance &&
	         p.y < rect.y + rect.height + tolerance;
	}

	/**
	 * Returns a point in the middle of points p and q
	 *
	 * @param  {Point}  p
	 * @param  {Point}  q
	 *
	 * @return {Point} middle point
	 */
	function getMidPoint(p, q) {
	  return {
	    x: Math.round(p.x + ((q.x - p.x) / 2.0)),
	    y: Math.round(p.y + ((q.y - p.y) / 2.0))
	  };
	}

	/**
	 * 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 has$1 = 'hasOwnProperty',
	    p2s = /,?([a-z]),?/gi,
	    toFloat = parseFloat,
	    math = Math,
	    PI = math.PI,
	    mmin = math.min,
	    mmax = math.max,
	    pow = math.pow,
	    abs = math.abs,
	    pathCommand = /([a-z])[\s,]*((-?\d*\.?\d*(?:e[-+]?\d+)?[\s]*,?[\s]*)+)/ig,
	    pathValues = /(-?\d*\.?\d*(?:e[-+]?\\d+)?)[\s]*,?[\s]*/ig;

	function is$2(o, type) {
	  type = String.prototype.toLowerCase.call(type);

	  if (type == 'finite') {
	    return isFinite(o);
	  }

	  if (type == 'array' && (o instanceof Array || Array.isArray && Array.isArray(o))) {
	    return true;
	  }

	  return (type == 'null' && o === null) ||
	         (type == typeof o && o !== null) ||
	         (type == 'object' && o === Object(o)) ||
	         Object.prototype.toString.call(o).slice(8, -1).toLowerCase() == type;
	}

	function clone$1(obj) {

	  if (typeof obj == 'function' || Object(obj) !== obj) {
	    return obj;
	  }

	  var res = new obj.constructor;

	  for (var key in obj) if (obj[has$1](key)) {
	    res[key] = clone$1(obj[key]);
	  }

	  return res;
	}

	function repush(array, item) {
	  for (var i = 0, ii = array.length; i < ii; i++) if (array[i] === item) {
	    return array.push(array.splice(i, 1)[0]);
	  }
	}

	function cacher(f, scope, postprocessor) {

	  function newf() {

	    var arg = Array.prototype.slice.call(arguments, 0),
	        args = arg.join('\u2400'),
	        cache = newf.cache = newf.cache || {},
	        count = newf.count = newf.count || [];

	    if (cache[has$1](args)) {
	      repush(count, args);
	      return postprocessor ? postprocessor(cache[args]) : cache[args];
	    }

	    count.length >= 1e3 && delete cache[count.shift()];
	    count.push(args);
	    cache[args] = f.apply(scope, arg);

	    return postprocessor ? postprocessor(cache[args]) : cache[args];
	  }
	  return newf;
	}

	function parsePathString(pathString) {

	  if (!pathString) {
	    return null;
	  }

	  var pth = paths(pathString);

	  if (pth.arr) {
	    return clone$1(pth.arr);
	  }

	  var paramCounts = { a: 7, c: 6, o: 2, h: 1, l: 2, m: 2, r: 4, q: 4, s: 4, t: 2, v: 1, u: 3, z: 0 },
	      data = [];

	  if (is$2(pathString, 'array') && is$2(pathString[0], 'array')) { // rough assumption
	    data = clone$1(pathString);
	  }

	  if (!data.length) {

	    String(pathString).replace(pathCommand, function(a, b, c) {
	      var params = [],
	          name = b.toLowerCase();

	      c.replace(pathValues, function(a, b) {
	        b && params.push(+b);
	      });

	      if (name == 'm' && params.length > 2) {
	        data.push([b].concat(params.splice(0, 2)));
	        name = 'l';
	        b = b == 'm' ? 'l' : 'L';
	      }

	      if (name == 'o' && params.length == 1) {
	        data.push([b, params[0]]);
	      }

	      if (name == 'r') {
	        data.push([b].concat(params));
	      } else while (params.length >= paramCounts[name]) {
	        data.push([b].concat(params.splice(0, paramCounts[name])));
	        if (!paramCounts[name]) {
	          break;
	        }
	      }
	    });
	  }

	  data.toString = paths.toString;
	  pth.arr = clone$1(data);

	  return data;
	}

	function paths(ps) {
	  var p = paths.ps = paths.ps || {};

	  if (p[ps]) {
	    p[ps].sleep = 100;
	  } else {
	    p[ps] = {
	      sleep: 100
	    };
	  }

	  setTimeout(function() {
	    for (var key in p) if (p[has$1](key) && key != ps) {
	      p[key].sleep--;
	      !p[key].sleep && delete p[key];
	    }
	  });

	  return p[ps];
	}

	function box(x, y, width, height) {
	  if (x == null) {
	    x = y = width = height = 0;
	  }

	  if (y == null) {
	    y = x.y;
	    width = x.width;
	    height = x.height;
	    x = x.x;
	  }

	  return {
	    x: x,
	    y: y,
	    width: width,
	    w: width,
	    height: height,
	    h: height,
	    x2: x + width,
	    y2: y + height,
	    cx: x + width / 2,
	    cy: y + height / 2,
	    r1: math.min(width, height) / 2,
	    r2: math.max(width, height) / 2,
	    r0: math.sqrt(width * width + height * height) / 2,
	    path: rectPath(x, y, width, height),
	    vb: [x, y, width, height].join(' ')
	  };
	}

	function pathToString() {
	  return this.join(',').replace(p2s, '$1');
	}

	function pathClone(pathArray) {
	  var res = clone$1(pathArray);
	  res.toString = pathToString;
	  return res;
	}

	function findDotsAtSegment(p1x, p1y, c1x, c1y, c2x, c2y, p2x, p2y, t) {
	  var t1 = 1 - t,
	      t13 = pow(t1, 3),
	      t12 = pow(t1, 2),
	      t2 = t * t,
	      t3 = t2 * t,
	      x = t13 * p1x + t12 * 3 * t * c1x + t1 * 3 * t * t * c2x + t3 * p2x,
	      y = t13 * p1y + t12 * 3 * t * c1y + t1 * 3 * t * t * c2y + t3 * p2y,
	      mx = p1x + 2 * t * (c1x - p1x) + t2 * (c2x - 2 * c1x + p1x),
	      my = p1y + 2 * t * (c1y - p1y) + t2 * (c2y - 2 * c1y + p1y),
	      nx = c1x + 2 * t * (c2x - c1x) + t2 * (p2x - 2 * c2x + c1x),
	      ny = c1y + 2 * t * (c2y - c1y) + t2 * (p2y - 2 * c2y + c1y),
	      ax = t1 * p1x + t * c1x,
	      ay = t1 * p1y + t * c1y,
	      cx = t1 * c2x + t * p2x,
	      cy = t1 * c2y + t * p2y,
	      alpha = (90 - math.atan2(mx - nx, my - ny) * 180 / PI);

	  return {
	    x: x,
	    y: y,
	    m: { x: mx, y: my },
	    n: { x: nx, y: ny },
	    start: { x: ax, y: ay },
	    end: { x: cx, y: cy },
	    alpha: alpha
	  };
	}

	function bezierBBox(p1x, p1y, c1x, c1y, c2x, c2y, p2x, p2y) {

	  if (!is$2(p1x, 'array')) {
	    p1x = [p1x, p1y, c1x, c1y, c2x, c2y, p2x, p2y];
	  }

	  var bbox = curveBBox.apply(null, p1x);

	  return box(
	    bbox.min.x,
	    bbox.min.y,
	    bbox.max.x - bbox.min.x,
	    bbox.max.y - bbox.min.y
	  );
	}

	function isPointInsideBBox(bbox, x, y) {
	  return x >= bbox.x &&
	    x <= bbox.x + bbox.width &&
	    y >= bbox.y &&
	    y <= bbox.y + bbox.height;
	}

	function isBBoxIntersect(bbox1, bbox2) {
	  bbox1 = box(bbox1);
	  bbox2 = box(bbox2);
	  return isPointInsideBBox(bbox2, bbox1.x, bbox1.y)
	    || isPointInsideBBox(bbox2, bbox1.x2, bbox1.y)
	    || isPointInsideBBox(bbox2, bbox1.x, bbox1.y2)
	    || isPointInsideBBox(bbox2, bbox1.x2, bbox1.y2)
	    || isPointInsideBBox(bbox1, bbox2.x, bbox2.y)
	    || isPointInsideBBox(bbox1, bbox2.x2, bbox2.y)
	    || isPointInsideBBox(bbox1, bbox2.x, bbox2.y2)
	    || isPointInsideBBox(bbox1, bbox2.x2, bbox2.y2)
	    || (bbox1.x < bbox2.x2 && bbox1.x > bbox2.x
	        || bbox2.x < bbox1.x2 && bbox2.x > bbox1.x)
	    && (bbox1.y < bbox2.y2 && bbox1.y > bbox2.y
	        || bbox2.y < bbox1.y2 && bbox2.y > bbox1.y);
	}

	function base3(t, p1, p2, p3, p4) {
	  var t1 = -3 * p1 + 9 * p2 - 9 * p3 + 3 * p4,
	      t2 = t * t1 + 6 * p1 - 12 * p2 + 6 * p3;
	  return t * t2 - 3 * p1 + 3 * p2;
	}

	function bezlen(x1, y1, x2, y2, x3, y3, x4, y4, z) {

	  if (z == null) {
	    z = 1;
	  }

	  z = z > 1 ? 1 : z < 0 ? 0 : z;

	  var z2 = z / 2,
	      n = 12,
	      Tvalues = [-.1252,.1252,-.3678,.3678,-.5873,.5873,-.7699,.7699,-.9041,.9041,-.9816,.9816],
	      Cvalues = [0.2491,0.2491,0.2335,0.2335,0.2032,0.2032,0.1601,0.1601,0.1069,0.1069,0.0472,0.0472],
	      sum = 0;

	  for (var i = 0; i < n; i++) {
	    var ct = z2 * Tvalues[i] + z2,
	        xbase = base3(ct, x1, x2, x3, x4),
	        ybase = base3(ct, y1, y2, y3, y4),
	        comb = xbase * xbase + ybase * ybase;

	    sum += Cvalues[i] * math.sqrt(comb);
	  }

	  return z2 * sum;
	}


	function intersectLines(x1, y1, x2, y2, x3, y3, x4, y4) {

	  if (
	    mmax(x1, x2) < mmin(x3, x4) ||
	      mmin(x1, x2) > mmax(x3, x4) ||
	      mmax(y1, y2) < mmin(y3, y4) ||
	      mmin(y1, y2) > mmax(y3, y4)
	  ) {
	    return;
	  }

	  var nx = (x1 * y2 - y1 * x2) * (x3 - x4) - (x1 - x2) * (x3 * y4 - y3 * x4),
	      ny = (x1 * y2 - y1 * x2) * (y3 - y4) - (y1 - y2) * (x3 * y4 - y3 * x4),
	      denominator = (x1 - x2) * (y3 - y4) - (y1 - y2) * (x3 - x4);

	  if (!denominator) {
	    return;
	  }

	  var px = nx / denominator,
	      py = ny / denominator,
	      px2 = +px.toFixed(2),
	      py2 = +py.toFixed(2);

	  if (
	    px2 < +mmin(x1, x2).toFixed(2) ||
	      px2 > +mmax(x1, x2).toFixed(2) ||
	      px2 < +mmin(x3, x4).toFixed(2) ||
	      px2 > +mmax(x3, x4).toFixed(2) ||
	      py2 < +mmin(y1, y2).toFixed(2) ||
	      py2 > +mmax(y1, y2).toFixed(2) ||
	      py2 < +mmin(y3, y4).toFixed(2) ||
	      py2 > +mmax(y3, y4).toFixed(2)
	  ) {
	    return;
	  }

	  return { x: px, y: py };
	}

	function findBezierIntersections(bez1, bez2, justCount) {
	  var bbox1 = bezierBBox(bez1),
	      bbox2 = bezierBBox(bez2);

	  if (!isBBoxIntersect(bbox1, bbox2)) {
	    return justCount ? 0 : [];
	  }

	  var l1 = bezlen.apply(0, bez1),
	      l2 = bezlen.apply(0, bez2),
	      n1 = ~~(l1 / 5),
	      n2 = ~~(l2 / 5),
	      dots1 = [],
	      dots2 = [],
	      xy = {},
	      res = justCount ? 0 : [];

	  for (var i = 0; i < n1 + 1; i++) {
	    var p = findDotsAtSegment.apply(0, bez1.concat(i / n1));
	    dots1.push({ x: p.x, y: p.y, t: i / n1 });
	  }

	  for (i = 0; i < n2 + 1; i++) {
	    p = findDotsAtSegment.apply(0, bez2.concat(i / n2));
	    dots2.push({ x: p.x, y: p.y, t: i / n2 });
	  }

	  for (i = 0; i < n1; i++) {

	    for (var j = 0; j < n2; j++) {
	      var di = dots1[i],
	          di1 = dots1[i + 1],
	          dj = dots2[j],
	          dj1 = dots2[j + 1],
	          ci = abs(di1.x - di.x) < .01 ? 'y' : 'x',
	          cj = abs(dj1.x - dj.x) < .01 ? 'y' : 'x',
	          is = intersectLines(di.x, di.y, di1.x, di1.y, dj.x, dj.y, dj1.x, dj1.y);

	      if (is) {

	        if (xy[is.x.toFixed(0)] == is.y.toFixed(0)) {
	          continue;
	        }

	        xy[is.x.toFixed(0)] = is.y.toFixed(0);

	        var t1 = di.t + abs((is[ci] - di[ci]) / (di1[ci] - di[ci])) * (di1.t - di.t),
	            t2 = dj.t + abs((is[cj] - dj[cj]) / (dj1[cj] - dj[cj])) * (dj1.t - dj.t);

	        if (t1 >= 0 && t1 <= 1 && t2 >= 0 && t2 <= 1) {

	          if (justCount) {
	            res++;
	          } else {
	            res.push({
	              x: is.x,
	              y: is.y,
	              t1: t1,
	              t2: t2
	            });
	          }
	        }
	      }
	    }
	  }

	  return res;
	}


	/**
	 * Find or counts the intersections between two SVG paths.
	 *
	 * Returns a number in counting mode and a list of intersections otherwise.
	 *
	 * A single intersection entry contains the intersection coordinates (x, y)
	 * as well as additional information regarding the intersecting segments
	 * on each path (segment1, segment2) and the relative location of the
	 * intersection on these segments (t1, t2).
	 *
	 * The path may be an SVG path string or a list of path components
	 * such as `[ [ 'M', 0, 10 ], [ 'L', 20, 0 ] ]`.
	 *
	 * @example
	 *
	 * var intersections = findPathIntersections(
	 *   'M0,0L100,100',
	 *   [ [ 'M', 0, 100 ], [ 'L', 100, 0 ] ]
	 * );
	 *
	 * // intersections = [
	 * //   { x: 50, y: 50, segment1: 1, segment2: 1, t1: 0.5, t2: 0.5 }
	 * //
	 *
	 * @param {String|Array<PathDef>} path1
	 * @param {String|Array<PathDef>} path2
	 * @param {Boolean} [justCount=false]
	 *
	 * @return {Array<Intersection>|Number}
	 */
	function findPathIntersections(path1, path2, justCount) {
	  path1 = pathToCurve(path1);
	  path2 = pathToCurve(path2);

	  var x1, y1, x2, y2, x1m, y1m, x2m, y2m, bez1, bez2,
	      res = justCount ? 0 : [];

	  for (var i = 0, ii = path1.length; i < ii; i++) {
	    var pi = path1[i];

	    if (pi[0] == 'M') {
	      x1 = x1m = pi[1];
	      y1 = y1m = pi[2];
	    } else {

	      if (pi[0] == 'C') {
	        bez1 = [x1, y1].concat(pi.slice(1));
	        x1 = bez1[6];
	        y1 = bez1[7];
	      } else {
	        bez1 = [x1, y1, x1, y1, x1m, y1m, x1m, y1m];
	        x1 = x1m;
	        y1 = y1m;
	      }

	      for (var j = 0, jj = path2.length; j < jj; j++) {
	        var pj = path2[j];

	        if (pj[0] == 'M') {
	          x2 = x2m = pj[1];
	          y2 = y2m = pj[2];
	        } else {

	          if (pj[0] == 'C') {
	            bez2 = [x2, y2].concat(pj.slice(1));
	            x2 = bez2[6];
	            y2 = bez2[7];
	          } else {
	            bez2 = [x2, y2, x2, y2, x2m, y2m, x2m, y2m];
	            x2 = x2m;
	            y2 = y2m;
	          }

	          var intr = findBezierIntersections(bez1, bez2, justCount);

	          if (justCount) {
	            res += intr;
	          } else {

	            for (var k = 0, kk = intr.length; k < kk; k++) {
	              intr[k].segment1 = i;
	              intr[k].segment2 = j;
	              intr[k].bez1 = bez1;
	              intr[k].bez2 = bez2;
	            }

	            res = res.concat(intr);
	          }
	        }
	      }
	    }
	  }

	  return res;
	}


	function rectPath(x, y, w, h, r) {
	  if (r) {
	    return [
	      ['M', +x + (+r), y],
	      ['l', w - r * 2, 0],
	      ['a', r, r, 0, 0, 1, r, r],
	      ['l', 0, h - r * 2],
	      ['a', r, r, 0, 0, 1, -r, r],
	      ['l', r * 2 - w, 0],
	      ['a', r, r, 0, 0, 1, -r, -r],
	      ['l', 0, r * 2 - h],
	      ['a', r, r, 0, 0, 1, r, -r],
	      ['z']
	    ];
	  }

	  var res = [['M', x, y], ['l', w, 0], ['l', 0, h], ['l', -w, 0], ['z']];
	  res.toString = pathToString;

	  return res;
	}

	function ellipsePath(x, y, rx, ry, a) {
	  if (a == null && ry == null) {
	    ry = rx;
	  }

	  x = +x;
	  y = +y;
	  rx = +rx;
	  ry = +ry;

	  if (a != null) {
	    var rad = Math.PI / 180,
	        x1 = x + rx * Math.cos(-ry * rad),
	        x2 = x + rx * Math.cos(-a * rad),
	        y1 = y + rx * Math.sin(-ry * rad),
	        y2 = y + rx * Math.sin(-a * rad),
	        res = [['M', x1, y1], ['A', rx, rx, 0, +(a - ry > 180), 0, x2, y2]];
	  } else {
	    res = [
	      ['M', x, y],
	      ['m', 0, -ry],
	      ['a', rx, ry, 0, 1, 1, 0, 2 * ry],
	      ['a', rx, ry, 0, 1, 1, 0, -2 * ry],
	      ['z']
	    ];
	  }

	  res.toString = pathToString;

	  return res;
	}


	function pathToAbsolute(pathArray) {
	  var pth = paths(pathArray);

	  if (pth.abs) {
	    return pathClone(pth.abs);
	  }

	  if (!is$2(pathArray, 'array') || !is$2(pathArray && pathArray[0], 'array')) { // rough assumption
	    pathArray = parsePathString(pathArray);
	  }

	  if (!pathArray || !pathArray.length) {
	    return [['M', 0, 0]];
	  }

	  var res = [],
	      x = 0,
	      y = 0,
	      mx = 0,
	      my = 0,
	      start = 0,
	      pa0;

	  if (pathArray[0][0] == 'M') {
	    x = +pathArray[0][1];
	    y = +pathArray[0][2];
	    mx = x;
	    my = y;
	    start++;
	    res[0] = ['M', x, y];
	  }

	  var crz = pathArray.length == 3 &&
	      pathArray[0][0] == 'M' &&
	      pathArray[1][0].toUpperCase() == 'R' &&
	      pathArray[2][0].toUpperCase() == 'Z';

	  for (var r, pa, i = start, ii = pathArray.length; i < ii; i++) {
	    res.push(r = []);
	    pa = pathArray[i];
	    pa0 = pa[0];

	    if (pa0 != pa0.toUpperCase()) {
	      r[0] = pa0.toUpperCase();

	      switch (r[0]) {
	      case 'A':
	        r[1] = pa[1];
	        r[2] = pa[2];
	        r[3] = pa[3];
	        r[4] = pa[4];
	        r[5] = pa[5];
	        r[6] = +pa[6] + x;
	        r[7] = +pa[7] + y;
	        break;
	      case 'V':
	        r[1] = +pa[1] + y;
	        break;
	      case 'H':
	        r[1] = +pa[1] + x;
	        break;
	      case 'R':
	        var dots = [x, y].concat(pa.slice(1));

	        for (var j = 2, jj = dots.length; j < jj; j++) {
	          dots[j] = +dots[j] + x;
	          dots[++j] = +dots[j] + y;
	        }

	        res.pop();
	        res = res.concat(catmulRomToBezier(dots, crz));
	        break;
	      case 'O':
	        res.pop();
	        dots = ellipsePath(x, y, pa[1], pa[2]);
	        dots.push(dots[0]);
	        res = res.concat(dots);
	        break;
	      case 'U':
	        res.pop();
	        res = res.concat(ellipsePath(x, y, pa[1], pa[2], pa[3]));
	        r = ['U'].concat(res[res.length - 1].slice(-2));
	        break;
	      case 'M':
	        mx = +pa[1] + x;
	        my = +pa[2] + y;
	      default:

	        for (j = 1, jj = pa.length; j < jj; j++) {
	          r[j] = +pa[j] + ((j % 2) ? x : y);
	        }
	      }
	    } else if (pa0 == 'R') {
	      dots = [x, y].concat(pa.slice(1));
	      res.pop();
	      res = res.concat(catmulRomToBezier(dots, crz));
	      r = ['R'].concat(pa.slice(-2));
	    } else if (pa0 == 'O') {
	      res.pop();
	      dots = ellipsePath(x, y, pa[1], pa[2]);
	      dots.push(dots[0]);
	      res = res.concat(dots);
	    } else if (pa0 == 'U') {
	      res.pop();
	      res = res.concat(ellipsePath(x, y, pa[1], pa[2], pa[3]));
	      r = ['U'].concat(res[res.length - 1].slice(-2));
	    } else {

	      for (var k = 0, kk = pa.length; k < kk; k++) {
	        r[k] = pa[k];
	      }
	    }
	    pa0 = pa0.toUpperCase();

	    if (pa0 != 'O') {
	      switch (r[0]) {
	      case 'Z':
	        x = +mx;
	        y = +my;
	        break;
	      case 'H':
	        x = r[1];
	        break;
	      case 'V':
	        y = r[1];
	        break;
	      case 'M':
	        mx = r[r.length - 2];
	        my = r[r.length - 1];
	      default:
	        x = r[r.length - 2];
	        y = r[r.length - 1];
	      }
	    }
	  }

	  res.toString = pathToString;
	  pth.abs = pathClone(res);

	  return res;
	}

	function lineToCurve(x1, y1, x2, y2) {
	  return [
	    x1, y1, x2,
	    y2, x2, y2
	  ];
	}

	function qubicToCurve(x1, y1, ax, ay, x2, y2) {
	  var _13 = 1 / 3,
	      _23 = 2 / 3;

	  return [
	    _13 * x1 + _23 * ax,
	    _13 * y1 + _23 * ay,
	    _13 * x2 + _23 * ax,
	    _13 * y2 + _23 * ay,
	    x2,
	    y2
	  ];
	}

	function arcToCurve(x1, y1, rx, ry, angle, large_arc_flag, sweep_flag, x2, y2, recursive) {

	  // for more information of where this math came from visit:
	  // http://www.w3.org/TR/SVG11/implnote.html#ArcImplementationNotes
	  var _120 = PI * 120 / 180,
	      rad = PI / 180 * (+angle || 0),
	      res = [],
	      xy,
	      rotate = cacher(function(x, y, rad) {
	        var X = x * math.cos(rad) - y * math.sin(rad),
	            Y = x * math.sin(rad) + y * math.cos(rad);

	        return { x: X, y: Y };
	      });

	  if (!recursive) {
	    xy = rotate(x1, y1, -rad);
	    x1 = xy.x;
	    y1 = xy.y;
	    xy = rotate(x2, y2, -rad);
	    x2 = xy.x;
	    y2 = xy.y;

	    var x = (x1 - x2) / 2,
	        y = (y1 - y2) / 2;

	    var h = (x * x) / (rx * rx) + (y * y) / (ry * ry);

	    if (h > 1) {
	      h = math.sqrt(h);
	      rx = h * rx;
	      ry = h * ry;
	    }

	    var rx2 = rx * rx,
	        ry2 = ry * ry,
	        k = (large_arc_flag == sweep_flag ? -1 : 1) *
	            math.sqrt(abs((rx2 * ry2 - rx2 * y * y - ry2 * x * x) / (rx2 * y * y + ry2 * x * x))),
	        cx = k * rx * y / ry + (x1 + x2) / 2,
	        cy = k * -ry * x / rx + (y1 + y2) / 2,
	        f1 = math.asin(((y1 - cy) / ry).toFixed(9)),
	        f2 = math.asin(((y2 - cy) / ry).toFixed(9));

	    f1 = x1 < cx ? PI - f1 : f1;
	    f2 = x2 < cx ? PI - f2 : f2;
	    f1 < 0 && (f1 = PI * 2 + f1);
	    f2 < 0 && (f2 = PI * 2 + f2);

	    if (sweep_flag && f1 > f2) {
	      f1 = f1 - PI * 2;
	    }
	    if (!sweep_flag && f2 > f1) {
	      f2 = f2 - PI * 2;
	    }
	  } else {
	    f1 = recursive[0];
	    f2 = recursive[1];
	    cx = recursive[2];
	    cy = recursive[3];
	  }

	  var df = f2 - f1;

	  if (abs(df) > _120) {
	    var f2old = f2,
	        x2old = x2,
	        y2old = y2;

	    f2 = f1 + _120 * (sweep_flag && f2 > f1 ? 1 : -1);
	    x2 = cx + rx * math.cos(f2);
	    y2 = cy + ry * math.sin(f2);
	    res = arcToCurve(x2, y2, rx, ry, angle, 0, sweep_flag, x2old, y2old, [f2, f2old, cx, cy]);
	  }

	  df = f2 - f1;

	  var c1 = math.cos(f1),
	      s1 = math.sin(f1),
	      c2 = math.cos(f2),
	      s2 = math.sin(f2),
	      t = math.tan(df / 4),
	      hx = 4 / 3 * rx * t,
	      hy = 4 / 3 * ry * t,
	      m1 = [x1, y1],
	      m2 = [x1 + hx * s1, y1 - hy * c1],
	      m3 = [x2 + hx * s2, y2 - hy * c2],
	      m4 = [x2, y2];

	  m2[0] = 2 * m1[0] - m2[0];
	  m2[1] = 2 * m1[1] - m2[1];

	  if (recursive) {
	    return [m2, m3, m4].concat(res);
	  } else {
	    res = [m2, m3, m4].concat(res).join().split(',');
	    var newres = [];

	    for (var i = 0, ii = res.length; i < ii; i++) {
	      newres[i] = i % 2 ? rotate(res[i - 1], res[i], rad).y : rotate(res[i], res[i + 1], rad).x;
	    }

	    return newres;
	  }
	}

	// http://schepers.cc/getting-to-the-point
	function catmulRomToBezier(crp, z) {
	  var d = [];

	  for (var i = 0, iLen = crp.length; iLen - 2 * !z > i; i += 2) {
	    var p = [
	      { x: +crp[i - 2], y: +crp[i - 1] },
	      { x: +crp[i], y: +crp[i + 1] },
	      { x: +crp[i + 2], y: +crp[i + 3] },
	      { x: +crp[i + 4], y: +crp[i + 5] }
	    ];

	    if (z) {

	      if (!i) {
	        p[0] = { x: +crp[iLen - 2], y: +crp[iLen - 1] };
	      } else if (iLen - 4 == i) {
	        p[3] = { x: +crp[0], y: +crp[1] };
	      } else if (iLen - 2 == i) {
	        p[2] = { x: +crp[0], y: +crp[1] };
	        p[3] = { x: +crp[2], y: +crp[3] };
	      }

	    } else {

	      if (iLen - 4 == i) {
	        p[3] = p[2];
	      } else if (!i) {
	        p[0] = { x: +crp[i], y: +crp[i + 1] };
	      }

	    }

	    d.push(['C',
	      (-p[0].x + 6 * p[1].x + p[2].x) / 6,
	      (-p[0].y + 6 * p[1].y + p[2].y) / 6,
	      (p[1].x + 6 * p[2].x - p[3].x) / 6,
	      (p[1].y + 6*p[2].y - p[3].y) / 6,
	      p[2].x,
	      p[2].y
	    ]);
	  }

	  return d;
	}

	// Returns bounding box of cubic bezier curve.
	// Source: http://blog.hackers-cafe.net/2009/06/how-to-calculate-bezier-curves-bounding.html
	// Original version: NISHIO Hirokazu
	// Modifications: https://github.com/timo22345
	function curveBBox(x0, y0, x1, y1, x2, y2, x3, y3) {
	  var tvalues = [],
	      bounds = [[], []],
	      a, b, c, t, t1, t2, b2ac, sqrtb2ac;

	  for (var i = 0; i < 2; ++i) {

	    if (i == 0) {
	      b = 6 * x0 - 12 * x1 + 6 * x2;
	      a = -3 * x0 + 9 * x1 - 9 * x2 + 3 * x3;
	      c = 3 * x1 - 3 * x0;
	    } else {
	      b = 6 * y0 - 12 * y1 + 6 * y2;
	      a = -3 * y0 + 9 * y1 - 9 * y2 + 3 * y3;
	      c = 3 * y1 - 3 * y0;
	    }

	    if (abs(a) < 1e-12) {

	      if (abs(b) < 1e-12) {
	        continue;
	      }

	      t = -c / b;

	      if (0 < t && t < 1) {
	        tvalues.push(t);
	      }

	      continue;
	    }

	    b2ac = b * b - 4 * c * a;
	    sqrtb2ac = math.sqrt(b2ac);

	    if (b2ac < 0) {
	      continue;
	    }

	    t1 = (-b + sqrtb2ac) / (2 * a);

	    if (0 < t1 && t1 < 1) {
	      tvalues.push(t1);
	    }

	    t2 = (-b - sqrtb2ac) / (2 * a);

	    if (0 < t2 && t2 < 1) {
	      tvalues.push(t2);
	    }
	  }

	  var j = tvalues.length,
	      jlen = j,
	      mt;

	  while (j--) {
	    t = tvalues[j];
	    mt = 1 - t;
	    bounds[0][j] = (mt * mt * mt * x0) + (3 * mt * mt * t * x1) + (3 * mt * t * t * x2) + (t * t * t * x3);
	    bounds[1][j] = (mt * mt * mt * y0) + (3 * mt * mt * t * y1) + (3 * mt * t * t * y2) + (t * t * t * y3);
	  }

	  bounds[0][jlen] = x0;
	  bounds[1][jlen] = y0;
	  bounds[0][jlen + 1] = x3;
	  bounds[1][jlen + 1] = y3;
	  bounds[0].length = bounds[1].length = jlen + 2;

	  return {
	    min: { x: mmin.apply(0, bounds[0]), y: mmin.apply(0, bounds[1]) },
	    max: { x: mmax.apply(0, bounds[0]), y: mmax.apply(0, bounds[1]) }
	  };
	}

	function pathToCurve(path, path2) {
	  var pth = !path2 && paths(path);

	  if (!path2 && pth.curve) {
	    return pathClone(pth.curve);
	  }

	  var p = pathToAbsolute(path),
	      p2 = path2 && pathToAbsolute(path2),
	      attrs = { x: 0, y: 0, bx: 0, by: 0, X: 0, Y: 0, qx: null, qy: null },
	      attrs2 = { x: 0, y: 0, bx: 0, by: 0, X: 0, Y: 0, qx: null, qy: null },
	      processPath = function(path, d, pcom) {
	        var nx, ny;

	        if (!path) {
	          return ['C', d.x, d.y, d.x, d.y, d.x, d.y];
	        }

	        !(path[0] in { T: 1, Q: 1 }) && (d.qx = d.qy = null);

	        switch (path[0]) {
	        case 'M':
	          d.X = path[1];
	          d.Y = path[2];
	          break;
	        case 'A':
	          path = ['C'].concat(arcToCurve.apply(0, [d.x, d.y].concat(path.slice(1))));
	          break;
	        case 'S':
	          if (pcom == 'C' || pcom == 'S') {
	            // In 'S' case we have to take into account, if the previous command is C/S.
	            nx = d.x * 2 - d.bx;
	            // And reflect the previous
	            ny = d.y * 2 - d.by;
	            // command's control point relative to the current point.
	          }
	          else {
	            // or some else or nothing
	            nx = d.x;
	            ny = d.y;
	          }
	          path = ['C', nx, ny].concat(path.slice(1));
	          break;
	        case 'T':
	          if (pcom == 'Q' || pcom == 'T') {
	            // In 'T' case we have to take into account, if the previous command is Q/T.
	            d.qx = d.x * 2 - d.qx;
	            // And make a reflection similar
	            d.qy = d.y * 2 - d.qy;
	            // to case 'S'.
	          }
	          else {
	            // or something else or nothing
	            d.qx = d.x;
	            d.qy = d.y;
	          }
	          path = ['C'].concat(qubicToCurve(d.x, d.y, d.qx, d.qy, path[1], path[2]));
	          break;
	        case 'Q':
	          d.qx = path[1];
	          d.qy = path[2];
	          path = ['C'].concat(qubicToCurve(d.x, d.y, path[1], path[2], path[3], path[4]));
	          break;
	        case 'L':
	          path = ['C'].concat(lineToCurve(d.x, d.y, path[1], path[2]));
	          break;
	        case 'H':
	          path = ['C'].concat(lineToCurve(d.x, d.y, path[1], d.y));
	          break;
	        case 'V':
	          path = ['C'].concat(lineToCurve(d.x, d.y, d.x, path[1]));
	          break;
	        case 'Z':
	          path = ['C'].concat(lineToCurve(d.x, d.y, d.X, d.Y));
	          break;
	        }

	        return path;
	      },

	      fixArc = function(pp, i) {

	        if (pp[i].length > 7) {
	          pp[i].shift();
	          var pi = pp[i];

	          while (pi.length) {
	            pcoms1[i] = 'A'; // if created multiple C:s, their original seg is saved
	            p2 && (pcoms2[i] = 'A'); // the same as above
	            pp.splice(i++, 0, ['C'].concat(pi.splice(0, 6)));
	          }

	          pp.splice(i, 1);
	          ii = mmax(p.length, p2 && p2.length || 0);
	        }
	      },

	      fixM = function(path1, path2, a1, a2, i) {

	        if (path1 && path2 && path1[i][0] == 'M' && path2[i][0] != 'M') {
	          path2.splice(i, 0, ['M', a2.x, a2.y]);
	          a1.bx = 0;
	          a1.by = 0;
	          a1.x = path1[i][1];
	          a1.y = path1[i][2];
	          ii = mmax(p.length, p2 && p2.length || 0);
	        }
	      },

	      pcoms1 = [], // path commands of original path p
	      pcoms2 = [], // path commands of original path p2
	      pfirst = '', // temporary holder for original path command
	      pcom = ''; // holder for previous path command of original path

	  for (var i = 0, ii = mmax(p.length, p2 && p2.length || 0); i < ii; i++) {
	    p[i] && (pfirst = p[i][0]); // save current path command

	    if (pfirst != 'C') // C is not saved yet, because it may be result of conversion
	    {
	      pcoms1[i] = pfirst; // Save current path command
	      i && (pcom = pcoms1[i - 1]); // Get previous path command pcom
	    }
	    p[i] = processPath(p[i], attrs, pcom); // Previous path command is inputted to processPath

	    if (pcoms1[i] != 'A' && pfirst == 'C') pcoms1[i] = 'C'; // A is the only command
	    // which may produce multiple C:s
	    // so we have to make sure that C is also C in original path

	    fixArc(p, i); // fixArc adds also the right amount of A:s to pcoms1

	    if (p2) { // the same procedures is done to p2
	      p2[i] && (pfirst = p2[i][0]);

	      if (pfirst != 'C') {
	        pcoms2[i] = pfirst;
	        i && (pcom = pcoms2[i - 1]);
	      }

	      p2[i] = processPath(p2[i], attrs2, pcom);

	      if (pcoms2[i] != 'A' && pfirst == 'C') {
	        pcoms2[i] = 'C';
	      }

	      fixArc(p2, i);
	    }

	    fixM(p, p2, attrs, attrs2, i);
	    fixM(p2, p, attrs2, attrs, i);

	    var seg = p[i],
	        seg2 = p2 && p2[i],
	        seglen = seg.length,
	        seg2len = p2 && seg2.length;

	    attrs.x = seg[seglen - 2];
	    attrs.y = seg[seglen - 1];
	    attrs.bx = toFloat(seg[seglen - 4]) || attrs.x;
	    attrs.by = toFloat(seg[seglen - 3]) || attrs.y;
	    attrs2.bx = p2 && (toFloat(seg2[seg2len - 4]) || attrs2.x);
	    attrs2.by = p2 && (toFloat(seg2[seg2len - 3]) || attrs2.y);
	    attrs2.x = p2 && seg2[seg2len - 2];
	    attrs2.y = p2 && seg2[seg2len - 1];
	  }

	  if (!p2) {
	    pth.curve = pathClone(p);
	  }

	  return p2 ? [p, p2] : p;
	}

	var intersect = findPathIntersections;

	function roundBounds(bounds) {
	  return {
	    x: Math.round(bounds.x),
	    y: Math.round(bounds.y),
	    width: Math.round(bounds.width),
	    height: Math.round(bounds.height)
	  };
	}


	function roundPoint(point) {

	  return {
	    x: Math.round(point.x),
	    y: Math.round(point.y)
	  };
	}


	/**
	 * Convert the given bounds to a { top, left, bottom, right } descriptor.
	 *
	 * @param {Bounds|Point} bounds
	 *
	 * @return {Object}
	 */
	function asTRBL(bounds) {
	  return {
	    top: bounds.y,
	    right: bounds.x + (bounds.width || 0),
	    bottom: bounds.y + (bounds.height || 0),
	    left: bounds.x
	  };
	}


	/**
	 * Convert a { top, left, bottom, right } to an objects bounds.
	 *
	 * @param {Object} trbl
	 *
	 * @return {Bounds}
	 */
	function asBounds(trbl) {
	  return {
	    x: trbl.left,
	    y: trbl.top,
	    width: trbl.right - trbl.left,
	    height: trbl.bottom - trbl.top
	  };
	}


	/**
	 * 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
	  });
	}


	// orientation utils //////////////////////

	/**
	 * Get orientation of the given rectangle with respect to
	 * the reference rectangle.
	 *
	 * A padding (positive or negative) may be passed to influence
	 * horizontal / vertical orientation and intersection.
	 *
	 * @param {Bounds} rect
	 * @param {Bounds} reference
	 * @param {Point|Number} padding
	 *
	 * @return {String} the orientation; one of top, top-left, left, ..., bottom, right or intersect.
	 */
	function getOrientation(rect, reference, padding) {

	  padding = padding || 0;

	  // make sure we can use an object, too
	  // for individual { x, y } padding
	  if (!isObject(padding)) {
	    padding = { x: padding, y: padding };
	  }


	  var rectOrientation = asTRBL(rect),
	      referenceOrientation = asTRBL(reference);

	  var top = rectOrientation.bottom + padding.y <= referenceOrientation.top,
	      right = rectOrientation.left - padding.x >= referenceOrientation.right,
	      bottom = rectOrientation.top - padding.y >= referenceOrientation.bottom,
	      left = rectOrientation.right + padding.x <= referenceOrientation.left;

	  var vertical = top ? 'top' : (bottom ? 'bottom' : null),
	      horizontal = left ? 'left' : (right ? 'right' : null);

	  if (horizontal && vertical) {
	    return vertical + '-' + horizontal;
	  } else {
	    return horizontal || vertical || 'intersect';
	  }
	}


	// intersection utils //////////////////////

	/**
	 * Get intersection between an element and a line path.
	 *
	 * @param {PathDef} elementPath
	 * @param {PathDef} linePath
	 * @param {Boolean} cropStart crop from start or end
	 *
	 * @return {Point}
	 */
	function getElementLineIntersection(elementPath, linePath, cropStart) {

	  var intersections = getIntersections(elementPath, linePath);

	  // recognize intersections
	  // only one -> choose
	  // two close together -> choose first
	  // two or more distinct -> pull out appropriate one
	  // none -> ok (fallback to point itself)
	  if (intersections.length === 1) {
	    return roundPoint(intersections[0]);
	  } else if (intersections.length === 2 && pointDistance(intersections[0], intersections[1]) < 1) {
	    return roundPoint(intersections[0]);
	  } else if (intersections.length > 1) {

	    // sort by intersections based on connection segment +
	    // distance from start
	    intersections = sortBy(intersections, function(i) {
	      var distance = Math.floor(i.t2 * 100) || 1;

	      distance = 100 - distance;

	      distance = (distance < 10 ? '0' : '') + distance;

	      // create a sort string that makes sure we sort
	      // line segment ASC + line segment position DESC (for cropStart)
	      // line segment ASC + line segment position ASC (for cropEnd)
	      return i.segment2 + '#' + distance;
	    });

	    return roundPoint(intersections[cropStart ? 0 : intersections.length - 1]);
	  }

	  return null;
	}


	function getIntersections(a, b) {
	  return intersect(a, b);
	}


	function filterRedundantWaypoints(waypoints) {

	  // alter copy of waypoints, not original
	  waypoints = waypoints.slice();

	  var idx = 0,
	      point,
	      previousPoint,
	      nextPoint;

	  while (waypoints[idx]) {
	    point = waypoints[idx];
	    previousPoint = waypoints[idx - 1];
	    nextPoint = waypoints[idx + 1];

	    if (pointDistance(point, nextPoint) === 0 ||
	        pointsOnLine(previousPoint, nextPoint, point)) {

	      // remove point, if overlapping with {nextPoint}
	      // or on line with {previousPoint} -> {point} -> {nextPoint}
	      waypoints.splice(idx, 1);
	    } else {
	      idx++;
	    }
	  }

	  return waypoints;
	}

	function elementData(semantic, attrs) {
	  return assign({
	    id: semantic.id,
	    type: semantic.$type,
	    businessObject: semantic
	  }, attrs);
	}

	function getWaypoints(bo, source, target) {

	  var waypoints = bo.di.waypoint;

	  if (!waypoints || waypoints.length < 2) {
	    return [ getMid(source), getMid(target) ];
	  }

	  return waypoints.map(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: getWaypoints(semantic, source, target)
	    }));

	    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) && getLabel(element)) {
      alert(11);
	    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 = getLabel(element);

	  if (text) {
	    // get corrected bounds from actual layouted text
	    bounds = this._textRenderer.getExternalLabelBounds(bounds, text);
	  }

	  label = this._elementFactory.createLabel(elementData(semantic, {
	    id: semantic.id + '_label',
	    labelTarget: element,
	    type: 'label',
	    hidden: element.hidden || !getLabel(element),
	    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 __stopPropagation(event) {
	  if (!event || typeof event.stopPropagation !== 'function') {
	    return;
	  }

	  event.stopPropagation();
	}


	function getOriginal(event) {
	  return event.originalEvent || event.srcEvent;
	}


	function stopPropagation(event, immediate) {
	  __stopPropagation(event);
	  __stopPropagation(getOriginal(event));
	}


	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 hasSecondaryModifier(event) {
	  var originalEvent = getOriginal(event) || event;

	  return isPrimaryButton(event) && originalEvent.shiftKey;
	}

	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 = ignoredFilters[localEventName] || isPrimaryButton;

	    // only react on left mouse button interactions
	    // except for interaction events that are enabled
	    // for secundary mouse button
	    return !filter(event);
	  }

	  var bindings = {
	    mouseover: 'element.hover',
	    mouseout: 'element.out',
	    click: 'element.click',
	    dblclick: 'element.dblclick',
	    mousedown: 'element.mousedown',
	    mousemove: 'element.mousemove',
	    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);
	    }

	    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);
	    }

	    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 <code>hover</code> and <code>selected</code> 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 = new IdGenerator('ov');

	var LOW_PRIORITY$2 = 500;


	/**
	 * 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: '<div style="width: 10px; background: fuchsia; color: white;">0</div>'
	 * });
	 *
	 * // or add via shape id
	 *
	 * overlays.add('some-element-id', {
	 *   position: {
	 *     top: -5,
	 *     left: -5
	 *   }
	 *   html: '<div style="width: 10px; background: fuchsia; color: white;">0</div>'
	 * });
	 *
	 * // or add with optional type
	 *
	 * overlays.add(someShape, 'badge', {
	 *   position: {
	 *     top: -5,
	 *     left: -5
	 *   }
	 *   html: '<div style="width: 10px; background: fuchsia; color: white;">0</div>'
	 * });
	 *
	 *
	 * // 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;

	  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<Object>} 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) {

	  var overlays = this.get(filter) || [];

	  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('<div class="djs-overlays" style="position: absolute" />');

	  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('<div class="djs-overlay" data-overlay-id="' + id + '" style="position: absolute">');

	  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 (
	      (isDefined(minZoom) && minZoom > viewbox.scale) ||
	      (isDefined(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 (isDefined(minScale) && viewbox.scale < minScale) {
	      scale = (1 / viewbox.scale || 1) * minScale;
	    }

	    if (isDefined(maxScale) && viewbox.scale > maxScale) {
	      scale = (1 / viewbox.scale || 1) * maxScale;
	    }
	  }

	  if (isDefined(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);
	};



	// helpers /////////////////////////////

	function createRoot(parentNode) {
	  var root = domify(
	    '<div class="djs-overlay-container" style="position: absolute; width: 0; height: 0;" />'
	  );

	  parentNode.insertBefore(root, parentNode.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;
	  });
	}

	var OverlaysModule = {
	  __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 = '<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 960 960"><path fill="#fff" d="M960 60v839c0 33-27 61-60 61H60c-33 0-60-27-60-60V60C0 27 27 0 60 0h839c34 0 61 27 61 60z"/><path fill="#52b415" d="M217 548a205 205 0 0 0-144 58 202 202 0 0 0-4 286 202 202 0 0 0 285 3 200 200 0 0 0 48-219 203 203 0 0 0-185-128zM752 6a206 206 0 0 0-192 285 206 206 0 0 0 269 111 207 207 0 0 0 111-260A204 204 0 0 0 752 6zM62 0A62 62 0 0 0 0 62v398l60 46a259 259 0 0 1 89-36c5-28 10-57 14-85l99 2 12 85a246 246 0 0 1 88 38l70-52 69 71-52 68c17 30 29 58 35 90l86 14-2 100-86 12a240 240 0 0 1-38 89l43 58h413c37 0 60-27 60-61V407a220 220 0 0 1-44 40l21 85-93 39-45-76a258 258 0 0 1-98 1l-45 76-94-39 22-85a298 298 0 0 1-70-69l-86 22-38-94 76-45a258 258 0 0 1-1-98l-76-45 40-94 85 22a271 271 0 0 1 41-47z"/></svg>';

	var BPMNIO_LOGO_URL = 'data:image/svg+xml,' + encodeURIComponent(BPMNIO_LOGO_SVG);

	var BPMNIO_IMG = '<img width="52" height="52" src="' + BPMNIO_LOGO_URL + '" />';

	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 =
	  '<div class="bjs-powered-by-lightbox" style="' + LIGHTBOX_STYLES + '">' +
	    '<div class="backdrop" style="' + BACKDROP_STYLES + '"></div>' +
	    '<div class="notice" style="' + NOTICE_STYLES + '">' +
	      '<a href="http://bpmn.io" target="_blank" style="float: left; margin-right: 10px">' +
	        BPMNIO_IMG +
	      '</a>' +
	      'Web-based tooling for BPMN, DMN and CMMN diagrams ' +
	      'powered by <a href="http://bpmn.io" target="_blank">bpmn.io</a>.' +
	    '</div>' +
	  '</div>';


	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 <project-logo></project-logo> 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' : '');
	}


	/**
	 * Find BPMNDiagram in definitions by ID
	 *
	 * @param {ModdleElement<Definitions>} definitions
	 * @param {String} diagramId
	 *
	 * @return {ModdleElement<BPMNDiagram>|null}
	 */
	function findBPMNDiagram(definitions, diagramId) {
	  if (!diagramId) {
	    return null;
	  }

	  return find(definitions.diagrams, function(element) {
	    return element.id === diagramId;
	  }) || null;
	}

	/**
	 * 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<didi.Module>} [options.modules] a list of modules to override the default modules
	 * @param {Array<didi.Module>} [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);

	  /* <project-logo> */

	  addProjectLogo(this._container);

	  /* </project-logo> */

	  this._init(this._container, this._moddle, options);
	}

	inherits_browser(Viewer, Diagram);


	/**
	 * 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 {ModdleElement<BPMNDiagram>|String} [bpmnDiagram] BPMN diagram or id of diagram to render (if not provided, the first one will be rendered)
	 * @param {Function} [done] invoked with (err, warnings=[])
	 */
	Viewer.prototype.importXML = function(xml, bpmnDiagram, done) {

	  if (isFunction(bpmnDiagram)) {
	    done = bpmnDiagram;
	    bpmnDiagram = null;
	  }

	  // 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, bpmnDiagram, function(err, importWarnings) {
	      var allWarnings = [].concat(parseWarnings, importWarnings || []);

	      self._emit('import.done', { error: err, warnings: allWarnings });

	      done(err, allWarnings);
	    });
	  });
	};

	/**
	 * Import parsed definitions 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.render.start (graphical import start)
	 *   * import.render.complete (graphical import finished)
	 *
	 * You can use these events to hook into the life-cycle.
	 *
	 * @param {ModdleElement<Definitions>} definitions parsed BPMN 2.0 definitions
	 * @param {ModdleElement<BPMNDiagram>|String} [bpmnDiagram] BPMN diagram or id of diagram to render (if not provided, the first one will be rendered)
	 * @param {Function} [done] invoked with (err, warnings=[])
	 */
	Viewer.prototype.importDefinitions = function(definitions, bpmnDiagram, done) {

	  if (isFunction(bpmnDiagram)) {
	    done = bpmnDiagram;
	    bpmnDiagram = null;
	  }

	  // done is optional
	  done = done || function() {};

	  this._setDefinitions(definitions);

	  return this.open(bpmnDiagram, done);
	};

	/**
	 * Open diagram of previously imported XML.
	 *
	 * Once finished the viewer reports back the result to the
	 * provided callback function with (err, warnings).
	 *
	 * ## Life-Cycle Events
	 *
	 * During switch the viewer will fire life-cycle events:
	 *
	 *   * import.render.start (graphical import start)
	 *   * import.render.complete (graphical import finished)
	 *
	 * You can use these events to hook into the life-cycle.
	 *
	 * @param {String|ModdleElement<BPMNDiagram>} [bpmnDiagramOrId] id or the diagram to open
	 * @param {Function} [done] invoked with (err, warnings=[])
	 */
	Viewer.prototype.open = function(bpmnDiagramOrId, done) {

	  if (isFunction(bpmnDiagramOrId)) {
	    done = bpmnDiagramOrId;
	    bpmnDiagramOrId = null;
	  }

	  var definitions = this._definitions;
	  var bpmnDiagram = bpmnDiagramOrId;

	  // done is optional
	  done = done || function() {};

	  if (!definitions) {
	    return done(new Error('no XML imported'));
	  }

	  if (typeof bpmnDiagramOrId === 'string') {
	    bpmnDiagram = findBPMNDiagram(definitions, bpmnDiagramOrId);

	    if (!bpmnDiagram) {
	      return done(new Error('BPMNDiagram <' + bpmnDiagramOrId + '> not found'));
	    }
	  }

	  // clear existing rendered diagram
	  // catch synchronous exceptions during #clear()
	  try {
	    this.clear();
	  } catch (error) {
	    return done(error);
	  }

	  // perform graphical import
	  return importBpmnDiagram(this, definitions, bpmnDiagram, done);
	};

	/**
	 * Export the currently displayed BPMN 2.0 diagram as
	 * a BPMN 2.0 XML document.
	 *
	 * ## Life-Cycle Events
	 *
	 * During XML saving the viewer will fire life-cycle events:
	 *
	 *   * saveXML.start (before serialization)
	 *   * saveXML.serialized (after xml generation)
	 *   * saveXML.done (everything done)
	 *
	 * You can use these events to hook into the life-cycle.
	 *
	 * @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 self = this;

	  var definitions = this._definitions;

	  if (!definitions) {
	    return done(new Error('no definitions loaded'));
	  }

	  // allow to fiddle around with definitions
	  definitions = this._emit('saveXML.start', {
	    definitions: definitions
	  }) || definitions;

	  this._moddle.toXML(definitions, options, function(err, xml) {

	    try {
	      xml = self._emit('saveXML.serialized', {
	        error: err,
	        xml: xml
	      }) || xml;

	      self._emit('saveXML.done', {
	        error: err,
	        xml: xml
	      });
	    } catch (e) {
	      console.error('error in saveXML life-cycle listener', e);
	    }

	    done(err, xml);
	  });
	};

	/**
	 * Export the currently displayed BPMN 2.0 diagram as
	 * an SVG image.
	 *
	 * ## Life-Cycle Events
	 *
	 * During SVG saving the viewer will fire life-cycle events:
	 *
	 *   * saveSVG.start (before serialization)
	 *   * saveSVG.done (everything done)
	 *
	 * You can use these events to hook into the life-cycle.
	 *
	 * @param {Object} [options]
	 * @param {Function} done invoked with (err, svgStr)
	 */
	Viewer.prototype.saveSVG = function(options, done) {

	  if (!done) {
	    done = options;
	    options = {};
	  }

	  this._emit('saveSVG.start');

	  var svg, err;

	  try {
	    var canvas = this.get('canvas');

	    var contentNode = canvas.getDefaultLayer(),
	        defsNode = query('defs', canvas._svg);

	    var contents = innerSVG(contentNode),
	        defs = defsNode ? '<defs>' + innerSVG(defsNode) + '</defs>' : '';

	    var bbox = contentNode.getBBox();

	    svg =
	      '<?xml version="1.0" encoding="utf-8"?>\n' +
	      '<!-- created with bpmn-js / http://bpmn.io -->\n' +
	      '<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">\n' +
	      '<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" ' +
	           'width="' + bbox.width + '" height="' + bbox.height + '" ' +
	           'viewBox="' + bbox.x + ' ' + bbox.y + ' ' + bbox.width + ' ' + bbox.height + '" version="1.1">' +
	        defs + contents +
	      '</svg>';
	  } catch (e) {
	    err = e;
	  }

	  this._emit('saveSVG.done', {
	    error: err,
	    svg: svg
	  });

	  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
	 */


	Viewer.prototype._setDefinitions = function(definitions) {
	  this._definitions = definitions;
	};

	Viewer.prototype.getModules = function() {
	  return this._modules;
	};

	/**
	 * 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.clear = function() {

	  // remove businessObject#di binding
	  //
	  // this is necessary, as we establish the bindings
	  // in the BpmnTreeWalker (and assume none are given
	  // on reimport)
	  this.get('elementRegistry').forEach(function(element) {
	    var bo = element.businessObject;

	    if (bo && bo.di) {
	      delete bo.di;
	    }
	  });

	  // remove drawn elements
	  Diagram.prototype.clear.call(this);
	};

	/**
	 * Destroy the viewer instance and remove all its
	 * remainders from the document tree.
	 */
	Viewer.prototype.destroy = function() {

	  // diagram destroy
	  Diagram.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, priority, callback, target) {
	  return this.get('eventBus').on(event, priority, callback, target);
	};

	/**
	 * De-register an event listener
	 *
	 * @param {String} event
	 * @param {Function} callback
	 */
	Viewer.prototype.off = function(event, callback) {
	  this.get('eventBus').off(event, 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
	  Diagram.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) {
	  return this.get('eventBus').fire(type, event);
	};

	Viewer.prototype._createContainer = function(options) {

	  var container = domify('<div class="bjs-container"></div>');

	  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,
	  OverlaysModule
	];

	// 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 =
	    '<a href="http://bpmn.io" ' +
	       'target="_blank" ' +
	       'class="bjs-powered-by" ' +
	       'title="Powered by bpmn.io" ' +
	       'style="position: absolute; bottom: 15px; right: 15px; z-index: 100">' +
	      img +
	    '</a>';

	  var linkElement = domify(linkMarkup);

	  container.appendChild(linkElement);

	  componentEvent.bind(linkElement, 'click', function(event) {
	    open();

	    event.preventDefault();
	  });
	}

	/* </project-logo> */

	/**
	 * Returns true if event was triggered with any modifier
	 * @param {KeyboardEvent} event
	 */
	function hasModifier(event) {
	  return (event.ctrlKey || event.metaKey || event.shiftKey || event.altKey);
	}

	/**
	 * @param {KeyboardEvent} event
	 */
	function isCmd(event) {

	  // ensure we don't react to AltGr
	  // (mapped to CTRL + ALT)
	  if (event.altKey) {
	    return false;
	  }

	  return event.ctrlKey || event.metaKey;
	}

	/**
	 * Checks if key pressed is one of provided keys.
	 *
	 * @param {String|String[]} keys
	 * @param {KeyboardEvent} event
	 */
	function isKey(keys, event) {
	  keys = isArray(keys) ? keys : [ keys ];

	  return keys.indexOf(event.key) > -1;
	}

	/**
	 * @param {KeyboardEvent} event
	 */
	function isShift(event) {
	  return event.shiftKey;
	}

	var KEYDOWN_EVENT = 'keyboard.keydown',
	    KEYUP_EVENT = 'keyboard.keyup';

	var DEFAULT_PRIORITY$1 = 1000;


	/**
	 * A keyboard abstraction that may be activated and
	 * deactivated by users at will, consuming key events
	 * and triggering diagram actions.
	 *
	 * For keys pressed down, keyboard fires `keyboard.keydown` event.
	 * The event context contains one field which is `KeyboardEvent` event.
	 *
	 * The implementation fires the following key events that allow
	 * other components to hook into key handling:
	 *
	 *  - keyboard.bind
	 *  - keyboard.unbind
	 *  - keyboard.init
	 *  - keyboard.destroy
	 *
	 * All events contain one field which is node.
	 *
	 * A default binding for the keyboard may be specified via the
	 * `keyboard.bindTo` configuration option.
	 *
	 * @param {Config} config
	 * @param {EventBus} eventBus
	 */
	function Keyboard(config, eventBus) {
	  var self = this;

	  this._config = config || {};
	  this._eventBus = eventBus;

	  this._keydownHandler = this._keydownHandler.bind(this);
	  this._keyupHandler = this._keyupHandler.bind(this);

	  // properly clean dom registrations
	  eventBus.on('diagram.destroy', function() {
	    self._fire('destroy');

	    self.unbind();
	  });

	  eventBus.on('diagram.init', function() {
	    self._fire('init');
	  });

	  eventBus.on('attach', function() {
	    if (config && config.bindTo) {
	      self.bind(config.bindTo);
	    }
	  });

	  eventBus.on('detach', function() {
	    self.unbind();
	  });
	}

	Keyboard.$inject = [
	  'config.keyboard',
	  'eventBus'
	];

	Keyboard.prototype._keydownHandler = function(event) {
	  this._keyHandler(event, KEYDOWN_EVENT);
	};

	Keyboard.prototype._keyupHandler = function(event) {
	  this._keyHandler(event, KEYUP_EVENT);
	};

	Keyboard.prototype._keyHandler = function(event, type) {
	  var target = event.target,
	      eventBusResult;

	  if (isInput(target)) {
	    return;
	  }

	  var context = {
	    keyEvent: event
	  };

	  eventBusResult = this._eventBus.fire(type || KEYDOWN_EVENT, context);

	  if (eventBusResult) {
	    event.preventDefault();
	  }
	};

	Keyboard.prototype.bind = function(node) {

	  // make sure that the keyboard is only bound once to the DOM
	  this.unbind();

	  this._node = node;

	  // bind key events
	  componentEvent.bind(node, 'keydown', this._keydownHandler, true);
	  componentEvent.bind(node, 'keyup', this._keyupHandler, true);

	  this._fire('bind');
	};

	Keyboard.prototype.getBinding = function() {
	  return this._node;
	};

	Keyboard.prototype.unbind = function() {
	  var node = this._node;

	  if (node) {
	    this._fire('unbind');

	    // unbind key events
	    componentEvent.unbind(node, 'keydown', this._keydownHandler, true);
	    componentEvent.unbind(node, 'keyup', this._keyupHandler, true);
	  }

	  this._node = null;
	};

	Keyboard.prototype._fire = function(event) {
	  this._eventBus.fire('keyboard.' + event, { node: this._node });
	};

	/**
	 * Add a listener function that is notified with `KeyboardEvent` whenever
	 * the keyboard is bound and the user presses a key. If no priority is
	 * provided, the default value of 1000 is used.
	 *
	 * @param {Number} [priority]
	 * @param {Function} listener
	 * @param {string} type
	 */
	Keyboard.prototype.addListener = function(priority, listener, type) {
	  if (isFunction(priority)) {
	    type = listener;
	    listener = priority;
	    priority = DEFAULT_PRIORITY$1;
	  }

	  this._eventBus.on(type || KEYDOWN_EVENT, priority, listener);
	};

	Keyboard.prototype.removeListener = function(listener, type) {
	  this._eventBus.off(type || KEYDOWN_EVENT, listener);
	};

	Keyboard.prototype.hasModifier = hasModifier;
	Keyboard.prototype.isCmd = isCmd;
	Keyboard.prototype.isShift = isShift;
	Keyboard.prototype.isKey = isKey;



	// helpers ///////

	function isInput(target) {
	  return target && (matchesSelector$1(target, 'input, textarea') || target.contentEditable === 'true');
	}

	var LOW_PRIORITY$3 = 500;


	/**
	 * Adds default keyboard bindings.
	 *
	 * This does not pull in any features will bind only actions that
	 * have previously been registered against the editorActions component.
	 *
	 * @param {EventBus} eventBus
	 * @param {Keyboard} keyboard
	 */
	function KeyboardBindings(eventBus, keyboard) {

	  var self = this;

	  eventBus.on('editorActions.init', LOW_PRIORITY$3, function(event) {

	    var editorActions = event.editorActions;

	    self.registerBindings(keyboard, editorActions);
	  });
	}

	KeyboardBindings.$inject = [
	  'eventBus',
	  'keyboard'
	];


	/**
	 * Register available keyboard bindings.
	 *
	 * @param {Keyboard} keyboard
	 * @param {EditorActions} editorActions
	 */
	KeyboardBindings.prototype.registerBindings = function(keyboard, editorActions) {

	  /**
	   * Add keyboard binding if respective editor action
	   * is registered.
	   *
	   * @param {String} action name
	   * @param {Function} fn that implements the key binding
	   */
	  function addListener(action, fn) {

	    if (editorActions.isRegistered(action)) {
	      keyboard.addListener(fn);
	    }
	  }


	  // undo
	  // (CTRL|CMD) + Z
	  addListener('undo', function(context) {

	    var event = context.keyEvent;

	    if (isCmd(event) && !isShift(event) && isKey(['z', 'Z'], event)) {
	      editorActions.trigger('undo');

	      return true;
	    }
	  });

	  // redo
	  // CTRL + Y
	  // CMD + SHIFT + Z
	  addListener('redo', function(context) {

	    var event = context.keyEvent;

	    if (isCmd(event) && (isKey(['y', 'Y'], event) || (isKey(['z', 'Z'], event) && isShift(event)))) {
	      editorActions.trigger('redo');

	      return true;
	    }
	  });

	  // copy
	  // CTRL/CMD + C
	  addListener('copy', function(context) {

	    var event = context.keyEvent;

	    if (isCmd(event) && isKey(['c', 'C'], event)) {
	      editorActions.trigger('copy');

	      return true;
	    }
	  });

	  // paste
	  // CTRL/CMD + V
	  addListener('paste', function(context) {

	    var event = context.keyEvent;

	    if (isCmd(event) && isKey(['v', 'V'], event)) {
	      editorActions.trigger('paste');

	      return true;
	    }
	  });

	  // zoom in one step
	  // CTRL/CMD + +
	  addListener('stepZoom', function(context) {

	    var event = context.keyEvent;

	    if (isKey([ '+', 'Add' ], event) && isCmd(event)) {
	      editorActions.trigger('stepZoom', { value: 1 });

	      return true;
	    }
	  });

	  // zoom out one step
	  // CTRL + -
	  addListener('stepZoom', function(context) {

	    var event = context.keyEvent;

	    if (isKey([ '-', 'Subtract' ], event) && isCmd(event)) {
	      editorActions.trigger('stepZoom', { value: -1 });

	      return true;
	    }
	  });

	  // zoom to the default level
	  // CTRL + 0
	  addListener('zoom', function(context) {

	    var event = context.keyEvent;

	    if (isKey('0', event) && isCmd(event)) {
	      editorActions.trigger('zoom', { value: 1 });

	      return true;
	    }
	  });

	  // delete selected element
	  // DEL
	  addListener('removeSelection', function(context) {

	    var event = context.keyEvent;

	    if (isKey([ 'Delete', 'Del' ], event)) {
	      editorActions.trigger('removeSelection');

	      return true;
	    }
	  });
	};

	var KeyboardModule = {
	  __init__: [ 'keyboard', 'keyboardBindings' ],
	  keyboard: [ 'type', Keyboard ],
	  keyboardBindings: [ 'type', KeyboardBindings ]
	};

	var DEFAULT_CONFIG = {
	  moveSpeed: 50,
	  moveSpeedAccelerated: 200
	};


	/**
	 * A feature that allows users to move the canvas using the keyboard.
	 *
	 * @param {Object} config
	 * @param {Number} [config.moveSpeed=50]
	 * @param {Number} [config.moveSpeedAccelerated=200]
	 * @param {Keyboard} keyboard
	 * @param {Canvas} canvas
	 */
	function KeyboardMove(
	    config,
	    keyboard,
	    canvas
	) {

	  var self = this;

	  this._config = assign({}, DEFAULT_CONFIG, config || {});

	  keyboard.addListener(arrowsListener);


	  function arrowsListener(context) {

	    var event = context.keyEvent,
	        config = self._config;

	    if (!keyboard.isCmd(event)) {
	      return;
	    }

	    if (keyboard.isKey([
	      'ArrowLeft', 'Left',
	      'ArrowUp', 'Up',
	      'ArrowDown', 'Down',
	      'ArrowRight', 'Right'
	    ], event)) {

	      var speed = (
	        keyboard.isShift(event) ?
	          config.moveSpeedAccelerated :
	          config.moveSpeed
	      );

	      var direction;

	      switch (event.key) {
	      case 'ArrowLeft':
	      case 'Left':
	        direction = 'left';
	        break;
	      case 'ArrowUp':
	      case 'Up':
	        direction = 'up';
	        break;
	      case 'ArrowRight':
	      case 'Right':
	        direction = 'right';
	        break;
	      case 'ArrowDown':
	      case 'Down':
	        direction = 'down';
	        break;
	      }

	      self.moveCanvas({
	        speed: speed,
	        direction: direction
	      });

	      return true;
	    }
	  }

	  this.moveCanvas = function(opts) {

	    var dx = 0,
	        dy = 0,
	        speed = opts.speed;

	    var actualSpeed = speed / Math.min(Math.sqrt(canvas.viewbox().scale), 1);

	    switch (opts.direction) {
	    case 'left': // Left
	      dx = actualSpeed;
	      break;
	    case 'up': // Up
	      dy = actualSpeed;
	      break;
	    case 'right': // Right
	      dx = -actualSpeed;
	      break;
	    case 'down': // Down
	      dy = -actualSpeed;
	      break;
	    }

	    canvas.scroll({
	      dx: dx,
	      dy: dy
	    });
	  };

	}


	KeyboardMove.$inject = [
	  'config.keyboardMove',
	  'keyboard',
	  'canvas'
	];

	var KeyboardMoveModule = {
	  __depends__: [
	    KeyboardModule
	  ],
	  __init__: [ 'keyboardMove' ],
	  keyboardMove: [ 'type', KeyboardMove ]
	};

	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 center(bounds) {
	  return {
	    x: bounds.x + (bounds.width / 2),
	    y: bounds.y + (bounds.height / 2)
	  };
	}


	function delta(a, b) {
	  return {
	    x: a.x - b.x,
	    y: a.y - b.y
	  };
	}

	var THRESHOLD = 15;


	/**
	 * Move the canvas via mouse.
	 *
	 * @param {EventBus} eventBus
	 * @param {Canvas} canvas
	 */
	function MoveCanvas(eventBus, canvas) {

	  var context;


	  // 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);
	  });


	  function handleMove(event) {

	    var start = context.start,
	        position = toPoint(event),
	        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.preventDefault();
	  }


	  function handleEnd(event) {
	    componentEvent.unbind(document, 'mousemove', handleMove);
	    componentEvent.unbind(document, 'mouseup', handleEnd);

	    context = null;

	    unset();
	  }

	  function handleStart(event) {
	    // event is already handled by '.djs-draggable'
	    if (closest(event.target, '.djs-draggable')) {
	      return;
	    }


	    // reject non-left left mouse button or modifier key
	    if (event.button || event.ctrlKey || event.shiftKey || event.altKey) {
	      return;
	    }

	    context = {
	      start: toPoint(event)
	    };

	    componentEvent.bind(document, 'mousemove', handleMove);
	    componentEvent.bind(document, 'mouseup', handleEnd);

	    // we've handled the event
	    return true;
	  }
	}


	MoveCanvas.$inject = [
	  'eventBus',
	  'canvas'
	];



	// helpers ///////

	function length(point) {
	  return Math.sqrt(Math.pow(point.x, 2) + Math.pow(point.y, 2));
	}

	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
	 * @param {number} range.min
	 * @param {number} range.max
	 */
	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) {
	  this._canvas.scroll(delta);
	};


	ZoomScroll.prototype.reset = function reset() {
	  this._canvas.zoom('fit-viewport');
	};

	/**
	 * Zoom depending on delta.
	 *
	 * @param {number} delta
	 * @param {Object} position
	 */
	ZoomScroll.prototype.zoom = function zoom(delta, position) {

	  // zoom with half the step size of stepZoom
	  var stepSize = getStepSize(RANGE, NUM_STEPS * 2);

	  // add until threshold reached
	  this._totalDelta += delta;

	  if (Math.abs(this._totalDelta) > DELTA_THRESHOLD) {
	    this._zoom(delta, position, stepSize);

	    // reset
	    this._totalDelta = 0;
	  }
	};


	ZoomScroll.prototype._handleWheel = function handleWheel(event) {
	  // event is already handled by '.djs-scrollable'
	  if (closest(event.target, '.djs-scrollable', true)) {
	    return;
	  }

	  var element = this._container;

	  event.preventDefault();

	  // pinch to zoom is mapped to wheel + ctrlKey = true
	  // in modern browsers (!)

	  var isZoom = event.ctrlKey;

	  var isHorizontalScroll = event.shiftKey;

	  var factor = -1 * this._scale,
	      delta;

	  if (isZoom) {
	    factor *= event.deltaMode === 0 ? 0.020 : 0.32;
	  } else {
	    factor *= event.deltaMode === 0 ? 1.0 : 16.0;
	  }

	  if (isZoom) {
	    var elementRect = element.getBoundingClientRect();

	    var offset = {
	      x: event.clientX - elementRect.left,
	      y: event.clientY - elementRect.top
	    };

	    delta = (
	      Math.sqrt(
	        Math.pow(event.deltaY, 2) +
	        Math.pow(event.deltaX, 2)
	      ) * sign(event.deltaY) * factor
	    );

	    // zoom in relative to diagram {x,y} coordinates
	    this.zoom(delta, offset);
	  } else {

	    if (isHorizontalScroll) {
	      delta = {
	        dx: factor * event.deltaY,
	        dy: 0
	      };
	    } else {
	      delta = {
	        dx: factor * event.deltaX,
	        dy: factor * event.deltaY
	      };
	    }

	    this.scroll(delta);
	  }
	};

	/**
	 * Zoom with fixed step size.
	 *
	 * @param {number} delta - Zoom delta (1 for zooming in, -1 for out).
	 * @param {Object} position
	 */
	ZoomScroll.prototype.stepZoom = function stepZoom(delta, position) {

	  var stepSize = getStepSize(RANGE, NUM_STEPS);

	  this._zoom(delta, position, stepSize);
	};


	/**
	 * Zoom in/out given a step size.
	 *
	 * @param {number} delta
	 * @param {Object} position
	 * @param {number} stepSize
	 */
	ZoomScroll.prototype._zoom = function(delta, position, stepSize) {
	  var canvas = this._canvas;

	  var direction = delta > 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 = [
	  KeyboardMoveModule,
	  MoveCanvasModule,
	  ZoomScrollModule
	];

	NavigatedViewer.prototype._modules = [].concat(
	  NavigatedViewer.prototype._modules,
	  NavigatedViewer.prototype._navigationModules);

	var hammer = createCommonjsModule(function (module) {
	/*! Hammer.JS - v2.0.7 - 2016-04-22
	 * http://hammerjs.github.io/
	 *
	 * Copyright (c) 2016 Jorik Tangelder;
	 * Licensed under the MIT license */
	(function(window, document, exportName, undefined$1) {

	var VENDOR_PREFIXES = ['', 'webkit', 'Moz', 'MS', 'ms', 'o'];
	var TEST_ELEMENT = document.createElement('div');

	var TYPE_FUNCTION = 'function';

	var round = Math.round;
	var abs = Math.abs;
	var now = Date.now;

	/**
	 * set a timeout with a given scope
	 * @param {Function} fn
	 * @param {Number} timeout
	 * @param {Object} context
	 * @returns {number}
	 */
	function setTimeoutContext(fn, timeout, context) {
	    return setTimeout(bindFn(fn, context), timeout);
	}

	/**
	 * if the argument is an array, we want to execute the fn on each entry
	 * if it aint an array we don't want to do a thing.
	 * this is used by all the methods that accept a single and array argument.
	 * @param {*|Array} arg
	 * @param {String} fn
	 * @param {Object} [context]
	 * @returns {Boolean}
	 */
	function invokeArrayArg(arg, fn, context) {
	    if (Array.isArray(arg)) {
	        each(arg, context[fn], context);
	        return true;
	    }
	    return false;
	}

	/**
	 * walk objects and arrays
	 * @param {Object} obj
	 * @param {Function} iterator
	 * @param {Object} context
	 */
	function each(obj, iterator, context) {
	    var i;

	    if (!obj) {
	        return;
	    }

	    if (obj.forEach) {
	        obj.forEach(iterator, context);
	    } else if (obj.length !== undefined$1) {
	        i = 0;
	        while (i < obj.length) {
	            iterator.call(context, obj[i], i, obj);
	            i++;
	        }
	    } else {
	        for (i in obj) {
	            obj.hasOwnProperty(i) && iterator.call(context, obj[i], i, obj);
	        }
	    }
	}

	/**
	 * wrap a method with a deprecation warning and stack trace
	 * @param {Function} method
	 * @param {String} name
	 * @param {String} message
	 * @returns {Function} A new function wrapping the supplied method.
	 */
	function deprecate(method, name, message) {
	    var deprecationMessage = 'DEPRECATED METHOD: ' + name + '\n' + message + ' AT \n';
	    return function() {
	        var e = new Error('get-stack-trace');
	        var stack = e && e.stack ? e.stack.replace(/^[^\(]+?[\n$]/gm, '')
	            .replace(/^\s+at\s+/gm, '')
	            .replace(/^Object.<anonymous>\s*\(/gm, '{anonymous}()@') : 'Unknown Stack Trace';

	        var log = window.console && (window.console.warn || window.console.log);
	        if (log) {
	            log.call(window.console, deprecationMessage, stack);
	        }
	        return method.apply(this, arguments);
	    };
	}

	/**
	 * extend object.
	 * means that properties in dest will be overwritten by the ones in src.
	 * @param {Object} target
	 * @param {...Object} objects_to_assign
	 * @returns {Object} target
	 */
	var assign;
	if (typeof Object.assign !== 'function') {
	    assign = function assign(target) {
	        if (target === undefined$1 || target === null) {
	            throw new TypeError('Cannot convert undefined or null to object');
	        }

	        var output = Object(target);
	        for (var index = 1; index < arguments.length; index++) {
	            var source = arguments[index];
	            if (source !== undefined$1 && source !== null) {
	                for (var nextKey in source) {
	                    if (source.hasOwnProperty(nextKey)) {
	                        output[nextKey] = source[nextKey];
	                    }
	                }
	            }
	        }
	        return output;
	    };
	} else {
	    assign = Object.assign;
	}

	/**
	 * extend object.
	 * means that properties in dest will be overwritten by the ones in src.
	 * @param {Object} dest
	 * @param {Object} src
	 * @param {Boolean} [merge=false]
	 * @returns {Object} dest
	 */
	var extend = deprecate(function extend(dest, src, merge) {
	    var keys = Object.keys(src);
	    var i = 0;
	    while (i < keys.length) {
	        if (!merge || (merge && dest[keys[i]] === undefined$1)) {
	            dest[keys[i]] = src[keys[i]];
	        }
	        i++;
	    }
	    return dest;
	}, 'extend', 'Use `assign`.');

	/**
	 * merge the values from src in the dest.
	 * means that properties that exist in dest will not be overwritten by src
	 * @param {Object} dest
	 * @param {Object} src
	 * @returns {Object} dest
	 */
	var merge = deprecate(function merge(dest, src) {
	    return extend(dest, src, true);
	}, 'merge', 'Use `assign`.');

	/**
	 * simple class inheritance
	 * @param {Function} child
	 * @param {Function} base
	 * @param {Object} [properties]
	 */
	function inherit(child, base, properties) {
	    var baseP = base.prototype,
	        childP;

	    childP = child.prototype = Object.create(baseP);
	    childP.constructor = child;
	    childP._super = baseP;

	    if (properties) {
	        assign(childP, properties);
	    }
	}

	/**
	 * simple function bind
	 * @param {Function} fn
	 * @param {Object} context
	 * @returns {Function}
	 */
	function bindFn(fn, context) {
	    return function boundFn() {
	        return fn.apply(context, arguments);
	    };
	}

	/**
	 * let a boolean value also be a function that must return a boolean
	 * this first item in args will be used as the context
	 * @param {Boolean|Function} val
	 * @param {Array} [args]
	 * @returns {Boolean}
	 */
	function boolOrFn(val, args) {
	    if (typeof val == TYPE_FUNCTION) {
	        return val.apply(args ? args[0] || undefined$1 : undefined$1, args);
	    }
	    return val;
	}

	/**
	 * use the val2 when val1 is undefined
	 * @param {*} val1
	 * @param {*} val2
	 * @returns {*}
	 */
	function ifUndefined(val1, val2) {
	    return (val1 === undefined$1) ? val2 : val1;
	}

	/**
	 * addEventListener with multiple events at once
	 * @param {EventTarget} target
	 * @param {String} types
	 * @param {Function} handler
	 */
	function addEventListeners(target, types, handler) {
	    each(splitStr(types), function(type) {
	        target.addEventListener(type, handler, false);
	    });
	}

	/**
	 * removeEventListener with multiple events at once
	 * @param {EventTarget} target
	 * @param {String} types
	 * @param {Function} handler
	 */
	function removeEventListeners(target, types, handler) {
	    each(splitStr(types), function(type) {
	        target.removeEventListener(type, handler, false);
	    });
	}

	/**
	 * find if a node is in the given parent
	 * @method hasParent
	 * @param {HTMLElement} node
	 * @param {HTMLElement} parent
	 * @return {Boolean} found
	 */
	function hasParent(node, parent) {
	    while (node) {
	        if (node == parent) {
	            return true;
	        }
	        node = node.parentNode;
	    }
	    return false;
	}

	/**
	 * small indexOf wrapper
	 * @param {String} str
	 * @param {String} find
	 * @returns {Boolean} found
	 */
	function inStr(str, find) {
	    return str.indexOf(find) > -1;
	}

	/**
	 * split string on whitespace
	 * @param {String} str
	 * @returns {Array} words
	 */
	function splitStr(str) {
	    return str.trim().split(/\s+/g);
	}

	/**
	 * find if a array contains the object using indexOf or a simple polyFill
	 * @param {Array} src
	 * @param {String} find
	 * @param {String} [findByKey]
	 * @return {Boolean|Number} false when not found, or the index
	 */
	function inArray(src, find, findByKey) {
	    if (src.indexOf && !findByKey) {
	        return src.indexOf(find);
	    } else {
	        var i = 0;
	        while (i < src.length) {
	            if ((findByKey && src[i][findByKey] == find) || (!findByKey && src[i] === find)) {
	                return i;
	            }
	            i++;
	        }
	        return -1;
	    }
	}

	/**
	 * convert array-like objects to real arrays
	 * @param {Object} obj
	 * @returns {Array}
	 */
	function toArray(obj) {
	    return Array.prototype.slice.call(obj, 0);
	}

	/**
	 * unique array with objects based on a key (like 'id') or just by the array's value
	 * @param {Array} src [{id:1},{id:2},{id:1}]
	 * @param {String} [key]
	 * @param {Boolean} [sort=False]
	 * @returns {Array} [{id:1},{id:2}]
	 */
	function uniqueArray(src, key, sort) {
	    var results = [];
	    var values = [];
	    var i = 0;

	    while (i < src.length) {
	        var val = key ? src[i][key] : src[i];
	        if (inArray(values, val) < 0) {
	            results.push(src[i]);
	        }
	        values[i] = val;
	        i++;
	    }

	    if (sort) {
	        if (!key) {
	            results = results.sort();
	        } else {
	            results = results.sort(function sortUniqueArray(a, b) {
	                return a[key] > b[key];
	            });
	        }
	    }

	    return results;
	}

	/**
	 * get the prefixed property
	 * @param {Object} obj
	 * @param {String} property
	 * @returns {String|Undefined} prefixed
	 */
	function prefixed(obj, property) {
	    var prefix, prop;
	    var camelProp = property[0].toUpperCase() + property.slice(1);

	    var i = 0;
	    while (i < VENDOR_PREFIXES.length) {
	        prefix = VENDOR_PREFIXES[i];
	        prop = (prefix) ? prefix + camelProp : property;

	        if (prop in obj) {
	            return prop;
	        }
	        i++;
	    }
	    return undefined$1;
	}

	/**
	 * get a unique id
	 * @returns {number} uniqueId
	 */
	var _uniqueId = 1;
	function uniqueId() {
	    return _uniqueId++;
	}

	/**
	 * get the window object of an element
	 * @param {HTMLElement} element
	 * @returns {DocumentView|Window}
	 */
	function getWindowForElement(element) {
	    var doc = element.ownerDocument || element;
	    return (doc.defaultView || doc.parentWindow || window);
	}

	var MOBILE_REGEX = /mobile|tablet|ip(ad|hone|od)|android/i;

	var SUPPORT_TOUCH = ('ontouchstart' in window);
	var SUPPORT_POINTER_EVENTS = prefixed(window, 'PointerEvent') !== undefined$1;
	var SUPPORT_ONLY_TOUCH = SUPPORT_TOUCH && MOBILE_REGEX.test(navigator.userAgent);

	var INPUT_TYPE_TOUCH = 'touch';
	var INPUT_TYPE_PEN = 'pen';
	var INPUT_TYPE_MOUSE = 'mouse';
	var INPUT_TYPE_KINECT = 'kinect';

	var COMPUTE_INTERVAL = 25;

	var INPUT_START = 1;
	var INPUT_MOVE = 2;
	var INPUT_END = 4;
	var INPUT_CANCEL = 8;

	var DIRECTION_NONE = 1;
	var DIRECTION_LEFT = 2;
	var DIRECTION_RIGHT = 4;
	var DIRECTION_UP = 8;
	var DIRECTION_DOWN = 16;

	var DIRECTION_HORIZONTAL = DIRECTION_LEFT | DIRECTION_RIGHT;
	var DIRECTION_VERTICAL = DIRECTION_UP | DIRECTION_DOWN;
	var DIRECTION_ALL = DIRECTION_HORIZONTAL | DIRECTION_VERTICAL;

	var PROPS_XY = ['x', 'y'];
	var PROPS_CLIENT_XY = ['clientX', 'clientY'];

	/**
	 * create new input type manager
	 * @param {Manager} manager
	 * @param {Function} callback
	 * @returns {Input}
	 * @constructor
	 */
	function Input(manager, callback) {
	    var self = this;
	    this.manager = manager;
	    this.callback = callback;
	    this.element = manager.element;
	    this.target = manager.options.inputTarget;

	    // smaller wrapper around the handler, for the scope and the enabled state of the manager,
	    // so when disabled the input events are completely bypassed.
	    this.domHandler = function(ev) {
	        if (boolOrFn(manager.options.enable, [manager])) {
	            self.handler(ev);
	        }
	    };

	    this.init();

	}

	Input.prototype = {
	    /**
	     * should handle the inputEvent data and trigger the callback
	     * @virtual
	     */
	    handler: function() { },

	    /**
	     * bind the events
	     */
	    init: function() {
	        this.evEl && addEventListeners(this.element, this.evEl, this.domHandler);
	        this.evTarget && addEventListeners(this.target, this.evTarget, this.domHandler);
	        this.evWin && addEventListeners(getWindowForElement(this.element), this.evWin, this.domHandler);
	    },

	    /**
	     * unbind the events
	     */
	    destroy: function() {
	        this.evEl && removeEventListeners(this.element, this.evEl, this.domHandler);
	        this.evTarget && removeEventListeners(this.target, this.evTarget, this.domHandler);
	        this.evWin && removeEventListeners(getWindowForElement(this.element), this.evWin, this.domHandler);
	    }
	};

	/**
	 * create new input type manager
	 * called by the Manager constructor
	 * @param {Hammer} manager
	 * @returns {Input}
	 */
	function createInputInstance(manager) {
	    var Type;
	    var inputClass = manager.options.inputClass;

	    if (inputClass) {
	        Type = inputClass;
	    } else if (SUPPORT_POINTER_EVENTS) {
	        Type = PointerEventInput;
	    } else if (SUPPORT_ONLY_TOUCH) {
	        Type = TouchInput;
	    } else if (!SUPPORT_TOUCH) {
	        Type = MouseInput;
	    } else {
	        Type = TouchMouseInput;
	    }
	    return new (Type)(manager, inputHandler);
	}

	/**
	 * handle input events
	 * @param {Manager} manager
	 * @param {String} eventType
	 * @param {Object} input
	 */
	function inputHandler(manager, eventType, input) {
	    var pointersLen = input.pointers.length;
	    var changedPointersLen = input.changedPointers.length;
	    var isFirst = (eventType & INPUT_START && (pointersLen - changedPointersLen === 0));
	    var isFinal = (eventType & (INPUT_END | INPUT_CANCEL) && (pointersLen - changedPointersLen === 0));

	    input.isFirst = !!isFirst;
	    input.isFinal = !!isFinal;

	    if (isFirst) {
	        manager.session = {};
	    }

	    // source event is the normalized value of the domEvents
	    // like 'touchstart, mouseup, pointerdown'
	    input.eventType = eventType;

	    // compute scale, rotation etc
	    computeInputData(manager, input);

	    // emit secret event
	    manager.emit('hammer.input', input);

	    manager.recognize(input);
	    manager.session.prevInput = input;
	}

	/**
	 * extend the data with some usable properties like scale, rotate, velocity etc
	 * @param {Object} manager
	 * @param {Object} input
	 */
	function computeInputData(manager, input) {
	    var session = manager.session;
	    var pointers = input.pointers;
	    var pointersLength = pointers.length;

	    // store the first input to calculate the distance and direction
	    if (!session.firstInput) {
	        session.firstInput = simpleCloneInputData(input);
	    }

	    // to compute scale and rotation we need to store the multiple touches
	    if (pointersLength > 1 && !session.firstMultiple) {
	        session.firstMultiple = simpleCloneInputData(input);
	    } else if (pointersLength === 1) {
	        session.firstMultiple = false;
	    }

	    var firstInput = session.firstInput;
	    var firstMultiple = session.firstMultiple;
	    var offsetCenter = firstMultiple ? firstMultiple.center : firstInput.center;

	    var center = input.center = getCenter(pointers);
	    input.timeStamp = now();
	    input.deltaTime = input.timeStamp - firstInput.timeStamp;

	    input.angle = getAngle(offsetCenter, center);
	    input.distance = getDistance(offsetCenter, center);

	    computeDeltaXY(session, input);
	    input.offsetDirection = getDirection(input.deltaX, input.deltaY);

	    var overallVelocity = getVelocity(input.deltaTime, input.deltaX, input.deltaY);
	    input.overallVelocityX = overallVelocity.x;
	    input.overallVelocityY = overallVelocity.y;
	    input.overallVelocity = (abs(overallVelocity.x) > abs(overallVelocity.y)) ? overallVelocity.x : overallVelocity.y;

	    input.scale = firstMultiple ? getScale(firstMultiple.pointers, pointers) : 1;
	    input.rotation = firstMultiple ? getRotation(firstMultiple.pointers, pointers) : 0;

	    input.maxPointers = !session.prevInput ? input.pointers.length : ((input.pointers.length >
	        session.prevInput.maxPointers) ? input.pointers.length : session.prevInput.maxPointers);

	    computeIntervalInputData(session, input);

	    // find the correct target
	    var target = manager.element;
	    if (hasParent(input.srcEvent.target, target)) {
	        target = input.srcEvent.target;
	    }
	    input.target = target;
	}

	function computeDeltaXY(session, input) {
	    var center = input.center;
	    var offset = session.offsetDelta || {};
	    var prevDelta = session.prevDelta || {};
	    var prevInput = session.prevInput || {};

	    if (input.eventType === INPUT_START || prevInput.eventType === INPUT_END) {
	        prevDelta = session.prevDelta = {
	            x: prevInput.deltaX || 0,
	            y: prevInput.deltaY || 0
	        };

	        offset = session.offsetDelta = {
	            x: center.x,
	            y: center.y
	        };
	    }

	    input.deltaX = prevDelta.x + (center.x - offset.x);
	    input.deltaY = prevDelta.y + (center.y - offset.y);
	}

	/**
	 * velocity is calculated every x ms
	 * @param {Object} session
	 * @param {Object} input
	 */
	function computeIntervalInputData(session, input) {
	    var last = session.lastInterval || input,
	        deltaTime = input.timeStamp - last.timeStamp,
	        velocity, velocityX, velocityY, direction;

	    if (input.eventType != INPUT_CANCEL && (deltaTime > COMPUTE_INTERVAL || last.velocity === undefined$1)) {
	        var deltaX = input.deltaX - last.deltaX;
	        var deltaY = input.deltaY - last.deltaY;

	        var v = getVelocity(deltaTime, deltaX, deltaY);
	        velocityX = v.x;
	        velocityY = v.y;
	        velocity = (abs(v.x) > abs(v.y)) ? v.x : v.y;
	        direction = getDirection(deltaX, deltaY);

	        session.lastInterval = input;
	    } else {
	        // use latest velocity info if it doesn't overtake a minimum period
	        velocity = last.velocity;
	        velocityX = last.velocityX;
	        velocityY = last.velocityY;
	        direction = last.direction;
	    }

	    input.velocity = velocity;
	    input.velocityX = velocityX;
	    input.velocityY = velocityY;
	    input.direction = direction;
	}

	/**
	 * create a simple clone from the input used for storage of firstInput and firstMultiple
	 * @param {Object} input
	 * @returns {Object} clonedInputData
	 */
	function simpleCloneInputData(input) {
	    // make a simple copy of the pointers because we will get a reference if we don't
	    // we only need clientXY for the calculations
	    var pointers = [];
	    var i = 0;
	    while (i < input.pointers.length) {
	        pointers[i] = {
	            clientX: round(input.pointers[i].clientX),
	            clientY: round(input.pointers[i].clientY)
	        };
	        i++;
	    }

	    return {
	        timeStamp: now(),
	        pointers: pointers,
	        center: getCenter(pointers),
	        deltaX: input.deltaX,
	        deltaY: input.deltaY
	    };
	}

	/**
	 * get the center of all the pointers
	 * @param {Array} pointers
	 * @return {Object} center contains `x` and `y` properties
	 */
	function getCenter(pointers) {
	    var pointersLength = pointers.length;

	    // no need to loop when only one touch
	    if (pointersLength === 1) {
	        return {
	            x: round(pointers[0].clientX),
	            y: round(pointers[0].clientY)
	        };
	    }

	    var x = 0, y = 0, i = 0;
	    while (i < pointersLength) {
	        x += pointers[i].clientX;
	        y += pointers[i].clientY;
	        i++;
	    }

	    return {
	        x: round(x / pointersLength),
	        y: round(y / pointersLength)
	    };
	}

	/**
	 * calculate the velocity between two points. unit is in px per ms.
	 * @param {Number} deltaTime
	 * @param {Number} x
	 * @param {Number} y
	 * @return {Object} velocity `x` and `y`
	 */
	function getVelocity(deltaTime, x, y) {
	    return {
	        x: x / deltaTime || 0,
	        y: y / deltaTime || 0
	    };
	}

	/**
	 * get the direction between two points
	 * @param {Number} x
	 * @param {Number} y
	 * @return {Number} direction
	 */
	function getDirection(x, y) {
	    if (x === y) {
	        return DIRECTION_NONE;
	    }

	    if (abs(x) >= abs(y)) {
	        return x < 0 ? DIRECTION_LEFT : DIRECTION_RIGHT;
	    }
	    return y < 0 ? DIRECTION_UP : DIRECTION_DOWN;
	}

	/**
	 * calculate the absolute distance between two points
	 * @param {Object} p1 {x, y}
	 * @param {Object} p2 {x, y}
	 * @param {Array} [props] containing x and y keys
	 * @return {Number} distance
	 */
	function getDistance(p1, p2, props) {
	    if (!props) {
	        props = PROPS_XY;
	    }
	    var x = p2[props[0]] - p1[props[0]],
	        y = p2[props[1]] - p1[props[1]];

	    return Math.sqrt((x * x) + (y * y));
	}

	/**
	 * calculate the angle between two coordinates
	 * @param {Object} p1
	 * @param {Object} p2
	 * @param {Array} [props] containing x and y keys
	 * @return {Number} angle
	 */
	function getAngle(p1, p2, props) {
	    if (!props) {
	        props = PROPS_XY;
	    }
	    var x = p2[props[0]] - p1[props[0]],
	        y = p2[props[1]] - p1[props[1]];
	    return Math.atan2(y, x) * 180 / Math.PI;
	}

	/**
	 * calculate the rotation degrees between two pointersets
	 * @param {Array} start array of pointers
	 * @param {Array} end array of pointers
	 * @return {Number} rotation
	 */
	function getRotation(start, end) {
	    return getAngle(end[1], end[0], PROPS_CLIENT_XY) + getAngle(start[1], start[0], PROPS_CLIENT_XY);
	}

	/**
	 * calculate the scale factor between two pointersets
	 * no scale is 1, and goes down to 0 when pinched together, and bigger when pinched out
	 * @param {Array} start array of pointers
	 * @param {Array} end array of pointers
	 * @return {Number} scale
	 */
	function getScale(start, end) {
	    return getDistance(end[0], end[1], PROPS_CLIENT_XY) / getDistance(start[0], start[1], PROPS_CLIENT_XY);
	}

	var MOUSE_INPUT_MAP = {
	    mousedown: INPUT_START,
	    mousemove: INPUT_MOVE,
	    mouseup: INPUT_END
	};

	var MOUSE_ELEMENT_EVENTS = 'mousedown';
	var MOUSE_WINDOW_EVENTS = 'mousemove mouseup';

	/**
	 * Mouse events input
	 * @constructor
	 * @extends Input
	 */
	function MouseInput() {
	    this.evEl = MOUSE_ELEMENT_EVENTS;
	    this.evWin = MOUSE_WINDOW_EVENTS;

	    this.pressed = false; // mousedown state

	    Input.apply(this, arguments);
	}

	inherit(MouseInput, Input, {
	    /**
	     * handle mouse events
	     * @param {Object} ev
	     */
	    handler: function MEhandler(ev) {
	        var eventType = MOUSE_INPUT_MAP[ev.type];

	        // on start we want to have the left mouse button down
	        if (eventType & INPUT_START && ev.button === 0) {
	            this.pressed = true;
	        }

	        if (eventType & INPUT_MOVE && ev.which !== 1) {
	            eventType = INPUT_END;
	        }

	        // mouse must be down
	        if (!this.pressed) {
	            return;
	        }

	        if (eventType & INPUT_END) {
	            this.pressed = false;
	        }

	        this.callback(this.manager, eventType, {
	            pointers: [ev],
	            changedPointers: [ev],
	            pointerType: INPUT_TYPE_MOUSE,
	            srcEvent: ev
	        });
	    }
	});

	var POINTER_INPUT_MAP = {
	    pointerdown: INPUT_START,
	    pointermove: INPUT_MOVE,
	    pointerup: INPUT_END,
	    pointercancel: INPUT_CANCEL,
	    pointerout: INPUT_CANCEL
	};

	// in IE10 the pointer types is defined as an enum
	var IE10_POINTER_TYPE_ENUM = {
	    2: INPUT_TYPE_TOUCH,
	    3: INPUT_TYPE_PEN,
	    4: INPUT_TYPE_MOUSE,
	    5: INPUT_TYPE_KINECT // see https://twitter.com/jacobrossi/status/480596438489890816
	};

	var POINTER_ELEMENT_EVENTS = 'pointerdown';
	var POINTER_WINDOW_EVENTS = 'pointermove pointerup pointercancel';

	// IE10 has prefixed support, and case-sensitive
	if (window.MSPointerEvent && !window.PointerEvent) {
	    POINTER_ELEMENT_EVENTS = 'MSPointerDown';
	    POINTER_WINDOW_EVENTS = 'MSPointerMove MSPointerUp MSPointerCancel';
	}

	/**
	 * Pointer events input
	 * @constructor
	 * @extends Input
	 */
	function PointerEventInput() {
	    this.evEl = POINTER_ELEMENT_EVENTS;
	    this.evWin = POINTER_WINDOW_EVENTS;

	    Input.apply(this, arguments);

	    this.store = (this.manager.session.pointerEvents = []);
	}

	inherit(PointerEventInput, Input, {
	    /**
	     * handle mouse events
	     * @param {Object} ev
	     */
	    handler: function PEhandler(ev) {
	        var store = this.store;
	        var removePointer = false;

	        var eventTypeNormalized = ev.type.toLowerCase().replace('ms', '');
	        var eventType = POINTER_INPUT_MAP[eventTypeNormalized];
	        var pointerType = IE10_POINTER_TYPE_ENUM[ev.pointerType] || ev.pointerType;

	        var isTouch = (pointerType == INPUT_TYPE_TOUCH);

	        // get index of the event in the store
	        var storeIndex = inArray(store, ev.pointerId, 'pointerId');

	        // start and mouse must be down
	        if (eventType & INPUT_START && (ev.button === 0 || isTouch)) {
	            if (storeIndex < 0) {
	                store.push(ev);
	                storeIndex = store.length - 1;
	            }
	        } else if (eventType & (INPUT_END | INPUT_CANCEL)) {
	            removePointer = true;
	        }

	        // it not found, so the pointer hasn't been down (so it's probably a hover)
	        if (storeIndex < 0) {
	            return;
	        }

	        // update the event in the store
	        store[storeIndex] = ev;

	        this.callback(this.manager, eventType, {
	            pointers: store,
	            changedPointers: [ev],
	            pointerType: pointerType,
	            srcEvent: ev
	        });

	        if (removePointer) {
	            // remove from the store
	            store.splice(storeIndex, 1);
	        }
	    }
	});

	var SINGLE_TOUCH_INPUT_MAP = {
	    touchstart: INPUT_START,
	    touchmove: INPUT_MOVE,
	    touchend: INPUT_END,
	    touchcancel: INPUT_CANCEL
	};

	var SINGLE_TOUCH_TARGET_EVENTS = 'touchstart';
	var SINGLE_TOUCH_WINDOW_EVENTS = 'touchstart touchmove touchend touchcancel';

	/**
	 * Touch events input
	 * @constructor
	 * @extends Input
	 */
	function SingleTouchInput() {
	    this.evTarget = SINGLE_TOUCH_TARGET_EVENTS;
	    this.evWin = SINGLE_TOUCH_WINDOW_EVENTS;
	    this.started = false;

	    Input.apply(this, arguments);
	}

	inherit(SingleTouchInput, Input, {
	    handler: function TEhandler(ev) {
	        var type = SINGLE_TOUCH_INPUT_MAP[ev.type];

	        // should we handle the touch events?
	        if (type === INPUT_START) {
	            this.started = true;
	        }

	        if (!this.started) {
	            return;
	        }

	        var touches = normalizeSingleTouches.call(this, ev, type);

	        // when done, reset the started state
	        if (type & (INPUT_END | INPUT_CANCEL) && touches[0].length - touches[1].length === 0) {
	            this.started = false;
	        }

	        this.callback(this.manager, type, {
	            pointers: touches[0],
	            changedPointers: touches[1],
	            pointerType: INPUT_TYPE_TOUCH,
	            srcEvent: ev
	        });
	    }
	});

	/**
	 * @this {TouchInput}
	 * @param {Object} ev
	 * @param {Number} type flag
	 * @returns {undefined|Array} [all, changed]
	 */
	function normalizeSingleTouches(ev, type) {
	    var all = toArray(ev.touches);
	    var changed = toArray(ev.changedTouches);

	    if (type & (INPUT_END | INPUT_CANCEL)) {
	        all = uniqueArray(all.concat(changed), 'identifier', true);
	    }

	    return [all, changed];
	}

	var TOUCH_INPUT_MAP = {
	    touchstart: INPUT_START,
	    touchmove: INPUT_MOVE,
	    touchend: INPUT_END,
	    touchcancel: INPUT_CANCEL
	};

	var TOUCH_TARGET_EVENTS = 'touchstart touchmove touchend touchcancel';

	/**
	 * Multi-user touch events input
	 * @constructor
	 * @extends Input
	 */
	function TouchInput() {
	    this.evTarget = TOUCH_TARGET_EVENTS;
	    this.targetIds = {};

	    Input.apply(this, arguments);
	}

	inherit(TouchInput, Input, {
	    handler: function MTEhandler(ev) {
	        var type = TOUCH_INPUT_MAP[ev.type];
	        var touches = getTouches.call(this, ev, type);
	        if (!touches) {
	            return;
	        }

	        this.callback(this.manager, type, {
	            pointers: touches[0],
	            changedPointers: touches[1],
	            pointerType: INPUT_TYPE_TOUCH,
	            srcEvent: ev
	        });
	    }
	});

	/**
	 * @this {TouchInput}
	 * @param {Object} ev
	 * @param {Number} type flag
	 * @returns {undefined|Array} [all, changed]
	 */
	function getTouches(ev, type) {
	    var allTouches = toArray(ev.touches);
	    var targetIds = this.targetIds;

	    // when there is only one touch, the process can be simplified
	    if (type & (INPUT_START | INPUT_MOVE) && allTouches.length === 1) {
	        targetIds[allTouches[0].identifier] = true;
	        return [allTouches, allTouches];
	    }

	    var i,
	        targetTouches,
	        changedTouches = toArray(ev.changedTouches),
	        changedTargetTouches = [],
	        target = this.target;

	    // get target touches from touches
	    targetTouches = allTouches.filter(function(touch) {
	        return hasParent(touch.target, target);
	    });

	    // collect touches
	    if (type === INPUT_START) {
	        i = 0;
	        while (i < targetTouches.length) {
	            targetIds[targetTouches[i].identifier] = true;
	            i++;
	        }
	    }

	    // filter changed touches to only contain touches that exist in the collected target ids
	    i = 0;
	    while (i < changedTouches.length) {
	        if (targetIds[changedTouches[i].identifier]) {
	            changedTargetTouches.push(changedTouches[i]);
	        }

	        // cleanup removed touches
	        if (type & (INPUT_END | INPUT_CANCEL)) {
	            delete targetIds[changedTouches[i].identifier];
	        }
	        i++;
	    }

	    if (!changedTargetTouches.length) {
	        return;
	    }

	    return [
	        // merge targetTouches with changedTargetTouches so it contains ALL touches, including 'end' and 'cancel'
	        uniqueArray(targetTouches.concat(changedTargetTouches), 'identifier', true),
	        changedTargetTouches
	    ];
	}

	/**
	 * Combined touch and mouse input
	 *
	 * Touch has a higher priority then mouse, and while touching no mouse events are allowed.
	 * This because touch devices also emit mouse events while doing a touch.
	 *
	 * @constructor
	 * @extends Input
	 */

	var DEDUP_TIMEOUT = 2500;
	var DEDUP_DISTANCE = 25;

	function TouchMouseInput() {
	    Input.apply(this, arguments);

	    var handler = bindFn(this.handler, this);
	    this.touch = new TouchInput(this.manager, handler);
	    this.mouse = new MouseInput(this.manager, handler);

	    this.primaryTouch = null;
	    this.lastTouches = [];
	}

	inherit(TouchMouseInput, Input, {
	    /**
	     * handle mouse and touch events
	     * @param {Hammer} manager
	     * @param {String} inputEvent
	     * @param {Object} inputData
	     */
	    handler: function TMEhandler(manager, inputEvent, inputData) {
	        var isTouch = (inputData.pointerType == INPUT_TYPE_TOUCH),
	            isMouse = (inputData.pointerType == INPUT_TYPE_MOUSE);

	        if (isMouse && inputData.sourceCapabilities && inputData.sourceCapabilities.firesTouchEvents) {
	            return;
	        }

	        // when we're in a touch event, record touches to  de-dupe synthetic mouse event
	        if (isTouch) {
	            recordTouches.call(this, inputEvent, inputData);
	        } else if (isMouse && isSyntheticEvent.call(this, inputData)) {
	            return;
	        }

	        this.callback(manager, inputEvent, inputData);
	    },

	    /**
	     * remove the event listeners
	     */
	    destroy: function destroy() {
	        this.touch.destroy();
	        this.mouse.destroy();
	    }
	});

	function recordTouches(eventType, eventData) {
	    if (eventType & INPUT_START) {
	        this.primaryTouch = eventData.changedPointers[0].identifier;
	        setLastTouch.call(this, eventData);
	    } else if (eventType & (INPUT_END | INPUT_CANCEL)) {
	        setLastTouch.call(this, eventData);
	    }
	}

	function setLastTouch(eventData) {
	    var touch = eventData.changedPointers[0];

	    if (touch.identifier === this.primaryTouch) {
	        var lastTouch = {x: touch.clientX, y: touch.clientY};
	        this.lastTouches.push(lastTouch);
	        var lts = this.lastTouches;
	        var removeLastTouch = function() {
	            var i = lts.indexOf(lastTouch);
	            if (i > -1) {
	                lts.splice(i, 1);
	            }
	        };
	        setTimeout(removeLastTouch, DEDUP_TIMEOUT);
	    }
	}

	function isSyntheticEvent(eventData) {
	    var x = eventData.srcEvent.clientX, y = eventData.srcEvent.clientY;
	    for (var i = 0; i < this.lastTouches.length; i++) {
	        var t = this.lastTouches[i];
	        var dx = Math.abs(x - t.x), dy = Math.abs(y - t.y);
	        if (dx <= DEDUP_DISTANCE && dy <= DEDUP_DISTANCE) {
	            return true;
	        }
	    }
	    return false;
	}

	var PREFIXED_TOUCH_ACTION = prefixed(TEST_ELEMENT.style, 'touchAction');
	var NATIVE_TOUCH_ACTION = PREFIXED_TOUCH_ACTION !== undefined$1;

	// magical touchAction value
	var TOUCH_ACTION_COMPUTE = 'compute';
	var TOUCH_ACTION_AUTO = 'auto';
	var TOUCH_ACTION_MANIPULATION = 'manipulation'; // not implemented
	var TOUCH_ACTION_NONE = 'none';
	var TOUCH_ACTION_PAN_X = 'pan-x';
	var TOUCH_ACTION_PAN_Y = 'pan-y';
	var TOUCH_ACTION_MAP = getTouchActionProps();

	/**
	 * Touch Action
	 * sets the touchAction property or uses the js alternative
	 * @param {Manager} manager
	 * @param {String} value
	 * @constructor
	 */
	function TouchAction(manager, value) {
	    this.manager = manager;
	    this.set(value);
	}

	TouchAction.prototype = {
	    /**
	     * set the touchAction value on the element or enable the polyfill
	     * @param {String} value
	     */
	    set: function(value) {
	        // find out the touch-action by the event handlers
	        if (value == TOUCH_ACTION_COMPUTE) {
	            value = this.compute();
	        }

	        if (NATIVE_TOUCH_ACTION && this.manager.element.style && TOUCH_ACTION_MAP[value]) {
	            this.manager.element.style[PREFIXED_TOUCH_ACTION] = value;
	        }
	        this.actions = value.toLowerCase().trim();
	    },

	    /**
	     * just re-set the touchAction value
	     */
	    update: function() {
	        this.set(this.manager.options.touchAction);
	    },

	    /**
	     * compute the value for the touchAction property based on the recognizer's settings
	     * @returns {String} value
	     */
	    compute: function() {
	        var actions = [];
	        each(this.manager.recognizers, function(recognizer) {
	            if (boolOrFn(recognizer.options.enable, [recognizer])) {
	                actions = actions.concat(recognizer.getTouchAction());
	            }
	        });
	        return cleanTouchActions(actions.join(' '));
	    },

	    /**
	     * this method is called on each input cycle and provides the preventing of the browser behavior
	     * @param {Object} input
	     */
	    preventDefaults: function(input) {
	        var srcEvent = input.srcEvent;
	        var direction = input.offsetDirection;

	        // if the touch action did prevented once this session
	        if (this.manager.session.prevented) {
	            srcEvent.preventDefault();
	            return;
	        }

	        var actions = this.actions;
	        var hasNone = inStr(actions, TOUCH_ACTION_NONE) && !TOUCH_ACTION_MAP[TOUCH_ACTION_NONE];
	        var hasPanY = inStr(actions, TOUCH_ACTION_PAN_Y) && !TOUCH_ACTION_MAP[TOUCH_ACTION_PAN_Y];
	        var hasPanX = inStr(actions, TOUCH_ACTION_PAN_X) && !TOUCH_ACTION_MAP[TOUCH_ACTION_PAN_X];

	        if (hasNone) {
	            //do not prevent defaults if this is a tap gesture

	            var isTapPointer = input.pointers.length === 1;
	            var isTapMovement = input.distance < 2;
	            var isTapTouchTime = input.deltaTime < 250;

	            if (isTapPointer && isTapMovement && isTapTouchTime) {
	                return;
	            }
	        }

	        if (hasPanX && hasPanY) {
	            // `pan-x pan-y` means browser handles all scrolling/panning, do not prevent
	            return;
	        }

	        if (hasNone ||
	            (hasPanY && direction & DIRECTION_HORIZONTAL) ||
	            (hasPanX && direction & DIRECTION_VERTICAL)) {
	            return this.preventSrc(srcEvent);
	        }
	    },

	    /**
	     * call preventDefault to prevent the browser's default behavior (scrolling in most cases)
	     * @param {Object} srcEvent
	     */
	    preventSrc: function(srcEvent) {
	        this.manager.session.prevented = true;
	        srcEvent.preventDefault();
	    }
	};

	/**
	 * when the touchActions are collected they are not a valid value, so we need to clean things up. *
	 * @param {String} actions
	 * @returns {*}
	 */
	function cleanTouchActions(actions) {
	    // none
	    if (inStr(actions, TOUCH_ACTION_NONE)) {
	        return TOUCH_ACTION_NONE;
	    }

	    var hasPanX = inStr(actions, TOUCH_ACTION_PAN_X);
	    var hasPanY = inStr(actions, TOUCH_ACTION_PAN_Y);

	    // if both pan-x and pan-y are set (different recognizers
	    // for different directions, e.g. horizontal pan but vertical swipe?)
	    // we need none (as otherwise with pan-x pan-y combined none of these
	    // recognizers will work, since the browser would handle all panning
	    if (hasPanX && hasPanY) {
	        return TOUCH_ACTION_NONE;
	    }

	    // pan-x OR pan-y
	    if (hasPanX || hasPanY) {
	        return hasPanX ? TOUCH_ACTION_PAN_X : TOUCH_ACTION_PAN_Y;
	    }

	    // manipulation
	    if (inStr(actions, TOUCH_ACTION_MANIPULATION)) {
	        return TOUCH_ACTION_MANIPULATION;
	    }

	    return TOUCH_ACTION_AUTO;
	}

	function getTouchActionProps() {
	    if (!NATIVE_TOUCH_ACTION) {
	        return false;
	    }
	    var touchMap = {};
	    var cssSupports = window.CSS && window.CSS.supports;
	    ['auto', 'manipulation', 'pan-y', 'pan-x', 'pan-x pan-y', 'none'].forEach(function(val) {

	        // If css.supports is not supported but there is native touch-action assume it supports
	        // all values. This is the case for IE 10 and 11.
	        touchMap[val] = cssSupports ? window.CSS.supports('touch-action', val) : true;
	    });
	    return touchMap;
	}

	/**
	 * Recognizer flow explained; *
	 * All recognizers have the initial state of POSSIBLE when a input session starts.
	 * The definition of a input session is from the first input until the last input, with all it's movement in it. *
	 * Example session for mouse-input: mousedown -> mousemove -> mouseup
	 *
	 * On each recognizing cycle (see Manager.recognize) the .recognize() method is executed
	 * which determines with state it should be.
	 *
	 * If the recognizer has the state FAILED, CANCELLED or RECOGNIZED (equals ENDED), it is reset to
	 * POSSIBLE to give it another change on the next cycle.
	 *
	 *               Possible
	 *                  |
	 *            +-----+---------------+
	 *            |                     |
	 *      +-----+-----+               |
	 *      |           |               |
	 *   Failed      Cancelled          |
	 *                          +-------+------+
	 *                          |              |
	 *                      Recognized       Began
	 *                                         |
	 *                                      Changed
	 *                                         |
	 *                                  Ended/Recognized
	 */
	var STATE_POSSIBLE = 1;
	var STATE_BEGAN = 2;
	var STATE_CHANGED = 4;
	var STATE_ENDED = 8;
	var STATE_RECOGNIZED = STATE_ENDED;
	var STATE_CANCELLED = 16;
	var STATE_FAILED = 32;

	/**
	 * Recognizer
	 * Every recognizer needs to extend from this class.
	 * @constructor
	 * @param {Object} options
	 */
	function Recognizer(options) {
	    this.options = assign({}, this.defaults, options || {});

	    this.id = uniqueId();

	    this.manager = null;

	    // default is enable true
	    this.options.enable = ifUndefined(this.options.enable, true);

	    this.state = STATE_POSSIBLE;

	    this.simultaneous = {};
	    this.requireFail = [];
	}

	Recognizer.prototype = {
	    /**
	     * @virtual
	     * @type {Object}
	     */
	    defaults: {},

	    /**
	     * set options
	     * @param {Object} options
	     * @return {Recognizer}
	     */
	    set: function(options) {
	        assign(this.options, options);

	        // also update the touchAction, in case something changed about the directions/enabled state
	        this.manager && this.manager.touchAction.update();
	        return this;
	    },

	    /**
	     * recognize simultaneous with an other recognizer.
	     * @param {Recognizer} otherRecognizer
	     * @returns {Recognizer} this
	     */
	    recognizeWith: function(otherRecognizer) {
	        if (invokeArrayArg(otherRecognizer, 'recognizeWith', this)) {
	            return this;
	        }

	        var simultaneous = this.simultaneous;
	        otherRecognizer = getRecognizerByNameIfManager(otherRecognizer, this);
	        if (!simultaneous[otherRecognizer.id]) {
	            simultaneous[otherRecognizer.id] = otherRecognizer;
	            otherRecognizer.recognizeWith(this);
	        }
	        return this;
	    },

	    /**
	     * drop the simultaneous link. it doesnt remove the link on the other recognizer.
	     * @param {Recognizer} otherRecognizer
	     * @returns {Recognizer} this
	     */
	    dropRecognizeWith: function(otherRecognizer) {
	        if (invokeArrayArg(otherRecognizer, 'dropRecognizeWith', this)) {
	            return this;
	        }

	        otherRecognizer = getRecognizerByNameIfManager(otherRecognizer, this);
	        delete this.simultaneous[otherRecognizer.id];
	        return this;
	    },

	    /**
	     * recognizer can only run when an other is failing
	     * @param {Recognizer} otherRecognizer
	     * @returns {Recognizer} this
	     */
	    requireFailure: function(otherRecognizer) {
	        if (invokeArrayArg(otherRecognizer, 'requireFailure', this)) {
	            return this;
	        }

	        var requireFail = this.requireFail;
	        otherRecognizer = getRecognizerByNameIfManager(otherRecognizer, this);
	        if (inArray(requireFail, otherRecognizer) === -1) {
	            requireFail.push(otherRecognizer);
	            otherRecognizer.requireFailure(this);
	        }
	        return this;
	    },

	    /**
	     * drop the requireFailure link. it does not remove the link on the other recognizer.
	     * @param {Recognizer} otherRecognizer
	     * @returns {Recognizer} this
	     */
	    dropRequireFailure: function(otherRecognizer) {
	        if (invokeArrayArg(otherRecognizer, 'dropRequireFailure', this)) {
	            return this;
	        }

	        otherRecognizer = getRecognizerByNameIfManager(otherRecognizer, this);
	        var index = inArray(this.requireFail, otherRecognizer);
	        if (index > -1) {
	            this.requireFail.splice(index, 1);
	        }
	        return this;
	    },

	    /**
	     * has require failures boolean
	     * @returns {boolean}
	     */
	    hasRequireFailures: function() {
	        return this.requireFail.length > 0;
	    },

	    /**
	     * if the recognizer can recognize simultaneous with an other recognizer
	     * @param {Recognizer} otherRecognizer
	     * @returns {Boolean}
	     */
	    canRecognizeWith: function(otherRecognizer) {
	        return !!this.simultaneous[otherRecognizer.id];
	    },

	    /**
	     * You should use `tryEmit` instead of `emit` directly to check
	     * that all the needed recognizers has failed before emitting.
	     * @param {Object} input
	     */
	    emit: function(input) {
	        var self = this;
	        var state = this.state;

	        function emit(event) {
	            self.manager.emit(event, input);
	        }

	        // 'panstart' and 'panmove'
	        if (state < STATE_ENDED) {
	            emit(self.options.event + stateStr(state));
	        }

	        emit(self.options.event); // simple 'eventName' events

	        if (input.additionalEvent) { // additional event(panleft, panright, pinchin, pinchout...)
	            emit(input.additionalEvent);
	        }

	        // panend and pancancel
	        if (state >= STATE_ENDED) {
	            emit(self.options.event + stateStr(state));
	        }
	    },

	    /**
	     * Check that all the require failure recognizers has failed,
	     * if true, it emits a gesture event,
	     * otherwise, setup the state to FAILED.
	     * @param {Object} input
	     */
	    tryEmit: function(input) {
	        if (this.canEmit()) {
	            return this.emit(input);
	        }
	        // it's failing anyway
	        this.state = STATE_FAILED;
	    },

	    /**
	     * can we emit?
	     * @returns {boolean}
	     */
	    canEmit: function() {
	        var i = 0;
	        while (i < this.requireFail.length) {
	            if (!(this.requireFail[i].state & (STATE_FAILED | STATE_POSSIBLE))) {
	                return false;
	            }
	            i++;
	        }
	        return true;
	    },

	    /**
	     * update the recognizer
	     * @param {Object} inputData
	     */
	    recognize: function(inputData) {
	        // make a new copy of the inputData
	        // so we can change the inputData without messing up the other recognizers
	        var inputDataClone = assign({}, inputData);

	        // is is enabled and allow recognizing?
	        if (!boolOrFn(this.options.enable, [this, inputDataClone])) {
	            this.reset();
	            this.state = STATE_FAILED;
	            return;
	        }

	        // reset when we've reached the end
	        if (this.state & (STATE_RECOGNIZED | STATE_CANCELLED | STATE_FAILED)) {
	            this.state = STATE_POSSIBLE;
	        }

	        this.state = this.process(inputDataClone);

	        // the recognizer has recognized a gesture
	        // so trigger an event
	        if (this.state & (STATE_BEGAN | STATE_CHANGED | STATE_ENDED | STATE_CANCELLED)) {
	            this.tryEmit(inputDataClone);
	        }
	    },

	    /**
	     * return the state of the recognizer
	     * the actual recognizing happens in this method
	     * @virtual
	     * @param {Object} inputData
	     * @returns {Const} STATE
	     */
	    process: function(inputData) { }, // jshint ignore:line

	    /**
	     * return the preferred touch-action
	     * @virtual
	     * @returns {Array}
	     */
	    getTouchAction: function() { },

	    /**
	     * called when the gesture isn't allowed to recognize
	     * like when another is being recognized or it is disabled
	     * @virtual
	     */
	    reset: function() { }
	};

	/**
	 * get a usable string, used as event postfix
	 * @param {Const} state
	 * @returns {String} state
	 */
	function stateStr(state) {
	    if (state & STATE_CANCELLED) {
	        return 'cancel';
	    } else if (state & STATE_ENDED) {
	        return 'end';
	    } else if (state & STATE_CHANGED) {
	        return 'move';
	    } else if (state & STATE_BEGAN) {
	        return 'start';
	    }
	    return '';
	}

	/**
	 * direction cons to string
	 * @param {Const} direction
	 * @returns {String}
	 */
	function directionStr(direction) {
	    if (direction == DIRECTION_DOWN) {
	        return 'down';
	    } else if (direction == DIRECTION_UP) {
	        return 'up';
	    } else if (direction == DIRECTION_LEFT) {
	        return 'left';
	    } else if (direction == DIRECTION_RIGHT) {
	        return 'right';
	    }
	    return '';
	}

	/**
	 * get a recognizer by name if it is bound to a manager
	 * @param {Recognizer|String} otherRecognizer
	 * @param {Recognizer} recognizer
	 * @returns {Recognizer}
	 */
	function getRecognizerByNameIfManager(otherRecognizer, recognizer) {
	    var manager = recognizer.manager;
	    if (manager) {
	        return manager.get(otherRecognizer);
	    }
	    return otherRecognizer;
	}

	/**
	 * This recognizer is just used as a base for the simple attribute recognizers.
	 * @constructor
	 * @extends Recognizer
	 */
	function AttrRecognizer() {
	    Recognizer.apply(this, arguments);
	}

	inherit(AttrRecognizer, Recognizer, {
	    /**
	     * @namespace
	     * @memberof AttrRecognizer
	     */
	    defaults: {
	        /**
	         * @type {Number}
	         * @default 1
	         */
	        pointers: 1
	    },

	    /**
	     * Used to check if it the recognizer receives valid input, like input.distance > 10.
	     * @memberof AttrRecognizer
	     * @param {Object} input
	     * @returns {Boolean} recognized
	     */
	    attrTest: function(input) {
	        var optionPointers = this.options.pointers;
	        return optionPointers === 0 || input.pointers.length === optionPointers;
	    },

	    /**
	     * Process the input and return the state for the recognizer
	     * @memberof AttrRecognizer
	     * @param {Object} input
	     * @returns {*} State
	     */
	    process: function(input) {
	        var state = this.state;
	        var eventType = input.eventType;

	        var isRecognized = state & (STATE_BEGAN | STATE_CHANGED);
	        var isValid = this.attrTest(input);

	        // on cancel input and we've recognized before, return STATE_CANCELLED
	        if (isRecognized && (eventType & INPUT_CANCEL || !isValid)) {
	            return state | STATE_CANCELLED;
	        } else if (isRecognized || isValid) {
	            if (eventType & INPUT_END) {
	                return state | STATE_ENDED;
	            } else if (!(state & STATE_BEGAN)) {
	                return STATE_BEGAN;
	            }
	            return state | STATE_CHANGED;
	        }
	        return STATE_FAILED;
	    }
	});

	/**
	 * Pan
	 * Recognized when the pointer is down and moved in the allowed direction.
	 * @constructor
	 * @extends AttrRecognizer
	 */
	function PanRecognizer() {
	    AttrRecognizer.apply(this, arguments);

	    this.pX = null;
	    this.pY = null;
	}

	inherit(PanRecognizer, AttrRecognizer, {
	    /**
	     * @namespace
	     * @memberof PanRecognizer
	     */
	    defaults: {
	        event: 'pan',
	        threshold: 10,
	        pointers: 1,
	        direction: DIRECTION_ALL
	    },

	    getTouchAction: function() {
	        var direction = this.options.direction;
	        var actions = [];
	        if (direction & DIRECTION_HORIZONTAL) {
	            actions.push(TOUCH_ACTION_PAN_Y);
	        }
	        if (direction & DIRECTION_VERTICAL) {
	            actions.push(TOUCH_ACTION_PAN_X);
	        }
	        return actions;
	    },

	    directionTest: function(input) {
	        var options = this.options;
	        var hasMoved = true;
	        var distance = input.distance;
	        var direction = input.direction;
	        var x = input.deltaX;
	        var y = input.deltaY;

	        // lock to axis?
	        if (!(direction & options.direction)) {
	            if (options.direction & DIRECTION_HORIZONTAL) {
	                direction = (x === 0) ? DIRECTION_NONE : (x < 0) ? DIRECTION_LEFT : DIRECTION_RIGHT;
	                hasMoved = x != this.pX;
	                distance = Math.abs(input.deltaX);
	            } else {
	                direction = (y === 0) ? DIRECTION_NONE : (y < 0) ? DIRECTION_UP : DIRECTION_DOWN;
	                hasMoved = y != this.pY;
	                distance = Math.abs(input.deltaY);
	            }
	        }
	        input.direction = direction;
	        return hasMoved && distance > options.threshold && direction & options.direction;
	    },

	    attrTest: function(input) {
	        return AttrRecognizer.prototype.attrTest.call(this, input) &&
	            (this.state & STATE_BEGAN || (!(this.state & STATE_BEGAN) && this.directionTest(input)));
	    },

	    emit: function(input) {

	        this.pX = input.deltaX;
	        this.pY = input.deltaY;

	        var direction = directionStr(input.direction);

	        if (direction) {
	            input.additionalEvent = this.options.event + direction;
	        }
	        this._super.emit.call(this, input);
	    }
	});

	/**
	 * Pinch
	 * Recognized when two or more pointers are moving toward (zoom-in) or away from each other (zoom-out).
	 * @constructor
	 * @extends AttrRecognizer
	 */
	function PinchRecognizer() {
	    AttrRecognizer.apply(this, arguments);
	}

	inherit(PinchRecognizer, AttrRecognizer, {
	    /**
	     * @namespace
	     * @memberof PinchRecognizer
	     */
	    defaults: {
	        event: 'pinch',
	        threshold: 0,
	        pointers: 2
	    },

	    getTouchAction: function() {
	        return [TOUCH_ACTION_NONE];
	    },

	    attrTest: function(input) {
	        return this._super.attrTest.call(this, input) &&
	            (Math.abs(input.scale - 1) > this.options.threshold || this.state & STATE_BEGAN);
	    },

	    emit: function(input) {
	        if (input.scale !== 1) {
	            var inOut = input.scale < 1 ? 'in' : 'out';
	            input.additionalEvent = this.options.event + inOut;
	        }
	        this._super.emit.call(this, input);
	    }
	});

	/**
	 * Press
	 * Recognized when the pointer is down for x ms without any movement.
	 * @constructor
	 * @extends Recognizer
	 */
	function PressRecognizer() {
	    Recognizer.apply(this, arguments);

	    this._timer = null;
	    this._input = null;
	}

	inherit(PressRecognizer, Recognizer, {
	    /**
	     * @namespace
	     * @memberof PressRecognizer
	     */
	    defaults: {
	        event: 'press',
	        pointers: 1,
	        time: 251, // minimal time of the pointer to be pressed
	        threshold: 9 // a minimal movement is ok, but keep it low
	    },

	    getTouchAction: function() {
	        return [TOUCH_ACTION_AUTO];
	    },

	    process: function(input) {
	        var options = this.options;
	        var validPointers = input.pointers.length === options.pointers;
	        var validMovement = input.distance < options.threshold;
	        var validTime = input.deltaTime > options.time;

	        this._input = input;

	        // we only allow little movement
	        // and we've reached an end event, so a tap is possible
	        if (!validMovement || !validPointers || (input.eventType & (INPUT_END | INPUT_CANCEL) && !validTime)) {
	            this.reset();
	        } else if (input.eventType & INPUT_START) {
	            this.reset();
	            this._timer = setTimeoutContext(function() {
	                this.state = STATE_RECOGNIZED;
	                this.tryEmit();
	            }, options.time, this);
	        } else if (input.eventType & INPUT_END) {
	            return STATE_RECOGNIZED;
	        }
	        return STATE_FAILED;
	    },

	    reset: function() {
	        clearTimeout(this._timer);
	    },

	    emit: function(input) {
	        if (this.state !== STATE_RECOGNIZED) {
	            return;
	        }

	        if (input && (input.eventType & INPUT_END)) {
	            this.manager.emit(this.options.event + 'up', input);
	        } else {
	            this._input.timeStamp = now();
	            this.manager.emit(this.options.event, this._input);
	        }
	    }
	});

	/**
	 * Rotate
	 * Recognized when two or more pointer are moving in a circular motion.
	 * @constructor
	 * @extends AttrRecognizer
	 */
	function RotateRecognizer() {
	    AttrRecognizer.apply(this, arguments);
	}

	inherit(RotateRecognizer, AttrRecognizer, {
	    /**
	     * @namespace
	     * @memberof RotateRecognizer
	     */
	    defaults: {
	        event: 'rotate',
	        threshold: 0,
	        pointers: 2
	    },

	    getTouchAction: function() {
	        return [TOUCH_ACTION_NONE];
	    },

	    attrTest: function(input) {
	        return this._super.attrTest.call(this, input) &&
	            (Math.abs(input.rotation) > this.options.threshold || this.state & STATE_BEGAN);
	    }
	});

	/**
	 * Swipe
	 * Recognized when the pointer is moving fast (velocity), with enough distance in the allowed direction.
	 * @constructor
	 * @extends AttrRecognizer
	 */
	function SwipeRecognizer() {
	    AttrRecognizer.apply(this, arguments);
	}

	inherit(SwipeRecognizer, AttrRecognizer, {
	    /**
	     * @namespace
	     * @memberof SwipeRecognizer
	     */
	    defaults: {
	        event: 'swipe',
	        threshold: 10,
	        velocity: 0.3,
	        direction: DIRECTION_HORIZONTAL | DIRECTION_VERTICAL,
	        pointers: 1
	    },

	    getTouchAction: function() {
	        return PanRecognizer.prototype.getTouchAction.call(this);
	    },

	    attrTest: function(input) {
	        var direction = this.options.direction;
	        var velocity;

	        if (direction & (DIRECTION_HORIZONTAL | DIRECTION_VERTICAL)) {
	            velocity = input.overallVelocity;
	        } else if (direction & DIRECTION_HORIZONTAL) {
	            velocity = input.overallVelocityX;
	        } else if (direction & DIRECTION_VERTICAL) {
	            velocity = input.overallVelocityY;
	        }

	        return this._super.attrTest.call(this, input) &&
	            direction & input.offsetDirection &&
	            input.distance > this.options.threshold &&
	            input.maxPointers == this.options.pointers &&
	            abs(velocity) > this.options.velocity && input.eventType & INPUT_END;
	    },

	    emit: function(input) {
	        var direction = directionStr(input.offsetDirection);
	        if (direction) {
	            this.manager.emit(this.options.event + direction, input);
	        }

	        this.manager.emit(this.options.event, input);
	    }
	});

	/**
	 * A tap is ecognized when the pointer is doing a small tap/click. Multiple taps are recognized if they occur
	 * between the given interval and position. The delay option can be used to recognize multi-taps without firing
	 * a single tap.
	 *
	 * The eventData from the emitted event contains the property `tapCount`, which contains the amount of
	 * multi-taps being recognized.
	 * @constructor
	 * @extends Recognizer
	 */
	function TapRecognizer() {
	    Recognizer.apply(this, arguments);

	    // previous time and center,
	    // used for tap counting
	    this.pTime = false;
	    this.pCenter = false;

	    this._timer = null;
	    this._input = null;
	    this.count = 0;
	}

	inherit(TapRecognizer, Recognizer, {
	    /**
	     * @namespace
	     * @memberof PinchRecognizer
	     */
	    defaults: {
	        event: 'tap',
	        pointers: 1,
	        taps: 1,
	        interval: 300, // max time between the multi-tap taps
	        time: 250, // max time of the pointer to be down (like finger on the screen)
	        threshold: 9, // a minimal movement is ok, but keep it low
	        posThreshold: 10 // a multi-tap can be a bit off the initial position
	    },

	    getTouchAction: function() {
	        return [TOUCH_ACTION_MANIPULATION];
	    },

	    process: function(input) {
	        var options = this.options;

	        var validPointers = input.pointers.length === options.pointers;
	        var validMovement = input.distance < options.threshold;
	        var validTouchTime = input.deltaTime < options.time;

	        this.reset();

	        if ((input.eventType & INPUT_START) && (this.count === 0)) {
	            return this.failTimeout();
	        }

	        // we only allow little movement
	        // and we've reached an end event, so a tap is possible
	        if (validMovement && validTouchTime && validPointers) {
	            if (input.eventType != INPUT_END) {
	                return this.failTimeout();
	            }

	            var validInterval = this.pTime ? (input.timeStamp - this.pTime < options.interval) : true;
	            var validMultiTap = !this.pCenter || getDistance(this.pCenter, input.center) < options.posThreshold;

	            this.pTime = input.timeStamp;
	            this.pCenter = input.center;

	            if (!validMultiTap || !validInterval) {
	                this.count = 1;
	            } else {
	                this.count += 1;
	            }

	            this._input = input;

	            // if tap count matches we have recognized it,
	            // else it has began recognizing...
	            var tapCount = this.count % options.taps;
	            if (tapCount === 0) {
	                // no failing requirements, immediately trigger the tap event
	                // or wait as long as the multitap interval to trigger
	                if (!this.hasRequireFailures()) {
	                    return STATE_RECOGNIZED;
	                } else {
	                    this._timer = setTimeoutContext(function() {
	                        this.state = STATE_RECOGNIZED;
	                        this.tryEmit();
	                    }, options.interval, this);
	                    return STATE_BEGAN;
	                }
	            }
	        }
	        return STATE_FAILED;
	    },

	    failTimeout: function() {
	        this._timer = setTimeoutContext(function() {
	            this.state = STATE_FAILED;
	        }, this.options.interval, this);
	        return STATE_FAILED;
	    },

	    reset: function() {
	        clearTimeout(this._timer);
	    },

	    emit: function() {
	        if (this.state == STATE_RECOGNIZED) {
	            this._input.tapCount = this.count;
	            this.manager.emit(this.options.event, this._input);
	        }
	    }
	});

	/**
	 * Simple way to create a manager with a default set of recognizers.
	 * @param {HTMLElement} element
	 * @param {Object} [options]
	 * @constructor
	 */
	function Hammer(element, options) {
	    options = options || {};
	    options.recognizers = ifUndefined(options.recognizers, Hammer.defaults.preset);
	    return new Manager(element, options);
	}

	/**
	 * @const {string}
	 */
	Hammer.VERSION = '2.0.7';

	/**
	 * default settings
	 * @namespace
	 */
	Hammer.defaults = {
	    /**
	     * set if DOM events are being triggered.
	     * But this is slower and unused by simple implementations, so disabled by default.
	     * @type {Boolean}
	     * @default false
	     */
	    domEvents: false,

	    /**
	     * The value for the touchAction property/fallback.
	     * When set to `compute` it will magically set the correct value based on the added recognizers.
	     * @type {String}
	     * @default compute
	     */
	    touchAction: TOUCH_ACTION_COMPUTE,

	    /**
	     * @type {Boolean}
	     * @default true
	     */
	    enable: true,

	    /**
	     * EXPERIMENTAL FEATURE -- can be removed/changed
	     * Change the parent input target element.
	     * If Null, then it is being set the to main element.
	     * @type {Null|EventTarget}
	     * @default null
	     */
	    inputTarget: null,

	    /**
	     * force an input class
	     * @type {Null|Function}
	     * @default null
	     */
	    inputClass: null,

	    /**
	     * Default recognizer setup when calling `Hammer()`
	     * When creating a new Manager these will be skipped.
	     * @type {Array}
	     */
	    preset: [
	        // RecognizerClass, options, [recognizeWith, ...], [requireFailure, ...]
	        [RotateRecognizer, {enable: false}],
	        [PinchRecognizer, {enable: false}, ['rotate']],
	        [SwipeRecognizer, {direction: DIRECTION_HORIZONTAL}],
	        [PanRecognizer, {direction: DIRECTION_HORIZONTAL}, ['swipe']],
	        [TapRecognizer],
	        [TapRecognizer, {event: 'doubletap', taps: 2}, ['tap']],
	        [PressRecognizer]
	    ],

	    /**
	     * Some CSS properties can be used to improve the working of Hammer.
	     * Add them to this method and they will be set when creating a new Manager.
	     * @namespace
	     */
	    cssProps: {
	        /**
	         * Disables text selection to improve the dragging gesture. Mainly for desktop browsers.
	         * @type {String}
	         * @default 'none'
	         */
	        userSelect: 'none',

	        /**
	         * Disable the Windows Phone grippers when pressing an element.
	         * @type {String}
	         * @default 'none'
	         */
	        touchSelect: 'none',

	        /**
	         * Disables the default callout shown when you touch and hold a touch target.
	         * On iOS, when you touch and hold a touch target such as a link, Safari displays
	         * a callout containing information about the link. This property allows you to disable that callout.
	         * @type {String}
	         * @default 'none'
	         */
	        touchCallout: 'none',

	        /**
	         * Specifies whether zooming is enabled. Used by IE10>
	         * @type {String}
	         * @default 'none'
	         */
	        contentZooming: 'none',

	        /**
	         * Specifies that an entire element should be draggable instead of its contents. Mainly for desktop browsers.
	         * @type {String}
	         * @default 'none'
	         */
	        userDrag: 'none',

	        /**
	         * Overrides the highlight color shown when the user taps a link or a JavaScript
	         * clickable element in iOS. This property obeys the alpha value, if specified.
	         * @type {String}
	         * @default 'rgba(0,0,0,0)'
	         */
	        tapHighlightColor: 'rgba(0,0,0,0)'
	    }
	};

	var STOP = 1;
	var FORCED_STOP = 2;

	/**
	 * Manager
	 * @param {HTMLElement} element
	 * @param {Object} [options]
	 * @constructor
	 */
	function Manager(element, options) {
	    this.options = assign({}, Hammer.defaults, options || {});

	    this.options.inputTarget = this.options.inputTarget || element;

	    this.handlers = {};
	    this.session = {};
	    this.recognizers = [];
	    this.oldCssProps = {};

	    this.element = element;
	    this.input = createInputInstance(this);
	    this.touchAction = new TouchAction(this, this.options.touchAction);

	    toggleCssProps(this, true);

	    each(this.options.recognizers, function(item) {
	        var recognizer = this.add(new (item[0])(item[1]));
	        item[2] && recognizer.recognizeWith(item[2]);
	        item[3] && recognizer.requireFailure(item[3]);
	    }, this);
	}

	Manager.prototype = {
	    /**
	     * set options
	     * @param {Object} options
	     * @returns {Manager}
	     */
	    set: function(options) {
	        assign(this.options, options);

	        // Options that need a little more setup
	        if (options.touchAction) {
	            this.touchAction.update();
	        }
	        if (options.inputTarget) {
	            // Clean up existing event listeners and reinitialize
	            this.input.destroy();
	            this.input.target = options.inputTarget;
	            this.input.init();
	        }
	        return this;
	    },

	    /**
	     * stop recognizing for this session.
	     * This session will be discarded, when a new [input]start event is fired.
	     * When forced, the recognizer cycle is stopped immediately.
	     * @param {Boolean} [force]
	     */
	    stop: function(force) {
	        this.session.stopped = force ? FORCED_STOP : STOP;
	    },

	    /**
	     * run the recognizers!
	     * called by the inputHandler function on every movement of the pointers (touches)
	     * it walks through all the recognizers and tries to detect the gesture that is being made
	     * @param {Object} inputData
	     */
	    recognize: function(inputData) {
	        var session = this.session;
	        if (session.stopped) {
	            return;
	        }

	        // run the touch-action polyfill
	        this.touchAction.preventDefaults(inputData);

	        var recognizer;
	        var recognizers = this.recognizers;

	        // this holds the recognizer that is being recognized.
	        // so the recognizer's state needs to be BEGAN, CHANGED, ENDED or RECOGNIZED
	        // if no recognizer is detecting a thing, it is set to `null`
	        var curRecognizer = session.curRecognizer;

	        // reset when the last recognizer is recognized
	        // or when we're in a new session
	        if (!curRecognizer || (curRecognizer && curRecognizer.state & STATE_RECOGNIZED)) {
	            curRecognizer = session.curRecognizer = null;
	        }

	        var i = 0;
	        while (i < recognizers.length) {
	            recognizer = recognizers[i];

	            // find out if we are allowed try to recognize the input for this one.
	            // 1.   allow if the session is NOT forced stopped (see the .stop() method)
	            // 2.   allow if we still haven't recognized a gesture in this session, or the this recognizer is the one
	            //      that is being recognized.
	            // 3.   allow if the recognizer is allowed to run simultaneous with the current recognized recognizer.
	            //      this can be setup with the `recognizeWith()` method on the recognizer.
	            if (session.stopped !== FORCED_STOP && ( // 1
	                    !curRecognizer || recognizer == curRecognizer || // 2
	                    recognizer.canRecognizeWith(curRecognizer))) { // 3
	                recognizer.recognize(inputData);
	            } else {
	                recognizer.reset();
	            }

	            // if the recognizer has been recognizing the input as a valid gesture, we want to store this one as the
	            // current active recognizer. but only if we don't already have an active recognizer
	            if (!curRecognizer && recognizer.state & (STATE_BEGAN | STATE_CHANGED | STATE_ENDED)) {
	                curRecognizer = session.curRecognizer = recognizer;
	            }
	            i++;
	        }
	    },

	    /**
	     * get a recognizer by its event name.
	     * @param {Recognizer|String} recognizer
	     * @returns {Recognizer|Null}
	     */
	    get: function(recognizer) {
	        if (recognizer instanceof Recognizer) {
	            return recognizer;
	        }

	        var recognizers = this.recognizers;
	        for (var i = 0; i < recognizers.length; i++) {
	            if (recognizers[i].options.event == recognizer) {
	                return recognizers[i];
	            }
	        }
	        return null;
	    },

	    /**
	     * add a recognizer to the manager
	     * existing recognizers with the same event name will be removed
	     * @param {Recognizer} recognizer
	     * @returns {Recognizer|Manager}
	     */
	    add: function(recognizer) {
	        if (invokeArrayArg(recognizer, 'add', this)) {
	            return this;
	        }

	        // remove existing
	        var existing = this.get(recognizer.options.event);
	        if (existing) {
	            this.remove(existing);
	        }

	        this.recognizers.push(recognizer);
	        recognizer.manager = this;

	        this.touchAction.update();
	        return recognizer;
	    },

	    /**
	     * remove a recognizer by name or instance
	     * @param {Recognizer|String} recognizer
	     * @returns {Manager}
	     */
	    remove: function(recognizer) {
	        if (invokeArrayArg(recognizer, 'remove', this)) {
	            return this;
	        }

	        recognizer = this.get(recognizer);

	        // let's make sure this recognizer exists
	        if (recognizer) {
	            var recognizers = this.recognizers;
	            var index = inArray(recognizers, recognizer);

	            if (index !== -1) {
	                recognizers.splice(index, 1);
	                this.touchAction.update();
	            }
	        }

	        return this;
	    },

	    /**
	     * bind event
	     * @param {String} events
	     * @param {Function} handler
	     * @returns {EventEmitter} this
	     */
	    on: function(events, handler) {
	        if (events === undefined$1) {
	            return;
	        }
	        if (handler === undefined$1) {
	            return;
	        }

	        var handlers = this.handlers;
	        each(splitStr(events), function(event) {
	            handlers[event] = handlers[event] || [];
	            handlers[event].push(handler);
	        });
	        return this;
	    },

	    /**
	     * unbind event, leave emit blank to remove all handlers
	     * @param {String} events
	     * @param {Function} [handler]
	     * @returns {EventEmitter} this
	     */
	    off: function(events, handler) {
	        if (events === undefined$1) {
	            return;
	        }

	        var handlers = this.handlers;
	        each(splitStr(events), function(event) {
	            if (!handler) {
	                delete handlers[event];
	            } else {
	                handlers[event] && handlers[event].splice(inArray(handlers[event], handler), 1);
	            }
	        });
	        return this;
	    },

	    /**
	     * emit event to the listeners
	     * @param {String} event
	     * @param {Object} data
	     */
	    emit: function(event, data) {
	        // we also want to trigger dom events
	        if (this.options.domEvents) {
	            triggerDomEvent(event, data);
	        }

	        // no handlers, so skip it all
	        var handlers = this.handlers[event] && this.handlers[event].slice();
	        if (!handlers || !handlers.length) {
	            return;
	        }

	        data.type = event;
	        data.preventDefault = function() {
	            data.srcEvent.preventDefault();
	        };

	        var i = 0;
	        while (i < handlers.length) {
	            handlers[i](data);
	            i++;
	        }
	    },

	    /**
	     * destroy the manager and unbinds all events
	     * it doesn't unbind dom events, that is the user own responsibility
	     */
	    destroy: function() {
	        this.element && toggleCssProps(this, false);

	        this.handlers = {};
	        this.session = {};
	        this.input.destroy();
	        this.element = null;
	    }
	};

	/**
	 * add/remove the css properties as defined in manager.options.cssProps
	 * @param {Manager} manager
	 * @param {Boolean} add
	 */
	function toggleCssProps(manager, add) {
	    var element = manager.element;
	    if (!element.style) {
	        return;
	    }
	    var prop;
	    each(manager.options.cssProps, function(value, name) {
	        prop = prefixed(element.style, name);
	        if (add) {
	            manager.oldCssProps[prop] = element.style[prop];
	            element.style[prop] = value;
	        } else {
	            element.style[prop] = manager.oldCssProps[prop] || '';
	        }
	    });
	    if (!add) {
	        manager.oldCssProps = {};
	    }
	}

	/**
	 * trigger dom event
	 * @param {String} event
	 * @param {Object} data
	 */
	function triggerDomEvent(event, data) {
	    var gestureEvent = document.createEvent('Event');
	    gestureEvent.initEvent(event, true, true);
	    gestureEvent.gesture = data;
	    data.target.dispatchEvent(gestureEvent);
	}

	assign(Hammer, {
	    INPUT_START: INPUT_START,
	    INPUT_MOVE: INPUT_MOVE,
	    INPUT_END: INPUT_END,
	    INPUT_CANCEL: INPUT_CANCEL,

	    STATE_POSSIBLE: STATE_POSSIBLE,
	    STATE_BEGAN: STATE_BEGAN,
	    STATE_CHANGED: STATE_CHANGED,
	    STATE_ENDED: STATE_ENDED,
	    STATE_RECOGNIZED: STATE_RECOGNIZED,
	    STATE_CANCELLED: STATE_CANCELLED,
	    STATE_FAILED: STATE_FAILED,

	    DIRECTION_NONE: DIRECTION_NONE,
	    DIRECTION_LEFT: DIRECTION_LEFT,
	    DIRECTION_RIGHT: DIRECTION_RIGHT,
	    DIRECTION_UP: DIRECTION_UP,
	    DIRECTION_DOWN: DIRECTION_DOWN,
	    DIRECTION_HORIZONTAL: DIRECTION_HORIZONTAL,
	    DIRECTION_VERTICAL: DIRECTION_VERTICAL,
	    DIRECTION_ALL: DIRECTION_ALL,

	    Manager: Manager,
	    Input: Input,
	    TouchAction: TouchAction,

	    TouchInput: TouchInput,
	    MouseInput: MouseInput,
	    PointerEventInput: PointerEventInput,
	    TouchMouseInput: TouchMouseInput,
	    SingleTouchInput: SingleTouchInput,

	    Recognizer: Recognizer,
	    AttrRecognizer: AttrRecognizer,
	    Tap: TapRecognizer,
	    Pan: PanRecognizer,
	    Swipe: SwipeRecognizer,
	    Pinch: PinchRecognizer,
	    Rotate: RotateRecognizer,
	    Press: PressRecognizer,

	    on: addEventListeners,
	    off: removeEventListeners,
	    each: each,
	    merge: merge,
	    extend: extend,
	    assign: assign,
	    inherit: inherit,
	    bindFn: bindFn,
	    prefixed: prefixed
	});

	// this prevents errors when Hammer is loaded in the presence of an AMD
	//  style loader but by script tag, not by the loader.
	var freeGlobal = (typeof window !== 'undefined' ? window : (typeof self !== 'undefined' ? self : {})); // jshint ignore:line
	freeGlobal.Hammer = Hammer;

	if (typeof undefined$1 === 'function' && undefined$1.amd) {
	    undefined$1(function() {
	        return Hammer;
	    });
	} else if ( module.exports) {
	    module.exports = Hammer;
	} else {
	    window[exportName] = Hammer;
	}

	})(window, document, 'Hammer');
	});

	var MIN_ZOOM = 0.2,
	    MAX_ZOOM = 4;

	var mouseEvents = [
	  'mousedown',
	  'mouseup',
	  'mouseover',
	  'mouseout',
	  'click',
	  'dblclick'
	];

	function get$1(service, injector) {
	  return injector.get(service, false);
	}

	function stopEvent(event) {

	  event.preventDefault();
	  event.stopPropagation();

	  if (typeof event.stopImmediatePropagation === 'function') {
	    event.stopImmediatePropagation();
	  }
	}


	function createTouchRecognizer(node) {

	  function stopMouse(event) {

	    forEach(mouseEvents, function(e) {
	      componentEvent.bind(node, e, stopEvent, true);
	    });
	  }

	  function allowMouse(event) {
	    setTimeout(function() {
	      forEach(mouseEvents, function(e) {
	        componentEvent.unbind(node, e, stopEvent, true);
	      });
	    }, 500);
	  }

	  componentEvent.bind(node, 'touchstart', stopMouse, true);
	  componentEvent.bind(node, 'touchend', allowMouse, true);
	  componentEvent.bind(node, 'touchcancel', allowMouse, true);

	  // A touch event recognizer that handles
	  // touch events only (we know, we can already handle
	  // mouse events out of the box)

	  var recognizer = new hammer.Manager(node, {
	    inputClass: hammer.TouchInput,
	    recognizers: []
	  });


	  var tap = new hammer.Tap();
	  var pan = new hammer.Pan({ threshold: 10 });
	  var press = new hammer.Press();
	  var pinch = new hammer.Pinch();

	  var doubleTap = new hammer.Tap({ event: 'doubletap', taps: 2 });

	  pinch.requireFailure(pan);
	  pinch.requireFailure(press);

	  recognizer.add([ pan, press, pinch, doubleTap, tap ]);

	  recognizer.reset = function(force) {
	    var recognizers = this.recognizers,
	        session = this.session;

	    if (session.stopped) {
	      return;
	    }

	    recognizer.stop(force);

	    setTimeout(function() {
	      var i, r;
	      for (i = 0; (r = recognizers[i]); i++) {
	        r.reset();
	        r.state = 8; // FAILED STATE
	      }

	      session.curRecognizer = null;
	    }, 0);
	  };

	  recognizer.on('hammer.input', function(event) {
	    if (event.srcEvent.defaultPrevented) {
	      recognizer.reset(true);
	    }
	  });

	  return recognizer;
	}

	/**
	 * A plugin that provides touch events for elements.
	 *
	 * @param {EventBus} eventBus
	 * @param {InteractionEvents} interactionEvents
	 */
	function TouchInteractionEvents(
	    injector, canvas, eventBus,
	    elementRegistry, interactionEvents) {

	  // optional integrations
	  var dragging = get$1('dragging', injector),
	      move = get$1('move', injector),
	      contextPad = get$1('contextPad', injector),
	      palette = get$1('palette', injector);

	  // the touch recognizer
	  var recognizer;

	  function handler(type) {

	    return function(event) {

	      interactionEvents.fire(type, event);
	    };
	  }

	  function getGfx(target) {
	    var node = closest(target, 'svg, .djs-element', true);
	    return node;
	  }

	  function initEvents(svg) {

	    // touch recognizer
	    recognizer = createTouchRecognizer(svg);

	    recognizer.on('doubletap', handler('element.dblclick'));

	    recognizer.on('tap', handler('element.click'));

	    function startGrabCanvas(event) {

	      var lx = 0, ly = 0;

	      function update(e) {

	        var dx = e.deltaX - lx,
	            dy = e.deltaY - ly;

	        canvas.scroll({ dx: dx, dy: dy });

	        lx = e.deltaX;
	        ly = e.deltaY;
	      }

	      function end(e) {
	        recognizer.off('panmove', update);
	        recognizer.off('panend', end);
	        recognizer.off('pancancel', end);
	      }

	      recognizer.on('panmove', update);
	      recognizer.on('panend', end);
	      recognizer.on('pancancel', end);
	    }

	    function startGrab(event) {

	      var gfx = getGfx(event.target),
	          element = gfx && elementRegistry.get(gfx);

	      // recognizer
	      if (move && canvas.getRootElement() !== element) {
	        return move.start(event, element, true);
	      } else {
	        startGrabCanvas();
	      }
	    }

	    function startZoom(e) {

	      var zoom = canvas.zoom(),
	          mid = e.center;

	      function update(e) {

	        var ratio = 1 - (1 - e.scale) / 1.50,
	            newZoom = Math.max(MIN_ZOOM, Math.min(MAX_ZOOM, ratio * zoom));

	        canvas.zoom(newZoom, mid);

	        stopEvent(e);
	      }

	      function end(e) {
	        recognizer.off('pinchmove', update);
	        recognizer.off('pinchend', end);
	        recognizer.off('pinchcancel', end);

	        recognizer.reset(true);
	      }

	      recognizer.on('pinchmove', update);
	      recognizer.on('pinchend', end);
	      recognizer.on('pinchcancel', end);
	    }

	    recognizer.on('panstart', startGrab);
	    recognizer.on('press', startGrab);

	    recognizer.on('pinchstart', startZoom);
	  }

	  if (dragging) {

	    // simulate hover during dragging
	    eventBus.on('drag.move', function(event) {

	      var originalEvent = event.originalEvent;

	      if (!originalEvent || originalEvent instanceof MouseEvent) {
	        return;
	      }

	      var position = toPoint(originalEvent);

	      // this gets really expensive ...
	      var node = document.elementFromPoint(position.x, position.y),
	          gfx = getGfx(node),
	          element = gfx && elementRegistry.get(gfx);

	      if (element !== event.hover) {
	        if (event.hover) {
	          dragging.out(event);
	        }

	        if (element) {
	          dragging.hover({ element: element, gfx: gfx });

	          event.hover = element;
	          event.hoverGfx = gfx;
	        }
	      }
	    });
	  }

	  if (contextPad) {

	    eventBus.on('contextPad.create', function(event) {
	      var node = event.pad.html;

	      // touch recognizer
	      var padRecognizer = createTouchRecognizer(node);

	      padRecognizer.on('panstart', function(event) {
	        contextPad.trigger('dragstart', event, true);
	      });

	      padRecognizer.on('press', function(event) {
	        contextPad.trigger('dragstart', event, true);
	      });

	      padRecognizer.on('tap', function(event) {
	        contextPad.trigger('click', event);
	      });
	    });
	  }

	  if (palette) {
	    eventBus.on('palette.create', function(event) {
	      var node = event.container;

	      // touch recognizer
	      var padRecognizer = createTouchRecognizer(node);

	      padRecognizer.on('panstart', function(event) {
	        palette.trigger('dragstart', event, true);
	      });

	      padRecognizer.on('press', function(event) {
	        palette.trigger('dragstart', event, true);
	      });

	      padRecognizer.on('tap', function(event) {
	        palette.trigger('click', event);
	      });
	    });
	  }

	  eventBus.on('canvas.init', function(event) {
	    initEvents(event.svg);
	  });
	}


	TouchInteractionEvents.$inject = [
	  'injector',
	  'canvas',
	  'eventBus',
	  'elementRegistry',
	  'interactionEvents',
	  'touchFix'
	];

	function TouchFix(canvas, eventBus) {

	  var self = this;

	  eventBus.on('canvas.init', function(e) {
	    self.addBBoxMarker(e.svg);
	  });
	}

	TouchFix.$inject = [ 'canvas', 'eventBus' ];


	/**
	 * Safari mobile (iOS 7) does not fire touchstart event in <SVG> element
	 * if there is no shape between 0,0 and viewport elements origin.
	 *
	 * So touchstart event is only fired when the <g class="viewport"> element was hit.
	 * Putting an element over and below the 'viewport' fixes that behavior.
	 */
	TouchFix.prototype.addBBoxMarker = function(svg) {

	  var markerStyle = {
	    fill: 'none',
	    class: 'outer-bound-marker'
	  };

	  var rect1 = create('rect');
	  attr$1(rect1, {
	    x: -10000,
	    y: 10000,
	    width: 10,
	    height: 10
	  });
	  attr$1(rect1, markerStyle);

	  append(svg, rect1);

	  var rect2 = create('rect');
	  attr$1(rect2, {
	    x: 10000,
	    y: 10000,
	    width: 10,
	    height: 10
	  });
	  attr$1(rect2, markerStyle);

	  append(svg, rect2);
	};

	var TouchModule = {
	  __depends__: [ InteractionEventsModule ],
	  __init__: [ 'touchInteractionEvents' ],
	  touchInteractionEvents: [ 'type', TouchInteractionEvents ],
	  touchFix: [ 'type', TouchFix ]
	};

	var TouchModule$1 = {
	  __depends__: [
	    TouchModule
	  ]
	};

	function last(arr) {
	  return arr && arr[arr.length - 1];
	}

	function sortTopOrMiddle(element) {
	  return element.y;
	}

	function sortLeftOrCenter(element) {
	  return element.x;
	}

	/**
	 * Sorting functions for different types of alignment
	 *
	 * @type {Object}
	 *
	 * @return {Function}
	 */
	var ALIGNMENT_SORTING = {
	  left: sortLeftOrCenter,
	  center: sortLeftOrCenter,
	  right: function(element) {
	    return element.x + element.width;
	  },
	  top: sortTopOrMiddle,
	  middle: sortTopOrMiddle,
	  bottom: function(element) {
	    return element.y + element.height;
	  }
	};


	function AlignElements(modeling) {
	  this._modeling = modeling;
	}

	AlignElements.$inject = [ 'modeling' ];


	/**
	 * Get the relevant "axis" and "dimension" related to the current type of alignment
	 *
	 * @param  {String} type left|right|center|top|bottom|middle
	 *
	 * @return {Object} { axis, dimension }
	 */
	AlignElements.prototype._getOrientationDetails = function(type) {
	  var vertical = [ 'top', 'bottom', 'middle' ],
	      axis = 'x',
	      dimension = 'width';

	  if (vertical.indexOf(type) !== -1) {
	    axis = 'y';
	    dimension = 'height';
	  }

	  return {
	    axis: axis,
	    dimension: dimension
	  };
	};

	AlignElements.prototype._isType = function(type, types) {
	  return types.indexOf(type) !== -1;
	};

	/**
	 * Get a point on the relevant axis where elements should align to
	 *
	 * @param  {String} type left|right|center|top|bottom|middle
	 * @param  {Array} sortedElements
	 *
	 * @return {Object}
	 */
	AlignElements.prototype._alignmentPosition = function(type, sortedElements) {
	  var orientation = this._getOrientationDetails(type),
	      axis = orientation.axis,
	      dimension = orientation.dimension,
	      alignment = {},
	      centers = {},
	      hasSharedCenters = false,
	      centeredElements,
	      firstElement,
	      lastElement;

	  function getMiddleOrTop(first, last) {
	    return Math.round((first[axis] + last[axis] + last[dimension]) / 2);
	  }

	  if (this._isType(type, [ 'left', 'top' ])) {
	    alignment[type] = sortedElements[0][axis];

	  } else if (this._isType(type, [ 'right', 'bottom' ])) {
	    lastElement = last(sortedElements);

	    alignment[type] = lastElement[axis] + lastElement[dimension];

	  } else if (this._isType(type, [ 'center', 'middle' ])) {

	    // check if there is a center shared by more than one shape
	    // if not, just take the middle of the range
	    forEach(sortedElements, function(element) {
	      var center = element[axis] + Math.round(element[dimension] / 2);

	      if (centers[center]) {
	        centers[center].elements.push(element);
	      } else {
	        centers[center] = {
	          elements: [ element ],
	          center: center
	        };
	      }
	    });

	    centeredElements = sortBy(centers, function(center) {
	      if (center.elements.length > 1) {
	        hasSharedCenters = true;
	      }

	      return center.elements.length;
	    });

	    if (hasSharedCenters) {
	      alignment[type] = last(centeredElements).center;

	      return alignment;
	    }

	    firstElement = sortedElements[0];

	    sortedElements = sortBy(sortedElements, function(element) {
	      return element[axis] + element[dimension];
	    });

	    lastElement = last(sortedElements);

	    alignment[type] = getMiddleOrTop(firstElement, lastElement);
	  }

	  return alignment;
	};

	/**
	 * Executes the alignment of a selection of elements
	 *
	 * @param  {Array} elements [description]
	 * @param  {String} type left|right|center|top|bottom|middle
	 */
	AlignElements.prototype.trigger = function(elements, type) {
	  var modeling = this._modeling;

	  var filteredElements = filter(elements, function(element) {
	    return !(element.waypoints || element.host || element.labelTarget);
	  });

	  var sortFn = ALIGNMENT_SORTING[type];

	  var sortedElements = sortBy(filteredElements, sortFn);

	  var alignment = this._alignmentPosition(type, sortedElements);

	  modeling.alignElements(sortedElements, alignment);
	};

	var AlignElementsModule = {
	  __init__: [ 'alignElements' ],
	  alignElements: [ 'type', AlignElements ]
	};

	/**
	 * Return true if element has any of the given types.
	 *
	 * @param {djs.model.Base} element
	 * @param {Array<String>} types
	 *
	 * @return {Boolean}
	 */
	function isAny(element, types) {
	  return some(types, function(t) {
	    return is$1(element, t);
	  });
	}


	/**
	 * Return the parent of the element with any of the given types.
	 *
	 * @param {djs.model.Base} element
	 * @param {String|Array<String>} anyType
	 *
	 * @return {djs.model.Base}
	 */
	function getParent(element, anyType) {

	  if (typeof anyType === 'string') {
	    anyType = [ anyType ];
	  }

	  while ((element = element.parent)) {
	    if (isAny(element, anyType)) {
	      return element;
	    }
	  }

	  return null;
	}

	var DEFAULT_HORIZONTAL_DISTANCE = 50;

	var MAX_HORIZONTAL_DISTANCE = 250;

	// padding to detect element placement
	var PLACEMENT_DETECTION_PAD = 10;

	/**
	 * Find the new position for the target element to
	 * connect to source.
	 *
	 * @param  {djs.model.Shape} source
	 * @param  {djs.model.Shape} element
	 *
	 * @return {Point}
	 */
	function getNewShapePosition(source, element) {

	  if (is$1(element, 'bpmn:TextAnnotation')) {
	    return getTextAnnotationPosition(source, element);
	  }

	  if (isAny(element, [ 'bpmn:DataObjectReference', 'bpmn:DataStoreReference' ])) {
	    return getDataElementPosition(source, element);
	  }

	  if (is$1(element, 'bpmn:FlowNode')) {
	    return getFlowNodePosition(source, element);
	  }

	  return getDefaultPosition(source, element);
	}

	/**
	 * Always try to place element right of source;
	 * compute actual distance from previous nodes in flow.
	 */
	function getFlowNodePosition(source, element) {

	  var sourceTrbl = asTRBL(source);
	  var sourceMid = getMid(source);

	  var horizontalDistance = getFlowNodeDistance(source);

	  var orientation = 'left',
	      rowSize = 80,
	      margin = 30;

	  if (is$1(source, 'bpmn:BoundaryEvent')) {
	    orientation = getOrientation(source, source.host, -25);

	    if (orientation.indexOf('top') !== -1) {
	      margin *= -1;
	    }
	  }

	  function getVerticalDistance(orient) {
	    if (orient.indexOf('top') != -1) {
	      return -1 * rowSize;
	    } else if (orient.indexOf('bottom') != -1) {
	      return rowSize;
	    } else {
	      return 0;
	    }
	  }

	  var position = {
	    x: sourceTrbl.right + horizontalDistance + element.width / 2,
	    y: sourceMid.y + getVerticalDistance(orientation)
	  };

	  var escapeDirection = {
	    y: {
	      margin: margin,
	      rowSize: rowSize
	    }
	  };

	  return deconflictPosition(source, element, position, escapeDirection);
	}


	/**
	 * Compute best distance between source and target,
	 * based on existing connections to and from source.
	 *
	 * @param {djs.model.Shape} source
	 * @param {djs.model.Shape} element
	 *
	 * @return {Number} distance
	 */
	function getFlowNodeDistance(source, element) {

	  var sourceTrbl = asTRBL(source);

	  // is connection a reference to consider?
	  function isReference(c) {
	    return is$1(c, 'bpmn:SequenceFlow');
	  }

	  function toTargetNode(weight) {

	    return function(shape) {
	      return {
	        shape: shape,
	        weight: weight,
	        distanceTo: function(shape) {
	          var shapeTrbl = asTRBL(shape);

	          return shapeTrbl.left - sourceTrbl.right;
	        }
	      };
	    };
	  }

	  function toSourceNode(weight) {
	    return function(shape) {
	      return {
	        shape: shape,
	        weight: weight,
	        distanceTo: function(shape) {
	          var shapeTrbl = asTRBL(shape);

	          return sourceTrbl.left - shapeTrbl.right;
	        }
	      };
	    };
	  }

	  // we create a list of nodes to take into consideration
	  // for calculating the optimal flow node distance
	  //
	  //   * weight existing target nodes higher than source nodes
	  //   * only take into account individual nodes once
	  //
	  var nodes = reduce([].concat(
	    getTargets(source, isReference).map(toTargetNode(5)),
	    getSources(source, isReference).map(toSourceNode(1))
	  ), function(nodes, node) {
	    // filter out shapes connected twice via source or target
	    nodes[node.shape.id + '__weight_' + node.weight] = node;

	    return nodes;
	  }, {});

	  // compute distances between source and incoming nodes;
	  // group at the same time by distance and expose the
	  // favourite distance as { fav: { count, value } }.
	  var distancesGrouped = reduce(nodes, function(result, node) {

	    var shape = node.shape,
	        weight = node.weight,
	        distanceTo = node.distanceTo;

	    var fav = result.fav,
	        currentDistance,
	        currentDistanceCount,
	        currentDistanceEntry;

	    currentDistance = distanceTo(shape);

	    // ignore too far away peers
	    // or non-left to right modeled nodes
	    if (currentDistance < 0 || currentDistance > MAX_HORIZONTAL_DISTANCE) {
	      return result;
	    }

	    currentDistanceEntry = result[String(currentDistance)] =
	      result[String(currentDistance)] || {
	        value: currentDistance,
	        count: 0
	      };

	    // inc diff count
	    currentDistanceCount = currentDistanceEntry.count += 1 * weight;

	    if (!fav || fav.count < currentDistanceCount) {
	      result.fav = currentDistanceEntry;
	    }

	    return result;
	  }, { });


	  if (distancesGrouped.fav) {
	    return distancesGrouped.fav.value;
	  } else {
	    return DEFAULT_HORIZONTAL_DISTANCE;
	  }
	}


	/**
	 * Always try to place text annotations top right of source.
	 */
	function getTextAnnotationPosition(source, element) {

	  var sourceTrbl = asTRBL(source);

	  var position = {
	    x: sourceTrbl.right + element.width / 2,
	    y: sourceTrbl.top - 50 - element.height / 2
	  };

	  var escapeDirection = {
	    y: {
	      margin: -30,
	      rowSize: 20
	    }
	  };

	  return deconflictPosition(source, element, position, escapeDirection);
	}


	/**
	 * Always put element bottom right of source.
	 */
	function getDataElementPosition(source, element) {

	  var sourceTrbl = asTRBL(source);

	  var position = {
	    x: sourceTrbl.right - 10 + element.width / 2,
	    y: sourceTrbl.bottom + 40 + element.width / 2
	  };

	  var escapeDirection = {
	    x: {
	      margin: 30,
	      rowSize: 30
	    }
	  };

	  return deconflictPosition(source, element, position, escapeDirection);
	}


	/**
	 * Always put element right of source per default.
	 */
	function getDefaultPosition(source, element) {

	  var sourceTrbl = asTRBL(source);

	  var sourceMid = getMid(source);

	  // simply put element right next to source
	  return {
	    x: sourceTrbl.right + DEFAULT_HORIZONTAL_DISTANCE + element.width / 2,
	    y: sourceMid.y
	  };
	}


	/**
	 * Returns all connected elements around the given source.
	 *
	 * This includes:
	 *
	 *   - connected elements
	 *   - host connected elements
	 *   - attachers connected elements
	 *
	 * @param  {djs.model.Shape} source
	 * @param  {djs.model.Shape} element
	 *
	 * @return {Array<djs.model.Shape>}
	 */
	function getAutoPlaceClosure(source, element) {

	  var allConnected = getConnected(source);

	  if (source.host) {
	    allConnected = allConnected.concat(getConnected(source.host));
	  }

	  if (source.attachers) {
	    allConnected = allConnected.concat(source.attachers.reduce(function(shapes, attacher) {
	      return shapes.concat(getConnected(attacher));
	    }, []));
	  }

	  return allConnected;
	}

	/**
	 * Return target at given position, if defined.
	 *
	 * This takes connected elements from host and attachers
	 * into account, too.
	 */
	function getConnectedAtPosition(source, position, element) {

	  var bounds = {
	    x: position.x - (element.width / 2),
	    y: position.y - (element.height / 2),
	    width: element.width,
	    height: element.height
	  };

	  var closure = getAutoPlaceClosure(source);

	  return find(closure, function(target) {

	    if (target === element) {
	      return false;
	    }

	    var orientation = getOrientation(target, bounds, PLACEMENT_DETECTION_PAD);

	    return orientation === 'intersect';
	  });
	}


	/**
	 * Returns a new, position for the given element
	 * based on the given element that is not occupied
	 * by some element connected to source.
	 *
	 * Take into account the escapeDirection (where to move
	 * on positining clashes) in the computation.
	 *
	 * @param {djs.model.Shape} source
	 * @param {djs.model.Shape} element
	 * @param {Point} position
	 * @param {Object} escapeDelta
	 *
	 * @return {Point}
	 */
	function deconflictPosition(source, element, position, escapeDelta) {

	  function nextPosition(existingElement) {

	    var newPosition = {
	      x: position.x,
	      y: position.y
	    };

	    [ 'x', 'y' ].forEach(function(axis) {

	      var axisDelta = escapeDelta[axis];

	      if (!axisDelta) {
	        return;
	      }

	      var dimension = axis === 'x' ? 'width' : 'height';

	      var margin = axisDelta.margin,
	          rowSize = axisDelta.rowSize;

	      if (margin < 0) {
	        newPosition[axis] = Math.min(
	          existingElement[axis] + margin - element[dimension] / 2,
	          position[axis] - rowSize + margin
	        );
	      } else {
	        newPosition[axis] = Math.max(
	          existingTarget[axis] + existingTarget[dimension] + margin + element[dimension] / 2,
	          position[axis] + rowSize + margin
	        );
	      }
	    });

	    return newPosition;
	  }

	  var existingTarget;

	  // deconflict position until free slot is found
	  while ((existingTarget = getConnectedAtPosition(source, position, element))) {
	    position = nextPosition(existingTarget);
	  }

	  return position;
	}



	// helpers //////////////////////

	function noneFilter() {
	  return true;
	}

	function getConnected(element, connectionFilter) {
	  return [].concat(
	    getTargets(element, connectionFilter),
	    getSources(element, connectionFilter)
	  );
	}

	function getSources(shape, connectionFilter) {

	  if (!connectionFilter) {
	    connectionFilter = noneFilter;
	  }

	  return shape.incoming.filter(connectionFilter).map(function(c) {
	    return c.source;
	  });
	}

	function getTargets(shape, connectionFilter) {

	  if (!connectionFilter) {
	    connectionFilter = noneFilter;
	  }

	  return shape.outgoing.filter(connectionFilter).map(function(c) {
	    return c.target;
	  });
	}

	/**
	 * A service that places elements connected to existing ones
	 * to an appropriate position in an _automated_ fashion.
	 *
	 * @param {EventBus} eventBus
	 * @param {Modeling} modeling
	 */
	function AutoPlace(eventBus, modeling) {

	  function emit(event, payload) {
	    return eventBus.fire(event, payload);
	  }


	  /**
	   * Append shape to source at appropriate position.
	   *
	   * @param {djs.model.Shape} source
	   * @param {djs.model.Shape} shape
	   *
	   * @return {djs.model.Shape} appended shape
	   */
	  this.append = function(source, shape) {

	    // allow others to provide the position
	    var position = emit('autoPlace', {
	      source: source,
	      shape: shape
	    });

	    if (!position) {
	      position = getNewShapePosition(source, shape);
	    }

	    var newShape = modeling.appendShape(source, shape, position, source.parent);

	    // notify interested parties on new shape placed
	    emit('autoPlace.end', {
	      shape: newShape
	    });

	    return newShape;
	  };

	}

	AutoPlace.$inject = [
	  'eventBus',
	  'modeling'
	];

	/**
	 * Select element after auto placement.
	 *
	 * @param {EventBus} eventBus
	 * @param {Selection} selection
	 */
	function AutoPlaceSelectionBehavior(eventBus, selection) {

	  eventBus.on('autoPlace.end', 500, function(e) {
	    selection.select(e.shape);
	  });

	}

	AutoPlaceSelectionBehavior.$inject = [
	  'eventBus',
	  'selection'
	];

	var AutoPlaceModule = {
	  __init__: [ 'autoPlaceSelectionBehavior' ],
	  autoPlace: [ 'type', AutoPlace ],
	  autoPlaceSelectionBehavior: [ 'type', AutoPlaceSelectionBehavior ]
	};

	var DEFAULT_PRIORITY$2 = 1000;

	/**
	 * A utility that can be used to plug-in into the command execution for
	 * extension and/or validation.
	 *
	 * @param {EventBus} eventBus
	 *
	 * @example
	 *
	 * import inherits from 'inherits';
	 *
	 * import CommandInterceptor from 'diagram-js/lib/command/CommandInterceptor';
	 *
	 * function CommandLogger(eventBus) {
	 *   CommandInterceptor.call(this, eventBus);
	 *
	 *   this.preExecute(function(event) {
	 *     console.log('command pre-execute', event);
	 *   });
	 * }
	 *
	 * inherits(CommandLogger, CommandInterceptor);
	 *
	 */
	function CommandInterceptor(eventBus) {
	  this._eventBus = eventBus;
	}

	CommandInterceptor.$inject = [ 'eventBus' ];

	function unwrapEvent(fn, that) {
	  return function(event) {
	    return fn.call(that || null, event.context, event.command, event);
	  };
	}

	/**
	 * Register an interceptor for a command execution
	 *
	 * @param {String|Array<String>} [events] list of commands to register on
	 * @param {String} [hook] command hook, i.e. preExecute, executed to listen on
	 * @param {Number} [priority] the priority on which to hook into the execution
	 * @param {Function} handlerFn interceptor to be invoked with (event)
	 * @param {Boolean} unwrap if true, unwrap the event and pass (context, command, event) to the
	 *                          listener instead
	 * @param {Object} [that] Pass context (`this`) to the handler function
	 */
	CommandInterceptor.prototype.on = function(events, hook, priority, handlerFn, unwrap, that) {

	  if (isFunction(hook) || isNumber(hook)) {
	    that = unwrap;
	    unwrap = handlerFn;
	    handlerFn = priority;
	    priority = hook;
	    hook = null;
	  }

	  if (isFunction(priority)) {
	    that = unwrap;
	    unwrap = handlerFn;
	    handlerFn = priority;
	    priority = DEFAULT_PRIORITY$2;
	  }

	  if (isObject(unwrap)) {
	    that = unwrap;
	    unwrap = false;
	  }

	  if (!isFunction(handlerFn)) {
	    throw new Error('handlerFn must be a function');
	  }

	  if (!isArray(events)) {
	    events = [ events ];
	  }

	  var eventBus = this._eventBus;

	  forEach(events, function(event) {
	    // concat commandStack(.event)?(.hook)?
	    var fullEvent = [ 'commandStack', event, hook ].filter(function(e) { return e; }).join('.');

	    eventBus.on(fullEvent, priority, unwrap ? unwrapEvent(handlerFn, that) : handlerFn, that);
	  });
	};


	var hooks = [
	  'canExecute',
	  'preExecute',
	  'preExecuted',
	  'execute',
	  'executed',
	  'postExecute',
	  'postExecuted',
	  'revert',
	  'reverted'
	];

	/*
	 * Install hook shortcuts
	 *
	 * This will generate the CommandInterceptor#(preExecute|...|reverted) methods
	 * which will in term forward to CommandInterceptor#on.
	 */
	forEach(hooks, function(hook) {

	  /**
	   * {canExecute|preExecute|preExecuted|execute|executed|postExecute|postExecuted|revert|reverted}
	   *
	   * A named hook for plugging into the command execution
	   *
	   * @param {String|Array<String>} [events] list of commands to register on
	   * @param {Number} [priority] the priority on which to hook into the execution
	   * @param {Function} handlerFn interceptor to be invoked with (event)
	   * @param {Boolean} [unwrap=false] if true, unwrap the event and pass (context, command, event) to the
	   *                          listener instead
	   * @param {Object} [that] Pass context (`this`) to the handler function
	   */
	  CommandInterceptor.prototype[hook] = function(events, priority, handlerFn, unwrap, that) {

	    if (isFunction(events) || isNumber(events)) {
	      that = unwrap;
	      unwrap = handlerFn;
	      handlerFn = priority;
	      priority = events;
	      events = null;
	    }

	    this.on(events, hook, priority, handlerFn, unwrap, that);
	  };
	});

	/**
	 * An auto resize component that takes care of expanding a parent element
	 * if child elements are created or moved close the parents edge.
	 *
	 * @param {EventBus} eventBus
	 * @param {ElementRegistry} elementRegistry
	 * @param {Modeling} modeling
	 * @param {Rules} rules
	 */
	function AutoResize(eventBus, elementRegistry, modeling, rules) {

	  CommandInterceptor.call(this, eventBus);

	  this._elementRegistry = elementRegistry;
	  this._modeling = modeling;
	  this._rules = rules;

	  var self = this;

	  this.postExecuted([ 'shape.create' ], function(event) {
	    var context = event.context,
	        hints = context.hints,
	        shape = context.shape,
	        parent = context.parent || context.newParent;

	    if (hints && (hints.root === false || hints.autoResize === false)) {
	      return;
	    }

	    self._expand([ shape ], parent);
	  });

	  this.postExecuted([ 'elements.move' ], function(event) {
	    var context = event.context,
	        elements = flatten(values(context.closure.topLevel)),
	        hints = context.hints;

	    var autoResize = hints ? hints.autoResize : true;

	    if (autoResize === false) {
	      return;
	    }

	    var expandings = groupBy(elements, function(element) {
	      return element.parent.id;
	    });

	    forEach(expandings, function(elements, parentId) {

	      // optionally filter elements to be considered when resizing
	      if (isArray(autoResize)) {
	        elements = elements.filter(function(element) {
	          return autoResize.indexOf(element) !== -1;
	        });
	      }

	      self._expand(elements, parentId);
	    });
	  });

	  this.postExecuted([ 'shape.toggleCollapse' ], function(event) {
	    var context = event.context,
	        hints = context.hints,
	        shape = context.shape;

	    if (hints && (hints.root === false || hints.autoResize === false)) {
	      return;
	    }

	    if (shape.collapsed) {
	      return;
	    }

	    self._expand(shape.children || [], shape);
	  });

	  this.postExecuted([ 'shape.resize' ], function(event) {
	    var context = event.context,
	        hints = context.hints,
	        shape = context.shape,
	        parent = shape.parent;

	    if (hints && (hints.root === false || hints.autoResize === false)) {
	      return;
	    }

	    if (parent) {
	      self._expand([ shape ], parent);
	    }
	  });

	}

	AutoResize.$inject = [
	  'eventBus',
	  'elementRegistry',
	  'modeling',
	  'rules'
	];

	inherits_browser(AutoResize, CommandInterceptor);


	/**
	 * Calculate the new bounds of the target shape, given
	 * a number of elements have been moved or added into the parent.
	 *
	 * This method considers the current size, the added elements as well as
	 * the provided padding for the new bounds.
	 *
	 * @param {Array<djs.model.Shape>} elements
	 * @param {djs.model.Shape} target
	 */
	AutoResize.prototype._getOptimalBounds = function(elements, target) {

	  var offset = this.getOffset(target),
	      padding = this.getPadding(target);

	  var elementsTrbl = asTRBL(getBBox(elements)),
	      targetTrbl = asTRBL(target);

	  var newTrbl = {};

	  if (elementsTrbl.top - targetTrbl.top < padding.top) {
	    newTrbl.top = elementsTrbl.top - offset.top;
	  }

	  if (elementsTrbl.left - targetTrbl.left < padding.left) {
	    newTrbl.left = elementsTrbl.left - offset.left;
	  }

	  if (targetTrbl.right - elementsTrbl.right < padding.right) {
	    newTrbl.right = elementsTrbl.right + offset.right;
	  }

	  if (targetTrbl.bottom - elementsTrbl.bottom < padding.bottom) {
	    newTrbl.bottom = elementsTrbl.bottom + offset.bottom;
	  }

	  return asBounds(assign({}, targetTrbl, newTrbl));
	};


	/**
	 * Expand the target shape respecting rules, offset and padding
	 *
	 * @param {Array<djs.model.Shape>} elements
	 * @param {djs.model.Shape|String} target|targetId
	 */
	AutoResize.prototype._expand = function(elements, target) {

	  if (typeof target === 'string') {
	    target = this._elementRegistry.get(target);
	  }

	  var allowed = this._rules.allowed('element.autoResize', {
	    elements: elements,
	    target: target
	  });

	  if (!allowed) {
	    return;
	  }

	  // calculate the new bounds
	  var newBounds = this._getOptimalBounds(elements, target);

	  if (!boundsChanged(newBounds, target)) {
	    return;
	  }

	  var resizeDirections = getResizeDirections(pick(target, [ 'x', 'y', 'width', 'height' ]), newBounds);

	  // resize the parent shape
	  this.resize(target, newBounds, {
	    autoResize: resizeDirections
	  });

	  var parent = target.parent;

	  // recursively expand parent elements
	  if (parent) {
	    this._expand([ target ], parent);
	  }
	};


	/**
	 * Get the amount to expand the given shape in each direction.
	 *
	 * @param {djs.model.Shape} shape
	 *
	 * @return {TRBL}
	 */
	AutoResize.prototype.getOffset = function(shape) {
	  return { top: 60, bottom: 60, left: 100, right: 100 };
	};


	/**
	 * Get the activation threshold for each side for which
	 * resize triggers.
	 *
	 * @param {djs.model.Shape} shape
	 *
	 * @return {TRBL}
	 */
	AutoResize.prototype.getPadding = function(shape) {
	  return { top: 2, bottom: 2, left: 15, right: 15 };
	};


	/**
	 * Perform the actual resize operation.
	 *
	 * @param {djs.model.Shape} shape
	 * @param {Bounds} newBounds
	 * @param {Object} [hints]
	 * @param {string} [hints.autoResize]
	 */
	AutoResize.prototype.resize = function(shape, newBounds, hints) {
	  this._modeling.resizeShape(shape, newBounds, null, hints);
	};


	function boundsChanged(newBounds, oldBounds) {
	  return (
	    newBounds.x !== oldBounds.x ||
	    newBounds.y !== oldBounds.y ||
	    newBounds.width !== oldBounds.width ||
	    newBounds.height !== oldBounds.height
	  );
	}

	/**
	 * Get directions of resize as {n|w|s|e} e.g. "nw".
	 *
	 * @param {Bounds} oldBounds
	 * @param {Bounds} newBounds
	 *
	 * @returns {string} Resize directions as {n|w|s|e}.
	 */
	function getResizeDirections(oldBounds, newBounds) {
	  var directions = '';

	  oldBounds = asTRBL(oldBounds);
	  newBounds = asTRBL(newBounds);

	  if (oldBounds.top > newBounds.top) {
	    directions = directions.concat('n');
	  }

	  if (oldBounds.right < newBounds.right) {
	    directions = directions.concat('w');
	  }

	  if (oldBounds.bottom < newBounds.bottom) {
	    directions = directions.concat('s');
	  }

	  if (oldBounds.left > newBounds.left) {
	    directions = directions.concat('e');
	  }

	  return directions;
	}

	/**
	 * Sub class of the AutoResize module which implements a BPMN
	 * specific resize function.
	 */
	function BpmnAutoResize(injector) {

	  injector.invoke(AutoResize, this);
	}

	BpmnAutoResize.$inject = [
	  'injector'
	];

	inherits_browser(BpmnAutoResize, AutoResize);


	/**
	 * Resize shapes and lanes.
	 *
	 * @param {djs.model.Shape} target
	 * @param {Bounds} newBounds
	 * @param {Object} hints
	 */
	BpmnAutoResize.prototype.resize = function(target, newBounds, hints) {

	  if (is$1(target, 'bpmn:Participant')) {
	    this._modeling.resizeLane(target, newBounds, null, hints);
	  } else {
	    this._modeling.resizeShape(target, newBounds, null, hints);
	  }
	};

	/**
	 * A basic provider that may be extended to implement modeling rules.
	 *
	 * Extensions should implement the init method to actually add their custom
	 * modeling checks. Checks may be added via the #addRule(action, fn) method.
	 *
	 * @param {EventBus} eventBus
	 */
	function RuleProvider(eventBus) {
	  CommandInterceptor.call(this, eventBus);

	  this.init();
	}

	RuleProvider.$inject = [ 'eventBus' ];

	inherits_browser(RuleProvider, CommandInterceptor);


	/**
	 * Adds a modeling rule for the given action, implemented through
	 * a callback function.
	 *
	 * The function will receive the modeling specific action context
	 * to perform its check. It must return `false` to disallow the
	 * action from happening or `true` to allow the action.
	 *
	 * A rule provider may pass over the evaluation to lower priority
	 * rules by returning return nothing (or <code>undefined</code>).
	 *
	 * @example
	 *
	 * ResizableRules.prototype.init = function() {
	 *
	 *   \/**
	 *    * Return `true`, `false` or nothing to denote
	 *    * _allowed_, _not allowed_ and _continue evaluating_.
	 *    *\/
	 *   this.addRule('shape.resize', function(context) {
	 *
	 *     var shape = context.shape;
	 *
	 *     if (!context.newBounds) {
	 *       // check general resizability
	 *       if (!shape.resizable) {
	 *         return false;
	 *       }
	 *
	 *       // not returning anything (read: undefined)
	 *       // will continue the evaluation of other rules
	 *       // (with lower priority)
	 *       return;
	 *     } else {
	 *       // element must have minimum size of 10*10 points
	 *       return context.newBounds.width > 10 && context.newBounds.height > 10;
	 *     }
	 *   });
	 * };
	 *
	 * @param {String|Array<String>} actions the identifier for the modeling action to check
	 * @param {Number} [priority] the priority at which this rule is being applied
	 * @param {Function} fn the callback function that performs the actual check
	 */
	RuleProvider.prototype.addRule = function(actions, priority, fn) {

	  var self = this;

	  if (typeof actions === 'string') {
	    actions = [ actions ];
	  }

	  actions.forEach(function(action) {

	    self.canExecute(action, priority, function(context, action, event) {
	      return fn(context);
	    }, true);
	  });
	};

	/**
	 * Implement this method to add new rules during provider initialization.
	 */
	RuleProvider.prototype.init = function() {};

	/**
	 * This is a base rule provider for the element.autoResize rule.
	 */
	function AutoResizeProvider(eventBus) {

	  RuleProvider.call(this, eventBus);

	  var self = this;

	  this.addRule('element.autoResize', function(context) {
	    return self.canResize(context.elements, context.target);
	  });
	}

	AutoResizeProvider.$inject = [ 'eventBus' ];

	inherits_browser(AutoResizeProvider, RuleProvider);

	/**
	 * Needs to be implemented by sub classes to allow actual auto resize
	 *
	 * @param  {Array<djs.model.Shape>} elements
	 * @param  {djs.model.Shape} target
	 *
	 * @return {Boolean}
	 */
	AutoResizeProvider.prototype.canResize = function(elements, target) {
	  return false;
	};

	/**
	 * This module is a provider for automatically resizing parent BPMN elements
	 */
	function BpmnAutoResizeProvider(eventBus, modeling) {
	  AutoResizeProvider.call(this, eventBus);

	  this._modeling = modeling;
	}

	inherits_browser(BpmnAutoResizeProvider, AutoResizeProvider);

	BpmnAutoResizeProvider.$inject = [
	  'eventBus',
	  'modeling'
	];


	/**
	 * Check if the given target can be expanded
	 *
	 * @param  {djs.model.Shape} target
	 *
	 * @return {boolean}
	 */
	BpmnAutoResizeProvider.prototype.canResize = function(elements, target) {

	  if (!is$1(target, 'bpmn:Participant') && !is$1(target, 'bpmn:Lane') && !(is$1(target, 'bpmn:SubProcess'))) {
	    return false;
	  }

	  var canResize = true;

	  forEach(elements, function(element) {

	    if (is$1(element, 'bpmn:Lane') || element.labelTarget) {
	      canResize = false;
	      return;
	    }
	  });

	  return canResize;
	};

	var AutoResizeModule = {
	  __init__: [
	    'bpmnAutoResize',
	    'bpmnAutoResizeProvider'
	  ],
	  bpmnAutoResize: [ 'type', BpmnAutoResize ],
	  bpmnAutoResizeProvider: [ 'type', BpmnAutoResizeProvider ]
	};

	/* global TouchEvent */

	var round$1 = Math.round;

	var DRAG_ACTIVE_CLS = 'djs-drag-active';


	function preventDefault(event) {
	  event.preventDefault();
	}

	function isTouchEvent(event) {
	  // check for TouchEvent being available first
	  // (i.e. not available on desktop Firefox)
	  return typeof TouchEvent !== 'undefined' && event instanceof TouchEvent;
	}

	function getLength(point) {
	  return Math.sqrt(Math.pow(point.x, 2) + Math.pow(point.y, 2));
	}

	/**
	 * A helper that fires canvas localized drag events and realizes
	 * the general "drag-and-drop" look and feel.
	 *
	 * Calling {@link Dragging#activate} activates dragging on a canvas.
	 *
	 * It provides the following:
	 *
	 *   * emits life cycle events, namespaced with a prefix assigned
	 *     during dragging activation
	 *   * sets and restores the cursor
	 *   * sets and restores the selection
	 *   * ensures there can be only one drag operation active at a time
	 *
	 * Dragging may be canceled manually by calling {@link Dragging#cancel}
	 * or by pressing ESC.
	 *
	 *
	 * ## Life-cycle events
	 *
	 * Dragging can be in three different states, off, initialized
	 * and active.
	 *
	 * (1) off: no dragging operation is in progress
	 * (2) initialized: a new drag operation got initialized but not yet
	 *                  started (i.e. because of no initial move)
	 * (3) started: dragging is in progress
	 *
	 * Eventually dragging will be off again after a drag operation has
	 * been ended or canceled via user click or ESC key press.
	 *
	 * To indicate transitions between these states dragging emits generic
	 * life-cycle events with the `drag.` prefix _and_ events namespaced
	 * to a prefix choosen by a user during drag initialization.
	 *
	 * The following events are emitted (appropriately prefixed) via
	 * the {@link EventBus}.
	 *
	 * * `init`
	 * * `start`
	 * * `move`
	 * * `end`
	 * * `ended` (dragging already in off state)
	 * * `cancel` (only if previously started)
	 * * `canceled` (dragging already in off state, only if previously started)
	 * * `cleanup`
	 *
	 *
	 * @example
	 *
	 * function MyDragComponent(eventBus, dragging) {
	 *
	 *   eventBus.on('mydrag.start', function(event) {
	 *     console.log('yes, we start dragging');
	 *   });
	 *
	 *   eventBus.on('mydrag.move', function(event) {
	 *     console.log('canvas local coordinates', event.x, event.y, event.dx, event.dy);
	 *
	 *     // local drag data is passed with the event
	 *     event.context.foo; // "BAR"
	 *
	 *     // the original mouse event, too
	 *     event.originalEvent; // MouseEvent(...)
	 *   });
	 *
	 *   eventBus.on('element.click', function(event) {
	 *     dragging.init(event, 'mydrag', {
	 *       cursor: 'grabbing',
	 *       data: {
	 *         context: {
	 *           foo: "BAR"
	 *         }
	 *       }
	 *     });
	 *   });
	 * }
	 */
	function Dragging(eventBus, canvas, selection) {

	  var defaultOptions = {
	    threshold: 5,
	    trapClick: true
	  };

	  // the currently active drag operation
	  // dragging is active as soon as this context exists.
	  //
	  // it is visually _active_ only when a context.active flag is set to true.
	  var context;

	  /* convert a global event into local coordinates */
	  function toLocalPoint(globalPosition) {

	    var viewbox = canvas.viewbox();

	    var clientRect = canvas._container.getBoundingClientRect();

	    return {
	      x: viewbox.x + (globalPosition.x - clientRect.left) / viewbox.scale,
	      y: viewbox.y + (globalPosition.y - clientRect.top) / viewbox.scale
	    };
	  }

	  // helpers

	  function fire(type, dragContext) {
	    dragContext = dragContext || context;

	    var event = eventBus.createEvent(
	      assign(
	        {},
	        dragContext.payload,
	        dragContext.data,
	        { isTouch: dragContext.isTouch }
	      )
	    );

	    // default integration
	    if (eventBus.fire('drag.' + type, event) === false) {
	      return false;
	    }

	    return eventBus.fire(dragContext.prefix + '.' + type, event);
	  }

	  // event listeners

	  function move(event, activate) {
	    var payload = context.payload,
	        displacement = context.displacement;

	    var globalStart = context.globalStart,
	        globalCurrent = toPoint(event),
	        globalDelta = delta(globalCurrent, globalStart);

	    var localStart = context.localStart,
	        localCurrent = toLocalPoint(globalCurrent),
	        localDelta = delta(localCurrent, localStart);


	    // activate context explicitly or once threshold is reached
	    if (!context.active && (activate || getLength(globalDelta) > context.threshold)) {

	      // fire start event with original
	      // starting coordinates

	      assign(payload, {
	        x: round$1(localStart.x + displacement.x),
	        y: round$1(localStart.y + displacement.y),
	        dx: 0,
	        dy: 0
	      }, { originalEvent: event });

	      if (false === fire('start')) {
	        return cancel();
	      }

	      context.active = true;

	      // unset selection and remember old selection
	      // the previous (old) selection will always passed
	      // with the event via the event.previousSelection property
	      if (!context.keepSelection) {
	        payload.previousSelection = selection.get();
	        selection.select(null);
	      }

	      // allow custom cursor
	      if (context.cursor) {
	        set$1(context.cursor);
	      }

	      // indicate dragging via marker on root element
	      canvas.addMarker(canvas.getRootElement(), DRAG_ACTIVE_CLS);
	    }

	    stopPropagation(event);

	    if (context.active) {

	      // update payload with actual coordinates
	      assign(payload, {
	        x: round$1(localCurrent.x + displacement.x),
	        y: round$1(localCurrent.y + displacement.y),
	        dx: round$1(localDelta.x),
	        dy: round$1(localDelta.y)
	      }, { originalEvent: event });

	      // emit move event
	      fire('move');
	    }
	  }

	  function end(event) {
	    var previousContext,
	        returnValue = true;

	    if (context.active) {

	      if (event) {
	        context.payload.originalEvent = event;

	        // suppress original event (click, ...)
	        // because we just ended a drag operation
	        stopPropagation(event);
	      }

	      // implementations may stop restoring the
	      // original state (selections, ...) by preventing the
	      // end events default action
	      returnValue = fire('end');
	    }

	    if (returnValue === false) {
	      fire('rejected');
	    }

	    previousContext = cleanup(returnValue !== true);

	    // last event to be fired when all drag operations are done
	    // at this point in time no drag operation is in progress anymore
	    fire('ended', previousContext);
	  }


	  // cancel active drag operation if the user presses
	  // the ESC key on the keyboard

	  function checkCancel(event) {

	    if (event.which === 27) {
	      preventDefault(event);

	      cancel();
	    }
	  }


	  // prevent ghost click that might occur after a finished
	  // drag and drop session

	  function trapClickAndEnd(event) {

	    var untrap;

	    // trap the click in case we are part of an active
	    // drag operation. This will effectively prevent
	    // the ghost click that cannot be canceled otherwise.
	    if (context.active) {

	      untrap = install(eventBus);

	      // remove trap after minimal delay
	      setTimeout(untrap, 400);

	      // prevent default action (click)
	      preventDefault(event);
	    }

	    end(event);
	  }

	  function trapTouch(event) {
	    move(event);
	  }

	  // update the drag events hover (djs.model.Base) and hoverGfx (Snap<SVGElement>)
	  // properties during hover and out and fire {prefix}.hover and {prefix}.out properties
	  // respectively

	  function hover(event) {
	    var payload = context.payload;

	    payload.hoverGfx = event.gfx;
	    payload.hover = event.element;

	    fire('hover');
	  }

	  function out(event) {
	    fire('out');

	    var payload = context.payload;

	    payload.hoverGfx = null;
	    payload.hover = null;
	  }


	  // life-cycle methods

	  function cancel(restore) {
	    var previousContext;

	    if (!context) {
	      return;
	    }

	    var wasActive = context.active;

	    if (wasActive) {
	      fire('cancel');
	    }

	    previousContext = cleanup(restore);

	    if (wasActive) {
	      // last event to be fired when all drag operations are done
	      // at this point in time no drag operation is in progress anymore
	      fire('canceled', previousContext);
	    }
	  }

	  function cleanup(restore) {
	    var previousContext,
	        endDrag;

	    fire('cleanup');

	    // reset cursor
	    unset();

	    if (context.trapClick) {
	      endDrag = trapClickAndEnd;
	    } else {
	      endDrag = end;
	    }

	    // reset dom listeners
	    componentEvent.unbind(document, 'mousemove', move);

	    componentEvent.unbind(document, 'dragstart', preventDefault);
	    componentEvent.unbind(document, 'selectstart', preventDefault);

	    componentEvent.unbind(document, 'mousedown', endDrag, true);
	    componentEvent.unbind(document, 'mouseup', endDrag, true);

	    componentEvent.unbind(document, 'keyup', checkCancel);

	    componentEvent.unbind(document, 'touchstart', trapTouch, true);
	    componentEvent.unbind(document, 'touchcancel', cancel, true);
	    componentEvent.unbind(document, 'touchmove', move, true);
	    componentEvent.unbind(document, 'touchend', end, true);

	    eventBus.off('element.hover', hover);
	    eventBus.off('element.out', out);

	    // remove drag marker on root element
	    canvas.removeMarker(canvas.getRootElement(), DRAG_ACTIVE_CLS);

	    // restore selection, unless it has changed
	    var previousSelection = context.payload.previousSelection;

	    if (restore !== false && previousSelection && !selection.get().length) {
	      selection.select(previousSelection);
	    }

	    previousContext = context;

	    context = null;

	    return previousContext;
	  }

	  /**
	   * Initialize a drag operation.
	   *
	   * If `localPosition` is given, drag events will be emitted
	   * relative to it.
	   *
	   * @param {MouseEvent|TouchEvent} [event]
	   * @param {Point} [localPosition] actual diagram local position this drag operation should start at
	   * @param {String} prefix
	   * @param {Object} [options]
	   */
	  function init(event, relativeTo, prefix, options) {

	    // only one drag operation may be active, at a time
	    if (context) {
	      cancel(false);
	    }

	    if (typeof relativeTo === 'string') {
	      options = prefix;
	      prefix = relativeTo;
	      relativeTo = null;
	    }

	    options = assign({}, defaultOptions, options || {});

	    var data = options.data || {},
	        originalEvent,
	        globalStart,
	        localStart,
	        endDrag,
	        isTouch;

	    if (options.trapClick) {
	      endDrag = trapClickAndEnd;
	    } else {
	      endDrag = end;
	    }

	    if (event) {
	      originalEvent = getOriginal(event) || event;
	      globalStart = toPoint(event);

	      stopPropagation(event);

	      // prevent default browser dragging behavior
	      if (originalEvent.type === 'dragstart') {
	        preventDefault(originalEvent);
	      }
	    } else {
	      originalEvent = null;
	      globalStart = { x: 0, y: 0 };
	    }

	    localStart = toLocalPoint(globalStart);

	    if (!relativeTo) {
	      relativeTo = localStart;
	    }

	    isTouch = isTouchEvent(originalEvent);

	    context = assign({
	      prefix: prefix,
	      data: data,
	      payload: {},
	      globalStart: globalStart,
	      displacement: delta(relativeTo, localStart),
	      localStart: localStart,
	      isTouch: isTouch
	    }, options);

	    // skip dom registration if trigger
	    // is set to manual (during testing)
	    if (!options.manual) {

	      // add dom listeners

	      if (isTouch) {
	        componentEvent.bind(document, 'touchstart', trapTouch, true);
	        componentEvent.bind(document, 'touchcancel', cancel, true);
	        componentEvent.bind(document, 'touchmove', move, true);
	        componentEvent.bind(document, 'touchend', end, true);
	      } else {
	        // assume we use the mouse to interact per default
	        componentEvent.bind(document, 'mousemove', move);

	        // prevent default browser drag and text selection behavior
	        componentEvent.bind(document, 'dragstart', preventDefault);
	        componentEvent.bind(document, 'selectstart', preventDefault);

	        componentEvent.bind(document, 'mousedown', endDrag, true);
	        componentEvent.bind(document, 'mouseup', endDrag, true);
	      }

	      componentEvent.bind(document, 'keyup', checkCancel);

	      eventBus.on('element.hover', hover);
	      eventBus.on('element.out', out);
	    }

	    fire('init');

	    if (options.autoActivate) {
	      move(event, true);
	    }
	  }

	  // cancel on diagram destruction
	  eventBus.on('diagram.destroy', cancel);


	  // API

	  this.init = init;
	  this.move = move;
	  this.hover = hover;
	  this.out = out;
	  this.end = end;

	  this.cancel = cancel;

	  // for introspection

	  this.context = function() {
	    return context;
	  };

	  this.setOptions = function(options) {
	    assign(defaultOptions, options);
	  };
	}

	Dragging.$inject = [
	  'eventBus',
	  'canvas',
	  'selection'
	];

	var HIGH_PRIORITY = 1500;


	/**
	 * Browsers may swallow certain events (hover, out ...) if users are to
	 * fast with the mouse.
	 *
	 * @see http://stackoverflow.com/questions/7448468/why-cant-i-reliably-capture-a-mouseout-event
	 *
	 * The fix implemented in this component ensure that we
	 *
	 * 1) have a hover state after a successive drag.move event
	 * 2) have an out event when dragging leaves an element
	 *
	 * @param {EventBus} eventBus
	 * @param {Dragging} dragging
	 * @param {ElementRegistry} elementRegistry
	 */
	function HoverFix(eventBus, dragging, elementRegistry) {

	  var self = this;

	  /**
	   * We wait for a specific sequence of events before
	   * emitting a fake drag.hover event.
	   *
	   * Event Sequence:
	   *
	   * drag.start
	   * drag.move
	   * drag.move >> ensure we are hovering
	   */
	  eventBus.on('drag.start', function(event) {

	    eventBus.once('drag.move', function() {

	      eventBus.once('drag.move', function(event) {

	        self.ensureHover(event);
	      });
	    });

	  });


	  /**
	   * We make sure that drag.out is always fired, even if the
	   * browser swallows an element.out event.
	   *
	   * Event sequence:
	   *
	   * drag.hover
	   * (element.out >> sometimes swallowed)
	   * element.hover >> ensure we fired drag.out
	   */
	  eventBus.on('drag.init', function() {

	    var hover, hoverGfx;

	    function setDragHover(event) {
	      hover = event.hover;
	      hoverGfx = event.hoverGfx;
	    }

	    function unsetHover() {
	      hover = null;
	      hoverGfx = null;
	    }

	    function ensureOut() {

	      if (!hover) {
	        return;
	      }

	      var element = hover,
	          gfx = hoverGfx;

	      hover = null;
	      hoverGfx = null;

	      // emit synthetic out event
	      dragging.out({
	        element: element,
	        gfx: gfx
	      });
	    }

	    eventBus.on('drag.hover', setDragHover);
	    eventBus.on('element.out', unsetHover);
	    eventBus.on('element.hover', HIGH_PRIORITY, ensureOut);

	    eventBus.once('drag.cleanup', function() {
	      eventBus.off('drag.hover', setDragHover);
	      eventBus.off('element.out', unsetHover);
	      eventBus.off('element.hover', ensureOut);
	    });

	  });


	  /**
	   * Make sure we are god damn hovering!
	   *
	   * @param {Event} dragging event
	   */
	  this.ensureHover = function(event) {

	    if (event.hover) {
	      return;
	    }

	    var originalEvent = event.originalEvent,
	        position,
	        target,
	        element,
	        gfx;

	    if (!(originalEvent instanceof MouseEvent)) {
	      return;
	    }

	    position = toPoint(originalEvent);

	    // damn expensive operation, ouch!
	    target = document.elementFromPoint(position.x, position.y);

	    gfx = getGfx(target);

	    if (gfx) {
	      element = elementRegistry.get(gfx);

	      dragging.hover({ element: element, gfx: gfx });
	    }
	  };

	}

	HoverFix.$inject = [
	  'eventBus',
	  'dragging',
	  'elementRegistry'
	];


	// helpers /////////////////////

	function getGfx(target) {
	  return closest(target, 'svg, .djs-element', true);
	}

	var DraggingModule = {
	  __init__: [
	    'hoverFix'
	  ],
	  __depends__: [
	    SelectionModule
	  ],
	  dragging: [ 'type', Dragging ],
	  hoverFix: [ 'type', HoverFix ]
	};

	function MouseTracking(eventBus, canvas) {
	  this._eventBus = eventBus;
	  this._canvas = canvas;

	  this._init();
	}

	MouseTracking.$inject = [
	  'eventBus',
	  'canvas'
	];


	MouseTracking.prototype.getHoverContext = function() {
	  var viewbox = this._canvas.viewbox();

	  return {
	    element: this._hoverElement,
	    point: {
	      x: viewbox.x + Math.round(this._mouseX / viewbox.scale),
	      y: viewbox.y + Math.round(this._mouseY / viewbox.scale)
	    }
	  };
	};

	MouseTracking.prototype._init = function() {
	  var eventBus = this._eventBus,
	      canvas = this._canvas;

	  var container = canvas.getContainer();

	  this._setMousePosition = bind(this._setMousePosition, this);

	  container.addEventListener('mousemove', this._setMousePosition);

	  eventBus.on('diagram.destroy', function() {
	    container.removeEventListener('mousemove', this._setMousePosition);
	  }, this);

	  eventBus.on('element.hover', this._setHoverElement, this);
	};


	MouseTracking.prototype._setHoverElement = function(event) {
	  this._hoverElement = event.element;
	};


	MouseTracking.prototype._setMousePosition = function(event) {
	  this._mouseX = event.layerX;
	  this._mouseY = event.layerY;
	};

	var MouseTrackingModule = {
	  __init__: [ 'mouseTracking' ],
	  mouseTracking: [ 'type', MouseTracking ]
	};

	/**
	 * Initiates canvas scrolling if current cursor point is close to a border.
	 * Cancelled when current point moves back inside the scrolling borders
	 * or cancelled manually.
	 *
	 * Default options :
	 *   scrollThresholdIn: [ 20, 20, 20, 20 ],
	 *   scrollThresholdOut: [ 0, 0, 0, 0 ],
	 *   scrollRepeatTimeout: 15,
	 *   scrollStep: 10
	 *
	 * Threshold order:
	 *   [ left, top, right, bottom ]
	 */
	function AutoScroll(config, eventBus, canvas, mouseTracking) {

	  this._canvas = canvas;
	  this._mouseTracking = mouseTracking;

	  this._opts = assign({
	    scrollThresholdIn: [ 20, 20, 20, 20 ],
	    scrollThresholdOut: [ 0, 0, 0, 0 ],
	    scrollRepeatTimeout: 15,
	    scrollStep: 10
	  }, config);

	  var self = this;

	  eventBus.on('drag.move', function(e) {
	    var point = self._toBorderPoint(e);

	    self.startScroll(point);
	  });

	  eventBus.on([ 'drag.cleanup' ], function() {
	    self.stopScroll();
	  });
	}

	AutoScroll.$inject = [
	  'config.autoScroll',
	  'eventBus',
	  'canvas',
	  'mouseTracking'
	];


	/**
	 * Starts scrolling loop.
	 * Point is given in global scale in canvas container box plane.
	 *
	 * @param  {Object} point { x: X, y: Y }
	 */
	AutoScroll.prototype.startScroll = function(point) {

	  var canvas = this._canvas;
	  var opts = this._opts;
	  var self = this;

	  var clientRect = canvas.getContainer().getBoundingClientRect();

	  var diff = [
	    point.x,
	    point.y,
	    clientRect.width - point.x,
	    clientRect.height - point.y
	  ];

	  this.stopScroll();

	  var dx = 0,
	      dy = 0;

	  for (var i = 0; i < 4; i++) {
	    if (between(diff[i], opts.scrollThresholdOut[i], opts.scrollThresholdIn[i])) {
	      if (i === 0) {
	        dx = opts.scrollStep;
	      } else if (i == 1) {
	        dy = opts.scrollStep;
	      } else if (i == 2) {
	        dx = -opts.scrollStep;
	      } else if (i == 3) {
	        dy = -opts.scrollStep;
	      }
	    }
	  }

	  if (dx !== 0 || dy !== 0) {
	    canvas.scroll({ dx: dx, dy: dy });

	    this._scrolling = setTimeout(function() {
	      self.startScroll(point);
	    }, opts.scrollRepeatTimeout);
	  }
	};

	function between(val, start, end) {
	  if (start < val && val < end) {
	    return true;
	  }

	  return false;
	}


	/**
	 * Stops scrolling loop.
	 */
	AutoScroll.prototype.stopScroll = function() {
	  clearTimeout(this._scrolling);
	};


	/**
	 * Overrides defaults options.
	 *
	 * @param  {Object} options
	 */
	AutoScroll.prototype.setOptions = function(options) {
	  this._opts = assign({}, this._opts, options);
	};


	/**
	 * Converts event to a point in canvas container plane in global scale.
	 *
	 * @param  {Event} event
	 * @return {Point}
	 */
	AutoScroll.prototype._toBorderPoint = function(event) {
	  var clientRect = this._canvas._container.getBoundingClientRect();

	  var globalPosition = toPoint(event.originalEvent);

	  return {
	    x: globalPosition.x - clientRect.left,
	    y: globalPosition.y - clientRect.top
	  };
	};

	var AutoScrollModule = {
	  __depends__: [
	    DraggingModule,
	    MouseTrackingModule
	  ],
	  __init__: [ 'autoScroll' ],
	  autoScroll: [ 'type', AutoScroll ]
	};

	/**
	 * A service that provides rules for certain diagram actions.
	 *
	 * The default implementation will hook into the {@link CommandStack}
	 * to perform the actual rule evaluation. Make sure to provide the
	 * `commandStack` service with this module if you plan to use it.
	 *
	 * Together with this implementation you may use the {@link RuleProvider}
	 * to implement your own rule checkers.
	 *
	 * This module is ment to be easily replaced, thus the tiny foot print.
	 *
	 * @param {Injector} injector
	 */
	function Rules(injector) {
	  this._commandStack = injector.get('commandStack', false);
	}

	Rules.$inject = [ 'injector' ];


	/**
	 * Returns whether or not a given modeling action can be executed
	 * in the specified context.
	 *
	 * This implementation will respond with allow unless anyone
	 * objects.
	 *
	 * @param {String} action the action to be checked
	 * @param {Object} [context] the context to check the action in
	 *
	 * @return {Boolean} returns true, false or null depending on whether the
	 *                   operation is allowed, not allowed or should be ignored.
	 */
	Rules.prototype.allowed = function(action, context) {
	  var allowed = true;

	  var commandStack = this._commandStack;

	  if (commandStack) {
	    allowed = commandStack.canExecute(action, context);
	  }

	  // map undefined to true, i.e. no rules
	  return allowed === undefined ? true : allowed;
	};

	var RulesModule = {
	  __init__: [ 'rules' ],
	  rules: [ 'type', Rules ]
	};

	var round$2 = Math.round,
	    max = Math.max;


	function circlePath(center, r) {
	  var x = center.x,
	      y = center.y;

	  return [
	    ['M', x, y],
	    ['m', 0, -r],
	    ['a', r, r, 0, 1, 1, 0, 2 * r],
	    ['a', r, r, 0, 1, 1, 0, -2 * r],
	    ['z']
	  ];
	}

	function linePath(points) {
	  var segments = [];

	  points.forEach(function(p, idx) {
	    segments.push([ idx === 0 ? 'M' : 'L', p.x, p.y ]);
	  });

	  return segments;
	}


	var INTERSECTION_THRESHOLD = 10;

	function getBendpointIntersection(waypoints, reference) {

	  var i, w;

	  for (i = 0; (w = waypoints[i]); i++) {

	    if (pointDistance(w, reference) <= INTERSECTION_THRESHOLD) {
	      return {
	        point: waypoints[i],
	        bendpoint: true,
	        index: i
	      };
	    }
	  }

	  return null;
	}

	function getPathIntersection(waypoints, reference) {

	  var intersections = intersect(circlePath(reference, INTERSECTION_THRESHOLD), linePath(waypoints));

	  var a = intersections[0],
	      b = intersections[intersections.length - 1],
	      idx;

	  if (!a) {
	    // no intersection
	    return null;
	  }

	  if (a !== b) {

	    if (a.segment2 !== b.segment2) {
	      // we use the bendpoint in between both segments
	      // as the intersection point

	      idx = max(a.segment2, b.segment2) - 1;

	      return {
	        point: waypoints[idx],
	        bendpoint: true,
	        index: idx
	      };
	    }

	    return {
	      point: {
	        x: (round$2(a.x + b.x) / 2),
	        y: (round$2(a.y + b.y) / 2)
	      },
	      index: a.segment2
	    };
	  }

	  return {
	    point: {
	      x: round$2(a.x),
	      y: round$2(a.y)
	    },
	    index: a.segment2
	  };
	}

	/**
	 * Returns the closest point on the connection towards a given reference point.
	 *
	 * @param  {Array<Point>} waypoints
	 * @param  {Point} reference
	 *
	 * @return {Object} intersection data (segment, point)
	 */
	function getApproxIntersection(waypoints, reference) {
	  return getBendpointIntersection(waypoints, reference) || getPathIntersection(waypoints, reference);
	}

	var BENDPOINT_CLS = 'djs-bendpoint';
	var SEGMENT_DRAGGER_CLS = 'djs-segment-dragger';

	function toCanvasCoordinates(canvas, event) {

	  var position = toPoint(event),
	      clientRect = canvas._container.getBoundingClientRect(),
	      offset;

	  // canvas relative position

	  offset = {
	    x: clientRect.left,
	    y: clientRect.top
	  };

	  // update actual event payload with canvas relative measures

	  var viewbox = canvas.viewbox();

	  return {
	    x: viewbox.x + (position.x - offset.x) / viewbox.scale,
	    y: viewbox.y + (position.y - offset.y) / viewbox.scale
	  };
	}

	function getConnectionIntersection(canvas, waypoints, event) {
	  var localPosition = toCanvasCoordinates(canvas, event),
	      intersection = getApproxIntersection(waypoints, localPosition);

	  return intersection;
	}

	function addBendpoint(parentGfx, cls) {
	  var groupGfx = create('g');
	  classes$1(groupGfx).add(BENDPOINT_CLS);

	  append(parentGfx, groupGfx);

	  var visual = create('circle');
	  attr$1(visual, {
	    cx: 0,
	    cy: 0,
	    r: 4
	  });
	  classes$1(visual).add('djs-visual');

	  append(groupGfx, visual);

	  var hit = create('circle');
	  attr$1(hit, {
	    cx: 0,
	    cy: 0,
	    r: 10
	  });
	  classes$1(hit).add('djs-hit');

	  append(groupGfx, hit);

	  if (cls) {
	    classes$1(groupGfx).add(cls);
	  }

	  return groupGfx;
	}

	function createParallelDragger(parentGfx, segmentStart, segmentEnd, alignment) {
	  var draggerGfx = create('g');

	  append(parentGfx, draggerGfx);

	  var width = 14,
	      height = 3,
	      padding = 6,
	      hitWidth = calculateHitWidth(segmentStart, segmentEnd, alignment),
	      hitHeight = height + padding;

	  var visual = create('rect');
	  attr$1(visual, {
	    x: -width / 2,
	    y: -height / 2,
	    width: width,
	    height: height
	  });
	  classes$1(visual).add('djs-visual');

	  append(draggerGfx, visual);

	  var hit = create('rect');
	  attr$1(hit, {
	    x: -hitWidth / 2,
	    y: -hitHeight / 2,
	    width: hitWidth,
	    height: hitHeight
	  });
	  classes$1(hit).add('djs-hit');

	  append(draggerGfx, hit);

	  rotate(draggerGfx, alignment === 'v' ? 90 : 0);

	  return draggerGfx;
	}


	function addSegmentDragger(parentGfx, segmentStart, segmentEnd) {

	  var groupGfx = create('g'),
	      mid = getMidPoint(segmentStart, segmentEnd),
	      alignment = pointsAligned(segmentStart, segmentEnd);

	  append(parentGfx, groupGfx);

	  createParallelDragger(groupGfx, segmentStart, segmentEnd, alignment);

	  classes$1(groupGfx).add(SEGMENT_DRAGGER_CLS);
	  classes$1(groupGfx).add(alignment === 'h' ? 'horizontal' : 'vertical');

	  translate(groupGfx, mid.x, mid.y);

	  return groupGfx;
	}

	/**
	 * Calculates region for segment move which is 2/3 of the full segment length
	 * @param {Number} segmentLength
	 *
	 * @return {Number}
	 */
	function calculateSegmentMoveRegion(segmentLength) {
	  return Math.abs(Math.round(segmentLength * 2 / 3));
	}

	// helper //////////

	function calculateHitWidth(segmentStart, segmentEnd, alignment) {
	  var segmentLengthXAxis = segmentEnd.x - segmentStart.x,
	      segmentLengthYAxis = segmentEnd.y - segmentStart.y;

	  return alignment === 'h' ?
	    calculateSegmentMoveRegion(segmentLengthXAxis) :
	    calculateSegmentMoveRegion(segmentLengthYAxis);
	}

	var css_escape = createCommonjsModule(function (module, exports) {
	(function(root, factory) {
		// https://github.com/umdjs/umd/blob/master/returnExports.js
		{
			// For Node.js.
			module.exports = factory(root);
		}
	}(typeof commonjsGlobal != 'undefined' ? commonjsGlobal : commonjsGlobal, function(root) {

		if (root.CSS && root.CSS.escape) {
			return root.CSS.escape;
		}

		// https://drafts.csswg.org/cssom/#serialize-an-identifier
		var cssEscape = function(value) {
			if (arguments.length == 0) {
				throw new TypeError('`CSS.escape` requires an argument.');
			}
			var string = String(value);
			var length = string.length;
			var index = -1;
			var codeUnit;
			var result = '';
			var firstCodeUnit = string.charCodeAt(0);
			while (++index < length) {
				codeUnit = string.charCodeAt(index);
				// Note: there’s no need to special-case astral symbols, surrogate
				// pairs, or lone surrogates.

				// If the character is NULL (U+0000), then the REPLACEMENT CHARACTER
				// (U+FFFD).
				if (codeUnit == 0x0000) {
					result += '\uFFFD';
					continue;
				}

				if (
					// If the character is in the range [\1-\1F] (U+0001 to U+001F) or is
					// U+007F, […]
					(codeUnit >= 0x0001 && codeUnit <= 0x001F) || codeUnit == 0x007F ||
					// If the character is the first character and is in the range [0-9]
					// (U+0030 to U+0039), […]
					(index == 0 && codeUnit >= 0x0030 && codeUnit <= 0x0039) ||
					// If the character is the second character and is in the range [0-9]
					// (U+0030 to U+0039) and the first character is a `-` (U+002D), […]
					(
						index == 1 &&
						codeUnit >= 0x0030 && codeUnit <= 0x0039 &&
						firstCodeUnit == 0x002D
					)
				) {
					// https://drafts.csswg.org/cssom/#escape-a-character-as-code-point
					result += '\\' + codeUnit.toString(16) + ' ';
					continue;
				}

				if (
					// If the character is the first character and is a `-` (U+002D), and
					// there is no second character, […]
					index == 0 &&
					length == 1 &&
					codeUnit == 0x002D
				) {
					result += '\\' + string.charAt(index);
					continue;
				}

				// If the character is not handled by one of the above rules and is
				// greater than or equal to U+0080, is `-` (U+002D) or `_` (U+005F), or
				// is in one of the ranges [0-9] (U+0030 to U+0039), [A-Z] (U+0041 to
				// U+005A), or [a-z] (U+0061 to U+007A), […]
				if (
					codeUnit >= 0x0080 ||
					codeUnit == 0x002D ||
					codeUnit == 0x005F ||
					codeUnit >= 0x0030 && codeUnit <= 0x0039 ||
					codeUnit >= 0x0041 && codeUnit <= 0x005A ||
					codeUnit >= 0x0061 && codeUnit <= 0x007A
				) {
					// the character itself
					result += string.charAt(index);
					continue;
				}

				// Otherwise, the escaped character.
				// https://drafts.csswg.org/cssom/#escape-a-character
				result += '\\' + string.charAt(index);

			}
			return result;
		};

		if (!root.CSS) {
			root.CSS = {};
		}

		root.CSS.escape = cssEscape;
		return cssEscape;

	}));
	});

	var HTML_ESCAPE_MAP = {
	  '&': '&amp;',
	  '<': '&lt;',
	  '>': '&gt;',
	  '"': '&quot;',
	  '\'': '&#39;'
	};

	function escapeHTML(str) {
	  str = '' + str;

	  return str && str.replace(/[&<>"']/g, function(match) {
	    return HTML_ESCAPE_MAP[match];
	  });
	}

	/**
	 * A service that adds editable bendpoints to connections.
	 */
	function Bendpoints(
	    eventBus, canvas, interactionEvents,
	    bendpointMove, connectionSegmentMove) {

	  /**
	   * Returns true if intersection point is inside middle region of segment, adjusted by
	   * optional threshold
	   */
	  function isIntersectionMiddle(intersection, waypoints, treshold) {
	    var idx = intersection.index,
	        p = intersection.point,
	        p0, p1, mid, aligned, xDelta, yDelta;

	    if (idx <= 0 || intersection.bendpoint) {
	      return false;
	    }

	    p0 = waypoints[idx - 1];
	    p1 = waypoints[idx];
	    mid = getMidPoint(p0, p1),
	    aligned = pointsAligned(p0, p1);
	    xDelta = Math.abs(p.x - mid.x);
	    yDelta = Math.abs(p.y - mid.y);

	    return aligned && xDelta <= treshold && yDelta <= treshold;
	  }

	  /**
	   * Calculates the threshold from a connection's middle which fits the two-third-region
	   */
	  function calculateIntersectionThreshold(connection, intersection) {
	    var waypoints = connection.waypoints,
	        relevantSegment, alignment, segmentLength, threshold;

	    if (intersection.index <= 0 || intersection.bendpoint) {
	      return null;
	    }

	    // segment relative to connection intersection
	    relevantSegment = {
	      start: waypoints[intersection.index - 1],
	      end: waypoints[intersection.index]
	    };

	    alignment = pointsAligned(relevantSegment.start, relevantSegment.end);

	    if (!alignment) {
	      return null;
	    }

	    if (alignment === 'h') {
	      segmentLength = relevantSegment.end.x - relevantSegment.start.x;
	    } else {
	      segmentLength = relevantSegment.end.y - relevantSegment.start.y;
	    }

	    // calculate threshold relative to 2/3 of segment length
	    threshold = calculateSegmentMoveRegion(segmentLength) / 2;

	    return threshold;
	  }

	  function activateBendpointMove(event, connection) {
	    var waypoints = connection.waypoints,
	        intersection = getConnectionIntersection(canvas, waypoints, event),
	        threshold;

	    if (!intersection) {
	      return;
	    }

	    threshold = calculateIntersectionThreshold(connection, intersection);

	    if (isIntersectionMiddle(intersection, waypoints, threshold)) {
	      connectionSegmentMove.start(event, connection, intersection.index);
	    } else {
	      bendpointMove.start(event, connection, intersection.index, !intersection.bendpoint);
	    }

	    // we've handled the event
	    return true;
	  }

	  function bindInteractionEvents(node, eventName, element) {

	    componentEvent.bind(node, eventName, function(event) {
	      interactionEvents.triggerMouseEvent(eventName, event, element);
	      event.stopPropagation();
	    });
	  }

	  function getBendpointsContainer(element, create$1) {

	    var layer = canvas.getLayer('overlays'),
	        gfx = query('.djs-bendpoints[data-element-id="' + css_escape(element.id) + '"]', layer);

	    if (!gfx && create$1) {
	      gfx = create('g');
	      attr$1(gfx, { 'data-element-id': element.id });
	      classes$1(gfx).add('djs-bendpoints');

	      append(layer, gfx);

	      bindInteractionEvents(gfx, 'mousedown', element);
	      bindInteractionEvents(gfx, 'click', element);
	      bindInteractionEvents(gfx, 'dblclick', element);
	    }

	    return gfx;
	  }

	  function getSegmentDragger(idx, parentGfx) {
	    return query(
	      '.djs-segment-dragger[data-segment-idx="' + idx + '"]',
	      parentGfx
	    );
	  }

	  function createBendpoints(gfx, connection) {
	    connection.waypoints.forEach(function(p, idx) {
	      var bendpoint = addBendpoint(gfx);

	      append(gfx, bendpoint);

	      translate(bendpoint, p.x, p.y);
	    });

	    // add floating bendpoint
	    addBendpoint(gfx, 'floating');
	  }

	  function createSegmentDraggers(gfx, connection) {

	    var waypoints = connection.waypoints;

	    var segmentStart,
	        segmentEnd,
	        segmentDraggerGfx;

	    for (var i = 1; i < waypoints.length; i++) {

	      segmentStart = waypoints[i - 1];
	      segmentEnd = waypoints[i];

	      if (pointsAligned(segmentStart, segmentEnd)) {
	        segmentDraggerGfx = addSegmentDragger(gfx, segmentStart, segmentEnd);

	        attr$1(segmentDraggerGfx, { 'data-segment-idx': i });

	        bindInteractionEvents(segmentDraggerGfx, 'mousemove', connection);
	      }
	    }
	  }

	  function clearBendpoints(gfx) {
	    forEach(all('.' + BENDPOINT_CLS, gfx), function(node) {
	      remove$1(node);
	    });
	  }

	  function clearSegmentDraggers(gfx) {
	    forEach(all('.' + SEGMENT_DRAGGER_CLS, gfx), function(node) {
	      remove$1(node);
	    });
	  }

	  function addHandles(connection) {

	    var gfx = getBendpointsContainer(connection);

	    if (!gfx) {
	      gfx = getBendpointsContainer(connection, true);

	      createBendpoints(gfx, connection);
	      createSegmentDraggers(gfx, connection);
	    }

	    return gfx;
	  }

	  function updateHandles(connection) {

	    var gfx = getBendpointsContainer(connection);

	    if (gfx) {
	      clearSegmentDraggers(gfx);
	      clearBendpoints(gfx);
	      createSegmentDraggers(gfx, connection);
	      createBendpoints(gfx, connection);
	    }
	  }

	  function updateFloatingBendpointPosition(parentGfx, intersection) {
	    var floating = query('.floating', parentGfx),
	        point = intersection.point;

	    if (!floating) {
	      return;
	    }

	    translate(floating, point.x, point.y);

	  }

	  function updateSegmentDraggerPosition(parentGfx, intersection, waypoints) {

	    var draggerGfx = getSegmentDragger(intersection.index, parentGfx),
	        segmentStart = waypoints[intersection.index - 1],
	        segmentEnd = waypoints[intersection.index],
	        point = intersection.point,
	        mid = getMidPoint(segmentStart, segmentEnd),
	        alignment = pointsAligned(segmentStart, segmentEnd),
	        draggerVisual, relativePosition;

	    if (!draggerGfx) {
	      return;
	    }

	    draggerVisual = getVisual(draggerGfx);

	    relativePosition = {
	      x: point.x - mid.x,
	      y: point.y - mid.y
	    };

	    if (alignment === 'v') {

	      // rotate position
	      relativePosition = {
	        x: relativePosition.y,
	        y: relativePosition.x
	      };
	    }

	    translate(draggerVisual, relativePosition.x, relativePosition.y);
	  }

	  eventBus.on('connection.changed', function(event) {
	    updateHandles(event.element);
	  });

	  eventBus.on('connection.remove', function(event) {
	    var gfx = getBendpointsContainer(event.element);

	    if (gfx) {
	      remove$1(gfx);
	    }
	  });

	  eventBus.on('element.marker.update', function(event) {

	    var element = event.element,
	        bendpointsGfx;

	    if (!element.waypoints) {
	      return;
	    }

	    bendpointsGfx = addHandles(element);

	    if (event.add) {
	      classes$1(bendpointsGfx).add(event.marker);
	    } else {
	      classes$1(bendpointsGfx).remove(event.marker);
	    }
	  });

	  eventBus.on('element.mousemove', function(event) {

	    var element = event.element,
	        waypoints = element.waypoints,
	        bendpointsGfx,
	        intersection;

	    if (waypoints) {
	      bendpointsGfx = getBendpointsContainer(element, true);

	      intersection = getConnectionIntersection(canvas, waypoints, event.originalEvent);

	      if (!intersection) {
	        return;
	      }

	      updateFloatingBendpointPosition(bendpointsGfx, intersection);

	      if (!intersection.bendpoint) {
	        updateSegmentDraggerPosition(bendpointsGfx, intersection, waypoints);
	      }

	    }
	  });

	  eventBus.on('element.mousedown', function(event) {

	    var originalEvent = event.originalEvent,
	        element = event.element,
	        waypoints = element.waypoints;

	    if (!waypoints) {
	      return;
	    }

	    return activateBendpointMove(originalEvent, element);
	  });

	  eventBus.on('selection.changed', function(event) {
	    var newSelection = event.newSelection,
	        primary = newSelection[0];

	    if (primary && primary.waypoints) {
	      addHandles(primary);
	    }
	  });

	  eventBus.on('element.hover', function(event) {
	    var element = event.element;

	    if (element.waypoints) {
	      addHandles(element);
	      interactionEvents.registerEvent(event.gfx, 'mousemove', 'element.mousemove');
	    }
	  });

	  eventBus.on('element.out', function(event) {
	    interactionEvents.unregisterEvent(event.gfx, 'mousemove', 'element.mousemove');
	  });

	  // update bendpoint container data attribute on element ID change
	  eventBus.on('element.updateId', function(context) {
	    var element = context.element,
	        newId = context.newId;

	    if (element.waypoints) {
	      var bendpointContainer = getBendpointsContainer(element);

	      if (bendpointContainer) {
	        attr$1(bendpointContainer, { 'data-element-id': newId });
	      }
	    }
	  });

	  // API

	  this.addHandles = addHandles;
	  this.updateHandles = updateHandles;
	  this.getBendpointsContainer = getBendpointsContainer;
	  this.getSegmentDragger = getSegmentDragger;
	}

	Bendpoints.$inject = [
	  'eventBus',
	  'canvas',
	  'interactionEvents',
	  'bendpointMove',
	  'connectionSegmentMove'
	];

	var COMMAND_BENDPOINT_UPDATE = 'connection.updateWaypoints',
	    COMMAND_RECONNECT_START = 'connection.reconnectStart',
	    COMMAND_RECONNECT_END = 'connection.reconnectEnd';

	var round$3 = Math.round;


	/**
	 * A component that implements moving of bendpoints
	 */
	function BendpointMove(injector, eventBus, canvas, dragging, rules, modeling) {

	  // optional connection docking integration
	  var connectionDocking = injector.get('connectionDocking', false);


	  // API
	  this.start = function(event, connection, bendpointIndex, insert) {

	    var type,
	        context,
	        waypoints = connection.waypoints,
	        gfx = canvas.getGraphics(connection);

	    if (!insert && bendpointIndex === 0) {
	      type = COMMAND_RECONNECT_START;
	    } else
	    if (!insert && bendpointIndex === waypoints.length - 1) {
	      type = COMMAND_RECONNECT_END;
	    } else {
	      type = COMMAND_BENDPOINT_UPDATE;
	    }

	    context = {
	      connection: connection,
	      bendpointIndex: bendpointIndex,
	      insert: insert,
	      type: type
	    };

	    var allowed = context.allowed = rules.allowed(context.type, context);

	    if (allowed === false) {
	      return;
	    }

	    dragging.init(event, 'bendpoint.move', {
	      data: {
	        connection: connection,
	        connectionGfx: gfx,
	        context: context
	      }
	    });
	  };

	  eventBus.on('bendpoint.move.hover', function(e) {
	    var context = e.context;

	    context.hover = e.hover;

	    if (e.hover) {
	      // asks whether reconnect / bendpoint move / bendpoint add
	      // is allowed at the given position
	      var allowed = context.allowed = rules.allowed(context.type, context);

	      if (allowed) {
	        context.target = context.hover;
	      }
	    }
	  });

	  eventBus.on([ 'bendpoint.move.out', 'bendpoint.move.cleanup' ], function(event) {
	    var context = event.context;

	    context.target = null;
	    context.allowed = false;
	  });

	  eventBus.on('bendpoint.move.end', function(e) {

	    var context = e.context,
	        connection = context.connection,
	        originalWaypoints = connection.waypoints,
	        newWaypoints = originalWaypoints.slice(),
	        bendpointIndex = context.bendpointIndex,
	        allowed = context.allowed,
	        insert = context.insert,
	        bendpoint,
	        hints;

	    // ensure we have actual pixel values bendpoint
	    // coordinates (important when zoom level was > 1 during move)
	    bendpoint = {
	      x: round$3(e.x),
	      y: round$3(e.y)
	    };

	    if (allowed && context.type === COMMAND_RECONNECT_START) {
	      modeling.reconnectStart(context.connection, context.target, bendpoint);
	    } else if (allowed && context.type === COMMAND_RECONNECT_END) {
	      modeling.reconnectEnd(context.connection, context.target, bendpoint);
	    } else if (allowed !== false && context.type === COMMAND_BENDPOINT_UPDATE) {
	      if (insert) {
	        // insert new bendpoint
	        newWaypoints.splice(bendpointIndex, 0, bendpoint);
	      } else {
	        // swap previous waypoint with the moved one
	        newWaypoints[bendpointIndex] = bendpoint;
	      }

	      // pass hints on the actual moved bendpoint
	      // this is useful for connection and label layouting
	      hints = {
	        bendpointMove: {
	          insert: insert,
	          bendpointIndex: bendpointIndex
	        }
	      };

	      if (connectionDocking) {
	        // (0) temporarily assign new waypoints
	        connection.waypoints = newWaypoints;

	        // (1) crop connection with new waypoints and save them
	        connection.waypoints = connectionDocking.getCroppedWaypoints(connection);
	        newWaypoints = connection.waypoints;

	        // (2) restore original waypoints to not mutate connection directly
	        connection.waypoints = originalWaypoints;
	      }

	      modeling.updateWaypoints(context.connection, filterRedundantWaypoints(newWaypoints), hints);
	    } else {
	      return false;
	    }
	  });
	}

	BendpointMove.$inject = [
	  'injector',
	  'eventBus',
	  'canvas',
	  'dragging',
	  'rules',
	  'modeling'
	];

	var MARKER_OK = 'connect-ok',
	    MARKER_NOT_OK = 'connect-not-ok',
	    MARKER_CONNECT_HOVER = 'connect-hover',
	    MARKER_CONNECT_UPDATING = 'djs-updating',
	    MARKER_ELEMENT_HIDDEN = 'djs-element-hidden';

	var COMMAND_RECONNECT_START$1 = 'connection.reconnectStart',
	    COMMAND_RECONNECT_END$1 = 'connection.reconnectEnd';

	var HIGH_PRIORITY$1 = 1100;

	/**
	 * A component that implements moving of bendpoints
	 */
	function BendpointMovePreview(injector, eventBus, canvas) {

	  var connectionPreview = injector.get('connectionPreview', false),
	      connectionDocking = injector.get('connectionDocking', false);

	  // DRAGGING IMPLEMENTATION

	  eventBus.on('bendpoint.move.start', function(e) {

	    var context = e.context,
	        connection = context.connection,
	        originalWaypoints = connection.waypoints,
	        waypoints = originalWaypoints.slice(),
	        insert = context.insert,
	        idx = context.bendpointIndex;

	    // save waypoints to restore at the end
	    context.originalWaypoints = originalWaypoints;

	    if (insert) {
	      // insert placeholder for bendpoint to-be-added
	      waypoints.splice(idx, 0, { x: e.x, y: e.y });
	    }

	    connection.waypoints = waypoints;

	    // add dragger gfx
	    context.draggerGfx = addBendpoint(canvas.getLayer('overlays'));
	    classes$1(context.draggerGfx).add('djs-dragging');

	    canvas.addMarker(connection, MARKER_CONNECT_UPDATING);
	    canvas.addMarker(connection, MARKER_ELEMENT_HIDDEN);
	  });

	  eventBus.on('bendpoint.move.hover', function(e) {
	    var context = e.context,
	        allowed = context.allowed,
	        hover = context.hover,
	        moveType = context.type;

	    if (e.hover) {
	      canvas.addMarker(hover, MARKER_CONNECT_HOVER);

	      if (
	        moveType !== COMMAND_RECONNECT_START$1 &&
	        moveType !== COMMAND_RECONNECT_END$1
	      ) {
	        return;
	      }

	      if (allowed) {
	        canvas.removeMarker(hover, MARKER_NOT_OK);
	        canvas.addMarker(hover, MARKER_OK);
	      } else if (allowed === false) {
	        canvas.removeMarker(hover, MARKER_OK);
	        canvas.addMarker(hover, MARKER_NOT_OK);
	      }
	    }
	  });

	  eventBus.on([
	    'bendpoint.move.out',
	    'bendpoint.move.cleanup'
	  ], HIGH_PRIORITY$1, function(e) {

	    // remove connect marker
	    // if it was added
	    var hover = e.context.hover;

	    if (hover) {
	      canvas.removeMarker(hover, MARKER_CONNECT_HOVER);
	      canvas.removeMarker(hover, e.context.target ? MARKER_OK : MARKER_NOT_OK);
	    }
	  });

	  eventBus.on('bendpoint.move.move', function(e) {

	    var context = e.context,
	        moveType = context.type,
	        connection = e.connection,
	        originalWaypoints = connection.waypoints,
	        waypoints = originalWaypoints.slice(),
	        bendpoint = { x: e.x, y: e.y },
	        hints;

	    if (connectionPreview) {
	      hints = {
	        source: connection.source,
	        target: connection.target
	      };

	      if (moveType === COMMAND_RECONNECT_START$1) {
	        hints.source = context.target;
	        hints.connectionStart = bendpoint;
	      } else if (moveType === COMMAND_RECONNECT_END$1) {
	        hints.target = context.target;
	        hints.connectionEnd = bendpoint;
	      } else {
	        hints.noCropping = true;
	        hints.noLayout = true;
	        waypoints[context.bendpointIndex] = bendpoint;
	      }

	      if (connectionDocking) {
	        // (0) temporarily assign new waypoints
	        connection.waypoints = waypoints;

	        // (1) crop connection with new waypoints and save them
	        connection.waypoints = connectionDocking.getCroppedWaypoints(connection);
	        waypoints = connection.waypoints;

	        // (2) restore original waypoints to not mutate connection directly
	        connection.waypoints = originalWaypoints;
	      }

	      // remove overlapping points
	      hints.waypoints = filterRedundantWaypoints(waypoints);

	      connectionPreview.drawPreview(context, context.allowed, hints);
	    }

	    // add dragger gfx
	    translate(context.draggerGfx, e.x, e.y);
	  });

	  eventBus.on([
	    'bendpoint.move.end',
	    'bendpoint.move.cancel'
	  ], HIGH_PRIORITY$1, function(e) {

	    var context = e.context,
	        hover = context.hover,
	        connection = context.connection;

	    // reassign original waypoints
	    connection.waypoints = context.originalWaypoints;

	    // remove dragger gfx
	    remove$1(context.draggerGfx);

	    canvas.removeMarker(connection, MARKER_CONNECT_UPDATING);
	    canvas.removeMarker(connection, MARKER_ELEMENT_HIDDEN);

	    if (hover) {
	      canvas.removeMarker(hover, MARKER_OK);
	      canvas.removeMarker(hover, context.target ? MARKER_OK : MARKER_NOT_OK);
	    }

	    if (connectionPreview) {
	      connectionPreview.cleanUp(context);
	    }
	  });
	}

	BendpointMovePreview.$inject = [
	  'injector',
	  'eventBus',
	  'canvas'
	];

	var MARKER_CONNECT_HOVER$1 = 'connect-hover',
	    MARKER_CONNECT_UPDATING$1 = 'djs-updating';


	function axisAdd(point, axis, delta) {
	  return axisSet(point, axis, point[axis] + delta);
	}

	function axisSet(point, axis, value) {
	  return {
	    x: (axis === 'x' ? value : point.x),
	    y: (axis === 'y' ? value : point.y)
	  };
	}

	function axisFenced(position, segmentStart, segmentEnd, axis) {

	  var maxValue = Math.max(segmentStart[axis], segmentEnd[axis]),
	      minValue = Math.min(segmentStart[axis], segmentEnd[axis]);

	  var padding = 20;

	  var fencedValue = Math.min(Math.max(minValue + padding, position[axis]), maxValue - padding);

	  return axisSet(segmentStart, axis, fencedValue);
	}

	function flipAxis(axis) {
	  return axis === 'x' ? 'y' : 'x';
	}

	/**
	 * Get the docking point on the given element.
	 *
	 * Compute a reasonable docking, if non exists.
	 *
	 * @param  {Point} point
	 * @param  {djs.model.Shape} referenceElement
	 * @param  {String} moveAxis (x|y)
	 *
	 * @return {Point}
	 */
	function getDocking(point, referenceElement, moveAxis) {

	  var referenceMid,
	      inverseAxis;

	  if (point.original) {
	    return point.original;
	  } else {
	    referenceMid = getMid(referenceElement);
	    inverseAxis = flipAxis(moveAxis);

	    return axisSet(point, inverseAxis, referenceMid[inverseAxis]);
	  }
	}

	/**
	 * A component that implements moving of bendpoints
	 */
	function ConnectionSegmentMove(
	    injector, eventBus, canvas,
	    dragging, graphicsFactory, modeling) {

	  // optional connection docking integration
	  var connectionDocking = injector.get('connectionDocking', false);


	  // API

	  this.start = function(event, connection, idx) {

	    var context,
	        gfx = canvas.getGraphics(connection),
	        segmentStartIndex = idx - 1,
	        segmentEndIndex = idx,
	        waypoints = connection.waypoints,
	        segmentStart = waypoints[segmentStartIndex],
	        segmentEnd = waypoints[segmentEndIndex],
	        intersection = getConnectionIntersection(canvas, waypoints, event),
	        direction, axis, dragPosition;

	    direction = pointsAligned(segmentStart, segmentEnd);

	    // do not move diagonal connection
	    if (!direction) {
	      return;
	    }

	    // the axis where we are going to move things
	    axis = direction === 'v' ? 'x' : 'y';

	    if (segmentStartIndex === 0) {
	      segmentStart = getDocking(segmentStart, connection.source, axis);
	    }

	    if (segmentEndIndex === waypoints.length - 1) {
	      segmentEnd = getDocking(segmentEnd, connection.target, axis);
	    }

	    if (intersection) {
	      dragPosition = intersection.point;
	    } else {
	      // set to segment center as default
	      dragPosition = {
	        x: (segmentStart.x + segmentEnd.x) / 2,
	        y: (segmentStart.y + segmentEnd.y) / 2
	      };
	    }

	    context = {
	      connection: connection,
	      segmentStartIndex: segmentStartIndex,
	      segmentEndIndex: segmentEndIndex,
	      segmentStart: segmentStart,
	      segmentEnd: segmentEnd,
	      axis: axis,
	      dragPosition: dragPosition
	    };

	    dragging.init(event, dragPosition, 'connectionSegment.move', {
	      cursor: axis === 'x' ? 'resize-ew' : 'resize-ns',
	      data: {
	        connection: connection,
	        connectionGfx: gfx,
	        context: context
	      }
	    });
	  };

	  /**
	   * Crop connection if connection cropping is provided.
	   *
	   * @param {Connection} connection
	   * @param {Array<Point>} newWaypoints
	   *
	   * @return {Array<Point>} cropped connection waypoints
	   */
	  function cropConnection(connection, newWaypoints) {

	    // crop connection, if docking service is provided only
	    if (!connectionDocking) {
	      return newWaypoints;
	    }

	    var oldWaypoints = connection.waypoints,
	        croppedWaypoints;

	    // temporary set new waypoints
	    connection.waypoints = newWaypoints;

	    croppedWaypoints = connectionDocking.getCroppedWaypoints(connection);

	    // restore old waypoints
	    connection.waypoints = oldWaypoints;

	    return croppedWaypoints;
	  }

	  // DRAGGING IMPLEMENTATION

	  function redrawConnection(data) {
	    graphicsFactory.update('connection', data.connection, data.connectionGfx);
	  }

	  function updateDragger(context, segmentOffset, event) {

	    var newWaypoints = context.newWaypoints,
	        segmentStartIndex = context.segmentStartIndex + segmentOffset,
	        segmentStart = newWaypoints[segmentStartIndex],
	        segmentEndIndex = context.segmentEndIndex + segmentOffset,
	        segmentEnd = newWaypoints[segmentEndIndex],
	        axis = flipAxis(context.axis);

	    // make sure the dragger does not move
	    // outside the connection
	    var draggerPosition = axisFenced(event, segmentStart, segmentEnd, axis);

	    // update dragger
	    translate(context.draggerGfx, draggerPosition.x, draggerPosition.y);
	  }

	  /**
	   * Filter waypoints for redundant ones (i.e. on the same axis).
	   * Returns the filtered waypoints and the offset related to the segment move.
	   *
	   * @param {Array<Point>} waypoints
	   * @param {Integer} segmentStartIndex of moved segment start
	   *
	   * @return {Object} { filteredWaypoints, segmentOffset }
	   */
	  function filterRedundantWaypoints(waypoints, segmentStartIndex) {

	    var segmentOffset = 0;

	    var filteredWaypoints = waypoints.filter(function(r, idx) {
	      if (pointsOnLine(waypoints[idx - 1], waypoints[idx + 1], r)) {

	        // remove point and increment offset
	        segmentOffset = idx <= segmentStartIndex ? segmentOffset - 1 : segmentOffset;
	        return false;
	      }

	      // dont remove point
	      return true;
	    });

	    return {
	      waypoints: filteredWaypoints,
	      segmentOffset: segmentOffset
	    };
	  }

	  eventBus.on('connectionSegment.move.start', function(e) {

	    var context = e.context,
	        connection = e.connection,
	        layer = canvas.getLayer('overlays');

	    context.originalWaypoints = connection.waypoints.slice();

	    // add dragger gfx
	    context.draggerGfx = addSegmentDragger(layer, context.segmentStart, context.segmentEnd);
	    classes$1(context.draggerGfx).add('djs-dragging');

	    canvas.addMarker(connection, MARKER_CONNECT_UPDATING$1);
	  });

	  eventBus.on('connectionSegment.move.move', function(e) {

	    var context = e.context,
	        connection = context.connection,
	        segmentStartIndex = context.segmentStartIndex,
	        segmentEndIndex = context.segmentEndIndex,
	        segmentStart = context.segmentStart,
	        segmentEnd = context.segmentEnd,
	        axis = context.axis;

	    var newWaypoints = context.originalWaypoints.slice(),
	        newSegmentStart = axisAdd(segmentStart, axis, e['d' + axis]),
	        newSegmentEnd = axisAdd(segmentEnd, axis, e['d' + axis]);

	    // original waypoint count and added / removed
	    // from start waypoint delta. We use the later
	    // to retrieve the updated segmentStartIndex / segmentEndIndex
	    var waypointCount = newWaypoints.length,
	        segmentOffset = 0;

	    // move segment start / end by axis delta
	    newWaypoints[segmentStartIndex] = newSegmentStart;
	    newWaypoints[segmentEndIndex] = newSegmentEnd;

	    var sourceToSegmentOrientation,
	        targetToSegmentOrientation;

	    // handle first segment
	    if (segmentStartIndex < 2) {
	      sourceToSegmentOrientation = getOrientation(connection.source, newSegmentStart);

	      // first bendpoint, remove first segment if intersecting
	      if (segmentStartIndex === 1) {

	        if (sourceToSegmentOrientation === 'intersect') {
	          newWaypoints.shift();
	          newWaypoints[0] = newSegmentStart;
	          segmentOffset--;
	        }
	      }

	      // docking point, add segment if not intersecting anymore
	      else {
	        if (sourceToSegmentOrientation !== 'intersect') {
	          newWaypoints.unshift(segmentStart);
	          segmentOffset++;
	        }
	      }
	    }

	    // handle last segment
	    if (segmentEndIndex > waypointCount - 3) {
	      targetToSegmentOrientation = getOrientation(connection.target, newSegmentEnd);

	      // last bendpoint, remove last segment if intersecting
	      if (segmentEndIndex === waypointCount - 2) {

	        if (targetToSegmentOrientation === 'intersect') {
	          newWaypoints.pop();
	          newWaypoints[newWaypoints.length - 1] = newSegmentEnd;
	        }
	      }

	      // last bendpoint, remove last segment if intersecting
	      else {
	        if (targetToSegmentOrientation !== 'intersect') {
	          newWaypoints.push(segmentEnd);
	        }
	      }
	    }

	    // update connection waypoints
	    context.newWaypoints = connection.waypoints = cropConnection(connection, newWaypoints);

	    // update dragger position
	    updateDragger(context, segmentOffset, e);

	    // save segmentOffset in context
	    context.newSegmentStartIndex = segmentStartIndex + segmentOffset;

	    // redraw connection
	    redrawConnection(e);
	  });

	  eventBus.on('connectionSegment.move.hover', function(e) {

	    e.context.hover = e.hover;
	    canvas.addMarker(e.hover, MARKER_CONNECT_HOVER$1);
	  });

	  eventBus.on([
	    'connectionSegment.move.out',
	    'connectionSegment.move.cleanup'
	  ], function(e) {

	    // remove connect marker
	    // if it was added
	    var hover = e.context.hover;

	    if (hover) {
	      canvas.removeMarker(hover, MARKER_CONNECT_HOVER$1);
	    }
	  });

	  eventBus.on('connectionSegment.move.cleanup', function(e) {

	    var context = e.context,
	        connection = context.connection;

	    // remove dragger gfx
	    if (context.draggerGfx) {
	      remove$1(context.draggerGfx);
	    }

	    canvas.removeMarker(connection, MARKER_CONNECT_UPDATING$1);
	  });

	  eventBus.on([
	    'connectionSegment.move.cancel',
	    'connectionSegment.move.end'
	  ], function(e) {
	    var context = e.context,
	        connection = context.connection;

	    connection.waypoints = context.originalWaypoints;

	    redrawConnection(e);
	  });

	  eventBus.on('connectionSegment.move.end', function(e) {

	    var context = e.context,
	        connection = context.connection,
	        newWaypoints = context.newWaypoints,
	        newSegmentStartIndex = context.newSegmentStartIndex;

	    // ensure we have actual pixel values bendpoint
	    // coordinates (important when zoom level was > 1 during move)
	    newWaypoints = newWaypoints.map(function(p) {
	      return {
	        original: p.original,
	        x: Math.round(p.x),
	        y: Math.round(p.y)
	      };
	    });

	    // apply filter redunant waypoints
	    var filtered = filterRedundantWaypoints(newWaypoints, newSegmentStartIndex);

	    // get filtered waypoints
	    var filteredWaypoints = filtered.waypoints,
	        croppedWaypoints = cropConnection(connection, filteredWaypoints),
	        segmentOffset = filtered.segmentOffset;

	    var hints = {
	      segmentMove: {
	        segmentStartIndex: context.segmentStartIndex,
	        newSegmentStartIndex: newSegmentStartIndex + segmentOffset
	      }
	    };

	    modeling.updateWaypoints(connection, croppedWaypoints, hints);
	  });
	}

	ConnectionSegmentMove.$inject = [
	  'injector',
	  'eventBus',
	  'canvas',
	  'dragging',
	  'graphicsFactory',
	  'modeling'
	];

	var abs$1 = Math.abs,
	    round$4 = Math.round;


	/**
	 * Snap value to a collection of reference values.
	 *
	 * @param  {Number} value
	 * @param  {Array<Number>} values
	 * @param  {Number} [tolerance=10]
	 *
	 * @return {Number} the value we snapped to or null, if none snapped
	 */
	function snapTo(value, values, tolerance) {
	  tolerance = tolerance === undefined ? 10 : tolerance;

	  var idx, snapValue;

	  for (idx = 0; idx < values.length; idx++) {
	    snapValue = values[idx];

	    if (abs$1(snapValue - value) <= tolerance) {
	      return snapValue;
	    }
	  }
	}


	function topLeft(bounds) {
	  return {
	    x: bounds.x,
	    y: bounds.y
	  };
	}

	function topRight(bounds) {
	  return {
	    x: bounds.x + bounds.width,
	    y: bounds.y
	  };
	}

	function bottomLeft(bounds) {
	  return {
	    x: bounds.x,
	    y: bounds.y + bounds.height
	  };
	}

	function bottomRight(bounds) {
	  return {
	    x: bounds.x + bounds.width,
	    y: bounds.y + bounds.height
	  };
	}

	function mid(bounds, defaultValue) {

	  if (!bounds || isNaN(bounds.x) || isNaN(bounds.y)) {
	    return defaultValue;
	  }

	  return {
	    x: round$4(bounds.x + bounds.width / 2),
	    y: round$4(bounds.y + bounds.height / 2)
	  };
	}


	/**
	 * Retrieve the snap state of the given event.
	 *
	 * @param  {Event} event
	 * @param  {String} axis
	 *
	 * @return {Boolean} the snapped state
	 *
	 */
	function isSnapped(event, axis) {
	  var snapped = event.snapped;

	  if (!snapped) {
	    return false;
	  }

	  if (typeof axis === 'string') {
	    return snapped[axis];
	  }

	  return snapped.x && snapped.y;
	}


	/**
	 * Set the given event as snapped.
	 *
	 * This method may change the x and/or y position of the shape
	 * from the given event!
	 *
	 * @param {Event} event
	 * @param {String} axis
	 * @param {Number|Boolean} value
	 *
	 * @return {Number} old value
	 */
	function setSnapped(event, axis, value) {
	  if (typeof axis !== 'string') {
	    throw new Error('axis must be in [x, y]');
	  }

	  if (typeof value !== 'number' && value !== false) {
	    throw new Error('value must be Number or false');
	  }

	  var delta,
	      previousValue = event[axis];

	  var snapped = event.snapped = (event.snapped || {});


	  if (value === false) {
	    snapped[axis] = false;
	  } else {
	    snapped[axis] = true;

	    delta = value - previousValue;

	    event[axis] += delta;
	    event['d' + axis] += delta;
	  }

	  return previousValue;
	}

	/**
	 * Get children of a shape.
	 *
	 * @param {djs.model.Shape} parent
	 *
	 * @returns {Array<djs.model.Shape|djs.model.Connection>}
	 */
	function getChildren$1(parent) {
	  return parent.children || [];
	}

	var abs$2= Math.abs,
	    round$5 = Math.round;

	var TOLERANCE = 10;


	function BendpointSnapping(eventBus) {

	  function snapTo(values, value) {

	    if (isArray(values)) {
	      var i = values.length;

	      while (i--) if (abs$2(values[i] - value) <= TOLERANCE) {
	        return values[i];
	      }
	    } else {
	      values = +values;
	      var rem = value % values;

	      if (rem < TOLERANCE) {
	        return value - rem;
	      }

	      if (rem > values - TOLERANCE) {
	        return value - rem + values;
	      }
	    }

	    return value;
	  }

	  function mid(element) {
	    if (element.width) {
	      return {
	        x: round$5(element.width / 2 + element.x),
	        y: round$5(element.height / 2 + element.y)
	      };
	    }
	  }

	  // connection segment snapping //////////////////////

	  function getConnectionSegmentSnaps(context) {

	    var snapPoints = context.snapPoints,
	        connection = context.connection,
	        waypoints = connection.waypoints,
	        segmentStart = context.segmentStart,
	        segmentStartIndex = context.segmentStartIndex,
	        segmentEnd = context.segmentEnd,
	        segmentEndIndex = context.segmentEndIndex,
	        axis = context.axis;

	    if (snapPoints) {
	      return snapPoints;
	    }

	    var referenceWaypoints = [
	      waypoints[segmentStartIndex - 1],
	      segmentStart,
	      segmentEnd,
	      waypoints[segmentEndIndex + 1]
	    ];

	    if (segmentStartIndex < 2) {
	      referenceWaypoints.unshift(mid(connection.source));
	    }

	    if (segmentEndIndex > waypoints.length - 3) {
	      referenceWaypoints.unshift(mid(connection.target));
	    }

	    context.snapPoints = snapPoints = { horizontal: [] , vertical: [] };

	    forEach(referenceWaypoints, function(p) {
	      // we snap on existing bendpoints only,
	      // not placeholders that are inserted during add
	      if (p) {
	        p = p.original || p;

	        if (axis === 'y') {
	          snapPoints.horizontal.push(p.y);
	        }

	        if (axis === 'x') {
	          snapPoints.vertical.push(p.x);
	        }
	      }
	    });

	    return snapPoints;
	  }

	  eventBus.on('connectionSegment.move.move', 1500, function(event) {
	    var context = event.context,
	        snapPoints = getConnectionSegmentSnaps(context),
	        x = event.x,
	        y = event.y,
	        sx, sy;

	    if (!snapPoints) {
	      return;
	    }

	    // snap
	    sx = snapTo(snapPoints.vertical, x);
	    sy = snapTo(snapPoints.horizontal, y);


	    // correction x/y
	    var cx = (x - sx),
	        cy = (y - sy);

	    // update delta
	    assign(event, {
	      dx: event.dx - cx,
	      dy: event.dy - cy,
	      x: sx,
	      y: sy
	    });

	    // only set snapped if actually snapped
	    if (cx || snapPoints.vertical.indexOf(x) !== -1) {
	      setSnapped(event, 'x', sx);
	    }

	    if (cy || snapPoints.horizontal.indexOf(y) !== -1) {
	      setSnapped(event, 'y', sy);
	    }
	  });


	  // bendpoint snapping //////////////////////

	  function getBendpointSnaps(context) {

	    var snapPoints = context.snapPoints,
	        waypoints = context.connection.waypoints,
	        bendpointIndex = context.bendpointIndex;

	    if (snapPoints) {
	      return snapPoints;
	    }

	    var referenceWaypoints = [ waypoints[bendpointIndex - 1], waypoints[bendpointIndex + 1] ];

	    context.snapPoints = snapPoints = { horizontal: [] , vertical: [] };

	    forEach(referenceWaypoints, function(p) {
	      // we snap on existing bendpoints only,
	      // not placeholders that are inserted during add
	      if (p) {
	        p = p.original || p;

	        snapPoints.horizontal.push(p.y);
	        snapPoints.vertical.push(p.x);
	      }
	    });

	    return snapPoints;
	  }


	  eventBus.on([ 'bendpoint.move.move', 'bendpoint.move.end' ], 1500, function(event) {

	    var context = event.context,
	        snapPoints = getBendpointSnaps(context),
	        target = context.target,
	        targetMid = target && mid(target),
	        x = event.x,
	        y = event.y,
	        sx, sy;

	    if (!snapPoints) {
	      return;
	    }

	    // snap
	    sx = snapTo(targetMid ? snapPoints.vertical.concat([ targetMid.x ]) : snapPoints.vertical, x);
	    sy = snapTo(targetMid ? snapPoints.horizontal.concat([ targetMid.y ]) : snapPoints.horizontal, y);

	    // correction x/y
	    var cx = (x - sx),
	        cy = (y - sy);

	    // update delta
	    assign(event, {
	      dx: event.dx - cx,
	      dy: event.dy - cy,
	      x: event.x - cx,
	      y: event.y - cy
	    });

	    // only set snapped if actually snapped
	    if (cx || snapPoints.vertical.indexOf(x) !== -1) {
	      setSnapped(event, 'x', sx);
	    }

	    if (cy || snapPoints.horizontal.indexOf(y) !== -1) {
	      setSnapped(event, 'y', sy);
	    }
	  });
	}


	BendpointSnapping.$inject = [ 'eventBus' ];

	var BendpointsModule = {
	  __depends__: [
	    DraggingModule,
	    RulesModule
	  ],
	  __init__: [ 'bendpoints', 'bendpointSnapping', 'bendpointMovePreview' ],
	  bendpoints: [ 'type', Bendpoints ],
	  bendpointMove: [ 'type', BendpointMove ],
	  bendpointMovePreview: [ 'type', BendpointMovePreview ],
	  connectionSegmentMove: [ 'type', ConnectionSegmentMove ],
	  bendpointSnapping: [ 'type', BendpointSnapping ]
	};

	function Connect(eventBus, dragging, modeling, rules) {

	  // rules

	  function canConnect(source, target) {
	    return rules.allowed('connection.create', {
	      source: source,
	      target: target
	    });
	  }


	  // event handlers

	  eventBus.on('connect.hover', function(event) {
	    var context = event.context,
	        source = context.source,
	        hover = event.hover,
	        canExecute;

	    canExecute = context.canExecute = canConnect(source, hover);

	    // simply ignore hover
	    if (canExecute === null) {
	      return;
	    }

	    context.target = hover;
	  });

	  eventBus.on([ 'connect.out', 'connect.cleanup' ], function(event) {
	    var context = event.context;

	    context.target = null;
	    context.canExecute = false;
	  });

	  eventBus.on('connect.end', function(event) {

	    var context = event.context,
	        source = context.source,
	        sourcePosition = context.sourcePosition,
	        target = context.target,
	        targetPosition = {
	          x: event.x,
	          y: event.y
	        },
	        canExecute = context.canExecute || canConnect(source, target);

	    if (!canExecute) {
	      return false;
	    }

	    var attrs = null,
	        hints = {
	          connectionStart: sourcePosition,
	          connectionEnd: targetPosition
	        };

	    if (typeof canExecute === 'object') {
	      attrs = canExecute;
	    }

	    modeling.connect(source, target, attrs, hints);
	  });


	  // API

	  /**
	   * Start connect operation.
	   *
	   * @param {DOMEvent} event
	   * @param {djs.model.Base} source
	   * @param {Point} [sourcePosition]
	   * @param {Boolean} [autoActivate=false]
	   */
	  this.start = function(event, source, sourcePosition, autoActivate) {

	    if (typeof sourcePosition !== 'object') {
	      autoActivate = sourcePosition;
	      sourcePosition = getMid(source);
	    }

	    dragging.init(event, 'connect', {
	      autoActivate: autoActivate,
	      data: {
	        shape: source,
	        context: {
	          source: source,
	          sourcePosition: sourcePosition
	        }
	      }
	    });
	  };
	}

	Connect.$inject = [
	  'eventBus',
	  'dragging',
	  'modeling',
	  'rules'
	];

	var HIGH_PRIORITY$2 = 1100,
	    LOW_PRIORITY$4 = 900;

	var MARKER_OK$1 = 'connect-ok',
	    MARKER_NOT_OK$1 = 'connect-not-ok';

	/**
	 * Shows connection preview during connect.
	 *
	 * @param {didi.Injector} injector
	 * @param {EventBus} eventBus
	 * @param {Canvas} canvas
	 */
	function ConnectPreview(injector, eventBus, canvas) {
	  var connectionPreview = injector.get('connectionPreview', false);

	  eventBus.on('connect.move', function(event) {
	    var context = event.context,
	        source = context.source,
	        target = context.target,
	        canConnect = context.canExecute;

	    var endPosition = {
	      x: event.x,
	      y: event.y
	    };


	    if (connectionPreview) {
	      connectionPreview.drawPreview(context, canConnect, {
	        source: source,
	        target: target,
	        connectionEnd: endPosition
	      });
	    }
	  });

	  eventBus.on('connect.hover', LOW_PRIORITY$4, function(event) {
	    var context = event.context,
	        hover = event.hover,
	        canExecute = context.canExecute;

	    // ignore hover
	    if (canExecute === null) {
	      return;
	    }

	    canvas.addMarker(hover, canExecute ? MARKER_OK$1 : MARKER_NOT_OK$1);
	  });

	  eventBus.on([ 'connect.out', 'connect.cleanup' ], HIGH_PRIORITY$2, function(event) {
	    var context = event.context;

	    // remove marker before target is removed from context
	    if (context.target) {
	      canvas.removeMarker(context.target, context.canExecute ? MARKER_OK$1 : MARKER_NOT_OK$1);
	    }
	  });

	  eventBus.on('connect.cleanup', function(event) {
	    if (connectionPreview) {
	      connectionPreview.cleanUp(event.context);
	    }
	  });
	}

	ConnectPreview.$inject = [
	  'injector',
	  'eventBus',
	  'canvas'
	];

	var ConnectModule = {
	  __depends__: [
	    SelectionModule,
	    RulesModule,
	    DraggingModule
	  ],
	  __init__: [
	    'connectPreview'
	  ],
	  connect: [ 'type', Connect ],
	  connectPreview: [ 'type', ConnectPreview ]
	};

	var MARKER_CONNECTION_PREVIEW = 'djs-connection-preview';

	/**
	 * Draws connection preview. Optionally, this can use layouter and connection docking to draw
	 * better looking previews.
	 *
	 * @param {didi.Injector} injector
	 * @param {Canvas} canvas
	 * @param {GraphicsFactory} graphicsFactory
	 * @param {ElementFactory} elementFactory
	 */
	function ConnectionPreview(
	    injector,
	    canvas,
	    graphicsFactory,
	    elementFactory
	) {
	  this._canvas = canvas;
	  this._graphicsFactory = graphicsFactory;
	  this._elementFactory = elementFactory;

	  // optional components
	  this._connectionDocking = injector.get('connectionDocking', false);
	  this._layouter = injector.get('layouter', false);
	}

	ConnectionPreview.$inject = [
	  'injector',
	  'canvas',
	  'graphicsFactory',
	  'elementFactory'
	];

	/**
	 * Draw connection preview.
	 *
	 * Provide at least one of <source, connectionStart> and <target, connectionEnd> to create a preview.
	 * In the clean up stage, call `connectionPreview#cleanUp` with the context to remove preview.
	 *
	 * @param {Object} context
	 * @param {Object|boolean} canConnect
	 * @param {Object} hints
	 * @param {djs.model.shape} [hints.source] source element
	 * @param {djs.model.shape} [hints.target] target element
	 * @param {Point} [hints.connectionStart] connection preview start
	 * @param {Point} [hints.connectionEnd] connection preview end
	 * @param {Array<Point>} [hints.waypoints] provided waypoints for preview
	 * @param {boolean} [hints.noLayout] true if preview should not be laid out
	 * @param {boolean} [hints.noCropping] true if preview should not be cropped
	 * @param {boolean} [hints.noNoop] true if simple connection should not be drawn
	 */
	ConnectionPreview.prototype.drawPreview = function(context, canConnect, hints) {

	  hints = hints || {};

	  var connectionPreviewGfx = context.connectionPreviewGfx,
	      getConnection = context.getConnection,
	      source = hints.source,
	      target = hints.target,
	      waypoints = hints.waypoints,
	      connectionStart = hints.connectionStart,
	      connectionEnd = hints.connectionEnd,
	      noLayout = hints.noLayout,
	      noCropping = hints.noCropping,
	      noNoop = hints.noNoop,
	      connection;

	  var self = this;

	  if (!connectionPreviewGfx) {
	    connectionPreviewGfx = context.connectionPreviewGfx = this.createConnectionPreviewGfx();
	  }

	  clear$1(connectionPreviewGfx);

	  if (!getConnection) {
	    getConnection = context.getConnection = cacheReturnValues(function(canConnect, source, target) {
	      return self.getConnection(canConnect, source, target);
	    });
	  }

	  if (canConnect) {
	    connection = getConnection(canConnect, source, target);
	  }

	  if (!connection) {
	    !noNoop && this.drawNoopPreview(connectionPreviewGfx, hints);
	    return;
	  }

	  connection.waypoints = waypoints || [];

	  // optional layout
	  if (this._layouter && !noLayout) {
	    connection.waypoints = this._layouter.layoutConnection(connection, {
	      source: source,
	      target: target,
	      connectionStart: connectionStart,
	      connectionEnd: connectionEnd
	    });
	  }

	  // fallback if no waypoints were provided nor created with layouter
	  if (!connection.waypoints || !connection.waypoints.length) {
	    connection.waypoints = [
	      source ? getMid(source) : connectionStart,
	      target ? getMid(target) : connectionEnd
	    ];
	  }

	  // optional cropping
	  if (this._connectionDocking && (source || target) && !noCropping) {
	    connection.waypoints = this._connectionDocking.getCroppedWaypoints(connection, source, target);
	  }

	  this._graphicsFactory.drawConnection(connectionPreviewGfx, connection);
	};

	/**
	 * Draw simple connection between source and target or provided points.
	 *
	 * @param {SVGElement} connectionPreviewGfx container for the connection
	 * @param {Object} hints
	 * @param {djs.model.shape} [hints.source] source element
	 * @param {djs.model.shape} [hints.target] target element
	 * @param {Point} [hints.connectionStart] required if source is not provided
	 * @param {Point} [hints.connectionEnd] required if target is not provided
	 */
	ConnectionPreview.prototype.drawNoopPreview = function(connectionPreviewGfx, hints) {
	  var source = hints.source,
	      target = hints.target,
	      start = hints.connectionStart || getMid(source),
	      end = hints.connectionEnd || getMid(target);

	  var waypoints = this.cropWaypoints(start, end, source, target);

	  var connection = this.createNoopConnection(waypoints[0], waypoints[1]);

	  append(connectionPreviewGfx, connection);
	};

	/**
	 * Return cropped waypoints.
	 *
	 * @param {Point} start
	 * @param {Point} end
	 * @param {djs.model.shape} source
	 * @param {djs.model.shape} target
	 *
	 * @returns {Array}
	 */
	ConnectionPreview.prototype.cropWaypoints = function(start, end, source, target) {
	  var graphicsFactory = this._graphicsFactory,
	      sourcePath = source && graphicsFactory.getShapePath(source),
	      targetPath = target && graphicsFactory.getShapePath(target),
	      connectionPath = graphicsFactory.getConnectionPath({ waypoints: [ start, end ] });

	  start = (source && getElementLineIntersection(sourcePath, connectionPath, true)) || start;
	  end = (target && getElementLineIntersection(targetPath, connectionPath, false)) || end;

	  return [ start, end ];
	};

	/**
	 * Remove connection preview container if it exists.
	 *
	 * @param {Object} [context]
	 * @param {SVGElement} [context.connectionPreviewGfx] preview container
	 */
	ConnectionPreview.prototype.cleanUp = function(context) {
	  if (context && context.connectionPreviewGfx) {
	    remove$1(context.connectionPreviewGfx);
	  }
	};

	/**
	 * Get connection that connects source and target.
	 *
	 * @param {Object|boolean} canConnect
	 *
	 * @returns {djs.model.connection}
	 */
	ConnectionPreview.prototype.getConnection = function(canConnect) {
	  var attrs = ensureConnectionAttrs(canConnect);

	  return this._elementFactory.createConnection(attrs);
	};


	/**
	 * Add and return preview graphics.
	 *
	 * @returns {SVGElement}
	 */
	ConnectionPreview.prototype.createConnectionPreviewGfx = function() {
	  var gfx = create('g');

	  attr$1(gfx, {
	    pointerEvents: 'none'
	  });

	  classes$1(gfx).add(MARKER_CONNECTION_PREVIEW);

	  append(this._canvas.getDefaultLayer(), gfx);

	  return gfx;
	};

	/**
	 * Create and return simple connection.
	 *
	 * @param {Point} start
	 * @param {Point} end
	 *
	 * @returns {SVGElement}
	 */
	ConnectionPreview.prototype.createNoopConnection = function(start, end) {
	  var connection = create('polyline');

	  attr$1(connection, {
	    'stroke': '#333',
	    'strokeDasharray': [ 1 ],
	    'strokeWidth': 2,
	    'pointer-events': 'none'
	  });

	  attr$1(connection, { 'points': [ start.x, start.y, end.x, end.y ] });

	  return connection;
	};

	// helpers //////////

	/**
	 * Returns function that returns cached return values referenced by stringified first argument.
	 *
	 * @param {Function} fn
	 *
	 * @return {Function}
	 */
	function cacheReturnValues(fn) {
	  var returnValues = {};

	  /**
	   * Return cached return value referenced by stringified first argument.
	   *
	   * @returns {*}
	   */
	  return function(firstArgument) {
	    var key = JSON.stringify(firstArgument);

	    var returnValue = returnValues[key];

	    if (!returnValue) {
	      returnValue = returnValues[key] = fn.apply(null, arguments);
	    }

	    return returnValue;
	  };
	}

	/**
	 * Ensure connection attributes is object.
	 *
	 * @param {Object|boolean} canConnect
	 *
	 * @returns {Object}
	 */
	function ensureConnectionAttrs(canConnect) {
	  if (isObject(canConnect)) {
	    return canConnect;
	  } else {
	    return {};
	  }
	}

	var ConnectionPreviewModule = {
	  __init__: [ 'connectionPreview' ],
	  connectionPreview: [ 'type', ConnectionPreview ]
	};

	var min = Math.min,
	    max$1 = Math.max;

	function preventDefault$1(e) {
	  e.preventDefault();
	}

	function stopPropagation$1(e) {
	  e.stopPropagation();
	}

	function isTextNode(node) {
	  return node.nodeType === Node.TEXT_NODE;
	}

	function toArray(nodeList) {
	  return [].slice.call(nodeList);
	}

	/**
	 * Initializes a container for a content editable div.
	 *
	 * Structure:
	 *
	 * container
	 *   parent
	 *     content
	 *     resize-handle
	 *
	 * @param {object} options
	 * @param {DOMElement} options.container The DOM element to append the contentContainer to
	 * @param {Function} options.keyHandler Handler for key events
	 * @param {Function} options.resizeHandler Handler for resize events
	 */
	function TextBox(options) {
	  this.container = options.container;

	  this.parent = domify(
	    '<div class="djs-direct-editing-parent">' +
	      '<div class="djs-direct-editing-content" contenteditable="true"></div>' +
	    '</div>'
	  );

	  this.content = query('[contenteditable]', this.parent);

	  this.keyHandler = options.keyHandler || function() {};
	  this.resizeHandler = options.resizeHandler || function() {};

	  this.autoResize = bind(this.autoResize, this);
	  this.handlePaste = bind(this.handlePaste, this);
	}


	/**
	 * Create a text box with the given position, size, style and text content
	 *
	 * @param {Object} bounds
	 * @param {Number} bounds.x absolute x position
	 * @param {Number} bounds.y absolute y position
	 * @param {Number} [bounds.width] fixed width value
	 * @param {Number} [bounds.height] fixed height value
	 * @param {Number} [bounds.maxWidth] maximum width value
	 * @param {Number} [bounds.maxHeight] maximum height value
	 * @param {Number} [bounds.minWidth] minimum width value
	 * @param {Number} [bounds.minHeight] minimum height value
	 * @param {Object} [style]
	 * @param {String} value text content
	 *
	 * @return {DOMElement} The created content DOM element
	 */
	TextBox.prototype.create  = function(bounds, style, value, options) {
	  var self = this;

	  var parent = this.parent,
	      content = this.content,
	      container = this.container;

	  options = this.options = options || {};

	  style = this.style = style || {};

	  var parentStyle = pick(style, [
	    'width',
	    'height',
	    'maxWidth',
	    'maxHeight',
	    'minWidth',
	    'minHeight',
	    'left',
	    'top',
	    'backgroundColor',
	    'position',
	    'overflow',
	    'border',
	    'wordWrap',
	    'textAlign',
	    'outline',
	    'transform'
	  ]);

	  assign(parent.style, {
	    width: bounds.width + 'px',
	    height: bounds.height + 'px',
	    maxWidth: bounds.maxWidth + 'px',
	    maxHeight: bounds.maxHeight + 'px',
	    minWidth: bounds.minWidth + 'px',
	    minHeight: bounds.minHeight + 'px',
	    left: bounds.x + 'px',
	    top: bounds.y + 'px',
	    backgroundColor: '#ffffff',
	    position: 'absolute',
	    overflow: 'visible',
	    border: '1px solid #ccc',
	    boxSizing: 'border-box',
	    wordWrap: 'normal',
	    textAlign: 'center',
	    outline: 'none'
	  }, parentStyle);

	  var contentStyle = pick(style, [
	    'fontFamily',
	    'fontSize',
	    'fontWeight',
	    'lineHeight',
	    'padding',
	    'paddingTop',
	    'paddingRight',
	    'paddingBottom',
	    'paddingLeft'
	  ]);

	  assign(content.style, {
	    boxSizing: 'border-box',
	    width: '100%',
	    outline: 'none',
	    wordWrap: 'break-word'
	  }, contentStyle);

	  if (options.centerVertically) {
	    assign(content.style, {
	      position: 'absolute',
	      top: '50%',
	      transform: 'translate(0, -50%)'
	    }, contentStyle);
	  }

	  content.innerText = value;

	  componentEvent.bind(content, 'keydown', this.keyHandler);
	  componentEvent.bind(content, 'mousedown', stopPropagation$1);
	  componentEvent.bind(content, 'paste', self.handlePaste);

	  if (options.autoResize) {
	    componentEvent.bind(content, 'input', this.autoResize);
	  }

	  if (options.resizable) {
	    this.resizable(style);
	  }

	  container.appendChild(parent);

	  // set selection to end of text
	  this.setSelection(content.lastChild, content.lastChild && content.lastChild.length);

	  return parent;
	};

	/**
	 * Intercept paste events to remove formatting from pasted text.
	 */
	TextBox.prototype.handlePaste = function(e) {
	  var options = this.options,
	      style = this.style;

	  e.preventDefault();

	  var text;

	  if (e.clipboardData) {

	    // Chrome, Firefox, Safari
	    text = e.clipboardData.getData('text/plain');
	  } else {

	    // Internet Explorer
	    text = window.clipboardData.getData('Text');
	  }

	  this.insertText(text);

	  if (options.autoResize) {
	    var hasResized = this.autoResize(style);

	    if (hasResized) {
	      this.resizeHandler(hasResized);
	    }
	  }
	};

	TextBox.prototype.insertText = function(text) {

	  // insertText command not supported by Internet Explorer
	  var success = document.execCommand('insertText', false, text);

	  if (success) {
	    return;
	  }

	  this._insertTextIE(text);
	};

	TextBox.prototype._insertTextIE = function(text) {

	  // Internet Explorer
	  var range = this.getSelection(),
	      startContainer = range.startContainer,
	      endContainer = range.endContainer,
	      startOffset = range.startOffset,
	      endOffset = range.endOffset,
	      commonAncestorContainer = range.commonAncestorContainer;

	  var childNodesArray = toArray(commonAncestorContainer.childNodes);

	  var container,
	      offset;

	  if (isTextNode(commonAncestorContainer)) {
	    var containerTextContent = startContainer.textContent;

	    startContainer.textContent =
	      containerTextContent.substring(0, startOffset)
	      + text
	      + containerTextContent.substring(endOffset);

	    container = startContainer;
	    offset = startOffset + text.length;

	  } else if (startContainer === this.content && endContainer === this.content) {
	    var textNode = document.createTextNode(text);

	    this.content.insertBefore(textNode, childNodesArray[startOffset]);

	    container = textNode;
	    offset = textNode.textContent.length;
	  } else {
	    var startContainerChildIndex = childNodesArray.indexOf(startContainer),
	        endContainerChildIndex = childNodesArray.indexOf(endContainer);

	    childNodesArray.forEach(function(childNode, index) {

	      if (index === startContainerChildIndex) {
	        childNode.textContent =
	          startContainer.textContent.substring(0, startOffset) +
	          text +
	          endContainer.textContent.substring(endOffset);
	      } else if (index > startContainerChildIndex && index <= endContainerChildIndex) {
	        remove(childNode);
	      }
	    });

	    container = startContainer;
	    offset = startOffset + text.length;
	  }

	  if (container && offset !== undefined) {

	    // is necessary in Internet Explorer
	    setTimeout(function() {
	      self.setSelection(container, offset);
	    });
	  }
	};

	/**
	 * Automatically resize element vertically to fit its content.
	 */
	TextBox.prototype.autoResize = function() {
	  var parent = this.parent,
	      content = this.content;

	  var fontSize = parseInt(this.style.fontSize) || 12;

	  if (content.scrollHeight > parent.offsetHeight ||
	      content.scrollHeight < parent.offsetHeight - fontSize) {
	    var bounds = parent.getBoundingClientRect();

	    var height = content.scrollHeight;
	    parent.style.height = height + 'px';

	    this.resizeHandler({
	      width: bounds.width,
	      height: bounds.height,
	      dx: 0,
	      dy: height - bounds.height
	    });
	  }
	};

	/**
	 * Make an element resizable by adding a resize handle.
	 */
	TextBox.prototype.resizable = function() {
	  var self = this;

	  var parent = this.parent,
	      resizeHandle = this.resizeHandle;

	  var minWidth = parseInt(this.style.minWidth) || 0,
	      minHeight = parseInt(this.style.minHeight) || 0,
	      maxWidth = parseInt(this.style.maxWidth) || Infinity,
	      maxHeight = parseInt(this.style.maxHeight) || Infinity;

	  if (!resizeHandle) {
	    resizeHandle = this.resizeHandle = domify(
	      '<div class="djs-direct-editing-resize-handle"></div>'
	    );

	    var startX, startY, startWidth, startHeight;

	    var onMouseDown = function(e) {
	      preventDefault$1(e);
	      stopPropagation$1(e);

	      startX = e.clientX;
	      startY = e.clientY;

	      var bounds = parent.getBoundingClientRect();

	      startWidth = bounds.width;
	      startHeight = bounds.height;

	      componentEvent.bind(document, 'mousemove', onMouseMove);
	      componentEvent.bind(document, 'mouseup', onMouseUp);
	    };

	    var onMouseMove = function(e) {
	      preventDefault$1(e);
	      stopPropagation$1(e);

	      var newWidth = min(max$1(startWidth + e.clientX - startX, minWidth), maxWidth);
	      var newHeight = min(max$1(startHeight + e.clientY - startY, minHeight), maxHeight);

	      parent.style.width = newWidth + 'px';
	      parent.style.height = newHeight + 'px';

	      self.resizeHandler({
	        width: startWidth,
	        height: startHeight,
	        dx: e.clientX - startX,
	        dy: e.clientY - startY
	      });
	    };

	    var onMouseUp = function(e) {
	      preventDefault$1(e);
	      stopPropagation$1(e);

	      componentEvent.unbind(document,'mousemove', onMouseMove, false);
	      componentEvent.unbind(document, 'mouseup', onMouseUp, false);
	    };

	    componentEvent.bind(resizeHandle, 'mousedown', onMouseDown);
	  }

	  assign(resizeHandle.style, {
	    position: 'absolute',
	    bottom: '0px',
	    right: '0px',
	    cursor: 'nwse-resize',
	    width: '0',
	    height: '0',
	    borderTop: (parseInt(this.style.fontSize) / 4 || 3) + 'px solid transparent',
	    borderRight: (parseInt(this.style.fontSize) / 4 || 3) + 'px solid #ccc',
	    borderBottom: (parseInt(this.style.fontSize) / 4 || 3) + 'px solid #ccc',
	    borderLeft: (parseInt(this.style.fontSize) / 4 || 3) + 'px solid transparent'
	  });

	  parent.appendChild(resizeHandle);
	};


	/**
	 * Clear content and style of the textbox, unbind listeners and
	 * reset CSS style.
	 */
	TextBox.prototype.destroy = function() {
	  var parent = this.parent,
	      content = this.content,
	      resizeHandle = this.resizeHandle;

	  // clear content
	  content.innerText = '';

	  // clear styles
	  parent.removeAttribute('style');
	  content.removeAttribute('style');

	  componentEvent.unbind(content, 'keydown', this.keyHandler);
	  componentEvent.unbind(content, 'mousedown', stopPropagation$1);
	  componentEvent.unbind(content, 'input', this.autoResize);
	  componentEvent.unbind(content, 'paste', this.handlePaste);

	  if (resizeHandle) {
	    resizeHandle.removeAttribute('style');

	    remove(resizeHandle);
	  }

	  remove(parent);
	};


	TextBox.prototype.getValue = function() {
	  return this.content.innerText.trim();
	};


	TextBox.prototype.getSelection = function() {
	  var selection = window.getSelection(),
	      range = selection.getRangeAt(0);

	  return range;
	};


	TextBox.prototype.setSelection = function(container, offset) {
	  var range = document.createRange();

	  if (container === null) {
	    range.selectNodeContents(this.content);
	  } else {
	    range.setStart(container, offset);
	    range.setEnd(container, offset);
	  }

	  var selection = window.getSelection();

	  selection.removeAllRanges();
	  selection.addRange(range);
	};

	/**
	 * A direct editing component that allows users
	 * to edit an elements text directly in the diagram
	 *
	 * @param {EventBus} eventBus the event bus
	 */
	function DirectEditing(eventBus, canvas) {

	  this._eventBus = eventBus;

	  this._providers = [];
	  this._textbox = new TextBox({
	    container: canvas.getContainer(),
	    keyHandler: bind(this._handleKey, this),
	    resizeHandler: bind(this._handleResize, this)
	  });
	}

	DirectEditing.$inject = [ 'eventBus', 'canvas' ];


	/**
	 * Register a direct editing provider

	 * @param {Object} provider the provider, must expose an #activate(element) method that returns
	 *                          an activation context ({ bounds: {x, y, width, height }, text }) if
	 *                          direct editing is available for the given element.
	 *                          Additionally the provider must expose a #update(element, value) method
	 *                          to receive direct editing updates.
	 */
	DirectEditing.prototype.registerProvider = function(provider) {
	  this._providers.push(provider);
	};


	/**
	 * Returns true if direct editing is currently active
	 *
	 * @return {Boolean}
	 */
	DirectEditing.prototype.isActive = function() {
	  return !!this._active;
	};


	/**
	 * Cancel direct editing, if it is currently active
	 */
	DirectEditing.prototype.cancel = function() {
	  if (!this._active) {
	    return;
	  }

	  this._fire('cancel');
	  this.close();
	};


	DirectEditing.prototype._fire = function(event, context) {
	  this._eventBus.fire('directEditing.' + event, context || { active: this._active });
	};

	DirectEditing.prototype.close = function() {
	  this._textbox.destroy();

	  this._fire('deactivate');

	  this._active = null;

	  this.resizable = undefined;
	};


	DirectEditing.prototype.complete = function() {

	  var active = this._active;

	  if (!active) {
	    return;
	  }

	  var containerBounds,
	      previousBounds = active.context.bounds,
	      newBounds = this.$textbox.getBoundingClientRect(),
	      newText = this.getValue(),
	      previousText = active.context.text;

	  if (
	    newText !== previousText ||
	    newBounds.height !== previousBounds.height ||
	    newBounds.width !== previousBounds.width
	  ) {
	    containerBounds = this._textbox.container.getBoundingClientRect();

	    active.provider.update(active.element, newText, active.context.text, {
	      x: newBounds.left - containerBounds.left,
	      y: newBounds.top - containerBounds.top,
	      width: newBounds.width,
	      height: newBounds.height
	    });
	  }

	  this._fire('complete');

	  this.close();
	};


	DirectEditing.prototype.getValue = function() {
	  return this._textbox.getValue();
	};


	DirectEditing.prototype._handleKey = function(e) {

	  // stop bubble
	  e.stopPropagation();

	  var key = e.keyCode || e.charCode;

	  // ESC
	  if (key === 27) {
	    e.preventDefault();
	    return this.cancel();
	  }

	  // Enter
	  if (key === 13 && !e.shiftKey) {
	    e.preventDefault();
	    return this.complete();
	  }
	};


	DirectEditing.prototype._handleResize = function(event) {
	  this._fire('resize', event);
	};


	/**
	 * Activate direct editing on the given element
	 *
	 * @param {Object} ElementDescriptor the descriptor for a shape or connection
	 * @return {Boolean} true if the activation was possible
	 */
	DirectEditing.prototype.activate = function(element) {
	  if (this.isActive()) {
	    this.cancel();
	  }

	  // the direct editing context
	  var context;

	  var provider = find(this._providers, function(p) {
	    return (context = p.activate(element)) ? p : null;
	  });

	  // check if activation took place
	  if (context) {
	    this.$textbox = this._textbox.create(
	      context.bounds,
	      context.style,
	      context.text,
	      context.options
	    );

	    this._active = {
	      element: element,
	      context: context,
	      provider: provider
	    };

	    if (context.options && context.options.resizable) {
	      this.resizable = true;
	    }

	    this._fire('activate');
	  }

	  return !!context;
	};

	var DirectEditingModule = {
	  __depends__: [
	    InteractionEventsModule
	  ],
	  __init__: [ 'directEditing' ],
	  directEditing: [ 'type', DirectEditing ]
	};

	var entrySelector = '.entry';


	/**
	 * A context pad that displays element specific, contextual actions next
	 * to a diagram element.
	 *
	 * @param {Object} config
	 * @param {Boolean|Object} [config.scale={ min: 1.0, max: 1.5 }]
	 * @param {Number} [config.scale.min]
	 * @param {Number} [config.scale.max]
	 * @param {EventBus} eventBus
	 * @param {Overlays} overlays
	 */
	function ContextPad(config, eventBus, overlays) {

	  this._providers = [];

	  this._eventBus = eventBus;
	  this._overlays = overlays;

	  var scale = isDefined(config && config.scale) ? config.scale : {
	    min: 1,
	    max: 1.5
	  };

	  this._overlaysConfig = {
	    position: {
	      right: -9,
	      top: -6
	    },
	    scale: scale
	  };

	  this._current = null;

	  this._init();
	}

	ContextPad.$inject = [
	  'config.contextPad',
	  'eventBus',
	  'overlays'
	];


	/**
	 * Registers events needed for interaction with other components
	 */
	ContextPad.prototype._init = function() {

	  var eventBus = this._eventBus;

	  var self = this;

	  eventBus.on('selection.changed', function(e) {

	    var selection = e.newSelection;

	    if (selection.length === 1) {
	      self.open(selection[0]);
	    } else {
	      self.close();
	    }
	  });

	  eventBus.on('elements.delete', function(event) {
	    var elements = event.elements;

	    forEach(elements, function(e) {
	      if (self.isOpen(e)) {
	        self.close();
	      }
	    });
	  });

	  eventBus.on('element.changed', function(event) {
	    var element = event.element,
	        current = self._current;

	    // force reopen if element for which we are currently opened changed
	    if (current && current.element === element) {
	      self.open(element, true);
	    }
	  });
	};


	/**
	 * Register a provider with the context pad
	 *
	 * @param  {ContextPadProvider} provider
	 */
	ContextPad.prototype.registerProvider = function(provider) {
	  this._providers.push(provider);
	};


	/**
	 * Returns the context pad entries for a given element
	 *
	 * @param {djs.element.Base} element
	 *
	 * @return {Array<ContextPadEntryDescriptor>} list of entries
	 */
	ContextPad.prototype.getEntries = function(element) {
	  var entries = {};

	  // loop through all providers and their entries.
	  // group entries by id so that overriding an entry is possible
	  forEach(this._providers, function(provider) {
	    var e = provider.getContextPadEntries(element);

	    forEach(e, function(entry, id) {
	      entries[id] = entry;
	    });
	  });

	  return entries;
	};


	/**
	 * Trigger an action available on the opened context pad
	 *
	 * @param  {String} action
	 * @param  {Event} event
	 * @param  {Boolean} [autoActivate=false]
	 */
	ContextPad.prototype.trigger = function(action, event, autoActivate) {

	  var element = this._current.element,
	      entries = this._current.entries,
	      entry,
	      handler,
	      originalEvent,
	      button = event.delegateTarget || event.target;

	  if (!button) {
	    return event.preventDefault();
	  }

	  entry = entries[attr(button, 'data-action')];
	  handler = entry.action;

	  originalEvent = event.originalEvent || event;

	  // simple action (via callback function)
	  if (isFunction(handler)) {
	    if (action === 'click') {
	      return handler(originalEvent, element, autoActivate);
	    }
	  } else {
	    if (handler[action]) {
	      return handler[action](originalEvent, element, autoActivate);
	    }
	  }

	  // silence other actions
	  event.preventDefault();
	};


	/**
	 * Open the context pad for the given element
	 *
	 * @param {djs.model.Base} element
	 * @param {Boolean} force if true, force reopening the context pad
	 */
	ContextPad.prototype.open = function(element, force) {
	  if (!force && this.isOpen(element)) {
	    return;
	  }

	  this.close();
	  this._updateAndOpen(element);
	};


	ContextPad.prototype._updateAndOpen = function(element) {

	  var entries = this.getEntries(element),
	      pad = this.getPad(element),
	      html = pad.html;

	  forEach(entries, function(entry, id) {
	    var grouping = entry.group || 'default',
	        control = domify(entry.html || '<div class="entry" draggable="true"></div>'),
	        container;

	    attr(control, 'data-action', id);

	    container = query('[data-group=' + grouping + ']', html);
	    if (!container) {
	      container = domify('<div class="group" data-group="' + grouping + '"></div>');
	      html.appendChild(container);
	    }

	    container.appendChild(control);

	    if (entry.className) {
	      addClasses(control, entry.className);
	    }

	    if (entry.title) {
	      attr(control, 'title', entry.title);
	    }

	    if (entry.imageUrl) {
	      control.appendChild(domify('<img src="' + entry.imageUrl + '">'));
	    }
	  });

	  classes(html).add('open');

	  this._current = {
	    element: element,
	    pad: pad,
	    entries: entries
	  };

	  this._eventBus.fire('contextPad.open', { current: this._current });
	};


	ContextPad.prototype.getPad = function(element) {
	  if (this.isOpen()) {
	    return this._current.pad;
	  }

	  var self = this;

	  var overlays = this._overlays;

	  var html = domify('<div class="djs-context-pad"></div>');

	  var overlaysConfig = assign({
	    html: html
	  }, this._overlaysConfig);

	  delegateEvents.bind(html, entrySelector, 'click', function(event) {
	    self.trigger('click', event);
	  });

	  delegateEvents.bind(html, entrySelector, 'dragstart', function(event) {
	    self.trigger('dragstart', event);
	  });

	  // stop propagation of mouse events
	  componentEvent.bind(html, 'mousedown', function(event) {
	    event.stopPropagation();
	  });

	  this._overlayId = overlays.add(element, 'context-pad', overlaysConfig);

	  var pad = overlays.get(this._overlayId);

	  this._eventBus.fire('contextPad.create', { element: element, pad: pad });

	  return pad;
	};


	/**
	 * Close the context pad
	 */
	ContextPad.prototype.close = function() {
	  if (!this.isOpen()) {
	    return;
	  }

	  this._overlays.remove(this._overlayId);

	  this._overlayId = null;

	  this._eventBus.fire('contextPad.close', { current: this._current });

	  this._current = null;
	};

	/**
	 * Check if pad is open. If element is given, will check
	 * if pad is opened with given element.
	 *
	 * @param {Element} element
	 * @return {Boolean}
	 */
	ContextPad.prototype.isOpen = function(element) {
	  return !!this._current && (!element ? true : this._current.element === element);
	};




	// helpers //////////////////////

	function addClasses(element, classNames) {

	  var classes$1 = classes(element);

	  var actualClassNames = isArray(classNames) ? classNames : classNames.split(/\s+/g);
	  actualClassNames.forEach(function(cls) {
	    classes$1.add(cls);
	  });
	}

	var ContextPadModule = {
	  __depends__: [
	    InteractionEventsModule,
	    OverlaysModule
	  ],
	  contextPad: [ 'type', ContextPad ]
	};

	var LOW_PRIORITY$5 = 750;

	var MARKER_OK$2 = 'drop-ok',
	    MARKER_NOT_OK$2 = 'drop-not-ok',
	    MARKER_ATTACH = 'attach-ok',
	    MARKER_NEW_PARENT = 'new-parent';


	/**
	 * Adds the ability to create new shapes via drag and drop.
	 *
	 * Create must be activated via {@link Create#start}. From that
	 * point on, create will invoke `shape.create` and `shape.attach`
	 * rules to query whether or not creation or attachment on a certain
	 * position is allowed.
	 *
	 * If create or attach is allowed and a source is given, Create it
	 * will invoke `connection.create` rules to query whether a connection
	 * can be drawn between source and new shape. During rule evaluation
	 * the target is not attached yet, however
	 *
	 *   hints = { targetParent, targetAttach }
	 *
	 * are passed to the evaluating rules.
	 *
	 *
	 * ## Rule Return Values
	 *
	 * Return values interpreted from  `shape.create`:
	 *
	 *   * `true`: create is allowed
	 *   * `false`: create is disallowed
	 *   * `null`: create is not allowed but should be ignored visually
	 *
	 * Return values interpreted from `shape.attach`:
	 *
	 *   * `true`: attach is allowed
	 *   * `Any`: attach is allowed with the constraints
	 *   * `false`: attach is disallowed
	 *
	 * Return values interpreted from `connection.create`:
	 *
	 *   * `true`: connection can be created
	 *   * `Any`: connection with the given attributes can be created
	 *   * `false`: connection can't be created
	 *
	 *
	 * @param {EventBus} eventBus
	 * @param {Dragging} dragging
	 * @param {Rules} rules
	 * @param {Modeling} modeling
	 * @param {Canvas} canvas
	 * @param {Styles} styles
	 * @param {GraphicsFactory} graphicsFactory
	 */
	function Create(
	    eventBus, dragging, rules, modeling,
	    canvas, styles, graphicsFactory) {

	  // rules

	  function canCreate(shape, target, source, position) {

	    if (!target) {
	      return false;
	    }

	    var ctx = {
	      source: source,
	      shape: shape,
	      target: target,
	      position: position
	    };

	    var create,
	        attach,
	        connect;

	    attach = rules.allowed('shape.attach', ctx);

	    if (!attach) {
	      create = rules.allowed('shape.create', ctx);
	    }

	    if (create || attach) {

	      connect = source && rules.allowed('connection.create', {
	        source: source,
	        target: shape,
	        hints: {
	          targetParent: target,
	          targetAttach: attach
	        }
	      });

	      return {
	        attach: attach,
	        connect: connect
	      };
	    }

	    // allow to ignore visually
	    if (create === null || attach === null) {
	      return null;
	    }

	    return false;
	  }


	  /** set drop marker on an element */
	  function setMarker(element, marker) {

	    [ MARKER_ATTACH, MARKER_OK$2, MARKER_NOT_OK$2, MARKER_NEW_PARENT ].forEach(function(m) {

	      if (m === marker) {
	        canvas.addMarker(element, m);
	      } else {
	        canvas.removeMarker(element, m);
	      }
	    });
	  }


	  // visual helpers

	  function createVisual(shape) {
	    var group, preview, visual;

	    group = create('g');
	    attr$1(group, styles.cls('djs-drag-group', [ 'no-events' ]));

	    append(canvas.getDefaultLayer(), group);

	    preview = create('g');
	    classes$1(preview).add('djs-dragger');

	    append(group, preview);

	    translate(preview, shape.width / -2, shape.height / -2);

	    var visualGroup = create('g');
	    classes$1(visualGroup).add('djs-visual');

	    append(preview, visualGroup);

	    visual = visualGroup;

	    // hijack renderer to draw preview
	    graphicsFactory.drawShape(visual, shape);

	    return group;
	  }


	  // event handlers

	  eventBus.on('create.move', function(event) {
	    var context = event.context,
	        hover = event.hover,
	        shape = context.shape,
	        source = context.source,
	        canExecute;

	    ensureConstraints(event);

	    var position = {
	      x: event.x,
	      y: event.y
	    };

	    canExecute = context.canExecute = hover && canCreate(shape, hover, source, position);

	    // ignore hover visually if canExecute is null
	    if (hover && canExecute !== null) {
	      context.target = hover;

	      if (canExecute && canExecute.attach) {
	        setMarker(hover, MARKER_ATTACH);
	      } else {
	        setMarker(hover, canExecute ? MARKER_NEW_PARENT : MARKER_NOT_OK$2);
	      }
	    }
	  });

	  eventBus.on('create.move', LOW_PRIORITY$5, function(event) {

	    var context = event.context,
	        shape = context.shape,
	        visual = context.visual;

	    // lazy init drag visual once we received the first real
	    // drag move event (this allows us to get the proper canvas local coordinates)
	    if (!visual) {
	      visual = context.visual = createVisual(shape);
	    }

	    translate(visual, event.x, event.y);
	  });


	  eventBus.on([ 'create.end', 'create.out', 'create.cleanup' ], function(event) {
	    var context = event.context,
	        target = context.target;

	    if (target) {
	      setMarker(target, null);
	    }
	  });

	  eventBus.on('create.end', function(event) {
	    var context = event.context,
	        source = context.source,
	        hints = context.hints,
	        shape = context.shape,
	        target = context.target,
	        canExecute = context.canExecute,
	        attach = canExecute && canExecute.attach,
	        connect = canExecute && canExecute.connect;

	    if (canExecute === false || !target) {
	      return false;
	    }

	    ensureConstraints(event);

	    var position = {
	      x: event.x,
	      y: event.y
	    };

	    if (connect) {
	      // invoke append if connect is set via rules
	      shape = modeling.appendShape(source, shape, position, target, {
	        attach: attach,
	        connection: connect === true ? {} : connect
	      });
	    } else {
	      // invoke create, if connect is not set
	      shape = modeling.createShape(shape, position, target, assign(
	        {},
	        hints, {
	          attach: attach
	        }
	      ));
	    }

	    // make sure we provide the actual attached
	    // shape with the context so that selection and
	    // other components can use it right after the create
	    // operation ends
	    context.shape = shape;
	  });


	  eventBus.on('create.cleanup', function(event) {
	    var context = event.context;

	    if (context.visual) {
	      remove$1(context.visual);
	    }
	  });

	  // API

	  this.start = function(event, shape, sourceOrContext) {
	    var context;

	    if (!sourceOrContext || sourceOrContext instanceof Shape) {
	      context = {
	        hints: {},
	        shape: shape,
	        source: sourceOrContext
	      };
	    } else {
	      context = assign({
	        hints: {},
	        shape: shape
	      }, sourceOrContext);
	    }

	    dragging.init(event, 'create', {
	      cursor: 'grabbing',
	      autoActivate: true,
	      data: {
	        shape: shape,
	        context: context
	      }
	    });
	  };
	}

	Create.$inject = [
	  'eventBus',
	  'dragging',
	  'rules',
	  'modeling',
	  'canvas',
	  'styles',
	  'graphicsFactory'
	];

	// helpers //////////

	function ensureConstraints(event) {
	  var context = event.context,
	      createConstraints = context.createConstraints;

	  if (!createConstraints) {
	    return;
	  }

	  if (createConstraints.left) {
	    event.x = Math.max(event.x, createConstraints.left);
	  }

	  if (createConstraints.right) {
	    event.x = Math.min(event.x, createConstraints.right);
	  }

	  if (createConstraints.top) {
	    event.y = Math.max(event.y, createConstraints.top);
	  }

	  if (createConstraints.bottom) {
	    event.y = Math.min(event.y, createConstraints.bottom);
	  }
	}

	var CreateModule = {
	  __depends__: [
	    DraggingModule,
	    SelectionModule,
	    RulesModule
	  ],
	  create: [ 'type', Create ]
	};

	var DATA_REF = 'data-id';


	/**
	 * A popup menu that can be used to display a list of actions anywhere in the canvas.
	 *
	 * @param {Object} config
	 * @param {Boolean|Object} [config.scale={ min: 1.0, max: 1.5 }]
	 * @param {Number} [config.scale.min]
	 * @param {Number} [config.scale.max]
	 * @param {EventBus} eventBus
	 * @param {Canvas} canvas
	 *
	 * @class
	 * @constructor
	 */
	function PopupMenu(config, eventBus, canvas) {

	  var scale = isDefined(config && config.scale) ? config.scale : {
	    min: 1,
	    max: 1.5
	  };

	  this._config = {
	    scale: scale
	  };

	  this._eventBus = eventBus;
	  this._canvas = canvas;
	  this._providers = {};
	  this._current = {};
	}

	PopupMenu.$inject = [
	  'config.popupMenu',
	  'eventBus',
	  'canvas'
	];

	/**
	 * Registers a popup menu provider
	 *
	 * @param  {String} id
	 * @param  {Object} provider
	 *
	 * @example
	 *
	 * popupMenu.registerProvider('myMenuID', {
	 *   getEntries: function(element) {
	 *     return [
	 *       {
	 *         id: 'entry-1',
	 *         label: 'My Entry',
	 *         action: 'alert("I have been clicked!")'
	 *       }
	 *     ];
	 *   }
	 * });
	 */
	PopupMenu.prototype.registerProvider = function(id, provider) {
	  this._providers[id] = provider;
	};


	/**
	 * Determine if the popup menu has entries.
	 *
	 * @return {Boolean} true if empty
	 */
	PopupMenu.prototype.isEmpty = function(element, providerId) {
	  if (!element) {
	    throw new Error('element parameter is missing');
	  }

	  if (!providerId) {
	    throw new Error('providerId parameter is missing');
	  }

	  var provider = this._providers[providerId];

	  var entries = provider.getEntries(element),
	      headerEntries = provider.getHeaderEntries && provider.getHeaderEntries(element);

	  var hasEntries = entries.length > 0,
	      hasHeaderEntries = headerEntries && headerEntries.length > 0;

	  return !hasEntries && !hasHeaderEntries;
	};


	/**
	 * Create entries and open popup menu at given position
	 *
	 * @param  {Object} element
	 * @param  {String} id provider id
	 * @param  {Object} position
	 *
	 * @return {Object} popup menu instance
	 */
	PopupMenu.prototype.open = function(element, id, position) {

	  var provider = this._providers[id];

	  if (!element) {
	    throw new Error('Element is missing');
	  }

	  if (!provider) {
	    throw new Error('Provider is not registered: ' + id);
	  }

	  if (!position) {
	    throw new Error('the position argument is missing');
	  }

	  if (this.isOpen()) {
	    this.close();
	  }

	  this._emit('open');

	  var current = this._current = {
	    provider: provider,
	    className: id,
	    element: element,
	    position: position
	  };

	  if (provider.getHeaderEntries) {
	    current.headerEntries = provider.getHeaderEntries(element);
	  }

	  current.entries = provider.getEntries(element);

	  current.container = this._createContainer();

	  var headerEntries = current.headerEntries || [],
	      entries = current.entries || [];

	  if (headerEntries.length) {
	    current.container.appendChild(
	      this._createEntries(current.headerEntries, 'djs-popup-header')
	    );
	  }

	  if (entries.length) {
	    current.container.appendChild(
	      this._createEntries(current.entries, 'djs-popup-body')
	    );
	  }

	  var canvas = this._canvas,
	      parent = canvas.getContainer();

	  this._attachContainer(current.container, parent, position.cursor);
	};


	/**
	 * Removes the popup menu and unbinds the event handlers.
	 */
	PopupMenu.prototype.close = function() {

	  if (!this.isOpen()) {
	    return;
	  }

	  this._emit('close');

	  this._unbindHandlers();
	  remove(this._current.container);
	  this._current.container = null;
	};


	/**
	 * Determine if an open popup menu exist.
	 *
	 * @return {Boolean} true if open
	 */
	PopupMenu.prototype.isOpen = function() {
	  return !!this._current.container;
	};


	/**
	 * Trigger an action associated with an entry.
	 *
	 * @param {Object} event
	 *
	 * @return the result of the action callback, if any
	 */
	PopupMenu.prototype.trigger = function(event) {

	  // silence other actions
	  event.preventDefault();

	  var element = event.delegateTarget || event.target,
	      entryId = attr(element, DATA_REF);

	  var entry = this._getEntry(entryId);

	  if (entry.action) {
	    return entry.action.call(null, event, entry);
	  }
	};

	/**
	 * Gets an entry instance (either entry or headerEntry) by id.
	 *
	 * @param  {String} entryId
	 *
	 * @return {Object} entry instance
	 */
	PopupMenu.prototype._getEntry = function(entryId) {

	  var search = matchPattern({ id: entryId });

	  var entry = find(this._current.entries, search) || find(this._current.headerEntries, search);

	  if (!entry) {
	    throw new Error('entry not found');
	  }

	  return entry;
	};

	PopupMenu.prototype._emit = function(eventName) {
	  this._eventBus.fire('popupMenu.' + eventName);
	};

	/**
	 * Creates the popup menu container.
	 *
	 * @return {Object} a DOM container
	 */
	PopupMenu.prototype._createContainer = function() {
	  var container = domify('<div class="djs-popup">'),
	      position = this._current.position,
	      className = this._current.className;

	  assign(container.style, {
	    position: 'absolute',
	    left: position.x + 'px',
	    top: position.y + 'px',
	    visibility: 'hidden'
	  });

	  classes(container).add(className);

	  return container;
	};


	/**
	 * Attaches the container to the DOM and binds the event handlers.
	 *
	 * @param {Object} container
	 * @param {Object} parent
	 */
	PopupMenu.prototype._attachContainer = function(container, parent, cursor) {
	  var self = this;

	  // Event handler
	  delegateEvents.bind(container, '.entry' ,'click', function(event) {
	    self.trigger(event);
	  });

	  this._updateScale(container);

	  // Attach to DOM
	  parent.appendChild(container);

	  if (cursor) {
	    this._assureIsInbounds(container, cursor);
	  }

	  // Add Handler
	  this._bindHandlers();
	};


	/**
	 * Updates popup style.transform with respect to the config and zoom level.
	 *
	 * @method _updateScale
	 *
	 * @param {Object} container
	 */
	PopupMenu.prototype._updateScale = function(container) {
	  var zoom = this._canvas.zoom();

	  var scaleConfig = this._config.scale,
	      minScale,
	      maxScale,
	      scale = zoom;

	  if (scaleConfig !== true) {

	    if (scaleConfig === false) {
	      minScale = 1;
	      maxScale = 1;
	    } else {
	      minScale = scaleConfig.min;
	      maxScale = scaleConfig.max;
	    }

	    if (isDefined(minScale) && zoom < minScale) {
	      scale = minScale;
	    }

	    if (isDefined(maxScale) && zoom > maxScale) {
	      scale = maxScale;
	    }

	  }

	  setTransform$1(container, 'scale(' + scale + ')');
	};


	/**
	 * Make sure that the menu is always fully shown
	 *
	 * @method function
	 *
	 * @param  {Object} container
	 * @param  {Position} cursor {x, y}
	 */
	PopupMenu.prototype._assureIsInbounds = function(container, cursor) {
	  var canvas = this._canvas,
	      clientRect = canvas._container.getBoundingClientRect();

	  var containerX = container.offsetLeft,
	      containerY = container.offsetTop,
	      containerWidth = container.scrollWidth,
	      containerHeight = container.scrollHeight,
	      overAxis = {},
	      left, top;

	  var cursorPosition = {
	    x: cursor.x - clientRect.left,
	    y: cursor.y - clientRect.top
	  };

	  if (containerX + containerWidth > clientRect.width) {
	    overAxis.x = true;
	  }

	  if (containerY + containerHeight > clientRect.height) {
	    overAxis.y = true;
	  }

	  if (overAxis.x && overAxis.y) {
	    left = cursorPosition.x - containerWidth + 'px';
	    top = cursorPosition.y - containerHeight + 'px';
	  } else if (overAxis.x) {
	    left = cursorPosition.x - containerWidth + 'px';
	    top = cursorPosition.y + 'px';
	  } else if (overAxis.y && cursorPosition.y < containerHeight) {
	    left = cursorPosition.x + 'px';
	    top = 10 + 'px';
	  } else if (overAxis.y) {
	    left = cursorPosition.x + 'px';
	    top = cursorPosition.y - containerHeight + 'px';
	  }

	  assign(container.style, { left: left, top: top }, { visibility: 'visible', 'z-index': 1000 });
	};


	/**
	 * Creates a list of entries and returns them as a DOM container.
	 *
	 * @param {Array<Object>} entries an array of entry objects
	 * @param {String} className the class name of the entry container
	 *
	 * @return {Object} a DOM container
	 */
	PopupMenu.prototype._createEntries = function(entries, className) {

	  var entriesContainer = domify('<div>'),
	      self = this;

	  classes(entriesContainer).add(className);

	  forEach(entries, function(entry) {
	    var entryContainer = self._createEntry(entry, entriesContainer);
	    entriesContainer.appendChild(entryContainer);
	  });

	  return entriesContainer;
	};


	/**
	 * Creates a single entry and returns it as a DOM container.
	 *
	 * @param  {Object} entry
	 *
	 * @return {Object} a DOM container
	 */
	PopupMenu.prototype._createEntry = function(entry) {

	  if (!entry.id) {
	    throw new Error ('every entry must have the id property set');
	  }

	  var entryContainer = domify('<div>'),
	      entryClasses = classes(entryContainer);

	  entryClasses.add('entry');

	  if (entry.className) {
	    entry.className.split(' ').forEach(function(className) {
	      entryClasses.add(className);
	    });
	  }

	  attr(entryContainer, DATA_REF, entry.id);

	  if (entry.label) {
	    var label = domify('<span>');
	    label.textContent = entry.label;
	    entryContainer.appendChild(label);
	  }

	  if (entry.imageUrl) {
	    entryContainer.appendChild(domify('<img src="' + entry.imageUrl + '" />'));
	  }

	  if (entry.active === true) {
	    entryClasses.add('active');
	  }

	  if (entry.disabled === true) {
	    entryClasses.add('disabled');
	  }

	  if (entry.title) {
	    entryContainer.title = entry.title;
	  }

	  return entryContainer;
	};


	/**
	 * Binds the `close` method to 'contextPad.close' & 'canvas.viewbox.changed'.
	 */
	PopupMenu.prototype._bindHandlers = function() {

	  var eventBus = this._eventBus,
	      self = this;

	  function close() {
	    self.close();
	  }

	  eventBus.once('contextPad.close', close);
	  eventBus.once('canvas.viewbox.changing', close);
	  eventBus.once('commandStack.changed', close);
	};


	/**
	 * Unbinds the `close` method to 'contextPad.close' & 'canvas.viewbox.changing'.
	 */
	PopupMenu.prototype._unbindHandlers = function() {

	  var eventBus = this._eventBus,
	      self = this;

	  function close() {
	    self.close();
	  }

	  eventBus.off('contextPad.close', close);
	  eventBus.off('canvas.viewbox.changed', close);
	  eventBus.off('commandStack.changed', close);
	};



	// helpers /////////////////////////////

	function setTransform$1(element, transform) {
	  element.style['transform-origin'] = 'top left';

	  [ '', '-ms-', '-webkit-' ].forEach(function(prefix) {
	    element.style[prefix + 'transform'] = transform;
	  });
	}

	var PopupMenuModule = {
	  __init__: [ 'popupMenu' ],
	  popupMenu: [ 'type', PopupMenu ]
	};

	var round$6 = Math.round;

	/**
	 * Service that allow replacing of elements.
	 */
	function Replace(modeling) {

	  this._modeling = modeling;
	}

	Replace.$inject = [ 'modeling' ];

	/**
	 * @param {Element} oldElement - Element to be replaced
	 * @param {Object}  newElementData - Containing information about the new Element, for example height, width, type.
	 * @param {Object}  options - Custom options that will be attached to the context. It can be used to inject data
	 *                            that is needed in the command chain. For example it could be used in
	 *                            eventbus.on('commandStack.shape.replace.postExecute') to change shape attributes after
	 *                            shape creation.
	 */
	Replace.prototype.replaceElement = function(oldElement, newElementData, options) {

	  var modeling = this._modeling;

	  var newElement = null;

	  if (oldElement.waypoints) ; else {
	    // set center of element for modeling API
	    // if no new width / height is given use old elements size
	    newElementData.x = round$6(oldElement.x + (newElementData.width || oldElement.width) / 2);
	    newElementData.y = round$6(oldElement.y + (newElementData.height || oldElement.height) / 2);

	    newElement = modeling.replaceShape(oldElement, newElementData, options);
	  }

	  return newElement;
	};

	var ReplaceModule = {
	  __init__: [ 'replace' ],
	  replace: [ 'type', Replace ]
	};

	/**
	 * These are the properties that should be ignored when cloning elements.
	 *
	 * @type {Array}
	 */
	var IGNORED_PROPERTIES = [
	  'lanes',
	  'incoming',
	  'outgoing',
	  'artifacts',
	  'default',
	  'flowElements',
	  'dataInputAssociations',
	  'dataOutputAssociations'
	];


	function getProperties(descriptor, keepDefault) {
	  var properties = [];

	  forEach(descriptor.properties, function(property) {

	    if (keepDefault && property.default) {
	      return;
	    }

	    properties.push(property.ns.name);
	  });

	  return properties;
	}

	function isAllowedIn(extProp, type) {
	  var allowedIn = extProp.meta.allowedIn;

	  // '*' is a wildcard, which means any element is allowed to use this property
	  if (allowedIn.length === 1 && allowedIn[0] === '*') {
	    return true;
	  }

	  return allowedIn.indexOf(type) !== -1;
	}

	function isType(element, types) {
	  return some(types, function(type) {
	    return typeof element === type;
	  });
	}

	/**
	 * A bpmn properties cloning interface
	 *
	 */
	function ModelCloneHelper(eventBus, bpmnFactory) {
	  this._eventBus = eventBus;
	  this._bpmnFactory = bpmnFactory;
	}


	ModelCloneHelper.prototype.clone = function(refElement, newElement, properties) {

	  var self = this;

	  // hasNestedProperty: property allows us to avoid ending up with empty (xml) tags
	  // f.ex: if extensionElements.values is empty, don't set it
	  var context = {
	    newElement: newElement,
	    hasNestedProperty: false
	  };

	  // we want the extensionElements to be cloned last
	  // so that they can check certain properties
	  properties = sortBy(properties, function(prop) {
	    return prop === 'bpmn:extensionElements';
	  });

	  forEach(properties, function(propName) {
	    var refElementProp = refElement.get(propName),
	        newElementProp = newElement.get(propName),
	        propDescriptor = newElement.$model.getPropertyDescriptor(newElement, propName),
	        newProperty, name;

	    // we're not interested in cloning:
	    // - same values from simple types
	    // - cloning id's
	    // - cloning reference elements
	    if (newElementProp === refElementProp) {
	      return;
	    }

	    if (propDescriptor && (propDescriptor.isId || propDescriptor.isReference)) {
	      return;
	    }

	    // if the property is of type 'boolean', 'string', 'number' or 'null', just set it
	    if (isType(refElementProp, [ 'boolean', 'string', 'number' ]) || refElementProp === null) {
	      newElement.set(propName, refElementProp);

	      return;
	    }

	    if (isArray(refElementProp)) {

	      forEach(refElementProp, function(extElement) {
	        var newProp;

	        context.refTopLevelProperty = extElement;

	        newProp = self._deepClone(extElement, context);

	        if (context.hasNestedProperty) {
	          newProp.$parent = newElement;

	          newElementProp.push(newProp);
	        }

	        context.hasNestedProperty = false;
	      });

	    } else {
	      name = propName.replace(/bpmn:/, '');

	      context.refTopLevelProperty = refElementProp;

	      newProperty = self._deepClone(refElementProp, context);

	      if (context.hasNestedProperty) {
	        newProperty.$parent = newElement;

	        newElement.set(name, newProperty);
	      }

	      context.hasNestedProperty = false;
	    }
	  });

	  return newElement;
	};

	ModelCloneHelper.prototype._deepClone = function _deepClone(propertyElement, context) {
	  var self = this;

	  var eventBus = this._eventBus;
	  var bpmnFactory = this._bpmnFactory;

	  var newProp = bpmnFactory.create(propertyElement.$type);

	  var properties = filter(Object.keys(propertyElement), function(prop) {
	    var descriptor = newProp.$model.getPropertyDescriptor(newProp, prop);

	    if (descriptor && (descriptor.isId || descriptor.isReference)) {
	      return false;
	    }

	    // we need to make sure we don't clone certain properties
	    // which we cannot easily know if they hold references or not
	    if (IGNORED_PROPERTIES.indexOf(prop) !== -1) {
	      return false;
	    }

	    // make sure we don't copy the type
	    return prop !== '$type';
	  });

	  if (!properties.length) {
	    context.hasNestedProperty = true;
	  }

	  forEach(properties, function(propName) {
	    // check if the propertyElement has this property defined
	    if (propertyElement[propName] !== undefined &&
	       (propertyElement[propName].$type || isArray(propertyElement[propName]))) {

	      if (isArray(propertyElement[propName])) {
	        newProp[propName] = [];

	        forEach(propertyElement[propName], function(property) {
	          var extProp = propertyElement.$model.getTypeDescriptor(property.$type),
	              newDeepProp;

	          // we're not going to copy undefined types
	          if (!extProp) {
	            return;
	          }

	          var canClone = eventBus.fire('property.clone', {
	            newElement: context.newElement,
	            refTopLevelProperty: context.refTopLevelProperty,
	            propertyDescriptor: extProp
	          });

	          if (!canClone) {
	            // if can clone is 'undefined' or 'false'
	            // check for the meta information if it is allowed
	            if (propertyElement.$type === 'bpmn:ExtensionElements' &&
	                extProp.meta && extProp.meta.allowedIn &&
	                !isAllowedIn(extProp, context.newElement.$type)) {
	              return false;
	            }
	          }

	          newDeepProp = self._deepClone(property, context);

	          newDeepProp.$parent = newProp;

	          if (!newProp[propName]) {
	            newProp[propName] = [];
	          }

	          context.hasNestedProperty = true;

	          newProp[propName].push(newDeepProp);
	        });

	      } else if (propertyElement[propName].$type) {
	        newProp[propName] = self._deepClone(propertyElement[propName], context);

	        if (newProp[propName]) {
	          context.hasNestedProperty = true;

	          newProp[propName].$parent = newProp;
	        }
	      }
	    } else {
	      context.hasNestedProperty = true;

	      // just assign directly if it's a value
	      newProp[propName] = propertyElement[propName];
	    }
	  });

	  return newProp;
	};

	var CUSTOM_PROPERTIES = [
	  'cancelActivity',
	  'instantiate',
	  'eventGatewayType',
	  'triggeredByEvent',
	  'isInterrupting'
	];


	function toggeling(element, target) {

	  var oldCollapsed = (
	    element && has(element, 'collapsed') ? element.collapsed : !isExpanded(element)
	  );

	  var targetCollapsed;

	  if (target && (has(target, 'collapsed') || has(target, 'isExpanded'))) {
	    // property is explicitly set so use it
	    targetCollapsed = (
	      has(target, 'collapsed') ? target.collapsed : !target.isExpanded
	    );
	  } else {
	    // keep old state
	    targetCollapsed = oldCollapsed;
	  }

	  if (oldCollapsed !== targetCollapsed) {
	    element.collapsed = oldCollapsed;
	    return true;
	  }

	  return false;
	}



	/**
	 * This module takes care of replacing BPMN elements
	 */
	function BpmnReplace(
	    bpmnFactory, elementFactory, replace,
	    selection, modeling, eventBus
	) {

	  var helper = new ModelCloneHelper(eventBus, bpmnFactory);

	  /**
	   * Prepares a new business object for the replacement element
	   * and triggers the replace operation.
	   *
	   * @param  {djs.model.Base} element
	   * @param  {Object} target
	   * @param  {Object} [hints]
	   *
	   * @return {djs.model.Base} the newly created element
	   */
	  function replaceElement(element, target, hints) {

	    hints = hints || {};

	    var type = target.type,
	        oldBusinessObject = element.businessObject;

	    if (isSubProcess(oldBusinessObject)) {
	      if (type === 'bpmn:SubProcess') {
	        if (toggeling(element, target)) {
	          // expanding or collapsing process
	          modeling.toggleCollapse(element);

	          return element;
	        }
	      }
	    }

	    var newBusinessObject = bpmnFactory.create(type);

	    var newElement = {
	      type: type,
	      businessObject: newBusinessObject
	    };

	    var elementProps = getProperties(oldBusinessObject.$descriptor),
	        newElementProps = getProperties(newBusinessObject.$descriptor, true),
	        copyProps = intersection(elementProps, newElementProps);

	    // initialize special properties defined in target definition
	    assign(newBusinessObject, pick(target, CUSTOM_PROPERTIES));

	    var properties = filter(copyProps, function(property) {
	      var propName = property.replace(/bpmn:/, '');

	      // copying event definitions, unless we replace
	      if (propName === 'eventDefinitions') {
	        return hasEventDefinition$1(element, target.eventDefinitionType);
	      }

	      // retain loop characteristics if the target element
	      // is not an event sub process
	      if (propName === 'loopCharacteristics') {
	        return !isEventSubProcess(newBusinessObject);
	      }

	      // so the applied properties from 'target' don't get lost
	      if (property in newBusinessObject) {
	        return false;
	      }

	      if (propName === 'processRef' && target.isExpanded === false) {
	        return false;
	      }

	      if (propName === 'triggeredByEvent') {
	        return false;
	      }

	      return IGNORED_PROPERTIES.indexOf(propName) === -1;
	    });

	    newBusinessObject = helper.clone(oldBusinessObject, newBusinessObject, properties);

	    // initialize custom BPMN extensions
	    if (target.eventDefinitionType) {

	      // only initialize with new eventDefinition
	      // if we did not set an event definition yet,
	      // i.e. because we cloned it
	      if (!hasEventDefinition$1(newBusinessObject, target.eventDefinitionType)) {
	        newElement.eventDefinitionType = target.eventDefinitionType;
	      }
	    }

	    if (is$1(oldBusinessObject, 'bpmn:Activity')) {

	      if (isSubProcess(oldBusinessObject)) {
	        // no toggeling, so keep old state
	        newElement.isExpanded = isExpanded(oldBusinessObject);
	      }
	      // else if property is explicitly set, use it
	      else if (target && has(target, 'isExpanded')) {
	        newElement.isExpanded = target.isExpanded;
	      }

	      // TODO: need also to respect min/max Size
	      // copy size, from an expanded subprocess to an expanded alternative subprocess
	      // except bpmn:Task, because Task is always expanded
	      if ((isExpanded(oldBusinessObject) && !is$1(oldBusinessObject, 'bpmn:Task')) && newElement.isExpanded) {
	        newElement.width = element.width;
	        newElement.height = element.height;
	      }
	    }

	    // remove children if not expanding sub process
	    if (isSubProcess(oldBusinessObject) && !isSubProcess(newBusinessObject)) {
	      hints.moveChildren = false;
	    }

	    // transform collapsed/expanded pools
	    if (is$1(oldBusinessObject, 'bpmn:Participant')) {

	      // create expanded pool
	      if (target.isExpanded === true) {
	        newBusinessObject.processRef = bpmnFactory.create('bpmn:Process');
	      } else {
	        // remove children when transforming to collapsed pool
	        hints.moveChildren = false;
	      }

	      // apply same width and default height
	      newElement.width = element.width;
	      newElement.height = elementFactory._getDefaultSize(newBusinessObject).height;
	    }

	    newBusinessObject.name = oldBusinessObject.name;

	    // retain default flow's reference between inclusive <-> exclusive gateways and activities
	    if (
	      isAny(oldBusinessObject, [
	        'bpmn:ExclusiveGateway',
	        'bpmn:InclusiveGateway',
	        'bpmn:Activity'
	      ]) &&
	      isAny(newBusinessObject, [
	        'bpmn:ExclusiveGateway',
	        'bpmn:InclusiveGateway',
	        'bpmn:Activity'
	      ])
	    ) {
	      newBusinessObject.default = oldBusinessObject.default;
	    }

	    if (
	      target.host &&
	      !is$1(oldBusinessObject, 'bpmn:BoundaryEvent') &&
	      is$1(newBusinessObject, 'bpmn:BoundaryEvent')
	    ) {
	      newElement.host = target.host;
	    }

	    if ('fill' in oldBusinessObject.di || 'stroke' in oldBusinessObject.di) {
	      assign(newElement, { colors: pick(oldBusinessObject.di, [ 'fill', 'stroke' ]) });
	    }

	    newElement = replace.replaceElement(element, newElement, hints);

	    if (hints.select !== false) {
	      selection.select(newElement);
	    }

	    return newElement;
	  }

	  this.replaceElement = replaceElement;
	}

	BpmnReplace.$inject = [
	  'bpmnFactory',
	  'elementFactory',
	  'replace',
	  'selection',
	  'modeling',
	  'eventBus'
	];


	function isSubProcess(bo) {
	  return is$1(bo, 'bpmn:SubProcess');
	}

	function hasEventDefinition$1(element, type) {

	  var bo = getBusinessObject(element);

	  return type && bo.get('eventDefinitions').some(function(definition) {
	    return is$1(definition, type);
	  });
	}

	/**
	 * Compute intersection between two arrays.
	 */
	function intersection(a1, a2) {
	  return a1.filter(function(el) {
	    return a2.indexOf(el) !== -1;
	  });
	}

	var ReplaceModule$1 = {
	  __depends__: [
	    SelectionModule,
	    ReplaceModule
	  ],
	  bpmnReplace: [ 'type', BpmnReplace ]
	};

	/**
	 * Returns true, if an element is from a different type
	 * than a target definition. Takes into account the type,
	 * event definition type and triggeredByEvent property.
	 *
	 * @param {djs.model.Base} element
	 *
	 * @return {Boolean}
	 */
	function isDifferentType(element) {

	  return function(entry) {
	    var target = entry.target;

	    var businessObject = getBusinessObject(element),
	        eventDefinition = businessObject.eventDefinitions && businessObject.eventDefinitions[0];

	    var isTypeEqual = businessObject.$type === target.type;

	    var isEventDefinitionEqual = (
	      (eventDefinition && eventDefinition.$type) === target.eventDefinitionType
	    );

	    var isTriggeredByEventEqual = (
	      businessObject.triggeredByEvent === target.triggeredByEvent
	    );

	    var isExpandedEqual = (
	      target.isExpanded === undefined ||
	      target.isExpanded === isExpanded(businessObject)
	    );

	    return !isTypeEqual || !isEventDefinitionEqual || !isTriggeredByEventEqual || !isExpandedEqual;
	  };
	}

	var START_EVENT = [
	  {
	    label: 'Start Event',
	    actionName: 'replace-with-none-start',
	    className: 'bpmn-icon-start-event-none',
	    target: {
	      type: 'bpmn:StartEvent'
	    }
	  },
	  {
	    label: 'Intermediate Throw Event',
	    actionName: 'replace-with-none-intermediate-throwing',
	    className: 'bpmn-icon-intermediate-event-none',
	    target: {
	      type: 'bpmn:IntermediateThrowEvent'
	    }
	  },
	  {
	    label: 'End Event',
	    actionName: 'replace-with-none-end',
	    className: 'bpmn-icon-end-event-none',
	    target: {
	      type: 'bpmn:EndEvent'
	    }
	  },
	  {
	    label: 'Message Start Event',
	    actionName: 'replace-with-message-start',
	    className: 'bpmn-icon-start-event-message',
	    target: {
	      type: 'bpmn:StartEvent',
	      eventDefinitionType: 'bpmn:MessageEventDefinition'
	    }
	  },
	  {
	    label: 'Timer Start Event',
	    actionName: 'replace-with-timer-start',
	    className: 'bpmn-icon-start-event-timer',
	    target: {
	      type: 'bpmn:StartEvent',
	      eventDefinitionType: 'bpmn:TimerEventDefinition'
	    }
	  },
	  {
	    label: 'Conditional Start Event',
	    actionName: 'replace-with-conditional-start',
	    className: 'bpmn-icon-start-event-condition',
	    target: {
	      type: 'bpmn:StartEvent',
	      eventDefinitionType: 'bpmn:ConditionalEventDefinition'
	    }
	  },
	  {
	    label: 'Signal Start Event',
	    actionName: 'replace-with-signal-start',
	    className: 'bpmn-icon-start-event-signal',
	    target: {
	      type: 'bpmn:StartEvent',
	      eventDefinitionType: 'bpmn:SignalEventDefinition'
	    }
	  }
	];

	var INTERMEDIATE_EVENT = [
	  {
	    label: 'Start Event',
	    actionName: 'replace-with-none-start',
	    className: 'bpmn-icon-start-event-none',
	    target: {
	      type: 'bpmn:StartEvent'
	    }
	  },
	  {
	    label: 'Intermediate Throw Event',
	    actionName: 'replace-with-none-intermediate-throw',
	    className: 'bpmn-icon-intermediate-event-none',
	    target: {
	      type: 'bpmn:IntermediateThrowEvent'
	    }
	  },
	  {
	    label: 'End Event',
	    actionName: 'replace-with-none-end',
	    className: 'bpmn-icon-end-event-none',
	    target: {
	      type: 'bpmn:EndEvent'
	    }
	  },
	  {
	    label: 'Message Intermediate Catch Event',
	    actionName: 'replace-with-message-intermediate-catch',
	    className: 'bpmn-icon-intermediate-event-catch-message',
	    target: {
	      type: 'bpmn:IntermediateCatchEvent',
	      eventDefinitionType: 'bpmn:MessageEventDefinition'
	    }
	  },
	  {
	    label: 'Message Intermediate Throw Event',
	    actionName: 'replace-with-message-intermediate-throw',
	    className: 'bpmn-icon-intermediate-event-throw-message',
	    target: {
	      type: 'bpmn:IntermediateThrowEvent',
	      eventDefinitionType: 'bpmn:MessageEventDefinition'
	    }
	  },
	  {
	    label: 'Timer Intermediate Catch Event',
	    actionName: 'replace-with-timer-intermediate-catch',
	    className: 'bpmn-icon-intermediate-event-catch-timer',
	    target: {
	      type: 'bpmn:IntermediateCatchEvent',
	      eventDefinitionType: 'bpmn:TimerEventDefinition'
	    }
	  },
	  {
	    label: 'Escalation Intermediate Throw Event',
	    actionName: 'replace-with-escalation-intermediate-throw',
	    className: 'bpmn-icon-intermediate-event-throw-escalation',
	    target: {
	      type: 'bpmn:IntermediateThrowEvent',
	      eventDefinitionType: 'bpmn:EscalationEventDefinition'
	    }
	  },
	  {
	    label: 'Conditional Intermediate Catch Event',
	    actionName: 'replace-with-conditional-intermediate-catch',
	    className: 'bpmn-icon-intermediate-event-catch-condition',
	    target: {
	      type: 'bpmn:IntermediateCatchEvent',
	      eventDefinitionType: 'bpmn:ConditionalEventDefinition'
	    }
	  },
	  {
	    label: 'Link Intermediate Catch Event',
	    actionName: 'replace-with-link-intermediate-catch',
	    className: 'bpmn-icon-intermediate-event-catch-link',
	    target: {
	      type: 'bpmn:IntermediateCatchEvent',
	      eventDefinitionType: 'bpmn:LinkEventDefinition'
	    }
	  },
	  {
	    label: 'Link Intermediate Throw Event',
	    actionName: 'replace-with-link-intermediate-throw',
	    className: 'bpmn-icon-intermediate-event-throw-link',
	    target: {
	      type: 'bpmn:IntermediateThrowEvent',
	      eventDefinitionType: 'bpmn:LinkEventDefinition'
	    }
	  },
	  {
	    label: 'Compensation Intermediate Throw Event',
	    actionName: 'replace-with-compensation-intermediate-throw',
	    className: 'bpmn-icon-intermediate-event-throw-compensation',
	    target: {
	      type: 'bpmn:IntermediateThrowEvent',
	      eventDefinitionType: 'bpmn:CompensateEventDefinition'
	    }
	  },
	  {
	    label: 'Signal Intermediate Catch Event',
	    actionName: 'replace-with-signal-intermediate-catch',
	    className: 'bpmn-icon-intermediate-event-catch-signal',
	    target: {
	      type: 'bpmn:IntermediateCatchEvent',
	      eventDefinitionType: 'bpmn:SignalEventDefinition'
	    }
	  },
	  {
	    label: 'Signal Intermediate Throw Event',
	    actionName: 'replace-with-signal-intermediate-throw',
	    className: 'bpmn-icon-intermediate-event-throw-signal',
	    target: {
	      type: 'bpmn:IntermediateThrowEvent',
	      eventDefinitionType: 'bpmn:SignalEventDefinition'
	    }
	  }
	];

	var END_EVENT = [
	  {
	    label: 'Start Event',
	    actionName: 'replace-with-none-start',
	    className: 'bpmn-icon-start-event-none',
	    target: {
	      type: 'bpmn:StartEvent'
	    }
	  },
	  {
	    label: 'Intermediate Throw Event',
	    actionName: 'replace-with-none-intermediate-throw',
	    className: 'bpmn-icon-intermediate-event-none',
	    target: {
	      type: 'bpmn:IntermediateThrowEvent'
	    }
	  },
	  {
	    label: 'End Event',
	    actionName: 'replace-with-none-end',
	    className: 'bpmn-icon-end-event-none',
	    target: {
	      type: 'bpmn:EndEvent'
	    }
	  },
	  {
	    label: 'Message End Event',
	    actionName: 'replace-with-message-end',
	    className: 'bpmn-icon-end-event-message',
	    target: {
	      type: 'bpmn:EndEvent',
	      eventDefinitionType: 'bpmn:MessageEventDefinition'
	    }
	  },
	  {
	    label: 'Escalation End Event',
	    actionName: 'replace-with-escalation-end',
	    className: 'bpmn-icon-end-event-escalation',
	    target: {
	      type: 'bpmn:EndEvent',
	      eventDefinitionType: 'bpmn:EscalationEventDefinition'
	    }
	  },
	  {
	    label: 'Error End Event',
	    actionName: 'replace-with-error-end',
	    className: 'bpmn-icon-end-event-error',
	    target: {
	      type: 'bpmn:EndEvent',
	      eventDefinitionType: 'bpmn:ErrorEventDefinition'
	    }
	  },
	  {
	    label: 'Cancel End Event',
	    actionName: 'replace-with-cancel-end',
	    className: 'bpmn-icon-end-event-cancel',
	    target: {
	      type: 'bpmn:EndEvent',
	      eventDefinitionType: 'bpmn:CancelEventDefinition'
	    }
	  },
	  {
	    label: 'Compensation End Event',
	    actionName: 'replace-with-compensation-end',
	    className: 'bpmn-icon-end-event-compensation',
	    target: {
	      type: 'bpmn:EndEvent',
	      eventDefinitionType: 'bpmn:CompensateEventDefinition'
	    }
	  },
	  {
	    label: 'Signal End Event',
	    actionName: 'replace-with-signal-end',
	    className: 'bpmn-icon-end-event-signal',
	    target: {
	      type: 'bpmn:EndEvent',
	      eventDefinitionType: 'bpmn:SignalEventDefinition'
	    }
	  },
	  {
	    label: 'Terminate End Event',
	    actionName: 'replace-with-terminate-end',
	    className: 'bpmn-icon-end-event-terminate',
	    target: {
	      type: 'bpmn:EndEvent',
	      eventDefinitionType: 'bpmn:TerminateEventDefinition'
	    }
	  }
	];

	var GATEWAY = [
	  {
	    label: 'Exclusive Gateway',
	    actionName: 'replace-with-exclusive-gateway',
	    className: 'bpmn-icon-gateway-xor',
	    target: {
	      type: 'bpmn:ExclusiveGateway'
	    }
	  },
	  {
	    label: 'Parallel Gateway',
	    actionName: 'replace-with-parallel-gateway',
	    className: 'bpmn-icon-gateway-parallel',
	    target: {
	      type: 'bpmn:ParallelGateway'
	    }
	  },
	  {
	    label: 'Inclusive Gateway',
	    actionName: 'replace-with-inclusive-gateway',
	    className: 'bpmn-icon-gateway-or',
	    target: {
	      type: 'bpmn:InclusiveGateway'
	    }
	  },
	  {
	    label: 'Complex Gateway',
	    actionName: 'replace-with-complex-gateway',
	    className: 'bpmn-icon-gateway-complex',
	    target: {
	      type: 'bpmn:ComplexGateway'
	    }
	  },
	  {
	    label: 'Event based Gateway',
	    actionName: 'replace-with-event-based-gateway',
	    className: 'bpmn-icon-gateway-eventbased',
	    target: {
	      type: 'bpmn:EventBasedGateway',
	      instantiate: false,
	      eventGatewayType: 'Exclusive'
	    }
	  }
	  // Gateways deactivated until https://github.com/bpmn-io/bpmn-js/issues/194
	  // {
	  //   label: 'Event based instantiating Gateway',
	  //   actionName: 'replace-with-exclusive-event-based-gateway',
	  //   className: 'bpmn-icon-exclusive-event-based',
	  //   target: {
	  //     type: 'bpmn:EventBasedGateway'
	  //   },
	  //   options: {
	  //     businessObject: { instantiate: true, eventGatewayType: 'Exclusive' }
	  //   }
	  // },
	  // {
	  //   label: 'Parallel Event based instantiating Gateway',
	  //   actionName: 'replace-with-parallel-event-based-instantiate-gateway',
	  //   className: 'bpmn-icon-parallel-event-based-instantiate-gateway',
	  //   target: {
	  //     type: 'bpmn:EventBasedGateway'
	  //   },
	  //   options: {
	  //     businessObject: { instantiate: true, eventGatewayType: 'Parallel' }
	  //   }
	  // }
	];

	var SUBPROCESS_EXPANDED = [
	  {
	    label: 'Transaction',
	    actionName: 'replace-with-transaction',
	    className: 'bpmn-icon-transaction',
	    target: {
	      type: 'bpmn:Transaction',
	      isExpanded: true
	    }
	  },
	  {
	    label: 'Event Sub Process',
	    actionName: 'replace-with-event-subprocess',
	    className: 'bpmn-icon-event-subprocess-expanded',
	    target: {
	      type: 'bpmn:SubProcess',
	      triggeredByEvent: true,
	      isExpanded: true
	    }
	  },
	  {
	    label: 'Sub Process (collapsed)',
	    actionName: 'replace-with-collapsed-subprocess',
	    className: 'bpmn-icon-subprocess-collapsed',
	    target: {
	      type: 'bpmn:SubProcess',
	      isExpanded: false
	    }
	  }
	];

	var TRANSACTION = [
	  {
	    label: 'Sub Process',
	    actionName: 'replace-with-subprocess',
	    className: 'bpmn-icon-subprocess-expanded',
	    target: {
	      type: 'bpmn:SubProcess',
	      isExpanded: true
	    }
	  },
	  {
	    label: 'Event Sub Process',
	    actionName: 'replace-with-event-subprocess',
	    className: 'bpmn-icon-event-subprocess-expanded',
	    target: {
	      type: 'bpmn:SubProcess',
	      triggeredByEvent: true,
	      isExpanded: true
	    }
	  }
	];

	var EVENT_SUB_PROCESS = [
	  {
	    label: 'Sub Process',
	    actionName: 'replace-with-subprocess',
	    className: 'bpmn-icon-subprocess-expanded',
	    target: {
	      type: 'bpmn:SubProcess',
	      isExpanded: true
	    }
	  },
	  {
	    label: 'Transaction',
	    actionName: 'replace-with-transaction',
	    className: 'bpmn-icon-transaction',
	    target: {
	      type: 'bpmn:Transaction',
	      isExpanded: true
	    }
	  }
	];

	var TASK = [
	  {
	    label: 'Task',
	    actionName: 'replace-with-task',
	    className: 'bpmn-icon-task',
	    target: {
	      type: 'bpmn:Task'
	    }
	  },
	  {
	    label: 'Send Task',
	    actionName: 'replace-with-send-task',
	    className: 'bpmn-icon-send',
	    target: {
	      type: 'bpmn:SendTask'
	    }
	  },
	  {
	    label: 'Receive Task',
	    actionName: 'replace-with-receive-task',
	    className: 'bpmn-icon-receive',
	    target: {
	      type: 'bpmn:ReceiveTask'
	    }
	  },
	  {
	    label: 'User Task',
	    actionName: 'replace-with-user-task',
	    className: 'bpmn-icon-user',
	    target: {
	      type: 'bpmn:UserTask'
	    }
	  },
	  {
	    label: 'Manual Task',
	    actionName: 'replace-with-manual-task',
	    className: 'bpmn-icon-manual',
	    target: {
	      type: 'bpmn:ManualTask'
	    }
	  },
	  {
	    label: 'Business Rule Task',
	    actionName: 'replace-with-rule-task',
	    className: 'bpmn-icon-business-rule',
	    target: {
	      type: 'bpmn:BusinessRuleTask'
	    }
	  },
	  {
	    label: 'Service Task',
	    actionName: 'replace-with-service-task',
	    className: 'bpmn-icon-service',
	    target: {
	      type: 'bpmn:ServiceTask'
	    }
	  },
	  {
	    label: 'Script Task',
	    actionName: 'replace-with-script-task',
	    className: 'bpmn-icon-script',
	    target: {
	      type: 'bpmn:ScriptTask'
	    }
	  },
	  {
	    label: 'Call Activity',
	    actionName: 'replace-with-call-activity',
	    className: 'bpmn-icon-call-activity',
	    target: {
	      type: 'bpmn:CallActivity'
	    }
	  },
	  {
	    label: 'Sub Process (collapsed)',
	    actionName: 'replace-with-collapsed-subprocess',
	    className: 'bpmn-icon-subprocess-collapsed',
	    target: {
	      type: 'bpmn:SubProcess',
	      isExpanded: false
	    }
	  },
	  {
	    label: 'Sub Process (expanded)',
	    actionName: 'replace-with-expanded-subprocess',
	    className: 'bpmn-icon-subprocess-expanded',
	    target: {
	      type: 'bpmn:SubProcess',
	      isExpanded: true
	    }
	  }
	];

	var BOUNDARY_EVENT = [
	  {
	    label: 'Message Boundary Event',
	    actionName: 'replace-with-message-boundary',
	    className: 'bpmn-icon-intermediate-event-catch-message',
	    target: {
	      type: 'bpmn:BoundaryEvent',
	      eventDefinitionType: 'bpmn:MessageEventDefinition'
	    }
	  },
	  {
	    label: 'Timer Boundary Event',
	    actionName: 'replace-with-timer-boundary',
	    className: 'bpmn-icon-intermediate-event-catch-timer',
	    target: {
	      type: 'bpmn:BoundaryEvent',
	      eventDefinitionType: 'bpmn:TimerEventDefinition'
	    }
	  },
	  {
	    label: 'Escalation Boundary Event',
	    actionName: 'replace-with-escalation-boundary',
	    className: 'bpmn-icon-intermediate-event-catch-escalation',
	    target: {
	      type: 'bpmn:BoundaryEvent',
	      eventDefinitionType: 'bpmn:EscalationEventDefinition'
	    }
	  },
	  {
	    label: 'Conditional Boundary Event',
	    actionName: 'replace-with-conditional-boundary',
	    className: 'bpmn-icon-intermediate-event-catch-condition',
	    target: {
	      type: 'bpmn:BoundaryEvent',
	      eventDefinitionType: 'bpmn:ConditionalEventDefinition'
	    }
	  },
	  {
	    label: 'Error Boundary Event',
	    actionName: 'replace-with-error-boundary',
	    className: 'bpmn-icon-intermediate-event-catch-error',
	    target: {
	      type: 'bpmn:BoundaryEvent',
	      eventDefinitionType: 'bpmn:ErrorEventDefinition'
	    }
	  },
	  {
	    label: 'Cancel Boundary Event',
	    actionName: 'replace-with-cancel-boundary',
	    className: 'bpmn-icon-intermediate-event-catch-cancel',
	    target: {
	      type: 'bpmn:BoundaryEvent',
	      eventDefinitionType: 'bpmn:CancelEventDefinition'
	    }
	  },
	  {
	    label: 'Signal Boundary Event',
	    actionName: 'replace-with-signal-boundary',
	    className: 'bpmn-icon-intermediate-event-catch-signal',
	    target: {
	      type: 'bpmn:BoundaryEvent',
	      eventDefinitionType: 'bpmn:SignalEventDefinition'
	    }
	  },
	  {
	    label: 'Compensation Boundary Event',
	    actionName: 'replace-with-compensation-boundary',
	    className: 'bpmn-icon-intermediate-event-catch-compensation',
	    target: {
	      type: 'bpmn:BoundaryEvent',
	      eventDefinitionType: 'bpmn:CompensateEventDefinition'
	    }
	  },
	  {
	    label: 'Message Boundary Event (non-interrupting)',
	    actionName: 'replace-with-non-interrupting-message-boundary',
	    className: 'bpmn-icon-intermediate-event-catch-non-interrupting-message',
	    target: {
	      type: 'bpmn:BoundaryEvent',
	      eventDefinitionType: 'bpmn:MessageEventDefinition',
	      cancelActivity: false
	    }
	  },
	  {
	    label: 'Timer Boundary Event (non-interrupting)',
	    actionName: 'replace-with-non-interrupting-timer-boundary',
	    className: 'bpmn-icon-intermediate-event-catch-non-interrupting-timer',
	    target: {
	      type: 'bpmn:BoundaryEvent',
	      eventDefinitionType: 'bpmn:TimerEventDefinition',
	      cancelActivity: false
	    }
	  },
	  {
	    label: 'Escalation Boundary Event (non-interrupting)',
	    actionName: 'replace-with-non-interrupting-escalation-boundary',
	    className: 'bpmn-icon-intermediate-event-catch-non-interrupting-escalation',
	    target: {
	      type: 'bpmn:BoundaryEvent',
	      eventDefinitionType: 'bpmn:EscalationEventDefinition',
	      cancelActivity: false
	    }
	  },
	  {
	    label: 'Conditional Boundary Event (non-interrupting)',
	    actionName: 'replace-with-non-interrupting-conditional-boundary',
	    className: 'bpmn-icon-intermediate-event-catch-non-interrupting-condition',
	    target: {
	      type: 'bpmn:BoundaryEvent',
	      eventDefinitionType: 'bpmn:ConditionalEventDefinition',
	      cancelActivity: false
	    }
	  },
	  {
	    label: 'Signal Boundary Event (non-interrupting)',
	    actionName: 'replace-with-non-interrupting-signal-boundary',
	    className: 'bpmn-icon-intermediate-event-catch-non-interrupting-signal',
	    target: {
	      type: 'bpmn:BoundaryEvent',
	      eventDefinitionType: 'bpmn:SignalEventDefinition',
	      cancelActivity: false
	    }
	  }
	];

	var EVENT_SUB_PROCESS_START_EVENT = [
	  {
	    label: 'Message Start Event',
	    actionName: 'replace-with-message-start',
	    className: 'bpmn-icon-start-event-message',
	    target: {
	      type: 'bpmn:StartEvent',
	      eventDefinitionType: 'bpmn:MessageEventDefinition'
	    }
	  },
	  {
	    label: 'Timer Start Event',
	    actionName: 'replace-with-timer-start',
	    className: 'bpmn-icon-start-event-timer',
	    target: {
	      type: 'bpmn:StartEvent',
	      eventDefinitionType: 'bpmn:TimerEventDefinition'
	    }
	  },
	  {
	    label: 'Conditional Start Event',
	    actionName: 'replace-with-conditional-start',
	    className: 'bpmn-icon-start-event-condition',
	    target: {
	      type: 'bpmn:StartEvent',
	      eventDefinitionType: 'bpmn:ConditionalEventDefinition'
	    }
	  },
	  {
	    label: 'Signal Start Event',
	    actionName: 'replace-with-signal-start',
	    className: 'bpmn-icon-start-event-signal',
	    target: {
	      type: 'bpmn:StartEvent',
	      eventDefinitionType: 'bpmn:SignalEventDefinition'
	    }
	  },
	  {
	    label: 'Error Start Event',
	    actionName: 'replace-with-error-start',
	    className: 'bpmn-icon-start-event-error',
	    target: {
	      type: 'bpmn:StartEvent',
	      eventDefinitionType: 'bpmn:ErrorEventDefinition'
	    }
	  },
	  {
	    label: 'Escalation Start Event',
	    actionName: 'replace-with-escalation-start',
	    className: 'bpmn-icon-start-event-escalation',
	    target: {
	      type: 'bpmn:StartEvent',
	      eventDefinitionType: 'bpmn:EscalationEventDefinition'
	    }
	  },
	  {
	    label: 'Compensation Start Event',
	    actionName: 'replace-with-compensation-start',
	    className: 'bpmn-icon-start-event-compensation',
	    target: {
	      type: 'bpmn:StartEvent',
	      eventDefinitionType: 'bpmn:CompensateEventDefinition'
	    }
	  },
	  {
	    label: 'Message Start Event (non-interrupting)',
	    actionName: 'replace-with-non-interrupting-message-start',
	    className: 'bpmn-icon-start-event-non-interrupting-message',
	    target: {
	      type: 'bpmn:StartEvent',
	      eventDefinitionType: 'bpmn:MessageEventDefinition',
	      isInterrupting: false
	    }
	  },
	  {
	    label: 'Timer Start Event (non-interrupting)',
	    actionName: 'replace-with-non-interrupting-timer-start',
	    className: 'bpmn-icon-start-event-non-interrupting-timer',
	    target: {
	      type: 'bpmn:StartEvent',
	      eventDefinitionType: 'bpmn:TimerEventDefinition',
	      isInterrupting: false
	    }
	  },
	  {
	    label: 'Conditional Start Event (non-interrupting)',
	    actionName: 'replace-with-non-interrupting-conditional-start',
	    className: 'bpmn-icon-start-event-non-interrupting-condition',
	    target: {
	      type: 'bpmn:StartEvent',
	      eventDefinitionType: 'bpmn:ConditionalEventDefinition',
	      isInterrupting: false
	    }
	  },
	  {
	    label: 'Signal Start Event (non-interrupting)',
	    actionName: 'replace-with-non-interrupting-signal-start',
	    className: 'bpmn-icon-start-event-non-interrupting-signal',
	    target: {
	      type: 'bpmn:StartEvent',
	      eventDefinitionType: 'bpmn:SignalEventDefinition',
	      isInterrupting: false
	    }
	  },
	  {
	    label: 'Escalation Start Event (non-interrupting)',
	    actionName: 'replace-with-non-interrupting-escalation-start',
	    className: 'bpmn-icon-start-event-non-interrupting-escalation',
	    target: {
	      type: 'bpmn:StartEvent',
	      eventDefinitionType: 'bpmn:EscalationEventDefinition',
	      isInterrupting: false
	    }
	  }
	];

	var SEQUENCE_FLOW = [
	  {
	    label: 'Sequence Flow',
	    actionName: 'replace-with-sequence-flow',
	    className: 'bpmn-icon-connection'
	  },
	  {
	    label: 'Default Flow',
	    actionName: 'replace-with-default-flow',
	    className: 'bpmn-icon-default-flow'
	  },
	  {
	    label: 'Conditional Flow',
	    actionName: 'replace-with-conditional-flow',
	    className: 'bpmn-icon-conditional-flow'
	  }
	];

	var PARTICIPANT = [
	  {
	    label: 'Expanded Pool',
	    actionName: 'replace-with-expanded-pool',
	    className: 'bpmn-icon-participant',
	    target: {
	      type: 'bpmn:Participant',
	      isExpanded: true
	    }
	  },
	  {
	    label: 'Collapsed Pool',
	    actionName: 'replace-with-collapsed-pool',
	    // TODO(@janstuemmel): maybe design new icon
	    className: 'bpmn-icon-lane',
	    target: {
	      type: 'bpmn:Participant',
	      isExpanded: false
	    }
	  }
	];

	/**
	 * This module is an element agnostic replace menu provider for the popup menu.
	 */
	function ReplaceMenuProvider(
	    popupMenu, modeling, moddle,
	    bpmnReplace, rules, translate) {

	  this._popupMenu = popupMenu;
	  this._modeling = modeling;
	  this._moddle = moddle;
	  this._bpmnReplace = bpmnReplace;
	  this._rules = rules;
	  this._translate = translate;

	  this.register();
	}

	ReplaceMenuProvider.$inject = [
	  'popupMenu',
	  'modeling',
	  'moddle',
	  'bpmnReplace',
	  'rules',
	  'translate'
	];


	/**
	 * Register replace menu provider in the popup menu
	 */
	ReplaceMenuProvider.prototype.register = function() {
	  this._popupMenu.registerProvider('bpmn-replace', this);
	};


	/**
	 * Get all entries from replaceOptions for the given element and apply filters
	 * on them. Get for example only elements, which are different from the current one.
	 *
	 * @param {djs.model.Base} element
	 *
	 * @return {Array<Object>} a list of menu entry items
	 */
	ReplaceMenuProvider.prototype.getEntries = function(element) {

	  var businessObject = element.businessObject;

	  var rules = this._rules;

	  var entries;

	  if (!rules.allowed('shape.replace', { element: element })) {
	    return [];
	  }

	  var differentType = isDifferentType(element);

	  // start events outside event sub processes
	  if (is$1(businessObject, 'bpmn:StartEvent') && !isEventSubProcess(businessObject.$parent)) {

	    entries = filter(START_EVENT, differentType);

	    return this._createEntries(element, entries);
	  }

	  // expanded/collapsed pools
	  if (is$1(businessObject, 'bpmn:Participant')) {

	    entries = filter(PARTICIPANT, function(entry) {
	      return isExpanded(businessObject) !== entry.target.isExpanded;
	    });

	    return this._createEntries(element, entries);
	  }

	  // start events inside event sub processes
	  if (is$1(businessObject, 'bpmn:StartEvent') && isEventSubProcess(businessObject.$parent)) {

	    entries = filter(EVENT_SUB_PROCESS_START_EVENT, function(entry) {

	      var target = entry.target;

	      var isInterrupting = target.isInterrupting !== false;

	      var isInterruptingEqual = getBusinessObject(element).isInterrupting === isInterrupting;

	      // filters elements which types and event definition are equal but have have different interrupting types
	      return differentType(entry) || !differentType(entry) && !isInterruptingEqual;

	    });

	    return this._createEntries(element, entries);
	  }

	  // end events
	  if (is$1(businessObject, 'bpmn:EndEvent')) {

	    entries = filter(END_EVENT, function(entry) {
	      var target = entry.target;

	      // hide cancel end events outside transactions
	      if (target.eventDefinitionType == 'bpmn:CancelEventDefinition' && !is$1(businessObject.$parent, 'bpmn:Transaction')) {
	        return false;
	      }

	      return differentType(entry);
	    });

	    return this._createEntries(element, entries);
	  }

	  // boundary events
	  if (is$1(businessObject, 'bpmn:BoundaryEvent')) {

	    entries = filter(BOUNDARY_EVENT, function(entry) {

	      var target = entry.target;

	      if (target.eventDefinition == 'bpmn:CancelEventDefinition' &&
	         !is$1(businessObject.attachedToRef, 'bpmn:Transaction')) {
	        return false;
	      }
	      var cancelActivity = target.cancelActivity !== false;

	      var isCancelActivityEqual = businessObject.cancelActivity == cancelActivity;

	      return differentType(entry) || !differentType(entry) && !isCancelActivityEqual;
	    });

	    return this._createEntries(element, entries);
	  }

	  // intermediate events
	  if (is$1(businessObject, 'bpmn:IntermediateCatchEvent') ||
	      is$1(businessObject, 'bpmn:IntermediateThrowEvent')) {

	    entries = filter(INTERMEDIATE_EVENT, differentType);

	    return this._createEntries(element, entries);
	  }

	  // gateways
	  if (is$1(businessObject, 'bpmn:Gateway')) {

	    entries = filter(GATEWAY, differentType);

	    return this._createEntries(element, entries);
	  }

	  // transactions
	  if (is$1(businessObject, 'bpmn:Transaction')) {

	    entries = filter(TRANSACTION, differentType);

	    return this._createEntries(element, entries);
	  }

	  // expanded event sub processes
	  if (isEventSubProcess(businessObject) && isExpanded(businessObject)) {

	    entries = filter(EVENT_SUB_PROCESS, differentType);

	    return this._createEntries(element, entries);
	  }

	  // expanded sub processes
	  if (is$1(businessObject, 'bpmn:SubProcess') && isExpanded(businessObject)) {

	    entries = filter(SUBPROCESS_EXPANDED, differentType);

	    return this._createEntries(element, entries);
	  }

	  // collapsed ad hoc sub processes
	  if (is$1(businessObject, 'bpmn:AdHocSubProcess') && !isExpanded(businessObject)) {

	    entries = filter(TASK, function(entry) {

	      var target = entry.target;

	      var isTargetSubProcess = target.type === 'bpmn:SubProcess';

	      var isTargetExpanded = target.isExpanded === true;

	      return isDifferentType(element) && (!isTargetSubProcess || isTargetExpanded);
	    });

	    return this._createEntries(element, entries);
	  }

	  // sequence flows
	  if (is$1(businessObject, 'bpmn:SequenceFlow')) {
	    return this._createSequenceFlowEntries(element, SEQUENCE_FLOW);
	  }

	  // flow nodes
	  if (is$1(businessObject, 'bpmn:FlowNode')) {
	    entries = filter(TASK, differentType);

	    // collapsed SubProcess can not be replaced with itself
	    if (is$1(businessObject, 'bpmn:SubProcess') && !isExpanded(businessObject)) {
	      entries = filter(entries, function(entry) {
	        return entry.label !== 'Sub Process (collapsed)';
	      });
	    }

	    return this._createEntries(element, entries);
	  }

	  return [];
	};


	/**
	 * Get a list of header items for the given element. This includes buttons
	 * for multi instance markers and for the ad hoc marker.
	 *
	 * @param {djs.model.Base} element
	 *
	 * @return {Array<Object>} a list of menu entry items
	 */
	ReplaceMenuProvider.prototype.getHeaderEntries = function(element) {

	  var headerEntries = [];

	  if (is$1(element, 'bpmn:Activity') && !isEventSubProcess(element)) {
	    headerEntries = headerEntries.concat(this._getLoopEntries(element));
	  }

	  if (is$1(element, 'bpmn:SubProcess') &&
	      !is$1(element, 'bpmn:Transaction') &&
	      !isEventSubProcess(element)) {
	    headerEntries.push(this._getAdHocEntry(element));
	  }

	  return headerEntries;
	};


	/**
	 * Creates an array of menu entry objects for a given element and filters the replaceOptions
	 * according to a filter function.
	 *
	 * @param  {djs.model.Base} element
	 * @param  {Object} replaceOptions
	 *
	 * @return {Array<Object>} a list of menu items
	 */
	ReplaceMenuProvider.prototype._createEntries = function(element, replaceOptions) {
	  var menuEntries = [];

	  var self = this;

	  forEach(replaceOptions, function(definition) {
	    var entry = self._createMenuEntry(definition, element);

	    menuEntries.push(entry);
	  });

	  return menuEntries;
	};

	/**
	 * Creates an array of menu entry objects for a given sequence flow.
	 *
	 * @param  {djs.model.Base} element
	 * @param  {Object} replaceOptions

	 * @return {Array<Object>} a list of menu items
	 */
	ReplaceMenuProvider.prototype._createSequenceFlowEntries = function(element, replaceOptions) {

	  var businessObject = getBusinessObject(element);

	  var menuEntries = [];

	  var modeling = this._modeling,
	      moddle = this._moddle;

	  var self = this;

	  forEach(replaceOptions, function(entry) {

	    switch (entry.actionName) {
	    case 'replace-with-default-flow':
	      if (businessObject.sourceRef.default !== businessObject &&
	            (is$1(businessObject.sourceRef, 'bpmn:ExclusiveGateway') ||
	             is$1(businessObject.sourceRef, 'bpmn:InclusiveGateway') ||
	             is$1(businessObject.sourceRef, 'bpmn:ComplexGateway') ||
	             is$1(businessObject.sourceRef, 'bpmn:Activity'))) {

	        menuEntries.push(self._createMenuEntry(entry, element, function() {
	          modeling.updateProperties(element.source, { default: businessObject });
	        }));
	      }
	      break;
	    case 'replace-with-conditional-flow':
	      if (!businessObject.conditionExpression && is$1(businessObject.sourceRef, 'bpmn:Activity')) {

	        menuEntries.push(self._createMenuEntry(entry, element, function() {
	          var conditionExpression = moddle.create('bpmn:FormalExpression', { body: '' });

	          modeling.updateProperties(element, { conditionExpression: conditionExpression });
	        }));
	      }
	      break;
	    default:
	      // default flows
	      if (is$1(businessObject.sourceRef, 'bpmn:Activity') && businessObject.conditionExpression) {
	        return menuEntries.push(self._createMenuEntry(entry, element, function() {
	          modeling.updateProperties(element, { conditionExpression: undefined });
	        }));
	      }
	      // conditional flows
	      if ((is$1(businessObject.sourceRef, 'bpmn:ExclusiveGateway') ||
	           is$1(businessObject.sourceRef, 'bpmn:InclusiveGateway') ||
	           is$1(businessObject.sourceRef, 'bpmn:ComplexGateway') ||
	           is$1(businessObject.sourceRef, 'bpmn:Activity')) &&
	           businessObject.sourceRef.default === businessObject) {

	        return menuEntries.push(self._createMenuEntry(entry, element, function() {
	          modeling.updateProperties(element.source, { default: undefined });
	        }));
	      }
	    }
	  });

	  return menuEntries;
	};


	/**
	 * Creates and returns a single menu entry item.
	 *
	 * @param  {Object} definition a single replace options definition object
	 * @param  {djs.model.Base} element
	 * @param  {Function} [action] an action callback function which gets called when
	 *                             the menu entry is being triggered.
	 *
	 * @return {Object} menu entry item
	 */
	ReplaceMenuProvider.prototype._createMenuEntry = function(definition, element, action) {
	  var translate = this._translate;
	  var replaceElement = this._bpmnReplace.replaceElement;

	  var replaceAction = function() {
	    return replaceElement(element, definition.target);
	  };

	  action = action || replaceAction;

	  var menuEntry = {
	    label: translate(definition.label),
	    className: definition.className,
	    id: definition.actionName,
	    action: action
	  };

	  return menuEntry;
	};

	/**
	 * Get a list of menu items containing buttons for multi instance markers
	 *
	 * @param  {djs.model.Base} element
	 *
	 * @return {Array<Object>} a list of menu items
	 */
	ReplaceMenuProvider.prototype._getLoopEntries = function(element) {

	  var self = this;
	  var translate = this._translate;

	  function toggleLoopEntry(event, entry) {
	    var loopCharacteristics;

	    if (entry.active) {
	      loopCharacteristics = undefined;
	    } else {
	      loopCharacteristics = self._moddle.create(entry.options.loopCharacteristics);

	      if (entry.options.isSequential) {
	        loopCharacteristics.isSequential = entry.options.isSequential;
	      }
	    }
	    self._modeling.updateProperties(element, { loopCharacteristics: loopCharacteristics });
	  }

	  var businessObject = getBusinessObject(element),
	      loopCharacteristics = businessObject.loopCharacteristics;

	  var isSequential,
	      isLoop,
	      isParallel;

	  if (loopCharacteristics) {
	    isSequential = loopCharacteristics.isSequential;
	    isLoop = loopCharacteristics.isSequential === undefined;
	    isParallel = loopCharacteristics.isSequential !== undefined && !loopCharacteristics.isSequential;
	  }


	  var loopEntries = [
	    {
	      id: 'toggle-parallel-mi',
	      className: 'bpmn-icon-parallel-mi-marker',
	      title: translate('Parallel Multi Instance'),
	      active: isParallel,
	      action: toggleLoopEntry,
	      options: {
	        loopCharacteristics: 'bpmn:MultiInstanceLoopCharacteristics',
	        isSequential: false
	      }
	    },
	    {
	      id: 'toggle-sequential-mi',
	      className: 'bpmn-icon-sequential-mi-marker',
	      title: translate('Sequential Multi Instance'),
	      active: isSequential,
	      action: toggleLoopEntry,
	      options: {
	        loopCharacteristics: 'bpmn:MultiInstanceLoopCharacteristics',
	        isSequential: true
	      }
	    },
	    {
	      id: 'toggle-loop',
	      className: 'bpmn-icon-loop-marker',
	      title: translate('Loop'),
	      active: isLoop,
	      action: toggleLoopEntry,
	      options: {
	        loopCharacteristics: 'bpmn:StandardLoopCharacteristics'
	      }
	    }
	  ];
	  return loopEntries;
	};


	/**
	 * Get the menu items containing a button for the ad hoc marker
	 *
	 * @param  {djs.model.Base} element
	 *
	 * @return {Object} a menu item
	 */
	ReplaceMenuProvider.prototype._getAdHocEntry = function(element) {
	  var translate = this._translate;
	  var businessObject = getBusinessObject(element);

	  var isAdHoc = is$1(businessObject, 'bpmn:AdHocSubProcess');

	  var replaceElement = this._bpmnReplace.replaceElement;

	  var adHocEntry = {
	    id: 'toggle-adhoc',
	    className: 'bpmn-icon-ad-hoc-marker',
	    title: translate('Ad-hoc'),
	    active: isAdHoc,
	    action: function(event, entry) {
	      if (isAdHoc) {
	        return replaceElement(element, { type: 'bpmn:SubProcess' });
	      } else {
	        return replaceElement(element, { type: 'bpmn:AdHocSubProcess' });
	      }
	    }
	  };

	  return adHocEntry;
	};

	var PopupMenuModule$1 = {
	  __depends__: [
	    PopupMenuModule,
	    ReplaceModule$1
	  ],
	  __init__: [ 'replaceMenuProvider' ],
	  replaceMenuProvider: [ 'type', ReplaceMenuProvider ]
	};

	var max$2 = Math.max,
	    min$1 = Math.min;

	var DEFAULT_CHILD_BOX_PADDING = 20;


	/**
	 * Substract a TRBL from another
	 *
	 * @param  {TRBL} trblA
	 * @param  {TRBL} trblB
	 *
	 * @return {TRBL}
	 */
	function substractTRBL(trblA, trblB) {
	  return {
	    top: trblA.top - trblB.top,
	    right: trblA.right - trblB.right,
	    bottom: trblA.bottom - trblB.bottom,
	    left: trblA.left - trblB.left
	  };
	}

	/**
	 * Resize the given bounds by the specified delta from a given anchor point.
	 *
	 * @param {Bounds} bounds the bounding box that should be resized
	 * @param {String} direction in which the element is resized (nw, ne, se, sw)
	 * @param {Point} delta of the resize operation
	 *
	 * @return {Bounds} resized bounding box
	 */
	function resizeBounds(bounds, direction, delta) {

	  var dx = delta.x,
	      dy = delta.y;

	  switch (direction) {

	  case 'nw':
	    return {
	      x: bounds.x + dx,
	      y: bounds.y + dy,
	      width: bounds.width - dx,
	      height: bounds.height - dy
	    };

	  case 'sw':
	    return {
	      x: bounds.x + dx,
	      y: bounds.y,
	      width: bounds.width - dx,
	      height: bounds.height + dy
	    };

	  case 'ne':
	    return {
	      x: bounds.x,
	      y: bounds.y + dy,
	      width: bounds.width + dx,
	      height: bounds.height - dy
	    };

	  case 'se':
	    return {
	      x: bounds.x,
	      y: bounds.y,
	      width: bounds.width + dx,
	      height: bounds.height + dy
	    };

	  default:
	    throw new Error('unrecognized direction: ' + direction);
	  }
	}


	/**
	 * Resize the given bounds by applying the passed
	 * { top, right, bottom, left } delta.
	 *
	 * @param {Bounds} bounds
	 * @param {TRBL} trblResize
	 *
	 * @return {Bounds}
	 */
	function resizeTRBL(bounds, resize) {
	  return {
	    x: bounds.x + (resize.left || 0),
	    y: bounds.y + (resize.top || 0),
	    width: bounds.width - (resize.left || 0) + (resize.right || 0),
	    height: bounds.height - (resize.top || 0) + (resize.bottom || 0)
	  };
	}


	function applyConstraints(attr, trbl, resizeConstraints) {

	  var value = trbl[attr],
	      minValue = resizeConstraints.min && resizeConstraints.min[attr],
	      maxValue = resizeConstraints.max && resizeConstraints.max[attr];

	  if (isNumber(minValue)) {
	    value = (/top|left/.test(attr) ? min$1 : max$2)(value, minValue);
	  }

	  if (isNumber(maxValue)) {
	    value = (/top|left/.test(attr) ? max$2 : min$1)(value, maxValue);
	  }

	  return value;
	}

	function ensureConstraints$1(currentBounds, resizeConstraints) {

	  if (!resizeConstraints) {
	    return currentBounds;
	  }

	  var currentTrbl = asTRBL(currentBounds);

	  return asBounds({
	    top: applyConstraints('top', currentTrbl, resizeConstraints),
	    right: applyConstraints('right', currentTrbl, resizeConstraints),
	    bottom: applyConstraints('bottom', currentTrbl, resizeConstraints),
	    left: applyConstraints('left', currentTrbl, resizeConstraints)
	  });
	}


	function getMinResizeBounds(direction, currentBounds, minDimensions, childrenBounds) {

	  var currentBox = asTRBL(currentBounds);

	  var minBox = {
	    top: /n/.test(direction) ? currentBox.bottom - minDimensions.height : currentBox.top,
	    left: /w/.test(direction) ? currentBox.right - minDimensions.width : currentBox.left,
	    bottom: /s/.test(direction) ? currentBox.top + minDimensions.height : currentBox.bottom,
	    right: /e/.test(direction) ? currentBox.left + minDimensions.width : currentBox.right
	  };

	  var childrenBox = childrenBounds ? asTRBL(childrenBounds) : minBox;

	  var combinedBox = {
	    top: min$1(minBox.top, childrenBox.top),
	    left: min$1(minBox.left, childrenBox.left),
	    bottom: max$2(minBox.bottom, childrenBox.bottom),
	    right: max$2(minBox.right, childrenBox.right)
	  };

	  return asBounds(combinedBox);
	}

	function asPadding(mayBePadding, defaultValue) {
	  if (typeof mayBePadding !== 'undefined') {
	    return mayBePadding;
	  } else {
	    return DEFAULT_CHILD_BOX_PADDING;
	  }
	}

	function addPadding(bbox, padding) {
	  var left, right, top, bottom;

	  if (typeof padding === 'object') {
	    left = asPadding(padding.left);
	    right = asPadding(padding.right);
	    top = asPadding(padding.top);
	    bottom = asPadding(padding.bottom);
	  } else {
	    left = right = top = bottom = asPadding(padding);
	  }

	  return {
	    x: bbox.x - left,
	    y: bbox.y - top,
	    width: bbox.width + left + right,
	    height: bbox.height + top + bottom
	  };
	}


	/**
	 * Is the given element part of the resize
	 * targets min boundary box?
	 *
	 * This is the default implementation which excludes
	 * connections and labels.
	 *
	 * @param {djs.model.Base} element
	 */
	function isBBoxChild(element) {

	  // exclude connections
	  if (element.waypoints) {
	    return false;
	  }

	  // exclude labels
	  if (element.type === 'label') {
	    return false;
	  }

	  return true;
	}

	/**
	 * Return children bounding computed from a shapes children
	 * or a list of prefiltered children.
	 *
	 * @param  {djs.model.Shape|Array<djs.model.Shape>} shapeOrChildren
	 * @param  {Number|Object} padding
	 *
	 * @return {Bounds}
	 */
	function computeChildrenBBox(shapeOrChildren, padding) {

	  var elements;

	  // compute based on shape
	  if (shapeOrChildren.length === undefined) {
	    // grab all the children that are part of the
	    // parents children box
	    elements = filter(shapeOrChildren.children, isBBoxChild);

	  } else {
	    elements = shapeOrChildren;
	  }

	  if (elements.length) {
	    return addPadding(getBBox(elements), padding);
	  }
	}

	var abs$3 = Math.abs;


	function getTRBLResize(oldBounds, newBounds) {
	  return substractTRBL(asTRBL(newBounds), asTRBL(oldBounds));
	}


	var LANE_PARENTS = [
	  'bpmn:Participant',
	  'bpmn:Process',
	  'bpmn:SubProcess'
	];

	var LANE_INDENTATION = 30;


	/**
	 * Collect all lane shapes in the given paren
	 *
	 * @param  {djs.model.Shape} shape
	 * @param  {Array<djs.model.Base>} [collectedShapes]
	 *
	 * @return {Array<djs.model.Base>}
	 */
	function collectLanes(shape, collectedShapes) {

	  collectedShapes = collectedShapes || [];

	  shape.children.filter(function(s) {
	    if (is$1(s, 'bpmn:Lane')) {
	      collectLanes(s, collectedShapes);

	      collectedShapes.push(s);
	    }
	  });

	  return collectedShapes;
	}


	/**
	 * Return the lane children of the given element.
	 *
	 * @param {djs.model.Shape} shape
	 *
	 * @return {Array<djs.model.Shape>}
	 */
	function getChildLanes(shape) {
	  return shape.children.filter(function(c) {
	    return is$1(c, 'bpmn:Lane');
	  });
	}


	/**
	 * Return the root element containing the given lane shape
	 *
	 * @param {djs.model.Shape} shape
	 *
	 * @return {djs.model.Shape}
	 */
	function getLanesRoot(shape) {
	  return getParent(shape, LANE_PARENTS) || shape;
	}


	/**
	 * Compute the required resize operations for lanes
	 * adjacent to the given shape, assuming it will be
	 * resized to the given new bounds.
	 *
	 * @param {djs.model.Shape} shape
	 * @param {Bounds} newBounds
	 *
	 * @return {Array<Object>}
	 */
	function computeLanesResize(shape, newBounds) {

	  var rootElement = getLanesRoot(shape);

	  var initialShapes = is$1(rootElement, 'bpmn:Process') ? [] : [ rootElement ];

	  var allLanes = collectLanes(rootElement, initialShapes),
	      shapeTrbl = asTRBL(shape),
	      shapeNewTrbl = asTRBL(newBounds),
	      trblResize = getTRBLResize(shape, newBounds),
	      resizeNeeded = [];

	  allLanes.forEach(function(other) {

	    if (other === shape) {
	      return;
	    }

	    var topResize = 0,
	        rightResize = trblResize.right,
	        bottomResize = 0,
	        leftResize = trblResize.left;

	    var otherTrbl = asTRBL(other);

	    if (trblResize.top) {
	      if (abs$3(otherTrbl.bottom - shapeTrbl.top) < 10) {
	        bottomResize = shapeNewTrbl.top - otherTrbl.bottom;
	      }

	      if (abs$3(otherTrbl.top - shapeTrbl.top) < 5) {
	        topResize = shapeNewTrbl.top - otherTrbl.top;
	      }
	    }

	    if (trblResize.bottom) {
	      if (abs$3(otherTrbl.top - shapeTrbl.bottom) < 10) {
	        topResize = shapeNewTrbl.bottom - otherTrbl.top;
	      }

	      if (abs$3(otherTrbl.bottom - shapeTrbl.bottom) < 5) {
	        bottomResize = shapeNewTrbl.bottom - otherTrbl.bottom;
	      }
	    }

	    if (topResize || rightResize || bottomResize || leftResize) {

	      resizeNeeded.push({
	        shape: other,
	        newBounds: resizeTRBL(other, {
	          top: topResize,
	          right: rightResize,
	          bottom: bottomResize,
	          left: leftResize
	        })
	      });
	    }

	  });

	  return resizeNeeded;
	}

	/**
	 * A provider for BPMN 2.0 elements context pad
	 */
	function ContextPadProvider(
	    config, injector, eventBus,
	    contextPad, modeling, elementFactory,
	    connect, create, popupMenu,
	    canvas, rules, translate) {

	  config = config || {};

	  contextPad.registerProvider(this);

	  this._contextPad = contextPad;

	  this._modeling = modeling;

	  this._elementFactory = elementFactory;
	  this._connect = connect;
	  this._create = create;
	  this._popupMenu = popupMenu;
	  this._canvas = canvas;
	  this._rules = rules;
	  this._translate = translate;

	  if (config.autoPlace !== false) {
	    this._autoPlace = injector.get('autoPlace', false);
	  }

	  eventBus.on('create.end', 250, function(event) {
	    var shape = event.context.shape;

	    if (!hasPrimaryModifier(event)) {
	      return;
	    }

	    var entries = contextPad.getEntries(shape);

	    if (entries.replace) {
	      entries.replace.action.click(event, shape);
	    }
	  });
	}

	ContextPadProvider.$inject = [
	  'config.contextPad',
	  'injector',
	  'eventBus',
	  'contextPad',
	  'modeling',
	  'elementFactory',
	  'connect',
	  'create',
	  'popupMenu',
	  'canvas',
	  'rules',
	  'translate'
	];


	ContextPadProvider.prototype.getContextPadEntries = function(element) {

	  var contextPad = this._contextPad,
	      modeling = this._modeling,

	      elementFactory = this._elementFactory,
	      connect = this._connect,
	      create = this._create,
	      popupMenu = this._popupMenu,
	      canvas = this._canvas,
	      rules = this._rules,
	      autoPlace = this._autoPlace,
	      translate = this._translate;

	  var actions = {};

	  if (element.type === 'label') {
	    return actions;
	  }

	  var businessObject = element.businessObject;

	  function startConnect(event, element) {
	    connect.start(event, element);
	  }

	  function removeElement(e) {
	    modeling.removeElements([ element ]);
	  }

	  function getReplaceMenuPosition(element) {

	    var Y_OFFSET = 5;

	    var diagramContainer = canvas.getContainer(),
	        pad = contextPad.getPad(element).html;

	    var diagramRect = diagramContainer.getBoundingClientRect(),
	        padRect = pad.getBoundingClientRect();

	    var top = padRect.top - diagramRect.top;
	    var left = padRect.left - diagramRect.left;

	    var pos = {
	      x: left,
	      y: top + padRect.height + Y_OFFSET
	    };

	    return pos;
	  }


	  /**
	   * Create an append action
	   *
	   * @param {String} type
	   * @param {String} className
	   * @param {String} [title]
	   * @param {Object} [options]
	   *
	   * @return {Object} descriptor
	   */
	  function appendAction(type, className, title, options) {

	    if (typeof title !== 'string') {
	      options = title;
	      title = translate('Append {type}', { type: type.replace(/^bpmn:/, '') });
	    }

	    function appendStart(event, element) {

	      var shape = elementFactory.createShape(assign({ type: type }, options));
	      create.start(event, shape, element);
	    }


	    var append = autoPlace ? function(event, element) {
	      var shape = elementFactory.createShape(assign({ type: type }, options));

	      autoPlace.append(element, shape);
	    } : appendStart;


	    return {
	      group: 'model',
	      className: className,
	      title: title,
	      action: {
	        dragstart: appendStart,
	        click: append
	      }
	    };
	  }

	  function splitLaneHandler(count) {

	    return function(event, element) {
	      // actual split
	      modeling.splitLane(element, count);

	      // refresh context pad after split to
	      // get rid of split icons
	      contextPad.open(element, true);
	    };
	  }


	  if (isAny(businessObject, [ 'bpmn:Lane', 'bpmn:Participant' ]) && isExpanded(businessObject)) {

	    var childLanes = getChildLanes(element);

	    assign(actions, {
	      'lane-insert-above': {
	        group: 'lane-insert-above',
	        className: 'bpmn-icon-lane-insert-above',
	        title: translate('Add Lane above'),
	        action: {
	          click: function(event, element) {
	            modeling.addLane(element, 'top');
	          }
	        }
	      }
	    });

	    if (childLanes.length < 2) {

	      if (element.height >= 120) {
	        assign(actions, {
	          'lane-divide-two': {
	            group: 'lane-divide',
	            className: 'bpmn-icon-lane-divide-two',
	            title: translate('Divide into two Lanes'),
	            action: {
	              click: splitLaneHandler(2)
	            }
	          }
	        });
	      }

	      if (element.height >= 180) {
	        assign(actions, {
	          'lane-divide-three': {
	            group: 'lane-divide',
	            className: 'bpmn-icon-lane-divide-three',
	            title: translate('Divide into three Lanes'),
	            action: {
	              click: splitLaneHandler(3)
	            }
	          }
	        });
	      }
	    }

	    assign(actions, {
	      'lane-insert-below': {
	        group: 'lane-insert-below',
	        className: 'bpmn-icon-lane-insert-below',
	        title: translate('Add Lane below'),
	        action: {
	          click: function(event, element) {
	            modeling.addLane(element, 'bottom');
	          }
	        }
	      }
	    });

	  }

	  if (is$1(businessObject, 'bpmn:FlowNode')) {

	    if (is$1(businessObject, 'bpmn:EventBasedGateway')) {

	      assign(actions, {
	        'append.receive-task': appendAction(
	          'bpmn:ReceiveTask',
	          'bpmn-icon-receive-task'
	        ),
	        'append.message-intermediate-event': appendAction(
	          'bpmn:IntermediateCatchEvent',
	          'bpmn-icon-intermediate-event-catch-message',
	          translate('Append MessageIntermediateCatchEvent'),
	          { eventDefinitionType: 'bpmn:MessageEventDefinition' }
	        ),
	        'append.timer-intermediate-event': appendAction(
	          'bpmn:IntermediateCatchEvent',
	          'bpmn-icon-intermediate-event-catch-timer',
	          translate('Append TimerIntermediateCatchEvent'),
	          { eventDefinitionType: 'bpmn:TimerEventDefinition' }
	        ),
	        'append.condtion-intermediate-event': appendAction(
	          'bpmn:IntermediateCatchEvent',
	          'bpmn-icon-intermediate-event-catch-condition',
	          translate('Append ConditionIntermediateCatchEvent'),
	          { eventDefinitionType: 'bpmn:ConditionalEventDefinition' }
	        ),
	        'append.signal-intermediate-event': appendAction(
	          'bpmn:IntermediateCatchEvent',
	          'bpmn-icon-intermediate-event-catch-signal',
	          translate('Append SignalIntermediateCatchEvent'),
	          { eventDefinitionType: 'bpmn:SignalEventDefinition' }
	        )
	      });
	    } else

	    if (isEventType(businessObject, 'bpmn:BoundaryEvent', 'bpmn:CompensateEventDefinition')) {

	      assign(actions, {
	        'append.compensation-activity':
	            appendAction(
	              'bpmn:Task',
	              'bpmn-icon-task',
	              translate('Append compensation activity'),
	              {
	                isForCompensation: true
	              }
	            )
	      });
	    } else

	    if (!is$1(businessObject, 'bpmn:EndEvent') &&
	        !businessObject.isForCompensation &&
	        !isEventType(businessObject, 'bpmn:IntermediateThrowEvent', 'bpmn:LinkEventDefinition') &&
	        !isEventSubProcess(businessObject)) {

	      assign(actions, {
	        'append.end-event': appendAction(
	          'bpmn:EndEvent',
	          'bpmn-icon-end-event-none',
	          translate('Append EndEvent')
	        ),
	        'append.gateway': appendAction(
	          'bpmn:ExclusiveGateway',
	          'bpmn-icon-gateway-none',
	          translate('Append Gateway')
	        ),
	        'append.append-task': appendAction(
	          'bpmn:Task',
	          'bpmn-icon-task',
	          translate('Append Task')
	        ),
	        'append.intermediate-event': appendAction(
	          'bpmn:IntermediateThrowEvent',
	          'bpmn-icon-intermediate-event-none',
	          translate('Append Intermediate/Boundary Event')
	        )
	      });
	    }
	  }

	  if (!popupMenu.isEmpty(element, 'bpmn-replace')) {
	    // Replace menu entry
	    assign(actions, {
	      'replace': {
	        group: 'edit',
	        className: 'bpmn-icon-screw-wrench',
	        title: translate('Change type'),
	        action: {
	          click: function(event, element) {

	            var position = assign(getReplaceMenuPosition(element), {
	              cursor: { x: event.x, y: event.y }
	            });

	            popupMenu.open(element, 'bpmn-replace', position);
	          }
	        }
	      }
	    });
	  }

	  if (isAny(businessObject, [
	    'bpmn:FlowNode',
	    'bpmn:InteractionNode',
	    'bpmn:DataObjectReference',
	    'bpmn:DataStoreReference'
	  ])) {

	    assign(actions, {
	      'append.text-annotation': appendAction('bpmn:TextAnnotation', 'bpmn-icon-text-annotation'),

	      'connect': {
	        group: 'connect',
	        className: 'bpmn-icon-connection-multi',
	        title: translate('Connect using ' +
	                  (businessObject.isForCompensation ? '' : 'Sequence/MessageFlow or ') +
	                  'Association'),
	        action: {
	          click: startConnect,
	          dragstart: startConnect
	        }
	      }
	    });
	  }

	  if (isAny(businessObject, [ 'bpmn:DataObjectReference', 'bpmn:DataStoreReference' ])) {
	    assign(actions, {
	      'connect': {
	        group: 'connect',
	        className: 'bpmn-icon-connection-multi',
	        title: translate('Connect using DataInputAssociation'),
	        action: {
	          click: startConnect,
	          dragstart: startConnect
	        }
	      }
	    });
	  }

	  // delete element entry, only show if allowed by rules
	  var deleteAllowed = rules.allowed('elements.delete', { elements: [ element ] });

	  if (isArray(deleteAllowed)) {
	    // was the element returned as a deletion candidate?
	    deleteAllowed = deleteAllowed[0] === element;
	  }

	  if (deleteAllowed) {
	    assign(actions, {
	      'delete': {
	        group: 'edit',
	        className: 'bpmn-icon-trash',
	        title: translate('Remove'),
	        action: {
	          click: removeElement
	        }
	      }
	    });
	  }

	  return actions;
	};

	function isEventType(eventBo, type, definition) {

	  var isType = eventBo.$instanceOf(type);
	  var isDefinition = false;

	  var definitions = eventBo.eventDefinitions || [];
	  forEach(definitions, function(def) {
	    if (def.$type === definition) {
	      isDefinition = true;
	    }
	  });

	  return isType && isDefinition;
	}

	var ContextPadModule$1 = {
	  __depends__: [
	    DirectEditingModule,
	    ContextPadModule,
	    SelectionModule,
	    ConnectModule,
	    CreateModule,
	    PopupMenuModule$1
	  ],
	  __init__: [ 'contextPadProvider' ],
	  contextPadProvider: [ 'type', ContextPadProvider ]
	};

	/**
	 * A clip board stub
	 */
	function Clipboard() {}


	Clipboard.prototype.get = function() {
	  return this._data;
	};

	Clipboard.prototype.set = function(data) {
	  this._data = data;
	};

	Clipboard.prototype.clear = function() {
	  var data = this._data;

	  delete this._data;

	  return data;
	};

	Clipboard.prototype.isEmpty = function() {
	  return !this._data;
	};

	var ClipboardModule = {
	  clipboard: [ 'type', Clipboard ]
	};

	function getTopLevel(elements) {
	  var topLevel = {},
	      parents = [],
	      result = [],
	      clearedParents = [];

	  forEach(elements, function(element) {
	    var parent = element.parent;

	    if (!topLevel[parent.id]) {
	      topLevel[parent.id] = [];
	    }

	    if (parents.indexOf(parent.id) === -1) {
	      parents.push(parent.id);
	    }

	    topLevel[parent.id].push(element);
	  });

	  forEach(parents, function(parent) {
	    forEach(topLevel[parent], function(element) {
	      if (topLevel[element.id]) {
	        clearedParents.push(element.id);
	      }
	    });
	  });

	  forEach(parents, function(parent) {
	    var idx = clearedParents.indexOf(parent);

	    if (idx === -1) {
	      result = result.concat(topLevel[parent]);
	    }
	  });

	  return result;
	}

	function CopyPaste(
	    eventBus, modeling, elementFactory,
	    rules, clipboard, canvas) {

	  this._eventBus = eventBus;
	  this._modeling = modeling;
	  this._elementFactory = elementFactory;
	  this._rules = rules;
	  this._canvas = canvas;

	  this._clipboard = clipboard;

	  this._descriptors = [];


	  // Element creation priorities:
	  // - 1: Independent shapes
	  // - 2: Attached shapes
	  // - 3: Connections
	  // - 4: labels
	  this.registerDescriptor(function(element, descriptor) {
	    // Base priority
	    descriptor.priority = 1;

	    descriptor.id = element.id;

	    if (element.parent) {
	      descriptor.parent = element.parent.id;
	    }

	    if (element.labelTarget) {
	      // Labels priority
	      descriptor.priority = 4;
	      descriptor.labelTarget = element.labelTarget.id;
	    }

	    if (element.host) {
	      // Attached shapes priority
	      descriptor.priority = 2;
	      descriptor.host = element.host.id;
	    }

	    if (typeof element.x === 'number') {
	      descriptor.x = element.x;
	      descriptor.y = element.y;
	    }

	    if (element.width) {
	      descriptor.width = element.width;
	      descriptor.height = element.height;
	    }

	    if (element.waypoints) {
	      // Connections priority
	      descriptor.priority = 3;
	      descriptor.waypoints = [];

	      forEach(element.waypoints, function(waypoint) {
	        var wp = {
	          x: waypoint.x,
	          y: waypoint.y
	        };

	        if (waypoint.original) {
	          wp.original = {
	            x: waypoint.original.x,
	            y: waypoint.original.y
	          };
	        }

	        descriptor.waypoints.push(wp);
	      });
	    }

	    if (element.source && element.target) {
	      descriptor.source = element.source.id;
	      descriptor.target = element.target.id;
	    }

	    return descriptor;
	  });
	}

	CopyPaste.$inject = [
	  'eventBus',
	  'modeling',
	  'elementFactory',
	  'rules',
	  'clipboard',
	  'canvas'
	];


	/**
	 * Copy a number of elements.
	 *
	 * @param {djs.model.Base} selectedElements
	 *
	 * @return {Object} the copied tree
	 */
	CopyPaste.prototype.copy = function(selectedElements) {
	  var clipboard = this._clipboard,
	      tree, bbox;

	  if (!isArray(selectedElements)) {
	    selectedElements = selectedElements ? [ selectedElements ] : [];
	  }

	  if (!selectedElements.length) {
	    return;
	  }

	  tree = this.createTree(selectedElements);

	  bbox = this._bbox = center(getBBox(tree.allShapes));

	  // not needed after computing the center position of the copied elements
	  delete tree.allShapes;

	  forEach(tree, function(elements) {

	    forEach(elements, function(element) {
	      var delta$1, labelTarget;

	      // set label's relative position to their label target
	      if (element.labelTarget) {
	        labelTarget = find(elements, matchPattern({ id: element.labelTarget }));

	        // just grab the delta from the first waypoint
	        if (labelTarget.waypoints) {
	          delta$1 = delta(element, labelTarget.waypoints[0]);
	        } else {
	          delta$1 = delta(element, labelTarget);
	        }

	      } else
	      if (element.priority === 3) {
	        // connections have priority 3
	        delta$1 = [];

	        forEach(element.waypoints, function(waypoint) {
	          var waypointDelta = delta(waypoint, bbox);

	          delta$1.push(waypointDelta);
	        });
	      } else {
	        delta$1 = delta(element, bbox);
	      }

	      element.delta = delta$1;
	    });
	  });

	  this._eventBus.fire('elements.copy', { context: { tree: tree } });

	  // if tree is empty, means that nothing can be or is allowed to be copied
	  if (Object.keys(tree).length === 0) {
	    clipboard.clear();
	  } else {
	    clipboard.set(tree);
	  }

	  this._eventBus.fire('elements.copied', { context: { tree: tree } });

	  return tree;
	};


	// Allow pasting under the cursor
	CopyPaste.prototype.paste = function(context) {
	  var clipboard = this._clipboard,
	      modeling = this._modeling,
	      eventBus = this._eventBus,
	      rules = this._rules;

	  var tree = clipboard.get(),
	      topParent = context.element,
	      position = context.point,
	      newTree, canPaste;

	  if (clipboard.isEmpty()) {
	    return;
	  }

	  newTree = reduce(tree, function(pasteTree, elements, depthStr) {
	    var depth = parseInt(depthStr, 10);

	    if (isNaN(depth)) {
	      return pasteTree;
	    }

	    pasteTree[depth] = elements;

	    return pasteTree;
	  }, {});


	  canPaste = rules.allowed('elements.paste', {
	    tree: newTree,
	    target: topParent
	  });

	  if (!canPaste) {
	    eventBus.fire('elements.paste.rejected', {
	      context: {
	        tree: newTree,
	        position: position,
	        target: topParent
	      }
	    });

	    return;
	  }

	  modeling.pasteElements(newTree, topParent, position);
	};


	CopyPaste.prototype._computeDelta = function(elements, element) {
	  var bbox = this._bbox,
	      delta$1 = {};

	  // set label's relative position to their label target
	  if (element.labelTarget) {
	    return delta(element, element.labelTarget);
	  }

	  // connections have prority 3
	  if (element.priority === 3) {
	    delta$1 = [];

	    forEach(element.waypoints, function(waypoint) {
	      var waypointDelta = delta(waypoint, bbox);

	      delta$1.push(waypointDelta);
	    });
	  } else {
	    delta$1 = delta(element, bbox);
	  }

	  return delta$1;
	};


	/**
	 * Checks if the element in question has a relations to other elements.
	 * Possible dependants: connections, labels, attachers
	 *
	 * @param  {Array} elements
	 * @param  {Object} element
	 *
	 * @return {Boolean}
	 */
	CopyPaste.prototype.hasRelations = function(elements, element) {
	  var source, target, labelTarget;

	  if (element.waypoints) {
	    source = find(elements, matchPattern({ id: element.source.id }));
	    target = find(elements, matchPattern({ id: element.target.id }));

	    if (!source || !target) {
	      return false;
	    }
	  }

	  if (element.labelTarget) {
	    labelTarget = find(elements, matchPattern({ id: element.labelTarget.id }));

	    if (!labelTarget) {
	      return false;
	    }
	  }

	  return true;
	};


	CopyPaste.prototype.registerDescriptor = function(descriptor) {
	  if (typeof descriptor !== 'function') {
	    throw new Error('the descriptor must be a function');
	  }

	  if (this._descriptors.indexOf(descriptor) !== -1) {
	    throw new Error('this descriptor is already registered');
	  }

	  this._descriptors.push(descriptor);
	};


	CopyPaste.prototype._executeDescriptors = function(data) {
	  if (!data.descriptor) {
	    data.descriptor = {};
	  }

	  forEach(this._descriptors, function(descriptor) {
	    data.descriptor = descriptor(data.element, data.descriptor);
	  });

	  return data;
	};

	/**
	 * Creates a tree like structure from an arbitrary collection of elements
	 *
	 * @example
	 * tree: {
	 *	0: [
	 *		{ id: 'shape_12da', priority: 1, ... },
	 *		{ id: 'shape_01bj', priority: 1, ... },
	 *		{ id: 'connection_79fa', source: 'shape_12da', target: 'shape_01bj', priority: 3, ... },
	 *	],
	 *	1: [ ... ]
	 * };
	 *
	 * @param  {Array} elements
	 * @return {Object}
	 */
	CopyPaste.prototype.createTree = function(elements) {
	  var rules = this._rules,
	      self = this;

	  var tree = {},
	      includedElements = [],
	      _elements;

	  var topLevel = getTopLevel(elements);

	  tree.allShapes = [];

	  function canCopy(collection, element) {
	    return rules.allowed('element.copy', {
	      collection: collection,
	      element: element
	    });
	  }

	  function includeElement(data) {
	    var idx = findIndex(includedElements, matchPattern({ element: data.element })),
	        element;

	    if (idx !== -1) {
	      element = includedElements[idx];
	    } else {
	      return includedElements.push(data);
	    }

	    // makes sure that it has the correct depth
	    if (element.depth < data.depth) {
	      includedElements.splice(idx, 1);

	      includedElements.push(data);
	    }
	  }


	  eachElement(topLevel, function(element, i, depth) {
	    var nestedChildren = element.children;

	    // don't add labels directly
	    if (element.labelTarget) {
	      return;
	    }

	    function getNested(lists) {
	      forEach(lists, function(list) {
	        if (list && list.length) {

	          forEach(list, function(elem) {

	            forEach(elem.labels, function(label) {
	              includeElement({
	                element: label,
	                depth: depth
	              });
	            });

	            includeElement({
	              element: elem,
	              depth: depth
	            });
	          });
	        }
	      });
	    }

	    // fetch element's labels
	    forEach(element.labels, function(label) {

	      includeElement({
	        element: label,
	        depth: depth
	      });
	    });

	    getNested([ element.attachers, element.incoming, element.outgoing ]);

	    includeElement({
	      element: element,
	      depth: depth
	    });

	    if (nestedChildren) {
	      return nestedChildren;
	    }
	  });

	  includedElements = map(includedElements, function(data) {
	    // this is where other registered descriptors hook in
	    return self._executeDescriptors(data);
	  });

	  // order the elements to check if the ones dependant on others (by relationship)
	  // can be copied. f.ex: label needs it's label target
	  includedElements = sortBy(includedElements, function(data) {
	    return data.descriptor.priority;
	  });

	  _elements = map(includedElements, function(data) {
	    return data.element;
	  });

	  forEach(includedElements, function(data) {
	    var depth = data.depth;

	    if (!self.hasRelations(tree.allShapes, data.element)) {
	      return;
	    }

	    if (!canCopy(_elements, data.element)) {
	      return;
	    }

	    tree.allShapes.push(data.element);

	    // create depth branches
	    if (!tree[depth]) {
	      tree[depth] = [];
	    }

	    tree[depth].push(data.descriptor);
	  });

	  return tree;
	};

	var CopyPasteModule = {
	  __depends__: [
	    ClipboardModule,
	    RulesModule,
	    MouseTrackingModule
	  ],
	  __init__: [ 'copyPaste' ],
	  copyPaste: [ 'type', CopyPaste ]
	};

	function setProperties(descriptor, data, properties) {
	  forEach(properties, function(property) {
	    if (data[property] !== undefined) {
	      descriptor[property] = data[property];
	    }
	  });
	}

	function removeProperties(element, properties) {
	  forEach(properties, function(prop) {
	    if (element[prop]) {
	      delete element[prop];
	    }
	  });
	}

	function BpmnCopyPaste(
	    bpmnFactory, eventBus, copyPaste,
	    clipboard, canvas, bpmnRules) {

	  var helper = new ModelCloneHelper(eventBus, bpmnFactory);

	  copyPaste.registerDescriptor(function(element, descriptor) {
	    var businessObject = descriptor.oldBusinessObject = getBusinessObject(element);

	    var colors = {};

	    descriptor.type = element.type;

	    setProperties(descriptor, businessObject.di, [ 'isExpanded' ]);

	    setProperties(colors, businessObject.di, [ 'fill', 'stroke' ]);

	    descriptor.colors = colors;

	    if (element.type === 'label') {
	      return descriptor;
	    }

	    setProperties(descriptor, businessObject, [
	      'processRef',
	      'triggeredByEvent'
	    ]);

	    if (businessObject.default) {
	      descriptor.default = businessObject.default.id;
	    }

	    return descriptor;
	  });

	  eventBus.on('element.paste', function(context) {
	    var descriptor = context.descriptor,
	        createdElements = context.createdElements,
	        parent = descriptor.parent,
	        rootElement = canvas.getRootElement(),
	        oldBusinessObject = descriptor.oldBusinessObject,
	        newBusinessObject,
	        source,
	        target,
	        canConnect;

	    newBusinessObject = bpmnFactory.create(oldBusinessObject.$type);

	    var properties = getProperties(oldBusinessObject.$descriptor);

	    properties = filter(properties, function(property) {
	      return IGNORED_PROPERTIES.indexOf(property.replace(/bpmn:/, '')) === -1;
	    });

	    descriptor.businessObject = helper.clone(oldBusinessObject, newBusinessObject, properties);

	    if (descriptor.type === 'label') {
	      return;
	    }

	    if (is$1(parent, 'bpmn:Process')) {
	      descriptor.parent = is$1(rootElement, 'bpmn:Collaboration') ? rootElement : parent;
	    }

	    if (descriptor.type === 'bpmn:DataOutputAssociation' ||
	        descriptor.type === 'bpmn:DataInputAssociation' ||
	        descriptor.type === 'bpmn:MessageFlow') {
	      descriptor.parent = rootElement;
	    }

	    if (is$1(parent, 'bpmn:Lane')) {
	      descriptor.parent = parent.parent;
	    }

	    // make sure that the correct type of connection is created
	    if (descriptor.waypoints) {
	      source = createdElements[descriptor.source];
	      target = createdElements[descriptor.target];

	      if (source && target) {
	        source = source.element;
	        target = target.element;
	      }

	      canConnect = bpmnRules.canConnect(source, target);

	      if (canConnect) {
	        descriptor.type = canConnect.type;
	      }
	    }

	    // remove the id or else we cannot paste multiple times
	    delete newBusinessObject.id;

	    // assign an ID
	    bpmnFactory._ensureId(newBusinessObject);

	    if (descriptor.type === 'bpmn:Participant' && descriptor.processRef) {
	      descriptor.processRef = newBusinessObject.processRef = bpmnFactory.create('bpmn:Process');
	    }

	    setProperties(newBusinessObject, descriptor, [
	      'isExpanded',
	      'triggeredByEvent'
	    ]);

	    removeProperties(descriptor, [
	      'triggeredByEvent'
	    ]);
	  });

	}


	BpmnCopyPaste.$inject = [
	  'bpmnFactory',
	  'eventBus',
	  'copyPaste',
	  'clipboard',
	  'canvas',
	  'bpmnRules'
	];

	var CopyPasteModule$1 = {
	  __depends__: [
	    CopyPasteModule
	  ],
	  __init__: [ 'bpmnCopyPaste' ],
	  bpmnCopyPaste: [ 'type', BpmnCopyPaste ]
	};

	var AXIS_DIMENSIONS = {
	  horizontal: [ 'x', 'width' ],
	  vertical: [ 'y', 'height' ]
	};

	var THRESHOLD$1 = 5;


	/**
	 * Groups and filters elements and then trigger even distribution.
	 */
	function DistributeElements(modeling) {
	  this._modeling = modeling;

	  this._filters = [];

	  // register filter for filtering big elements
	  this.registerFilter(function(elements, axis, dimension) {
	    var elementsSize = 0,
	        numOfShapes = 0,
	        avgDimension;

	    forEach(elements, function(element) {
	      if (element.waypoints || element.labelTarget) {
	        return;
	      }

	      elementsSize += element[dimension];

	      numOfShapes += 1;
	    });

	    avgDimension = Math.round(elementsSize / numOfShapes);

	    return filter(elements, function(element) {
	      return element[dimension] < (avgDimension + 50);
	    });
	  });

	}

	DistributeElements.$inject = [ 'modeling' ];


	/**
	 * Registers filter functions that allow external parties to filter
	 * out certain elements.
	 *
	 * @param  {Function} filterFn
	 */
	DistributeElements.prototype.registerFilter = function(filterFn) {
	  if (typeof filterFn !== 'function') {
	    throw new Error('the filter has to be a function');
	  }

	  this._filters.push(filterFn);
	};

	/**
	 * Distributes the elements with a given orientation
	 *
	 * @param  {Array} elements    [description]
	 * @param  {String} orientation [description]
	 */
	DistributeElements.prototype.trigger = function(elements, orientation) {
	  var modeling = this._modeling;

	  var groups,
	      distributableElements;

	  if (elements.length < 3) {
	    return;
	  }

	  this._setOrientation(orientation);

	  distributableElements = this._filterElements(elements);

	  groups = this._createGroups(distributableElements);

	  // nothing to distribute
	  if (groups.length <= 2) {
	    return;
	  }

	  modeling.distributeElements(groups, this._axis, this._dimension);

	  return groups;
	};

	/**
	 * Filters the elements with provided filters by external parties
	 *
	 * @param  {Array[Elements]} elements
	 *
	 * @return {Array[Elements]}
	 */
	DistributeElements.prototype._filterElements = function(elements) {
	  var filters = this._filters,
	      axis = this._axis,
	      dimension = this._dimension,
	      distributableElements = [].concat(elements);

	  if (!filters.length) {
	    return elements;
	  }

	  forEach(filters, function(filterFn) {
	    distributableElements = filterFn(distributableElements, axis, dimension);
	  });

	  return distributableElements;
	};


	/**
	 * Create range (min, max) groups. Also tries to group elements
	 * together that share the same range.
	 *
	 * @example
	 * 	var distributableElements = [
	 * 		{
	 * 			range: {
	 * 				min: 100,
	 * 				max: 200
	 * 			},
	 * 			elements: [ { id: 'shape1', .. }]
	 * 		}
	 * 	]
	 *
	 * @param  {Array} elements
	 *
	 * @return {Array[Objects]}
	 */
	DistributeElements.prototype._createGroups = function(elements) {
	  var rangeGroups = [],
	      self = this,
	      axis = this._axis,
	      dimension = this._dimension;

	  if (!axis) {
	    throw new Error('must have a defined "axis" and "dimension"');
	  }

	  // sort by 'left->right' or 'top->bottom'
	  var sortedElements = sortBy(elements, axis);

	  forEach(sortedElements, function(element, idx) {
	    var elementRange = self._findRange(element, axis, dimension),
	        range;

	    var previous = rangeGroups[rangeGroups.length - 1];

	    if (previous && self._hasIntersection(previous.range, elementRange)) {
	      rangeGroups[rangeGroups.length - 1].elements.push(element);
	    } else {
	      range = { range: elementRange, elements: [ element ] };

	      rangeGroups.push(range);
	    }
	  });

	  return rangeGroups;
	};


	/**
	 * Maps a direction to the according axis and dimension
	 *
	 * @param  {String} direction 'horizontal' or 'vertical'
	 */
	DistributeElements.prototype._setOrientation = function(direction) {
	  var orientation = AXIS_DIMENSIONS[direction];

	  this._axis = orientation[0];
	  this._dimension = orientation[1];
	};


	/**
	 * Checks if the two ranges intercept each other
	 *
	 * @param  {Object} rangeA {min, max}
	 * @param  {Object} rangeB {min, max}
	 *
	 * @return {Boolean}
	 */
	DistributeElements.prototype._hasIntersection = function(rangeA, rangeB) {
	  return Math.max(rangeA.min, rangeA.max) >= Math.min(rangeB.min, rangeB.max) &&
	         Math.min(rangeA.min, rangeA.max) <= Math.max(rangeB.min, rangeB.max);
	};


	/**
	 * Returns the min and max values for an element
	 *
	 * @param  {[type]} element   [description]
	 * @param  {[type]} axis      [description]
	 * @param  {[type]} dimension [description]
	 *
	 * @return {[type]}           [description]
	 */
	DistributeElements.prototype._findRange = function(element) {
	  var axis = element[this._axis],
	      dimension = element[this._dimension];

	  return {
	    min: axis + THRESHOLD$1,
	    max: axis + dimension - THRESHOLD$1
	  };
	};

	var DistributeElementsModule = {
	  __init__: [ 'distributeElements' ],
	  distributeElements: [ 'type', DistributeElements ]
	};

	/**
	 * Registers element exclude filters for elements that
	 * currently do not support distribution.
	 */
	function BpmnDistributeElements(distributeElements) {

	  distributeElements.registerFilter(function(elements) {
	    return filter(elements, function(element) {
	      var cannotDistribute = isAny(element, [
	        'bpmn:Association',
	        'bpmn:BoundaryEvent',
	        'bpmn:DataInputAssociation',
	        'bpmn:DataOutputAssociation',
	        'bpmn:Lane',
	        'bpmn:MessageFlow',
	        'bpmn:Participant',
	        'bpmn:SequenceFlow',
	        'bpmn:TextAnnotation'
	      ]);

	      return !(element.labelTarget || cannotDistribute);
	    });
	  });
	}

	BpmnDistributeElements.$inject = [ 'distributeElements' ];

	var DistributeElementsModule$1 = {
	  __depends__: [
	    DistributeElementsModule
	  ],
	  __init__: [ 'bpmnDistributeElements' ],
	  bpmnDistributeElements: [ 'type', BpmnDistributeElements ]
	};

	var NOT_REGISTERED_ERROR = 'is not a registered action',
	    IS_REGISTERED_ERROR = 'is already registered';


	/**
	 * An interface that provides access to modeling actions by decoupling
	 * the one who requests the action to be triggered and the trigger itself.
	 *
	 * It's possible to add new actions by registering them with ´registerAction´
	 * and likewise unregister existing ones with ´unregisterAction´.
	 *
	 *
	 * ## Life-Cycle and configuration
	 *
	 * The editor actions will wait for diagram initialization before
	 * registering default actions _and_ firing an `editorActions.init` event.
	 *
	 * Interested parties may listen to the `editorActions.init` event with
	 * low priority to check, which actions got registered. Other components
	 * may use the event to register their own actions via `registerAction`.
	 *
	 * @param {EventBus} eventBus
	 * @param {Injector} injector
	 */
	function EditorActions(eventBus, injector) {

	  // initialize actions
	  this._actions = {};

	  var self = this;

	  eventBus.on('diagram.init', function() {

	    // all diagram modules got loaded; check which ones
	    // are available and register the respective default actions
	    self._registerDefaultActions(injector);

	    // ask interested parties to register available editor
	    // actions on diagram initialization
	    eventBus.fire('editorActions.init', {
	      editorActions: self
	    });
	  });

	}

	EditorActions.$inject = [
	  'eventBus',
	  'injector'
	];

	/**
	 * Register default actions.
	 *
	 * @param {Injector} injector
	 */
	EditorActions.prototype._registerDefaultActions = function(injector) {

	  // (1) retrieve optional components to integrate with

	  var commandStack = injector.get('commandStack', false);
	  var modeling = injector.get('modeling', false);
	  var selection = injector.get('selection', false);
	  var zoomScroll = injector.get('zoomScroll', false);
	  var copyPaste = injector.get('copyPaste', false);
	  var canvas = injector.get('canvas', false);
	  var rules = injector.get('rules', false);
	  var mouseTracking = injector.get('mouseTracking', false);
	  var keyboardMove = injector.get('keyboardMove', false);
	  var keyboardMoveSelection = injector.get('keyboardMoveSelection', false);

	  // (2) check components and register actions

	  if (commandStack) {
	    this.register('undo', function() {
	      commandStack.undo();
	    });

	    this.register('redo', function() {
	      commandStack.redo();
	    });
	  }

	  if (copyPaste && selection) {
	    this.register('copy', function() {
	      var selectedElements = selection.get();

	      copyPaste.copy(selectedElements);
	    });
	  }

	  if (mouseTracking && copyPaste) {
	    this.register('paste', function() {
	      var context = mouseTracking.getHoverContext();

	      copyPaste.paste(context);
	    });
	  }

	  if (zoomScroll) {
	    this.register('stepZoom', function(opts) {
	      zoomScroll.stepZoom(opts.value);
	    });
	  }

	  if (canvas) {
	    this.register('zoom', function(opts) {
	      canvas.zoom(opts.value);
	    });
	  }

	  if (modeling && selection && rules) {
	    this.register('removeSelection', function() {

	      var selectedElements = selection.get();

	      if (!selectedElements.length) {
	        return;
	      }

	      var allowed = rules.allowed('elements.delete', { elements: selectedElements }),
	          removableElements;

	      if (allowed === false) {
	        return;
	      }
	      else if (isArray(allowed)) {
	        removableElements = allowed;
	      }
	      else {
	        removableElements = selectedElements;
	      }

	      if (removableElements.length) {
	        modeling.removeElements(removableElements.slice());
	      }
	    });
	  }

	  if (keyboardMove) {
	    this.register('moveCanvas', function(opts) {
	      keyboardMove.moveCanvas(opts);
	    });
	  }

	  if (keyboardMoveSelection) {
	    this.register('moveSelection', function(opts) {
	      keyboardMoveSelection.moveSelection(opts.direction, opts.accelerated);
	    });
	  }

	};


	/**
	 * Triggers a registered action
	 *
	 * @param  {String} action
	 * @param  {Object} opts
	 *
	 * @return {Unknown} Returns what the registered listener returns
	 */
	EditorActions.prototype.trigger = function(action, opts) {
	  if (!this._actions[action]) {
	    throw error$2(action, NOT_REGISTERED_ERROR);
	  }

	  return this._actions[action](opts);
	};


	/**
	 * Registers a collections of actions.
	 * The key of the object will be the name of the action.
	 *
	 * @example
	 * ´´´
	 * var actions = {
	 *   spaceTool: function() {
	 *     spaceTool.activateSelection();
	 *   },
	 *   lassoTool: function() {
	 *     lassoTool.activateSelection();
	 *   }
	 * ];
	 *
	 * editorActions.register(actions);
	 *
	 * editorActions.isRegistered('spaceTool'); // true
	 * ´´´
	 *
	 * @param  {Object} actions
	 */
	EditorActions.prototype.register = function(actions, listener) {
	  var self = this;

	  if (typeof actions === 'string') {
	    return this._registerAction(actions, listener);
	  }

	  forEach(actions, function(listener, action) {
	    self._registerAction(action, listener);
	  });
	};

	/**
	 * Registers a listener to an action key
	 *
	 * @param  {String} action
	 * @param  {Function} listener
	 */
	EditorActions.prototype._registerAction = function(action, listener) {
	  if (this.isRegistered(action)) {
	    throw error$2(action, IS_REGISTERED_ERROR);
	  }

	  this._actions[action] = listener;
	};

	/**
	 * Unregister an existing action
	 *
	 * @param {String} action
	 */
	EditorActions.prototype.unregister = function(action) {
	  if (!this.isRegistered(action)) {
	    throw error$2(action, NOT_REGISTERED_ERROR);
	  }

	  this._actions[action] = undefined;
	};

	/**
	 * Returns the number of actions that are currently registered
	 *
	 * @return {Number}
	 */
	EditorActions.prototype.getActions = function() {
	  return Object.keys(this._actions);
	};

	/**
	 * Checks wether the given action is registered
	 *
	 * @param {String} action
	 *
	 * @return {Boolean}
	 */
	EditorActions.prototype.isRegistered = function(action) {
	  return !!this._actions[action];
	};


	function error$2(action, message) {
	  return new Error(action + ' ' + message);
	}

	var EditorActionsModule = {
	  __init__: [ 'editorActions' ],
	  editorActions: [ 'type', EditorActions ]
	};

	/**
	 * Registers and executes BPMN specific editor actions.
	 *
	 * @param {Injector} injector
	 */
	function BpmnEditorActions(injector) {
	  injector.invoke(EditorActions, this);
	}

	inherits_browser(BpmnEditorActions, EditorActions);

	BpmnEditorActions.$inject = [
	  'injector'
	];

	/**
	 * Register default actions.
	 *
	 * @param {Injector} injector
	 */
	BpmnEditorActions.prototype._registerDefaultActions = function(injector) {

	  // (0) invoke super method

	  EditorActions.prototype._registerDefaultActions.call(this, injector);

	  // (1) retrieve optional components to integrate with

	  var canvas = injector.get('canvas', false);
	  var elementRegistry = injector.get('elementRegistry', false);
	  var selection = injector.get('selection', false);
	  var spaceTool = injector.get('spaceTool', false);
	  var lassoTool = injector.get('lassoTool', false);
	  var handTool = injector.get('handTool', false);
	  var globalConnect = injector.get('globalConnect', false);
	  var distributeElements = injector.get('distributeElements', false);
	  var alignElements = injector.get('alignElements', false);
	  var directEditing = injector.get('directEditing', false);
	  var searchPad = injector.get('searchPad', false);
	  var modeling = injector.get('modeling', false);

	  // (2) check components and register actions

	  if (canvas && elementRegistry && selection) {
	    this._registerAction('selectElements', function() {
	      // select all elements except for the invisible
	      // root element
	      var rootElement = canvas.getRootElement();

	      var elements = elementRegistry.filter(function(element) {
	        return element !== rootElement;
	      });

	      selection.select(elements);

	      return elements;
	    });
	  }

	  if (spaceTool) {
	    this._registerAction('spaceTool', function() {
	      spaceTool.toggle();
	    });
	  }

	  if (lassoTool) {
	    this._registerAction('lassoTool', function() {
	      lassoTool.toggle();
	    });
	  }

	  if (handTool) {
	    this._registerAction('handTool', function() {
	      handTool.toggle();
	    });
	  }

	  if (globalConnect) {
	    this._registerAction('globalConnectTool', function() {
	      globalConnect.toggle();
	    });
	  }

	  if (selection && distributeElements) {
	    this._registerAction('distributeElements', function(opts) {
	      var currentSelection = selection.get(),
	          type = opts.type;

	      if (currentSelection.length) {
	        distributeElements.trigger(currentSelection, type);
	      }
	    });
	  }

	  if (selection && alignElements) {
	    this._registerAction('alignElements', function(opts) {
	      var currentSelection = selection.get(),
	          aligneableElements = [],
	          type = opts.type;

	      if (currentSelection.length) {
	        aligneableElements = filter(currentSelection, function(element) {
	          return !is$1(element, 'bpmn:Lane');
	        });

	        alignElements.trigger(aligneableElements, type);
	      }
	    });
	  }

	  if (selection && modeling) {
	    this._registerAction('setColor', function(opts) {
	      var currentSelection = selection.get();

	      if (currentSelection.length) {
	        modeling.setColor(currentSelection, opts);
	      }
	    });
	  }

	  if (selection && directEditing) {
	    this._registerAction('directEditing', function() {
	      var currentSelection = selection.get();

	      if (currentSelection.length) {
	        directEditing.activate(currentSelection[0]);
	      }
	    });
	  }

	  if (searchPad) {
	    this._registerAction('find', function() {
	      searchPad.toggle();
	    });
	  }

	  if (canvas && modeling) {
	    this._registerAction('moveToOrigin', function() {
	      var rootElement = canvas.getRootElement(),
	          boundingBox,
	          elements;

	      if (is$1(rootElement, 'bpmn:Collaboration')) {
	        elements = elementRegistry.filter(function(element) {
	          return is$1(element.parent, 'bpmn:Collaboration');
	        });
	      } else {
	        elements = elementRegistry.filter(function(element) {
	          return element !== rootElement && !is$1(element.parent, 'bpmn:SubProcess');
	        });
	      }

	      boundingBox = getBBox(elements);

	      modeling.moveElements(
	        elements,
	        { x: -boundingBox.x, y: -boundingBox.y },
	        rootElement
	      );
	    });
	  }

	};

	var EditorActionsModule$1 = {
	  __depends__: [
	    EditorActionsModule
	  ],
	  editorActions: [ 'type', BpmnEditorActions ]
	};

	function BpmnGridSnapping(eventBus) {
	  eventBus.on([
	    'create.init',
	    'shape.move.init'
	  ], function(event) {
	    var context = event.context,
	        shape = event.shape;

	    if (isAny(shape, [
	      'bpmn:Participant',
	      'bpmn:SubProcess',
	      'bpmn:TextAnnotation'
	    ])) {
	      if (!context.gridSnappingContext) {
	        context.gridSnappingContext = {};
	      }

	      context.gridSnappingContext.snapLocation = 'top-left';
	    }
	  });
	}

	BpmnGridSnapping.$inject = [ 'eventBus' ];

	var SPACING = 10;

	function quantize(value, quantum, fn) {
	  if (!fn) {
	    fn = 'round';
	  }

	  return Math[ fn ](value / quantum) * quantum;
	}

	var LOWER_PRIORITY = 1200;
	var LOW_PRIORITY$6 = 800;

	/**
	 * Basic grid snapping that covers connecting, creating, moving, resizing shapes, moving bendpoints
	 * and connection segments.
	 */
	function GridSnapping(
	    eventBus,
	    config
	) {

	  var active = !config || config.active !== false;

	  this._eventBus = eventBus;

	  var self = this;

	  eventBus.on('diagram.init', LOW_PRIORITY$6, function() {
	    self.setActive(active);
	  });

	  eventBus.on([
	    'create.move',
	    'create.end',
	    'bendpoint.move.move',
	    'bendpoint.move.end',
	    'connect.move',
	    'connect.end',
	    'connectionSegment.move.move',
	    'connectionSegment.move.end',
	    'resize.move',
	    'resize.end',
	    'shape.move.move',
	    'shape.move.end'
	  ], LOWER_PRIORITY, function(event) {
	    var originalEvent = event.originalEvent;

	    if (!self.active || (originalEvent && isCmd(originalEvent))) {
	      return;
	    }

	    var context = event.context;

	    [ 'x', 'y' ].forEach(function(axis) {
	      var options = {};

	      // allow snapping with offset
	      if (context.gridSnappingContext && context.gridSnappingContext.snapLocation) {
	        assign(options, {
	          offset: getSnapOffset(event, axis)
	        });
	      }

	      // allow snapping with min and max
	      var snapConstraints = getSnapConstraints(event, axis);

	      if (snapConstraints) {
	        assign(options, snapConstraints);
	      }

	      if (!isSnapped(event, axis)) {
	        self.snapEvent(event, axis, options);
	      }
	    });
	  });
	}

	/**
	 * Snap an events x or y with optional min, max and offset.
	 *
	 * @param {Object} event
	 * @param {string} axis
	 * @param {number} [options.min]
	 * @param {number} [options.max]
	 * @param {number} [options.offset]
	 */
	GridSnapping.prototype.snapEvent = function(event, axis, options) {
	  var snappedValue = this.snapValue(event[ axis ], options);

	  setSnapped(event, axis, snappedValue);
	};

	/**
	 * Expose grid spacing for third parties (i.e. extensions).
	 *
	 * @return {Number} spacing of grid dots
	 */
	GridSnapping.prototype.getGridSpacing = function() {
	  return SPACING;
	};

	/**
	 * Snap value with optional min, max and offset.
	 *
	 * @param {number} value
	 * @param {Object} options
	 * @param {number} [options.min]
	 * @param {number} [options.max]
	 * @param {number} [options.offset]
	 */
	GridSnapping.prototype.snapValue = function(value, options) {
	  var offset = 0;

	  if (options && options.offset) {
	    offset = options.offset;
	  }

	  value += offset;

	  value = quantize(value, SPACING);

	  var min, max;

	  if (options && options.min) {
	    min = options.min;

	    if (isNumber(min)) {
	      min = quantize(min + offset, SPACING, 'ceil');

	      value = Math.max(value, min);
	    }
	  }

	  if (options && options.max) {
	    max = options.max;

	    if (isNumber(max)) {
	      max = quantize(max + offset, SPACING, 'floor');

	      value = Math.min(value, max);
	    }
	  }

	  value -= offset;

	  return value;
	};

	GridSnapping.prototype.isActive = function() {
	  return this.active;
	};

	GridSnapping.prototype.setActive = function(active) {
	  this.active = active;

	  this._eventBus.fire('gridSnapping.toggle', { active: active });
	};

	GridSnapping.prototype.toggleActive = function() {
	  this.setActive(!this.active);
	};

	GridSnapping.$inject = [
	  'eventBus',
	  'config.gridSnapping'
	];

	// helpers //////////

	/**
	 * Get minimum and maximum snap constraints.
	 *
	 * @param {Object} event
	 * @param {Object} event.context
	 * @param {string} axis
	 *
	 * @returns {Object}
	 */
	function getSnapConstraints(event, axis) {
	  var context = event.context,
	      createConstraints = context.createConstraints,
	      resizeConstraints = context.resizeConstraints || {};

	  var direction = context.direction;

	  var snapConstraints = null;

	  // create
	  if (createConstraints) {
	    snapConstraints = {};

	    if (isHorizontal(axis)) {
	      snapConstraints.min = createConstraints.left;
	      snapConstraints.max = createConstraints.right;
	    } else {
	      snapConstraints.min = createConstraints.top;
	      snapConstraints.max = createConstraints.bottom;
	    }
	  }

	  // resize
	  var minResizeConstraints = resizeConstraints.min,
	      maxResizeConstraints = resizeConstraints.max;

	  if (minResizeConstraints) {
	    snapConstraints = {};

	    if (isHorizontal(axis)) {

	      if (isWest(direction)) {
	        snapConstraints.max = minResizeConstraints.left;
	      } else {
	        snapConstraints.min = minResizeConstraints.right;
	      }

	    } else {

	      if (isNorth(direction)) {
	        snapConstraints.max = minResizeConstraints.top;
	      } else {
	        snapConstraints.min = minResizeConstraints.bottom;
	      }

	    }
	  }

	  if (maxResizeConstraints) {
	    snapConstraints = {};

	    if (isHorizontal(axis)) {

	      if (isWest(direction)) {
	        snapConstraints.min = maxResizeConstraints.left;
	      } else {
	        snapConstraints.max = maxResizeConstraints.right;
	      }

	    } else {

	      if (isNorth(direction)) {
	        snapConstraints.min = maxResizeConstraints.top;
	      } else {
	        snapConstraints.max = maxResizeConstraints.bottom;
	      }

	    }
	  }

	  return snapConstraints;
	}

	/**
	 * Get snap offset assuming that event is at center of shape.
	 *
	 * @param {Object} event
	 * @param {string} axis
	 *
	 * @returns {number}
	 */
	function getSnapOffset(event, axis) {
	  var context = event.context,
	      shape = event.shape,
	      gridSnappingContext = context.gridSnappingContext || {},
	      snapLocation = gridSnappingContext.snapLocation;

	  if (!shape || !snapLocation) {
	    return 0;
	  }

	  if (axis === 'x') {
	    if (/left/.test(snapLocation)) {
	      return -shape.width / 2;
	    }

	    if (/right/.test(snapLocation)) {
	      return shape.width / 2;
	    }
	  } else {
	    if (/top/.test(snapLocation)) {
	      return -shape.height / 2;
	    }

	    if (/bottom/.test(snapLocation)) {
	      return shape.height / 2;
	    }
	  }

	  return 0;
	}

	function isHorizontal(axis) {
	  return axis === 'x';
	}

	function isNorth(direction) {
	  return direction.charAt(0) === 'n';
	}

	function isWest(direction) {
	  return direction.charAt(1) === 'w';
	}

	var HIGH_PRIORITY$3 = 2000;


	function PasteBehavior(eventBus, gridSnapping) {
	  CommandInterceptor.call(this, eventBus);

	  this.preExecute('elements.paste', HIGH_PRIORITY$3, function(event) {
	    var context = event.context,
	        position = context.position,
	        tree = context.tree;

	    if (!tree[0]) {
	      return;
	    }

	    var mid = getMid(getBBox(tree[0]));

	    // snap paste position but maintain each element's grid offset
	    position.x = gridSnapping.snapValue(position.x) + (mid.x % SPACING);
	    position.y = gridSnapping.snapValue(position.y) + (mid.y % SPACING);
	  });
	}

	PasteBehavior.$inject = [
	  'eventBus',
	  'gridSnapping'
	];

	inherits_browser(PasteBehavior, CommandInterceptor);

	/**
	 * Integrates resizing with grid snapping.
	 */
	function ResizeBehavior(eventBus, gridSnapping) {
	  CommandInterceptor.call(this, eventBus);

	  this._gridSnapping = gridSnapping;

	  var self = this;

	  this.preExecute('shape.resize', function(event) {
	    var context = event.context,
	        hints = context.hints || {},
	        autoResize = hints.autoResize;

	    if (!autoResize) {
	      return;
	    }

	    var shape = context.shape,
	        newBounds = context.newBounds;

	    if (isString(autoResize)) {
	      context.newBounds = self.snapComplex(newBounds, autoResize);
	    } else {
	      context.newBounds = self.snapSimple(shape, newBounds);
	    }
	  });
	}

	ResizeBehavior.$inject = [
	  'eventBus',
	  'gridSnapping',
	  'modeling'
	];

	inherits_browser(ResizeBehavior, CommandInterceptor);

	/**
	 * Snap width and height in relation to center.
	 *
	 * @param {djs.model.shape} shape
	 * @param {Bounds} newBounds
	 *
	 * @returns {Bounds} Snapped bounds.
	 */
	ResizeBehavior.prototype.snapSimple = function(shape, newBounds) {
	  var gridSnapping = this._gridSnapping;

	  newBounds.width = gridSnapping.snapValue(newBounds.width, {
	    min: newBounds.width
	  });

	  newBounds.height = gridSnapping.snapValue(newBounds.height, {
	    min: newBounds.height
	  });

	  newBounds.x = shape.x + (shape.width / 2) - (newBounds.width / 2);
	  newBounds.y = shape.y + (shape.height / 2) - (newBounds.height / 2);

	  return newBounds;
	};

	/**
	 * Snap x, y, width and height according to given directions.
	 *
	 * @param {Bounds} newBounds
	 * @param {string} directions - Directions as {n|w|s|e}.
	 *
	 * @returns {Bounds} Snapped bounds.
	 */
	ResizeBehavior.prototype.snapComplex = function(newBounds, directions) {
	  if (/w|e/.test(directions)) {
	    newBounds = this.snapHorizontally(newBounds, directions);
	  }

	  if (/n|s/.test(directions)) {
	    newBounds = this.snapVertically(newBounds, directions);
	  }

	  return newBounds;
	};

	/**
	 * Snap in one or both directions horizontally.
	 *
	 * @param {Bounds} newBounds
	 * @param {string} directions - Directions as {n|w|s|e}.
	 *
	 * @returns {Bounds} Snapped bounds.
	 */
	ResizeBehavior.prototype.snapHorizontally = function(newBounds, directions) {
	  var gridSnapping = this._gridSnapping,
	      west = /w/.test(directions),
	      east = /e/.test(directions);

	  var snappedNewBounds = {};

	  snappedNewBounds.width = gridSnapping.snapValue(newBounds.width, {
	    min: newBounds.width
	  });

	  if (east) {

	    // handle <we>
	    if (west) {
	      snappedNewBounds.x = gridSnapping.snapValue(newBounds.x, {
	        max: newBounds.x
	      });

	      snappedNewBounds.width += gridSnapping.snapValue(newBounds.x - snappedNewBounds.x, {
	        min: newBounds.x - snappedNewBounds.x
	      });
	    }

	    // handle <e>
	    else {
	      newBounds.x = newBounds.x + newBounds.width - snappedNewBounds.width;
	    }
	  }

	  // assign snapped x and width
	  assign(newBounds, snappedNewBounds);

	  return newBounds;
	};

	/**
	 * Snap in one or both directions vertically.
	 *
	 * @param {Bounds} newBounds
	 * @param {string} directions - Directions as {n|w|s|e}.
	 *
	 * @returns {Bounds} Snapped bounds.
	 */
	ResizeBehavior.prototype.snapVertically = function(newBounds, directions) {
	  var gridSnapping = this._gridSnapping,
	      north = /n/.test(directions),
	      south = /s/.test(directions);

	  var snappedNewBounds = {};

	  snappedNewBounds.height = gridSnapping.snapValue(newBounds.height, {
	    min: newBounds.height
	  });

	  if (north) {

	    // handle <ns>
	    if (south) {
	      snappedNewBounds.y = gridSnapping.snapValue(newBounds.y, {
	        max: newBounds.y
	      });

	      snappedNewBounds.height += gridSnapping.snapValue(newBounds.y - snappedNewBounds.y, {
	        min: newBounds.y - snappedNewBounds.y
	      });
	    }

	    // handle <n>
	    else {
	      newBounds.y = newBounds.y + newBounds.height - snappedNewBounds.height;
	    }
	  }

	  // assign snapped y and height
	  assign(newBounds, snappedNewBounds);

	  return newBounds;
	};

	var HIGH_PRIORITY$4 = 2000;

	/**
	 * Integrates space tool with grid snapping.
	 */
	function SpaceToolBehavior(eventBus, gridSnapping) {
	  eventBus.on([
	    'spaceTool.move',
	    'spaceTool.end'
	  ], HIGH_PRIORITY$4, function(event) {
	    var context = event.context;

	    if (!context.initialized) {
	      return;
	    }

	    var axis = context.axis;

	    if (axis === 'x') {

	      // snap delta x to multiple of 10
	      event.dx = gridSnapping.snapValue(event.dx);
	    } else {

	      // snap delta y to multiple of 10
	      event.dy = gridSnapping.snapValue(event.dy);
	    }
	  });
	}

	SpaceToolBehavior.$inject = [
	  'eventBus',
	  'gridSnapping'
	];

	var GridSnappingBehaviorModule = {
	  __init__: [
	    'gridSnappingPasteBehavior',
	    'gridSnappingResizeBehavior',
	    'gridSnappingSpaceToolBehavior'
	  ],
	  gridSnappingPasteBehavior: [ 'type', PasteBehavior ],
	  gridSnappingResizeBehavior: [ 'type', ResizeBehavior ],
	  gridSnappingSpaceToolBehavior: [ 'type', SpaceToolBehavior ]
	};

	var GridSnappingModule = {
	  __depends__: [ GridSnappingBehaviorModule ],
	  __init__: [ 'gridSnapping' ],
	  gridSnapping: [ 'type', GridSnapping ]
	};

	function AutoPlaceBehavior(eventBus, gridSnapping) {
	  eventBus.on('autoPlace', function(context) {
	    var source = context.source,
	        sourceMid = getMid(source),
	        shape = context.shape;

	    var position = getNewShapePosition(source, shape);

	    [ 'x', 'y' ].forEach(function(axis) {
	      var options = {};

	      // do not snap if x/y equal
	      if (position[ axis ] === sourceMid[ axis ]) {
	        return;
	      }

	      if (position[ axis ] > sourceMid[ axis ]) {
	        options.min = position[ axis ];
	      } else {
	        options.max = position[ axis ];
	      }

	      if (is$1(shape, 'bpmn:TextAnnotation')) {

	        if (isHorizontal$1(axis)) {
	          options.offset = -shape.width / 2;
	        } else {
	          options.offset = -shape.height / 2;
	        }

	      }

	      position[ axis ] = gridSnapping.snapValue(position[ axis ], options);

	    });

	    // must be returned to be considered by auto place
	    return position;
	  });
	}

	AutoPlaceBehavior.$inject = [
	  'eventBus',
	  'gridSnapping'
	];

	// helpers //////////

	function isHorizontal$1(axis) {
	  return axis === 'x';
	}

	var HIGHER_PRIORITY = 1750;


	function CreateParticipantBehavior(canvas, eventBus, gridSnapping) {
	  eventBus.on([
	    'create.start',
	    'shape.move.start'
	  ], HIGHER_PRIORITY, function(event) {
	    var context = event.context,
	        shape = context.shape,
	        rootElement = canvas.getRootElement();

	    if (!is$1(shape, 'bpmn:Participant') ||
	      !is$1(rootElement, 'bpmn:Process') ||
	      !rootElement.children.length) {
	      return;
	    }

	    var createConstraints = context.createConstraints;

	    if (!createConstraints) {
	      return;
	    }

	    shape.width = gridSnapping.snapValue(shape.width, { min: shape.width });
	    shape.height = gridSnapping.snapValue(shape.height, { min: shape.height });
	  });
	}

	CreateParticipantBehavior.$inject = [
	  'canvas',
	  'eventBus',
	  'gridSnapping'
	];

	var HIGH_PRIORITY$5 = 3000;


	/**
	 * Snaps connections with Manhattan layout.
	 */
	function LayoutConnectionBehavior(eventBus, gridSnapping, modeling) {
	  CommandInterceptor.call(this, eventBus);

	  this._gridSnapping = gridSnapping;

	  var self = this;

	  this.postExecuted([
	    'connection.create',
	    'connection.layout'
	  ], HIGH_PRIORITY$5, function(event) {
	    var context = event.context,
	        connection = context.connection,
	        hints = context.hints || {},
	        waypoints = connection.waypoints;

	    if (hints.connectionStart || hints.connectionEnd) {
	      return;
	    }

	    if (!hasMiddleSegments(waypoints)) {
	      return;
	    }

	    modeling.updateWaypoints(connection, self.snapMiddleSegments(waypoints));
	  });
	}

	LayoutConnectionBehavior.$inject = [
	  'eventBus',
	  'gridSnapping',
	  'modeling'
	];

	inherits_browser(LayoutConnectionBehavior, CommandInterceptor);

	/**
	 * Snap middle segments of a given connection.
	 *
	 * @param {Array<Point>} waypoints
	 *
	 * @returns {Array<Point>}
	 */
	LayoutConnectionBehavior.prototype.snapMiddleSegments = function(waypoints) {
	  var gridSnapping = this._gridSnapping,
	      snapped;

	  waypoints = waypoints.slice();

	  for (var i = 1; i < waypoints.length - 2; i++) {

	    snapped = snapSegment(gridSnapping, waypoints[i], waypoints[i + 1]);

	    waypoints[i] = snapped[0];
	    waypoints[i + 1] = snapped[1];
	  }

	  return waypoints;
	};


	// helpers //////////

	/**
	 * Check wether a connection has a middle segments.
	 *
	 * @param {Array} waypoints
	 *
	 * @returns {boolean}
	 */
	function hasMiddleSegments(waypoints) {
	  return waypoints.length > 3;
	}

	/**
	 * Check wether an alignment is horizontal.
	 *
	 * @param {string} aligned
	 *
	 * @returns {boolean}
	 */
	function horizontallyAligned(aligned) {
	  return aligned === 'h';
	}

	/**
	 * Check wether an alignment is vertical.
	 *
	 * @param {string} aligned
	 *
	 * @returns {boolean}
	 */
	function verticallyAligned(aligned) {
	  return aligned === 'v';
	}

	/**
	 * Get middle segments from a given connection.
	 *
	 * @param {Array} waypoints
	 *
	 * @returns {Array}
	 */
	function snapSegment(gridSnapping, segmentStart, segmentEnd) {

	  var aligned = pointsAligned(segmentStart, segmentEnd);

	  var snapped = {};

	  if (horizontallyAligned(aligned)) {

	    // snap horizontally
	    snapped.y = gridSnapping.snapValue(segmentStart.y);
	  }

	  if (verticallyAligned(aligned)) {

	    // snap vertically
	    snapped.x = gridSnapping.snapValue(segmentStart.x);
	  }

	  if ('x' in snapped || 'y' in snapped) {
	    segmentStart = assign({}, segmentStart, snapped);
	    segmentEnd = assign({}, segmentEnd, snapped);
	  }

	  return [ segmentStart, segmentEnd ];
	}

	var GridSnappingBehaviorModule$1 = {
	  __init__: [
	    'gridSnappingAutoPlaceBehavior',
	    'gridSnappingCreateParticipantBehavior',
	    'gridSnappingLayoutConnectionBehavior',
	  ],
	  gridSnappingAutoPlaceBehavior: [ 'type', AutoPlaceBehavior ],
	  gridSnappingCreateParticipantBehavior: [ 'type', CreateParticipantBehavior ],
	  gridSnappingLayoutConnectionBehavior: [ 'type', LayoutConnectionBehavior ]
	};

	var GridSnappingModule$1 = {
	  __depends__: [
	    GridSnappingModule,
	    GridSnappingBehaviorModule$1
	  ],
	  __init__: [ 'bpmnGridSnapping' ],
	  bpmnGridSnapping: [ 'type', BpmnGridSnapping ]
	};

	/**
	 * BPMN 2.0 specific keyboard bindings.
	 *
	 * @param {Injector} injector
	 */
	function BpmnKeyboardBindings(injector) {
	  injector.invoke(KeyboardBindings, this);
	}

	inherits_browser(BpmnKeyboardBindings, KeyboardBindings);

	BpmnKeyboardBindings.$inject = [
	  'injector'
	];


	/**
	 * Register available keyboard bindings.
	 *
	 * @param {Keyboard} keyboard
	 * @param {EditorActions} editorActions
	 */
	BpmnKeyboardBindings.prototype.registerBindings = function(keyboard, editorActions) {

	  // inherit default bindings
	  KeyboardBindings.prototype.registerBindings.call(this, keyboard, editorActions);

	  /**
	   * Add keyboard binding if respective editor action
	   * is registered.
	   *
	   * @param {String} action name
	   * @param {Function} fn that implements the key binding
	   */
	  function addListener(action, fn) {

	    if (editorActions.isRegistered(action)) {
	      keyboard.addListener(fn);
	    }
	  }

	  // select all elements
	  // CTRL + A
	  addListener('selectElements', function(context) {

	    var event = context.keyEvent;

	    if (keyboard.isKey(['a', 'A'], event) && keyboard.isCmd(event)) {
	      editorActions.trigger('selectElements');

	      return true;
	    }
	  });

	  // search labels
	  // CTRL + F
	  addListener('find', function(context) {

	    var event = context.keyEvent;

	    if (keyboard.isKey(['f', 'F'], event) && keyboard.isCmd(event)) {
	      editorActions.trigger('find');

	      return true;
	    }
	  });

	  // activate space tool
	  // S
	  addListener('spaceTool', function(context) {

	    var event = context.keyEvent;

	    if (keyboard.hasModifier(event)) {
	      return;
	    }

	    if (keyboard.isKey(['s', 'S'], event)) {
	      editorActions.trigger('spaceTool');

	      return true;
	    }
	  });

	  // activate lasso tool
	  // L
	  addListener('lassoTool', function(context) {

	    var event = context.keyEvent;

	    if (keyboard.hasModifier(event)) {
	      return;
	    }

	    if (keyboard.isKey(['l', 'L'], event)) {
	      editorActions.trigger('lassoTool');

	      return true;
	    }
	  });

	  // activate hand tool
	  // H
	  addListener('handTool', function(context) {

	    var event = context.keyEvent;

	    if (keyboard.hasModifier(event)) {
	      return;
	    }

	    if (keyboard.isKey(['h', 'H'], event)) {
	      editorActions.trigger('handTool');

	      return true;
	    }
	  });

	  // activate global connect tool
	  // C
	  addListener('globalConnectTool', function(context) {

	    var event = context.keyEvent;

	    if (keyboard.hasModifier(event)) {
	      return;
	    }

	    if (keyboard.isKey(['c', 'C'], event)) {
	      editorActions.trigger('globalConnectTool');

	      return true;
	    }
	  });

	  // activate direct editing
	  // E
	  addListener('directEditing', function(context) {

	    var event = context.keyEvent;

	    if (keyboard.hasModifier(event)) {
	      return;
	    }

	    if (keyboard.isKey(['e', 'E'], event)) {
	      editorActions.trigger('directEditing');

	      return true;
	    }
	  });

	};

	var KeyboardModule$1 = {
	  __depends__: [
	    KeyboardModule
	  ],
	  __init__: [ 'keyboardBindings' ],
	  keyboardBindings: [ 'type', BpmnKeyboardBindings ]
	};

	var DEFAULT_CONFIG$1 = {
	  moveSpeed: 1,
	  moveSpeedAccelerated: 10
	};

	var HIGHER_PRIORITY$1 = 1500;

	var LEFT = 'left';
	var UP = 'up';
	var RIGHT = 'right';
	var DOWN = 'down';

	var KEY_TO_DIRECTION = {
	  ArrowLeft: LEFT,
	  Left: LEFT,
	  ArrowUp: UP,
	  Up: UP,
	  ArrowRight: RIGHT,
	  Right: RIGHT,
	  ArrowDown: DOWN,
	  Down: DOWN
	};

	var DIRECTIONS_DELTA = {
	  left: function(speed) {
	    return {
	      x: -speed,
	      y: 0
	    };
	  },
	  up: function(speed) {
	    return {
	      x: 0,
	      y: -speed
	    };
	  },
	  right: function(speed) {
	    return {
	      x: speed,
	      y: 0
	    };
	  },
	  down: function(speed) {
	    return {
	      x: 0,
	      y: speed
	    };
	  }
	};


	/**
	 * Enables to move selection with keyboard arrows.
	 * Use with Shift for modified speed (default=1, with Shift=10).
	 * Pressed Cmd/Ctrl turns the feature off.
	 *
	 * @param {Object} config
	 * @param {Number} [config.moveSpeed=1]
	 * @param {Number} [config.moveSpeedAccelerated=10]
	 * @param {Keyboard} keyboard
	 * @param {Modeling} modeling
	 * @param {Selection} selection
	 */
	function KeyboardMoveSelection(
	    config, keyboard,
	    modeling, selection
	) {

	  var self = this;

	  this._config = assign({}, DEFAULT_CONFIG$1, config || {});

	  keyboard.addListener(HIGHER_PRIORITY$1, function(event) {

	    var keyEvent = event.keyEvent;

	    var direction = KEY_TO_DIRECTION[keyEvent.key];

	    if (!direction) {
	      return;
	    }

	    if (keyboard.isCmd(keyEvent)) {
	      return;
	    }

	    var accelerated = keyboard.isShift(keyEvent);

	    self.moveSelection(direction, accelerated);

	    return true;
	  });


	  /**
	   * Move selected elements in the given direction,
	   * optionally specifying accelerated movement.
	   *
	   * @param {String} direction
	   * @param {Boolean} [accelerated=false]
	   */
	  this.moveSelection = function(direction, accelerated) {

	    var selectedElements = selection.get();

	    if (!selectedElements.length) {
	      return;
	    }

	    var speed = this._config[
	      accelerated ?
	        'moveSpeedAccelerated' :
	        'moveSpeed'
	    ];

	    var delta = DIRECTIONS_DELTA[direction](speed);

	    modeling.moveElements(selectedElements, delta);
	  };

	}

	KeyboardMoveSelection.$inject = [
	  'config.keyboardMoveSelection',
	  'keyboard',
	  'modeling',
	  'selection'
	];

	var KeyboardMoveSelectionModule = {
	  __depends__: [
	    KeyboardModule,
	    SelectionModule
	  ],
	  __init__: [
	    'keyboardMoveSelection'
	  ],
	  keyboardMoveSelection: [ 'type', KeyboardMoveSelection ]
	};

	/**
	 * Adds change support to the diagram, including
	 *
	 * <ul>
	 *   <li>redrawing shapes and connections on change</li>
	 * </ul>
	 *
	 * @param {EventBus} eventBus
	 * @param {Canvas} canvas
	 * @param {ElementRegistry} elementRegistry
	 * @param {GraphicsFactory} graphicsFactory
	 */
	function ChangeSupport(
	    eventBus, canvas, elementRegistry,
	    graphicsFactory) {


	  // redraw shapes / connections on change

	  eventBus.on('element.changed', function(event) {

	    var element = event.element;

	    // element might have been deleted and replaced by new element with same ID
	    // thus check for parent of element except for root element
	    if (element.parent || element === canvas.getRootElement()) {
	      event.gfx = elementRegistry.getGraphics(element);
	    }

	    // shape + gfx may have been deleted
	    if (!event.gfx) {
	      return;
	    }

	    eventBus.fire(getType(element) + '.changed', event);
	  });

	  eventBus.on('elements.changed', function(event) {

	    var elements = event.elements;

	    elements.forEach(function(e) {
	      eventBus.fire('element.changed', { element: e });
	    });

	    graphicsFactory.updateContainments(elements);
	  });

	  eventBus.on('shape.changed', function(event) {
	    graphicsFactory.update('shape', event.element, event.gfx);
	  });

	  eventBus.on('connection.changed', function(event) {
	    graphicsFactory.update('connection', event.element, event.gfx);
	  });
	}

	ChangeSupport.$inject = [
	  'eventBus',
	  'canvas',
	  'elementRegistry',
	  'graphicsFactory'
	];

	var ChangeSupportModule = {
	  __init__: [ 'changeSupport'],
	  changeSupport: [ 'type', ChangeSupport ]
	};

	var MARKER_TYPES = [
	  'marker-start',
	  'marker-mid',
	  'marker-end'
	];

	var NODES_CAN_HAVE_MARKER = [
	  'circle',
	  'ellipse',
	  'line',
	  'path',
	  'polygon',
	  'polyline',
	  'rect'
	];


	/**
	 * Adds support for previews of moving/resizing elements.
	 */
	function PreviewSupport(elementRegistry, eventBus, canvas, styles) {
	  this._elementRegistry = elementRegistry;
	  this._canvas = canvas;
	  this._styles = styles;

	  this._clonedMarkers = {};

	  var self = this;

	  eventBus.on('drag.cleanup', function() {
	    forEach(self._clonedMarkers, function(clonedMarker) {
	      remove$1(clonedMarker);
	    });

	    self._clonedMarkers = {};
	  });
	}

	PreviewSupport.$inject = [
	  'elementRegistry',
	  'eventBus',
	  'canvas',
	  'styles'
	];


	/**
	 * Returns graphics of an element.
	 *
	 * @param {djs.model.Base} element
	 *
	 * @return {SVGElement}
	 */
	PreviewSupport.prototype.getGfx = function(element) {
	  return this._elementRegistry.getGraphics(element);
	};

	/**
	 * Adds a move preview of a given shape to a given svg group.
	 *
	 * @param {djs.model.Base} element
	 * @param {SVGElement} group
	 *
	 * @return {SVGElement} dragger
	 */
	PreviewSupport.prototype.addDragger = function(shape, group) {
	  var gfx = this.getGfx(shape);

	  var dragger = clone(gfx);
	  var bbox = gfx.getBoundingClientRect();

	  this._cloneMarkers(getVisual(dragger));

	  attr$1(dragger, this._styles.cls('djs-dragger', [], {
	    x: bbox.top,
	    y: bbox.left
	  }));

	  append(group, dragger);

	  return dragger;
	};

	/**
	 * Adds a resize preview of a given shape to a given svg group.
	 *
	 * @param {djs.model.Base} element
	 * @param {SVGElement} group
	 *
	 * @return {SVGElement} frame
	 */
	PreviewSupport.prototype.addFrame = function(shape, group) {

	  var frame = create('rect', {
	    class: 'djs-resize-overlay',
	    width:  shape.width,
	    height: shape.height,
	    x: shape.x,
	    y: shape.y
	  });

	  append(group, frame);

	  return frame;
	};

	/**
	 * Clone all markers referenced by a node and its child nodes.
	 *
	 * @param {SVGElement} gfx
	 */
	PreviewSupport.prototype._cloneMarkers = function(gfx) {
	  var self = this;

	  if (gfx.childNodes) {

	    // TODO: use forEach once we drop PhantomJS
	    for (var i = 0; i < gfx.childNodes.length; i++) {

	      // recursively clone markers of child nodes
	      self._cloneMarkers(gfx.childNodes[ i ]);
	    }
	  }

	  if (!canHaveMarker(gfx)) {
	    return;
	  }

	  MARKER_TYPES.forEach(function(markerType) {
	    if (attr$1(gfx, markerType)) {
	      var marker = getMarker(gfx, markerType, self._canvas.getContainer());

	      self._cloneMarker(gfx, marker, markerType);
	    }
	  });
	};

	/**
	 * Clone marker referenced by an element.
	 *
	 * @param {SVGElement} gfx
	 * @param {SVGElement} marker
	 * @param {string} markerType
	 */
	PreviewSupport.prototype._cloneMarker = function(gfx, marker, markerType) {
	  var markerId = marker.id;

	  var clonedMarker = this._clonedMarkers[ markerId ];

	  if (!clonedMarker) {
	    clonedMarker = clone(marker);

	    var clonedMarkerId = markerId + '-clone';

	    clonedMarker.id = clonedMarkerId;

	    classes$1(clonedMarker)
	      .add('djs-dragger')
	      .add('djs-dragger-marker');

	    this._clonedMarkers[ markerId ] = clonedMarker;

	    var defs = query('defs', this._canvas._svg);

	    if (!defs) {
	      defs = create('defs');

	      append(this._canvas._svg, defs);
	    }

	    append(defs, clonedMarker);
	  }

	  var reference = idToReference(this._clonedMarkers[ markerId ].id);

	  attr$1(gfx, markerType, reference);
	};

	// helpers //////////

	/**
	 * Get marker of given type referenced by node.
	 *
	 * @param {Node} node
	 * @param {string} markerType
	 * @param {Node} [parentNode]
	 *
	 * @param {Node}
	 */
	function getMarker(node, markerType, parentNode) {
	  var id = referenceToId(attr$1(node, markerType));

	  return query('marker#' + id, parentNode || document);
	}

	/**
	 * Get ID of fragment within current document from its functional IRI reference.
	 * References may use single or double quotes.
	 *
	 * @param {string} reference
	 *
	 * @returns {string}
	 */
	function referenceToId(reference) {
	  return reference.match(/url\(['"]?#([^'"]*)['"]?\)/)[1];
	}

	/**
	 * Get functional IRI reference for given ID of fragment within current document.
	 *
	 * @param {string} id
	 *
	 * @returns {string}
	 */
	function idToReference(id) {
	  return 'url(#' + id + ')';
	}

	/**
	 * Check wether node type can have marker attributes.
	 *
	 * @param {Node} node
	 *
	 * @returns {boolean}
	 */
	function canHaveMarker(node) {
	  return NODES_CAN_HAVE_MARKER.indexOf(node.nodeName) !== -1;
	}

	var PreviewSupportModule = {
	  __init__: [ 'previewSupport' ],
	  previewSupport: [ 'type', PreviewSupport ]
	};

	var DEFAULT_MIN_WIDTH = 10;


	/**
	 * A component that provides resizing of shapes on the canvas.
	 *
	 * The following components are part of shape resize:
	 *
	 *  * adding resize handles,
	 *  * creating a visual during resize
	 *  * checking resize rules
	 *  * committing a change once finished
	 *
	 *
	 * ## Customizing
	 *
	 * It's possible to customize the resizing behaviour by intercepting 'resize.start'
	 * and providing the following parameters through the 'context':
	 *
	 *   * minDimensions ({ width, height }): minimum shape dimensions
	 *
	 *   * childrenBoxPadding ({ left, top, bottom, right } || number):
	 *     gap between the minimum bounding box and the container
	 *
	 * f.ex:
	 *
	 * ```javascript
	 * eventBus.on('resize.start', 1500, function(event) {
	 *   var context = event.context,
	 *
	 *  context.minDimensions = { width: 140, height: 120 };
	 *
	 *  // Passing general padding
	 *  context.childrenBoxPadding = 30;
	 *
	 *  // Passing padding to a specific side
	 *  context.childrenBoxPadding.left = 20;
	 * });
	 * ```
	 */
	function Resize(eventBus, rules, modeling, dragging) {

	  this._dragging = dragging;
	  this._rules = rules;

	  var self = this;


	  /**
	   * Handle resize move by specified delta.
	   *
	   * @param {Object} context
	   * @param {Point} delta
	   */
	  function handleMove(context, delta) {

	    var shape = context.shape,
	        direction = context.direction,
	        resizeConstraints = context.resizeConstraints,
	        newBounds;

	    context.delta = delta;

	    newBounds = resizeBounds(shape, direction, delta);

	    // ensure constraints during resize
	    context.newBounds = ensureConstraints$1(newBounds, resizeConstraints);

	    // update + cache executable state
	    context.canExecute = self.canResize(context);
	  }

	  /**
	   * Handle resize start.
	   *
	   * @param  {Object} context
	   */
	  function handleStart(context) {

	    var resizeConstraints = context.resizeConstraints,
	        // evaluate minBounds for backwards compatibility
	        minBounds = context.minBounds;

	    if (resizeConstraints !== undefined) {
	      return;
	    }

	    if (minBounds === undefined) {
	      minBounds = self.computeMinResizeBox(context);
	    }

	    context.resizeConstraints = {
	      min: asTRBL(minBounds)
	    };
	  }

	  /**
	   * Handle resize end.
	   *
	   * @param  {Object} context
	   */
	  function handleEnd(context) {
	    var shape = context.shape,
	        canExecute = context.canExecute,
	        newBounds = context.newBounds;

	    if (canExecute) {

	      // ensure we have actual pixel values for new bounds
	      // (important when zoom level was > 1 during move)
	      newBounds = roundBounds(newBounds);

	      if (!boundsChanged$1(shape, newBounds)) {

	        // no resize necessary
	        return;
	      }

	      // perform the actual resize
	      modeling.resizeShape(shape, newBounds);
	    }
	  }


	  eventBus.on('resize.start', function(event) {
	    handleStart(event.context);
	  });

	  eventBus.on('resize.move', function(event) {
	    var delta = {
	      x: event.dx,
	      y: event.dy
	    };

	    handleMove(event.context, delta);
	  });

	  eventBus.on('resize.end', function(event) {
	    handleEnd(event.context);
	  });

	}


	Resize.prototype.canResize = function(context) {
	  var rules = this._rules;

	  var ctx = pick(context, [ 'newBounds', 'shape', 'delta', 'direction' ]);

	  return rules.allowed('shape.resize', ctx);
	};

	/**
	 * Activate a resize operation.
	 *
	 * You may specify additional contextual information and must specify a
	 * resize direction during activation of the resize event.
	 *
	 * @param {MouseEvent} event
	 * @param {djs.model.Shape} shape
	 * @param {Object|String} contextOrDirection
	 */
	Resize.prototype.activate = function(event, shape, contextOrDirection) {
	  var dragging = this._dragging,
	      context,
	      direction;

	  if (typeof contextOrDirection === 'string') {
	    contextOrDirection = {
	      direction: contextOrDirection
	    };
	  }

	  context = assign({ shape: shape }, contextOrDirection);

	  direction = context.direction;

	  if (!direction) {
	    throw new Error('must provide a direction (nw|se|ne|sw)');
	  }

	  var referencePoint = {
	    x: /w/.test(direction) ? shape.x : shape.x + shape.width,
	    y: /n/.test(direction) ? shape.y : shape.y + shape.height
	  };

	  dragging.init(event, referencePoint, 'resize', {
	    autoActivate: true,
	    cursor: 'resize-' + (/nw|se/.test(direction) ? 'nwse' : 'nesw'),
	    data: {
	      shape: shape,
	      context: context
	    }
	  });
	};

	Resize.prototype.computeMinResizeBox = function(context) {
	  var shape = context.shape,
	      direction = context.direction,
	      minDimensions,
	      childrenBounds;

	  minDimensions = context.minDimensions || {
	    width: DEFAULT_MIN_WIDTH,
	    height: DEFAULT_MIN_WIDTH
	  };

	  // get children bounds
	  childrenBounds = computeChildrenBBox(shape, context.childrenBoxPadding);

	  // get correct minimum bounds from given resize direction
	  // basically ensures that the minBounds is max(childrenBounds, minDimensions)
	  return getMinResizeBounds(direction, shape, minDimensions, childrenBounds);
	};


	Resize.$inject = [
	  'eventBus',
	  'rules',
	  'modeling',
	  'dragging'
	];

	// helpers //////////

	function boundsChanged$1(shape, newBounds) {
	  return shape.x !== newBounds.x ||
	    shape.y !== newBounds.y ||
	    shape.width !== newBounds.width ||
	    shape.height !== newBounds.height;
	}

	var MARKER_RESIZING = 'djs-resizing',
	    MARKER_RESIZE_NOT_OK = 'resize-not-ok';

	var LOW_PRIORITY$7 = 500;


	/**
	 * Provides previews for resizing shapes when resizing.
	 *
	 * @param {EventBus} eventBus
	 * @param {Canvas} canvas
	 * @param {PreviewSupport} previewSupport
	 */
	function ResizePreview(eventBus, canvas, previewSupport) {

	  /**
	   * Update resizer frame.
	   *
	   * @param {Object} context
	   */
	  function updateFrame(context) {

	    var shape = context.shape,
	        bounds = context.newBounds,
	        frame = context.frame;

	    if (!frame) {
	      frame = context.frame = previewSupport.addFrame(shape, canvas.getDefaultLayer());

	      canvas.addMarker(shape, MARKER_RESIZING);
	    }

	    if (bounds.width > 5) {
	      attr$1(frame, { x: bounds.x, width: bounds.width });
	    }

	    if (bounds.height > 5) {
	      attr$1(frame, { y: bounds.y, height: bounds.height });
	    }

	    if (context.canExecute) {
	      classes$1(frame).remove(MARKER_RESIZE_NOT_OK);
	    } else {
	      classes$1(frame).add(MARKER_RESIZE_NOT_OK);
	    }
	  }

	  /**
	   * Remove resizer frame.
	   *
	   * @param {Object} context
	   */
	  function removeFrame(context) {
	    var shape = context.shape,
	        frame = context.frame;

	    if (frame) {
	      remove$1(context.frame);
	    }

	    canvas.removeMarker(shape, MARKER_RESIZING);
	  }

	  // add and update previews
	  eventBus.on('resize.move', LOW_PRIORITY$7, function(event) {
	    updateFrame(event.context);
	  });

	  // remove previews
	  eventBus.on('resize.cleanup', function(event) {
	    removeFrame(event.context);
	  });

	}

	ResizePreview.$inject = [
	  'eventBus',
	  'canvas',
	  'previewSupport'
	];

	var HANDLE_OFFSET = -2,
	    HANDLE_SIZE = 5,
	    HANDLE_HIT_SIZE = 20;

	var CLS_RESIZER = 'djs-resizer';


	/**
	 * This component is responsible for adding resize handles.
	 *
	 * @param {EventBus} eventBus
	 * @param {Canvas} canvas
	 * @param {Selection} selection
	 * @param {Resize} resize
	 */
	function ResizeHandles(eventBus, canvas, selection, resize) {

	  this._resize = resize;
	  this._canvas = canvas;

	  var self = this;

	  eventBus.on('selection.changed', function(e) {
	    var newSelection = e.newSelection;

	    // remove old selection markers
	    self.removeResizers();

	    // add new selection markers ONLY if single selection
	    if (newSelection.length === 1) {
	      forEach(newSelection, bind(self.addResizer, self));
	    }
	  });

	  eventBus.on('shape.changed', function(e) {
	    var shape = e.element;

	    if (selection.isSelected(shape)) {
	      self.removeResizers();

	      self.addResizer(shape);
	    }
	  });
	}


	ResizeHandles.prototype.makeDraggable = function(element, gfx, direction) {
	  var resize = this._resize;

	  function startResize(event) {
	    // only trigger on left mouse button
	    if (isPrimaryButton(event)) {
	      resize.activate(event, element, direction);
	    }
	  }

	  componentEvent.bind(gfx, 'mousedown', startResize);
	  componentEvent.bind(gfx, 'touchstart', startResize);
	};


	ResizeHandles.prototype._createResizer = function(element, x, y, rotation, direction) {
	  var resizersParent = this._getResizersParent();

	  var group = create('g');
	  classes$1(group).add(CLS_RESIZER);
	  classes$1(group).add(CLS_RESIZER + '-' + element.id);
	  classes$1(group).add(CLS_RESIZER + '-' + direction);

	  append(resizersParent, group);

	  var origin = -HANDLE_SIZE + HANDLE_OFFSET;

	  // Create four drag indicators on the outline
	  var visual = create('rect');
	  attr$1(visual, {
	    x: origin,
	    y: origin,
	    width: HANDLE_SIZE,
	    height: HANDLE_SIZE
	  });
	  classes$1(visual).add(CLS_RESIZER + '-visual');

	  append(group, visual);

	  var hit = create('rect');
	  attr$1(hit, {
	    x: origin,
	    y: origin,
	    width: HANDLE_HIT_SIZE,
	    height: HANDLE_HIT_SIZE
	  });
	  classes$1(hit).add(CLS_RESIZER + '-hit');

	  append(group, hit);

	  transform$1(group, x, y, rotation);

	  return group;
	};

	ResizeHandles.prototype.createResizer = function(element, direction) {
	  var resizer;

	  var trbl = asTRBL(element);

	  if (direction === 'nw') {
	    resizer = this._createResizer(element, trbl.left, trbl.top, 0, direction);
	  } else if (direction === 'ne') {
	    resizer = this._createResizer(element, trbl.right, trbl.top, 90, direction);
	  } else if (direction === 'se') {
	    resizer = this._createResizer(element, trbl.right, trbl.bottom, 180, direction);
	  } else {
	    resizer = this._createResizer(element, trbl.left, trbl.bottom, 270, direction);
	  }

	  this.makeDraggable(element, resizer, direction);
	};

	// resize handles implementation ///////////////////////////////

	/**
	 * Add resizers for a given element.
	 *
	 * @param {djs.model.Shape} shape
	 */
	ResizeHandles.prototype.addResizer = function(shape) {
	  var resize = this._resize;

	  if (!resize.canResize({ shape: shape })) {
	    return;
	  }

	  this.createResizer(shape, 'nw');
	  this.createResizer(shape, 'ne');
	  this.createResizer(shape, 'se');
	  this.createResizer(shape, 'sw');
	};

	/**
	 * Remove all resizers
	 */
	ResizeHandles.prototype.removeResizers = function() {
	  var resizersParent = this._getResizersParent();

	  clear$1(resizersParent);
	};

	ResizeHandles.prototype._getResizersParent = function() {
	  return this._canvas.getLayer('resizers');
	};

	ResizeHandles.$inject = [
	  'eventBus',
	  'canvas',
	  'selection',
	  'resize'
	];

	var ResizeModule = {
	  __depends__: [
	    RulesModule,
	    DraggingModule,
	    PreviewSupportModule
	  ],
	  __init__: [
	    'resize',
	    'resizePreview',
	    'resizeHandles'
	  ],
	  resize: [ 'type', Resize ],
	  resizePreview: [ 'type', ResizePreview ],
	  resizeHandles: [ 'type', ResizeHandles ]
	};

	/**
	 * Creates a new bpmn:CategoryValue inside a new bpmn:Category
	 *
	 * @param {ModdleElement} definitions
	 * @param {BpmnFactory} bpmnFactory
	 *
	 * @return {ModdleElement} categoryValue.
	 */
	function createCategoryValue(definitions, bpmnFactory) {
	  var categoryValue = bpmnFactory.create('bpmn:CategoryValue'),
	      category = bpmnFactory.create('bpmn:Category', {
	        categoryValue: [ categoryValue ]
	      });

	  // add to correct place
	  add$1(definitions.get('rootElements'), category);
	  getBusinessObject(category).$parent = definitions;
	  getBusinessObject(categoryValue).$parent = category;

	  return categoryValue;

	}

	function LabelEditingProvider(
	    eventBus, bpmnFactory, canvas, directEditing,
	    modeling, resizeHandles, textRenderer) {

	  this._bpmnFactory = bpmnFactory;
	  this._canvas = canvas;
	  this._modeling = modeling;
	  this._textRenderer = textRenderer;

	  directEditing.registerProvider(this);

	  // listen to dblclick on non-root elements
	  eventBus.on('element.dblclick', function(event) {
	    activateDirectEdit(event.element, true);
	  });

	  // complete on followup canvas operation
	  eventBus.on([
	    'element.mousedown',
	    'drag.init',
	    'canvas.viewbox.changing',
	    'autoPlace',
	    'popupMenu.open'
	  ], function(event) {

	    if (directEditing.isActive()) {
	      directEditing.complete();
	    }
	  });

	  // cancel on command stack changes
	  eventBus.on([ 'commandStack.changed' ], function(e) {
	    if (directEditing.isActive()) {
	      directEditing.cancel();
	    }
	  });


	  eventBus.on('directEditing.activate', function(event) {
	    resizeHandles.removeResizers();
	  });

	  eventBus.on('create.end', 500, function(event) {

	    var element = event.shape,
	        canExecute = event.context.canExecute,
	        isTouch = event.isTouch;

	    // TODO(nikku): we need to find a way to support the
	    // direct editing on mobile devices; right now this will
	    // break for desworkflowediting on mobile devices
	    // as it breaks the user interaction workflow

	    // TODO(nre): we should temporarily focus the edited element
	    // here and release the focused viewport after the direct edit
	    // operation is finished
	    if (isTouch) {
	      return;
	    }

	    if (!canExecute) {
	      return;
	    }

	    activateDirectEdit(element);
	  });

	  eventBus.on('autoPlace.end', 500, function(event) {
	    activateDirectEdit(event.shape);
	  });


	  function activateDirectEdit(element, force) {
	    if (force ||
	        isAny(element, [ 'bpmn:Task', 'bpmn:TextAnnotation', 'bpmn:Group' ]) ||
	        isCollapsedSubProcess(element)) {

	      directEditing.activate(element);
	    }
	  }

	}

	LabelEditingProvider.$inject = [
	  'eventBus',
	  'bpmnFactory',
	  'canvas',
	  'directEditing',
	  'modeling',
	  'resizeHandles',
	  'textRenderer'
	];


	/**
	 * Activate direct editing for activities and text annotations.
	 *
	 * @param  {djs.model.Base} element
	 *
	 * @return {Object} an object with properties bounds (position and size), text and options
	 */
	LabelEditingProvider.prototype.activate = function(element) {

	  // text
	  var text = getLabel(element);

	  if (text === undefined) {
	    return;
	  }

	  var context = {
	    text: text
	  };

	  // bounds
	  var bounds = this.getEditingBBox(element);

	  assign(context, bounds);

	  var options = {};

	  // tasks
	  if (
	    isAny(element, [
	      'bpmn:Task',
	      'bpmn:Participant',
	      'bpmn:Lane',
	      'bpmn:CallActivity'
	    ]) ||
	    isCollapsedSubProcess(element)
	  ) {
	    assign(options, {
	      centerVertically: true
	    });
	  }

	  // external labels
	  if (isLabelExternal(element)) {
	    assign(options, {
	      autoResize: true
	    });
	  }

	  // text annotations
	  if (is$1(element, 'bpmn:TextAnnotation')) {
	    assign(options, {
	      resizable: true,
	      autoResize: true
	    });
	  }

	  assign(context, {
	    options: options
	  });

	  return context;
	};


	/**
	 * Get the editing bounding box based on the element's size and position
	 *
	 * @param  {djs.model.Base} element
	 *
	 * @return {Object} an object containing information about position
	 *                  and size (fixed or minimum and/or maximum)
	 */
	LabelEditingProvider.prototype.getEditingBBox = function(element) {
	  var canvas = this._canvas;

	  var target = element.label || element;

	  var bbox = canvas.getAbsoluteBBox(target);

	  var mid = {
	    x: bbox.x + bbox.width / 2,
	    y: bbox.y + bbox.height / 2
	  };

	  // default position
	  var bounds = { x: bbox.x, y: bbox.y };

	  var zoom = canvas.zoom();

	  var defaultStyle = this._textRenderer.getDefaultStyle(),
	      externalStyle = this._textRenderer.getExternalStyle();

	  // take zoom into account
	  var externalFontSize = externalStyle.fontSize * zoom,
	      externalLineHeight = externalStyle.lineHeight,
	      defaultFontSize = defaultStyle.fontSize * zoom,
	      defaultLineHeight = defaultStyle.lineHeight;

	  var style = {
	    fontFamily: this._textRenderer.getDefaultStyle().fontFamily,
	    fontWeight: this._textRenderer.getDefaultStyle().fontWeight
	  };

	  // adjust for expanded pools AND lanes
	  if (is$1(element, 'bpmn:Lane') || isExpandedPool(element)) {

	    assign(bounds, {
	      width: bbox.height,
	      height: 30 * zoom,
	      x: bbox.x - bbox.height / 2 + (15 * zoom),
	      y: mid.y - (30 * zoom) / 2
	    });

	    assign(style, {
	      fontSize: defaultFontSize + 'px',
	      lineHeight: defaultLineHeight,
	      paddingTop: (7 * zoom) + 'px',
	      paddingBottom: (7 * zoom) + 'px',
	      paddingLeft: (5 * zoom) + 'px',
	      paddingRight: (5 * zoom) + 'px',
	      transform: 'rotate(-90deg)'
	    });
	  }


	  // internal labels for tasks and collapsed call activities,
	  // sub processes and participants
	  if (isAny(element, [ 'bpmn:Task', 'bpmn:CallActivity']) ||
	      isCollapsedPool(element) ||
	      isCollapsedSubProcess(element)) {

	    assign(bounds, {
	      width: bbox.width,
	      height: bbox.height
	    });

	    assign(style, {
	      fontSize: defaultFontSize + 'px',
	      lineHeight: defaultLineHeight,
	      paddingTop: (7 * zoom) + 'px',
	      paddingBottom: (7 * zoom) + 'px',
	      paddingLeft: (5 * zoom) + 'px',
	      paddingRight: (5 * zoom) + 'px'
	    });
	  }


	  // internal labels for expanded sub processes
	  if (isExpandedSubProcess(element)) {
	    assign(bounds, {
	      width: bbox.width,
	      x: bbox.x
	    });

	    assign(style, {
	      fontSize: defaultFontSize + 'px',
	      lineHeight: defaultLineHeight,
	      paddingTop: (7 * zoom) + 'px',
	      paddingBottom: (7 * zoom) + 'px',
	      paddingLeft: (5 * zoom) + 'px',
	      paddingRight: (5 * zoom) + 'px'
	    });
	  }

	  var width = 90 * zoom,
	      paddingTop = 7 * zoom,
	      paddingBottom = 4 * zoom;

	  // external labels for events, data elements, gateways, groups and connections
	  if (target.labelTarget) {
	    assign(bounds, {
	      width: width,
	      height: bbox.height + paddingTop + paddingBottom,
	      x: mid.x - width / 2,
	      y: bbox.y - paddingTop
	    });

	    assign(style, {
	      fontSize: externalFontSize + 'px',
	      lineHeight: externalLineHeight,
	      paddingTop: paddingTop + 'px',
	      paddingBottom: paddingBottom + 'px'
	    });
	  }

	  // external label not yet created
	  if (isLabelExternal(target)
	      && !hasExternalLabel(target)
	      && !isLabel(target)) {

	    var externalLabelMid = getExternalLabelMid(element);

	    var absoluteBBox = canvas.getAbsoluteBBox({
	      x: externalLabelMid.x,
	      y: externalLabelMid.y,
	      width: 0,
	      height: 0
	    });

	    var height = externalFontSize + paddingTop + paddingBottom;

	    assign(bounds, {
	      width: width,
	      height: height,
	      x: absoluteBBox.x - width / 2,
	      y: absoluteBBox.y - height / 2
	    });

	    assign(style, {
	      fontSize: externalFontSize + 'px',
	      lineHeight: externalLineHeight,
	      paddingTop: paddingTop + 'px',
	      paddingBottom: paddingBottom + 'px'
	    });
	  }

	  // text annotations
	  if (is$1(element, 'bpmn:TextAnnotation')) {
	    assign(bounds, {
	      width: bbox.width,
	      height: bbox.height,
	      minWidth: 30 * zoom,
	      minHeight: 10 * zoom
	    });

	    assign(style, {
	      textAlign: 'left',
	      paddingTop: (5 * zoom) + 'px',
	      paddingBottom: (7 * zoom) + 'px',
	      paddingLeft: (7 * zoom) + 'px',
	      paddingRight: (5 * zoom) + 'px',
	      fontSize: defaultFontSize + 'px',
	      lineHeight: defaultLineHeight
	    });
	  }

	  return { bounds: bounds, style: style };
	};


	LabelEditingProvider.prototype.update = function(
	    element, newLabel,
	    activeContextText, bounds) {

	  var newBounds,
	      bbox;

	  if (is$1(element, 'bpmn:TextAnnotation')) {

	    bbox = this._canvas.getAbsoluteBBox(element);

	    newBounds = {
	      x: element.x,
	      y: element.y,
	      width: element.width / bbox.width * bounds.width,
	      height: element.height / bbox.height * bounds.height
	    };
	  }

	  if (is$1(element, 'bpmn:Group')) {

	    var businessObject = getBusinessObject(element);

	    // initialize categoryValue if not existing
	    if (!businessObject.categoryValueRef) {

	      var rootElement = this._canvas.getRootElement(),
	          definitions = getBusinessObject(rootElement).$parent;

	      var categoryValue = createCategoryValue(definitions, this._bpmnFactory);

	      getBusinessObject(element).categoryValueRef = categoryValue;
	    }

	  }

	  if (isEmptyText(newLabel)) {
	    newLabel = null;
	  }

	  this._modeling.updateLabel(element, newLabel, newBounds);
	};



	// helpers //////////////////////

	function isCollapsedSubProcess(element) {
	  return is$1(element, 'bpmn:SubProcess') && !isExpanded(element);
	}

	function isExpandedSubProcess(element) {
	  return is$1(element, 'bpmn:SubProcess') && isExpanded(element);
	}

	function isCollapsedPool(element) {
	  return is$1(element, 'bpmn:Participant') && !isExpanded(element);
	}

	function isExpandedPool(element) {
	  return is$1(element, 'bpmn:Participant') && isExpanded(element);
	}

	function isEmptyText(label) {
	  return !label || !label.trim();
	}

	var MARKER_HIDDEN = 'djs-element-hidden',
	    MARKER_LABEL_HIDDEN = 'djs-label-hidden';


	function LabelEditingPreview(
	    eventBus, canvas, elementRegistry,
	    pathMap) {

	  var self = this;

	  var defaultLayer = canvas.getDefaultLayer();

	  var element, absoluteElementBBox, gfx;

	  eventBus.on('directEditing.activate', function(context) {
	    var activeProvider = context.active;

	    element = activeProvider.element.label || activeProvider.element;

	    // text annotation
	    if (is$1(element, 'bpmn:TextAnnotation')) {
	      absoluteElementBBox = canvas.getAbsoluteBBox(element);

	      gfx = create('g');

	      var textPathData = pathMap.getScaledPath('TEXT_ANNOTATION', {
	        xScaleFactor: 1,
	        yScaleFactor: 1,
	        containerWidth: element.width,
	        containerHeight: element.height,
	        position: {
	          mx: 0.0,
	          my: 0.0
	        }
	      });

	      var path = self.path = create('path');

	      attr$1(path, {
	        d: textPathData,
	        strokeWidth: 2,
	        stroke: getStrokeColor$1(element)
	      });

	      append(gfx, path);

	      append(defaultLayer, gfx);

	      translate(gfx, element.x, element.y);
	    }

	    if (is$1(element, 'bpmn:TextAnnotation') ||
	        element.labelTarget) {
	      canvas.addMarker(element, MARKER_HIDDEN);
	    } else if (is$1(element, 'bpmn:Task') ||
	               is$1(element, 'bpmn:CallActivity') ||
	               is$1(element, 'bpmn:SubProcess') ||
	               is$1(element, 'bpmn:Participant')) {
	      canvas.addMarker(element, MARKER_LABEL_HIDDEN);
	    }
	  });

	  eventBus.on('directEditing.resize', function(context) {

	    // text annotation
	    if (is$1(element, 'bpmn:TextAnnotation')) {
	      var height = context.height,
	          dy = context.dy;

	      var newElementHeight = Math.max(element.height / absoluteElementBBox.height * (height + dy), 0);

	      var textPathData = pathMap.getScaledPath('TEXT_ANNOTATION', {
	        xScaleFactor: 1,
	        yScaleFactor: 1,
	        containerWidth: element.width,
	        containerHeight: newElementHeight,
	        position: {
	          mx: 0.0,
	          my: 0.0
	        }
	      });

	      attr$1(self.path, {
	        d: textPathData
	      });
	    }
	  });

	  eventBus.on([ 'directEditing.complete', 'directEditing.cancel' ], function(context) {
	    var activeProvider = context.active;

	    if (activeProvider) {
	      canvas.removeMarker(activeProvider.element.label || activeProvider.element, MARKER_HIDDEN);
	      canvas.removeMarker(element, MARKER_LABEL_HIDDEN);
	    }

	    element = undefined;
	    absoluteElementBBox = undefined;

	    if (gfx) {
	      remove$1(gfx);

	      gfx = undefined;
	    }
	  });
	}

	LabelEditingPreview.$inject = [
	  'eventBus',
	  'canvas',
	  'elementRegistry',
	  'pathMap'
	];


	// helpers ///////////////////

	function getStrokeColor$1(element, defaultColor) {
	  var bo = getBusinessObject(element);

	  return bo.di.get('stroke') || defaultColor || 'black';
	}

	var LabelEditingModule = {
	  __depends__: [
	    ChangeSupportModule,
	    ResizeModule,
	    DirectEditingModule
	  ],
	  __init__: [
	    'labelEditingProvider',
	    'labelEditingPreview'
	  ],
	  labelEditingProvider: [ 'type', LabelEditingProvider ],
	  labelEditingPreview: [ 'type', LabelEditingPreview ]
	};

	var ALIGNMENTS = [
	  'top',
	  'bottom',
	  'left',
	  'right'
	];

	var ELEMENT_LABEL_DISTANCE = 10;

	/**
	 * A component that makes sure that external labels are added
	 * together with respective elements and properly updated (DI wise)
	 * during move.
	 *
	 * @param {EventBus} eventBus
	 * @param {Modeling} modeling
	 */
	function AdaptiveLabelPositioningBehavior(eventBus, modeling) {

	  CommandInterceptor.call(this, eventBus);

	  this.postExecuted([
	    'connection.create',
	    'connection.layout',
	    'connection.updateWaypoints'
	  ], function(event) {

	    var context = event.context,
	        connection = context.connection;

	    var source = connection.source,
	        target = connection.target;

	    checkLabelAdjustment(source);
	    checkLabelAdjustment(target);
	  });


	  this.postExecuted([
	    'label.create'
	  ], function(event) {
	    checkLabelAdjustment(event.context.shape.labelTarget);
	  });


	  function checkLabelAdjustment(element) {

	    // skip non-existing labels
	    if (!hasExternalLabel(element)) {
	      return;
	    }

	    var optimalPosition = getOptimalPosition(element);

	    // no optimal position found
	    if (!optimalPosition) {
	      return;
	    }

	    adjustLabelPosition(element, optimalPosition);
	  }

	  function adjustLabelPosition(element, orientation) {

	    var elementMid = getMid(element),
	        label = element.label,
	        labelMid = getMid(label);

	    var elementTrbl = asTRBL(element);

	    var newLabelMid;

	    switch (orientation) {
	    case 'top':
	      newLabelMid = {
	        x: elementMid.x,
	        y: elementTrbl.top - ELEMENT_LABEL_DISTANCE - label.height / 2
	      };

	      break;

	    case 'left':

	      newLabelMid = {
	        x: elementTrbl.left - ELEMENT_LABEL_DISTANCE - label.width / 2,
	        y: elementMid.y
	      };

	      break;

	    case 'bottom':

	      newLabelMid = {
	        x: elementMid.x,
	        y: elementTrbl.bottom + ELEMENT_LABEL_DISTANCE + label.height / 2
	      };

	      break;

	    case 'right':

	      newLabelMid = {
	        x: elementTrbl.right + ELEMENT_LABEL_DISTANCE + label.width / 2,
	        y: elementMid.y
	      };

	      break;
	    }


	    var delta$1 = delta(newLabelMid, labelMid);

	    modeling.moveShape(label, delta$1);
	  }

	}

	inherits_browser(AdaptiveLabelPositioningBehavior, CommandInterceptor);

	AdaptiveLabelPositioningBehavior.$inject = [
	  'eventBus',
	  'modeling'
	];


	// helpers //////////////////////

	/**
	 * Return alignments which are taken by a boundary's host element
	 *
	 * @param {Shape} element
	 *
	 * @return {Array<String>}
	 */
	function getTakenHostAlignments(element) {

	  var hostElement = element.host,
	      elementMid = getMid(element),
	      hostOrientation = getOrientation(elementMid, hostElement);

	  var freeAlignments;

	  // check whether there is a multi-orientation, e.g. 'top-left'
	  if (hostOrientation.indexOf('-') >= 0) {
	    freeAlignments = hostOrientation.split('-');
	  } else {
	    freeAlignments = [ hostOrientation ];
	  }

	  var takenAlignments = ALIGNMENTS.filter(function(alignment) {

	    return freeAlignments.indexOf(alignment) === -1;
	  });

	  return takenAlignments;

	}

	/**
	 * Return alignments which are taken by related connections
	 *
	 * @param {Shape} element
	 *
	 * @return {Array<String>}
	 */
	function getTakenConnectionAlignments(element) {

	  var elementMid = getMid(element);

	  var takenAlignments = [].concat(
	    element.incoming.map(function(c) {
	      return c.waypoints[c.waypoints.length - 2 ];
	    }),
	    element.outgoing.map(function(c) {
	      return c.waypoints[1];
	    })
	  ).map(function(point) {
	    return getApproximateOrientation(elementMid, point);
	  });

	  return takenAlignments;
	}

	/**
	 * Return the optimal label position around an element
	 * or _undefined_, if none was found.
	 *
	 * @param  {Shape} element
	 *
	 * @return {String} positioning identifier
	 */
	function getOptimalPosition(element) {

	  var labelMid = getMid(element.label);

	  var elementMid = getMid(element);

	  var labelOrientation = getApproximateOrientation(elementMid, labelMid);

	  if (!isAligned(labelOrientation)) {
	    return;
	  }

	  var takenAlignments = getTakenConnectionAlignments(element);

	  if (element.host) {
	    var takenHostAlignments = getTakenHostAlignments(element);

	    takenAlignments = takenAlignments.concat(takenHostAlignments);
	  }

	  var freeAlignments = ALIGNMENTS.filter(function(alignment) {

	    return takenAlignments.indexOf(alignment) === -1;
	  });

	  // NOTHING TO DO; label already aligned a.O.K.
	  if (freeAlignments.indexOf(labelOrientation) !== -1) {
	    return;
	  }

	  return freeAlignments[0];
	}

	function getApproximateOrientation(p0, p1) {
	  return getOrientation(p1, p0, 5);
	}

	function isAligned(orientation) {
	  return ALIGNMENTS.indexOf(orientation) !== -1;
	}

	function AppendBehavior(eventBus, elementFactory, bpmnRules) {

	  CommandInterceptor.call(this, eventBus);

	  // assign correct shape position unless already set

	  this.preExecute('shape.append', function(context) {

	    var source = context.source,
	        shape = context.shape;

	    if (!context.position) {

	      if (is$1(shape, 'bpmn:TextAnnotation')) {
	        context.position = {
	          x: source.x + source.width / 2 + 75,
	          y: source.y - (50) - shape.height / 2
	        };
	      } else {
	        context.position = {
	          x: source.x + source.width + 80 + shape.width / 2,
	          y: source.y + source.height / 2
	        };
	      }
	    }
	  }, true);
	}

	inherits_browser(AppendBehavior, CommandInterceptor);

	AppendBehavior.$inject = [
	  'eventBus',
	  'elementFactory',
	  'bpmnRules'
	];

	/**
	 * BPMN specific attach event behavior
	 */
	function AttachEventBehavior(eventBus, bpmnReplace) {

	  CommandInterceptor.call(this, eventBus);

	  /**
	   * replace intermediate event with boundary event when
	   * attaching it to a shape
	   */

	  this.preExecute('elements.move', function(context) {
	    var shapes = context.shapes,
	        host = context.newHost,
	        shape,
	        eventDefinition,
	        boundaryEvent,
	        newShape;

	    if (shapes.length !== 1) {
	      return;
	    }

	    shape = shapes[0];

	    if (host && isAny(shape, [ 'bpmn:IntermediateThrowEvent', 'bpmn:IntermediateCatchEvent' ])) {

	      eventDefinition = getEventDefinition(shape);

	      boundaryEvent = {
	        type: 'bpmn:BoundaryEvent',
	        host: host
	      };

	      if (eventDefinition) {
	        boundaryEvent.eventDefinitionType = eventDefinition.$type;
	      }

	      newShape = bpmnReplace.replaceElement(shape, boundaryEvent, { layoutConnection: false });

	      context.shapes = [ newShape ];
	    }
	  }, true);
	}

	AttachEventBehavior.$inject = [
	  'eventBus',
	  'bpmnReplace'
	];

	inherits_browser(AttachEventBehavior, CommandInterceptor);



	// helper /////
	function getEventDefinition(element) {
	  var bo = getBusinessObject(element);

	  return bo && bo.eventDefinitions && bo.eventDefinitions[0];
	}

	/**
	 * BPMN specific boundary event behavior
	 */
	function BoundaryEventBehavior(eventBus, modeling) {

	  CommandInterceptor.call(this, eventBus);

	  function getBoundaryEvents(element) {
	    return filter(element.attachers, function(attacher) {
	      return is$1(attacher, 'bpmn:BoundaryEvent');
	    });
	  }

	  // remove after connecting to event-based gateway
	  this.postExecute('connection.create', function(event) {
	    var source = event.context.source,
	        target = event.context.target,
	        boundaryEvents = getBoundaryEvents(target);

	    if (
	      is$1(source, 'bpmn:EventBasedGateway') &&
	      is$1(target, 'bpmn:ReceiveTask') &&
	      boundaryEvents.length > 0
	    ) {
	      modeling.removeElements(boundaryEvents);
	    }

	  });

	  // remove after replacing connected gateway with event-based gateway
	  this.postExecute('connection.reconnectStart', function(event) {
	    var oldSource = event.context.oldSource,
	        newSource = event.context.newSource;

	    if (is$1(oldSource, 'bpmn:Gateway') &&
	        is$1(newSource, 'bpmn:EventBasedGateway')) {
	      forEach(newSource.outgoing, function(connection) {
	        var target = connection.target,
	            attachedboundaryEvents = getBoundaryEvents(target);

	        if (is$1(target, 'bpmn:ReceiveTask') &&
	            attachedboundaryEvents.length > 0) {
	          modeling.removeElements(attachedboundaryEvents);
	        }
	      });
	    }
	  });
	}

	BoundaryEventBehavior.$inject = [
	  'eventBus',
	  'modeling'
	];

	inherits_browser(BoundaryEventBehavior, CommandInterceptor);

	function CopyPasteBehavior(eventBus, modeling, canvas) {

	  CommandInterceptor.call(this, eventBus);

	  this.preExecute('elements.paste', 1500, function(context) {
	    var topParent = context.topParent;

	    // always grab the latest root
	    if (!topParent.parent) {
	      context.topParent = canvas.getRootElement();
	    }

	    if (is$1(topParent, 'bpmn:Lane')) {
	      do {
	        // unwrap Lane -> LaneSet -> (Lane | FlowElementsContainer)
	        topParent = context.topParent = topParent.parent;

	      } while (is$1(topParent, 'bpmn:Lane') || !is$1(topParent, 'bpmn:Participant'));
	    }
	  }, true);

	  this.postExecute('elements.paste', function(context) {

	    var tree = context.tree,
	        createdElements = tree.createdElements;

	    forEach(createdElements, function(data) {
	      var element = data.element,
	          businessObject = element.businessObject,
	          descriptor = data.descriptor,
	          defaultFlow;

	      if ((is$1(businessObject, 'bpmn:ExclusiveGateway') || is$1(businessObject, 'bpmn:InclusiveGateway') ||
	           is$1(businessObject, 'bpmn:Activity')) && descriptor.default) {

	        defaultFlow = createdElements[descriptor.default];

	        // if the default flow wasn't created, means that it wasn't copied
	        if (defaultFlow) {
	          defaultFlow = defaultFlow.element;
	        } else {
	          defaultFlow = undefined;
	        }

	        delete element.default;

	        modeling.updateProperties(element, { default: defaultFlow });
	      }
	    });
	  }, true);
	}


	CopyPasteBehavior.$inject = [
	  'eventBus',
	  'modeling',
	  'canvas'
	];

	inherits_browser(CopyPasteBehavior, CommandInterceptor);

	var HIGH_PRIORITY$6 = 1500;
	var HIGHEST_PRIORITY = 2000;


	/**
	 * Correct hover targets in certain situations to improve diagram interaction.
	 *
	 * @param {ElementRegistry} elementRegistry
	 * @param {EventBus} eventBus
	 * @param {Canvas} canvas
	 */
	function FixHoverBehavior(elementRegistry, eventBus, canvas) {

	  eventBus.on([
	    'create.hover',
	    'create.move',
	    'create.end',
	    'shape.move.hover',
	    'shape.move.move',
	    'shape.move.end'
	  ], HIGH_PRIORITY$6, function(event) {
	    var context = event.context,
	        shape = context.shape || event.shape,
	        hover = event.hover;

	    // ensure elements are not dropped onto a bpmn:Lane but onto
	    // the underlying bpmn:Participant
	    if (is$1(hover, 'bpmn:Lane') && !isAny(shape, [ 'bpmn:Lane', 'bpmn:Participant' ])) {
	      event.hover = getLanesRoot(hover);
	      event.hoverGfx = elementRegistry.getGraphics(event.hover);
	    }

	    var rootElement = canvas.getRootElement();

	    // ensure bpmn:Group and label elements are dropped
	    // always onto the root
	    if (hover !== rootElement && (shape.labelTarget || is$1(shape, 'bpmn:Group'))) {
	      event.hover = rootElement;
	      event.hoverGfx = elementRegistry.getGraphics(event.hover);
	    }
	  });


	  eventBus.on([
	    'connect.hover',
	    'global-connect.hover'
	  ], HIGH_PRIORITY$6, function(event) {
	    var hover = event.hover;

	    // ensure connections start/end on bpmn:Participant,
	    // not the underlying bpmn:Lane
	    if (is$1(hover, 'bpmn:Lane')) {
	      event.hover = getLanesRoot(hover) || hover;
	      event.hoverGfx = elementRegistry.getGraphics(event.hover);
	    }
	  });


	  eventBus.on([
	    'bendpoint.move.hover'
	  ], HIGH_PRIORITY$6, function(event) {
	    var context = event.context,
	        type = context.type,
	        hover = event.hover;

	    // ensure reconnect start/end on bpmn:Participant,
	    // not the underlying bpmn:Lane
	    if (is$1(hover, 'bpmn:Lane') && /reconnect/.test(type)) {
	      event.hover = getLanesRoot(hover) || hover;
	      event.hoverGfx = elementRegistry.getGraphics(event.hover);
	    }
	  });


	  eventBus.on([
	    'connect.start'
	  ], HIGH_PRIORITY$6, function(event) {

	    var context = event.context,
	        source = context.source;

	    // ensure connect start on bpmn:Participant,
	    // not the underlying bpmn:Lane
	    if (is$1(source, 'bpmn:Lane')) {
	      context.source = getLanesRoot(source) || source;
	    }
	  });


	  // allow movement of participants from lanes
	  eventBus.on('shape.move.start', HIGHEST_PRIORITY, function(event) {
	    var shape = event.shape;

	    if (is$1(shape, 'bpmn:Lane')) {
	      event.shape = getLanesRoot(shape) || shape;
	    }
	  });

	}

	FixHoverBehavior.$inject = [
	  'elementRegistry',
	  'eventBus',
	  'canvas'
	];

	/**
	 * BPMN specific create boundary event behavior
	 */
	function CreateBoundaryEventBehavior(
	    eventBus, modeling, elementFactory,
	    bpmnFactory) {

	  CommandInterceptor.call(this, eventBus);

	  /**
	   * replace intermediate event with boundary event when
	   * attaching it to a shape
	   */

	  this.preExecute('shape.create', function(context) {
	    var shape = context.shape,
	        host = context.host,
	        businessObject,
	        boundaryEvent;

	    var attrs = {
	      cancelActivity: true
	    };

	    if (host && is$1(shape, 'bpmn:IntermediateThrowEvent')) {
	      attrs.attachedToRef = host.businessObject;

	      businessObject = bpmnFactory.create('bpmn:BoundaryEvent', attrs);

	      boundaryEvent = {
	        type: 'bpmn:BoundaryEvent',
	        businessObject: businessObject
	      };

	      context.shape = elementFactory.createShape(boundaryEvent);
	    }
	  }, true);
	}

	CreateBoundaryEventBehavior.$inject = [
	  'eventBus',
	  'modeling',
	  'elementFactory',
	  'bpmnFactory'
	];

	inherits_browser(CreateBoundaryEventBehavior, CommandInterceptor);

	/**
	 * BPMN specific create data object behavior
	 */
	function CreateDataObjectBehavior(eventBus, bpmnFactory, moddle) {

	  CommandInterceptor.call(this, eventBus);

	  this.preExecute('shape.create', function(event) {

	    var context = event.context,
	        shape = context.shape;

	    if (is$1(shape, 'bpmn:DataObjectReference') && shape.type !== 'label') {

	      // create a DataObject every time a DataObjectReference is created
	      var dataObject = bpmnFactory.create('bpmn:DataObject');

	      // set the reference to the DataObject
	      shape.businessObject.dataObjectRef = dataObject;
	    }
	  });

	}

	CreateDataObjectBehavior.$inject = [
	  'eventBus',
	  'bpmnFactory',
	  'moddle'
	];

	inherits_browser(CreateDataObjectBehavior, CommandInterceptor);

	var HORIZONTAL_PARTICIPANT_PADDING = 20,
	    VERTICAL_PARTICIPANT_PADDING = 20;

	var PARTICIPANT_BORDER_WIDTH = 30;

	var HIGH_PRIORITY$7 = 2000;


	/**
	 * BPMN-specific behavior for creating participants.
	 */
	function CreateParticipantBehavior$1(canvas, eventBus, modeling) {
	  CommandInterceptor.call(this, eventBus);

	  // fit participant
	  eventBus.on([
	    'create.start',
	    'shape.move.start'
	  ], HIGH_PRIORITY$7, function(event) {
	    var context = event.context,
	        shape = context.shape,
	        rootElement = canvas.getRootElement();

	    if (!is$1(shape, 'bpmn:Participant') ||
	      !is$1(rootElement, 'bpmn:Process') ||
	      !rootElement.children.length) {
	      return;
	    }

	    // ignore connections, groups and labels
	    var children = rootElement.children.filter(function(element) {
	      return !is$1(element, 'bpmn:Group') &&
	        !isLabel(element) &&
	        !isConnection(element);
	    });

	    // ensure for available children to calculate bounds
	    if (!children.length) {
	      return;
	    }

	    var childrenBBox = getBBox(children);

	    var participantBounds = getParticipantBounds(shape, childrenBBox);

	    // assign width and height
	    assign(shape, participantBounds);

	    // assign create constraints
	    context.createConstraints = getParticipantCreateConstraints(shape, childrenBBox);
	  });

	  // force hovering process when creating first participant
	  eventBus.on('create.start', HIGH_PRIORITY$7, function(event) {
	    var context = event.context,
	        shape = context.shape,
	        rootElement = canvas.getRootElement(),
	        rootElementGfx = canvas.getGraphics(rootElement);

	    function ensureHoveringProcess(event) {
	      event.element = rootElement;
	      event.gfx = rootElementGfx;
	    }

	    if (is$1(shape, 'bpmn:Participant') && is$1(rootElement, 'bpmn:Process')) {
	      eventBus.on('element.hover', HIGH_PRIORITY$7, ensureHoveringProcess);

	      eventBus.once('create.cleanup', function() {
	        eventBus.off('element.hover', ensureHoveringProcess);
	      });
	    }
	  });

	  // turn process into collaboration before adding participant
	  this.preExecute('shape.create', function(context) {
	    var parent = context.parent,
	        shape = context.shape,
	        position = context.position;

	    var rootElement = canvas.getRootElement();

	    if (
	      is$1(parent, 'bpmn:Process') &&
	      is$1(shape, 'bpmn:Participant') &&
	      !is$1(rootElement, 'bpmn:Collaboration')
	    ) {

	      // this is going to detach the process root
	      // and set the returned collaboration element
	      // as the new root element
	      var collaborationElement = modeling.makeCollaboration();

	      // monkey patch the create context
	      // so that the participant is being dropped
	      // onto the new collaboration root instead
	      context.position = position;
	      context.parent = collaborationElement;

	      context.processRoot = parent;
	    }
	  }, true);

	  this.execute('shape.create', function(context) {
	    var processRoot = context.processRoot,
	        shape = context.shape;

	    if (processRoot) {
	      context.oldProcessRef = shape.businessObject.processRef;

	      // assign the participant processRef
	      shape.businessObject.processRef = processRoot.businessObject;
	    }
	  }, true);

	  this.revert('shape.create', function(context) {
	    var processRoot = context.processRoot,
	        shape = context.shape;

	    if (processRoot) {

	      // assign the participant processRef
	      shape.businessObject.processRef = context.oldProcessRef;
	    }
	  }, true);

	  this.postExecute('shape.create', function(context) {
	    var processRoot = context.processRoot,
	        shape = context.shape;

	    if (processRoot) {

	      // process root is already detached at this point
	      var processChildren = processRoot.children.slice();

	      modeling.moveElements(processChildren, { x: 0, y: 0 }, shape);
	    }

	  }, true);
	}

	CreateParticipantBehavior$1.$inject = [
	  'canvas',
	  'eventBus',
	  'modeling'
	];

	inherits_browser(CreateParticipantBehavior$1, CommandInterceptor);

	// helpers //////////

	function getParticipantBounds(shape, childrenBBox) {
	  childrenBBox = {
	    width: childrenBBox.width + HORIZONTAL_PARTICIPANT_PADDING * 2 + PARTICIPANT_BORDER_WIDTH,
	    height: childrenBBox.height + VERTICAL_PARTICIPANT_PADDING * 2
	  };

	  return {
	    width: Math.max(shape.width, childrenBBox.width),
	    height: Math.max(shape.height, childrenBBox.height)
	  };
	}

	function getParticipantCreateConstraints(shape, childrenBBox) {
	  childrenBBox = asTRBL(childrenBBox);

	  return {
	    bottom: childrenBBox.top + shape.height / 2 - VERTICAL_PARTICIPANT_PADDING,
	    left: childrenBBox.right - shape.width / 2 + HORIZONTAL_PARTICIPANT_PADDING,
	    top: childrenBBox.bottom - shape.height / 2 + VERTICAL_PARTICIPANT_PADDING,
	    right: childrenBBox.left + shape.width / 2 - HORIZONTAL_PARTICIPANT_PADDING - PARTICIPANT_BORDER_WIDTH
	  };
	}

	function isConnection(element) {
	  return !!element.waypoints;
	}

	var TARGET_REF_PLACEHOLDER_NAME = '__targetRef_placeholder';


	/**
	 * This behavior makes sure we always set a fake
	 * DataInputAssociation#targetRef as demanded by the BPMN 2.0
	 * XSD schema.
	 *
	 * The reference is set to a bpmn:Property{ name: '__targetRef_placeholder' }
	 * which is created on the fly and cleaned up afterwards if not needed
	 * anymore.
	 *
	 * @param {EventBus} eventBus
	 * @param {BpmnFactory} bpmnFactory
	 */
	function DataInputAssociationBehavior(eventBus, bpmnFactory) {

	  CommandInterceptor.call(this, eventBus);


	  this.executed([
	    'connection.create',
	    'connection.delete',
	    'connection.move',
	    'connection.reconnectEnd'
	  ], ifDataInputAssociation(fixTargetRef));

	  this.reverted([
	    'connection.create',
	    'connection.delete',
	    'connection.move',
	    'connection.reconnectEnd'
	  ], ifDataInputAssociation(fixTargetRef));


	  function usesTargetRef(element, targetRef, removedConnection) {

	    var inputAssociations = element.get('dataInputAssociations');

	    return find(inputAssociations, function(association) {
	      return association !== removedConnection &&
	             association.targetRef === targetRef;
	    });
	  }

	  function getTargetRef(element, create) {

	    var properties = element.get('properties');

	    var targetRefProp = find(properties, function(p) {
	      return p.name === TARGET_REF_PLACEHOLDER_NAME;
	    });

	    if (!targetRefProp && create) {
	      targetRefProp = bpmnFactory.create('bpmn:Property', {
	        name: TARGET_REF_PLACEHOLDER_NAME
	      });

	      add$1(properties, targetRefProp);
	    }

	    return targetRefProp;
	  }

	  function cleanupTargetRef(element, connection) {

	    var targetRefProp = getTargetRef(element);

	    if (!targetRefProp) {
	      return;
	    }

	    if (!usesTargetRef(element, targetRefProp, connection)) {
	      remove$2(element.get('properties'), targetRefProp);
	    }
	  }

	  /**
	   * Make sure targetRef is set to a valid property or
	   * `null` if the connection is detached.
	   *
	   * @param {Event} event
	   */
	  function fixTargetRef(event) {

	    var context = event.context,
	        connection = context.connection,
	        connectionBo = connection.businessObject,
	        target = connection.target,
	        targetBo = target && target.businessObject,
	        newTarget = context.newTarget,
	        newTargetBo = newTarget && newTarget.businessObject,
	        oldTarget = context.oldTarget || context.target,
	        oldTargetBo = oldTarget && oldTarget.businessObject;

	    var dataAssociation = connection.businessObject,
	        targetRefProp;

	    if (oldTargetBo && oldTargetBo !== targetBo) {
	      cleanupTargetRef(oldTargetBo, connectionBo);
	    }

	    if (newTargetBo && newTargetBo !== targetBo) {
	      cleanupTargetRef(newTargetBo, connectionBo);
	    }

	    if (targetBo) {
	      targetRefProp = getTargetRef(targetBo, true);
	      dataAssociation.targetRef = targetRefProp;
	    } else {
	      dataAssociation.targetRef = null;
	    }
	  }
	}

	DataInputAssociationBehavior.$inject = [
	  'eventBus',
	  'bpmnFactory'
	];

	inherits_browser(DataInputAssociationBehavior, CommandInterceptor);


	/**
	 * Only call the given function when the event
	 * touches a bpmn:DataInputAssociation.
	 *
	 * @param {Function} fn
	 * @return {Function}
	 */
	function ifDataInputAssociation(fn) {

	  return function(event) {
	    var context = event.context,
	        connection = context.connection;

	    if (is$1(connection, 'bpmn:DataInputAssociation')) {
	      return fn(event);
	    }
	  };
	}

	function UpdateSemanticParentHandler(bpmnUpdater) {
	  this._bpmnUpdater = bpmnUpdater;
	}

	UpdateSemanticParentHandler.$inject = [ 'bpmnUpdater' ];


	UpdateSemanticParentHandler.prototype.execute = function(context) {
	  var dataStoreBo = context.dataStoreBo,
	      newSemanticParent = context.newSemanticParent,
	      newDiParent = context.newDiParent;

	  context.oldSemanticParent = dataStoreBo.$parent;
	  context.oldDiParent = dataStoreBo.di.$parent;

	  // update semantic parent
	  this._bpmnUpdater.updateSemanticParent(dataStoreBo, newSemanticParent);

	  // update DI parent
	  this._bpmnUpdater.updateDiParent(dataStoreBo.di, newDiParent);
	};

	UpdateSemanticParentHandler.prototype.revert = function(context) {
	  var dataStoreBo = context.dataStoreBo,
	      oldSemanticParent = context.oldSemanticParent,
	      oldDiParent = context.oldDiParent;

	  // update semantic parent
	  this._bpmnUpdater.updateSemanticParent(dataStoreBo, oldSemanticParent);

	  // update DI parent
	  this._bpmnUpdater.updateDiParent(dataStoreBo.di, oldDiParent);
	};

	/**
	 * BPMN specific data store behavior
	 */
	function DataStoreBehavior(
	    canvas, commandStack, elementRegistry,
	    eventBus) {

	  CommandInterceptor.call(this, eventBus);

	  commandStack.registerHandler('dataStore.updateContainment', UpdateSemanticParentHandler);

	  function getFirstParticipant() {
	    return elementRegistry.filter(function(element) {
	      return is$1(element, 'bpmn:Participant');
	    })[0];
	  }

	  function getDataStores(element) {
	    return element.children.filter(function(child) {
	      return is$1(child, 'bpmn:DataStoreReference') && !child.labelTarget;
	    });
	  }

	  function updateDataStoreParent(dataStore, newDataStoreParent) {
	    var dataStoreBo = dataStore.businessObject || dataStore;

	    newDataStoreParent = newDataStoreParent || getFirstParticipant();

	    if (newDataStoreParent) {
	      var newDataStoreParentBo = newDataStoreParent.businessObject || newDataStoreParent;

	      commandStack.execute('dataStore.updateContainment', {
	        dataStoreBo: dataStoreBo,
	        newSemanticParent: newDataStoreParentBo.processRef || newDataStoreParentBo,
	        newDiParent: newDataStoreParentBo.di
	      });
	    }
	  }


	  // disable auto-resize for data stores
	  this.preExecute('shape.create', function(event) {

	    var context = event.context,
	        shape = context.shape;

	    if (is$1(shape, 'bpmn:DataStoreReference') &&
	        shape.type !== 'label') {

	      if (!context.hints) {
	        context.hints = {};
	      }

	      // prevent auto resizing
	      context.hints.autoResize = false;
	    }
	  });


	  // disable auto-resize for data stores
	  this.preExecute('elements.move', function(event) {
	    var context = event.context,
	        shapes = context.shapes;

	    var dataStoreReferences = shapes.filter(function(shape) {
	      return is$1(shape, 'bpmn:DataStoreReference');
	    });

	    if (dataStoreReferences.length) {
	      if (!context.hints) {
	        context.hints = {};
	      }

	      // prevent auto resizing for data store references
	      context.hints.autoResize = shapes.filter(function(shape) {
	        return !is$1(shape, 'bpmn:DataStoreReference');
	      });
	    }
	  });


	  // update parent on data store created
	  this.postExecute('shape.create', function(event) {
	    var context = event.context,
	        shape = context.shape,
	        parent = shape.parent;


	    if (is$1(shape, 'bpmn:DataStoreReference') &&
	        shape.type !== 'label' &&
	        is$1(parent, 'bpmn:Collaboration')) {

	      updateDataStoreParent(shape);
	    }
	  });


	  // update parent on data store moved
	  this.postExecute('shape.move', function(event) {
	    var context = event.context,
	        shape = context.shape,
	        oldParent = context.oldParent,
	        parent = shape.parent;

	    if (is$1(oldParent, 'bpmn:Collaboration')) {

	      // do nothing if not necessary
	      return;
	    }

	    if (is$1(shape, 'bpmn:DataStoreReference') &&
	        shape.type !== 'label' &&
	        is$1(parent, 'bpmn:Collaboration')) {

	      var participant = is$1(oldParent, 'bpmn:Participant') ?
	        oldParent :
	        getAncestor(oldParent, 'bpmn:Participant');

	      updateDataStoreParent(shape, participant);
	    }
	  });


	  // update data store parents on participant or subprocess deleted
	  this.postExecute('shape.delete', function(event) {
	    var context = event.context,
	        shape = context.shape,
	        rootElement = canvas.getRootElement();

	    if (isAny(shape, [ 'bpmn:Participant', 'bpmn:SubProcess' ])
	        && is$1(rootElement, 'bpmn:Collaboration')) {
	      getDataStores(rootElement)
	        .filter(function(dataStore) {
	          return isDescendant(dataStore, shape);
	        })
	        .forEach(function(dataStore) {
	          updateDataStoreParent(dataStore);
	        });
	    }
	  });

	  // update data store parents on collaboration -> process
	  this.postExecute('canvas.updateRoot', function(event) {
	    var context = event.context,
	        oldRoot = context.oldRoot,
	        newRoot = context.newRoot;

	    var dataStores = getDataStores(oldRoot);

	    dataStores.forEach(function(dataStore) {

	      if (is$1(newRoot, 'bpmn:Process')) {
	        updateDataStoreParent(dataStore, newRoot);
	      }

	    });
	  });
	}

	DataStoreBehavior.$inject = [
	  'canvas',
	  'commandStack',
	  'elementRegistry',
	  'eventBus',
	];

	inherits_browser(DataStoreBehavior, CommandInterceptor);


	// helpers //////////

	function isDescendant(descendant, ancestor) {
	  var descendantBo = descendant.businessObject || descendant,
	      ancestorBo = ancestor.businessObject || ancestor;

	  while (descendantBo.$parent) {
	    if (descendantBo.$parent === ancestorBo.processRef || ancestorBo) {
	      return true;
	    }

	    descendantBo = descendantBo.$parent;
	  }

	  return false;
	}

	function getAncestor(element, type) {

	  while (element.parent) {
	    if (is$1(element.parent, type)) {
	      return element.parent;
	    }

	    element = element.parent;
	  }
	}

	var LOW_PRIORITY$8 = 500;


	/**
	 * BPMN specific delete lane behavior
	 */
	function DeleteLaneBehavior(eventBus, modeling, spaceTool) {

	  CommandInterceptor.call(this, eventBus);


	  function compensateLaneDelete(shape, oldParent) {

	    var siblings = getChildLanes(oldParent);

	    var topAffected = [];
	    var bottomAffected = [];

	    eachElement(siblings, function(element) {

	      if (element.y > shape.y) {
	        bottomAffected.push(element);
	      } else {
	        topAffected.push(element);
	      }

	      return element.children;
	    });

	    if (!siblings.length) {
	      return;
	    }

	    var offset;

	    if (bottomAffected.length && topAffected.length) {
	      offset = shape.height / 2;
	    } else {
	      offset = shape.height;
	    }

	    var topAdjustments,
	        bottomAdjustments;

	    if (topAffected.length) {
	      topAdjustments = spaceTool.calculateAdjustments(
	        topAffected, 'y', offset, shape.y - 10);

	      spaceTool.makeSpace(
	        topAdjustments.movingShapes,
	        topAdjustments.resizingShapes,
	        { x: 0, y: offset }, 's');
	    }

	    if (bottomAffected.length) {
	      bottomAdjustments = spaceTool.calculateAdjustments(
	        bottomAffected, 'y', -offset, shape.y + shape.height + 10);

	      spaceTool.makeSpace(
	        bottomAdjustments.movingShapes,
	        bottomAdjustments.resizingShapes,
	        { x: 0, y: -offset }, 'n');
	    }
	  }


	  /**
	   * Adjust sizes of other lanes after lane deletion
	   */
	  this.postExecuted('shape.delete', LOW_PRIORITY$8, function(event) {

	    var context = event.context,
	        hints = context.hints,
	        shape = context.shape,
	        oldParent = context.oldParent;

	    // only compensate lane deletes
	    if (!is$1(shape, 'bpmn:Lane')) {
	      return;
	    }

	    // compensate root deletes only
	    if (hints && hints.nested) {
	      return;
	    }

	    compensateLaneDelete(shape, oldParent);
	  });
	}

	DeleteLaneBehavior.$inject = [
	  'eventBus',
	  'modeling',
	  'spaceTool'
	];

	inherits_browser(DeleteLaneBehavior, CommandInterceptor);

	/**
	 * BPMN specific detach event behavior
	 */
	function DetachEventBehavior(eventBus, bpmnReplace) {

	  CommandInterceptor.call(this, eventBus);

	  /**
	   * replace boundary event with intermediate event when
	   * detaching from a shape
	   */

	  this.preExecute('elements.move', function(context) {
	    var shapes = context.shapes,
	        host = context.newHost,
	        shape,
	        eventDefinition,
	        intermediateEvent,
	        newShape;

	    if (shapes.length !== 1) {
	      return;
	    }

	    shape = shapes[0];

	    if (!isLabel(shape) && !host && is$1(shape, 'bpmn:BoundaryEvent')) {

	      eventDefinition = getEventDefinition$1(shape);

	      if (eventDefinition) {
	        intermediateEvent = {
	          type: 'bpmn:IntermediateCatchEvent',
	          eventDefinitionType: eventDefinition.$type
	        };
	      } else {
	        intermediateEvent = {
	          type: 'bpmn:IntermediateThrowEvent'
	        };
	      }

	      newShape = bpmnReplace.replaceElement(shape, intermediateEvent, { layoutConnection: false });

	      context.shapes = [ newShape ];
	    }
	  }, true);
	}

	DetachEventBehavior.$inject = [
	  'eventBus',
	  'bpmnReplace'
	];

	inherits_browser(DetachEventBehavior, CommandInterceptor);



	// helper /////
	function getEventDefinition$1(element) {
	  var bo = getBusinessObject(element);

	  return bo && bo.eventDefinitions && bo.eventDefinitions[0];
	}

	function DropOnFlowBehavior(eventBus, bpmnRules, modeling) {

	  CommandInterceptor.call(this, eventBus);

	  /**
	   * Reconnect start / end of a connection after
	   * dropping an element on a flow.
	   */

	  function insertShape(shape, targetFlow, position) {
	    var waypoints = targetFlow.waypoints,
	        waypointsBefore,
	        waypointsAfter,
	        dockingPoint,
	        source,
	        target,
	        incomingConnection,
	        outgoingConnection,
	        oldOutgoing = shape.outgoing.slice(),
	        oldIncoming = shape.incoming.slice();

	    var intersection = getApproxIntersection(waypoints, position);

	    if (intersection) {
	      waypointsBefore = waypoints.slice(0, intersection.index);
	      waypointsAfter = waypoints.slice(intersection.index + (intersection.bendpoint ? 1 : 0));

	      // due to inaccuracy intersection might have been found
	      if (!waypointsBefore.length || !waypointsAfter.length) {
	        return;
	      }

	      dockingPoint = intersection.bendpoint ? waypoints[intersection.index] : position;

	      // if last waypointBefore is inside shape's bounds, ignore docking point
	      if (!isPointInsideBBox$2(shape, waypointsBefore[waypointsBefore.length-1])) {
	        waypointsBefore.push(copy(dockingPoint));
	      }

	      // if first waypointAfter is inside shape's bounds, ignore docking point
	      if (!isPointInsideBBox$2(shape, waypointsAfter[0])) {
	        waypointsAfter.unshift(copy(dockingPoint));
	      }
	    }

	    source = targetFlow.source;
	    target = targetFlow.target;

	    if (bpmnRules.canConnect(source, shape, targetFlow)) {
	      // reconnect source -> inserted shape
	      modeling.reconnectEnd(targetFlow, shape, waypointsBefore || position);

	      incomingConnection = targetFlow;
	    }

	    if (bpmnRules.canConnect(shape, target, targetFlow)) {

	      if (!incomingConnection) {
	        // reconnect inserted shape -> end
	        modeling.reconnectStart(targetFlow, shape, waypointsAfter || position);

	        outgoingConnection = targetFlow;
	      } else {
	        outgoingConnection = modeling.connect(
	          shape, target, { type: targetFlow.type, waypoints: waypointsAfter }
	        );
	      }
	    }

	    var duplicateConnections = [].concat(

	      incomingConnection && filter(oldIncoming, function(connection) {
	        return connection.source === incomingConnection.source;
	      }) || [],

	      outgoingConnection && filter(oldOutgoing, function(connection) {
	        return connection.source === outgoingConnection.source;
	      }) || []
	    );

	    if (duplicateConnections.length) {
	      modeling.removeElements(duplicateConnections);
	    }
	  }

	  this.preExecute('elements.move', function(context) {

	    var newParent = context.newParent,
	        shapes = context.shapes,
	        delta = context.delta,
	        shape = shapes[0];

	    if (!shape || !newParent) {
	      return;
	    }

	    // if the new parent is a connection,
	    // change it to the new parent's parent
	    if (newParent && newParent.waypoints) {
	      context.newParent = newParent = newParent.parent;
	    }

	    var shapeMid = getMid$1(shape);
	    var newShapeMid = {
	      x: shapeMid.x + delta.x,
	      y: shapeMid.y + delta.y
	    };

	    // find a connection which intersects with the
	    // element's mid point
	    var connection = find(newParent.children, function(element) {
	      var canInsert = bpmnRules.canInsert(shapes, element);

	      return canInsert && getApproxIntersection(element.waypoints, newShapeMid);
	    });

	    if (connection) {
	      context.targetFlow = connection;
	      context.position = newShapeMid;
	    }

	  }, true);

	  this.postExecuted('elements.move', function(context) {

	    var shapes = context.shapes,
	        targetFlow = context.targetFlow,
	        position = context.position;

	    if (targetFlow) {
	      insertShape(shapes[0], targetFlow, position);
	    }

	  }, true);

	  this.preExecute('shape.create', function(context) {

	    var parent = context.parent,
	        shape = context.shape;

	    if (bpmnRules.canInsert(shape, parent)) {
	      context.targetFlow = parent;
	      context.parent = parent.parent;
	    }
	  }, true);

	  this.postExecuted('shape.create', function(context) {

	    var shape = context.shape,
	        targetFlow = context.targetFlow,
	        position = context.position;

	    if (targetFlow) {
	      insertShape(shape, targetFlow, position);
	    }
	  }, true);
	}

	inherits_browser(DropOnFlowBehavior, CommandInterceptor);

	DropOnFlowBehavior.$inject = [
	  'eventBus',
	  'bpmnRules',
	  'modeling'
	];


	// helpers /////////////////////

	function isPointInsideBBox$2(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;
	}

	function copy(obj) {
	  return assign({}, obj);
	}

	function getMid$1(bounds) {

	  return {
	    x: Math.round(bounds.x + bounds.width / 2),
	    y: Math.round(bounds.y + bounds.height / 2)
	  };
	}

	function EventBasedGatewayBehavior(eventBus, modeling) {

	  CommandInterceptor.call(this, eventBus);

	  /**
	   * Remove existing sequence flows of event-based target before connecting
	   * from event-based gateway.
	   */
	  this.preExecuted('connection.create', function(event) {

	    var source = event.context.source,
	        target = event.context.target,
	        existingIncomingConnections = target.incoming.slice();

	    if (
	      is$1(source, 'bpmn:EventBasedGateway') &&
	      target.incoming.length
	    ) {

	      existingIncomingConnections.filter(isSequenceFlow)
	        .forEach(function(sequenceFlow) {
	          modeling.removeConnection(sequenceFlow);
	        });
	    }
	  });

	  /**
	   *  After replacing shape with event-based gateway, remove incoming sequence
	   *  flows of event-based targets which do not belong to event-based gateway
	   *  source.
	   */
	  this.preExecuted('shape.replace', function(event) {

	    var newShape = event.context.newShape,
	        newShapeTargets,
	        newShapeTargetsIncomingSequenceFlows;

	    if (!is$1(newShape, 'bpmn:EventBasedGateway')) {
	      return;
	    }

	    newShapeTargets = newShape.outgoing.filter(isSequenceFlow)
	      .map(function(sequenceFlow) {
	        return sequenceFlow.target;
	      });

	    newShapeTargetsIncomingSequenceFlows = newShapeTargets.reduce(function(sequenceFlows, target) {
	      var incomingSequenceFlows = target.incoming.filter(isSequenceFlow);

	      return sequenceFlows.concat(incomingSequenceFlows);
	    }, []);

	    newShapeTargetsIncomingSequenceFlows.forEach(function(sequenceFlow) {
	      if (sequenceFlow.source !== newShape) {
	        modeling.removeConnection(sequenceFlow);
	      }
	    });
	  });
	}

	EventBasedGatewayBehavior.$inject = [
	  'eventBus',
	  'modeling'
	];

	inherits_browser(EventBasedGatewayBehavior, CommandInterceptor);



	// helpers //////////////////////

	function isSequenceFlow(connection) {
	  return is$1(connection, 'bpmn:SequenceFlow');
	}

	/**
	 * BPMN specific Group behavior
	 */
	function GroupBehavior(eventBus, bpmnFactory, canvas, elementRegistry) {

	  CommandInterceptor.call(this, eventBus);

	  /**
	   * Gets process definitions
	   *
	   * @return {ModdleElement} definitions
	   */
	  function getDefinitions() {
	    var rootElement = canvas.getRootElement(),
	        businessObject = getBusinessObject(rootElement);

	    return businessObject.$parent;
	  }

	  /**
	   * Removes a referenced category value for a given group shape
	   *
	   * @param {djs.model.Shape} shape
	   */
	  function removeReferencedCategoryValue(shape) {

	    var businessObject = getBusinessObject(shape),
	        categoryValue = businessObject.categoryValueRef,
	        category = categoryValue.$parent;

	    if (!categoryValue) {
	      return;
	    }

	    remove$2(category.categoryValue, categoryValue);

	    // cleanup category if it is empty
	    if (category && !category.categoryValue.length) {
	      removeCategory(category);
	    }
	  }

	  /**
	   * Removes a given category from the definitions
	   *
	   * @param {ModdleElement} category
	   */
	  function removeCategory(category) {

	    var definitions = getDefinitions();

	    remove$2(definitions.get('rootElements'), category);
	  }

	  /**
	   * Returns all group element in the current registry
	   *
	   * @return {Array<djs.model.shape>} a list of group shapes
	   */
	  function getGroupElements() {
	    return elementRegistry.filter(function(e) {
	      return is$1(e, 'bpmn:Group');
	    });
	  }

	  /**
	   * Returns true if given categoryValue is referenced in one of the given elements
	   *
	   * @param {Array<djs.model.shape>} elements
	   * @param {ModdleElement} categoryValue
	   * @return {Boolean}
	   */
	  function isReferenced(elements, categoryValue) {
	    return elements.some(function(e) {

	      var businessObject = getBusinessObject(e);

	      return businessObject.categoryValueRef
	        && businessObject.categoryValueRef === categoryValue;
	    });
	  }

	  /**
	   * remove referenced category + value when group was deleted
	   */
	  this.executed('shape.delete', function(event) {

	    var context = event.context,
	        shape = context.shape;

	    if (is$1(shape, 'bpmn:Group')) {

	      var businessObject = getBusinessObject(shape),
	          categoryValueRef = businessObject.categoryValueRef,
	          groupElements = getGroupElements();

	      if (!isReferenced(groupElements, categoryValueRef)) {
	        removeReferencedCategoryValue(shape);
	      }
	    }
	  });

	  /**
	   * re-attach removed category
	   */
	  this.reverted('shape.delete', function(event) {

	    var context = event.context,
	        shape = context.shape;

	    if (is$1(shape, 'bpmn:Group')) {

	      var businessObject = getBusinessObject(shape),
	          categoryValueRef = businessObject.categoryValueRef,
	          definitions = getDefinitions(),
	          category = categoryValueRef ? categoryValueRef.$parent : null;

	      add$1(category.get('categoryValue'), categoryValueRef);
	      add$1(definitions.get('rootElements'), category);
	    }
	  });

	  /**
	   * create new category + value when group was created
	   */
	  this.execute('shape.create', function(event) {

	    var context = event.context,
	        shape = context.shape,
	        businessObject = getBusinessObject(shape),
	        oldBusinessObject = shape.oldBusinessObject;

	    if (is$1(businessObject, 'bpmn:Group') && !businessObject.categoryValueRef) {

	      var definitions = getDefinitions(),
	          categoryValue = createCategoryValue(definitions, bpmnFactory);

	      // set name from copied group if existing
	      if (oldBusinessObject && oldBusinessObject.categoryValueRef) {
	        categoryValue.value = oldBusinessObject.categoryValueRef.value;
	      }

	      // link the reference to the Group
	      businessObject.categoryValueRef = categoryValue;

	    }

	  });


	  this.revert('shape.create', function(event) {

	    var context = event.context,
	        shape = context.shape;

	    if (is$1(shape, 'bpmn:Group')) {

	      removeReferencedCategoryValue(shape);

	      delete getBusinessObject(shape).categoryValueRef;

	    }
	  });

	}

	GroupBehavior.$inject = [
	  'eventBus',
	  'bpmnFactory',
	  'canvas',
	  'elementRegistry'
	];

	inherits_browser(GroupBehavior, CommandInterceptor);

	/**
	 * Returns the intersection between two line segments a and b.
	 *
	 * @param {Point} l1s
	 * @param {Point} l1e
	 * @param {Point} l2s
	 * @param {Point} l2e
	 *
	 * @return {Point}
	 */
	function lineIntersect(l1s, l1e, l2s, l2e) {
	  // if the lines intersect, the result contains the x and y of the
	  // intersection (treating the lines as infinite) and booleans for
	  // whether line segment 1 or line segment 2 contain the point
	  var denominator, a, b, c, numerator;

	  denominator = ((l2e.y - l2s.y) * (l1e.x - l1s.x)) - ((l2e.x - l2s.x) * (l1e.y - l1s.y));

	  if (denominator == 0) {
	    return null;
	  }

	  a = l1s.y - l2s.y;
	  b = l1s.x - l2s.x;
	  numerator = ((l2e.x - l2s.x) * a) - ((l2e.y - l2s.y) * b);

	  c = numerator / denominator;

	  // if we cast these lines infinitely in
	  // both directions, they intersect here
	  return {
	    x: Math.round(l1s.x + (c * (l1e.x - l1s.x))),
	    y: Math.round(l1s.y + (c * (l1e.y - l1s.y)))
	  };
	}

	/**
	 * Fix broken dockings after DI imports.
	 *
	 * @param {EventBus} eventBus
	 */
	function ImportDockingFix(eventBus) {

	  function adjustDocking(startPoint, nextPoint, elementMid) {

	    var elementTop = {
	      x: elementMid.x,
	      y: elementMid.y - 50
	    };

	    var elementLeft = {
	      x: elementMid.x - 50,
	      y: elementMid.y
	    };

	    var verticalIntersect = lineIntersect(startPoint, nextPoint, elementMid, elementTop),
	        horizontalIntersect = lineIntersect(startPoint, nextPoint, elementMid, elementLeft);

	    // original is horizontal or vertical center cross intersection
	    var centerIntersect;

	    if (verticalIntersect && horizontalIntersect) {
	      if (getDistance(verticalIntersect, elementMid) > getDistance(horizontalIntersect, elementMid)) {
	        centerIntersect = horizontalIntersect;
	      } else {
	        centerIntersect = verticalIntersect;
	      }
	    } else {
	      centerIntersect = verticalIntersect || horizontalIntersect;
	    }

	    startPoint.original = centerIntersect;
	  }

	  function fixDockings(connection) {
	    var waypoints = connection.waypoints;

	    adjustDocking(
	      waypoints[0],
	      waypoints[1],
	      getMid(connection.source)
	    );

	    adjustDocking(
	      waypoints[waypoints.length - 1],
	      waypoints[waypoints.length - 2],
	      getMid(connection.target)
	    );
	  }

	  eventBus.on('bpmnElement.added', function(e) {

	    var element = e.element;

	    if (element.waypoints) {
	      fixDockings(element);
	    }
	  });
	}

	ImportDockingFix.$inject = [
	  'eventBus'
	];


	// helpers //////////////////////

	function getDistance(p1, p2) {
	  return Math.sqrt(Math.pow(p1.x - p2.x, 2) + Math.pow(p1.y - p2.y, 2));
	}

	/**
	 * A component that makes sure that each created or updated
	 * Pool and Lane is assigned an isHorizontal property set to true.
	 *
	 * @param {EventBus} eventBus
	 */
	function IsHorizontalFix(eventBus) {

	  CommandInterceptor.call(this, eventBus);

	  var elementTypesToUpdate = [
	    'bpmn:Participant',
	    'bpmn:Lane'
	  ];

	  this.executed([ 'shape.move', 'shape.create', 'shape.resize' ], function(event) {
	    var bo = getBusinessObject(event.context.shape);

	    if (isAny(bo, elementTypesToUpdate) && !bo.di.get('isHorizontal')) {
	      // set attribute directly to avoid modeling#updateProperty side effects
	      bo.di.set('isHorizontal', true);
	    }
	  });

	}

	IsHorizontalFix.$inject = [ 'eventBus' ];

	inherits_browser(IsHorizontalFix, CommandInterceptor);

	/**
	 * Returns the length of a vector
	 *
	 * @param {Vector}
	 * @return {Float}
	 */
	function vectorLength(v) {
	  return Math.sqrt(Math.pow(v.x, 2) + Math.pow(v.y, 2));
	}


	/**
	 * Calculates the angle between a line a the yAxis
	 *
	 * @param {Array}
	 * @return {Float}
	 */
	function getAngle(line) {
	  // return value is between 0, 180 and -180, -0
	  // @janstuemmel: maybe replace return a/b with b/a
	  return Math.atan((line[1].y - line[0].y) / (line[1].x - line[0].x));
	}


	/**
	 * Rotates a vector by a given angle
	 *
	 * @param {Vector}
	 * @param {Float} Angle in radians
	 * @return {Vector}
	 */
	function rotateVector(vector, angle) {
	  return (!angle) ? vector : {
	    x: Math.cos(angle) * vector.x - Math.sin(angle) * vector.y,
	    y: Math.sin(angle) * vector.x + Math.cos(angle) * vector.y
	  };
	}


	/**
	 * Solves a 2D equation system
	 * a + r*b = c, where a,b,c are 2D vectors
	 *
	 * @param {Vector}
	 * @param {Vector}
	 * @param {Vector}
	 * @return {Float}
	 */
	function solveLambaSystem(a, b, c) {

	  // the 2d system
	  var system = [
	    { n: a[0] - c[0], lambda: b[0] },
	    { n: a[1] - c[1], lambda: b[1] }
	  ];

	  // solve
	  var n = system[0].n * b[0] + system[1].n * b[1],
	      l = system[0].lambda * b[0] + system[1].lambda * b[1];

	  return -n/l;
	}


	/**
	 * Position of perpendicular foot
	 *
	 * @param {Point}
	 * @param [ {Point}, {Point} ] line defined throug two points
	 * @return {Point} the perpendicular foot position
	 */
	function perpendicularFoot(point, line) {

	  var a = line[0], b = line[1];

	  // relative position of b from a
	  var bd = { x: b.x - a.x, y: b.y - a.y };

	  // solve equation system to the parametrized vectors param real value
	  var r = solveLambaSystem([ a.x, a.y ], [ bd.x, bd.y ], [ point.x, point.y ]);

	  return { x: a.x + r*bd.x, y: a.y + r*bd.y };
	}


	/**
	 * Calculates the distance between a point and a line
	 *
	 * @param {Point}
	 * @param [ {Point}, {Point} ] line defined throug two points
	 * @return {Float} distance
	 */
	function getDistancePointLine(point, line) {

	  var pfPoint = perpendicularFoot(point, line);

	  // distance vector
	  var connectionVector = {
	    x: pfPoint.x - point.x,
	    y: pfPoint.y - point.y
	  };

	  return vectorLength(connectionVector);
	}


	/**
	 * Calculates the distance between two points
	 *
	 * @param {Point}
	 * @param {Point}
	 * @return {Float} distance
	 */
	function getDistancePointPoint(point1, point2) {

	  return vectorLength({
	    x: point1.x - point2.x,
	    y: point1.y - point2.y
	  });
	}

	var sqrt = Math.sqrt,
	    min$2 = Math.min,
	    max$3 = Math.max,
	    abs$4 = Math.abs;

	/**
	 * Calculate the square (power to two) of a number.
	 *
	 * @param {Number} n
	 *
	 * @return {Number}
	 */
	function sq(n) {
	  return Math.pow(n, 2);
	}

	/**
	 * Get distance between two points.
	 *
	 * @param {Point} p1
	 * @param {Point} p2
	 *
	 * @return {Number}
	 */
	function getDistance$1(p1, p2) {
	  return sqrt(sq(p1.x - p2.x) + sq(p1.y - p2.y));
	}

	/**
	 * Return the attachment of the given point on the specified line.
	 *
	 * The attachment is either a bendpoint (attached to the given point)
	 * or segment (attached to a location on a line segment) attachment:
	 *
	 * ```javascript
	 * var pointAttachment = {
	 *   type: 'bendpoint',
	 *   bendpointIndex: 3,
	 *   position: { x: 10, y: 10 } // the attach point on the line
	 * };
	 *
	 * var segmentAttachment = {
	 *   type: 'segment',
	 *   segmentIndex: 2,
	 *   relativeLocation: 0.31, // attach point location between 0 (at start) and 1 (at end)
	 *   position: { x: 10, y: 10 } // the attach point on the line
	 * };
	 * ```
	 *
	 * @param {Point} point
	 * @param {Array<Point>} line
	 *
	 * @return {Object} attachment
	 */
	function getAttachment(point, line) {

	  var idx = 0,
	      segmentStart,
	      segmentEnd,
	      segmentStartDistance,
	      segmentEndDistance,
	      attachmentPosition,
	      minDistance,
	      intersections,
	      attachment,
	      attachmentDistance,
	      closestAttachmentDistance,
	      closestAttachment;

	  for (idx = 0; idx < line.length - 1; idx++) {

	    segmentStart = line[idx];
	    segmentEnd = line[idx + 1];

	    if (pointsEqual(segmentStart, segmentEnd)) {
	      intersections = [ segmentStart ];
	    } else {
	      segmentStartDistance = getDistance$1(point, segmentStart);
	      segmentEndDistance = getDistance$1(point, segmentEnd);

	      minDistance = min$2(segmentStartDistance, segmentEndDistance);

	      intersections = getCircleSegmentIntersections(segmentStart, segmentEnd, point, minDistance);
	    }

	    if (intersections.length < 1) {
	      throw new Error('expected between [1, 2] circle -> line intersections');
	    }

	    // one intersection -> bendpoint attachment
	    if (intersections.length === 1) {
	      attachment = {
	        type: 'bendpoint',
	        position: intersections[0],
	        segmentIndex: idx,
	        bendpointIndex: pointsEqual(segmentStart, intersections[0]) ? idx : idx + 1
	      };
	    }

	    // two intersections -> segment attachment
	    if (intersections.length === 2) {

	      attachmentPosition = mid$1(intersections[0], intersections[1]);

	      attachment = {
	        type: 'segment',
	        position: attachmentPosition,
	        segmentIndex: idx,
	        relativeLocation: getDistance$1(segmentStart, attachmentPosition) / getDistance$1(segmentStart, segmentEnd)
	      };
	    }

	    attachmentDistance = getDistance$1(attachment.position, point);

	    if (!closestAttachment || closestAttachmentDistance > attachmentDistance) {
	      closestAttachment = attachment;
	      closestAttachmentDistance = attachmentDistance;
	    }
	  }

	  return closestAttachment;
	}

	/**
	 * Gets the intersection between a circle and a line segment.
	 *
	 * @param {Point} s1 segment start
	 * @param {Point} s2 segment end
	 * @param {Point} cc circle center
	 * @param {Number} cr circle radius
	 *
	 * @return {Array<Point>} intersections
	 */
	function getCircleSegmentIntersections(s1, s2, cc, cr) {

	  var baX = s2.x - s1.x;
	  var baY = s2.y - s1.y;
	  var caX = cc.x - s1.x;
	  var caY = cc.y - s1.y;

	  var a = baX * baX + baY * baY;
	  var bBy2 = baX * caX + baY * caY;
	  var c = caX * caX + caY * caY - cr * cr;

	  var pBy2 = bBy2 / a;
	  var q = c / a;

	  var disc = pBy2 * pBy2 - q;

	  // check against negative value to work around
	  // negative, very close to zero results (-4e-15)
	  // being produced in some environments
	  if (disc < 0 && disc > -0.000001) {
	    disc = 0;
	  }

	  if (disc < 0) {
	    return [];
	  }

	  // if disc == 0 ... dealt with later
	  var tmpSqrt = sqrt(disc);
	  var abScalingFactor1 = -pBy2 + tmpSqrt;
	  var abScalingFactor2 = -pBy2 - tmpSqrt;

	  var i1 = {
	    x: s1.x - baX * abScalingFactor1,
	    y: s1.y - baY * abScalingFactor1
	  };

	  if (disc === 0) { // abScalingFactor1 == abScalingFactor2
	    return [ i1 ];
	  }

	  var i2 = {
	    x: s1.x - baX * abScalingFactor2,
	    y: s1.y - baY * abScalingFactor2
	  };

	  // return only points on line segment
	  return [ i1, i2 ].filter(function(p) {
	    return isPointInSegment(p, s1, s2);
	  });
	}


	function isPointInSegment(p, segmentStart, segmentEnd) {
	  return (
	    fenced(p.x, segmentStart.x, segmentEnd.x) &&
	    fenced(p.y, segmentStart.y, segmentEnd.y)
	  );
	}

	function fenced(n, rangeStart, rangeEnd) {

	  // use matching threshold to work around
	  // precisison errors in intersection computation

	  return (
	    n >= min$2(rangeStart, rangeEnd) - EQUAL_THRESHOLD &&
	    n <= max$3(rangeStart, rangeEnd) + EQUAL_THRESHOLD
	  );
	}

	/**
	 * Calculate mid of two points.
	 *
	 * @param {Point} p1
	 * @param {Point} p2
	 *
	 * @return {Point}
	 */
	function mid$1(p1, p2) {

	  return {
	    x: (p1.x + p2.x) / 2,
	    y: (p1.y + p2.y) / 2
	  };
	}

	var EQUAL_THRESHOLD = 0.1;

	function pointsEqual(p1, p2) {

	  return (
	    abs$4(p1.x - p2.x) <= EQUAL_THRESHOLD &&
	    abs$4(p1.y - p2.y) <= EQUAL_THRESHOLD
	  );
	}

	function findNewLabelLineStartIndex(oldWaypoints, newWaypoints, attachment, hints) {

	  var index = attachment.segmentIndex;

	  var offset = newWaypoints.length - oldWaypoints.length;

	  // segmentMove happend
	  if (hints.segmentMove) {

	    var oldSegmentStartIndex = hints.segmentMove.segmentStartIndex,
	        newSegmentStartIndex = hints.segmentMove.newSegmentStartIndex;

	    // if label was on moved segment return new segment index
	    if (index === oldSegmentStartIndex) {
	      return newSegmentStartIndex;
	    }

	    // label is after new segment index
	    if (index >= newSegmentStartIndex) {
	      return (index+offset < newSegmentStartIndex) ? newSegmentStartIndex : index+offset;
	    }

	    // if label is before new segment index
	    return index;
	  }

	  // bendpointMove happend
	  if (hints.bendpointMove) {

	    var insert = hints.bendpointMove.insert,
	        bendpointIndex = hints.bendpointMove.bendpointIndex,
	        newIndex;

	    // waypoints length didnt change
	    if (offset === 0) {
	      return index;
	    }

	    // label behind new/removed bendpoint
	    if (index >= bendpointIndex) {
	      newIndex = insert ? index + 1 : index - 1;
	    }

	    // label before new/removed bendpoint
	    if (index < bendpointIndex) {

	      newIndex = index;

	      // decide label should take right or left segment
	      if (insert && attachment.type !== 'bendpoint' && bendpointIndex-1 === index) {

	        var rel = relativePositionMidWaypoint(newWaypoints, bendpointIndex);

	        if (rel < attachment.relativeLocation) {
	          newIndex++;
	        }
	      }
	    }

	    return newIndex;
	  }

	  // start/end changed
	  if (offset === 0) {
	    return index;
	  }

	  if (hints.connectionStart) {
	    return (index === 0) ? 0 : null;
	  }

	  if (hints.connectionEnd) {
	    return (index === oldWaypoints.length - 2) ? newWaypoints.length - 2 : null;
	  }

	  // if nothing fits, return null
	  return null;
	}


	/**
	 * Calculate the required adjustment (move delta) for the given label
	 * after the connection waypoints got updated.
	 *
	 * @param {djs.model.Label} label
	 * @param {Array<Point>} newWaypoints
	 * @param {Array<Point>} oldWaypoints
	 * @param {Object} hints
	 *
	 * @return {Point} delta
	 */
	function getLabelAdjustment(label, newWaypoints, oldWaypoints, hints) {

	  var x = 0,
	      y = 0;

	  var labelPosition = getLabelMid(label);

	  // get closest attachment
	  var attachment = getAttachment(labelPosition, oldWaypoints),
	      oldLabelLineIndex = attachment.segmentIndex,
	      newLabelLineIndex = findNewLabelLineStartIndex(oldWaypoints, newWaypoints, attachment, hints);

	  if (newLabelLineIndex === null) {
	    return { x: x, y: y };
	  }

	  // should never happen
	  // TODO(@janstuemmel): throw an error here when connectionSegmentMove is refactored
	  if (newLabelLineIndex < 0 ||
	      newLabelLineIndex > newWaypoints.length - 2) {
	    return { x: x, y: y };
	  }

	  var oldLabelLine = getLine(oldWaypoints, oldLabelLineIndex),
	      newLabelLine = getLine(newWaypoints, newLabelLineIndex),
	      oldFoot = attachment.position;

	  var relativeFootPosition = getRelativeFootPosition(oldLabelLine, oldFoot),
	      angleDelta = getAngleDelta(oldLabelLine, newLabelLine);

	  // special rule if label on bendpoint
	  if (attachment.type === 'bendpoint') {

	    var offset = newWaypoints.length - oldWaypoints.length,
	        oldBendpointIndex = attachment.bendpointIndex,
	        oldBendpoint = oldWaypoints[oldBendpointIndex];

	    // bendpoint position hasnt changed, return same position
	    if (newWaypoints.indexOf(oldBendpoint) !== -1) {
	      return { x: x, y: y };
	    }

	    // new bendpoint and old bendpoint have same index, then just return the offset
	    if (offset === 0) {
	      var newBendpoint = newWaypoints[oldBendpointIndex];

	      return {
	        x: newBendpoint.x - attachment.position.x,
	        y: newBendpoint.y - attachment.position.y
	      };
	    }

	    // if bendpoints get removed
	    if (offset < 0 && oldBendpointIndex !== 0 && oldBendpointIndex < oldWaypoints.length - 1) {
	      relativeFootPosition = relativePositionMidWaypoint(oldWaypoints, oldBendpointIndex);
	    }
	  }

	  var newFoot = {
	    x: (newLabelLine[1].x - newLabelLine[0].x) * relativeFootPosition + newLabelLine[0].x,
	    y: (newLabelLine[1].y - newLabelLine[0].y) * relativeFootPosition + newLabelLine[0].y
	  };

	  // the rotated vector to label
	  var newLabelVector = rotateVector({
	    x: labelPosition.x - oldFoot.x,
	    y: labelPosition.y - oldFoot.y
	  }, angleDelta);

	  // the new relative position
	  x = newFoot.x + newLabelVector.x - labelPosition.x;
	  y = newFoot.y + newLabelVector.y - labelPosition.y;

	  return roundPoint({
	    x: x,
	    y: y
	  });
	}


	// HELPERS //////////////////////

	function relativePositionMidWaypoint(waypoints, idx) {

	  var distanceSegment1 = getDistancePointPoint(waypoints[idx-1], waypoints[idx]),
	      distanceSegment2 = getDistancePointPoint(waypoints[idx], waypoints[idx+1]);

	  var relativePosition = distanceSegment1 / (distanceSegment1 + distanceSegment2);

	  return relativePosition;
	}

	function getLabelMid(label) {
	  return {
	    x: label.x + label.width / 2,
	    y: label.y + label.height / 2
	  };
	}

	function getAngleDelta(l1, l2) {
	  var a1 = getAngle(l1),
	      a2 = getAngle(l2);
	  return a2 - a1;
	}

	function getLine(waypoints, idx) {
	  return [ waypoints[idx], waypoints[idx+1] ];
	}

	function getRelativeFootPosition(line, foot) {

	  var length = getDistancePointPoint(line[0], line[1]),
	      lengthToFoot = getDistancePointPoint(line[0], foot);

	  return length === 0 ? 0 : lengthToFoot / length;
	}

	/**
	 * Calculates the absolute point relative to the new element's position
	 *
	 * @param {point} point [absolute]
	 * @param {bounds} oldBounds
	 * @param {bounds} newBounds
	 *
	 * @return {point} point [absolute]
	 */
	function getNewAttachPoint(point, oldBounds, newBounds) {
	  var oldCenter = center(oldBounds),
	      newCenter = center(newBounds),
	      oldDelta = delta(point, oldCenter);

	  var newDelta = {
	    x: oldDelta.x * (newBounds.width / oldBounds.width),
	    y: oldDelta.y * (newBounds.height / oldBounds.height)
	  };

	  return roundPoint({
	    x: newCenter.x + newDelta.x,
	    y: newCenter.y + newDelta.y
	  });
	}


	/**
	 * Calculates the shape's delta relative to a new position
	 * of a certain element's bounds
	 *
	 * @param {djs.model.Shape} point [absolute]
	 * @param {bounds} oldBounds
	 * @param {bounds} newBounds
	 *
	 * @return {delta} delta
	 */
	function getNewAttachShapeDelta(shape, oldBounds, newBounds) {
	  var shapeCenter = center(shape),
	      oldCenter = center(oldBounds),
	      newCenter = center(newBounds),
	      shapeDelta = delta(shape, shapeCenter),
	      oldCenterDelta = delta(shapeCenter, oldCenter);

	  var newCenterDelta = {
	    x: oldCenterDelta.x * (newBounds.width / oldBounds.width),
	    y: oldCenterDelta.y * (newBounds.height / oldBounds.height)
	  };

	  var newShapeCenter = {
	    x: newCenter.x + newCenterDelta.x,
	    y: newCenter.y + newCenterDelta.y
	  };

	  return roundPoint({
	    x: newShapeCenter.x + shapeDelta.x - shape.x,
	    y: newShapeCenter.y + shapeDelta.y - shape.y
	  });
	}

	var DEFAULT_LABEL_DIMENSIONS = {
	  width: 90,
	  height: 20
	};

	var NAME_PROPERTY = 'name';
	var TEXT_PROPERTY = 'text';

	/**
	 * A component that makes sure that external labels are added
	 * together with respective elements and properly updated (DI wise)
	 * during move.
	 *
	 * @param {EventBus} eventBus
	 * @param {Modeling} modeling
	 * @param {BpmnFactory} bpmnFactory
	 * @param {TextRenderer} textRenderer
	 */
	function LabelBehavior(
	    eventBus, modeling, bpmnFactory,
	    textRenderer) {

	  CommandInterceptor.call(this, eventBus);

	  // update label if name property was updated
	  this.postExecute('element.updateProperties', function(e) {
	    var context = e.context,
	        element = context.element,
	        properties = context.properties;

	    if (NAME_PROPERTY in properties) {
	      modeling.updateLabel(element, properties[NAME_PROPERTY]);
	    }

	    if (TEXT_PROPERTY in properties
	        && is$1(element, 'bpmn:TextAnnotation')) {

	      var newBounds = textRenderer.getTextAnnotationBounds(
	        {
	          x: element.x,
	          y: element.y,
	          width: element.width,
	          height: element.height
	        },
	        properties[TEXT_PROPERTY] || ''
	      );

	      modeling.updateLabel(element, properties.text, newBounds);
	    }
	  });

	  // create label shape after shape/connection was created
	  this.postExecute([ 'shape.create', 'connection.create' ], function(e) {
	    var context = e.context;

	    var element = context.shape || context.connection,
	        businessObject = element.businessObject;

	    if (!isLabelExternal(element)) {
	      return;
	    }

	    // only create label if attribute available
	    if (!getLabel(element)) {
	      return;
	    }

	    var labelCenter = getExternalLabelMid(element);

	    // we don't care about x and y
	    var labelDimensions = textRenderer.getExternalLabelBounds(
	      DEFAULT_LABEL_DIMENSIONS,
	      getLabel(element)
	    );

	    modeling.createLabel(element, labelCenter, {
	      id: businessObject.id + '_label',
	      businessObject: businessObject,
	      width: labelDimensions.width,
	      height: labelDimensions.height
	    });
	  });

	  // update label after label shape was deleted
	  this.postExecute('shape.delete', function(event) {
	    var context = event.context,
	        labelTarget = context.labelTarget,
	        hints = context.hints || {};

	    // check if label
	    if (labelTarget && hints.unsetLabel !== false) {
	      modeling.updateLabel(labelTarget, null, null, { removeShape: false });
	    }
	  });

	  // update di information on label creation
	  this.postExecute([ 'label.create' ], function(event) {

	    var context = event.context,
	        element = context.shape,
	        businessObject,
	        di;

	    // we want to trigger on real labels only
	    if (!element.labelTarget) {
	      return;
	    }

	    // we want to trigger on BPMN elements only
	    if (!is$1(element.labelTarget || element, 'bpmn:BaseElement')) {
	      return;
	    }

	    businessObject = element.businessObject,
	    di = businessObject.di;


	    if (!di.label) {
	      di.label = bpmnFactory.create('bpmndi:BPMNLabel', {
	        bounds: bpmnFactory.create('dc:Bounds')
	      });
	    }

	    assign(di.label.bounds, {
	      x: element.x,
	      y: element.y,
	      width: element.width,
	      height: element.height
	    });
	  });

	  function getVisibleLabelAdjustment(event) {

	    var context = event.context,
	        connection = context.connection,
	        label = connection.label,
	        hints = assign({}, context.hints),
	        newWaypoints = context.newWaypoints || connection.waypoints,
	        oldWaypoints = context.oldWaypoints;


	    if (typeof hints.startChanged === 'undefined') {
	      hints.startChanged = !!hints.connectionStart;
	    }

	    if (typeof hints.endChanged === 'undefined') {
	      hints.endChanged = !!hints.connectionEnd;
	    }

	    return getLabelAdjustment(label, newWaypoints, oldWaypoints, hints);
	  }

	  this.postExecute([
	    'connection.layout',
	    'connection.updateWaypoints'
	  ], function(event) {

	    var label = event.context.connection.label,
	        labelAdjustment;

	    if (!label) {
	      return;
	    }

	    labelAdjustment = getVisibleLabelAdjustment(event);

	    modeling.moveShape(label, labelAdjustment);
	  });


	  // keep label position on shape replace
	  this.postExecute([ 'shape.replace' ], function(event) {
	    var context = event.context,
	        newShape = context.newShape,
	        oldShape = context.oldShape;

	    var businessObject = getBusinessObject(newShape);

	    if (businessObject
	      && isLabelExternal(businessObject)
	      && oldShape.label
	      && newShape.label) {
	      newShape.label.x = oldShape.label.x;
	      newShape.label.y = oldShape.label.y;
	    }
	  });


	  // move external label after resizing
	  this.postExecute('shape.resize', function(event) {

	    var context = event.context,
	        shape = context.shape,
	        newBounds = context.newBounds,
	        oldBounds = context.oldBounds;

	    if (hasExternalLabel(shape)) {

	      var label = shape.label,
	          labelMid = getMid(label),
	          edges = asEdges(oldBounds);

	      // get nearest border point to label as reference point
	      var referencePoint = getReferencePoint(labelMid, edges);

	      var delta = getReferencePointDelta(referencePoint, oldBounds, newBounds);

	      modeling.moveShape(label, delta);

	    }

	  });

	}

	inherits_browser(LabelBehavior, CommandInterceptor);

	LabelBehavior.$inject = [
	  'eventBus',
	  'modeling',
	  'bpmnFactory',
	  'textRenderer'
	];

	// helpers //////////////////////

	/**
	 * Calculates a reference point delta relative to a new position
	 * of a certain element's bounds
	 *
	 * @param {Point} point
	 * @param {Bounds} oldBounds
	 * @param {Bounds} newBounds
	 *
	 * @return {Delta} delta
	 */
	function getReferencePointDelta(referencePoint, oldBounds, newBounds) {

	  var newReferencePoint = getNewAttachPoint(referencePoint, oldBounds, newBounds);

	  return roundPoint(delta(newReferencePoint, referencePoint));
	}

	/**
	 * Generates the nearest point (reference point) for a given point
	 * onto given set of lines
	 *
	 * @param {Array<Point, Point>} lines
	 * @param {Point} point
	 *
	 * @param {Point}
	 */
	function getReferencePoint(point, lines) {

	  if (!lines.length) {
	    return;
	  }

	  var nearestLine = getNearestLine(point, lines);

	  return perpendicularFoot(point, nearestLine);
	}

	/**
	 * Convert the given bounds to a lines array containing all edges
	 *
	 * @param {Bounds|Point} bounds
	 *
	 * @return Array<Point>
	 */
	function asEdges(bounds) {
	  return [
	    [ // top
	      {
	        x: bounds.x,
	        y: bounds.y
	      },
	      {
	        x: bounds.x + (bounds.width || 0),
	        y: bounds.y
	      }
	    ],
	    [ // right
	      {
	        x: bounds.x + (bounds.width || 0),
	        y: bounds.y
	      },
	      {
	        x: bounds.x + (bounds.width || 0),
	        y: bounds.y + (bounds.height || 0)
	      }
	    ],
	    [ // bottom
	      {
	        x: bounds.x,
	        y: bounds.y + (bounds.height || 0)
	      },
	      {
	        x: bounds.x + (bounds.width || 0),
	        y: bounds.y + (bounds.height || 0)
	      }
	    ],
	    [ // left
	      {
	        x: bounds.x,
	        y: bounds.y
	      },
	      {
	        x: bounds.x,
	        y: bounds.y + (bounds.height || 0)
	      }
	    ]
	  ];
	}

	/**
	 * Returns the nearest line for a given point by distance
	 * @param {Point} point
	 * @param Array<Point> lines
	 *
	 * @return Array<Point>
	 */
	function getNearestLine(point, lines) {

	  var distances = lines.map(function(l) {
	    return {
	      line: l,
	      distance: getDistancePointLine(point, l)
	    };
	  });

	  var sorted = sortBy(distances, 'distance');

	  return sorted[0].line;
	}

	var COLLAB_ERR_MSG = 'flow elements must be children of pools/participants',
	    PROCESS_ERR_MSG = 'participants cannot be pasted onto a non-empty process diagram';


	function ModelingFeedback(eventBus, tooltips, translate) {

	  function showError(position, message, timeout) {
	    tooltips.add({
	      position: {
	        x: position.x + 5,
	        y: position.y + 5
	      },
	      type: 'error',
	      timeout: timeout || 2000,
	      html: '<div>' + message + '</div>'
	    });
	  }

	  eventBus.on([ 'shape.move.rejected', 'create.rejected' ], function(event) {
	    var context = event.context,
	        shape = context.shape,
	        target = context.target;

	    if (is$1(target, 'bpmn:Collaboration') && is$1(shape, 'bpmn:FlowNode')) {
	      showError(event, translate(COLLAB_ERR_MSG));
	    }
	  });

	  eventBus.on([ 'elements.paste.rejected' ], function(event) {
	    var context = event.context,
	        position = context.position,
	        target = context.target;

	    if (is$1(target, 'bpmn:Collaboration')) {
	      showError(position, translate(COLLAB_ERR_MSG));
	    }

	    if (is$1(target, 'bpmn:Process')) {
	      showError(position, translate(PROCESS_ERR_MSG), 3000);
	    }
	  });
	}

	ModelingFeedback.$inject = [
	  'eventBus',
	  'tooltips',
	  'translate'
	];

	function ReplaceConnectionBehavior(eventBus, modeling, bpmnRules, injector) {

	  CommandInterceptor.call(this, eventBus);

	  var dragging = injector.get('dragging', false);

	  function fixConnection(connection) {

	    var source = connection.source,
	        target = connection.target,
	        parent = connection.parent;

	    // do not do anything if connection
	    // is already deleted (may happen due to other
	    // behaviors plugged-in before)
	    if (!parent) {
	      return;
	    }

	    var replacementType,
	        remove;

	    /**
	     * Check if incoming or outgoing connections
	     * can stay or could be substituted with an
	     * appropriate replacement.
	     *
	     * This holds true for SequenceFlow <> MessageFlow.
	     */

	    if (is$1(connection, 'bpmn:SequenceFlow')) {
	      if (!bpmnRules.canConnectSequenceFlow(source, target)) {
	        remove = true;
	      }

	      if (bpmnRules.canConnectMessageFlow(source, target)) {
	        replacementType = 'bpmn:MessageFlow';
	      }
	    }

	    // transform message flows into sequence flows, if possible

	    if (is$1(connection, 'bpmn:MessageFlow')) {

	      if (!bpmnRules.canConnectMessageFlow(source, target)) {
	        remove = true;
	      }

	      if (bpmnRules.canConnectSequenceFlow(source, target)) {
	        replacementType = 'bpmn:SequenceFlow';
	      }
	    }

	    if (is$1(connection, 'bpmn:Association') && !bpmnRules.canConnectAssociation(source, target)) {
	      remove = true;
	    }


	    // remove invalid connection,
	    // unless it has been removed already
	    if (remove) {
	      modeling.removeConnection(connection);
	    }

	    // replace SequenceFlow <> MessageFlow

	    if (replacementType) {
	      modeling.connect(source, target, {
	        type: replacementType,
	        waypoints: connection.waypoints.slice()
	      });
	    }
	  }

	  function replaceReconnectedConnection(event) {

	    var context = event.context,
	        connection = context.connection,
	        source = context.newSource || connection.source,
	        target = context.newTarget || connection.target,
	        allowed,
	        replacement;

	    allowed = bpmnRules.canConnect(source, target);

	    if (!allowed || allowed.type === connection.type) {
	      return;
	    }

	    replacement = modeling.connect(source, target, {
	      type: allowed.type,
	      waypoints: connection.waypoints.slice()
	    });

	    // remove old connection
	    modeling.removeConnection(connection);

	    // replace connection in context to reconnect end/start
	    context.connection = replacement;

	    if (dragging) {
	      cleanDraggingSelection(connection, replacement);
	    }
	  }

	  // monkey-patch selection saved in dragging in order to not re-select non-existing connection
	  function cleanDraggingSelection(oldConnection, newConnection) {
	    var context = dragging.context(),
	        previousSelection = context && context.payload.previousSelection,
	        index;

	    // do nothing if not dragging or no selection was present
	    if (!previousSelection || !previousSelection.length) {
	      return;
	    }

	    index = previousSelection.indexOf(oldConnection);

	    if (index === -1) {
	      return;
	    }

	    previousSelection.splice(index, 1, newConnection);
	  }

	  // lifecycle hooks

	  this.postExecuted('elements.move', function(context) {

	    var closure = context.closure,
	        allConnections = closure.allConnections;

	    forEach(allConnections, fixConnection);
	  }, true);

	  this.preExecute([
	    'connection.reconnectStart',
	    'connection.reconnectEnd'
	  ], replaceReconnectedConnection);

	  this.postExecuted('element.updateProperties', function(event) {
	    var context = event.context,
	        properties = context.properties,
	        element = context.element,
	        businessObject = element.businessObject,
	        connection;

	    // remove condition expression when morphing to default flow
	    if (properties.default) {
	      connection = find(
	        element.outgoing,
	        matchPattern({ id: element.businessObject.default.id })
	      );

	      if (connection) {
	        modeling.updateProperties(connection, { conditionExpression: undefined });
	      }
	    }

	    // remove default property from source when morphing to conditional flow
	    if (properties.conditionExpression && businessObject.sourceRef.default === businessObject) {
	      modeling.updateProperties(element.source, { default: undefined });
	    }
	  });
	}

	inherits_browser(ReplaceConnectionBehavior, CommandInterceptor);

	ReplaceConnectionBehavior.$inject = [
	  'eventBus',
	  'modeling',
	  'bpmnRules',
	  'injector'
	];

	/**
	 * BPMN specific remove behavior
	 */
	function RemoveParticipantBehavior(eventBus, modeling) {

	  CommandInterceptor.call(this, eventBus);


	  /**
	   * morph collaboration diagram into process diagram
	   * after the last participant has been removed
	   */

	  this.preExecute('shape.delete', function(context) {

	    var shape = context.shape,
	        parent = shape.parent;

	    // activate the behavior if the shape to be removed
	    // is a participant
	    if (is$1(shape, 'bpmn:Participant')) {
	      context.collaborationRoot = parent;
	    }
	  }, true);

	  this.postExecute('shape.delete', function(context) {

	    var collaborationRoot = context.collaborationRoot;

	    if (collaborationRoot && !collaborationRoot.businessObject.participants.length) {
	      // replace empty collaboration with process diagram
	      modeling.makeProcess();
	    }
	  }, true);

	}

	RemoveParticipantBehavior.$inject = [ 'eventBus', 'modeling' ];

	inherits_browser(RemoveParticipantBehavior, CommandInterceptor);

	/**
	 * Defines the behaviour of what happens to the elements inside a container
	 * that morphs into another BPMN element
	 */
	function ReplaceElementBehaviour(
	    eventBus, bpmnReplace, bpmnRules,
	    elementRegistry, selection, modeling) {

	  CommandInterceptor.call(this, eventBus);

	  this._bpmnReplace = bpmnReplace;
	  this._elementRegistry = elementRegistry;
	  this._selection = selection;
	  this._modeling = modeling;

	  this.postExecuted([ 'elements.move' ], 500, function(event) {

	    var context = event.context,
	        target = context.newParent,
	        newHost = context.newHost,
	        elements = [];

	    forEach(context.closure.topLevel, function(topLevelElements) {
	      if (isEventSubProcess(topLevelElements)) {
	        elements = elements.concat(topLevelElements.children);
	      } else {
	        elements = elements.concat(topLevelElements);
	      }
	    });

	    // Change target to host when the moving element is a `bpmn:BoundaryEvent`
	    if (elements.length === 1 && newHost) {
	      target = newHost;
	    }

	    var canReplace = bpmnRules.canReplace(elements, target);

	    if (canReplace) {
	      this.replaceElements(elements, canReplace.replacements, newHost);
	    }
	  }, this);

	  // update attachments if the host is replaced
	  this.postExecute([ 'shape.replace' ], 1500, function(e) {

	    var context = e.context,
	        oldShape = context.oldShape,
	        newShape = context.newShape,
	        attachers = oldShape.attachers,
	        canReplace;

	    if (attachers && attachers.length) {
	      canReplace = bpmnRules.canReplace(attachers, newShape);

	      this.replaceElements(attachers, canReplace.replacements);
	    }

	  }, this);

	  this.postExecuted([ 'shape.replace' ], 1500, function(e) {
	    var context = e.context,
	        oldShape = context.oldShape,
	        newShape = context.newShape;

	    modeling.unclaimId(oldShape.businessObject.id, oldShape.businessObject);
	    modeling.updateProperties(newShape, { id: oldShape.id });
	  });
	}

	inherits_browser(ReplaceElementBehaviour, CommandInterceptor);


	ReplaceElementBehaviour.prototype.replaceElements = function(elements, newElements, newHost) {
	  var elementRegistry = this._elementRegistry,
	      bpmnReplace = this._bpmnReplace,
	      selection = this._selection,
	      modeling = this._modeling;

	  forEach(newElements, function(replacement) {

	    var newElement = {
	      type: replacement.newElementType
	    };

	    var oldElement = elementRegistry.get(replacement.oldElementId);

	    if (newHost && is$1(oldElement, 'bpmn:BoundaryEvent')) {
	      modeling.updateAttachment(oldElement, null);
	    }

	    var idx = elements.indexOf(oldElement);

	    elements[idx] = bpmnReplace.replaceElement(oldElement, newElement, { select: false });

	    if (newHost && is$1(elements[idx], 'bpmn:BoundaryEvent')) {
	      modeling.updateAttachment(elements[idx], newHost);
	    }
	  });

	  if (newElements) {
	    selection.select(elements);
	  }
	};

	ReplaceElementBehaviour.$inject = [
	  'eventBus',
	  'bpmnReplace',
	  'bpmnRules',
	  'elementRegistry',
	  'selection',
	  'modeling'
	];

	var abs$5 = Math.abs,
	    min$3 = Math.min,
	    max$4 = Math.max;


	function addToTrbl(trbl, attr, value, choice) {
	  var current = trbl[attr];

	  // make sure to set the value if it does not exist
	  // or apply the correct value by comparing against
	  // choice(value, currentValue)
	  trbl[attr] = current === undefined ? value : choice(value, current);
	}

	function addMin(trbl, attr, value) {
	  return addToTrbl(trbl, attr, value, min$3);
	}

	function addMax(trbl, attr, value) {
	  return addToTrbl(trbl, attr, value, max$4);
	}

	var LANE_MIN_HEIGHT = 60,
	    LANE_MIN_WIDTH = 300,
	    LANE_RIGHT_PADDING = 20,
	    LANE_LEFT_PADDING = 50,
	    LANE_TOP_PADDING = 20,
	    LANE_BOTTOM_PADDING = 20;


	function getParticipantResizeConstraints(laneShape, resizeDirection, balanced) {
	  var lanesRoot = getLanesRoot(laneShape);

	  var isFirst = true,
	      isLast = true;

	  // max top/bottom size for lanes
	  var allLanes = collectLanes(lanesRoot, [ lanesRoot ]);

	  var laneTrbl = asTRBL(laneShape);

	  var maxTrbl = {},
	      minTrbl = {};

	  if (/e/.test(resizeDirection)) {
	    minTrbl.right = laneTrbl.left + LANE_MIN_WIDTH;
	  } else
	  if (/w/.test(resizeDirection)) {
	    minTrbl.left = laneTrbl.right - LANE_MIN_WIDTH;
	  }

	  allLanes.forEach(function(other) {

	    var otherTrbl = asTRBL(other);

	    if (/n/.test(resizeDirection)) {

	      if (otherTrbl.top < (laneTrbl.top - 10)) {
	        isFirst = false;
	      }

	      // max top size (based on next element)
	      if (balanced && abs$5(laneTrbl.top - otherTrbl.bottom) < 10) {
	        addMax(maxTrbl, 'top', otherTrbl.top + LANE_MIN_HEIGHT);
	      }

	      // min top size (based on self or nested element)
	      if (abs$5(laneTrbl.top - otherTrbl.top) < 5) {
	        addMin(minTrbl, 'top', otherTrbl.bottom - LANE_MIN_HEIGHT);
	      }
	    }

	    if (/s/.test(resizeDirection)) {

	      if (otherTrbl.bottom > (laneTrbl.bottom + 10)) {
	        isLast = false;
	      }

	      // max bottom size (based on previous element)
	      if (balanced && abs$5(laneTrbl.bottom - otherTrbl.top) < 10) {
	        addMin(maxTrbl, 'bottom', otherTrbl.bottom - LANE_MIN_HEIGHT);
	      }

	      // min bottom size (based on self or nested element)
	      if (abs$5(laneTrbl.bottom - otherTrbl.bottom) < 5) {
	        addMax(minTrbl, 'bottom', otherTrbl.top + LANE_MIN_HEIGHT);
	      }
	    }
	  });

	  // max top/bottom/left/right size based on flow nodes
	  var flowElements = lanesRoot.children.filter(function(s) {
	    return !s.hidden && !s.waypoints && (is$1(s, 'bpmn:FlowElement') || is$1(s, 'bpmn:Artifact'));
	  });

	  flowElements.forEach(function(flowElement) {

	    var flowElementTrbl = asTRBL(flowElement);

	    if (isFirst && /n/.test(resizeDirection)) {
	      addMin(minTrbl, 'top', flowElementTrbl.top - LANE_TOP_PADDING);
	    }

	    if (/e/.test(resizeDirection)) {
	      addMax(minTrbl, 'right', flowElementTrbl.right + LANE_RIGHT_PADDING);
	    }

	    if (isLast && /s/.test(resizeDirection)) {
	      addMax(minTrbl, 'bottom', flowElementTrbl.bottom + LANE_BOTTOM_PADDING);
	    }

	    if (/w/.test(resizeDirection)) {
	      addMin(minTrbl, 'left', flowElementTrbl.left - LANE_LEFT_PADDING);
	    }
	  });

	  return {
	    min: minTrbl,
	    max: maxTrbl
	  };
	}

	var HIGH_PRIORITY$8 = 1500;

	var PARTICIPANT_MIN_DIMENSIONS = { width: 300, height: 150 },
	    SUB_PROCESS_MIN_DIMENSIONS = { width: 140, height: 120 },
	    TEXT_ANNOTATION_MIN_DIMENSIONS = { width: 50, height: 30 };


	/**
	 * Set minimum bounds/resize constraints on resize.
	 *
	 * @param {EventBus} eventBus
	 */
	function ResizeBehavior$1(eventBus) {
	  eventBus.on('resize.start', HIGH_PRIORITY$8, function(event) {
	    var context = event.context,
	        shape = context.shape,
	        direction = context.direction,
	        balanced = context.balanced;

	    if (is$1(shape, 'bpmn:Lane') || is$1(shape, 'bpmn:Participant')) {
	      context.resizeConstraints = getParticipantResizeConstraints(shape, direction, balanced);
	    }

	    if (is$1(shape, 'bpmn:Participant')) {
	      context.minDimensions = PARTICIPANT_MIN_DIMENSIONS;
	    }

	    if (is$1(shape, 'bpmn:SubProcess') && isExpanded(shape)) {
	      context.minDimensions = SUB_PROCESS_MIN_DIMENSIONS;
	    }

	    if (is$1(shape, 'bpmn:TextAnnotation')) {
	      context.minDimensions = TEXT_ANNOTATION_MIN_DIMENSIONS;
	    }
	  });
	}

	ResizeBehavior$1.$inject = [ 'eventBus' ];

	var SLIGHTLY_HIGHER_PRIORITY = 1001;


	/**
	 * Invoke {@link Modeling#resizeLane} instead of
	 * {@link Modeling#resizeShape} when resizing a Lane
	 * or Participant shape.
	 */
	function ResizeLaneBehavior(eventBus, modeling) {

	  eventBus.on('resize.start', SLIGHTLY_HIGHER_PRIORITY + 500, function(event) {
	    var context = event.context,
	        shape = context.shape;

	    if (is$1(shape, 'bpmn:Lane') || is$1(shape, 'bpmn:Participant')) {

	      // should we resize the opposite lane(s) in
	      // order to compensate for the resize operation?
	      context.balanced = !hasPrimaryModifier(event);
	    }
	  });

	  /**
	   * Intercept resize end and call resize lane function instead.
	   */
	  eventBus.on('resize.end', SLIGHTLY_HIGHER_PRIORITY, function(event) {
	    var context = event.context,
	        shape = context.shape,
	        canExecute = context.canExecute,
	        newBounds = context.newBounds;

	    if (is$1(shape, 'bpmn:Lane') || is$1(shape, 'bpmn:Participant')) {

	      if (canExecute) {
	        // ensure we have actual pixel values for new bounds
	        // (important when zoom level was > 1 during move)
	        newBounds = roundBounds(newBounds);

	        // perform the actual resize
	        modeling.resizeLane(shape, newBounds, context.balanced);
	      }

	      // stop propagation
	      return false;
	    }
	  });
	}

	ResizeLaneBehavior.$inject = [
	  'eventBus',
	  'modeling'
	];

	function RemoveElementBehavior(eventBus, bpmnRules, modeling) {

	  CommandInterceptor.call(this, eventBus);

	  /**
	   * Combine sequence flows when deleting an element
	   * if there is one incoming and one outgoing
	   * sequence flow
	   */
	  this.preExecute('shape.delete', function(e) {

	    var shape = e.context.shape;

	    // only handle [a] -> [shape] -> [b] patterns
	    if (shape.incoming.length !== 1 || shape.outgoing.length !== 1) {
	      return;
	    }

	    var inConnection = shape.incoming[0],
	        outConnection = shape.outgoing[0];

	    // only handle sequence flows
	    if (!is$1(inConnection, 'bpmn:SequenceFlow') || !is$1(outConnection, 'bpmn:SequenceFlow')) {
	      return;
	    }

	    if (bpmnRules.canConnect(inConnection.source, outConnection.target, inConnection)) {

	      // compute new, combined waypoints
	      var newWaypoints = getNewWaypoints(inConnection.waypoints, outConnection.waypoints);

	      modeling.reconnectEnd(inConnection, outConnection.target, newWaypoints);
	    }
	  });

	}

	inherits_browser(RemoveElementBehavior, CommandInterceptor);

	RemoveElementBehavior.$inject = [
	  'eventBus',
	  'bpmnRules',
	  'modeling'
	];


	// helpers //////////////////////

	function getDocking$1(point) {
	  return point.original || point;
	}


	function getNewWaypoints(inWaypoints, outWaypoints) {

	  var intersection = lineIntersect(
	    getDocking$1(inWaypoints[inWaypoints.length - 2]),
	    getDocking$1(inWaypoints[inWaypoints.length - 1]),
	    getDocking$1(outWaypoints[1]),
	    getDocking$1(outWaypoints[0]));

	  if (intersection) {
	    return [].concat(
	      inWaypoints.slice(0, inWaypoints.length - 1),
	      [ intersection ],
	      outWaypoints.slice(1));
	  } else {
	    return [
	      getDocking$1(inWaypoints[0]),
	      getDocking$1(outWaypoints[outWaypoints.length - 1])
	    ];
	  }
	}

	/**
	 * Add start event child by default when creating an expanded subprocess
	 * with create.start or replacing a task with an expanded subprocess.
	 */
	function SubProcessStartEventBehavior(eventBus, modeling) {
	  CommandInterceptor.call(this, eventBus);

	  eventBus.on('create.start', function(event) {
	    var shape = event.context.shape,
	        hints = event.context.hints;

	    hints.shouldAddStartEvent = is$1(shape, 'bpmn:SubProcess') && isExpanded(shape);
	  });

	  this.postExecuted('shape.create', function(event) {
	    var shape = event.context.shape,
	        hints = event.context.hints,
	        position;

	    if (!hints.shouldAddStartEvent) {
	      return;
	    }

	    position = calculatePositionRelativeToShape(shape);

	    modeling.createShape({ type: 'bpmn:StartEvent' }, position, shape);
	  });

	  this.postExecuted('shape.replace', function(event) {
	    var oldShape = event.context.oldShape,
	        newShape = event.context.newShape,
	        position;

	    if (
	      !is$1(newShape, 'bpmn:SubProcess') ||
	      !is$1(oldShape, 'bpmn:Task') ||
	      !isExpanded(newShape)
	    ) {
	      return;
	    }

	    position = calculatePositionRelativeToShape(newShape);

	    modeling.createShape({ type: 'bpmn:StartEvent' }, position, newShape);
	  });
	}

	SubProcessStartEventBehavior.$inject = [
	  'eventBus',
	  'modeling'
	];

	inherits_browser(SubProcessStartEventBehavior, CommandInterceptor);

	// helpers //////////

	function calculatePositionRelativeToShape(shape) {
	  return {
	    x: shape.x + shape.width / 6,
	    y: shape.y + shape.height / 2
	  };
	}

	var LOW_PRIORITY$9 = 500;


	function ToggleElementCollapseBehaviour(
	    eventBus, elementFactory, modeling,
	    resize) {

	  CommandInterceptor.call(this, eventBus);


	  function hideEmptyLables(children) {
	    if (children.length) {
	      children.forEach(function(child) {
	        if (child.type === 'label' && !child.businessObject.name) {
	          child.hidden = true;
	        }
	      });
	    }
	  }

	  function expandedBounds(shape, defaultSize) {
	    var children = shape.children,
	        newBounds = defaultSize,
	        visibleElements,
	        visibleBBox;

	    visibleElements = filterVisible(children).concat([ shape ]);

	    visibleBBox = computeChildrenBBox(visibleElements);

	    if (visibleBBox) {
	      // center to visibleBBox with max(defaultSize, childrenBounds)
	      newBounds.width = Math.max(visibleBBox.width, newBounds.width);
	      newBounds.height = Math.max(visibleBBox.height, newBounds.height);

	      newBounds.x = visibleBBox.x + (visibleBBox.width - newBounds.width) / 2;
	      newBounds.y = visibleBBox.y + (visibleBBox.height - newBounds.height) / 2;
	    } else {
	      // center to collapsed shape with defaultSize
	      newBounds.x = shape.x + (shape.width - newBounds.width) / 2;
	      newBounds.y = shape.y + (shape.height - newBounds.height) / 2;
	    }

	    return newBounds;
	  }

	  function collapsedBounds(shape, defaultSize) {

	    return {
	      x: shape.x + (shape.width - defaultSize.width) / 2,
	      y: shape.y + (shape.height - defaultSize.height) / 2,
	      width: defaultSize.width,
	      height: defaultSize.height
	    };
	  }

	  this.executed([ 'shape.toggleCollapse' ], LOW_PRIORITY$9, function(e) {

	    var context = e.context,
	        shape = context.shape;

	    if (!is$1(shape, 'bpmn:SubProcess')) {
	      return;
	    }

	    if (!shape.collapsed) {
	      // all children got made visible through djs, hide empty labels
	      hideEmptyLables(shape.children);

	      // remove collapsed marker
	      getBusinessObject(shape).di.isExpanded = true;
	    } else {
	      // place collapsed marker
	      getBusinessObject(shape).di.isExpanded = false;
	    }
	  });

	  this.reverted([ 'shape.toggleCollapse' ], LOW_PRIORITY$9, function(e) {

	    var context = e.context;
	    var shape = context.shape;


	    // revert removing/placing collapsed marker
	    if (!shape.collapsed) {
	      getBusinessObject(shape).di.isExpanded = true;

	    } else {
	      getBusinessObject(shape).di.isExpanded = false;
	    }
	  });

	  this.postExecuted([ 'shape.toggleCollapse' ], LOW_PRIORITY$9, function(e) {
	    var shape = e.context.shape,
	        defaultSize = elementFactory._getDefaultSize(shape),
	        newBounds;

	    if (shape.collapsed) {

	      // resize to default size of collapsed shapes
	      newBounds = collapsedBounds(shape, defaultSize);
	    } else {

	      // resize to bounds of max(visible children, defaultSize)
	      newBounds = expandedBounds(shape, defaultSize);
	    }

	    modeling.resizeShape(shape, newBounds, null, {
	      autoResize: shape.collapsed ? false : 'nwse'
	    });
	  });

	}


	inherits_browser(ToggleElementCollapseBehaviour, CommandInterceptor);

	ToggleElementCollapseBehaviour.$inject = [
	  'eventBus',
	  'elementFactory',
	  'modeling'
	];


	// helpers //////////////////////

	function filterVisible(elements) {
	  return elements.filter(function(e) {
	    return !e.hidden;
	  });
	}

	/**
	 * Unclaims model IDs on element deletion.
	 *
	 * @param {EventBus} eventBus
	 * @param {Modeling} modeling
	 */
	function UnclaimIdBehavior(eventBus, modeling) {

	  CommandInterceptor.call(this, eventBus);

	  this.preExecute('elements.delete', function(event) {
	    var context = event.context,
	        elements = context.elements;

	    forEach(elements, function(element) {
	      modeling.unclaimId(element.businessObject.id, element.businessObject);
	    });

	  });
	}

	inherits_browser(UnclaimIdBehavior, CommandInterceptor);

	UnclaimIdBehavior.$inject = [ 'eventBus', 'modeling' ];

	var LOW_PRIORITY$a = 500,
	    HIGH_PRIORITY$9 = 5000;


	/**
	 * BPMN specific delete lane behavior
	 */
	function UpdateFlowNodeRefsBehavior(eventBus, modeling, translate) {

	  CommandInterceptor.call(this, eventBus);

	  /**
	   * Ok, this is it:
	   *
	   * We have to update the Lane#flowNodeRefs _and_
	   * FlowNode#lanes with every FlowNode move/resize and
	   * Lane move/resize.
	   *
	   * We want to group that stuff to recompute containments
	   * as efficient as possible.
	   *
	   * Yea!
	   */

	  // the update context
	  var context;


	  function initContext() {
	    context = context || new UpdateContext();
	    context.enter();

	    return context;
	  }

	  function getContext() {
	    if (!context) {
	      throw new Error(translate('out of bounds release'));
	    }

	    return context;
	  }

	  function releaseContext() {

	    if (!context) {
	      throw new Error(translate('out of bounds release'));
	    }

	    var triggerUpdate = context.leave();

	    if (triggerUpdate) {
	      modeling.updateLaneRefs(context.flowNodes, context.lanes);

	      context = null;
	    }

	    return triggerUpdate;
	  }


	  var laneRefUpdateEvents = [
	    'spaceTool',
	    'lane.add',
	    'lane.resize',
	    'lane.split',
	    'elements.move',
	    'elements.delete',
	    'shape.create',
	    'shape.delete',
	    'shape.move',
	    'shape.resize'
	  ];


	  // listen to a lot of stuff to group lane updates

	  this.preExecute(laneRefUpdateEvents, HIGH_PRIORITY$9, function(event) {
	    initContext();
	  });

	  this.postExecuted(laneRefUpdateEvents, LOW_PRIORITY$a, function(event) {
	    releaseContext();
	  });


	  // Mark flow nodes + lanes that need an update

	  this.preExecute([
	    'shape.create',
	    'shape.move',
	    'shape.delete',
	    'shape.resize'
	  ], function(event) {

	    var context = event.context,
	        shape = context.shape;

	    var updateContext = getContext();

	    // no need to update labels
	    if (shape.labelTarget) {
	      return;
	    }

	    if (is$1(shape, 'bpmn:Lane')) {
	      updateContext.addLane(shape);
	    }

	    if (is$1(shape, 'bpmn:FlowNode')) {
	      updateContext.addFlowNode(shape);
	    }
	  });
	}

	UpdateFlowNodeRefsBehavior.$inject = [
	  'eventBus',
	  'modeling' ,
	  'translate'
	];

	inherits_browser(UpdateFlowNodeRefsBehavior, CommandInterceptor);


	function UpdateContext() {

	  this.flowNodes = [];
	  this.lanes = [];

	  this.counter = 0;

	  this.addLane = function(lane) {
	    this.lanes.push(lane);
	  };

	  this.addFlowNode = function(flowNode) {
	    this.flowNodes.push(flowNode);
	  };

	  this.enter = function() {
	    this.counter++;
	  };

	  this.leave = function() {
	    this.counter--;

	    return !this.counter;
	  };
	}

	/**
	 * A behavior that unsets the Default property of
	 * sequence flow source on element delete, if the
	 * removed element is the Gateway or Task's default flow.
	 *
	 * @param {EventBus} eventBus
	 * @param {Modeling} modeling
	 */
	function DeleteSequenceFlowBehavior(eventBus, modeling) {

	  CommandInterceptor.call(this, eventBus);


	  this.preExecute('connection.delete', function(event) {
	    var context = event.context,
	        connection = context.connection,
	        source = connection.source;

	    if (isDefaultFlow(connection, source)) {
	      modeling.updateProperties(source, {
	        'default': null
	      });
	    }
	  });
	}

	inherits_browser(DeleteSequenceFlowBehavior, CommandInterceptor);

	DeleteSequenceFlowBehavior.$inject = [
	  'eventBus',
	  'modeling'
	];


	// helpers //////////////////////

	function isDefaultFlow(connection, source) {

	  if (!is$1(connection, 'bpmn:SequenceFlow')) {
	    return false;
	  }

	  var sourceBo = getBusinessObject(source),
	      sequenceFlow = getBusinessObject(connection);

	  return sourceBo.get('default') === sequenceFlow;
	}

	var BehaviorModule = {
	  __init__: [
	    'adaptiveLabelPositioningBehavior',
	    'appendBehavior',
	    'attachEventBehavior',
	    'boundaryEventBehavior',
	    'copyPasteBehavior',
	    'fixHoverBehavior',
	    'createBoundaryEventBehavior',
	    'createDataObjectBehavior',
	    'createParticipantBehavior',
	    'dataStoreBehavior',
	    'dataInputAssociationBehavior',
	    'deleteLaneBehavior',
	    'detachEventBehavior',
	    'dropOnFlowBehavior',
	    'eventBasedGatewayBehavior',
	    'groupBehavior',
	    'importDockingFix',
	    'isHorizontalFix',
	    'labelBehavior',
	    'modelingFeedback',
	    'removeElementBehavior',
	    'removeParticipantBehavior',
	    'replaceConnectionBehavior',
	    'replaceElementBehaviour',
	    'resizeBehavior',
	    'resizeLaneBehavior',
	    'toggleElementCollapseBehaviour',
	    'subProcessStartEventBehavior',
	    'unclaimIdBehavior',
	    'unsetDefaultFlowBehavior',
	    'updateFlowNodeRefsBehavior'
	  ],
	  adaptiveLabelPositioningBehavior: [ 'type', AdaptiveLabelPositioningBehavior ],
	  appendBehavior: [ 'type', AppendBehavior ],
	  attachEventBehavior: [ 'type', AttachEventBehavior ],
	  boundaryEventBehavior: [ 'type', BoundaryEventBehavior ],
	  copyPasteBehavior: [ 'type', CopyPasteBehavior ],
	  fixHoverBehavior: [ 'type', FixHoverBehavior ],
	  createBoundaryEventBehavior: [ 'type', CreateBoundaryEventBehavior ],
	  createDataObjectBehavior: [ 'type', CreateDataObjectBehavior ],
	  createParticipantBehavior: [ 'type', CreateParticipantBehavior$1 ],
	  dataInputAssociationBehavior: [ 'type', DataInputAssociationBehavior ],
	  dataStoreBehavior: [ 'type', DataStoreBehavior ],
	  deleteLaneBehavior: [ 'type', DeleteLaneBehavior ],
	  detachEventBehavior: [ 'type', DetachEventBehavior ],
	  dropOnFlowBehavior: [ 'type', DropOnFlowBehavior ],
	  eventBasedGatewayBehavior: [ 'type', EventBasedGatewayBehavior ],
	  groupBehavior: [ 'type', GroupBehavior ],
	  importDockingFix: [ 'type', ImportDockingFix ],
	  isHorizontalFix: [ 'type', IsHorizontalFix ],
	  labelBehavior: [ 'type', LabelBehavior ],
	  modelingFeedback: [ 'type', ModelingFeedback ],
	  replaceConnectionBehavior: [ 'type', ReplaceConnectionBehavior ],
	  removeParticipantBehavior: [ 'type', RemoveParticipantBehavior ],
	  replaceElementBehaviour: [ 'type', ReplaceElementBehaviour ],
	  resizeBehavior: [ 'type', ResizeBehavior$1 ],
	  resizeLaneBehavior: [ 'type', ResizeLaneBehavior ],
	  removeElementBehavior: [ 'type', RemoveElementBehavior ],
	  toggleElementCollapseBehaviour : [ 'type', ToggleElementCollapseBehaviour ],
	  subProcessStartEventBehavior: [ 'type', SubProcessStartEventBehavior ],
	  unclaimIdBehavior: [ 'type', UnclaimIdBehavior ],
	  updateFlowNodeRefsBehavior: [ 'type', UpdateFlowNodeRefsBehavior ],
	  unsetDefaultFlowBehavior: [ 'type', DeleteSequenceFlowBehavior ]
	};

	function getBoundaryAttachment(position, targetBounds) {

	  var orientation = getOrientation(position, targetBounds, -15);

	  if (orientation !== 'intersect') {
	    return orientation;
	  } else {
	    return null;
	  }
	}

	/**
	 * BPMN specific modeling rule
	 */
	function BpmnRules(eventBus) {
	  RuleProvider.call(this, eventBus);
	}

	inherits_browser(BpmnRules, RuleProvider);

	BpmnRules.$inject = [ 'eventBus' ];

	BpmnRules.prototype.init = function() {

	  this.addRule('connection.start', function(context) {
	    var source = context.source;

	    return canStartConnection(source);
	  });

	  this.addRule('connection.create', function(context) {
	    var source = context.source,
	        target = context.target,
	        hints = context.hints || {},
	        targetParent = hints.targetParent,
	        targetAttach = hints.targetAttach;

	    // don't allow incoming connections on
	    // newly created boundary events
	    // to boundary events
	    if (targetAttach) {
	      return false;
	    }

	    // temporarily set target parent for scoping
	    // checks to work
	    if (targetParent) {
	      target.parent = targetParent;
	    }

	    try {
	      return canConnect(source, target);
	    } finally {
	      // unset temporary target parent
	      if (targetParent) {
	        target.parent = null;
	      }
	    }
	  });

	  this.addRule('connection.reconnectStart', function(context) {

	    var connection = context.connection,
	        source = context.hover || context.source,
	        target = connection.target;

	    return canConnect(source, target, connection);
	  });

	  this.addRule('connection.reconnectEnd', function(context) {

	    var connection = context.connection,
	        source = connection.source,
	        target = context.hover || context.target;

	    return canConnect(source, target, connection);
	  });

	  this.addRule('connection.updateWaypoints', function(context) {
	    return {
	      type: context.connection.type
	    };
	  });

	  this.addRule('shape.resize', function(context) {

	    var shape = context.shape,
	        newBounds = context.newBounds;

	    return canResize(shape, newBounds);
	  });

	  this.addRule('elements.move', function(context) {

	    var target = context.target,
	        shapes = context.shapes,
	        position = context.position;

	    return canAttach(shapes, target, null, position) ||
	           canReplace(shapes, target, position) ||
	           canMove(shapes, target) ||
	           canInsert(shapes, target);
	  });

	  this.addRule('shape.create', function(context) {
	    return canCreate(
	      context.shape,
	      context.target,
	      context.source,
	      context.position
	    );
	  });

	  this.addRule('shape.attach', function(context) {

	    return canAttach(
	      context.shape,
	      context.target,
	      null,
	      context.position
	    );
	  });

	  this.addRule('element.copy', function(context) {
	    var collection = context.collection,
	        element = context.element;

	    return canCopy(collection, element);
	  });

	  this.addRule('element.paste', function(context) {
	    var parent = context.parent,
	        element = context.element,
	        position = context.position,
	        source = context.source,
	        target = context.target;

	    if (source || target) {
	      return canConnect(source, target);
	    }

	    return canAttach([ element ], parent, null, position) || canCreate(element, parent, null);
	  });

	  this.addRule('elements.paste', function(context) {
	    var tree = context.tree,
	        target = context.target;

	    return canPaste(tree, target);
	  });
	};

	BpmnRules.prototype.canConnectMessageFlow = canConnectMessageFlow;

	BpmnRules.prototype.canConnectSequenceFlow = canConnectSequenceFlow;

	BpmnRules.prototype.canConnectDataAssociation = canConnectDataAssociation;

	BpmnRules.prototype.canConnectAssociation = canConnectAssociation;

	BpmnRules.prototype.canMove = canMove;

	BpmnRules.prototype.canAttach = canAttach;

	BpmnRules.prototype.canReplace = canReplace;

	BpmnRules.prototype.canDrop = canDrop;

	BpmnRules.prototype.canInsert = canInsert;

	BpmnRules.prototype.canCreate = canCreate;

	BpmnRules.prototype.canConnect = canConnect;

	BpmnRules.prototype.canResize = canResize;

	BpmnRules.prototype.canCopy = canCopy;

	/**
	 * Utility functions for rule checking
	 */

	/**
	 * Checks if given element can be used for starting connection.
	 *
	 * @param  {Element} source
	 * @return {Boolean}
	 */
	function canStartConnection(element) {
	  if (nonExistingOrLabel(element)) {
	    return null;
	  }

	  return isAny(element, [
	    'bpmn:FlowNode',
	    'bpmn:InteractionNode',
	    'bpmn:DataObjectReference',
	    'bpmn:DataStoreReference'
	  ]);
	}

	function nonExistingOrLabel(element) {
	  return !element || isLabel(element);
	}

	function isSame(a, b) {
	  return a === b;
	}

	function getOrganizationalParent(element) {

	  do {
	    if (is$1(element, 'bpmn:Process')) {
	      return getBusinessObject(element);
	    }

	    if (is$1(element, 'bpmn:Participant')) {
	      return (
	        getBusinessObject(element).processRef ||
	        getBusinessObject(element)
	      );
	    }
	  } while ((element = element.parent));

	}

	function isTextAnnotation(element) {
	  return is$1(element, 'bpmn:TextAnnotation');
	}

	function isGroup(element) {
	  return is$1(element, 'bpmn:Group') && !element.labelTarget;
	}

	function isCompensationBoundary(element) {
	  return is$1(element, 'bpmn:BoundaryEvent') &&
	         hasEventDefinition$2(element, 'bpmn:CompensateEventDefinition');
	}

	function isForCompensation(e) {
	  return getBusinessObject(e).isForCompensation;
	}

	function isSameOrganization(a, b) {
	  var parentA = getOrganizationalParent(a),
	      parentB = getOrganizationalParent(b);

	  return parentA === parentB;
	}

	function isMessageFlowSource(element) {
	  return (
	    is$1(element, 'bpmn:InteractionNode') && (
	      !is$1(element, 'bpmn:Event') || (
	        is$1(element, 'bpmn:ThrowEvent') &&
	        hasEventDefinitionOrNone(element, 'bpmn:MessageEventDefinition')
	      )
	    )
	  );
	}

	function isMessageFlowTarget(element) {
	  return (
	    is$1(element, 'bpmn:InteractionNode') &&
	    !isForCompensation(element) && (
	      !is$1(element, 'bpmn:Event') || (
	        is$1(element, 'bpmn:CatchEvent') &&
	        hasEventDefinitionOrNone(element, 'bpmn:MessageEventDefinition')
	      )
	    )
	  );
	}

	function getScopeParent(element) {

	  var parent = element;

	  while ((parent = parent.parent)) {

	    if (is$1(parent, 'bpmn:FlowElementsContainer')) {
	      return getBusinessObject(parent);
	    }

	    if (is$1(parent, 'bpmn:Participant')) {
	      return getBusinessObject(parent).processRef;
	    }
	  }

	  return null;
	}

	function isSameScope(a, b) {
	  var scopeParentA = getScopeParent(a),
	      scopeParentB = getScopeParent(b);

	  return scopeParentA && (scopeParentA === scopeParentB);
	}

	function hasEventDefinition$2(element, eventDefinition) {
	  var bo = getBusinessObject(element);

	  return !!find(bo.eventDefinitions || [], function(definition) {
	    return is$1(definition, eventDefinition);
	  });
	}

	function hasEventDefinitionOrNone(element, eventDefinition) {
	  var bo = getBusinessObject(element);

	  return (bo.eventDefinitions || []).every(function(definition) {
	    return is$1(definition, eventDefinition);
	  });
	}

	function isSequenceFlowSource(element) {
	  return (
	    is$1(element, 'bpmn:FlowNode') &&
	    !is$1(element, 'bpmn:EndEvent') &&
	    !isEventSubProcess(element) &&
	    !(is$1(element, 'bpmn:IntermediateThrowEvent') &&
	      hasEventDefinition$2(element, 'bpmn:LinkEventDefinition')
	    ) &&
	    !isCompensationBoundary(element) &&
	    !isForCompensation(element)
	  );
	}

	function isSequenceFlowTarget(element) {
	  return (
	    is$1(element, 'bpmn:FlowNode') &&
	    !is$1(element, 'bpmn:StartEvent') &&
	    !is$1(element, 'bpmn:BoundaryEvent') &&
	    !isEventSubProcess(element) &&
	    !(is$1(element, 'bpmn:IntermediateCatchEvent') &&
	      hasEventDefinition$2(element, 'bpmn:LinkEventDefinition')
	    ) &&
	    !isForCompensation(element)
	  );
	}

	function isEventBasedTarget(element) {
	  return (
	    is$1(element, 'bpmn:ReceiveTask') || (
	      is$1(element, 'bpmn:IntermediateCatchEvent') && (
	        hasEventDefinition$2(element, 'bpmn:MessageEventDefinition') ||
	        hasEventDefinition$2(element, 'bpmn:TimerEventDefinition') ||
	        hasEventDefinition$2(element, 'bpmn:ConditionalEventDefinition') ||
	        hasEventDefinition$2(element, 'bpmn:SignalEventDefinition')
	      )
	    )
	  );
	}

	function isConnection$1(element) {
	  return element.waypoints;
	}

	function getParents(element) {

	  var parents = [];

	  while (element) {
	    element = element.parent;

	    if (element) {
	      parents.push(element);
	    }
	  }

	  return parents;
	}

	function isParent(possibleParent, element) {
	  var allParents = getParents(element);
	  return allParents.indexOf(possibleParent) !== -1;
	}

	function canConnect(source, target, connection) {

	  if (nonExistingOrLabel(source) || nonExistingOrLabel(target)) {
	    return null;
	  }

	  if (!is$1(connection, 'bpmn:DataAssociation')) {

	    if (canConnectMessageFlow(source, target)) {
	      return { type: 'bpmn:MessageFlow' };
	    }

	    if (canConnectSequenceFlow(source, target)) {
	      return { type: 'bpmn:SequenceFlow' };
	    }
	  }

	  var connectDataAssociation = canConnectDataAssociation(source, target);

	  if (connectDataAssociation) {
	    return connectDataAssociation;
	  }

	  if (isCompensationBoundary(source) && isForCompensation(target)) {
	    return {
	      type: 'bpmn:Association',
	      associationDirection: 'One'
	    };
	  }

	  if (canConnectAssociation(source, target)) {

	    return {
	      type: 'bpmn:Association'
	    };
	  }

	  return false;
	}

	/**
	 * Can an element be dropped into the target element
	 *
	 * @return {Boolean}
	 */
	function canDrop(element, target, position) {

	  // can move labels and groups everywhere
	  if (isLabel(element) || isGroup(element)) {
	    return true;
	  }


	  // disallow to create elements on collapsed pools
	  if (is$1(target, 'bpmn:Participant') && !isExpanded(target)) {
	    return false;
	  }

	  // allow to create new participants on
	  // existing collaboration and process diagrams
	  if (is$1(element, 'bpmn:Participant')) {
	    return is$1(target, 'bpmn:Process') || is$1(target, 'bpmn:Collaboration');
	  }

	  // allow moving DataInput / DataOutput within its original container only
	  if (isAny(element, [ 'bpmn:DataInput', 'bpmn:DataOutput' ])) {

	    if (element.parent) {
	      return target === element.parent;
	    }
	  }

	  // allow creating lanes on participants and other lanes only
	  if (is$1(element, 'bpmn:Lane')) {
	    return is$1(target, 'bpmn:Participant') || is$1(target, 'bpmn:Lane');
	  }

	  // disallow dropping boundary events which cannot replace with intermediate event
	  if (is$1(element, 'bpmn:BoundaryEvent') && !isDroppableBoundaryEvent(element)) {
	    return false;
	  }

	  // drop flow elements onto flow element containers
	  // and participants
	  if (is$1(element, 'bpmn:FlowElement') && !is$1(element, 'bpmn:DataStoreReference')) {
	    if (is$1(target, 'bpmn:FlowElementsContainer')) {
	      return isExpanded(target);
	    }

	    return isAny(target, [ 'bpmn:Participant', 'bpmn:Lane' ]);
	  }

	  // account for the fact that data associations are always
	  // rendered and moved to top (Process or Collaboration level)
	  //
	  // artifacts may be placed wherever, too
	  if (isAny(element, [ 'bpmn:Artifact', 'bpmn:DataAssociation', 'bpmn:DataStoreReference' ])) {
	    return isAny(target, [
	      'bpmn:Collaboration',
	      'bpmn:Lane',
	      'bpmn:Participant',
	      'bpmn:Process',
	      'bpmn:SubProcess' ]);
	  }

	  if (is$1(element, 'bpmn:MessageFlow')) {
	    return is$1(target, 'bpmn:Collaboration')
	      || element.source.parent == target
	      || element.target.parent == target;
	  }

	  return false;
	}

	function isDroppableBoundaryEvent(event) {
	  return getBusinessObject(event).cancelActivity && (
	    hasNoEventDefinition(event) || hasCommonBoundaryIntermediateEventDefinition(event)
	  );
	}

	function canPaste(tree, target) {
	  var topLevel = tree[0],
	      participants;

	  if (is$1(target, 'bpmn:Collaboration')) {
	    return every(topLevel, function(e) {
	      return e.type === 'bpmn:Participant';
	    });
	  }

	  if (is$1(target, 'bpmn:Process')) {
	    participants = some(topLevel, function(e) {
	      return e.type === 'bpmn:Participant';
	    });

	    return !(participants && target.children.length > 0);
	  }

	  // disallow to create elements on collapsed pools
	  if (is$1(target, 'bpmn:Participant') && !isExpanded(target)) {
	    return false;
	  }

	  if (is$1(target, 'bpmn:FlowElementsContainer')) {
	    return isExpanded(target);
	  }

	  return isAny(target, [
	    'bpmn:Collaboration',
	    'bpmn:Lane',
	    'bpmn:Participant',
	    'bpmn:Process',
	    'bpmn:SubProcess' ]);
	}

	function isBoundaryEvent(element) {
	  return !isLabel(element) && is$1(element, 'bpmn:BoundaryEvent');
	}

	function isLane(element) {
	  return is$1(element, 'bpmn:Lane');
	}

	/**
	 * We treat IntermediateThrowEvents as boundary events during create,
	 * this must be reflected in the rules.
	 */
	function isBoundaryCandidate(element) {
	  if (isBoundaryEvent(element)) {
	    return true;
	  }

	  if (is$1(element, 'bpmn:IntermediateThrowEvent') && hasNoEventDefinition(element)) {
	    return true;
	  }

	  return (
	    is$1(element, 'bpmn:IntermediateCatchEvent') &&
	    hasCommonBoundaryIntermediateEventDefinition(element)
	  );
	}

	function hasNoEventDefinition(element) {
	  var bo = getBusinessObject(element);

	  return bo && !(bo.eventDefinitions && bo.eventDefinitions.length);
	}

	function hasCommonBoundaryIntermediateEventDefinition(element) {
	  return hasOneOfEventDefinitions(element, [
	    'bpmn:MessageEventDefinition',
	    'bpmn:TimerEventDefinition',
	    'bpmn:SignalEventDefinition',
	    'bpmn:ConditionalEventDefinition'
	  ]);
	}

	function hasOneOfEventDefinitions(element, eventDefinitions) {
	  return eventDefinitions.some(function(definition) {
	    return hasEventDefinition$2(element, definition);
	  });
	}

	function isReceiveTaskAfterEventBasedGateway(element) {
	  return (
	    is$1(element, 'bpmn:ReceiveTask') &&
	    find(element.incoming, function(incoming) {
	      return is$1(incoming.source, 'bpmn:EventBasedGateway');
	    })
	  );
	}


	function canAttach(elements, target, source, position) {

	  if (!Array.isArray(elements)) {
	    elements = [ elements ];
	  }

	  // only (re-)attach one element at a time
	  if (elements.length !== 1) {
	    return false;
	  }

	  var element = elements[0];

	  // do not attach labels
	  if (isLabel(element)) {
	    return false;
	  }

	  // only handle boundary events
	  if (!isBoundaryCandidate(element)) {
	    return false;
	  }

	  // disallow drop on event sub processes
	  if (isEventSubProcess(target)) {
	    return false;
	  }

	  // only allow drop on non compensation activities
	  if (!is$1(target, 'bpmn:Activity') || isForCompensation(target)) {
	    return false;
	  }

	  // only attach to subprocess border
	  if (position && !getBoundaryAttachment(position, target)) {
	    return false;
	  }

	  // do not attach on receive tasks after event based gateways
	  if (isReceiveTaskAfterEventBasedGateway(target)) {
	    return false;
	  }

	  return 'attach';
	}


	/**
	 * Defines how to replace elements for a given target.
	 *
	 * Returns an array containing all elements which will be replaced.
	 *
	 * @example
	 *
	 *  [{ id: 'IntermediateEvent_2',
	 *     type: 'bpmn:StartEvent'
	 *   },
	 *   { id: 'IntermediateEvent_5',
	 *     type: 'bpmn:EndEvent'
	 *   }]
	 *
	 * @param  {Array} elements
	 * @param  {Object} target
	 *
	 * @return {Object} an object containing all elements which have to be replaced
	 */
	function canReplace(elements, target, position) {

	  if (!target) {
	    return false;
	  }

	  var canExecute = {
	    replacements: []
	  };

	  forEach(elements, function(element) {

	    if (!isEventSubProcess(target)) {

	      if (is$1(element, 'bpmn:StartEvent') &&
	          element.type !== 'label' &&
	          canDrop(element, target)) {

	        // replace a non-interrupting start event by a blank interrupting start event
	        // when the target is not an event sub process
	        if (!isInterrupting(element)) {
	          canExecute.replacements.push({
	            oldElementId: element.id,
	            newElementType: 'bpmn:StartEvent'
	          });
	        }

	        // replace an error/escalation/compansate start event by a blank interrupting start event
	        // when the target is not an event sub process
	        if (hasErrorEventDefinition(element) ||
	            hasEscalationEventDefinition(element) ||
	            hasCompensateEventDefinition(element)) {
	          canExecute.replacements.push({
	            oldElementId: element.id,
	            newElementType: 'bpmn:StartEvent'
	          });
	        }
	      }
	    }

	    if (!is$1(target, 'bpmn:Transaction')) {
	      if (hasEventDefinition$2(element, 'bpmn:CancelEventDefinition') &&
	          element.type !== 'label') {

	        if (is$1(element, 'bpmn:EndEvent') && canDrop(element, target)) {
	          canExecute.replacements.push({
	            oldElementId: element.id,
	            newElementType: 'bpmn:EndEvent'
	          });
	        }

	        if (is$1(element, 'bpmn:BoundaryEvent') && canAttach(element, target, null, position)) {
	          canExecute.replacements.push({
	            oldElementId: element.id,
	            newElementType: 'bpmn:BoundaryEvent'
	          });
	        }
	      }
	    }
	  });

	  return canExecute.replacements.length ? canExecute : false;
	}

	function canMove(elements, target) {

	  // do not move selection containing lanes
	  if (some(elements, isLane)) {
	    return false;
	  }

	  // allow default move check to start move operation
	  if (!target) {
	    return true;
	  }

	  return elements.every(function(element) {
	    return canDrop(element, target);
	  });
	}

	function canCreate(shape, target, source, position) {

	  if (!target) {
	    return false;
	  }

	  if (isLabel(shape) || isGroup(shape)) {
	    return true;
	  }

	  if (isSame(source, target)) {
	    return false;
	  }

	  // ensure we do not drop the element
	  // into source
	  if (source && isParent(source, target)) {
	    return false;
	  }

	  return canDrop(shape, target) || canInsert(shape, target);
	}

	function canResize(shape, newBounds) {
	  if (is$1(shape, 'bpmn:SubProcess')) {
	    return (
	      isExpanded(shape) && (
	        !newBounds || (newBounds.width >= 100 && newBounds.height >= 80)
	      )
	    );
	  }

	  if (is$1(shape, 'bpmn:Lane')) {
	    return !newBounds || (newBounds.width >= 130 && newBounds.height >= 60);
	  }

	  if (is$1(shape, 'bpmn:Participant')) {
	    return !newBounds || (newBounds.width >= 250 && newBounds.height >= 50);
	  }

	  if (isTextAnnotation(shape)) {
	    return true;
	  }

	  if (isGroup(shape)) {
	    return true;
	  }

	  return false;
	}

	/**
	 * Check, whether one side of the relationship
	 * is a text annotation.
	 */
	function isOneTextAnnotation(source, target) {

	  var sourceTextAnnotation = isTextAnnotation(source),
	      targetTextAnnotation = isTextAnnotation(target);

	  return (
	    (sourceTextAnnotation || targetTextAnnotation) &&
	    (sourceTextAnnotation !== targetTextAnnotation)
	  );
	}


	function canConnectAssociation(source, target) {

	  // do not connect connections
	  if (isConnection$1(source) || isConnection$1(target)) {
	    return false;
	  }

	  // compensation boundary events are exception
	  if (isCompensationBoundary(source) && isForCompensation(target)) {
	    return true;
	  }

	  // don't connect parent <-> child
	  if (isParent(target, source) || isParent(source, target)) {
	    return false;
	  }

	  // allow connection of associations between <!TextAnnotation> and <TextAnnotation>
	  return isOneTextAnnotation(source, target);
	}

	function canConnectMessageFlow(source, target) {

	  // handle the case where target does not have a parent,
	  // because it is not dropped within the diagram (bpmn-io/bpmn-js#1033)
	  if (!target.parent) {
	    return false;
	  }

	  return (
	    isMessageFlowSource(source) &&
	    isMessageFlowTarget(target) &&
	    !isSameOrganization(source, target)
	  );
	}

	function canConnectSequenceFlow(source, target) {

	  if (
	    isEventBasedTarget(target) &&
	    target.incoming.length > 0 &&
	    areOutgoingEventBasedGatewayConnections(target.incoming) &&
	    !is$1(source, 'bpmn:EventBasedGateway')
	  ) {
	    return false;
	  }

	  return isSequenceFlowSource(source) &&
	         isSequenceFlowTarget(target) &&
	         isSameScope(source, target) &&
	         !(is$1(source, 'bpmn:EventBasedGateway') && !isEventBasedTarget(target));
	}


	function canConnectDataAssociation(source, target) {

	  if (isAny(source, [ 'bpmn:DataObjectReference', 'bpmn:DataStoreReference' ]) &&
	      isAny(target, [ 'bpmn:Activity', 'bpmn:ThrowEvent' ])) {
	    return { type: 'bpmn:DataInputAssociation' };
	  }

	  if (isAny(target, [ 'bpmn:DataObjectReference', 'bpmn:DataStoreReference' ]) &&
	      isAny(source, [ 'bpmn:Activity', 'bpmn:CatchEvent' ])) {
	    return { type: 'bpmn:DataOutputAssociation' };
	  }

	  return false;
	}

	function canInsert(shape, flow, position) {

	  if (!flow) {
	    return false;
	  }

	  if (Array.isArray(shape)) {
	    if (shape.length !== 1) {
	      return false;
	    }

	    shape = shape[0];
	  }

	  if (flow.source === shape ||
	      flow.target === shape) {
	    return false;
	  }

	  // return true if we can drop on the
	  // underlying flow parent
	  //
	  // at this point we are not really able to talk
	  // about connection rules (yet)

	  return (
	    isAny(flow, [ 'bpmn:SequenceFlow', 'bpmn:MessageFlow' ]) &&
	    !isLabel(flow) &&
	    is$1(shape, 'bpmn:FlowNode') &&
	    !is$1(shape, 'bpmn:BoundaryEvent') &&
	    canDrop(shape, flow.parent));
	}

	function contains(collection, element) {
	  return (collection && element) && collection.indexOf(element) !== -1;
	}

	function canCopy(collection, element) {
	  if (is$1(element, 'bpmn:Lane') && !contains(collection, element.parent)) {
	    return false;
	  }

	  if (is$1(element, 'bpmn:BoundaryEvent') && !contains(collection, element.host)) {
	    return false;
	  }

	  return true;
	}

	function isOutgoingEventBasedGatewayConnection(connection) {

	  if (connection && connection.source) {
	    return is$1(connection.source, 'bpmn:EventBasedGateway');
	  }
	}

	function areOutgoingEventBasedGatewayConnections(connections) {

	  connections = connections || [];

	  return connections.some(isOutgoingEventBasedGatewayConnection);
	}

	var RulesModule$1 = {
	  __depends__: [
	    RulesModule
	  ],
	  __init__: [ 'bpmnRules' ],
	  bpmnRules: [ 'type', BpmnRules ]
	};

	/**
	 * An abstract provider that allows modelers to implement a custom
	 * ordering of diagram elements on the canvas.
	 *
	 * It makes sure that the order is always preserved during element
	 * creation and move operations.
	 *
	 * In order to use this behavior, inherit from it and override
	 * the method {@link OrderingProvider#getOrdering}.
	 *
	 * @example
	 *
	 * ```javascript
	 * function CustomOrderingProvider(eventBus) {
	 *   OrderingProvider.call(this, eventBus);
	 *
	 *   this.getOrdering = function(element, newParent) {
	 *     // always insert elements at the front
	 *     // when moving
	 *     return {
	 *       index: 0,
	 *       parent: newParent
	 *     };
	 *   };
	 * }
	 * ```
	 *
	 * @param {EventBus} eventBus
	 */
	function OrderingProvider(eventBus) {

	  CommandInterceptor.call(this, eventBus);


	  var self = this;

	  this.preExecute([ 'shape.create', 'connection.create' ], function(event) {

	    var context = event.context,
	        element = context.shape || context.connection,
	        parent = context.parent;

	    var ordering = self.getOrdering(element, parent);

	    if (ordering) {

	      if (ordering.parent !== undefined) {
	        context.parent = ordering.parent;
	      }

	      context.parentIndex = ordering.index;
	    }
	  });

	  this.preExecute([ 'shape.move', 'connection.move' ], function(event) {

	    var context = event.context,
	        element = context.shape || context.connection,
	        parent = context.newParent || element.parent;

	    var ordering = self.getOrdering(element, parent);

	    if (ordering) {

	      if (ordering.parent !== undefined) {
	        context.newParent = ordering.parent;
	      }

	      context.newParentIndex = ordering.index;
	    }
	  });
	}

	/**
	 * Return a custom ordering of the element, both in terms
	 * of parent element and index in the new parent.
	 *
	 * Implementors of this method must return an object with
	 * `parent` _and_ `index` in it.
	 *
	 * @param {djs.model.Base} element
	 * @param {djs.model.Shape} newParent
	 *
	 * @return {Object} ordering descriptor
	 */
	OrderingProvider.prototype.getOrdering = function(element, newParent) {
	  return null;
	};

	inherits_browser(OrderingProvider, CommandInterceptor);

	/**
	 * a simple ordering provider that makes sure:
	 *
	 * (0) labels and groups are rendered always on top
	 * (1) elements are ordered by a {level} property
	 */
	function BpmnOrderingProvider(eventBus, canvas, translate) {

	  OrderingProvider.call(this, eventBus);

	  var orders = [
	    { type: 'bpmn:SubProcess', order: { level: 6 } },
	    {
	      type: 'bpmn:SequenceFlow',
	      order: {
	        level: 3,
	        containers: [
	          'bpmn:Participant',
	          'bpmn:FlowElementsContainer'
	        ]
	      }
	    },
	    // handle DataAssociation(s) like message flows and render them always on top
	    {
	      type: 'bpmn:DataAssociation',
	      order: {
	        level: 9,
	        containers: [
	          'bpmn:Collaboration',
	          'bpmn:Process'
	        ]
	      }
	    },
	    {
	      type: 'bpmn:MessageFlow', order: {
	        level: 9,
	        containers: [ 'bpmn:Collaboration' ]
	      }
	    },
	    {
	      type: 'bpmn:Association',
	      order: {
	        level: 6,
	        containers: [
	          'bpmn:Participant',
	          'bpmn:FlowElementsContainer',
	          'bpmn:Collaboration'
	        ]
	      }
	    },
	    { type: 'bpmn:BoundaryEvent', order: { level: 8 } },
	    {
	      type: 'bpmn:Group',
	      order: {
	        level: 10,
	        containers: [
	          'bpmn:Collaboration',
	          'bpmn:Process'
	        ]
	      }
	    },
	    { type: 'bpmn:FlowElement', order: { level: 5 } },
	    { type: 'bpmn:Participant', order: { level: -2 } },
	    { type: 'bpmn:Lane', order: { level: -1 } }
	  ];

	  function computeOrder(element) {
	    if (element.labelTarget) {
	      return { level: 10 };
	    }

	    var entry = find(orders, function(o) {
	      return isAny(element, [ o.type ]);
	    });

	    return entry && entry.order || { level: 1 };
	  }

	  function getOrder(element) {

	    var order = element.order;

	    if (!order) {
	      element.order = order = computeOrder(element);
	    }

	    return order;
	  }

	  function findActualParent(element, newParent, containers) {

	    var actualParent = newParent;

	    while (actualParent) {

	      if (isAny(actualParent, containers)) {
	        break;
	      }

	      actualParent = actualParent.parent;
	    }

	    if (!actualParent) {
	      throw new Error(translate('no parent for {element} in {parent}', {
	        element: element.id,
	        parent: newParent.id
	      }));
	    }

	    return actualParent;
	  }

	  this.getOrdering = function(element, newParent) {

	    // render labels always on top
	    if (element.labelTarget) {
	      return {
	        parent: canvas.getRootElement(),
	        index: -1
	      };
	    }

	    var elementOrder = getOrder(element);


	    if (elementOrder.containers) {
	      newParent = findActualParent(element, newParent, elementOrder.containers);
	    }


	    var currentIndex = newParent.children.indexOf(element);

	    var insertIndex = findIndex(newParent.children, function(child) {

	      // do not compare with labels, they are created
	      // in the wrong order (right after elements) during import and
	      // mess up the positioning.
	      if (!element.labelTarget && child.labelTarget) {
	        return false;
	      }

	      return elementOrder.level < getOrder(child).level;
	    });


	    // if the element is already in the child list at
	    // a smaller index, we need to adjust the insert index.
	    // this takes into account that the element is being removed
	    // before being re-inserted
	    if (insertIndex !== -1) {
	      if (currentIndex !== -1 && currentIndex < insertIndex) {
	        insertIndex -= 1;
	      }
	    }

	    return {
	      index: insertIndex,
	      parent: newParent
	    };
	  };
	}

	BpmnOrderingProvider.$inject = [ 'eventBus', 'canvas', 'translate' ];

	inherits_browser(BpmnOrderingProvider, OrderingProvider);

	var OrderingModule = {
	  __depends__: [
	    translate$2
	  ],
	  __init__: [ 'bpmnOrderingProvider' ],
	  bpmnOrderingProvider: [ 'type', BpmnOrderingProvider ]
	};

	/**
	 * A service that offers un- and redoable execution of commands.
	 *
	 * The command stack is responsible for executing modeling actions
	 * in a un- and redoable manner. To do this it delegates the actual
	 * command execution to {@link CommandHandler}s.
	 *
	 * Command handlers provide {@link CommandHandler#execute(ctx)} and
	 * {@link CommandHandler#revert(ctx)} methods to un- and redo a command
	 * identified by a command context.
	 *
	 *
	 * ## Life-Cycle events
	 *
	 * In the process the command stack fires a number of life-cycle events
	 * that other components to participate in the command execution.
	 *
	 *    * preExecute
	 *    * preExecuted
	 *    * execute
	 *    * executed
	 *    * postExecute
	 *    * postExecuted
	 *    * revert
	 *    * reverted
	 *
	 * A special event is used for validating, whether a command can be
	 * performed prior to its execution.
	 *
	 *    * canExecute
	 *
	 * Each of the events is fired as `commandStack.{eventName}` and
	 * `commandStack.{commandName}.{eventName}`, respectively. This gives
	 * components fine grained control on where to hook into.
	 *
	 * The event object fired transports `command`, the name of the
	 * command and `context`, the command context.
	 *
	 *
	 * ## Creating Command Handlers
	 *
	 * Command handlers should provide the {@link CommandHandler#execute(ctx)}
	 * and {@link CommandHandler#revert(ctx)} methods to implement
	 * redoing and undoing of a command.
	 *
	 * A command handler _must_ ensure undo is performed properly in order
	 * not to break the undo chain. It must also return the shapes that
	 * got changed during the `execute` and `revert` operations.
	 *
	 * Command handlers may execute other modeling operations (and thus
	 * commands) in their `preExecute` and `postExecute` phases. The command
	 * stack will properly group all commands together into a logical unit
	 * that may be re- and undone atomically.
	 *
	 * Command handlers must not execute other commands from within their
	 * core implementation (`execute`, `revert`).
	 *
	 *
	 * ## Change Tracking
	 *
	 * During the execution of the CommandStack it will keep track of all
	 * elements that have been touched during the command's execution.
	 *
	 * At the end of the CommandStack execution it will notify interested
	 * components via an 'elements.changed' event with all the dirty
	 * elements.
	 *
	 * The event can be picked up by components that are interested in the fact
	 * that elements have been changed. One use case for this is updating
	 * their graphical representation after moving / resizing or deletion.
	 *
	 * @see CommandHandler
	 *
	 * @param {EventBus} eventBus
	 * @param {Injector} injector
	 */
	function CommandStack(eventBus, injector) {

	  /**
	   * A map of all registered command handlers.
	   *
	   * @type {Object}
	   */
	  this._handlerMap = {};

	  /**
	   * A stack containing all re/undoable actions on the diagram
	   *
	   * @type {Array<Object>}
	   */
	  this._stack = [];

	  /**
	   * The current index on the stack
	   *
	   * @type {Number}
	   */
	  this._stackIdx = -1;

	  /**
	   * Current active commandStack execution
	   *
	   * @type {Object}
	   */
	  this._currentExecution = {
	    actions: [],
	    dirty: []
	  };


	  this._injector = injector;
	  this._eventBus = eventBus;

	  this._uid = 1;

	  eventBus.on([
	    'diagram.destroy',
	    'diagram.clear'
	  ], function() {
	    this.clear(false);
	  }, this);
	}

	CommandStack.$inject = [ 'eventBus', 'injector' ];


	/**
	 * Execute a command
	 *
	 * @param {String} command the command to execute
	 * @param {Object} context the environment to execute the command in
	 */
	CommandStack.prototype.execute = function(command, context) {
	  if (!command) {
	    throw new Error('command required');
	  }

	  var action = { command: command, context: context };

	  this._pushAction(action);
	  this._internalExecute(action);
	  this._popAction(action);
	};


	/**
	 * Ask whether a given command can be executed.
	 *
	 * Implementors may hook into the mechanism on two ways:
	 *
	 *   * in event listeners:
	 *
	 *     Users may prevent the execution via an event listener.
	 *     It must prevent the default action for `commandStack.(<command>.)canExecute` events.
	 *
	 *   * in command handlers:
	 *
	 *     If the method {@link CommandHandler#canExecute} is implemented in a handler
	 *     it will be called to figure out whether the execution is allowed.
	 *
	 * @param  {String} command the command to execute
	 * @param  {Object} context the environment to execute the command in
	 *
	 * @return {Boolean} true if the command can be executed
	 */
	CommandStack.prototype.canExecute = function(command, context) {

	  var action = { command: command, context: context };

	  var handler = this._getHandler(command);

	  var result = this._fire(command, 'canExecute', action);

	  // handler#canExecute will only be called if no listener
	  // decided on a result already
	  if (result === undefined) {
	    if (!handler) {
	      return false;
	    }

	    if (handler.canExecute) {
	      result = handler.canExecute(context);
	    }
	  }

	  return result;
	};


	/**
	 * Clear the command stack, erasing all undo / redo history
	 */
	CommandStack.prototype.clear = function(emit) {
	  this._stack.length = 0;
	  this._stackIdx = -1;

	  if (emit !== false) {
	    this._fire('changed');
	  }
	};


	/**
	 * Undo last command(s)
	 */
	CommandStack.prototype.undo = function() {
	  var action = this._getUndoAction(),
	      next;

	  if (action) {
	    this._pushAction(action);

	    while (action) {
	      this._internalUndo(action);
	      next = this._getUndoAction();

	      if (!next || next.id !== action.id) {
	        break;
	      }

	      action = next;
	    }

	    this._popAction();
	  }
	};


	/**
	 * Redo last command(s)
	 */
	CommandStack.prototype.redo = function() {
	  var action = this._getRedoAction(),
	      next;

	  if (action) {
	    this._pushAction(action);

	    while (action) {
	      this._internalExecute(action, true);
	      next = this._getRedoAction();

	      if (!next || next.id !== action.id) {
	        break;
	      }

	      action = next;
	    }

	    this._popAction();
	  }
	};


	/**
	 * Register a handler instance with the command stack
	 *
	 * @param {String} command
	 * @param {CommandHandler} handler
	 */
	CommandStack.prototype.register = function(command, handler) {
	  this._setHandler(command, handler);
	};


	/**
	 * Register a handler type with the command stack
	 * by instantiating it and injecting its dependencies.
	 *
	 * @param {String} command
	 * @param {Function} a constructor for a {@link CommandHandler}
	 */
	CommandStack.prototype.registerHandler = function(command, handlerCls) {

	  if (!command || !handlerCls) {
	    throw new Error('command and handlerCls must be defined');
	  }

	  var handler = this._injector.instantiate(handlerCls);
	  this.register(command, handler);
	};

	CommandStack.prototype.canUndo = function() {
	  return !!this._getUndoAction();
	};

	CommandStack.prototype.canRedo = function() {
	  return !!this._getRedoAction();
	};

	// stack access  //////////////////////

	CommandStack.prototype._getRedoAction = function() {
	  return this._stack[this._stackIdx + 1];
	};


	CommandStack.prototype._getUndoAction = function() {
	  return this._stack[this._stackIdx];
	};


	// internal functionality //////////////////////

	CommandStack.prototype._internalUndo = function(action) {
	  var self = this;

	  var command = action.command,
	      context = action.context;

	  var handler = this._getHandler(command);

	  // guard against illegal nested command stack invocations
	  this._atomicDo(function() {
	    self._fire(command, 'revert', action);

	    if (handler.revert) {
	      self._markDirty(handler.revert(context));
	    }

	    self._revertedAction(action);

	    self._fire(command, 'reverted', action);
	  });
	};


	CommandStack.prototype._fire = function(command, qualifier, event) {
	  if (arguments.length < 3) {
	    event = qualifier;
	    qualifier = null;
	  }

	  var names = qualifier ? [ command + '.' + qualifier, qualifier ] : [ command ],
	      i, name, result;

	  event = this._eventBus.createEvent(event);

	  for (i = 0; (name = names[i]); i++) {
	    result = this._eventBus.fire('commandStack.' + name, event);

	    if (event.cancelBubble) {
	      break;
	    }
	  }

	  return result;
	};

	CommandStack.prototype._createId = function() {
	  return this._uid++;
	};

	CommandStack.prototype._atomicDo = function(fn) {

	  var execution = this._currentExecution;

	  execution.atomic = true;

	  try {
	    fn();
	  } finally {
	    execution.atomic = false;
	  }
	};

	CommandStack.prototype._internalExecute = function(action, redo) {
	  var self = this;

	  var command = action.command,
	      context = action.context;

	  var handler = this._getHandler(command);

	  if (!handler) {
	    throw new Error('no command handler registered for <' + command + '>');
	  }

	  this._pushAction(action);

	  if (!redo) {
	    this._fire(command, 'preExecute', action);

	    if (handler.preExecute) {
	      handler.preExecute(context);
	    }

	    this._fire(command, 'preExecuted', action);
	  }

	  // guard against illegal nested command stack invocations
	  this._atomicDo(function() {

	    self._fire(command, 'execute', action);

	    if (handler.execute) {
	      // actual execute + mark return results as dirty
	      self._markDirty(handler.execute(context));
	    }

	    // log to stack
	    self._executedAction(action, redo);

	    self._fire(command, 'executed', action);
	  });

	  if (!redo) {
	    this._fire(command, 'postExecute', action);

	    if (handler.postExecute) {
	      handler.postExecute(context);
	    }

	    this._fire(command, 'postExecuted', action);
	  }

	  this._popAction(action);
	};


	CommandStack.prototype._pushAction = function(action) {

	  var execution = this._currentExecution,
	      actions = execution.actions;

	  var baseAction = actions[0];

	  if (execution.atomic) {
	    throw new Error('illegal invocation in <execute> or <revert> phase (action: ' + action.command + ')');
	  }

	  if (!action.id) {
	    action.id = (baseAction && baseAction.id) || this._createId();
	  }

	  actions.push(action);
	};


	CommandStack.prototype._popAction = function() {
	  var execution = this._currentExecution,
	      actions = execution.actions,
	      dirty = execution.dirty;

	  actions.pop();

	  if (!actions.length) {
	    this._eventBus.fire('elements.changed', { elements: uniqueBy('id', dirty) });

	    dirty.length = 0;

	    this._fire('changed');
	  }
	};


	CommandStack.prototype._markDirty = function(elements) {
	  var execution = this._currentExecution;

	  if (!elements) {
	    return;
	  }

	  elements = isArray(elements) ? elements : [ elements ];

	  execution.dirty = execution.dirty.concat(elements);
	};


	CommandStack.prototype._executedAction = function(action, redo) {
	  var stackIdx = ++this._stackIdx;

	  if (!redo) {
	    this._stack.splice(stackIdx, this._stack.length, action);
	  }
	};


	CommandStack.prototype._revertedAction = function(action) {
	  this._stackIdx--;
	};


	CommandStack.prototype._getHandler = function(command) {
	  return this._handlerMap[command];
	};

	CommandStack.prototype._setHandler = function(command, handler) {
	  if (!command || !handler) {
	    throw new Error('command and handler required');
	  }

	  if (this._handlerMap[command]) {
	    throw new Error('overriding handler for command <' + command + '>');
	  }

	  this._handlerMap[command] = handler;
	};

	var CommandModule = {
	  commandStack: [ 'type', CommandStack ]
	};

	// document wide unique tooltip ids
	var ids$1 = new IdGenerator('tt');


	function createRoot$1(parentNode) {
	  var root = domify(
	    '<div class="djs-tooltip-container" style="position: absolute; width: 0; height: 0;" />'
	  );

	  parentNode.insertBefore(root, parentNode.firstChild);

	  return root;
	}


	function setPosition$1(el, x, y) {
	  assign(el.style, { left: x + 'px', top: y + 'px' });
	}

	function setVisible$1(el, visible) {
	  el.style.display = visible === false ? 'none' : '';
	}


	var tooltipClass = 'djs-tooltip',
	    tooltipSelector = '.' + tooltipClass;

	/**
	 * A service that allows users to render tool tips on the diagram.
	 *
	 * The tooltip service will take care of updating the tooltip positioning
	 * during navigation + zooming.
	 *
	 * @example
	 *
	 * ```javascript
	 *
	 * // add a pink badge on the top left of the shape
	 * tooltips.add({
	 *   position: {
	 *     x: 50,
	 *     y: 100
	 *   },
	 *   html: '<div style="width: 10px; background: fuchsia; color: white;">0</div>'
	 * });
	 *
	 * // or with optional life span
	 * tooltips.add({
	 *   position: {
	 *     top: -5,
	 *     left: -5
	 *   },
	 *   html: '<div style="width: 10px; background: fuchsia; color: white;">0</div>',
	 *   ttl: 2000
	 * });
	 *
	 * // remove a tool tip
	 * var id = tooltips.add(...);
	 * tooltips.remove(id);
	 * ```
	 *
	 * @param {EventBus} eventBus
	 * @param {Canvas} canvas
	 */
	function Tooltips(eventBus, canvas) {

	  this._eventBus = eventBus;
	  this._canvas = canvas;

	  this._ids = ids$1;

	  this._tooltipDefaults = {
	    show: {
	      minZoom: 0.7,
	      maxZoom: 5.0
	    }
	  };

	  /**
	   * Mapping tooltipId -> tooltip
	   */
	  this._tooltips = {};

	  // root html element for all tooltips
	  this._tooltipRoot = createRoot$1(canvas.getContainer());


	  var self = this;

	  delegateEvents.bind(this._tooltipRoot, tooltipSelector, 'mousedown', function(event) {
	    event.stopPropagation();
	  });

	  delegateEvents.bind(this._tooltipRoot, tooltipSelector, 'mouseover', function(event) {
	    self.trigger('mouseover', event);
	  });

	  delegateEvents.bind(this._tooltipRoot, tooltipSelector, 'mouseout', function(event) {
	    self.trigger('mouseout', event);
	  });

	  this._init();
	}


	Tooltips.$inject = [ 'eventBus', 'canvas' ];


	/**
	 * Adds a HTML tooltip to the diagram
	 *
	 * @param {Object}               tooltip   the tooltip configuration
	 *
	 * @param {String|DOMElement}    tooltip.html                 html element to use as an tooltip
	 * @param {Object}               [tooltip.show]               show configuration
	 * @param {Number}               [tooltip.show.minZoom]       minimal zoom level to show the tooltip
	 * @param {Number}               [tooltip.show.maxZoom]       maximum zoom level to show the tooltip
	 * @param {Object}               tooltip.position             where to attach the tooltip
	 * @param {Number}               [tooltip.position.left]      relative to element bbox left attachment
	 * @param {Number}               [tooltip.position.top]       relative to element bbox top attachment
	 * @param {Number}               [tooltip.position.bottom]    relative to element bbox bottom attachment
	 * @param {Number}               [tooltip.position.right]     relative to element bbox right attachment
	 * @param {Number}               [tooltip.timeout=-1]
	 *
	 * @return {String}              id that may be used to reference the tooltip for update or removal
	 */
	Tooltips.prototype.add = function(tooltip) {

	  if (!tooltip.position) {
	    throw new Error('must specifiy tooltip position');
	  }

	  if (!tooltip.html) {
	    throw new Error('must specifiy tooltip html');
	  }

	  var id = this._ids.next();

	  tooltip = assign({}, this._tooltipDefaults, tooltip, {
	    id: id
	  });

	  this._addTooltip(tooltip);

	  if (tooltip.timeout) {
	    this.setTimeout(tooltip);
	  }

	  return id;
	};

	Tooltips.prototype.trigger = function(action, event) {

	  var node = event.delegateTarget || event.target;

	  var tooltip = this.get(attr(node, 'data-tooltip-id'));

	  if (!tooltip) {
	    return;
	  }

	  if (action === 'mouseover' && tooltip.timeout) {
	    this.clearTimeout(tooltip);
	  }

	  if (action === 'mouseout' && tooltip.timeout) {
	    // cut timeout after mouse out
	    tooltip.timeout = 1000;

	    this.setTimeout(tooltip);
	  }
	};

	/**
	 * Get a tooltip with the given id
	 *
	 * @param {String} id
	 */
	Tooltips.prototype.get = function(id) {

	  if (typeof id !== 'string') {
	    id = id.id;
	  }

	  return this._tooltips[id];
	};

	Tooltips.prototype.clearTimeout = function(tooltip) {

	  tooltip = this.get(tooltip);

	  if (!tooltip) {
	    return;
	  }

	  var removeTimer = tooltip.removeTimer;

	  if (removeTimer) {
	    clearTimeout(removeTimer);
	    tooltip.removeTimer = null;
	  }
	};

	Tooltips.prototype.setTimeout = function(tooltip) {

	  tooltip = this.get(tooltip);

	  if (!tooltip) {
	    return;
	  }

	  this.clearTimeout(tooltip);

	  var self = this;

	  tooltip.removeTimer = setTimeout(function() {
	    self.remove(tooltip);
	  }, tooltip.timeout);
	};

	/**
	 * Remove an tooltip with the given id
	 *
	 * @param {String} id
	 */
	Tooltips.prototype.remove = function(id) {

	  var tooltip = this.get(id);

	  if (tooltip) {
	    remove(tooltip.html);
	    remove(tooltip.htmlContainer);

	    delete tooltip.htmlContainer;

	    delete this._tooltips[tooltip.id];
	  }
	};


	Tooltips.prototype.show = function() {
	  setVisible$1(this._tooltipRoot);
	};


	Tooltips.prototype.hide = function() {
	  setVisible$1(this._tooltipRoot, false);
	};


	Tooltips.prototype._updateRoot = function(viewbox) {
	  var a = viewbox.scale || 1;
	  var d = viewbox.scale || 1;

	  var matrix = 'matrix(' + a + ',0,0,' + d + ',' + (-1 * viewbox.x * a) + ',' + (-1 * viewbox.y * d) + ')';

	  this._tooltipRoot.style.transform = matrix;
	  this._tooltipRoot.style['-ms-transform'] = matrix;
	};


	Tooltips.prototype._addTooltip = function(tooltip) {

	  var id = tooltip.id,
	      html = tooltip.html,
	      htmlContainer,
	      tooltipRoot = this._tooltipRoot;

	  // unwrap jquery (for those who need it)
	  if (html.get && html.constructor.prototype.jquery) {
	    html = html.get(0);
	  }

	  // create proper html elements from
	  // tooltip HTML strings
	  if (isString(html)) {
	    html = domify(html);
	  }

	  htmlContainer = domify('<div data-tooltip-id="' + id + '" class="' + tooltipClass + '" style="position: absolute">');

	  htmlContainer.appendChild(html);

	  if (tooltip.type) {
	    classes(htmlContainer).add('djs-tooltip-' + tooltip.type);
	  }

	  if (tooltip.className) {
	    classes(htmlContainer).add(tooltip.className);
	  }

	  tooltip.htmlContainer = htmlContainer;

	  tooltipRoot.appendChild(htmlContainer);

	  this._tooltips[id] = tooltip;

	  this._updateTooltip(tooltip);
	};


	Tooltips.prototype._updateTooltip = function(tooltip) {

	  var position = tooltip.position,
	      htmlContainer = tooltip.htmlContainer;

	  // update overlay html based on tooltip x, y

	  setPosition$1(htmlContainer, position.x, position.y);
	};


	Tooltips.prototype._updateTooltipVisibilty = function(viewbox) {

	  forEach(this._tooltips, function(tooltip) {
	    var show = tooltip.show,
	        htmlContainer = tooltip.htmlContainer,
	        visible = true;

	    if (show) {
	      if (show.minZoom > viewbox.scale ||
	          show.maxZoom < viewbox.scale) {
	        visible = false;
	      }

	      setVisible$1(htmlContainer, visible);
	    }
	  });
	};

	Tooltips.prototype._init = function() {

	  var self = this;

	  // scroll/zoom integration

	  function updateViewbox(viewbox) {
	    self._updateRoot(viewbox);
	    self._updateTooltipVisibilty(viewbox);

	    self.show();
	  }

	  this._eventBus.on('canvas.viewbox.changing', function(event) {
	    self.hide();
	  });

	  this._eventBus.on('canvas.viewbox.changed', function(event) {
	    updateViewbox(event.viewbox);
	  });
	};

	var TooltipsModule = {
	  __init__: [ 'tooltips' ],
	  tooltips: [ 'type', Tooltips ]
	};

	/**
	 * Remove from the beginning of a collection until it is empty.
	 *
	 * This is a null-safe operation that ensures elements
	 * are being removed from the given collection until the
	 * collection is empty.
	 *
	 * The implementation deals with the fact that a remove operation
	 * may touch, i.e. remove multiple elements in the collection
	 * at a time.
	 *
	 * @param {Array<Object>} [collection]
	 * @param {Function} removeFn
	 *
	 * @return {Array<Object>} the cleared collection
	 */
	function saveClear(collection, removeFn) {

	  if (typeof removeFn !== 'function') {
	    throw new Error('removeFn iterator must be a function');
	  }

	  if (!collection) {
	    return;
	  }

	  var e;

	  while ((e = collection[0])) {
	    removeFn(e);
	  }

	  return collection;
	}

	var LOW_PRIORITY$b = 250,
	    HIGH_PRIORITY$a = 1400;


	/**
	 * A handler that makes sure labels are properly moved with
	 * their label targets.
	 *
	 * @param {didi.Injector} injector
	 * @param {EventBus} eventBus
	 * @param {Modeling} modeling
	 */
	function LabelSupport(injector, eventBus, modeling) {

	  CommandInterceptor.call(this, eventBus);

	  var movePreview = injector.get('movePreview', false);

	  // remove labels from the collection that are being
	  // moved with other elements anyway
	  eventBus.on('shape.move.start', HIGH_PRIORITY$a, function(e) {

	    var context = e.context,
	        shapes = context.shapes,
	        validatedShapes = context.validatedShapes;

	    context.shapes = removeLabels(shapes);
	    context.validatedShapes = removeLabels(validatedShapes);
	  });

	  // add labels to visual's group
	  movePreview && eventBus.on('shape.move.start', LOW_PRIORITY$b, function(e) {

	    var context = e.context,
	        shapes = context.shapes;

	    var labels = [];

	    forEach(shapes, function(element) {

	      forEach(element.labels, function(label) {

	        if (!label.hidden && context.shapes.indexOf(label) === -1) {
	          labels.push(label);
	        }

	        if (element.labelTarget) {
	          labels.push(element);
	        }
	      });
	    });

	    forEach(labels, function(label) {
	      movePreview.makeDraggable(context, label, true);
	    });

	  });

	  // add all labels to move closure
	  this.preExecuted('elements.move', HIGH_PRIORITY$a, function(e) {
	    var context = e.context,
	        closure = context.closure,
	        enclosedElements = closure.enclosedElements;

	    var enclosedLabels = [];

	    // find labels that are not part of
	    // move closure yet and add them
	    forEach(enclosedElements, function(element) {
	      forEach(element.labels, function(label) {

	        if (!enclosedElements[label.id]) {
	          enclosedLabels.push(label);
	        }
	      });
	    });

	    closure.addAll(enclosedLabels);
	  });


	  this.preExecute([
	    'connection.delete',
	    'shape.delete'
	  ], function(e) {

	    var context = e.context,
	        element = context.connection || context.shape;

	    saveClear(element.labels, function(label) {
	      modeling.removeShape(label, { nested: true });
	    });
	  });


	  this.execute('shape.delete', function(e) {

	    var context = e.context,
	        shape = context.shape,
	        labelTarget = shape.labelTarget;

	    // unset labelTarget
	    if (labelTarget) {
	      context.labelTargetIndex = indexOf$1(labelTarget.labels, shape);
	      context.labelTarget = labelTarget;

	      shape.labelTarget = null;
	    }
	  });

	  this.revert('shape.delete', function(e) {

	    var context = e.context,
	        shape = context.shape,
	        labelTarget = context.labelTarget,
	        labelTargetIndex = context.labelTargetIndex;

	    // restore labelTarget
	    if (labelTarget) {
	      add$1(labelTarget.labels, shape, labelTargetIndex);

	      shape.labelTarget = labelTarget;
	    }
	  });

	}

	inherits_browser(LabelSupport, CommandInterceptor);

	LabelSupport.$inject = [
	  'injector',
	  'eventBus',
	  'modeling'
	];


	/**
	 * Return a filtered list of elements that do not
	 * contain attached elements with hosts being part
	 * of the selection.
	 *
	 * @param  {Array<djs.model.Base>} elements
	 *
	 * @return {Array<djs.model.Base>} filtered
	 */
	function removeLabels(elements) {

	  return filter(elements, function(element) {

	    // filter out labels that are move together
	    // with their label targets
	    return elements.indexOf(element.labelTarget) === -1;
	  });
	}

	var LabelSupportModule = {
	  __init__: [ 'labelSupport'],
	  labelSupport: [ 'type', LabelSupport ]
	};

	var LOW_PRIORITY$c = 251,
	    HIGH_PRIORITY$b = 1401;

	var MARKER_ATTACH$1 = 'attach-ok';


	/**
	 * Adds the notion of attached elements to the modeler.
	 *
	 * Optionally depends on `diagram-js/lib/features/move` to render
	 * the attached elements during move preview.
	 *
	 * Optionally depends on `diagram-js/lib/features/label-support`
	 * to render attached labels during move preview.
	 *
	 * @param {didi.Injector} injector
	 * @param {EventBus} eventBus
	 * @param {Canvas} canvas
	 * @param {Rules} rules
	 * @param {Modeling} modeling
	 */
	function AttachSupport(injector, eventBus, canvas, rules, modeling) {

	  CommandInterceptor.call(this, eventBus);

	  var movePreview = injector.get('movePreview', false);


	  // remove all the attached elements from the shapes to be validated
	  // add all the attached shapes to the overall list of moved shapes
	  eventBus.on('shape.move.start', HIGH_PRIORITY$b, function(e) {

	    var context = e.context,
	        shapes = context.shapes,
	        validatedShapes = context.validatedShapes;

	    context.shapes = addAttached(shapes);

	    context.validatedShapes = removeAttached(validatedShapes);
	  });

	  // add attachers to the visual's group
	  movePreview && eventBus.on('shape.move.start', LOW_PRIORITY$c, function(e) {

	    var context = e.context,
	        shapes = context.shapes,
	        attachers = getAttachers(shapes);

	    forEach(attachers, function(attacher) {
	      movePreview.makeDraggable(context, attacher, true);

	      forEach(attacher.labels, function(label) {
	        movePreview.makeDraggable(context, label, true);
	      });
	    });
	  });

	  // add attach-ok marker to current host
	  movePreview && eventBus.on('shape.move.start', function(event) {
	    var context = event.context,
	        shapes = context.shapes;

	    if (shapes.length !== 1) {
	      return;
	    }

	    var shape = shapes[0];

	    var host = shape.host;

	    if (host) {
	      canvas.addMarker(host, MARKER_ATTACH$1);

	      eventBus.once([
	        'shape.move.out',
	        'shape.move.cleanup'
	      ], function() {
	        canvas.removeMarker(host, MARKER_ATTACH$1);
	      });
	    }
	  });

	  // add all attachers to move closure
	  this.preExecuted('elements.move', HIGH_PRIORITY$b, function(e) {
	    var context = e.context,
	        closure = context.closure,
	        shapes = context.shapes,
	        attachers = getAttachers(shapes);

	    forEach(attachers, function(attacher) {
	      closure.add(attacher, closure.topLevel[attacher.host.id]);
	    });
	  });

	  // perform the attaching after shapes are done moving
	  this.postExecuted('elements.move', function(e) {

	    var context = e.context,
	        shapes = context.shapes,
	        newHost = context.newHost,
	        attachers;

	    // we only support attachment / detachment of one element
	    if (shapes.length !== 1) {
	      return;
	    }

	    if (newHost) {

	      attachers = shapes;
	    } else {

	      attachers = filter(shapes, function(s) {
	        return !!s.host;
	      });
	    }

	    forEach(attachers, function(attacher) {
	      modeling.updateAttachment(attacher, newHost);
	    });
	  });

	  // ensure invalid attachment connections are removed
	  this.postExecuted('elements.move', function(e) {

	    var shapes = e.context.shapes;

	    forEach(shapes, function(shape) {

	      forEach(shape.attachers, function(attacher) {

	        // remove invalid outgoing connections
	        forEach(attacher.outgoing.slice(), function(connection) {
	          var allowed = rules.allowed('connection.reconnectStart', {
	            connection: connection,
	            source: connection.source,
	            target: connection.target
	          });

	          if (!allowed) {
	            modeling.removeConnection(connection);
	          }
	        });

	        // remove invalid incoming connections
	        forEach(attacher.incoming.slice(), function(connection) {
	          var allowed = rules.allowed('connection.reconnectEnd', {
	            connection: connection,
	            source: connection.source,
	            target: connection.target
	          });

	          if (!allowed) {
	            modeling.removeConnection(connection);
	          }
	        });
	      });
	    });
	  });

	  this.postExecute('shape.create', function(e) {
	    var context = e.context,
	        shape = context.shape,
	        host = context.host;

	    if (host) {
	      modeling.updateAttachment(shape, host);
	    }
	  });

	  // update attachments if the host is replaced
	  this.postExecute('shape.replace', function(e) {

	    var context = e.context,
	        oldShape = context.oldShape,
	        newShape = context.newShape;

	    // move the attachers to the new host
	    saveClear(oldShape.attachers, function(attacher) {
	      var allowed = rules.allowed('elements.move', {
	        target: newShape,
	        shapes: [attacher]
	      });

	      if (allowed === 'attach') {
	        modeling.updateAttachment(attacher, newShape);
	      } else {
	        modeling.removeShape(attacher);
	      }
	    });

	    // move attachers if new host has different size
	    if (newShape.attachers.length) {

	      forEach(newShape.attachers, function(attacher) {
	        var delta = getNewAttachShapeDelta(attacher, oldShape, newShape);
	        modeling.moveShape(attacher, delta, attacher.parent);
	      });
	    }

	  });

	  // move shape on host resize
	  this.postExecute('shape.resize', function(event) {
	    var context = event.context,
	        shape = context.shape,
	        oldBounds = context.oldBounds,
	        newBounds = context.newBounds,
	        attachers = shape.attachers;

	    forEach(attachers, function(attacher) {
	      var delta = getNewAttachShapeDelta(attacher, oldBounds, newBounds);

	      modeling.moveShape(attacher, delta, attacher.parent);

	      forEach(attacher.labels, function(label) {
	        modeling.moveShape(label, delta, label.parent);
	      });
	    });
	  });

	  // remove attachments
	  this.preExecute('shape.delete', function(event) {

	    var shape = event.context.shape;

	    saveClear(shape.attachers, function(attacher) {
	      modeling.removeShape(attacher);
	    });

	    if (shape.host) {
	      modeling.updateAttachment(shape, null);
	    }
	  });


	  // Prevent attachers and their labels from moving, when the space tool is performed.
	  // Otherwise the attachers and their labels would be moved twice.
	  eventBus.on('spaceTool.move', function(event) {

	    var context = event.context,
	        initialized = context.initialized,
	        attachSupportInitialized = context.attachSupportInitialized;

	    if (!initialized || attachSupportInitialized) {
	      return;
	    }

	    var movingShapes = context.movingShapes;

	    // collect attachers whose host is not being moved using the space tool
	    var staticAttachers = filter(movingShapes, function(shape) {
	      var host = shape.host;

	      return host && movingShapes.indexOf(host) === -1;
	    });

	    // remove attachers that are not going to be moved from moving shapes
	    forEach(staticAttachers, function(shape) {
	      remove$2(movingShapes, shape);

	      forEach(shape.labels, function(label) {
	        remove$2(movingShapes, shape.label);
	      });
	    });

	    context.attachSupportInitialized = true;
	  });
	}

	inherits_browser(AttachSupport, CommandInterceptor);

	AttachSupport.$inject = [
	  'injector',
	  'eventBus',
	  'canvas',
	  'rules',
	  'modeling'
	];


	/**
	 * Return attachers of the given shapes
	 *
	 * @param {Array<djs.model.Base>} shapes
	 * @return {Array<djs.model.Base>}
	 */
	function getAttachers(shapes) {
	  return flatten(map(shapes, function(s) {
	    return s.attachers || [];
	  }));
	}

	/**
	 * Return a combined list of elements and
	 * attachers.
	 *
	 * @param {Array<djs.model.Base>} elements
	 * @return {Array<djs.model.Base>} filtered
	 */
	function addAttached(elements) {
	  var attachers = getAttachers(elements);

	  return unionBy('id', elements, attachers);
	}

	/**
	 * Return a filtered list of elements that do not
	 * contain attached elements with hosts being part
	 * of the selection.
	 *
	 * @param  {Array<djs.model.Base>} elements
	 *
	 * @return {Array<djs.model.Base>} filtered
	 */
	function removeAttached(elements) {

	  var ids = groupBy(elements, 'id');

	  return filter(elements, function(element) {
	    while (element) {

	      // host in selection
	      if (element.host && ids[element.host.id]) {
	        return false;
	      }

	      element = element.parent;
	    }

	    return true;
	  });
	}

	var AttachSupportModule = {
	  __depends__: [
	    RulesModule
	  ],
	  __init__: [ 'attachSupport' ],
	  attachSupport: [ 'type', AttachSupport ]
	};

	var LOW_PRIORITY$d = 250;

	/**
	 * The tool manager acts as middle-man between the available tool's and the Palette,
	 * it takes care of making sure that the correct active state is set.
	 *
	 * @param  {Object}    eventBus
	 * @param  {Object}    dragging
	 */
	function ToolManager(eventBus, dragging) {
	  this._eventBus = eventBus;
	  this._dragging = dragging;

	  this._tools = [];
	  this._active = null;
	}

	ToolManager.$inject = [ 'eventBus', 'dragging' ];

	ToolManager.prototype.registerTool = function(name, events) {
	  var tools = this._tools;

	  if (!events) {
	    throw new Error('A tool has to be registered with it\'s "events"');
	  }

	  tools.push(name);

	  this.bindEvents(name, events);
	};

	ToolManager.prototype.isActive = function(tool) {
	  return tool && this._active === tool;
	};

	ToolManager.prototype.length = function(tool) {
	  return this._tools.length;
	};

	ToolManager.prototype.setActive = function(tool) {
	  var eventBus = this._eventBus;

	  if (this._active !== tool) {
	    this._active = tool;

	    eventBus.fire('tool-manager.update', { tool: tool });
	  }
	};

	ToolManager.prototype.bindEvents = function(name, events) {
	  var eventBus = this._eventBus,
	      dragging = this._dragging;

	  var eventsToRegister = [];

	  eventBus.on(events.tool + '.init', function(event) {
	    var context = event.context;

	    // Active tools that want to reactivate themselves must do this explicitly
	    if (!context.reactivate && this.isActive(name)) {
	      this.setActive(null);

	      dragging.cancel();
	      return;
	    }

	    this.setActive(name);

	  }, this);

	  // Todo[ricardo]: add test cases
	  forEach(events, function(event) {
	    eventsToRegister.push(event + '.ended');
	    eventsToRegister.push(event + '.canceled');
	  });

	  eventBus.on(eventsToRegister, LOW_PRIORITY$d, function(event) {
	    var originalEvent = event.originalEvent;

	    // We defer the de-activation of the tool to the .activate phase,
	    // so we're able to check if we want to toggle off the current
	    // active tool or switch to a new one
	    if (!this._active) {
	      return;
	    }

	    if (originalEvent && closest(originalEvent.target, '.group[data-group="tools"]')) {
	      return;
	    }

	    this.setActive(null);
	  }, this);
	};

	var ToolManagerModule = {
	  __depends__: [
	    DraggingModule
	  ],
	  __init__: [ 'toolManager' ],
	  toolManager: [ 'type', ToolManager ]
	};

	/**
	 * Get Resize direction given axis + offset
	 *
	 * @param {String} axis (x|y)
	 * @param {Number} offset
	 *
	 * @return {String} (e|w|n|s)
	 */
	function getDirection(axis, offset) {

	  if (axis === 'x') {
	    if (offset > 0) {
	      return 'e';
	    }

	    if (offset < 0) {
	      return 'w';
	    }
	  }

	  if (axis === 'y') {
	    if (offset > 0) {
	      return 's';
	    }

	    if (offset < 0) {
	      return 'n';
	    }
	  }

	  return null;
	}


	/**
	 * Resize the given bounds by the specified delta from a given anchor point.
	 *
	 * @param {Bounds} bounds the bounding box that should be resized
	 * @param {String} direction in which the element is resized (n, s, e, w)
	 * @param {Point} delta of the resize operation
	 *
	 * @return {Bounds} resized bounding box
	 */
	function resizeBounds$1(bounds, direction, delta) {

	  var dx = delta.x,
	      dy = delta.y;

	  switch (direction) {

	  case 'n':
	    return {
	      x: bounds.x,
	      y: bounds.y + dy,
	      width: bounds.width,
	      height: bounds.height - dy
	    };

	  case 's':
	    return {
	      x: bounds.x,
	      y: bounds.y,
	      width: bounds.width,
	      height: bounds.height + dy
	    };

	  case 'w':
	    return {
	      x: bounds.x + dx,
	      y: bounds.y,
	      width: bounds.width - dx,
	      height: bounds.height
	    };

	  case 'e':
	    return {
	      x: bounds.x,
	      y: bounds.y,
	      width: bounds.width + dx,
	      height: bounds.height
	    };

	  default:
	    throw new Error('unrecognized direction: ' + direction);
	  }
	}

	var abs$6 = Math.abs,
	    round$7 = Math.round;

	var HIGH_PRIORITY$c = 1500,
	    SPACE_TOOL_CURSOR = 'crosshair';

	var AXIS_TO_DIMENSION = { x: 'width', y: 'height' },
	    AXIS_INVERTED = { x: 'y', y: 'x' };


	/**
	 * A tool that allows users to create and remove space in a diagram.
	 *
	 * The tool needs to be activated manually via {@link SpaceTool#activate(MouseEvent)}.
	 */
	function SpaceTool(
	    eventBus, dragging, canvas,
	    modeling, rules, toolManager) {

	  this._canvas = canvas;
	  this._dragging = dragging;
	  this._modeling = modeling;
	  this._rules = rules;
	  this._toolManager = toolManager;

	  var self = this;

	  toolManager.registerTool('space', {
	    tool: 'spaceTool.selection',
	    dragging: 'spaceTool'
	  });

	  eventBus.on('spaceTool.selection.end', function(event) {
	    var target = event.originalEvent.target;

	    // only reactive on diagram click
	    // on some occasions, event.hover is not set and we have to check if the target is an svg
	    if (!event.hover && !(target instanceof SVGElement)) {
	      return;
	    }

	    eventBus.once('spaceTool.selection.ended', function() {
	      self.activateMakeSpace(event.originalEvent);
	    });
	  });


	  eventBus.on('spaceTool.move', HIGH_PRIORITY$c , function(event) {

	    var context = event.context;

	    if (!context.initialized) {
	      context.initialized = self.initializeMakeSpace(event, context);
	    }
	  });


	  eventBus.on('spaceTool.end', function(event) {

	    var context = event.context,
	        axis = context.axis,
	        direction = context.direction,
	        movingShapes = context.movingShapes,
	        resizingShapes = context.resizingShapes;

	    // skip if create space has not been initialized yet
	    if (!context.initialized) {
	      return;
	    }

	    var delta = { x: round$7(event.dx), y: round$7(event.dy) };
	    delta[ AXIS_INVERTED[ axis ] ] = 0;

	    var insideBounds = true;

	    // check if the space tool cursor is inside of bounds of
	    // any of the shapes that would be resized.
	    forEach(resizingShapes, function(shape) {

	      if ((direction === 'w' && event.x > shape.x + shape.width) ||
	          (direction === 'e' && event.x < shape.x) ||
	          (direction === 'n' && event.y > shape.y + shape.height) ||
	          (direction === 's' && event.y < shape.y)) {

	        insideBounds = false;
	        return;
	      }
	    });

	    if (insideBounds) {
	      // make space only if the cursor is inside bounds
	      self.makeSpace(movingShapes, resizingShapes, delta, direction);
	    }

	    eventBus.once('spaceTool.ended', function(event) {
	      // reactivate space tool after usage
	      self.activateSelection(event.originalEvent, true, true);
	    });

	  });
	}

	SpaceTool.$inject = [
	  'eventBus',
	  'dragging',
	  'canvas',
	  'modeling',
	  'rules',
	  'toolManager'
	];


	/**
	 * Activate space tool selection
	 *
	 * @param  {MouseEvent} event
	 * @param  {Boolean} autoActivate
	 */
	SpaceTool.prototype.activateSelection = function(event, autoActivate, reactivate) {
	  this._dragging.init(event, 'spaceTool.selection', {
	    trapClick: false,
	    cursor: SPACE_TOOL_CURSOR,
	    autoActivate: autoActivate,
	    data: {
	      context: {
	        reactivate: reactivate
	      }
	    }
	  });
	};

	/**
	 * Activate make space
	 *
	 * @param  {MouseEvent} event
	 */
	SpaceTool.prototype.activateMakeSpace = function(event) {
	  this._dragging.init(event, 'spaceTool', {
	    autoActivate: true,
	    cursor: SPACE_TOOL_CURSOR,
	    data: {
	      context: {}
	    }
	  });
	};

	/**
	 * Actually make space on the diagram
	 *
	 * @param  {Array<djs.model.Shape>} movingShapes
	 * @param  {Array<djs.model.Shape>} resizingShapes
	 * @param  {Point} delta
	 * @param  {String} direction
	 */
	SpaceTool.prototype.makeSpace = function(movingShapes, resizingShapes, delta, direction) {
	  return this._modeling.createSpace(movingShapes, resizingShapes, delta, direction);
	};

	/**
	 * Initialize make space and return true if that was successful.
	 *
	 * @param {Event} event
	 * @param {Object} context
	 *
	 * @return {Boolean} true, if successful
	 */
	SpaceTool.prototype.initializeMakeSpace = function(event, context) {

	  var axis = abs$6(event.dx) > abs$6(event.dy) ? 'x' : 'y',
	      offset = event['d' + axis],
	      // start point of create space operation
	      spacePos = event[axis] - offset;

	  if (abs$6(offset) < 5) {
	    return false;
	  }

	  // invert the offset in order to remove space when moving left
	  if (offset < 0) {
	    offset *= -1;
	  }

	  // inverts the offset to choose the shapes
	  // on the opposite side of the resizer if
	  // a key modifier is pressed
	  if (hasPrimaryModifier(event)) {
	    offset *= -1;
	  }

	  var rootShape = this._canvas.getRootElement();

	  var allShapes = selfAndAllChildren(rootShape, true);

	  var adjustments = this.calculateAdjustments(allShapes, axis, offset, spacePos);

	  // store data in context
	  assign(context, adjustments, {
	    axis: axis,
	    direction: getDirection(axis, offset)
	  });

	  set$1('resize-' + (axis === 'x' ? 'ew' : 'ns'));

	  return true;
	};

	/**
	 * Calculate adjustments needed when making space
	 *
	 * @param  {Array<djs.model.Shape>} elements
	 * @param  {String} axis
	 * @param  {Number} offset
	 * @param  {Number} spacePos
	 *
	 * @return {Object}
	 */
	SpaceTool.prototype.calculateAdjustments = function(elements, axis, offset, spacePos) {

	  var movingShapes = [],
	      resizingShapes = [];

	  var rules = this._rules;

	  // collect all elements that need to be moved _AND_
	  // resized given on the initial create space position
	  elements.forEach(function(shape) {

	    var shapeStart = shape[axis],
	        shapeEnd = shapeStart + shape[AXIS_TO_DIMENSION[axis]];

	    // checking if it's root
	    if (!shape.parent) {
	      return;
	    }

	    // checking if it's a shape
	    if (shape.waypoints) {
	      return;
	    }

	    // shape after spacePos
	    if (offset > 0 && shapeStart > spacePos) {
	      return movingShapes.push(shape);
	    }

	    // shape before spacePos
	    if (offset < 0 && shapeEnd < spacePos) {
	      return movingShapes.push(shape);
	    }

	    // shape on top of spacePos, resize only if allowed
	    if (shapeStart < spacePos &&
	        shapeEnd > spacePos &&
	        rules.allowed('shape.resize', { shape: shape })) {

	      return resizingShapes.push(shape);
	    }
	  });

	  return {
	    movingShapes: movingShapes,
	    resizingShapes: resizingShapes
	  };
	};

	SpaceTool.prototype.toggle = function() {
	  if (this.isActive()) {
	    this._dragging.cancel();
	  } else {
	    this.activateSelection();
	  }
	};

	SpaceTool.prototype.isActive = function() {
	  var context = this._dragging.context();

	  return context && /^spaceTool/.test(context.prefix);
	};

	var MARKER_DRAGGING = 'djs-dragging',
	    MARKER_RESIZING$1 = 'djs-resizing';

	var LOW_PRIORITY$e = 250;

	var max$5 = Math.max;


	/**
	 * Provides previews for selecting/moving/resizing shapes when creating/removing space.
	 *
	 * @param {EventBus} eventBus
	 * @param {ElementRegistry} elementRegistry
	 * @param {Canvas} canvas
	 * @param {Styles} styles
	 */
	function SpaceToolPreview(
	    eventBus, elementRegistry, canvas,
	    styles, previewSupport) {

	  function addPreviewGfx(collection, dragGroup) {
	    forEach(collection, function(element) {
	      previewSupport.addDragger(element, dragGroup);

	      canvas.addMarker(element, MARKER_DRAGGING);
	    });
	  }

	  // add crosshair
	  eventBus.on('spaceTool.selection.start', function(event) {
	    var space = canvas.getLayer('space'),
	        context = event.context;

	    var orientation = {
	      x: 'M 0,-10000 L 0,10000',
	      y: 'M -10000,0 L 10000,0'
	    };

	    var crosshairGroup = create('g');
	    attr$1(crosshairGroup, styles.cls('djs-crosshair-group', [ 'no-events' ]));

	    append(space, crosshairGroup);

	    // horizontal path
	    var pathX = create('path');
	    attr$1(pathX, 'd', orientation.x);
	    classes$1(pathX).add('djs-crosshair');

	    append(crosshairGroup, pathX);

	    // vertical path
	    var pathY = create('path');
	    attr$1(pathY, 'd', orientation.y);
	    classes$1(pathY).add('djs-crosshair');

	    append(crosshairGroup, pathY);

	    context.crosshairGroup = crosshairGroup;
	  });

	  // update crosshair
	  eventBus.on('spaceTool.selection.move', function(event) {
	    var crosshairGroup = event.context.crosshairGroup;

	    translate(crosshairGroup, event.x, event.y);
	  });

	  // remove crosshair
	  eventBus.on('spaceTool.selection.cleanup', function(event) {
	    var context = event.context,
	        crosshairGroup = context.crosshairGroup;

	    if (crosshairGroup) {
	      remove$1(crosshairGroup);
	    }
	  });

	  // add and update move/resize previews
	  eventBus.on('spaceTool.move', LOW_PRIORITY$e, function(event) {

	    var context = event.context,
	        line = context.line,
	        axis = context.axis,
	        movingShapes = context.movingShapes,
	        resizingShapes = context.resizingShapes;

	    if (!context.initialized) {
	      return;
	    }

	    if (!context.dragGroup) {
	      var spaceLayer = canvas.getLayer('space');

	      line = create('path');
	      attr$1(line, 'd', 'M0,0 L0,0');
	      classes$1(line).add('djs-crosshair');

	      append(spaceLayer, line);

	      context.line = line;

	      var dragGroup = create('g');
	      attr$1(dragGroup, styles.cls('djs-drag-group', [ 'no-events' ]));

	      append(canvas.getDefaultLayer(), dragGroup);

	      // shapes
	      addPreviewGfx(movingShapes, dragGroup);

	      // connections
	      var movingConnections = context.movingConnections = elementRegistry.filter(function(element) {
	        var sourceIsMoving = false;

	        forEach(movingShapes, function(shape) {
	          forEach(shape.outgoing, function(connection) {
	            if (element === connection) {
	              sourceIsMoving = true;
	            }
	          });
	        });

	        var targetIsMoving = false;

	        forEach(movingShapes, function(shape) {
	          forEach(shape.incoming, function(connection) {
	            if (element === connection) {
	              targetIsMoving = true;
	            }
	          });
	        });

	        var sourceIsResizing = false;

	        forEach(resizingShapes, function(shape) {
	          forEach(shape.outgoing, function(connection) {
	            if (element === connection) {
	              sourceIsResizing = true;
	            }
	          });
	        });

	        var targetIsResizing = false;

	        forEach(resizingShapes, function(shape) {
	          forEach(shape.incoming, function(connection) {
	            if (element === connection) {
	              targetIsResizing = true;
	            }
	          });
	        });

	        return isConnection$2(element)
	          && (sourceIsMoving || sourceIsResizing)
	          && (targetIsMoving || targetIsResizing);
	      });


	      addPreviewGfx(movingConnections, dragGroup);

	      context.dragGroup = dragGroup;
	    }

	    if (!context.frameGroup) {
	      var frameGroup = create('g');
	      attr$1(frameGroup, styles.cls('djs-frame-group', [ 'no-events' ]));

	      append(canvas.getDefaultLayer(), frameGroup);

	      var frames = [];

	      forEach(resizingShapes, function(shape) {
	        var frame = previewSupport.addFrame(shape, frameGroup);

	        var initialBounds = frame.getBBox();

	        frames.push({
	          element: frame,
	          initialBounds: initialBounds
	        });

	        canvas.addMarker(shape, MARKER_RESIZING$1);
	      });

	      context.frameGroup = frameGroup;
	      context.frames = frames;
	    }

	    var orientation = {
	      x: 'M' + event.x + ', -10000 L' + event.x + ', 10000',
	      y: 'M -10000, ' + event.y + ' L 10000, ' + event.y
	    };

	    attr$1(line, { path: orientation[ axis ], display: '' });

	    var opposite = { x: 'y', y: 'x' };
	    var delta = { x: event.dx, y: event.dy };
	    delta[ opposite[ context.axis ] ] = 0;

	    // update move previews
	    translate(context.dragGroup, delta.x, delta.y);

	    // update resize previews
	    forEach(context.frames, function(frame) {
	      var element = frame.element,
	          initialBounds = frame.initialBounds,
	          width,
	          height;

	      if (context.direction === 'e') {
	        attr$1(element, {
	          width: max$5(initialBounds.width + delta.x, 5)
	        });
	      } else {
	        width = max$5(initialBounds.width - delta.x, 5);

	        attr$1(element, {
	          width: width,
	          x: initialBounds.x + initialBounds.width - width
	        });
	      }

	      if (context.direction === 's') {
	        attr$1(element, {
	          height: max$5(initialBounds.height + delta.y, 5)
	        });
	      } else {
	        height = max$5(initialBounds.height - delta.y, 5);

	        attr$1(element, {
	          height: height,
	          y: initialBounds.y + initialBounds.height - height
	        });
	      }
	    });

	  });

	  // remove move/resize previews
	  eventBus.on('spaceTool.cleanup', function(event) {

	    var context = event.context,
	        movingShapes = context.movingShapes,
	        movingConnections = context.movingConnections,
	        resizingShapes = context.resizingShapes,
	        line = context.line,
	        dragGroup = context.dragGroup,
	        frameGroup = context.frameGroup;

	    // moving shapes
	    forEach(movingShapes, function(shape) {
	      canvas.removeMarker(shape, MARKER_DRAGGING);
	    });

	    // moving connections
	    forEach(movingConnections, function(connection) {
	      canvas.removeMarker(connection, MARKER_DRAGGING);
	    });

	    if (dragGroup) {
	      remove$1(line);
	      remove$1(dragGroup);
	    }

	    forEach(resizingShapes, function(shape) {
	      canvas.removeMarker(shape, MARKER_RESIZING$1);
	    });

	    if (frameGroup) {
	      remove$1(frameGroup);
	    }
	  });
	}

	SpaceToolPreview.$inject = [
	  'eventBus',
	  'elementRegistry',
	  'canvas',
	  'styles',
	  'previewSupport'
	];


	// helpers //////////////////////

	/**
	 * Checks if an element is a connection.
	 */
	function isConnection$2(element) {
	  return element.waypoints;
	}

	var SpaceToolModule = {
	  __init__: ['spaceToolPreview'],
	  __depends__: [
	    DraggingModule,
	    RulesModule,
	    ToolManagerModule,
	    PreviewSupportModule
	  ],
	  spaceTool: ['type', SpaceTool ],
	  spaceToolPreview: ['type', SpaceToolPreview ]
	};

	function BpmnFactory(moddle) {
	  this._model = moddle;
	}

	BpmnFactory.$inject = [ 'moddle' ];


	BpmnFactory.prototype._needsId = function(element) {
	  return isAny(element, [
	    'bpmn:RootElement',
	    'bpmn:FlowElement',
	    'bpmn:MessageFlow',
	    'bpmn:DataAssociation',
	    'bpmn:Artifact',
	    'bpmn:Participant',
	    'bpmn:Lane',
	    'bpmn:LaneSet',
	    'bpmn:Process',
	    'bpmn:Collaboration',
	    'bpmndi:BPMNShape',
	    'bpmndi:BPMNEdge',
	    'bpmndi:BPMNDiagram',
	    'bpmndi:BPMNPlane',
	    'bpmn:Property',
	    'bpmn:CategoryValue'
	  ]);
	};

	BpmnFactory.prototype._ensureId = function(element) {

	  // generate semantic ids for elements
	  // bpmn:SequenceFlow -> SequenceFlow_ID
	  var prefix = (element.$type || '').replace(/^[^:]*:/g, '') + '_';

	  if (!element.id && this._needsId(element)) {
	    element.id = this._model.ids.nextPrefixed(prefix, element);
	  }
	};


	BpmnFactory.prototype.create = function(type, attrs) {
	  var element = this._model.create(type, attrs || {});

	  this._ensureId(element);

	  return element;
	};


	BpmnFactory.prototype.createDiLabel = function() {
	  return this.create('bpmndi:BPMNLabel', {
	    bounds: this.createDiBounds()
	  });
	};


	BpmnFactory.prototype.createDiShape = function(semantic, bounds, attrs) {

	  return this.create('bpmndi:BPMNShape', assign({
	    bpmnElement: semantic,
	    bounds: this.createDiBounds(bounds)
	  }, attrs));
	};


	BpmnFactory.prototype.createDiBounds = function(bounds) {
	  return this.create('dc:Bounds', bounds);
	};


	BpmnFactory.prototype.createDiWaypoints = function(waypoints) {
	  var self = this;

	  return map(waypoints, function(pos) {
	    return self.createDiWaypoint(pos);
	  });
	};

	BpmnFactory.prototype.createDiWaypoint = function(point) {
	  return this.create('dc:Point', pick(point, [ 'x', 'y' ]));
	};


	BpmnFactory.prototype.createDiEdge = function(semantic, waypoints, attrs) {
	  return this.create('bpmndi:BPMNEdge', assign({
	    bpmnElement: semantic
	  }, attrs));
	};

	BpmnFactory.prototype.createDiPlane = function(semantic) {
	  return this.create('bpmndi:BPMNPlane', {
	    bpmnElement: semantic
	  });
	};

	/**
	 * A handler responsible for updating the underlying BPMN 2.0 XML + DI
	 * once changes on the diagram happen
	 */
	function BpmnUpdater(
	    eventBus, bpmnFactory, connectionDocking,
	    translate) {

	  CommandInterceptor.call(this, eventBus);

	  this._bpmnFactory = bpmnFactory;
	  this._translate = translate;

	  var self = this;



	  // connection cropping //////////////////////

	  // crop connection ends during create/update
	  function cropConnection(e) {
	    var context = e.context,
	        connection;

	    if (!context.cropped) {
	      connection = context.connection;
	      connection.waypoints = connectionDocking.getCroppedWaypoints(connection);
	      context.cropped = true;
	    }
	  }

	  this.executed([
	    'connection.layout',
	    'connection.create'
	  ], cropConnection);

	  this.reverted([ 'connection.layout' ], function(e) {
	    delete e.context.cropped;
	  });



	  // BPMN + DI update //////////////////////


	  // update parent
	  function updateParent(e) {
	    var context = e.context;

	    self.updateParent(context.shape || context.connection, context.oldParent);
	  }

	  function reverseUpdateParent(e) {
	    var context = e.context;

	    var element = context.shape || context.connection,
	        // oldParent is the (old) new parent, because we are undoing
	        oldParent = context.parent || context.newParent;

	    self.updateParent(element, oldParent);
	  }

	  this.executed([
	    'shape.move',
	    'shape.create',
	    'shape.delete',
	    'connection.create',
	    'connection.move',
	    'connection.delete'
	  ], ifBpmn(updateParent));

	  this.reverted([
	    'shape.move',
	    'shape.create',
	    'shape.delete',
	    'connection.create',
	    'connection.move',
	    'connection.delete'
	  ], ifBpmn(reverseUpdateParent));

	  /*
	   * ## Updating Parent
	   *
	   * When morphing a Process into a Collaboration or vice-versa,
	   * make sure that both the *semantic* and *di* parent of each element
	   * is updated.
	   *
	   */
	  function updateRoot(event) {
	    var context = event.context,
	        oldRoot = context.oldRoot,
	        children = oldRoot.children;

	    forEach(children, function(child) {
	      if (is$1(child, 'bpmn:BaseElement')) {
	        self.updateParent(child);
	      }
	    });
	  }

	  this.executed([ 'canvas.updateRoot' ], updateRoot);
	  this.reverted([ 'canvas.updateRoot' ], updateRoot);


	  // update bounds
	  function updateBounds(e) {
	    var shape = e.context.shape;

	    if (!is$1(shape, 'bpmn:BaseElement')) {
	      return;
	    }

	    self.updateBounds(shape);
	  }

	  this.executed([ 'shape.move', 'shape.create', 'shape.resize' ], ifBpmn(function(event) {

	    // exclude labels because they're handled separately during shape.changed
	    if (event.context.shape.type === 'label') {
	      return;
	    }

	    updateBounds(event);
	  }));

	  this.reverted([ 'shape.move', 'shape.create', 'shape.resize' ], ifBpmn(function(event) {

	    // exclude labels because they're handled separately during shape.changed
	    if (event.context.shape.type === 'label') {
	      return;
	    }

	    updateBounds(event);
	  }));

	  // Handle labels separately. This is necessary, because the label bounds have to be updated
	  // every time its shape changes, not only on move, create and resize.
	  eventBus.on('shape.changed', function(event) {
	    if (event.element.type === 'label') {
	      updateBounds({ context: { shape: event.element } });
	    }
	  });

	  // attach / detach connection
	  function updateConnection(e) {
	    self.updateConnection(e.context);
	  }

	  this.executed([
	    'connection.create',
	    'connection.move',
	    'connection.delete',
	    'connection.reconnectEnd',
	    'connection.reconnectStart'
	  ], ifBpmn(updateConnection));

	  this.reverted([
	    'connection.create',
	    'connection.move',
	    'connection.delete',
	    'connection.reconnectEnd',
	    'connection.reconnectStart'
	  ], ifBpmn(updateConnection));


	  // update waypoints
	  function updateConnectionWaypoints(e) {
	    self.updateConnectionWaypoints(e.context.connection);
	  }

	  this.executed([
	    'connection.layout',
	    'connection.move',
	    'connection.updateWaypoints',
	  ], ifBpmn(updateConnectionWaypoints));

	  this.reverted([
	    'connection.layout',
	    'connection.move',
	    'connection.updateWaypoints',
	  ], ifBpmn(updateConnectionWaypoints));


	  // update Default & Conditional flows
	  this.executed([
	    'connection.reconnectEnd',
	    'connection.reconnectStart'
	  ], ifBpmn(function(e) {
	    var context = e.context,
	        connection = context.connection,
	        businessObject = getBusinessObject(connection),
	        oldSource = getBusinessObject(context.oldSource),
	        oldTarget = getBusinessObject(context.oldTarget),
	        newSource = getBusinessObject(connection.source),
	        newTarget = getBusinessObject(connection.target);

	    if (oldSource === newSource || oldTarget === newTarget) {
	      return;
	    }

	    // on reconnectStart -> default flow
	    if (oldSource && oldSource.default === businessObject) {
	      context.default = oldSource.default;
	      oldSource.default = undefined;
	    }

	    // on reconnectEnd -> default flow
	    if ((businessObject.sourceRef && businessObject.sourceRef.default) &&
	        !(is$1(newTarget, 'bpmn:Activity') ||
	          is$1(newTarget, 'bpmn:EndEvent') ||
	          is$1(newTarget, 'bpmn:Gateway') ||
	          is$1(newTarget, 'bpmn:IntermediateThrowEvent'))) {
	      context.default = businessObject.sourceRef.default;
	      businessObject.sourceRef.default = undefined;
	    }

	    // on reconnectStart -> conditional flow
	    if (oldSource && (businessObject.conditionExpression) &&
	      !(is$1(newSource, 'bpmn:Activity') ||
	        is$1(newSource, 'bpmn:Gateway'))) {
	      context.conditionExpression = businessObject.conditionExpression;
	      businessObject.conditionExpression = undefined;
	    }

	    // on reconnectEnd -> conditional flow
	    if (oldTarget && (businessObject.conditionExpression) &&
	        !(is$1(newTarget, 'bpmn:Activity') ||
	          is$1(newTarget, 'bpmn:EndEvent') ||
	          is$1(newTarget, 'bpmn:Gateway') ||
	          is$1(newTarget, 'bpmn:IntermediateThrowEvent'))) {
	      context.conditionExpression = businessObject.conditionExpression;
	      businessObject.conditionExpression = undefined;
	    }
	  }));

	  this.reverted([
	    'connection.reconnectEnd',
	    'connection.reconnectStart'
	  ], ifBpmn(function(e) {
	    var context = e.context,
	        connection = context.connection,
	        businessObject = getBusinessObject(connection),
	        newSource = getBusinessObject(connection.source);

	    // default flow
	    if (context.default) {
	      if (is$1(newSource, 'bpmn:ExclusiveGateway') || is$1(newSource, 'bpmn:InclusiveGateway') ||
	          is$1(newSource, 'bpmn:Activity')) {
	        newSource.default = context.default;
	      }
	    }

	    // conditional flow
	    if (context.conditionExpression && is$1(newSource, 'bpmn:Activity')) {
	      businessObject.conditionExpression = context.conditionExpression;
	    }
	  }));

	  // update attachments
	  function updateAttachment(e) {
	    self.updateAttachment(e.context);
	  }

	  this.executed([ 'element.updateAttachment' ], ifBpmn(updateAttachment));
	  this.reverted([ 'element.updateAttachment' ], ifBpmn(updateAttachment));
	}

	inherits_browser(BpmnUpdater, CommandInterceptor);

	BpmnUpdater.$inject = [
	  'eventBus',
	  'bpmnFactory',
	  'connectionDocking',
	  'translate'
	];


	// implementation //////////////////////

	BpmnUpdater.prototype.updateAttachment = function(context) {

	  var shape = context.shape,
	      businessObject = shape.businessObject,
	      host = shape.host;

	  businessObject.attachedToRef = host && host.businessObject;
	};

	BpmnUpdater.prototype.updateParent = function(element, oldParent) {
	  // do not update BPMN 2.0 label parent
	  if (element instanceof Label) {
	    return;
	  }

	  // data stores in collaborations are handled seperately by DataStoreBehavior
	  if (is$1(element, 'bpmn:DataStoreReference') &&
	      element.parent &&
	      is$1(element.parent, 'bpmn:Collaboration')) {
	    return;
	  }

	  var parentShape = element.parent;

	  var businessObject = element.businessObject,
	      parentBusinessObject = parentShape && parentShape.businessObject,
	      parentDi = parentBusinessObject && parentBusinessObject.di;

	  if (is$1(element, 'bpmn:FlowNode')) {
	    this.updateFlowNodeRefs(businessObject, parentBusinessObject, oldParent && oldParent.businessObject);
	  }

	  if (is$1(element, 'bpmn:DataOutputAssociation')) {
	    if (element.source) {
	      parentBusinessObject = element.source.businessObject;
	    } else {
	      parentBusinessObject = null;
	    }
	  }

	  if (is$1(element, 'bpmn:DataInputAssociation')) {
	    if (element.target) {
	      parentBusinessObject = element.target.businessObject;
	    } else {
	      parentBusinessObject = null;
	    }
	  }

	  this.updateSemanticParent(businessObject, parentBusinessObject);

	  if (is$1(element, 'bpmn:DataObjectReference') && businessObject.dataObjectRef) {
	    this.updateSemanticParent(businessObject.dataObjectRef, parentBusinessObject);
	  }

	  this.updateDiParent(businessObject.di, parentDi);
	};


	BpmnUpdater.prototype.updateBounds = function(shape) {

	  var di = shape.businessObject.di;

	  var target = (shape instanceof Label) ? this._getLabel(di) : di;

	  var bounds = target.bounds;

	  if (!bounds) {
	    bounds = this._bpmnFactory.createDiBounds();
	    target.set('bounds', bounds);
	  }

	  assign(bounds, {
	    x: shape.x,
	    y: shape.y,
	    width: shape.width,
	    height: shape.height
	  });
	};

	BpmnUpdater.prototype.updateFlowNodeRefs = function(businessObject, newContainment, oldContainment) {

	  if (oldContainment === newContainment) {
	    return;
	  }

	  var oldRefs, newRefs;

	  if (is$1 (oldContainment, 'bpmn:Lane')) {
	    oldRefs = oldContainment.get('flowNodeRef');
	    remove$2(oldRefs, businessObject);
	  }

	  if (is$1(newContainment, 'bpmn:Lane')) {
	    newRefs = newContainment.get('flowNodeRef');
	    add$1(newRefs, businessObject);
	  }
	};


	// update existing sourceElement and targetElement di information
	BpmnUpdater.prototype.updateDiConnection = function(di, newSource, newTarget) {

	  if (di.sourceElement && di.sourceElement.bpmnElement !== newSource) {
	    di.sourceElement = newSource && newSource.di;
	  }

	  if (di.targetElement && di.targetElement.bpmnElement !== newTarget) {
	    di.targetElement = newTarget && newTarget.di;
	  }

	};


	BpmnUpdater.prototype.updateDiParent = function(di, parentDi) {

	  if (parentDi && !is$1(parentDi, 'bpmndi:BPMNPlane')) {
	    parentDi = parentDi.$parent;
	  }

	  if (di.$parent === parentDi) {
	    return;
	  }

	  var planeElements = (parentDi || di.$parent).get('planeElement');

	  if (parentDi) {
	    planeElements.push(di);
	    di.$parent = parentDi;
	  } else {
	    remove$2(planeElements, di);
	    di.$parent = null;
	  }
	};

	function getDefinitions(element) {
	  while (element && !is$1(element, 'bpmn:Definitions')) {
	    element = element.$parent;
	  }

	  return element;
	}

	BpmnUpdater.prototype.getLaneSet = function(container) {

	  var laneSet, laneSets;

	  // bpmn:Lane
	  if (is$1(container, 'bpmn:Lane')) {
	    laneSet = container.childLaneSet;

	    if (!laneSet) {
	      laneSet = this._bpmnFactory.create('bpmn:LaneSet');
	      container.childLaneSet = laneSet;
	      laneSet.$parent = container;
	    }

	    return laneSet;
	  }

	  // bpmn:Participant
	  if (is$1(container, 'bpmn:Participant')) {
	    container = container.processRef;
	  }

	  // bpmn:FlowElementsContainer
	  laneSets = container.get('laneSets');
	  laneSet = laneSets[0];

	  if (!laneSet) {
	    laneSet = this._bpmnFactory.create('bpmn:LaneSet');
	    laneSet.$parent = container;
	    laneSets.push(laneSet);
	  }

	  return laneSet;
	};

	BpmnUpdater.prototype.updateSemanticParent = function(businessObject, newParent, visualParent) {

	  var containment,
	      translate = this._translate;

	  if (businessObject.$parent === newParent) {
	    return;
	  }

	  if (is$1(businessObject, 'bpmn:DataInput') || is$1(businessObject, 'bpmn:DataOutput')) {

	    if (is$1(newParent, 'bpmn:Participant') && 'processRef' in newParent) {
	      newParent = newParent.processRef;
	    }

	    // already in correct ioSpecification
	    if ('ioSpecification' in newParent && newParent.ioSpecification === businessObject.$parent) {
	      return;
	    }
	  }

	  if (is$1(businessObject, 'bpmn:Lane')) {

	    if (newParent) {
	      newParent = this.getLaneSet(newParent);
	    }

	    containment = 'lanes';
	  } else

	  if (is$1(businessObject, 'bpmn:FlowElement')) {

	    if (newParent) {

	      if (is$1(newParent, 'bpmn:Participant')) {
	        newParent = newParent.processRef;
	      } else

	      if (is$1(newParent, 'bpmn:Lane')) {
	        do {
	          // unwrap Lane -> LaneSet -> (Lane | FlowElementsContainer)
	          newParent = newParent.$parent.$parent;
	        } while (is$1(newParent, 'bpmn:Lane'));

	      }
	    }

	    containment = 'flowElements';

	  } else

	  if (is$1(businessObject, 'bpmn:Artifact')) {

	    while (newParent &&
	           !is$1(newParent, 'bpmn:Process') &&
	           !is$1(newParent, 'bpmn:SubProcess') &&
	           !is$1(newParent, 'bpmn:Collaboration')) {

	      if (is$1(newParent, 'bpmn:Participant')) {
	        newParent = newParent.processRef;
	        break;
	      } else {
	        newParent = newParent.$parent;
	      }
	    }

	    containment = 'artifacts';
	  } else

	  if (is$1(businessObject, 'bpmn:MessageFlow')) {
	    containment = 'messageFlows';

	  } else

	  if (is$1(businessObject, 'bpmn:Participant')) {
	    containment = 'participants';

	    // make sure the participants process is properly attached / detached
	    // from the XML document

	    var process = businessObject.processRef,
	        definitions;

	    if (process) {
	      definitions = getDefinitions(businessObject.$parent || newParent);

	      if (businessObject.$parent) {
	        remove$2(definitions.get('rootElements'), process);
	        process.$parent = null;
	      }

	      if (newParent) {
	        add$1(definitions.get('rootElements'), process);
	        process.$parent = definitions;
	      }
	    }
	  } else

	  if (is$1(businessObject, 'bpmn:DataOutputAssociation')) {
	    containment = 'dataOutputAssociations';
	  } else

	  if (is$1(businessObject, 'bpmn:DataInputAssociation')) {
	    containment = 'dataInputAssociations';
	  }

	  if (!containment) {
	    throw new Error(translate(
	      'no parent for {element} in {parent}',
	      {
	        element: businessObject.id,
	        parent: newParent.id
	      }
	    ));
	  }

	  var children;

	  if (businessObject.$parent) {
	    // remove from old parent
	    children = businessObject.$parent.get(containment);
	    remove$2(children, businessObject);
	  }

	  if (!newParent) {
	    businessObject.$parent = null;
	  } else {
	    // add to new parent
	    children = newParent.get(containment);
	    children.push(businessObject);
	    businessObject.$parent = newParent;
	  }

	  if (visualParent) {
	    var diChildren = visualParent.get(containment);

	    remove$2(children, businessObject);

	    if (newParent) {

	      if (!diChildren) {
	        diChildren = [];
	        newParent.set(containment, diChildren);
	      }

	      diChildren.push(businessObject);
	    }
	  }
	};


	BpmnUpdater.prototype.updateConnectionWaypoints = function(connection) {
	  connection.businessObject.di.set('waypoint', this._bpmnFactory.createDiWaypoints(connection.waypoints));
	};


	BpmnUpdater.prototype.updateConnection = function(context) {

	  var connection = context.connection,
	      businessObject = getBusinessObject(connection),
	      newSource = getBusinessObject(connection.source),
	      newTarget = getBusinessObject(connection.target),
	      visualParent;

	  if (!is$1(businessObject, 'bpmn:DataAssociation')) {

	    var inverseSet = is$1(businessObject, 'bpmn:SequenceFlow');

	    if (businessObject.sourceRef !== newSource) {
	      if (inverseSet) {
	        remove$2(businessObject.sourceRef && businessObject.sourceRef.get('outgoing'), businessObject);

	        if (newSource && newSource.get('outgoing')) {
	          newSource.get('outgoing').push(businessObject);
	        }
	      }

	      businessObject.sourceRef = newSource;
	    }

	    if (businessObject.targetRef !== newTarget) {
	      if (inverseSet) {
	        remove$2(businessObject.targetRef && businessObject.targetRef.get('incoming'), businessObject);

	        if (newTarget && newTarget.get('incoming')) {
	          newTarget.get('incoming').push(businessObject);
	        }
	      }

	      businessObject.targetRef = newTarget;
	    }
	  } else

	  if (is$1(businessObject, 'bpmn:DataInputAssociation')) {
	    // handle obnoxious isMsome sourceRef
	    businessObject.get('sourceRef')[0] = newSource;

	    visualParent = context.parent || context.newParent || newTarget;

	    this.updateSemanticParent(businessObject, newTarget, visualParent);
	  } else

	  if (is$1(businessObject, 'bpmn:DataOutputAssociation')) {
	    visualParent = context.parent || context.newParent || newSource;

	    this.updateSemanticParent(businessObject, newSource, visualParent);

	    // targetRef = new target
	    businessObject.targetRef = newTarget;
	  }

	  this.updateConnectionWaypoints(connection);

	  this.updateDiConnection(businessObject.di, newSource, newTarget);
	};


	// helpers //////////////////////

	BpmnUpdater.prototype._getLabel = function(di) {
	  if (!di.label) {
	    di.label = this._bpmnFactory.createDiLabel();
	  }

	  return di.label;
	};


	/**
	 * Make sure the event listener is only called
	 * if the touched element is a BPMN element.
	 *
	 * @param  {Function} fn
	 * @return {Function} guarded function
	 */
	function ifBpmn(fn) {

	  return function(event) {

	    var context = event.context,
	        element = context.shape || context.connection;

	    if (is$1(element, 'bpmn:BaseElement')) {
	      fn(event);
	    }
	  };
	}

	/**
	 * A bpmn-aware factory for diagram-js shapes
	 */
	function ElementFactory$1(bpmnFactory, moddle, translate) {
	  ElementFactory.call(this);

	  this._bpmnFactory = bpmnFactory;
	  this._moddle = moddle;
	  this._translate = translate;
	}

	inherits_browser(ElementFactory$1, ElementFactory);

	ElementFactory$1.$inject = [
	  'bpmnFactory',
	  'moddle',
	  'translate'
	];

	ElementFactory$1.prototype.baseCreate = ElementFactory.prototype.create;

	ElementFactory$1.prototype.create = function(elementType, attrs) {
	  // no special magic for labels,
	  // we assume their businessObjects have already been created
	  // and wired via attrs
	  if (elementType === 'label') {
	    return this.baseCreate(elementType, assign({ type: 'label' }, DEFAULT_LABEL_SIZE$1, attrs));
	  }

	  return this.createBpmnElement(elementType, attrs);
	};

	ElementFactory$1.prototype.createBpmnElement = function(elementType, attrs) {
	  var size,
	      translate = this._translate;

	  attrs = attrs || {};

	  var businessObject = attrs.businessObject;

	  if (!businessObject) {
	    if (!attrs.type) {
	      throw new Error(translate('no shape type specified'));
	    }

	    businessObject = this._bpmnFactory.create(attrs.type);
	  }

	  if (!businessObject.di) {
	    if (elementType === 'root') {
	      businessObject.di = this._bpmnFactory.createDiPlane(businessObject, [], {
	        id: businessObject.id + '_di'
	      });
	    } else
	    if (elementType === 'connection') {
	      businessObject.di = this._bpmnFactory.createDiEdge(businessObject, [], {
	        id: businessObject.id + '_di'
	      });
	    } else {
	      businessObject.di = this._bpmnFactory.createDiShape(businessObject, {}, {
	        id: businessObject.id + '_di'
	      });
	    }
	  }

	  if (is$1(businessObject, 'bpmn:Group')) {
	    attrs = assign({
	      isFrame: true
	    }, attrs);
	  }

	  if (attrs.colors) {
	    assign(businessObject.di, attrs.colors);

	    delete attrs.colors;
	  }

	  applyAttributes(businessObject, attrs, [
	    'processRef',
	    'isInterrupting',
	    'associationDirection',
	    'isForCompensation'
	  ]);

	  if (attrs.isExpanded) {
	    applyAttribute(businessObject.di, attrs, 'isExpanded');
	  }

	  if (is$1(businessObject, 'bpmn:ExclusiveGateway')) {
	    businessObject.di.isMarkerVisible = true;
	  }

	  var eventDefinitions,
	      newEventDefinition;

	  if (attrs.eventDefinitionType) {
	    eventDefinitions = businessObject.get('eventDefinitions') || [];
	    newEventDefinition = this._moddle.create(attrs.eventDefinitionType);

	    if (attrs.eventDefinitionType === 'bpmn:ConditionalEventDefinition') {
	      newEventDefinition.condition = this._moddle.create('bpmn:FormalExpression');
	    }

	    eventDefinitions.push(newEventDefinition);

	    newEventDefinition.$parent = businessObject;
	    businessObject.eventDefinitions = eventDefinitions;

	    delete attrs.eventDefinitionType;
	  }

	  size = this._getDefaultSize(businessObject);

	  attrs = assign({
	    businessObject: businessObject,
	    id: businessObject.id
	  }, size, attrs);

	  return this.baseCreate(elementType, attrs);
	};


	ElementFactory$1.prototype._getDefaultSize = function(semantic) {

	  if (is$1(semantic, 'bpmn:SubProcess')) {

	    if (isExpanded(semantic)) {
	      return { width: 350, height: 200 };
	    } else {
	      return { width: 100, height: 80 };
	    }
	  }

	  if (is$1(semantic, 'bpmn:Task')) {
	    return { width: 100, height: 80 };
	  }

	  if (is$1(semantic, 'bpmn:Gateway')) {
	    return { width: 50, height: 50 };
	  }

	  if (is$1(semantic, 'bpmn:Event')) {
	    return { width: 36, height: 36 };
	  }

	  if (is$1(semantic, 'bpmn:Participant')) {
	    if (!isExpanded(semantic)) {
	      return { width: 400, height: 60 };
	    } else {
	      return { width: 600, height: 250 };
	    }
	  }

	  if (is$1(semantic, 'bpmn:Lane')) {
	    return { width: 400, height: 100 };
	  }

	  if (is$1(semantic, 'bpmn:DataObjectReference')) {
	    return { width: 36, height: 50 };
	  }

	  if (is$1(semantic, 'bpmn:DataStoreReference')) {
	    return { width: 50, height: 50 };
	  }

	  if (is$1(semantic, 'bpmn:TextAnnotation')) {
	    return { width: 100, height: 30 };
	  }

	  if (is$1(semantic, 'bpmn:Group')) {
	    return { width: 300, height: 300 };
	  }

	  return { width: 100, height: 80 };
	};


	ElementFactory$1.prototype.createParticipantShape = function(collapsed) {

	  var attrs = { type: 'bpmn:Participant' };

	  if (!collapsed) {
	    attrs.processRef = this._bpmnFactory.create('bpmn:Process');
	  }

	  return this.createShape(attrs);
	};


	// helpers //////////////////////

	/**
	 * Apply attributes from a map to the given element,
	 * remove attribute from the map on application.
	 *
	 * @param {Base} element
	 * @param {Object} attrs (in/out map of attributes)
	 * @param {Array<String>} attributeNames name of attributes to apply
	 */
	function applyAttributes(element, attrs, attributeNames) {

	  forEach(attributeNames, function(property) {
	    if (attrs[property] !== undefined) {
	      applyAttribute(element, attrs, property);
	    }
	  });
	}

	/**
	 * Apply named property to element and drain it from the attrs
	 * collection.
	 *
	 * @param {Base} element
	 * @param {Object} attrs (in/out map of attributes)
	 * @param {String} attributeName to apply
	 */
	function applyAttribute(element, attrs, attributeName) {
	  element[attributeName] = attrs[attributeName];

	  delete attrs[attributeName];
	}

	/**
	 * A handler that implements reversible appending of shapes
	 * to a source shape.
	 *
	 * @param {canvas} Canvas
	 * @param {elementFactory} ElementFactory
	 * @param {modeling} Modeling
	 */
	function AppendShapeHandler(modeling) {
	  this._modeling = modeling;
	}

	AppendShapeHandler.$inject = [ 'modeling' ];


	// api //////////////////////


	/**
	 * Creates a new shape
	 *
	 * @param {Object} context
	 * @param {ElementDescriptor} context.shape the new shape
	 * @param {ElementDescriptor} context.source the source object
	 * @param {ElementDescriptor} context.parent the parent object
	 * @param {Point} context.position position of the new element
	 */
	AppendShapeHandler.prototype.preExecute = function(context) {

	  var source = context.source;

	  if (!source) {
	    throw new Error('source required');
	  }

	  var target = context.target || source.parent,
	      shape = context.shape;

	  shape = context.shape =
	    this._modeling.createShape(
	      shape,
	      context.position,
	      target, { attach: context.attach });

	  context.shape = shape;
	};

	AppendShapeHandler.prototype.postExecute = function(context) {
	  var parent = context.connectionParent || context.shape.parent;

	  if (!existsConnection(context.source, context.shape)) {

	    // create connection
	    this._modeling.connect(context.source, context.shape, context.connection, parent);
	  }
	};


	function existsConnection(source, target) {
	  return some(source.outgoing, function(c) {
	    return c.target === target;
	  });
	}

	var round$8 = Math.round;


	/**
	 * A handler that implements reversible addition of shapes.
	 *
	 * @param {canvas} Canvas
	 */
	function CreateShapeHandler(canvas) {
	  this._canvas = canvas;
	}

	CreateShapeHandler.$inject = [ 'canvas' ];


	// api //////////////////////


	/**
	 * Appends a shape to a target shape
	 *
	 * @param {Object} context
	 * @param {djs.model.Base} context.parent the parent object
	 * @param {Point} context.position position of the new element
	 */
	CreateShapeHandler.prototype.execute = function(context) {

	  var shape = context.shape,
	      positionOrBounds = context.position,
	      parent = context.parent,
	      parentIndex = context.parentIndex;

	  if (!parent) {
	    throw new Error('parent required');
	  }

	  if (!positionOrBounds) {
	    throw new Error('position required');
	  }

	  // (1) add at event center position _or_ at given bounds
	  if (positionOrBounds.width !== undefined) {
	    assign(shape, positionOrBounds);
	  } else {
	    assign(shape, {
	      x: positionOrBounds.x - round$8(shape.width / 2),
	      y: positionOrBounds.y - round$8(shape.height / 2)
	    });
	  }

	  // (2) add to canvas
	  this._canvas.addShape(shape, parent, parentIndex);

	  return shape;
	};


	/**
	 * Undo append by removing the shape
	 */
	CreateShapeHandler.prototype.revert = function(context) {

	  // (3) remove form canvas
	  this._canvas.removeShape(context.shape);
	};

	/**
	 * A handler that implements reversible deletion of shapes.
	 *
	 */
	function DeleteShapeHandler(canvas, modeling) {
	  this._canvas = canvas;
	  this._modeling = modeling;
	}

	DeleteShapeHandler.$inject = [ 'canvas', 'modeling' ];


	/**
	 * - Remove connections
	 * - Remove all direct children
	 */
	DeleteShapeHandler.prototype.preExecute = function(context) {

	  var modeling = this._modeling;

	  var shape = context.shape;

	  // remove connections
	  saveClear(shape.incoming, function(connection) {
	    // To make sure that the connection isn't removed twice
	    // For example if a container is removed
	    modeling.removeConnection(connection, { nested: true });
	  });

	  saveClear(shape.outgoing, function(connection) {
	    modeling.removeConnection(connection, { nested: true });
	  });

	  // remove child shapes and connections
	  saveClear(shape.children, function(child) {
	    if (isConnection$3(child)) {
	      modeling.removeConnection(child, { nested: true });
	    } else {
	      modeling.removeShape(child, { nested: true });
	    }
	  });
	};

	/**
	 * Remove shape and remember the parent
	 */
	DeleteShapeHandler.prototype.execute = function(context) {
	  var canvas = this._canvas;

	  var shape = context.shape,
	      oldParent = shape.parent;

	  context.oldParent = oldParent;

	  // remove containment
	  context.oldParentIndex = indexOf$1(oldParent.children, shape);

	  // remove shape
	  canvas.removeShape(shape);

	  return shape;
	};


	/**
	 * Command revert implementation
	 */
	DeleteShapeHandler.prototype.revert = function(context) {

	  var canvas = this._canvas;

	  var shape = context.shape,
	      oldParent = context.oldParent,
	      oldParentIndex = context.oldParentIndex;

	  // restore containment
	  add$1(oldParent.children, shape, oldParentIndex);

	  canvas.addShape(shape, oldParent);

	  return shape;
	};

	function isConnection$3(element) {
	  return element.waypoints;
	}

	function getResizedSourceAnchor(connection, shape, oldBounds) {

	  var waypoints = safeGetWaypoints(connection),
	      oldAnchor = waypoints[0];

	  return getNewAttachPoint(oldAnchor.original || oldAnchor, oldBounds, shape);
	}


	function getResizedTargetAnchor(connection, shape, oldBounds) {

	  var waypoints = safeGetWaypoints(connection),
	      oldAnchor = waypoints[waypoints.length - 1];

	  return getNewAttachPoint(oldAnchor.original || oldAnchor, oldBounds, shape);
	}


	function getMovedSourceAnchor(connection, source, moveDelta) {
	  return getResizedSourceAnchor(connection, source, substractPosition(source, moveDelta));
	}


	function getMovedTargetAnchor(connection, target, moveDelta) {
	  return getResizedTargetAnchor(connection, target, substractPosition(target, moveDelta));
	}


	// helpers //////////////////////

	function substractPosition(bounds, delta) {
	  return {
	    x: bounds.x - delta.x,
	    y: bounds.y - delta.y,
	    width: bounds.width,
	    height: bounds.height
	  };
	}


	/**
	 * Return waypoints of given connection; throw if non exists (should not happen!!).
	 *
	 * @param {Connection} connection
	 *
	 * @return {Array<Point>}
	 */
	function safeGetWaypoints(connection) {

	  var waypoints = connection.waypoints;

	  if (!waypoints.length) {
	    throw new Error('connection#' + connection.id + ': no waypoints');
	  }

	  return waypoints;
	}

	function MoveClosure() {

	  this.allShapes = {};
	  this.allConnections = {};

	  this.enclosedElements = {};
	  this.enclosedConnections = {};

	  this.topLevel = {};
	}


	MoveClosure.prototype.add = function(element, isTopLevel) {
	  return this.addAll([ element ], isTopLevel);
	};


	MoveClosure.prototype.addAll = function(elements, isTopLevel) {

	  var newClosure = getClosure(elements, !!isTopLevel, this);

	  assign(this, newClosure);

	  return this;
	};

	/**
	 * A helper that is able to carry out serialized move
	 * operations on multiple elements.
	 *
	 * @param {Modeling} modeling
	 */
	function MoveHelper(modeling) {
	  this._modeling = modeling;
	}

	/**
	 * Move the specified elements and all children by the given delta.
	 *
	 * This moves all enclosed connections, too and layouts all affected
	 * external connections.
	 *
	 * @param  {Array<djs.model.Base>} elements
	 * @param  {Point} delta
	 * @param  {djs.model.Base} newParent applied to the first level of shapes
	 *
	 * @return {Array<djs.model.Base>} list of touched elements
	 */
	MoveHelper.prototype.moveRecursive = function(elements, delta, newParent) {
	  if (!elements) {
	    return [];
	  } else {
	    return this.moveClosure(this.getClosure(elements), delta, newParent);
	  }
	};

	/**
	 * Move the given closure of elmements.
	 *
	 * @param {Object} closure
	 * @param {Point} delta
	 * @param {djs.model.Base} [newParent]
	 * @param {djs.model.Base} [newHost]
	 */
	MoveHelper.prototype.moveClosure = function(closure, delta, newParent, newHost, primaryShape) {
	  var modeling = this._modeling;

	  var allShapes = closure.allShapes,
	      allConnections = closure.allConnections,
	      enclosedConnections = closure.enclosedConnections,
	      topLevel = closure.topLevel,
	      keepParent = false;

	  if (primaryShape && primaryShape.parent === newParent) {
	    keepParent = true;
	  }

	  // move all shapes
	  forEach(allShapes, function(shape) {

	    // move the element according to the given delta
	    modeling.moveShape(shape, delta, topLevel[shape.id] && !keepParent && newParent, {
	      recurse: false,
	      layout: false
	    });
	  });

	  // move all child connections / layout external connections
	  forEach(allConnections, function(c) {

	    var sourceMoved = !!allShapes[c.source.id],
	        targetMoved = !!allShapes[c.target.id];

	    if (enclosedConnections[c.id] && sourceMoved && targetMoved) {
	      modeling.moveConnection(c, delta, topLevel[c.id] && !keepParent && newParent);
	    } else {
	      modeling.layoutConnection(c, {
	        connectionStart: sourceMoved && getMovedSourceAnchor(c, c.source, delta),
	        connectionEnd: targetMoved && getMovedTargetAnchor(c, c.target, delta)
	      });
	    }
	  });
	};

	/**
	 * Returns the closure for the selected elements
	 *
	 * @param  {Array<djs.model.Base>} elements
	 * @return {MoveClosure} closure
	 */
	MoveHelper.prototype.getClosure = function(elements) {
	  return new MoveClosure().addAll(elements, true);
	};

	/**
	 * A handler that implements reversible moving of shapes.
	 */
	function MoveShapeHandler(modeling) {
	  this._modeling = modeling;

	  this._helper = new MoveHelper(modeling);
	}

	MoveShapeHandler.$inject = [ 'modeling' ];


	MoveShapeHandler.prototype.execute = function(context) {

	  var shape = context.shape,
	      delta = context.delta,
	      newParent = context.newParent || shape.parent,
	      newParentIndex = context.newParentIndex,
	      oldParent = shape.parent;

	  context.oldBounds = pick(shape, [ 'x', 'y', 'width', 'height']);

	  // save old parent in context
	  context.oldParent = oldParent;
	  context.oldParentIndex = remove$2(oldParent.children, shape);

	  // add to new parent at position
	  add$1(newParent.children, shape, newParentIndex);

	  // update shape parent + position
	  assign(shape, {
	    parent: newParent,
	    x: shape.x + delta.x,
	    y: shape.y + delta.y
	  });

	  return shape;
	};

	MoveShapeHandler.prototype.postExecute = function(context) {

	  var shape = context.shape,
	      delta = context.delta,
	      hints = context.hints;

	  var modeling = this._modeling;

	  if (hints.layout !== false) {

	    forEach(shape.incoming, function(c) {
	      modeling.layoutConnection(c, {
	        connectionEnd: getMovedTargetAnchor(c, shape, delta)
	      });
	    });

	    forEach(shape.outgoing, function(c) {
	      modeling.layoutConnection(c, {
	        connectionStart: getMovedSourceAnchor(c, shape, delta)
	      });
	    });
	  }

	  if (hints.recurse !== false) {
	    this.moveChildren(context);
	  }
	};

	MoveShapeHandler.prototype.revert = function(context) {

	  var shape = context.shape,
	      oldParent = context.oldParent,
	      oldParentIndex = context.oldParentIndex,
	      delta = context.delta;

	  // restore previous location in old parent
	  add$1(oldParent.children, shape, oldParentIndex);

	  // revert to old position and parent
	  assign(shape, {
	    parent: oldParent,
	    x: shape.x - delta.x,
	    y: shape.y - delta.y
	  });

	  return shape;
	};

	MoveShapeHandler.prototype.moveChildren = function(context) {

	  var delta = context.delta,
	      shape = context.shape;

	  this._helper.moveRecursive(shape.children, delta, null);
	};

	MoveShapeHandler.prototype.getNewParent = function(context) {
	  return context.newParent || context.shape.parent;
	};

	/**
	 * A handler that implements reversible resizing of shapes.
	 *
	 * @param {Modeling} modeling
	 */
	function ResizeShapeHandler(modeling) {
	  this._modeling = modeling;
	}

	ResizeShapeHandler.$inject = [ 'modeling' ];

	/**
	 * {
	 *   shape: {....}
	 *   newBounds: {
	 *     width:  20,
	 *     height: 40,
	 *     x:       5,
	 *     y:      10
	 *   }
	 *
	 * }
	 */
	ResizeShapeHandler.prototype.execute = function(context) {
	  var shape = context.shape,
	      newBounds = context.newBounds,
	      minBounds = context.minBounds;

	  if (newBounds.x === undefined || newBounds.y === undefined ||
	      newBounds.width === undefined || newBounds.height === undefined) {
	    throw new Error('newBounds must have {x, y, width, height} properties');
	  }

	  if (minBounds && (newBounds.width < minBounds.width
	    || newBounds.height < minBounds.height)) {
	    throw new Error('width and height cannot be less than minimum height and width');
	  } else if (!minBounds
	    && newBounds.width < 10 || newBounds.height < 10) {
	    throw new Error('width and height cannot be less than 10px');
	  }

	  // save old bbox in context
	  context.oldBounds = {
	    width:  shape.width,
	    height: shape.height,
	    x:      shape.x,
	    y:      shape.y
	  };

	  // update shape
	  assign(shape, {
	    width:  newBounds.width,
	    height: newBounds.height,
	    x:      newBounds.x,
	    y:      newBounds.y
	  });

	  return shape;
	};

	ResizeShapeHandler.prototype.postExecute = function(context) {

	  var shape = context.shape,
	      oldBounds = context.oldBounds;

	  var modeling = this._modeling;

	  forEach(shape.incoming, function(c) {
	    modeling.layoutConnection(c, {
	      connectionEnd: getResizedTargetAnchor(c, shape, oldBounds)
	    });
	  });

	  forEach(shape.outgoing, function(c) {
	    modeling.layoutConnection(c, {
	      connectionStart: getResizedSourceAnchor(c, shape, oldBounds)
	    });
	  });

	};

	ResizeShapeHandler.prototype.revert = function(context) {

	  var shape = context.shape,
	      oldBounds = context.oldBounds;

	  // restore previous bbox
	  assign(shape, {
	    width:  oldBounds.width,
	    height: oldBounds.height,
	    x:      oldBounds.x,
	    y:      oldBounds.y
	  });

	  return shape;
	};

	/**
	 * A handler that implements reversible replacing of shapes.
	 * Internally the old shape will be removed and the new shape will be added.
	 *
	 *
	 * @class
	 * @constructor
	 *
	 * @param {canvas} Canvas
	 */
	function ReplaceShapeHandler(modeling, rules) {
	  this._modeling = modeling;
	  this._rules = rules;
	}

	ReplaceShapeHandler.$inject = [ 'modeling', 'rules' ];


	// api //////////////////////


	/**
	 * Replaces a shape with an replacement Element.
	 *
	 * The newData object should contain type, x, y.
	 *
	 * If possible also the incoming/outgoing connection
	 * will be restored.
	 *
	 * @param {Object} context
	 */
	ReplaceShapeHandler.prototype.preExecute = function(context) {

	  var self = this,
	      modeling = this._modeling,
	      rules = this._rules;

	  var oldShape = context.oldShape,
	      newData = context.newData,
	      hints = context.hints,
	      newShape;

	  function canReconnect(type, source, target, connection) {
	    return rules.allowed(type, {
	      source: source,
	      target: target,
	      connection: connection
	    });
	  }


	  // (1) place a new shape at the given position

	  var position = {
	    x: newData.x,
	    y: newData.y
	  };

	  newShape = context.newShape =
	    context.newShape ||
	    self.createShape(newData, position, oldShape.parent, hints);


	  // (2) update the host

	  if (oldShape.host) {
	    modeling.updateAttachment(newShape, oldShape.host);
	  }


	  // (3) adopt all children from the old shape

	  var children;

	  if (hints.moveChildren !== false) {
	    children = oldShape.children.slice();

	    modeling.moveElements(children, { x: 0, y: 0 }, newShape);
	  }

	  // (4) reconnect connections to the new shape (where allowed)

	  var incoming = oldShape.incoming.slice(),
	      outgoing = oldShape.outgoing.slice();

	  forEach(incoming, function(connection) {
	    var waypoints = connection.waypoints,
	        docking = waypoints[waypoints.length - 1],
	        source = connection.source,
	        allowed = canReconnect('connection.reconnectEnd', source, newShape, connection);

	    if (allowed) {
	      self.reconnectEnd(connection, newShape, docking, { layoutConnection: hints.layoutConnection });
	    }
	  });

	  forEach(outgoing, function(connection) {
	    var waypoints = connection.waypoints,
	        docking = waypoints[0],
	        target = connection.target,
	        allowed = canReconnect('connection.reconnectStart', newShape, target, connection);

	    if (allowed) {
	      self.reconnectStart(connection, newShape, docking, { layoutConnection: hints.layoutConnection });
	    }

	  });
	};


	ReplaceShapeHandler.prototype.postExecute = function(context) {
	  var modeling = this._modeling;

	  var oldShape = context.oldShape,
	      newShape = context.newShape,
	      layoutConnection = context.hints.layoutConnection;

	  // do not layout if explicitly excluded
	  if (layoutConnection !== false) {
	    forEach(newShape.incoming, function(c) {
	      modeling.layoutConnection(c, { endChanged: true });
	    });

	    forEach(newShape.outgoing, function(c) {
	      modeling.layoutConnection(c, { startChanged: true });
	    });
	  }

	  modeling.removeShape(oldShape);
	};


	ReplaceShapeHandler.prototype.execute = function(context) {};

	ReplaceShapeHandler.prototype.revert = function(context) {};


	ReplaceShapeHandler.prototype.createShape = function(shape, position, target, hints) {
	  return this._modeling.createShape(shape, position, target, hints);
	};


	ReplaceShapeHandler.prototype.reconnectStart = function(connection, newSource, dockingPoint, hints) {
	  this._modeling.reconnectStart(connection, newSource, dockingPoint, hints);
	};


	ReplaceShapeHandler.prototype.reconnectEnd = function(connection, newTarget, dockingPoint, hints) {
	  this._modeling.reconnectEnd(connection, newTarget, dockingPoint, hints);
	};

	/**
	 * A handler that toggles the collapsed state of an element
	 * and the visibility of all its children.
	 *
	 * @param {Modeling} modeling
	 */
	function ToggleShapeCollapseHandler(modeling) {
	  this._modeling = modeling;
	}

	ToggleShapeCollapseHandler.$inject = [ 'modeling' ];


	ToggleShapeCollapseHandler.prototype.execute = function(context) {

	  var shape = context.shape,
	      children = shape.children;

	  // remember previous visibility of children
	  context.oldChildrenVisibility = getElementsVisibility(children);

	  // toggle state
	  shape.collapsed = !shape.collapsed;

	  // hide/show children
	  setHidden(children, shape.collapsed);

	  return [shape].concat(children);
	};


	ToggleShapeCollapseHandler.prototype.revert = function(context) {

	  var shape = context.shape,
	      oldChildrenVisibility = context.oldChildrenVisibility;

	  var children = shape.children;

	  // set old visability of children
	  restoreVisibility(children, oldChildrenVisibility);

	  // retoggle state
	  shape.collapsed = !shape.collapsed;

	  return [shape].concat(children);
	};


	// helpers //////////////////////

	/**
	 * Return a map { elementId -> hiddenState}.
	 *
	 * @param {Array<djs.model.Shape>} elements
	 *
	 * @return {Object}
	 */
	function getElementsVisibility(elements) {

	  var result = {};

	  elements.forEach(function(e) {
	    result[e.id] = e.hidden;
	  });

	  return result;
	}


	function setHidden(elements, newHidden) {
	  elements.forEach(function(element) {
	    element.hidden = newHidden;
	  });
	}

	function restoreVisibility(elements, lastState) {
	  elements.forEach(function(e) {
	    e.hidden = lastState[e.id];
	  });
	}

	/**
	 * A handler that implements reversible creating and removing of space.
	 *
	 * It executes in two phases:
	 *
	 *  (1) resize all affected resizeShapes
	 *  (2) move all affected moveElements
	 */
	function SpaceToolHandler(modeling) {
	  this._modeling = modeling;
	}

	SpaceToolHandler.$inject = [ 'modeling' ];


	SpaceToolHandler.prototype.preExecute = function(context) {

	  // resize
	  var modeling = this._modeling,
	      resizingShapes = context.resizingShapes,
	      delta = context.delta,
	      direction = context.direction;

	  forEach(resizingShapes, function(shape) {
	    var newBounds = resizeBounds$1(shape, direction, delta);

	    modeling.resizeShape(shape, newBounds);
	  });
	};

	SpaceToolHandler.prototype.postExecute = function(context) {
	  // move
	  var modeling = this._modeling,
	      movingShapes = context.movingShapes,
	      delta = context.delta;

	  modeling.moveElements(movingShapes, delta, undefined, { autoResize: false, attach: false });
	};

	SpaceToolHandler.prototype.execute = function(context) {};
	SpaceToolHandler.prototype.revert = function(context) {};

	/**
	 * A handler that attaches a label to a given target shape.
	 *
	 * @param {Canvas} canvas
	 */
	function CreateLabelHandler(canvas) {
	  CreateShapeHandler.call(this, canvas);
	}

	inherits_browser(CreateLabelHandler, CreateShapeHandler);

	CreateLabelHandler.$inject = [ 'canvas' ];


	// api //////////////////////


	var originalExecute = CreateShapeHandler.prototype.execute;

	/**
	 * Appends a label to a target shape.
	 *
	 * @method CreateLabelHandler#execute
	 *
	 * @param {Object} context
	 * @param {ElementDescriptor} context.target the element the label is attached to
	 * @param {ElementDescriptor} context.parent the parent object
	 * @param {Point} context.position position of the new element
	 */
	CreateLabelHandler.prototype.execute = function(context) {

	  var label = context.shape;

	  ensureValidDimensions(label);

	  label.labelTarget = context.labelTarget;

	  return originalExecute.call(this, context);
	};

	var originalRevert = CreateShapeHandler.prototype.revert;

	/**
	 * Undo append by removing the shape
	 */
	CreateLabelHandler.prototype.revert = function(context) {
	  context.shape.labelTarget = null;

	  return originalRevert.call(this, context);
	};


	// helpers //////////////////////

	function ensureValidDimensions(label) {
	  // make sure a label has valid { width, height } dimensions
	  [ 'width', 'height' ].forEach(function(prop) {
	    if (typeof label[prop] === 'undefined') {
	      label[prop] = 0;
	    }
	  });
	}

	function CreateConnectionHandler(canvas, layouter) {
	  this._canvas = canvas;
	  this._layouter = layouter;
	}

	CreateConnectionHandler.$inject = [ 'canvas', 'layouter' ];


	// api //////////////////////


	/**
	 * Appends a shape to a target shape
	 *
	 * @param {Object} context
	 * @param {djs.element.Base} context.source the source object
	 * @param {djs.element.Base} context.target the parent object
	 * @param {Point} context.position position of the new element
	 */
	CreateConnectionHandler.prototype.execute = function(context) {

	  var connection = context.connection,
	      source = context.source,
	      target = context.target,
	      parent = context.parent,
	      parentIndex = context.parentIndex,
	      hints = context.hints;

	  if (!source || !target) {
	    throw new Error('source and target required');
	  }

	  if (!parent) {
	    throw new Error('parent required');
	  }

	  connection.source = source;
	  connection.target = target;

	  if (!connection.waypoints) {
	    connection.waypoints = this._layouter.layoutConnection(connection, hints);
	  }

	  // add connection
	  this._canvas.addConnection(connection, parent, parentIndex);

	  return connection;
	};

	CreateConnectionHandler.prototype.revert = function(context) {
	  var connection = context.connection;

	  this._canvas.removeConnection(connection);

	  connection.source = null;
	  connection.target = null;
	};

	/**
	 * A handler that implements reversible deletion of Connections.
	 */
	function DeleteConnectionHandler(canvas, modeling) {
	  this._canvas = canvas;
	  this._modeling = modeling;
	}

	DeleteConnectionHandler.$inject = [
	  'canvas',
	  'modeling'
	];


	DeleteConnectionHandler.prototype.execute = function(context) {

	  var connection = context.connection,
	      parent = connection.parent;

	  context.parent = parent;

	  // remember containment
	  context.parentIndex = indexOf$1(parent.children, connection);

	  context.source = connection.source;
	  context.target = connection.target;

	  this._canvas.removeConnection(connection);

	  connection.source = null;
	  connection.target = null;

	  return connection;
	};

	/**
	 * Command revert implementation.
	 */
	DeleteConnectionHandler.prototype.revert = function(context) {

	  var connection = context.connection,
	      parent = context.parent,
	      parentIndex = context.parentIndex;

	  connection.source = context.source;
	  connection.target = context.target;

	  // restore containment
	  add$1(parent.children, connection, parentIndex);

	  this._canvas.addConnection(connection, parent);

	  return connection;
	};

	/**
	 * A handler that implements reversible moving of connections.
	 *
	 * The handler differs from the layout connection handler in a sense
	 * that it preserves the connection layout.
	 */
	function MoveConnectionHandler() { }


	MoveConnectionHandler.prototype.execute = function(context) {

	  var connection = context.connection,
	      delta = context.delta;

	  var newParent = context.newParent || connection.parent,
	      newParentIndex = context.newParentIndex,
	      oldParent = connection.parent;

	  // save old parent in context
	  context.oldParent = oldParent;
	  context.oldParentIndex = remove$2(oldParent.children, connection);

	  // add to new parent at position
	  add$1(newParent.children, connection, newParentIndex);

	  // update parent
	  connection.parent = newParent;

	  // update waypoint positions
	  forEach(connection.waypoints, function(p) {
	    p.x += delta.x;
	    p.y += delta.y;

	    if (p.original) {
	      p.original.x += delta.x;
	      p.original.y += delta.y;
	    }
	  });

	  return connection;
	};

	MoveConnectionHandler.prototype.revert = function(context) {

	  var connection = context.connection,
	      newParent = connection.parent,
	      oldParent = context.oldParent,
	      oldParentIndex = context.oldParentIndex,
	      delta = context.delta;

	  // remove from newParent
	  remove$2(newParent.children, connection);

	  // restore previous location in old parent
	  add$1(oldParent.children, connection, oldParentIndex);

	  // restore parent
	  connection.parent = oldParent;

	  // revert to old waypoint positions
	  forEach(connection.waypoints, function(p) {
	    p.x -= delta.x;
	    p.y -= delta.y;

	    if (p.original) {
	      p.original.x -= delta.x;
	      p.original.y -= delta.y;
	    }
	  });

	  return connection;
	};

	/**
	 * A handler that implements reversible moving of shapes.
	 */
	function LayoutConnectionHandler(layouter, canvas) {
	  this._layouter = layouter;
	  this._canvas = canvas;
	}

	LayoutConnectionHandler.$inject = [ 'layouter', 'canvas' ];

	LayoutConnectionHandler.prototype.execute = function(context) {

	  var connection = context.connection;

	  var oldWaypoints = connection.waypoints;

	  assign(context, {
	    oldWaypoints: oldWaypoints
	  });

	  connection.waypoints = this._layouter.layoutConnection(connection, context.hints);

	  return connection;
	};

	LayoutConnectionHandler.prototype.revert = function(context) {

	  var connection = context.connection;

	  connection.waypoints = context.oldWaypoints;

	  return connection;
	};

	function UpdateWaypointsHandler() { }

	UpdateWaypointsHandler.prototype.execute = function(context) {

	  var connection = context.connection,
	      newWaypoints = context.newWaypoints;

	  context.oldWaypoints = connection.waypoints;

	  connection.waypoints = newWaypoints;

	  return connection;
	};

	UpdateWaypointsHandler.prototype.revert = function(context) {

	  var connection = context.connection,
	      oldWaypoints = context.oldWaypoints;

	  connection.waypoints = oldWaypoints;

	  return connection;
	};

	/**
	 * Reconnect connection handler
	 */
	function ReconnectConnectionHandler(modeling) {
	  this._modeling = modeling;
	}

	ReconnectConnectionHandler.$inject = [ 'modeling' ];

	ReconnectConnectionHandler.prototype.execute = function(context) {

	  var newSource = context.newSource,
	      newTarget = context.newTarget,
	      connection = context.connection,
	      dockingOrPoints = context.dockingOrPoints;

	  if (!newSource && !newTarget) {
	    throw new Error('newSource or newTarget are required');
	  }

	  if (newSource && newTarget) {
	    throw new Error('must specify either newSource or newTarget');
	  }

	  if (isArray(dockingOrPoints)) {
	    context.oldWaypoints = connection.waypoints;
	    connection.waypoints = dockingOrPoints;
	  }

	  if (newSource) {
	    context.oldSource = connection.source;
	    connection.source = newSource;
	  }

	  if (newTarget) {
	    context.oldTarget = connection.target;
	    connection.target = newTarget;
	  }

	  return connection;
	};

	ReconnectConnectionHandler.prototype.postExecute = function(context) {
	  var connection = context.connection,
	      dockingOrPoints = context.dockingOrPoints,
	      newSource = context.newSource,
	      movedEnd = newSource ? 'connectionStart' : 'connectionEnd',
	      newWaypoint,
	      hints = context.hints,
	      layoutHints = {};

	  if (hints.layoutConnection === false) {
	    return;
	  }

	  if (isArray(dockingOrPoints)) {
	    newWaypoint = newSource ? dockingOrPoints[0] : dockingOrPoints[dockingOrPoints.length - 1];
	  } else {
	    newWaypoint = dockingOrPoints;
	  }

	  layoutHints[movedEnd] = getDocking$2(newWaypoint);

	  this._modeling.layoutConnection(connection, layoutHints);
	};

	ReconnectConnectionHandler.prototype.revert = function(context) {

	  var oldSource = context.oldSource,
	      oldTarget = context.oldTarget,
	      oldWaypoints = context.oldWaypoints,
	      connection = context.connection;

	  if (oldSource) {
	    connection.source = oldSource;
	  }

	  if (oldTarget) {
	    connection.target = oldTarget;
	  }

	  if (oldWaypoints) {
	    connection.waypoints = oldWaypoints;
	  }

	  return connection;
	};



	// helper ///////////////

	function getDocking$2(point) {
	  return point.original || point;
	}

	/**
	 * A handler that implements reversible moving of shapes.
	 */
	function MoveElementsHandler(modeling) {
	  this._helper = new MoveHelper(modeling);
	}

	MoveElementsHandler.$inject = [ 'modeling' ];

	MoveElementsHandler.prototype.preExecute = function(context) {
	  context.closure = this._helper.getClosure(context.shapes);
	};

	MoveElementsHandler.prototype.postExecute = function(context) {

	  var hints = context.hints,
	      primaryShape;

	  if (hints && hints.primaryShape) {
	    primaryShape = hints.primaryShape;
	    hints.oldParent = primaryShape.parent;
	  }

	  this._helper.moveClosure(
	    context.closure,
	    context.delta,
	    context.newParent,
	    context.newHost,
	    primaryShape
	  );
	};

	function DeleteElementsHandler(modeling, elementRegistry) {
	  this._modeling = modeling;
	  this._elementRegistry = elementRegistry;
	}

	DeleteElementsHandler.$inject = [
	  'modeling',
	  'elementRegistry'
	];


	DeleteElementsHandler.prototype.postExecute = function(context) {

	  var modeling = this._modeling,
	      elementRegistry = this._elementRegistry,
	      elements = context.elements;

	  forEach(elements, function(element) {

	    // element may have been removed with previous
	    // remove operations already (e.g. in case of nesting)
	    if (!elementRegistry.get(element.id)) {
	      return;
	    }

	    if (element.waypoints) {
	      modeling.removeConnection(element);
	    } else {
	      modeling.removeShape(element);
	    }
	  });
	};

	/**
	 * A handler that distributes elements evenly.
	 */
	function DistributeElements$1(modeling) {
	  this._modeling = modeling;
	}

	DistributeElements$1.$inject = [ 'modeling' ];

	var OFF_AXIS = {
	  x: 'y',
	  y: 'x'
	};

	DistributeElements$1.prototype.preExecute = function(context) {
	  var modeling = this._modeling;

	  var groups = context.groups,
	      axis = context.axis,
	      dimension = context.dimension;

	  function updateRange(group, element) {
	    group.range.min = Math.min(element[axis], group.range.min);
	    group.range.max = Math.max(element[axis] + element[dimension], group.range.max);
	  }

	  function center(element) {
	    return element[axis] + element[dimension] / 2;
	  }

	  function lastIdx(arr) {
	    return arr.length - 1;
	  }

	  function rangeDiff(range) {
	    return range.max - range.min;
	  }

	  function centerElement(refCenter, element) {
	    var delta = { y: 0 };

	    delta[axis] = refCenter - center(element);

	    if (delta[axis]) {

	      delta[OFF_AXIS[axis]] = 0;

	      modeling.moveElements([ element ], delta, element.parent);
	    }
	  }

	  var firstGroup = groups[0],
	      lastGroupIdx = lastIdx(groups),
	      lastGroup = groups[ lastGroupIdx ];

	  var margin,
	      spaceInBetween,
	      groupsSize = 0; // the size of each range

	  forEach(groups, function(group, idx) {
	    var sortedElements,
	        refElem,
	        refCenter;

	    if (group.elements.length < 2) {
	      if (idx && idx !== groups.length - 1) {
	        updateRange(group, group.elements[0]);

	        groupsSize += rangeDiff(group.range);
	      }
	      return;
	    }

	    sortedElements = sortBy(group.elements, axis);

	    refElem = sortedElements[0];

	    if (idx === lastGroupIdx) {
	      refElem = sortedElements[lastIdx(sortedElements)];
	    }

	    refCenter = center(refElem);

	    // wanna update the ranges after the shapes have been centered
	    group.range = null;

	    forEach(sortedElements, function(element) {

	      centerElement(refCenter, element);

	      if (group.range === null) {
	        group.range = {
	          min: element[axis],
	          max: element[axis] + element[dimension]
	        };

	        return;
	      }

	      // update group's range after centering the range elements
	      updateRange(group, element);
	    });

	    if (idx && idx !== groups.length - 1) {
	      groupsSize += rangeDiff(group.range);
	    }
	  });

	  spaceInBetween = Math.abs(lastGroup.range.min - firstGroup.range.max);

	  margin = Math.round((spaceInBetween - groupsSize) / (groups.length - 1));

	  if (margin < groups.length - 1) {
	    return;
	  }

	  forEach(groups, function(group, groupIdx) {
	    var delta = {},
	        prevGroup;

	    if (group === firstGroup || group === lastGroup) {
	      return;
	    }

	    prevGroup = groups[groupIdx - 1];

	    group.range.max = 0;

	    forEach(group.elements, function(element, idx) {
	      delta[OFF_AXIS[axis]] = 0;
	      delta[axis] = (prevGroup.range.max - element[axis]) + margin;

	      if (group.range.min !== element[axis]) {
	        delta[axis] += element[axis] - group.range.min;
	      }

	      if (delta[axis]) {
	        modeling.moveElements([ element ], delta, element.parent);
	      }

	      group.range.max = Math.max(element[axis] + element[dimension], idx ? group.range.max : 0);
	    });
	  });
	};

	DistributeElements$1.prototype.postExecute = function(context) {

	};

	/**
	 * A handler that align elements in a certain way.
	 *
	 */
	function AlignElements$1(modeling, canvas) {
	  this._modeling = modeling;
	  this._canvas = canvas;
	}

	AlignElements$1.$inject = [ 'modeling', 'canvas' ];


	AlignElements$1.prototype.preExecute = function(context) {
	  var modeling = this._modeling;

	  var elements = context.elements,
	      alignment = context.alignment;


	  forEach(elements, function(element) {
	    var delta = {
	      x: 0,
	      y: 0
	    };

	    if (alignment.left) {
	      delta.x = alignment.left - element.x;

	    } else if (alignment.right) {
	      delta.x = (alignment.right - element.width) - element.x;

	    } else if (alignment.center) {
	      delta.x = (alignment.center - Math.round(element.width / 2)) - element.x;

	    } else if (alignment.top) {
	      delta.y = alignment.top - element.y;

	    } else if (alignment.bottom) {
	      delta.y = (alignment.bottom - element.height) - element.y;

	    } else if (alignment.middle) {
	      delta.y = (alignment.middle - Math.round(element.height / 2)) - element.y;
	    }

	    modeling.moveElements([ element ], delta, element.parent);
	  });
	};

	AlignElements$1.prototype.postExecute = function(context) {

	};

	/**
	 * A handler that implements reversible attaching/detaching of shapes.
	 */
	function UpdateAttachmentHandler(modeling) {
	  this._modeling = modeling;
	}

	UpdateAttachmentHandler.$inject = [ 'modeling' ];


	UpdateAttachmentHandler.prototype.execute = function(context) {
	  var shape = context.shape,
	      newHost = context.newHost,
	      oldHost = shape.host;

	  // (0) detach from old host
	  context.oldHost = oldHost;
	  context.attacherIdx = removeAttacher(oldHost, shape);

	  // (1) attach to new host
	  addAttacher(newHost, shape);

	  // (2) update host
	  shape.host = newHost;

	  return shape;
	};

	UpdateAttachmentHandler.prototype.revert = function(context) {
	  var shape = context.shape,
	      newHost = context.newHost,
	      oldHost = context.oldHost,
	      attacherIdx = context.attacherIdx;

	  // (2) update host
	  shape.host = oldHost;

	  // (1) attach to new host
	  removeAttacher(newHost, shape);

	  // (0) detach from old host
	  addAttacher(oldHost, shape, attacherIdx);

	  return shape;
	};


	function removeAttacher(host, attacher) {
	  // remove attacher from host
	  return remove$2(host && host.attachers, attacher);
	}

	function addAttacher(host, attacher, idx) {

	  if (!host) {
	    return;
	  }

	  var attachers = host.attachers;

	  if (!attachers) {
	    host.attachers = attachers = [];
	  }

	  add$1(attachers, attacher, idx);
	}

	function removeProperties$1(element, properties) {
	  forEach(properties, function(prop) {
	    if (element[prop]) {
	      delete element[prop];
	    }
	  });
	}

	/**
	 * A handler that implements pasting of elements onto the diagram.
	 *
	 * @param {eventBus} EventBus
	 * @param {canvas} Canvas
	 * @param {selection} Selection
	 * @param {elementFactory} ElementFactory
	 * @param {modeling} Modeling
	 * @param {rules} Rules
	 */
	function PasteHandler(
	    eventBus, canvas, selection,
	    elementFactory, modeling, rules) {

	  this._eventBus = eventBus;
	  this._canvas = canvas;
	  this._selection = selection;
	  this._elementFactory = elementFactory;
	  this._modeling = modeling;
	  this._rules = rules;
	}


	PasteHandler.$inject = [
	  'eventBus',
	  'canvas',
	  'selection',
	  'elementFactory',
	  'modeling',
	  'rules'
	];


	// api //////////////////////

	/**
	 * Creates a new shape
	 *
	 * @param {Object} context
	 * @param {Object} context.tree the new shape
	 * @param {Element} context.topParent the paste target
	 */
	PasteHandler.prototype.preExecute = function(context) {
	  var eventBus = this._eventBus,
	      self = this;

	  var tree = context.tree,
	      topParent = context.topParent,
	      position = context.position;

	  tree.createdElements = {};

	  tree.labels = [];

	  forEach(tree, function(elements, depthStr) {
	    var depth = parseInt(depthStr, 10);

	    if (isNaN(depth)) {
	      return;
	    }

	    // set the parent on the top level elements
	    if (!depth) {
	      elements = map(elements, function(descriptor) {
	        descriptor.parent = topParent;

	        return descriptor;
	      });
	    }

	    // Order by priority for element creation
	    elements = sortBy(elements, 'priority');

	    forEach(elements, function(descriptor) {
	      var id = descriptor.id,
	          parent = descriptor.parent,
	          hints = {},
	          newPosition;

	      var element = assign({}, descriptor);

	      if (depth) {
	        element.parent = self._getCreatedElement(parent, tree);
	      }

	      // this happens when shapes have not been created due to rules
	      if (!parent) {
	        return;
	      }

	      eventBus.fire('element.paste', {
	        createdElements: tree.createdElements,
	        descriptor: element
	      });

	      // in case the parent changed during 'element.paste'
	      parent = element.parent;

	      if (element.waypoints) {
	        element = self._createConnection(element, parent, position, tree);

	        if (element) {
	          tree.createdElements[id] = {
	            element: element,
	            descriptor: descriptor
	          };
	        }

	        return;
	      }


	      // supply not-root information as hint
	      if (element.parent !== topParent) {
	        hints.root = false;
	      }

	      // set host
	      if (element.host) {
	        hints.attach = true;

	        parent = self._getCreatedElement(element.host, tree);
	      }

	      // handle labels
	      if (element.labelTarget) {
	        return tree.labels.push(element);
	      }

	      newPosition = {
	        x: Math.round(position.x + element.delta.x + (element.width / 2)),
	        y: Math.round(position.y + element.delta.y + (element.height / 2))
	      };

	      removeProperties$1(element, [
	        'id',
	        'parent',
	        'delta',
	        'host',
	        'priority'
	      ]);

	      element = self._createShape(element, parent, newPosition, hints);

	      if (element) {
	        tree.createdElements[id] = {
	          element: element,
	          descriptor: descriptor
	        };
	      }
	    });
	  });
	};

	// move label's to their relative position
	PasteHandler.prototype.postExecute = function(context) {
	  var modeling = this._modeling,
	      selection = this._selection,
	      self = this;

	  var tree = context.tree,
	      labels = tree.labels,
	      topLevelElements = [];

	  forEach(labels, function(labelDescriptor) {
	    var labelTarget = self._getCreatedElement(labelDescriptor.labelTarget, tree),
	        labels, labelTargetPos, newPosition;

	    if (!labelTarget) {
	      return;
	    }

	    labels = labelTarget.labels;

	    if (!labels || !labels.length) {
	      return;
	    }

	    labelTargetPos = {
	      x: labelTarget.x,
	      y: labelTarget.y
	    };

	    if (labelTarget.waypoints) {
	      labelTargetPos = labelTarget.waypoints[0];
	    }

	    forEach(labels, function(label) {
	      newPosition = {
	        x: Math.round((labelTargetPos.x - label.x) + labelDescriptor.delta.x),
	        y: Math.round((labelTargetPos.y - label.y) + labelDescriptor.delta.y)
	      };

	      modeling.moveShape(label, newPosition, labelTarget.parent);
	    });
	  });

	  forEach(tree[0], function(descriptor) {
	    var id = descriptor.id,
	        toplevel = tree.createdElements[id];

	    if (toplevel) {
	      topLevelElements.push(toplevel.element);
	    }
	  });

	  selection.select(topLevelElements);
	};


	PasteHandler.prototype._createConnection = function(element, parent, parentCenter, tree) {
	  var modeling = this._modeling,
	      rules = this._rules;

	  var connection, source, target, canPaste;

	  element.waypoints = map(element.waypoints, function(waypoint, idx) {
	    return {
	      x: Math.round(parentCenter.x + element.delta[idx].x),
	      y: Math.round(parentCenter.y + element.delta[idx].y)
	    };
	  });

	  source = this._getCreatedElement(element.source, tree);
	  target = this._getCreatedElement(element.target, tree);

	  if (!source || !target) {
	    return null;
	  }

	  canPaste = rules.allowed('element.paste', {
	    source: source,
	    target: target
	  });

	  if (!canPaste) {
	    return null;
	  }

	  removeProperties$1(element, [
	    'id',
	    'parent',
	    'delta',
	    'source',
	    'target',
	    'width',
	    'height',
	    'priority'
	  ]);

	  connection = modeling.createConnection(source, target, element, parent);

	  return connection;
	};


	PasteHandler.prototype._createShape = function(element, parent, position, isAttach, hints) {
	  var modeling = this._modeling,
	      elementFactory = this._elementFactory,
	      rules = this._rules;

	  var canPaste = rules.allowed('element.paste', {
	    element: element,
	    position: position,
	    parent: parent
	  });

	  if (!canPaste) {
	    return null;
	  }

	  var shape = elementFactory.createShape(element);

	  modeling.createShape(shape, position, parent, isAttach, hints);

	  return shape;
	};


	PasteHandler.prototype._getCreatedElement = function(id, tree) {
	  return tree.createdElements[id] && tree.createdElements[id].element;
	};

	/**
	 * The basic modeling entry point.
	 *
	 * @param {EventBus} eventBus
	 * @param {ElementFactory} elementFactory
	 * @param {CommandStack} commandStack
	 */
	function Modeling(eventBus, elementFactory, commandStack) {
	  this._eventBus = eventBus;
	  this._elementFactory = elementFactory;
	  this._commandStack = commandStack;

	  var self = this;

	  eventBus.on('diagram.init', function() {
	    // register modeling handlers
	    self.registerHandlers(commandStack);
	  });
	}

	Modeling.$inject = [ 'eventBus', 'elementFactory', 'commandStack' ];


	Modeling.prototype.getHandlers = function() {
	  return {
	    'shape.append': AppendShapeHandler,
	    'shape.create': CreateShapeHandler,
	    'shape.delete': DeleteShapeHandler,
	    'shape.move': MoveShapeHandler,
	    'shape.resize': ResizeShapeHandler,
	    'shape.replace': ReplaceShapeHandler,
	    'shape.toggleCollapse': ToggleShapeCollapseHandler,

	    'spaceTool': SpaceToolHandler,

	    'label.create': CreateLabelHandler,

	    'connection.create': CreateConnectionHandler,
	    'connection.delete': DeleteConnectionHandler,
	    'connection.move': MoveConnectionHandler,
	    'connection.layout': LayoutConnectionHandler,

	    'connection.updateWaypoints': UpdateWaypointsHandler,

	    'connection.reconnectStart': ReconnectConnectionHandler,
	    'connection.reconnectEnd': ReconnectConnectionHandler,

	    'elements.move': MoveElementsHandler,
	    'elements.delete': DeleteElementsHandler,

	    'elements.distribute': DistributeElements$1,
	    'elements.align': AlignElements$1,

	    'element.updateAttachment': UpdateAttachmentHandler,

	    'elements.paste': PasteHandler
	  };
	};

	/**
	 * Register handlers with the command stack
	 *
	 * @param {CommandStack} commandStack
	 */
	Modeling.prototype.registerHandlers = function(commandStack) {
	  forEach(this.getHandlers(), function(handler, id) {
	    commandStack.registerHandler(id, handler);
	  });
	};


	// modeling helpers //////////////////////

	Modeling.prototype.moveShape = function(shape, delta, newParent, newParentIndex, hints) {

	  if (typeof newParentIndex === 'object') {
	    hints = newParentIndex;
	    newParentIndex = null;
	  }

	  var context = {
	    shape: shape,
	    delta:  delta,
	    newParent: newParent,
	    newParentIndex: newParentIndex,
	    hints: hints || {}
	  };

	  this._commandStack.execute('shape.move', context);
	};


	/**
	 * Update the attachment of the given shape.
	 *
	 * @param {djs.mode.Base} shape
	 * @param {djs.model.Base} [newHost]
	 */
	Modeling.prototype.updateAttachment = function(shape, newHost) {
	  var context = {
	    shape: shape,
	    newHost: newHost
	  };

	  this._commandStack.execute('element.updateAttachment', context);
	};


	/**
	 * Move a number of shapes to a new target, either setting it as
	 * the new parent or attaching it.
	 *
	 * @param {Array<djs.mode.Base>} shapes
	 * @param {Point} delta
	 * @param {djs.model.Base} [target]
	 * @param {Object} [hints]
	 * @param {Boolean} [hints.attach=false]
	 */
	Modeling.prototype.moveElements = function(shapes, delta, target, hints) {

	  hints = hints || {};

	  var attach = hints.attach;

	  var newParent = target,
	      newHost;

	  if (attach === true) {
	    newHost = target;
	    newParent = target.parent;
	  } else

	  if (attach === false) {
	    newHost = null;
	  }

	  var context = {
	    shapes: shapes,
	    delta: delta,
	    newParent: newParent,
	    newHost: newHost,
	    hints: hints
	  };

	  this._commandStack.execute('elements.move', context);
	};


	Modeling.prototype.moveConnection = function(connection, delta, newParent, newParentIndex, hints) {

	  if (typeof newParentIndex === 'object') {
	    hints = newParentIndex;
	    newParentIndex = undefined;
	  }

	  var context = {
	    connection: connection,
	    delta: delta,
	    newParent: newParent,
	    newParentIndex: newParentIndex,
	    hints: hints || {}
	  };

	  this._commandStack.execute('connection.move', context);
	};


	Modeling.prototype.layoutConnection = function(connection, hints) {
	  var context = {
	    connection: connection,
	    hints: hints || {}
	  };

	  this._commandStack.execute('connection.layout', context);
	};


	/**
	 * Create connection.
	 *
	 * @param {djs.model.Base} source
	 * @param {djs.model.Base} target
	 * @param {Number} [targetIndex]
	 * @param {Object|djs.model.Connection} connection
	 * @param {djs.model.Base} parent
	 * @param {Object} hints
	 *
	 * @return {djs.model.Connection} the created connection.
	 */
	Modeling.prototype.createConnection = function(source, target, parentIndex, connection, parent, hints) {

	  if (typeof parentIndex === 'object') {
	    hints = parent;
	    parent = connection;
	    connection = parentIndex;
	    parentIndex = undefined;
	  }

	  connection = this._create('connection', connection);

	  var context = {
	    source: source,
	    target: target,
	    parent: parent,
	    parentIndex: parentIndex,
	    connection: connection,
	    hints: hints
	  };

	  this._commandStack.execute('connection.create', context);

	  return context.connection;
	};


	/**
	 * Create a shape at the specified position.
	 *
	 * @param {djs.model.Shape|Object} shape
	 * @param {Point} position
	 * @param {djs.model.Shape|djs.model.Root} target
	 * @param {Number} [parentIndex] position in parents children list
	 * @param {Object} [hints]
	 * @param {Boolean} [hints.attach] whether to attach to target or become a child
	 *
	 * @return {djs.model.Shape} the created shape
	 */
	Modeling.prototype.createShape = function(shape, position, target, parentIndex, hints) {

	  if (typeof parentIndex !== 'number') {
	    hints = parentIndex;
	    parentIndex = undefined;
	  }

	  hints = hints || {};

	  var attach = hints.attach,
	      parent,
	      host;

	  shape = this._create('shape', shape);

	  if (attach) {
	    parent = target.parent;
	    host = target;
	  } else {
	    parent = target;
	  }

	  var context = {
	    position: position,
	    shape: shape,
	    parent: parent,
	    parentIndex: parentIndex,
	    host: host,
	    hints: hints
	  };

	  this._commandStack.execute('shape.create', context);

	  return context.shape;
	};


	Modeling.prototype.createLabel = function(labelTarget, position, label, parent) {

	  label = this._create('label', label);

	  var context = {
	    labelTarget: labelTarget,
	    position: position,
	    parent: parent || labelTarget.parent,
	    shape: label
	  };

	  this._commandStack.execute('label.create', context);

	  return context.shape;
	};


	/**
	 * Append shape to given source, drawing a connection
	 * between source and the newly created shape.
	 *
	 * @param {djs.model.Shape} source
	 * @param {djs.model.Shape|Object} shape
	 * @param {Point} position
	 * @param {djs.model.Shape} target
	 * @param {Object} [hints]
	 * @param {Boolean} [hints.attach]
	 * @param {djs.model.Connection|Object} [hints.connection]
	 * @param {djs.model.Base} [hints.connectionParent]
	 *
	 * @return {djs.model.Shape} the newly created shape
	 */
	Modeling.prototype.appendShape = function(source, shape, position, target, hints) {

	  hints = hints || {};

	  shape = this._create('shape', shape);

	  var context = {
	    source: source,
	    position: position,
	    target: target,
	    shape: shape,
	    connection: hints.connection,
	    connectionParent: hints.connectionParent,
	    attach: hints.attach
	  };

	  this._commandStack.execute('shape.append', context);

	  return context.shape;
	};


	Modeling.prototype.removeElements = function(elements) {
	  var context = {
	    elements: elements
	  };

	  this._commandStack.execute('elements.delete', context);
	};


	Modeling.prototype.distributeElements = function(groups, axis, dimension) {
	  var context = {
	    groups: groups,
	    axis: axis,
	    dimension: dimension
	  };

	  this._commandStack.execute('elements.distribute', context);
	};


	Modeling.prototype.removeShape = function(shape, hints) {
	  var context = {
	    shape: shape,
	    hints: hints || {}
	  };

	  this._commandStack.execute('shape.delete', context);
	};


	Modeling.prototype.removeConnection = function(connection, hints) {
	  var context = {
	    connection: connection,
	    hints: hints || {}
	  };

	  this._commandStack.execute('connection.delete', context);
	};

	Modeling.prototype.replaceShape = function(oldShape, newShape, hints) {
	  var context = {
	    oldShape: oldShape,
	    newData: newShape,
	    hints: hints || {}
	  };

	  this._commandStack.execute('shape.replace', context);

	  return context.newShape;
	};

	Modeling.prototype.pasteElements = function(tree, topParent, position) {
	  var context = {
	    tree: tree,
	    topParent: topParent,
	    position: position
	  };

	  this._commandStack.execute('elements.paste', context);
	};

	Modeling.prototype.alignElements = function(elements, alignment) {
	  var context = {
	    elements: elements,
	    alignment: alignment
	  };

	  this._commandStack.execute('elements.align', context);
	};

	Modeling.prototype.resizeShape = function(shape, newBounds, minBounds, hints) {
	  var context = {
	    shape: shape,
	    newBounds: newBounds,
	    minBounds: minBounds,
	    hints: hints
	  };

	  this._commandStack.execute('shape.resize', context);
	};

	Modeling.prototype.createSpace = function(movingShapes, resizingShapes, delta, direction) {
	  var context = {
	    movingShapes: movingShapes,
	    resizingShapes: resizingShapes,
	    delta: delta,
	    direction: direction
	  };

	  this._commandStack.execute('spaceTool', context);
	};

	Modeling.prototype.updateWaypoints = function(connection, newWaypoints, hints) {
	  var context = {
	    connection: connection,
	    newWaypoints: newWaypoints,
	    hints: hints || {}
	  };

	  this._commandStack.execute('connection.updateWaypoints', context);
	};

	Modeling.prototype.reconnectStart = function(connection, newSource, dockingOrPoints, hints) {
	  var context = {
	    connection: connection,
	    newSource: newSource,
	    dockingOrPoints: dockingOrPoints,
	    hints: hints || {}
	  };

	  this._commandStack.execute('connection.reconnectStart', context);
	};

	Modeling.prototype.reconnectEnd = function(connection, newTarget, dockingOrPoints, hints) {
	  var context = {
	    connection: connection,
	    newTarget: newTarget,
	    dockingOrPoints: dockingOrPoints,
	    hints: hints || {}
	  };

	  this._commandStack.execute('connection.reconnectEnd', context);
	};

	Modeling.prototype.connect = function(source, target, attrs, hints) {
	  return this.createConnection(source, target, attrs || {}, source.parent, hints);
	};

	Modeling.prototype._create = function(type, attrs) {
	  if (attrs instanceof Base) {
	    return attrs;
	  } else {
	    return this._elementFactory.create(type, attrs);
	  }
	};

	Modeling.prototype.toggleCollapse = function(shape, hints) {
	  var context = {
	    shape: shape,
	    hints: hints || {}
	  };

	  this._commandStack.execute('shape.toggleCollapse', context);
	};

	var DEFAULT_FLOW = 'default',
	    ID = 'id',
	    DI = 'di';

	var NULL_DIMENSIONS = {
	  width: 0,
	  height: 0
	};

	/**
	 * A handler that implements a BPMN 2.0 property update.
	 *
	 * This should be used to set simple properties on elements with
	 * an underlying BPMN business object.
	 *
	 * Use respective diagram-js provided handlers if you would
	 * like to perform automated modeling.
	 */
	function UpdatePropertiesHandler(
	    elementRegistry, moddle, translate,
	    modeling, textRenderer) {

	  this._elementRegistry = elementRegistry;
	  this._moddle = moddle;
	  this._translate = translate;
	  this._modeling = modeling;
	  this._textRenderer = textRenderer;
	}

	UpdatePropertiesHandler.$inject = [
	  'elementRegistry',
	  'moddle',
	  'translate',
	  'modeling',
	  'textRenderer'
	];


	// api //////////////////////

	/**
	 * Updates a BPMN element with a list of new properties
	 *
	 * @param {Object} context
	 * @param {djs.model.Base} context.element the element to update
	 * @param {Object} context.properties a list of properties to set on the element's
	 *                                    businessObject (the BPMN model element)
	 *
	 * @return {Array<djs.model.Base>} the updated element
	 */
	UpdatePropertiesHandler.prototype.execute = function(context) {

	  var element = context.element,
	      changed = [ element ],
	      translate = this._translate;

	  if (!element) {
	    throw new Error(translate('element required'));
	  }

	  var elementRegistry = this._elementRegistry,
	      ids = this._moddle.ids;

	  var businessObject = element.businessObject,
	      properties = unwrapBusinessObjects(context.properties),
	      oldProperties = context.oldProperties || getProperties$1(businessObject, properties);

	  if (isIdChange(properties, businessObject)) {
	    ids.unclaim(businessObject[ID]);

	    elementRegistry.updateId(element, properties[ID]);

	    ids.claim(properties[ID], businessObject);
	  }

	  // correctly indicate visual changes on default flow updates
	  if (DEFAULT_FLOW in properties) {

	    if (properties[DEFAULT_FLOW]) {
	      changed.push(elementRegistry.get(properties[DEFAULT_FLOW].id));
	    }

	    if (businessObject[DEFAULT_FLOW]) {
	      changed.push(elementRegistry.get(businessObject[DEFAULT_FLOW].id));
	    }
	  }

	  // update properties
	  setProperties$1(businessObject, properties);

	  // store old values
	  context.oldProperties = oldProperties;
	  context.changed = changed;

	  // indicate changed on objects affected by the update
	  return changed;
	};


	UpdatePropertiesHandler.prototype.postExecute = function(context) {
	  var element = context.element,
	      label = element.label;

	  var text = label && getBusinessObject(label).name;

	  if (!text) {
	    return;
	  }

	  // get layouted text bounds and resize external
	  // external label accordingly
	  var newLabelBounds = this._textRenderer.getExternalLabelBounds(label, text);

	  this._modeling.resizeShape(label, newLabelBounds, NULL_DIMENSIONS);
	};

	/**
	 * Reverts the update on a BPMN elements properties.
	 *
	 * @param  {Object} context
	 *
	 * @return {djs.model.Base} the updated element
	 */
	UpdatePropertiesHandler.prototype.revert = function(context) {

	  var element = context.element,
	      properties = context.properties,
	      oldProperties = context.oldProperties,
	      businessObject = element.businessObject,
	      elementRegistry = this._elementRegistry,
	      ids = this._moddle.ids;

	  // update properties
	  setProperties$1(businessObject, oldProperties);

	  if (isIdChange(properties, businessObject)) {
	    ids.unclaim(properties[ID]);

	    elementRegistry.updateId(element, oldProperties[ID]);

	    ids.claim(oldProperties[ID], businessObject);
	  }

	  return context.changed;
	};


	function isIdChange(properties, businessObject) {
	  return ID in properties && properties[ID] !== businessObject[ID];
	}


	function getProperties$1(businessObject, properties) {
	  var propertyNames = keys(properties);

	  return reduce(propertyNames, function(result, key) {

	    // handle DI seperately
	    if (key !== DI) {
	      result[key] = businessObject.get(key);
	    } else {
	      result[key] = getDiProperties(businessObject.di, keys(properties.di));
	    }

	    return result;
	  }, {});
	}


	function getDiProperties(di, propertyNames) {
	  return reduce(propertyNames, function(result, key) {
	    result[key] = di.get(key);

	    return result;
	  }, {});
	}


	function setProperties$1(businessObject, properties) {
	  forEach(properties, function(value, key) {

	    if (key !== DI) {
	      businessObject.set(key, value);
	    } else {
	      // only update, if businessObject.di exists
	      if (businessObject.di) {
	        setDiProperties(businessObject.di, value);
	      }
	    }
	  });
	}


	function setDiProperties(di, properties) {
	  forEach(properties, function(value, key) {
	    di.set(key, value);
	  });
	}


	var referencePropertyNames = [ 'default' ];

	/**
	 * Make sure we unwrap the actual business object
	 * behind diagram element that may have been
	 * passed as arguments.
	 *
	 * @param  {Object} properties
	 *
	 * @return {Object} unwrappedProps
	 */
	function unwrapBusinessObjects(properties) {

	  var unwrappedProps = assign({}, properties);

	  referencePropertyNames.forEach(function(name) {
	    if (name in properties) {
	      unwrappedProps[name] = getBusinessObject(unwrappedProps[name]);
	    }
	  });

	  return unwrappedProps;
	}

	function UpdateCanvasRootHandler(canvas, modeling) {
	  this._canvas = canvas;
	  this._modeling = modeling;
	}

	UpdateCanvasRootHandler.$inject = [
	  'canvas',
	  'modeling'
	];


	UpdateCanvasRootHandler.prototype.execute = function(context) {

	  var canvas = this._canvas;

	  var newRoot = context.newRoot,
	      newRootBusinessObject = newRoot.businessObject,
	      oldRoot = canvas.getRootElement(),
	      oldRootBusinessObject = oldRoot.businessObject,
	      bpmnDefinitions = oldRootBusinessObject.$parent,
	      diPlane = oldRootBusinessObject.di;

	  // (1) replace process old <> new root
	  canvas.setRootElement(newRoot, true);

	  // (2) update root elements
	  add$1(bpmnDefinitions.rootElements, newRootBusinessObject);
	  newRootBusinessObject.$parent = bpmnDefinitions;

	  remove$2(bpmnDefinitions.rootElements, oldRootBusinessObject);
	  oldRootBusinessObject.$parent = null;

	  // (3) wire di
	  oldRootBusinessObject.di = null;

	  diPlane.bpmnElement = newRootBusinessObject;
	  newRootBusinessObject.di = diPlane;

	  context.oldRoot = oldRoot;

	  // TODO(nikku): return changed elements?
	  // return [ newRoot, oldRoot ];
	};


	UpdateCanvasRootHandler.prototype.revert = function(context) {

	  var canvas = this._canvas;

	  var newRoot = context.newRoot,
	      newRootBusinessObject = newRoot.businessObject,
	      oldRoot = context.oldRoot,
	      oldRootBusinessObject = oldRoot.businessObject,
	      bpmnDefinitions = newRootBusinessObject.$parent,
	      diPlane = newRootBusinessObject.di;

	  // (1) replace process old <> new root
	  canvas.setRootElement(oldRoot, true);

	  // (2) update root elements
	  remove$2(bpmnDefinitions.rootElements, newRootBusinessObject);
	  newRootBusinessObject.$parent = null;

	  add$1(bpmnDefinitions.rootElements, oldRootBusinessObject);
	  oldRootBusinessObject.$parent = bpmnDefinitions;

	  // (3) wire di
	  newRootBusinessObject.di = null;

	  diPlane.bpmnElement = oldRootBusinessObject;
	  oldRootBusinessObject.di = diPlane;

	  // TODO(nikku): return changed elements?
	  // return [ newRoot, oldRoot ];
	};

	/**
	 * A handler that allows us to add a new lane
	 * above or below an existing one.
	 *
	 * @param {Modeling} modeling
	 */
	function AddLaneHandler(modeling, spaceTool) {
	  this._modeling = modeling;
	  this._spaceTool = spaceTool;
	}

	AddLaneHandler.$inject = [
	  'modeling',
	  'spaceTool'
	];


	AddLaneHandler.prototype.preExecute = function(context) {

	  var spaceTool = this._spaceTool,
	      modeling = this._modeling;

	  var shape = context.shape,
	      location = context.location;

	  var lanesRoot = getLanesRoot(shape);

	  var isRoot = lanesRoot === shape,
	      laneParent = isRoot ? shape : shape.parent;

	  var existingChildLanes = getChildLanes(laneParent);

	  // (0) add a lane if we currently got none and are adding to root
	  if (!existingChildLanes.length) {
	    modeling.createShape({ type: 'bpmn:Lane' }, {
	      x: shape.x + LANE_INDENTATION,
	      y: shape.y,
	      width: shape.width - LANE_INDENTATION,
	      height: shape.height
	    }, laneParent);
	  }

	  // (1) collect affected elements to create necessary space
	  var allAffected = [];

	  eachElement(lanesRoot, function(element) {
	    allAffected.push(element);

	    if (element === shape) {
	      return [];
	    }

	    return filter(element.children, function(c) {
	      return c !== shape;
	    });
	  });

	  var offset = location === 'top' ? -120 : 120,
	      lanePosition = location === 'top' ? shape.y : shape.y + shape.height,
	      spacePos = lanePosition + (location === 'top' ? 10 : -10),
	      direction = location === 'top' ? 'n' : 's';

	  var adjustments = spaceTool.calculateAdjustments(allAffected, 'y', offset, spacePos);

	  spaceTool.makeSpace(adjustments.movingShapes, adjustments.resizingShapes, { x: 0, y: offset }, direction);

	  // (2) create new lane at open space
	  context.newLane = modeling.createShape({ type: 'bpmn:Lane' }, {
	    x: shape.x + (isRoot ? LANE_INDENTATION : 0),
	    y: lanePosition - (location === 'top' ? 120 : 0),
	    width: shape.width - (isRoot ? LANE_INDENTATION : 0),
	    height: 120
	  }, laneParent);
	};

	/**
	 * A handler that splits a lane into a number of sub-lanes,
	 * creating new sub lanes, if neccessary.
	 *
	 * @param {Modeling} modeling
	 */
	function SplitLaneHandler(modeling, translate) {
	  this._modeling = modeling;
	  this._translate = translate;
	}

	SplitLaneHandler.$inject = [
	  'modeling',
	  'translate'
	];


	SplitLaneHandler.prototype.preExecute = function(context) {

	  var modeling = this._modeling,
	      translate = this._translate;

	  var shape = context.shape,
	      newLanesCount = context.count;

	  var childLanes = getChildLanes(shape),
	      existingLanesCount = childLanes.length;

	  if (existingLanesCount > newLanesCount) {
	    throw new Error(translate('more than {count} child lanes', { count: newLanesCount }));
	  }

	  var newLanesHeight = Math.round(shape.height / newLanesCount);

	  // Iterate from top to bottom in child lane order,
	  // resizing existing lanes and creating new ones
	  // so that they split the parent proportionally.
	  //
	  // Due to rounding related errors, the bottom lane
	  // needs to take up all the remaining space.
	  var laneY,
	      laneHeight,
	      laneBounds,
	      newLaneAttrs,
	      idx;

	  for (idx = 0; idx < newLanesCount; idx++) {

	    laneY = shape.y + idx * newLanesHeight;

	    // if bottom lane
	    if (idx === newLanesCount - 1) {
	      laneHeight = shape.height - (newLanesHeight * idx);
	    } else {
	      laneHeight = newLanesHeight;
	    }

	    laneBounds = {
	      x: shape.x + LANE_INDENTATION,
	      y: laneY,
	      width: shape.width - LANE_INDENTATION,
	      height: laneHeight
	    };

	    if (idx < existingLanesCount) {
	      // resize existing lane
	      modeling.resizeShape(childLanes[idx], laneBounds);
	    } else {
	      // create a new lane at position
	      newLaneAttrs = {
	        type: 'bpmn:Lane'
	      };

	      modeling.createShape(newLaneAttrs, laneBounds, shape);
	    }
	  }
	};

	/**
	 * A handler that resizes a lane.
	 *
	 * @param {Modeling} modeling
	 */
	function ResizeLaneHandler(modeling, spaceTool) {
	  this._modeling = modeling;
	  this._spaceTool = spaceTool;
	}

	ResizeLaneHandler.$inject = [
	  'modeling',
	  'spaceTool'
	];


	ResizeLaneHandler.prototype.preExecute = function(context) {

	  var shape = context.shape,
	      newBounds = context.newBounds,
	      balanced = context.balanced;

	  if (balanced !== false) {
	    this.resizeBalanced(shape, newBounds);
	  } else {
	    this.resizeSpace(shape, newBounds);
	  }
	};


	/**
	 * Resize balanced, adjusting next / previous lane sizes.
	 *
	 * @param {djs.model.Shape} shape
	 * @param {Bounds} newBounds
	 */
	ResizeLaneHandler.prototype.resizeBalanced = function(shape, newBounds) {

	  var modeling = this._modeling;

	  var resizeNeeded = computeLanesResize(shape, newBounds);

	  // resize the lane
	  modeling.resizeShape(shape, newBounds);

	  // resize other lanes as needed
	  resizeNeeded.forEach(function(r) {
	    modeling.resizeShape(r.shape, r.newBounds);
	  });
	};


	/**
	 * Resize, making actual space and moving below / above elements.
	 *
	 * @param {djs.model.Shape} shape
	 * @param {Bounds} newBounds
	 */
	ResizeLaneHandler.prototype.resizeSpace = function(shape, newBounds) {
	  var spaceTool = this._spaceTool;

	  var shapeTrbl = asTRBL(shape),
	      newTrbl = asTRBL(newBounds);

	  var trblDiff = substractTRBL(newTrbl, shapeTrbl);

	  var lanesRoot = getLanesRoot(shape);

	  var allAffected = [],
	      allLanes = [];

	  eachElement(lanesRoot, function(element) {
	    allAffected.push(element);

	    if (is$1(element, 'bpmn:Lane') || is$1(element, 'bpmn:Participant')) {
	      allLanes.push(element);
	    }

	    return element.children;
	  });

	  var change,
	      spacePos,
	      direction,
	      offset,
	      adjustments;

	  if (trblDiff.bottom || trblDiff.top) {

	    change = trblDiff.bottom || trblDiff.top;
	    spacePos = shape.y + (trblDiff.bottom ? shape.height : 0) + (trblDiff.bottom ? -10 : 10);
	    direction = trblDiff.bottom ? 's' : 'n';

	    offset = trblDiff.top > 0 || trblDiff.bottom < 0 ? -change : change;

	    adjustments = spaceTool.calculateAdjustments(allAffected, 'y', offset, spacePos);

	    spaceTool.makeSpace(adjustments.movingShapes, adjustments.resizingShapes, { x: 0, y: change }, direction);
	  }


	  if (trblDiff.left || trblDiff.right) {

	    change = trblDiff.right || trblDiff.left;
	    spacePos = shape.x + (trblDiff.right ? shape.width : 0) + (trblDiff.right ? -10 : 100);
	    direction = trblDiff.right ? 'e' : 'w';

	    offset = trblDiff.left > 0 || trblDiff.right < 0 ? -change : change;

	    adjustments = spaceTool.calculateAdjustments(allLanes, 'x', offset, spacePos);

	    spaceTool.makeSpace(adjustments.movingShapes, adjustments.resizingShapes, { x: change, y: 0 }, direction);
	  }
	};

	var FLOW_NODE_REFS_ATTR = 'flowNodeRef',
	    LANES_ATTR = 'lanes';


	/**
	 * A handler that updates lane refs on changed elements
	 */
	function UpdateFlowNodeRefsHandler(elementRegistry) {
	  this._elementRegistry = elementRegistry;
	}

	UpdateFlowNodeRefsHandler.$inject = [
	  'elementRegistry'
	];


	UpdateFlowNodeRefsHandler.prototype.computeUpdates = function(flowNodeShapes, laneShapes) {

	  var handledNodes = {};

	  var updates = [];

	  var participantCache = {};

	  var allFlowNodeShapes = [];

	  function isInLaneShape(element, laneShape) {

	    var laneTrbl = asTRBL(laneShape);

	    var elementMid = {
	      x: element.x + element.width / 2,
	      y: element.y + element.height / 2
	    };

	    return elementMid.x > laneTrbl.left &&
	           elementMid.x < laneTrbl.right &&
	           elementMid.y > laneTrbl.top &&
	           elementMid.y < laneTrbl.bottom;
	  }

	  function addFlowNodeShape(flowNodeShape) {
	    if (!handledNodes[flowNodeShape.id]) {
	      allFlowNodeShapes.push(flowNodeShape);
	      handledNodes[flowNodeShape.id] = flowNodeShape;
	    }
	  }

	  function getAllLaneShapes(flowNodeShape) {

	    var root = getLanesRoot(flowNodeShape);

	    if (!participantCache[root.id]) {
	      participantCache[root.id] = collectLanes(root);
	    }

	    return participantCache[root.id];
	  }

	  function getNewLanes(flowNodeShape) {
	    if (!flowNodeShape.parent) {
	      return [];
	    }

	    var allLaneShapes = getAllLaneShapes(flowNodeShape);

	    return allLaneShapes.filter(function(l) {
	      return isInLaneShape(flowNodeShape, l);
	    }).map(function(shape) {
	      return shape.businessObject;
	    });
	  }

	  laneShapes.forEach(function(laneShape) {
	    var root = getLanesRoot(laneShape);

	    if (!root || handledNodes[root.id]) {
	      return;
	    }

	    var children = root.children.filter(function(c) {
	      return is$1(c, 'bpmn:FlowNode');
	    });

	    children.forEach(addFlowNodeShape);

	    handledNodes[root.id] = root;
	  });

	  flowNodeShapes.forEach(addFlowNodeShape);


	  allFlowNodeShapes.forEach(function(flowNodeShape) {

	    var flowNode = flowNodeShape.businessObject;

	    var lanes = flowNode.get(LANES_ATTR),
	        remove = lanes.slice(),
	        add = getNewLanes(flowNodeShape);

	    updates.push({ flowNode: flowNode, remove: remove, add: add });
	  });

	  laneShapes.forEach(function(laneShape) {

	    var lane = laneShape.businessObject;

	    // lane got removed XX-)
	    if (!laneShape.parent) {
	      lane.get(FLOW_NODE_REFS_ATTR).forEach(function(flowNode) {
	        updates.push({ flowNode: flowNode, remove: [ lane ], add: [] });
	      });
	    }
	  });

	  return updates;
	};

	UpdateFlowNodeRefsHandler.prototype.execute = function(context) {

	  var updates = context.updates;

	  if (!updates) {
	    updates = context.updates = this.computeUpdates(context.flowNodeShapes, context.laneShapes);
	  }


	  updates.forEach(function(update) {

	    var flowNode = update.flowNode,
	        lanes = flowNode.get(LANES_ATTR);

	    // unwire old
	    update.remove.forEach(function(oldLane) {
	      remove$2(lanes, oldLane);
	      remove$2(oldLane.get(FLOW_NODE_REFS_ATTR), flowNode);
	    });

	    // wire new
	    update.add.forEach(function(newLane) {
	      add$1(lanes, newLane);
	      add$1(newLane.get(FLOW_NODE_REFS_ATTR), flowNode);
	    });
	  });

	  // TODO(nikku): return changed elements
	  // return [ ... ];
	};


	UpdateFlowNodeRefsHandler.prototype.revert = function(context) {

	  var updates = context.updates;

	  updates.forEach(function(update) {

	    var flowNode = update.flowNode,
	        lanes = flowNode.get(LANES_ATTR);

	    // unwire new
	    update.add.forEach(function(newLane) {
	      remove$2(lanes, newLane);
	      remove$2(newLane.get(FLOW_NODE_REFS_ATTR), flowNode);
	    });

	    // wire old
	    update.remove.forEach(function(oldLane) {
	      add$1(lanes, oldLane);
	      add$1(oldLane.get(FLOW_NODE_REFS_ATTR), flowNode);
	    });
	  });

	  // TODO(nikku): return changed elements
	  // return [ ... ];
	};

	function IdClaimHandler(moddle) {
	  this._moddle = moddle;
	}

	IdClaimHandler.$inject = [ 'moddle' ];


	IdClaimHandler.prototype.execute = function(context) {
	  var ids = this._moddle.ids,
	      id = context.id,
	      element = context.element,
	      claiming = context.claiming;

	  if (claiming) {
	    ids.claim(id, element);
	  } else {
	    ids.unclaim(id);
	  }
	};

	/**
	 * Command revert implementation.
	 */
	IdClaimHandler.prototype.revert = function(context) {
	  var ids = this._moddle.ids,
	      id = context.id,
	      element = context.element,
	      claiming = context.claiming;

	  if (claiming) {
	    ids.unclaim(id);
	  } else {
	    ids.claim(id, element);
	  }
	};

	var DEFAULT_COLORS = {
	  fill: undefined,
	  stroke: undefined
	};


	function SetColorHandler(commandStack) {
	  this._commandStack = commandStack;
	}

	SetColorHandler.$inject = [
	  'commandStack'
	];


	SetColorHandler.prototype.postExecute = function(context) {
	  var elements = context.elements,
	      colors = context.colors || DEFAULT_COLORS;

	  var self = this;

	  var di = {};

	  if ('fill' in colors) {
	    assign(di, { fill: colors.fill });
	  }

	  if ('stroke' in colors) {
	    assign(di, { stroke: colors.stroke });
	  }

	  forEach(elements, function(element) {

	    self._commandStack.execute('element.updateProperties', {
	      element: element,
	      properties: {
	        di: di
	      }
	    });
	  });

	};

	var NULL_DIMENSIONS$1 = {
	  width: 0,
	  height: 0
	};


	/**
	 * A handler that updates the text of a BPMN element.
	 */
	function UpdateLabelHandler(modeling, textRenderer) {

	  /**
	   * Set the label and return the changed elements.
	   *
	   * Element parameter can be label itself or connection (i.e. sequence flow).
	   *
	   * @param {djs.model.Base} element
	   * @param {String} text
	   */
	  function setText(element, text) {

	    // external label if present
	    var label = element.label || element;

	    var labelTarget = element.labelTarget || element;

	    setLabel(label, text);

	    return [ label, labelTarget ];
	  }

	  function preExecute(ctx) {
	    var element = ctx.element,
	        businessObject = element.businessObject,
	        newLabel = ctx.newLabel;

	    if (!isLabel(element)
	        && isLabelExternal(element)
	        && !hasExternalLabel(element)
	        && !isEmptyText$1(newLabel)) {

	      // create label
	      var paddingTop = 7;

	      var labelCenter = getExternalLabelMid(element);

	      labelCenter = {
	        x: labelCenter.x,
	        y: labelCenter.y + paddingTop
	      };

	      modeling.createLabel(element, labelCenter, {
	        id: businessObject.id + '_label',
	        businessObject: businessObject
	      });
	    }
	  }

	  function execute(ctx) {
	    ctx.oldLabel = getLabel(ctx.element);
	    return setText(ctx.element, ctx.newLabel);
	  }

	  function revert(ctx) {
	    return setText(ctx.element, ctx.oldLabel);
	  }

	  function postExecute(ctx) {
	    var element = ctx.element,
	        label = element.label || element,
	        newLabel = ctx.newLabel,
	        newBounds = ctx.newBounds,
	        hints = ctx.hints || {};

	    if (isLabel(label) && isEmptyText$1(newLabel)) {

	      if (hints.removeShape !== false) {
	        modeling.removeShape(label, { unsetLabel: false });
	      }

	      return;
	    }

	    // ignore internal labels for elements except text annotations
	    if (!isLabelExternal(element) && !is$1(element, 'bpmn:TextAnnotation')) {
	      return;
	    }

	    var text = getLabel(label);

	    // don't resize without text
	    if (!text) {
	      return;
	    }

	    // resize element based on label _or_ pre-defined bounds
	    if (typeof newBounds === 'undefined') {
	      newBounds = textRenderer.getExternalLabelBounds(label, text);
	    }

	    // setting newBounds to false or _null_ will
	    // disable the postExecute resize operation
	    if (newBounds) {
	      modeling.resizeShape(label, newBounds, NULL_DIMENSIONS$1);
	    }
	  }

	  // API

	  this.preExecute = preExecute;
	  this.execute = execute;
	  this.revert = revert;
	  this.postExecute = postExecute;
	}

	UpdateLabelHandler.$inject = [
	  'modeling',
	  'textRenderer'
	];


	// helpers ///////////////////////

	function isEmptyText$1(label) {
	  return !label || !label.trim();
	}

	/**
	 * BPMN 2.0 modeling features activator
	 *
	 * @param {EventBus} eventBus
	 * @param {ElementFactory} elementFactory
	 * @param {CommandStack} commandStack
	 * @param {BpmnRules} bpmnRules
	 */
	function Modeling$1(
	    eventBus, elementFactory, commandStack,
	    bpmnRules) {

	  Modeling.call(this, eventBus, elementFactory, commandStack);

	  this._bpmnRules = bpmnRules;
	}

	inherits_browser(Modeling$1, Modeling);

	Modeling$1.$inject = [
	  'eventBus',
	  'elementFactory',
	  'commandStack',
	  'bpmnRules'
	];


	Modeling$1.prototype.getHandlers = function() {
	  var handlers = Modeling.prototype.getHandlers.call(this);

	  handlers['element.updateProperties'] = UpdatePropertiesHandler;
	  handlers['canvas.updateRoot'] = UpdateCanvasRootHandler;
	  handlers['lane.add'] = AddLaneHandler;
	  handlers['lane.resize'] = ResizeLaneHandler;
	  handlers['lane.split'] = SplitLaneHandler;
	  handlers['lane.updateRefs'] = UpdateFlowNodeRefsHandler;
	  handlers['id.updateClaim'] = IdClaimHandler;
	  handlers['element.setColor'] = SetColorHandler;
	  handlers['element.updateLabel'] = UpdateLabelHandler;

	  return handlers;
	};


	Modeling$1.prototype.updateLabel = function(element, newLabel, newBounds, hints) {
	  this._commandStack.execute('element.updateLabel', {
	    element: element,
	    newLabel: newLabel,
	    newBounds: newBounds,
	    hints: hints || {}
	  });
	};


	Modeling$1.prototype.connect = function(source, target, attrs, hints) {

	  var bpmnRules = this._bpmnRules;

	  if (!attrs) {
	    attrs = bpmnRules.canConnect(source, target);
	  }

	  if (!attrs) {
	    return;
	  }

	  return this.createConnection(source, target, attrs, source.parent, hints);
	};


	Modeling$1.prototype.updateProperties = function(element, properties) {
	  this._commandStack.execute('element.updateProperties', {
	    element: element,
	    properties: properties
	  });
	};

	Modeling$1.prototype.resizeLane = function(laneShape, newBounds, balanced) {
	  this._commandStack.execute('lane.resize', {
	    shape: laneShape,
	    newBounds: newBounds,
	    balanced: balanced
	  });
	};

	Modeling$1.prototype.addLane = function(targetLaneShape, location) {
	  var context = {
	    shape: targetLaneShape,
	    location: location
	  };

	  this._commandStack.execute('lane.add', context);

	  return context.newLane;
	};

	Modeling$1.prototype.splitLane = function(targetLane, count) {
	  this._commandStack.execute('lane.split', {
	    shape: targetLane,
	    count: count
	  });
	};

	/**
	 * Transform the current diagram into a collaboration.
	 *
	 * @return {djs.model.Root} the new root element
	 */
	Modeling$1.prototype.makeCollaboration = function() {

	  var collaborationElement = this._create('root', {
	    type: 'bpmn:Collaboration'
	  });

	  var context = {
	    newRoot: collaborationElement
	  };

	  this._commandStack.execute('canvas.updateRoot', context);

	  return collaborationElement;
	};

	Modeling$1.prototype.updateLaneRefs = function(flowNodeShapes, laneShapes) {

	  this._commandStack.execute('lane.updateRefs', {
	    flowNodeShapes: flowNodeShapes,
	    laneShapes: laneShapes
	  });
	};

	/**
	 * Transform the current diagram into a process.
	 *
	 * @return {djs.model.Root} the new root element
	 */
	Modeling$1.prototype.makeProcess = function() {

	  var processElement = this._create('root', {
	    type: 'bpmn:Process'
	  });

	  var context = {
	    newRoot: processElement
	  };

	  this._commandStack.execute('canvas.updateRoot', context);
	};


	Modeling$1.prototype.claimId = function(id, moddleElement) {
	  this._commandStack.execute('id.updateClaim', {
	    id: id,
	    element: moddleElement,
	    claiming: true
	  });
	};


	Modeling$1.prototype.unclaimId = function(id, moddleElement) {
	  this._commandStack.execute('id.updateClaim', {
	    id: id,
	    element: moddleElement
	  });
	};

	Modeling$1.prototype.setColor = function(elements, colors) {
	  if (!elements.length) {
	    elements = [ elements ];
	  }

	  this._commandStack.execute('element.setColor', {
	    elements: elements,
	    colors: colors
	  });
	};

	/**
	 * A base connection layouter implementation
	 * that layouts the connection by directly connecting
	 * mid(source) + mid(target).
	 */
	function BaseLayouter() {}


	/**
	 * Return the new layouted waypoints for the given connection.
	 *
	 * The connection passed is still unchanged; you may figure out about
	 * the new connection start / end via the layout hints provided.
	 *
	 * @param {djs.model.Connection} connection
	 * @param {Object} [hints]
	 * @param {Point} [hints.connectionStart]
	 * @param {Point} [hints.connectionEnd]
	 * @param {Point} [hints.source]
	 * @param {Point} [hints.target]
	 *
	 * @return {Array<Point>} the layouted connection waypoints
	 */
	BaseLayouter.prototype.layoutConnection = function(connection, hints) {

	  hints = hints || {};

	  return [
	    hints.connectionStart || getMid(hints.source || connection.source),
	    hints.connectionEnd || getMid(hints.target || connection.target)
	  ];
	};

	var MIN_SEGMENT_LENGTH = 20,
	    POINT_ORIENTATION_PADDING = 5;

	var round$9 = Math.round;

	var INTERSECTION_THRESHOLD$1 = 20,
	    ORIENTATION_THRESHOLD = {
	      'h:h': 20,
	      'v:v': 20,
	      'h:v': -10,
	      'v:h': -10
	    };

	function needsTurn(orientation, startDirection) {
	  return !{
	    t: /top/,
	    r: /right/,
	    b: /bottom/,
	    l: /left/,
	    h: /./,
	    v: /./
	  }[startDirection].test(orientation);
	}

	function canLayoutStraight(direction, targetOrientation) {
	  return {
	    t: /top/,
	    r: /right/,
	    b: /bottom/,
	    l: /left/,
	    h: /left|right/,
	    v: /top|bottom/
	  }[direction].test(targetOrientation);
	}

	function getSegmentBendpoints(a, b, directions) {
	  var orientation = getOrientation(b, a, POINT_ORIENTATION_PADDING);

	  var startDirection = directions.split(':')[0];

	  var xmid = round$9((b.x - a.x) / 2 + a.x),
	      ymid = round$9((b.y - a.y) / 2 + a.y);

	  var segmentEnd, segmentDirections;

	  var layoutStraight = canLayoutStraight(startDirection, orientation),
	      layoutHorizontal = /h|r|l/.test(startDirection),
	      layoutTurn = false;

	  var turnNextDirections = false;

	  if (layoutStraight) {
	    segmentEnd = layoutHorizontal ? { x: xmid, y: a.y } : { x: a.x, y: ymid };

	    segmentDirections = layoutHorizontal ? 'h:h' : 'v:v';
	  } else {
	    layoutTurn = needsTurn(orientation, startDirection);

	    segmentDirections = layoutHorizontal ? 'h:v' : 'v:h';

	    if (layoutTurn) {

	      if (layoutHorizontal) {
	        turnNextDirections = ymid === a.y;

	        segmentEnd = {
	          x: a.x + MIN_SEGMENT_LENGTH * (/l/.test(startDirection) ? -1 : 1),
	          y: turnNextDirections ? ymid + MIN_SEGMENT_LENGTH : ymid
	        };
	      } else {
	        turnNextDirections = xmid === a.x;

	        segmentEnd = {
	          x: turnNextDirections ? xmid + MIN_SEGMENT_LENGTH : xmid,
	          y: a.y + MIN_SEGMENT_LENGTH * (/t/.test(startDirection) ? -1 : 1)
	        };
	      }

	    } else {
	      segmentEnd = {
	        x: xmid,
	        y: ymid
	      };
	    }
	  }

	  return {
	    waypoints: getBendpoints(a, segmentEnd, segmentDirections).concat(segmentEnd),
	    directions:  segmentDirections,
	    turnNextDirections: turnNextDirections
	  };
	}

	function getStartSegment(a, b, directions) {
	  return getSegmentBendpoints(a, b, directions);
	}

	function getEndSegment(a, b, directions) {
	  var invertedSegment = getSegmentBendpoints(b, a, invertDirections(directions));

	  return {
	    waypoints: invertedSegment.waypoints.slice().reverse(),
	    directions: invertDirections(invertedSegment.directions),
	    turnNextDirections: invertedSegment.turnNextDirections
	  };
	}

	function getMidSegment(startSegment, endSegment) {

	  var startDirection = startSegment.directions.split(':')[1],
	      endDirection = endSegment.directions.split(':')[0];

	  if (startSegment.turnNextDirections) {
	    startDirection = startDirection == 'h' ? 'v' : 'h';
	  }

	  if (endSegment.turnNextDirections) {
	    endDirection = endDirection == 'h' ? 'v' : 'h';
	  }

	  var directions = startDirection + ':' + endDirection;

	  var bendpoints = getBendpoints(
	    startSegment.waypoints[startSegment.waypoints.length - 1],
	    endSegment.waypoints[0],
	    directions
	  );

	  return {
	    waypoints: bendpoints,
	    directions: directions
	  };
	}

	function invertDirections(directions) {
	  return directions.split(':').reverse().join(':');
	}

	/**
	 * Handle simple layouts with maximum two bendpoints.
	 */
	function getSimpleBendpoints(a, b, directions) {

	  var xmid = round$9((b.x - a.x) / 2 + a.x),
	      ymid = round$9((b.y - a.y) / 2 + a.y);

	  // one point, right or left from a
	  if (directions === 'h:v') {
	    return [ { x: b.x, y: a.y } ];
	  }

	  // one point, above or below a
	  if (directions === 'v:h') {
	    return [ { x: a.x, y: b.y } ];
	  }

	  // vertical segment between a and b
	  if (directions === 'h:h') {
	    return [
	      { x: xmid, y: a.y },
	      { x: xmid, y: b.y }
	    ];
	  }

	  // horizontal segment between a and b
	  if (directions === 'v:v') {
	    return [
	      { x: a.x, y: ymid },
	      { x: b.x, y: ymid }
	    ];
	  }

	  throw new Error('invalid directions: can only handle varians of [hv]:[hv]');
	}


	/**
	 * Returns the mid points for a manhattan connection between two points.
	 *
	 * @example h:h (horizontal:horizontal)
	 *
	 * [a]----[x]
	 *         |
	 *        [x]----[b]
	 *
	 * @example h:v (horizontal:vertical)
	 *
	 * [a]----[x]
	 *         |
	 *        [b]
	 *
	 * @example h:r (horizontal:right)
	 *
	 * [a]----[x]
	 *         |
	 *    [b]-[x]
	 *
	 * @param  {Point} a
	 * @param  {Point} b
	 * @param  {String} directions
	 *
	 * @return {Array<Point>}
	 */
	function getBendpoints(a, b, directions) {
	  directions = directions || 'h:h';

	  if (!isValidDirections(directions)) {
	    throw new Error(
	      'unknown directions: <' + directions + '>: ' +
	      'must be specified as <start>:<end> ' +
	      'with start/end in { h,v,t,r,b,l }'
	    );
	  }

	  // compute explicit directions, involving trbl dockings
	  // using a three segmented layouting algorithm
	  if (isExplicitDirections(directions)) {
	    var startSegment = getStartSegment(a, b, directions),
	        endSegment = getEndSegment(a, b, directions),
	        midSegment = getMidSegment(startSegment, endSegment);

	    return [].concat(
	      startSegment.waypoints,
	      midSegment.waypoints,
	      endSegment.waypoints
	    );
	  }

	  // handle simple [hv]:[hv] cases that can be easily computed
	  return getSimpleBendpoints(a, b, directions);
	}

	/**
	 * Create a connection between the two points according
	 * to the manhattan layout (only horizontal and vertical) edges.
	 *
	 * @param {Point} a
	 * @param {Point} b
	 *
	 * @param {String} [directions='h:h'] specifies manhattan directions for each point as {adirection}:{bdirection}.
	                   A directionfor a point is either `h` (horizontal) or `v` (vertical)
	 *
	 * @return {Array<Point>}
	 */
	function connectPoints(a, b, directions) {

	  var points = getBendpoints(a, b, directions);

	  points.unshift(a);
	  points.push(b);

	  return withoutRedundantPoints(points);
	}


	/**
	 * Connect two rectangles using a manhattan layouted connection.
	 *
	 * @param {Bounds} source source rectangle
	 * @param {Bounds} target target rectangle
	 * @param {Point} [start] source docking
	 * @param {Point} [end] target docking
	 *
	 * @param {Object} [hints]
	 * @param {String} [hints.preserveDocking=source] preserve docking on selected side
	 * @param {Array<String>} [hints.preferredLayouts]
	 * @param {Point|Boolean} [hints.connectionStart] whether the start changed
	 * @param {Point|Boolean} [hints.connectionEnd] whether the end changed
	 *
	 * @return {Array<Point>} connection points
	 */
	function connectRectangles(source, target, start, end, hints) {

	  var preferredLayouts = hints && hints.preferredLayouts || [];

	  var preferredLayout = without(preferredLayouts, 'straight')[0] || 'h:h';

	  var threshold = ORIENTATION_THRESHOLD[preferredLayout] || 0;

	  var orientation = getOrientation(source, target, threshold);

	  var directions = getDirections(orientation, preferredLayout);

	  start = start || getMid(source);
	  end = end || getMid(target);

	  var directionSplit = directions.split(':');

	  // compute actual docking points for start / end
	  // this ensures we properly layout only parts of the
	  // connection that lies in between the two rectangles
	  var startDocking = getDockingPoint(start, source, directionSplit[0], invertOrientation(orientation)),
	      endDocking = getDockingPoint(end, target, directionSplit[1], orientation);

	  return connectPoints(startDocking, endDocking, directions);
	}


	/**
	 * Repair the connection between two rectangles, of which one has been updated.
	 *
	 * @param {Bounds} source
	 * @param {Bounds} target
	 * @param {Point} [start]
	 * @param {Point} [end]
	 * @param {Array<Point>} [waypoints]
	 * @param {Object} [hints]
	 * @param {Array<String>} [hints.preferredLayouts] list of preferred layouts
	 * @param {Boolean} [hints.connectionStart]
	 * @param {Boolean} [hints.connectionEnd]
	 *
	 * @return {Array<Point>} repaired waypoints
	 */
	function repairConnection(source, target, start, end, waypoints, hints) {

	  if (isArray(start)) {
	    waypoints = start;
	    hints = end;

	    start = getMid(source);
	    end = getMid(target);
	  }

	  hints = assign({ preferredLayouts: [] }, hints);
	  waypoints = waypoints || [];

	  var preferredLayouts = hints.preferredLayouts,
	      preferStraight = preferredLayouts.indexOf('straight') !== -1,
	      repairedWaypoints;

	  // just layout non-existing or simple connections
	  // attempt to render straight lines, if required

	  // attempt to layout a straight line
	  repairedWaypoints = preferStraight && tryLayoutStraight(source, target, start, end, hints);

	  if (repairedWaypoints) {
	    return repairedWaypoints;
	  }

	  // try to layout from end
	  repairedWaypoints = hints.connectionEnd && tryRepairConnectionEnd(target, source, end, waypoints);

	  if (repairedWaypoints) {
	    return repairedWaypoints;
	  }

	  // try to layout from start
	  repairedWaypoints = hints.connectionStart && tryRepairConnectionStart(source, target, start, waypoints);

	  if (repairedWaypoints) {
	    return repairedWaypoints;
	  }

	  // or whether nothing seems to have changed
	  if (!hints.connectionStart && !hints.connectionEnd && waypoints && waypoints.length) {
	    return waypoints;
	  }

	  // simply reconnect if nothing else worked
	  return connectRectangles(source, target, start, end, hints);
	}


	function inRange(a, start, end) {
	  return a >= start && a <= end;
	}

	function isInRange(axis, a, b) {
	  var size = {
	    x: 'width',
	    y: 'height'
	  };

	  return inRange(a[axis], b[axis], b[axis] + b[size[axis]]);
	}

	/**
	 * Layout a straight connection
	 *
	 * @param {Bounds} source
	 * @param {Bounds} target
	 * @param {Point} start
	 * @param {Point} end
	 * @param {Object} [hints]
	 *
	 * @return {Array<Point>|null} waypoints if straight layout worked
	 */
	function tryLayoutStraight(source, target, start, end, hints) {
	  var axis = {},
	      primaryAxis,
	      orientation;

	  orientation = getOrientation(source, target);

	  // only layout a straight connection if shapes are
	  // horizontally or vertically aligned
	  if (!/^(top|bottom|left|right)$/.test(orientation)) {
	    return null;
	  }

	  if (/top|bottom/.test(orientation)) {
	    primaryAxis = 'x';
	  }

	  if (/left|right/.test(orientation)) {
	    primaryAxis = 'y';
	  }

	  if (hints.preserveDocking === 'target') {

	    if (!isInRange(primaryAxis, end, source)) {
	      return null;
	    }

	    axis[primaryAxis] = end[primaryAxis];

	    return [
	      {
	        x: axis.x !== undefined ? axis.x : start.x,
	        y: axis.y !== undefined ? axis.y : start.y,
	        original: {
	          x: axis.x !== undefined ? axis.x : start.x,
	          y: axis.y !== undefined ? axis.y : start.y
	        }
	      },
	      {
	        x: end.x,
	        y: end.y
	      }
	    ];

	  } else {

	    if (!isInRange(primaryAxis, start, target)) {
	      return null;
	    }

	    axis[primaryAxis] = start[primaryAxis];

	    return [
	      {
	        x: start.x,
	        y: start.y
	      },
	      {
	        x: axis.x !== undefined ? axis.x : end.x,
	        y: axis.y !== undefined ? axis.y : end.y,
	        original: {
	          x: axis.x !== undefined ? axis.x : end.x,
	          y: axis.y !== undefined ? axis.y : end.y
	        }
	      }
	    ];
	  }

	}

	/**
	 * Repair a connection from start.
	 *
	 * @param {Bounds} moved
	 * @param {Bounds} other
	 * @param {Point} newDocking
	 * @param {Array<Point>} points originalPoints from moved to other
	 *
	 * @return {Array<Point>|null} the repaired points between the two rectangles
	 */
	function tryRepairConnectionStart(moved, other, newDocking, points) {
	  return _tryRepairConnectionSide(moved, other, newDocking, points);
	}

	/**
	 * Repair a connection from end.
	 *
	 * @param {Bounds} moved
	 * @param {Bounds} other
	 * @param {Point} newDocking
	 * @param {Array<Point>} points originalPoints from moved to other
	 *
	 * @return {Array<Point>|null} the repaired points between the two rectangles
	 */
	function tryRepairConnectionEnd(moved, other, newDocking, points) {
	  var waypoints = points.slice().reverse();

	  waypoints = _tryRepairConnectionSide(moved, other, newDocking, waypoints);

	  return waypoints ? waypoints.reverse() : null;
	}

	/**
	 * Repair a connection from one side that moved.
	 *
	 * @param {Bounds} moved
	 * @param {Bounds} other
	 * @param {Point} newDocking
	 * @param {Array<Point>} points originalPoints from moved to other
	 *
	 * @return {Array<Point>} the repaired points between the two rectangles
	 */
	function _tryRepairConnectionSide(moved, other, newDocking, points) {

	  function needsRelayout(moved, other, points) {

	    if (points.length < 3) {
	      return true;
	    }

	    if (points.length > 4) {
	      return false;
	    }

	    // relayout if two points overlap
	    // this is most likely due to
	    return !!find(points, function(p, idx) {
	      var q = points[idx - 1];

	      return q && pointDistance(p, q) < 3;
	    });
	  }

	  function repairBendpoint(candidate, oldPeer, newPeer) {

	    var alignment = pointsAligned(oldPeer, candidate);

	    switch (alignment) {
	    case 'v':
	      // repair horizontal alignment
	      return { x: newPeer.x, y: candidate.y };
	    case 'h':
	      // repair vertical alignment
	      return { x: candidate.x, y: newPeer.y };
	    }

	    return { x: candidate.x, y: candidate. y };
	  }

	  function removeOverlapping(points, a, b) {
	    var i;

	    for (i = points.length - 2; i !== 0; i--) {

	      // intersects (?) break, remove all bendpoints up to this one and relayout
	      if (pointInRect(points[i], a, INTERSECTION_THRESHOLD$1) ||
	          pointInRect(points[i], b, INTERSECTION_THRESHOLD$1)) {

	        // return sliced old connection
	        return points.slice(i);
	      }
	    }

	    return points;
	  }


	  // (0) only repair what has layoutable bendpoints

	  // (1) if only one bendpoint and on shape moved onto other shapes axis
	  //     (horizontally / vertically), relayout

	  if (needsRelayout(moved, other, points)) {
	    return null;
	  }

	  var oldDocking = points[0],
	      newPoints = points.slice(),
	      slicedPoints;

	  // (2) repair only last line segment and only if it was layouted before

	  newPoints[0] = newDocking;
	  newPoints[1] = repairBendpoint(newPoints[1], oldDocking, newDocking);


	  // (3) if shape intersects with any bendpoint after repair,
	  //     remove all segments up to this bendpoint and repair from there

	  slicedPoints = removeOverlapping(newPoints, moved, other);

	  if (slicedPoints !== newPoints) {
	    return _tryRepairConnectionSide(moved, other, newDocking, slicedPoints);
	  }

	  return newPoints;
	}


	/**
	 * Returns the manhattan directions connecting two rectangles
	 * with the given orientation.
	 *
	 * Will always return the default layout, if it is specific
	 * regarding sides already (trbl).
	 *
	 * @example
	 *
	 * getDirections('top'); // -> 'v:v'
	 * getDirections('intersect'); // -> 't:t'
	 *
	 * getDirections('top-right', 'v:h'); // -> 'v:h'
	 * getDirections('top-right', 'h:h'); // -> 'h:h'
	 *
	 *
	 * @param {String} orientation
	 * @param {String} defaultLayout
	 *
	 * @return {String}
	 */
	function getDirections(orientation, defaultLayout) {

	  // don't override specific trbl directions
	  if (isExplicitDirections(defaultLayout)) {
	    return defaultLayout;
	  }

	  switch (orientation) {
	  case 'intersect':
	    return 't:t';

	  case 'top':
	  case 'bottom':
	    return 'v:v';

	  case 'left':
	  case 'right':
	    return 'h:h';

	  // 'top-left'
	  // 'top-right'
	  // 'bottom-left'
	  // 'bottom-right'
	  default:
	    return defaultLayout;
	  }
	}

	function isValidDirections(directions) {
	  return directions && /^h|v|t|r|b|l:h|v|t|r|b|l$/.test(directions);
	}

	function isExplicitDirections(directions) {
	  return directions && /t|r|b|l/.test(directions);
	}

	function invertOrientation(orientation) {
	  return {
	    'top': 'bottom',
	    'bottom': 'top',
	    'left': 'right',
	    'right': 'left',
	    'top-left': 'bottom-right',
	    'bottom-right': 'top-left',
	    'top-right': 'bottom-left',
	    'bottom-left': 'top-right',
	  }[orientation];
	}

	function getDockingPoint(point, rectangle, dockingDirection, targetOrientation) {

	  // ensure we end up with a specific docking direction
	  // based on the targetOrientation, if <h|v> is being passed

	  if (dockingDirection === 'h') {
	    dockingDirection = /left/.test(targetOrientation) ? 'l' : 'r';
	  }

	  if (dockingDirection === 'v') {
	    dockingDirection = /top/.test(targetOrientation) ? 't' : 'b';
	  }

	  if (dockingDirection === 't') {
	    return { original: point, x: point.x, y: rectangle.y };
	  }

	  if (dockingDirection === 'r') {
	    return { original: point, x: rectangle.x + rectangle.width, y: point.y };
	  }

	  if (dockingDirection === 'b') {
	    return { original: point, x: point.x, y: rectangle.y + rectangle.height };
	  }

	  if (dockingDirection === 'l') {
	    return { original: point, x: rectangle.x, y: point.y };
	  }

	  throw new Error('unexpected dockingDirection: <' + dockingDirection + '>');
	}


	/**
	 * Return list of waypoints with redundant ones filtered out.
	 *
	 * @example
	 *
	 * Original points:
	 *
	 *   [x] ----- [x] ------ [x]
	 *                         |
	 *                        [x] ----- [x] - [x]
	 *
	 * Filtered:
	 *
	 *   [x] ---------------- [x]
	 *                         |
	 *                        [x] ----------- [x]
	 *
	 * @param  {Array<Point>} waypoints
	 *
	 * @return {Array<Point>}
	 */
	function withoutRedundantPoints(waypoints) {
	  return waypoints.reduce(function(points, p, idx) {

	    var previous = points[points.length - 1],
	        next = waypoints[idx + 1];

	    if (!pointsOnLine(previous, next, p, 0)) {
	      points.push(p);
	    }

	    return points;
	  }, []);
	}

	var BOUNDARY_TO_HOST_THRESHOLD = 40;

	function BpmnLayouter() {}

	inherits_browser(BpmnLayouter, BaseLayouter);


	BpmnLayouter.prototype.layoutConnection = function(connection, hints) {
	  hints = hints || {};

	  var source = hints.source || connection.source,
	      target = hints.target || connection.target,
	      waypoints = connection.waypoints,
	      start = hints.connectionStart,
	      end = hints.connectionEnd;

	  var manhattanOptions,
	      updatedWaypoints;

	  if (!start) {
	    start = getConnectionDocking(waypoints && waypoints[0], source);
	  }

	  if (!end) {
	    end = getConnectionDocking(waypoints && waypoints[waypoints.length - 1], target);
	  }

	  // TODO(nikku): support vertical modeling
	  // and invert preferredLayouts accordingly

	  if (is$1(connection, 'bpmn:Association') ||
	      is$1(connection, 'bpmn:DataAssociation')) {

	    if (waypoints && !isCompensationAssociation(source, target)) {
	      return [].concat([ start ], waypoints.slice(1, -1), [ end ]);
	    }
	  }

	  // manhattan layout sequence / message flows
	  if (is$1(connection, 'bpmn:MessageFlow')) {
	    manhattanOptions = getMessageFlowManhattanOptions(source, target);

	  } else


	  // layout all connection between flow elements h:h,
	  //
	  // except for
	  //
	  // (1) outgoing of BoundaryEvents -> layout based on attach orientation and target orientation
	  // (2) incoming / outgoing of Gateway -> v:h (outgoing), h:v (incoming)
	  // (3) loops from / to the same element
	  //
	  if (is$1(connection, 'bpmn:SequenceFlow') ||
	      isCompensationAssociation(source, target)) {

	    if (source === target) {
	      manhattanOptions = {
	        preferredLayouts: [ 'b:l' ]
	      };
	    } else

	    if (is$1(source, 'bpmn:BoundaryEvent')) {

	      manhattanOptions = {
	        preferredLayouts: getBoundaryEventPreferredLayouts(source, target, end)
	      };

	    } else

	    if (is$1(source, 'bpmn:Gateway')) {

	      manhattanOptions = {
	        preferredLayouts: [ 'v:h' ]
	      };
	    } else

	    if (is$1(target, 'bpmn:Gateway')) {

	      manhattanOptions = {
	        preferredLayouts: [ 'h:v' ]
	      };
	    }

	    else {
	      manhattanOptions = {
	        preferredLayouts: [ 'h:h' ]
	      };
	    }

	  }

	  if (manhattanOptions) {

	    manhattanOptions = assign(manhattanOptions, hints);

	    updatedWaypoints =
	      withoutRedundantPoints(
	        repairConnection(
	          source, target,
	          start, end,
	          waypoints,
	          manhattanOptions
	        )
	      );
	  }

	  return updatedWaypoints || [ start, end ];
	};


	function getAttachOrientation(attachedElement) {

	  var hostElement = attachedElement.host,
	      padding = -10;

	  return getOrientation(getMid(attachedElement), hostElement, padding);
	}


	function getMessageFlowManhattanOptions(source, target) {
	  return {
	    preferredLayouts: [ 'straight', 'v:v' ],
	    preserveDocking: getMessageFlowPreserveDocking(source, target)
	  };
	}


	function getMessageFlowPreserveDocking(source, target) {

	  // (1) docking element connected to participant has precedence

	  if (is$1(target, 'bpmn:Participant')) {
	    return 'source';
	  }

	  if (is$1(source, 'bpmn:Participant')) {
	    return 'target';
	  }

	  // (2) docking element connected to expanded sub-process has precedence

	  if (isExpandedSubProcess$1(target)) {
	    return 'source';
	  }

	  if (isExpandedSubProcess$1(source)) {
	    return 'target';
	  }

	  // (3) docking event has precedence

	  if (is$1(target, 'bpmn:Event')) {
	    return 'target';
	  }

	  if (is$1(source, 'bpmn:Event')) {
	    return 'source';
	  }

	  return null;
	}


	function getConnectionDocking(point, shape) {
	  return point ? (point.original || point) : getMid(shape);
	}

	function isCompensationAssociation(source, target) {
	  return is$1(target, 'bpmn:Activity') &&
	         is$1(source, 'bpmn:BoundaryEvent') &&
	         target.businessObject.isForCompensation;
	}


	function isExpandedSubProcess$1(element) {
	  return is$1(element, 'bpmn:SubProcess') && isExpanded(element);
	}

	function isSame$1(a, b) {
	  return a === b;
	}

	function isAnyOrientation(orientation, orientations) {
	  return orientations.indexOf(orientation) !== -1;
	}

	var oppositeOrientationMapping = {
	  'top': 'bottom',
	  'top-right': 'bottom-left',
	  'top-left': 'bottom-right',
	  'right': 'left',
	  'bottom': 'top',
	  'bottom-right': 'top-left',
	  'bottom-left': 'top-right',
	  'left': 'right'
	};

	var orientationDirectionMapping = {
	  top: 't',
	  right: 'r',
	  bottom: 'b',
	  left: 'l'
	};

	function getHorizontalOrientation(orientation) {
	  var matches = /right|left/.exec(orientation);

	  return matches && matches[0];
	}

	function getVerticalOrientation(orientation) {
	  var matches = /top|bottom/.exec(orientation);

	  return matches && matches[0];
	}

	function isOppositeOrientation(a, b) {
	  return oppositeOrientationMapping[a] === b;
	}

	function isOppositeHorizontalOrientation(a, b) {
	  var horizontalOrientation = getHorizontalOrientation(a);

	  var oppositeHorizontalOrientation = oppositeOrientationMapping[horizontalOrientation];

	  return b.indexOf(oppositeHorizontalOrientation) !== -1;
	}

	function isOppositeVerticalOrientation(a, b) {
	  var verticalOrientation = getVerticalOrientation(a);

	  var oppositeVerticalOrientation = oppositeOrientationMapping[verticalOrientation];

	  return b.indexOf(oppositeVerticalOrientation) !== -1;
	}

	function isHorizontalOrientation(orientation) {
	  return orientation === 'right' || orientation === 'left';
	}

	function getBoundaryEventPreferredLayouts(source, target, end) {
	  var sourceMid = getMid(source),
	      targetMid = getMid(target),
	      attachOrientation = getAttachOrientation(source),
	      sourceLayout,
	      targetLayout;

	  var isLoop = isSame$1(source.host, target);

	  var attachedToSide = isAnyOrientation(attachOrientation, [ 'top', 'right', 'bottom', 'left' ]);

	  var targetOrientation = getOrientation(targetMid, sourceMid, {
	    x: source.width / 2 + target.width / 2,
	    y: source.height / 2 + target.height / 2
	  });

	  if (isLoop) {
	    return getBoundaryEventLoopLayout(attachOrientation, attachedToSide, source, target, end);
	  }

	  // source layout
	  sourceLayout = getBoundaryEventSourceLayout(attachOrientation, targetOrientation, attachedToSide);

	  // target layout
	  targetLayout = getBoundaryEventTargetLayout(attachOrientation, targetOrientation, attachedToSide);

	  return [ sourceLayout + ':' + targetLayout ];
	}

	function getBoundaryEventLoopLayout(attachOrientation, attachedToSide, source, target, end) {

	  var sourceLayout = orientationDirectionMapping[attachedToSide ? attachOrientation : getVerticalOrientation(attachOrientation)],
	      targetLayout;

	  if (attachedToSide) {
	    if (isHorizontalOrientation(attachOrientation)) {
	      targetLayout = shouldConnectToSameSide('y', source, target, end) ? 'h' : 'b';
	    } else {
	      targetLayout = shouldConnectToSameSide('x', source, target, end) ? 'v' : 'l';
	    }
	  } else {
	    targetLayout = 'v';
	  }

	  return [ sourceLayout + ':' + targetLayout ];
	}

	function shouldConnectToSameSide(axis, source, target, end) {
	  var threshold = BOUNDARY_TO_HOST_THRESHOLD;

	  return !(
	    areCloseOnAxis(axis, end, target, threshold) ||
	    areCloseOnAxis(axis, end, { x: target.x + target.width, y: target.y + target.height }, threshold) ||
	    areCloseOnAxis(axis, end, getMid(source), threshold)
	  );
	}

	function areCloseOnAxis(axis, a, b, threshold) {
	  return Math.abs(a[axis] - b[axis]) < threshold;
	}

	function getBoundaryEventSourceLayout(attachOrientation, targetOrientation, attachedToSide) {

	  // attached to either top, right, bottom or left side
	  if (attachedToSide) {
	    return orientationDirectionMapping[attachOrientation];
	  }

	  // attached to either top-right, top-left, bottom-right or bottom-left corner

	  // same vertical or opposite horizontal orientation
	  if (isSame$1(
	    getVerticalOrientation(attachOrientation), getVerticalOrientation(targetOrientation)
	  ) || isOppositeOrientation(
	    getHorizontalOrientation(attachOrientation), getHorizontalOrientation(targetOrientation)
	  )) {
	    return orientationDirectionMapping[getVerticalOrientation(attachOrientation)];
	  }

	  // fallback
	  return orientationDirectionMapping[getHorizontalOrientation(attachOrientation)];
	}

	function getBoundaryEventTargetLayout(attachOrientation, targetOrientation, attachedToSide) {

	  // attached to either top, right, bottom or left side
	  if (attachedToSide) {
	    if (isHorizontalOrientation(attachOrientation)) {
	      // orientation is 'right' or 'left'

	      // opposite horizontal orientation or same orientation
	      if (
	        isOppositeHorizontalOrientation(attachOrientation, targetOrientation) ||
	        isSame$1(attachOrientation, targetOrientation)
	      ) {
	        return 'h';
	      }

	      // fallback
	      return 'v';
	    } else {
	      // orientation is 'top' or 'bottom'

	      // opposite vertical orientation or same orientation
	      if (
	        isOppositeVerticalOrientation(attachOrientation, targetOrientation) ||
	        isSame$1(attachOrientation, targetOrientation)
	      ) {
	        return 'v';
	      }

	      // fallback
	      return 'h';
	    }
	  }
	  // attached to either top-right, top-left, bottom-right or bottom-left corner

	  // orientation is 'right', 'left'
	  // or same vertical orientation but also 'right' or 'left'
	  if (isHorizontalOrientation(targetOrientation) ||
	    (isSame$1(getVerticalOrientation(attachOrientation), getVerticalOrientation(targetOrientation)) &&
	      getHorizontalOrientation(targetOrientation))) {
	    return 'h';
	  } else {
	    return 'v';
	  }
	}

	function dockingToPoint(docking) {
	  // use the dockings actual point and
	  // retain the original docking
	  return assign({ original: docking.point.original || docking.point }, docking.actual);
	}


	/**
	 * A {@link ConnectionDocking} that crops connection waypoints based on
	 * the path(s) of the connection source and target.
	 *
	 * @param {djs.core.ElementRegistry} elementRegistry
	 */
	function CroppingConnectionDocking(elementRegistry, graphicsFactory) {
	  this._elementRegistry = elementRegistry;
	  this._graphicsFactory = graphicsFactory;
	}

	CroppingConnectionDocking.$inject = [ 'elementRegistry', 'graphicsFactory' ];


	/**
	 * @inheritDoc ConnectionDocking#getCroppedWaypoints
	 */
	CroppingConnectionDocking.prototype.getCroppedWaypoints = function(connection, source, target) {

	  source = source || connection.source;
	  target = target || connection.target;

	  var sourceDocking = this.getDockingPoint(connection, source, true),
	      targetDocking = this.getDockingPoint(connection, target);

	  var croppedWaypoints = connection.waypoints.slice(sourceDocking.idx + 1, targetDocking.idx);

	  croppedWaypoints.unshift(dockingToPoint(sourceDocking));
	  croppedWaypoints.push(dockingToPoint(targetDocking));

	  return croppedWaypoints;
	};

	/**
	 * Return the connection docking point on the specified shape
	 *
	 * @inheritDoc ConnectionDocking#getDockingPoint
	 */
	CroppingConnectionDocking.prototype.getDockingPoint = function(connection, shape, dockStart) {

	  var waypoints = connection.waypoints,
	      dockingIdx,
	      dockingPoint,
	      croppedPoint;

	  dockingIdx = dockStart ? 0 : waypoints.length - 1;
	  dockingPoint = waypoints[dockingIdx];

	  croppedPoint = this._getIntersection(shape, connection, dockStart);

	  return {
	    point: dockingPoint,
	    actual: croppedPoint || dockingPoint,
	    idx: dockingIdx
	  };
	};


	// helpers //////////////////////

	CroppingConnectionDocking.prototype._getIntersection = function(shape, connection, takeFirst) {

	  var shapePath = this._getShapePath(shape),
	      connectionPath = this._getConnectionPath(connection);

	  return getElementLineIntersection(shapePath, connectionPath, takeFirst);
	};

	CroppingConnectionDocking.prototype._getConnectionPath = function(connection) {
	  return this._graphicsFactory.getConnectionPath(connection);
	};

	CroppingConnectionDocking.prototype._getShapePath = function(shape) {
	  return this._graphicsFactory.getShapePath(shape);
	};

	CroppingConnectionDocking.prototype._getGfx = function(element) {
	  return this._elementRegistry.getGraphics(element);
	};

	var ModelingModule = {
	  __init__: [
	    'modeling',
	    'bpmnUpdater'
	  ],
	  __depends__: [
	    BehaviorModule,
	    RulesModule$1,
	    OrderingModule,
	    ReplaceModule$1,
	    CommandModule,
	    TooltipsModule,
	    LabelSupportModule,
	    AttachSupportModule,
	    SelectionModule,
	    ChangeSupportModule,
	    SpaceToolModule
	  ],
	  bpmnFactory: [ 'type', BpmnFactory ],
	  bpmnUpdater: [ 'type', BpmnUpdater ],
	  elementFactory: [ 'type', ElementFactory$1 ],
	  modeling: [ 'type', Modeling$1 ],
	  layouter: [ 'type', BpmnLayouter ],
	  connectionDocking: [ 'type', CroppingConnectionDocking ]
	};

	var LOW_PRIORITY$f = 500,
	    MEDIUM_PRIORITY = 1250,
	    HIGH_PRIORITY$d = 1500;

	var round$a = Math.round;

	function mid$2(element) {
	  return {
	    x: element.x + round$a(element.width / 2),
	    y: element.y + round$a(element.height / 2)
	  };
	}

	/**
	 * A plugin that makes shapes draggable / droppable.
	 *
	 * @param {EventBus} eventBus
	 * @param {Dragging} dragging
	 * @param {Modeling} modeling
	 * @param {Selection} selection
	 * @param {Rules} rules
	 */
	function MoveEvents(
	    eventBus, dragging, modeling,
	    selection, rules) {

	  // rules

	  function canMove(shapes, delta, position, target) {

	    return rules.allowed('elements.move', {
	      shapes: shapes,
	      delta: delta,
	      position: position,
	      target: target
	    });
	  }


	  // move events

	  // assign a high priority to this handler to setup the environment
	  // others may hook up later, e.g. at default priority and modify
	  // the move environment.
	  //
	  // This sets up the context with
	  //
	  // * shape: the primary shape being moved
	  // * shapes: a list of shapes to be moved
	  // * validatedShapes: a list of shapes that are being checked
	  //                    against the rules before and during move
	  //
	  eventBus.on('shape.move.start', HIGH_PRIORITY$d, function(event) {

	    var context = event.context,
	        shape = event.shape,
	        shapes = selection.get().slice();

	    // move only single shape if the dragged element
	    // is not part of the current selection
	    if (shapes.indexOf(shape) === -1) {
	      shapes = [ shape ];
	    }

	    // ensure we remove nested elements in the collection
	    // and add attachers for a proper dragger
	    shapes = removeNested(shapes);

	    // attach shapes to drag context
	    assign(context, {
	      shapes: shapes,
	      validatedShapes: shapes,
	      shape: shape
	    });
	  });


	  // assign a high priority to this handler to setup the environment
	  // others may hook up later, e.g. at default priority and modify
	  // the move environment
	  //
	  eventBus.on('shape.move.start', MEDIUM_PRIORITY, function(event) {

	    var context = event.context,
	        validatedShapes = context.validatedShapes,
	        canExecute;

	    canExecute = context.canExecute = canMove(validatedShapes);

	    // check if we can move the elements
	    if (!canExecute) {
	      return false;
	    }
	  });

	  // assign a low priority to this handler
	  // to let others modify the move event before we update
	  // the context
	  //
	  eventBus.on('shape.move.move', LOW_PRIORITY$f, function(event) {

	    var context = event.context,
	        validatedShapes = context.validatedShapes,
	        hover = event.hover,
	        delta = { x: event.dx, y: event.dy },
	        position = { x: event.x, y: event.y },
	        canExecute;

	    // check if we can move the elements
	    canExecute = canMove(validatedShapes, delta, position, hover);

	    context.delta = delta;
	    context.canExecute = canExecute;

	    // simply ignore move over
	    if (canExecute === null) {
	      context.target = null;

	      return;
	    }

	    context.target = hover;
	  });

	  eventBus.on('shape.move.end', function(event) {

	    var context = event.context;

	    var delta = context.delta,
	        canExecute = context.canExecute,
	        isAttach = canExecute === 'attach',
	        shapes = context.shapes;

	    if (canExecute === false) {
	      return false;
	    }

	    // ensure we have actual pixel values deltas
	    // (important when zoom level was > 1 during move)
	    delta.x = round$a(delta.x);
	    delta.y = round$a(delta.y);

	    if (delta.x === 0 && delta.y === 0) {

	      // didn't move
	      return;
	    }

	    modeling.moveElements(shapes, delta, context.target, {
	      primaryShape: context.shape,
	      attach: isAttach
	    });
	  });


	  // move activation

	  eventBus.on('element.mousedown', function(event) {

	    var originalEvent = getOriginal(event);

	    if (!originalEvent) {
	      throw new Error('must supply DOM mousedown event');
	    }

	    return start(originalEvent, event.element);
	  });

	  /**
	   * Start move.
	   *
	   * @param {MouseEvent} event
	   * @param {djs.model.Shape} shape
	   * @param {boolean} [activate]
	   * @param {Object} [context]
	   */
	  function start(event, element, activate, context) {
	    if (isObject(activate)) {
	      context = activate;
	      activate = false;
	    }

	    // do not move connections or the root element
	    if (element.waypoints || !element.parent) {
	      return;
	    }

	    var referencePoint = mid$2(element);

	    dragging.init(event, referencePoint, 'shape.move', {
	      cursor: 'grabbing',
	      autoActivate: activate,
	      data: {
	        shape: element,
	        context: context || {}
	      }
	    });

	    // we've handled the event
	    return true;
	  }

	  // API

	  this.start = start;
	}

	MoveEvents.$inject = [
	  'eventBus',
	  'dragging',
	  'modeling',
	  'selection',
	  'rules'
	];


	/**
	 * Return a filtered list of elements that do not contain
	 * those nested into others.
	 *
	 * @param  {Array<djs.model.Base>} elements
	 *
	 * @return {Array<djs.model.Base>} filtered
	 */
	function removeNested(elements) {

	  var ids = groupBy(elements, 'id');

	  return filter(elements, function(element) {
	    while ((element = element.parent)) {

	      // parent in selection
	      if (ids[element.id]) {
	        return false;
	      }
	    }

	    return true;
	  });
	}

	var LOW_PRIORITY$g = 499;

	var MARKER_DRAGGING$1 = 'djs-dragging',
	    MARKER_OK$3 = 'drop-ok',
	    MARKER_NOT_OK$3 = 'drop-not-ok',
	    MARKER_NEW_PARENT$1 = 'new-parent',
	    MARKER_ATTACH$2 = 'attach-ok';


	/**
	 * Provides previews for moving shapes when moving.
	 *
	 * @param {EventBus} eventBus
	 * @param {ElementRegistry} elementRegistry
	 * @param {Canvas} canvas
	 * @param {Styles} styles
	 */
	function MovePreview(
	    eventBus, canvas, styles, previewSupport) {

	  function getVisualDragShapes(shapes) {
	    var elements = getAllDraggedElements(shapes);

	    var filteredElements = removeEdges(elements);

	    return filteredElements;
	  }

	  function getAllDraggedElements(shapes) {
	    var allShapes = selfAndAllChildren(shapes, true);

	    var allConnections = map(allShapes, function(shape) {
	      return (shape.incoming || []).concat(shape.outgoing || []);
	    });

	    return flatten(allShapes.concat(allConnections));
	  }

	  /**
	   * Sets drop marker on an element.
	   */
	  function setMarker(element, marker) {

	    [ MARKER_ATTACH$2, MARKER_OK$3, MARKER_NOT_OK$3, MARKER_NEW_PARENT$1 ].forEach(function(m) {

	      if (m === marker) {
	        canvas.addMarker(element, m);
	      } else {
	        canvas.removeMarker(element, m);
	      }
	    });
	  }

	  /**
	   * Make an element draggable.
	   *
	   * @param {Object} context
	   * @param {djs.model.Base} element
	   * @param {Boolean} addMarker
	   */
	  function makeDraggable(context, element, addMarker) {

	    previewSupport.addDragger(element, context.dragGroup);

	    if (addMarker) {
	      canvas.addMarker(element, MARKER_DRAGGING$1);
	    }

	    if (context.allDraggedElements) {
	      context.allDraggedElements.push(element);
	    } else {
	      context.allDraggedElements = [ element ];
	    }
	  }

	  // assign a low priority to this handler
	  // to let others modify the move context before
	  // we draw things
	  eventBus.on('shape.move.start', LOW_PRIORITY$g, function(event) {
	    var context = event.context,
	        dragShapes = context.shapes,
	        allDraggedElements = context.allDraggedElements;

	    var visuallyDraggedShapes = getVisualDragShapes(dragShapes);

	    if (!context.dragGroup) {
	      var dragGroup = create('g');

	      attr$1(dragGroup, styles.cls('djs-drag-group', [ 'no-events' ]));

	      var defaultLayer = canvas.getDefaultLayer();

	      append(defaultLayer, dragGroup);

	      context.dragGroup = dragGroup;
	    }

	    // add previews
	    visuallyDraggedShapes.forEach(function(shape) {
	      previewSupport.addDragger(shape, context.dragGroup);
	    });

	    // cache all dragged elements / gfx
	    // so that we can quickly undo their state changes later
	    if (!allDraggedElements) {
	      allDraggedElements = getAllDraggedElements(dragShapes);
	    } else {
	      allDraggedElements = flatten([
	        allDraggedElements,
	        getAllDraggedElements(dragShapes)
	      ]);
	    }

	    // add dragging marker
	    forEach(allDraggedElements, function(e) {
	      canvas.addMarker(e, MARKER_DRAGGING$1);
	    });

	    context.allDraggedElements = allDraggedElements;

	    // determine, if any of the dragged elements have different parents
	    context.differentParents = haveDifferentParents(dragShapes);
	  });

	  // update previews
	  eventBus.on('shape.move.move', LOW_PRIORITY$g, function(event) {

	    var context = event.context,
	        dragGroup = context.dragGroup,
	        target = context.target,
	        parent = context.shape.parent,
	        canExecute = context.canExecute;

	    if (target) {
	      if (canExecute === 'attach') {
	        setMarker(target, MARKER_ATTACH$2);
	      } else if (context.canExecute && target && target.id !== parent.id) {
	        setMarker(target, MARKER_NEW_PARENT$1);
	      } else {
	        setMarker(target, context.canExecute ? MARKER_OK$3 : MARKER_NOT_OK$3);
	      }
	    }

	    translate(dragGroup, event.dx, event.dy);
	  });

	  eventBus.on([ 'shape.move.out', 'shape.move.cleanup' ], function(event) {
	    var context = event.context,
	        target = context.target;

	    if (target) {
	      setMarker(target, null);
	    }
	  });

	  // remove previews
	  eventBus.on('shape.move.cleanup', function(event) {

	    var context = event.context,
	        allDraggedElements = context.allDraggedElements,
	        dragGroup = context.dragGroup;


	    // remove dragging marker
	    forEach(allDraggedElements, function(e) {
	      canvas.removeMarker(e, MARKER_DRAGGING$1);
	    });

	    if (dragGroup) {
	      remove$1(dragGroup);
	    }
	  });


	  // API //////////////////////

	  /**
	   * Make an element draggable.
	   *
	   * @param {Object} context
	   * @param {djs.model.Base} element
	   * @param {Boolean} addMarker
	   */
	  this.makeDraggable = makeDraggable;
	}

	MovePreview.$inject = [
	  'eventBus',
	  'canvas',
	  'styles',
	  'previewSupport'
	];


	// helpers //////////////////////

	/**
	 * returns elements minus all connections
	 * where source or target is not elements
	 */
	function removeEdges(elements) {

	  var filteredElements = filter(elements, function(element) {

	    if (!isConnection$4(element)) {
	      return true;
	    } else {

	      return (
	        find(elements, matchPattern({ id: element.source.id })) &&
	        find(elements, matchPattern({ id: element.target.id }))
	      );
	    }
	  });

	  return filteredElements;
	}

	function haveDifferentParents(elements) {
	  return size(groupBy(elements, function(e) { return e.parent && e.parent.id; })) !== 1;
	}

	/**
	 * Checks if an element is a connection.
	 */
	function isConnection$4(element) {
	  return element.waypoints;
	}

	var MoveModule = {
	  __depends__: [
	    InteractionEventsModule,
	    SelectionModule,
	    OutlineModule,
	    RulesModule,
	    DraggingModule,
	    PreviewSupportModule
	  ],
	  __init__: [
	    'move',
	    'movePreview'
	  ],
	  move: [ 'type', MoveEvents ],
	  movePreview: [ 'type', MovePreview ]
	};

	var TOGGLE_SELECTOR = '.djs-palette-toggle',
	    ENTRY_SELECTOR = '.entry',
	    ELEMENT_SELECTOR = TOGGLE_SELECTOR + ', ' + ENTRY_SELECTOR;

	var PALETTE_OPEN_CLS = 'open',
	    PALETTE_TWO_COLUMN_CLS = 'two-column';


	/**
	 * A palette containing modeling elements.
	 */
	function Palette(eventBus, canvas) {

	  this._eventBus = eventBus;
	  this._canvas = canvas;

	  this._providers = [];

	  var self = this;

	  eventBus.on('tool-manager.update', function(event) {
	    var tool = event.tool;

	    self.updateToolHighlight(tool);
	  });

	  eventBus.on('i18n.changed', function() {
	    self._update();
	  });

	  eventBus.on('diagram.init', function() {

	    self._diagramInitialized = true;

	    // initialize + update once diagram is ready
	    if (self._providers.length) {
	      self._init();

	      self._update();
	    }
	  });
	}

	Palette.$inject = [ 'eventBus', 'canvas' ];


	/**
	 * Register a provider with the palette
	 *
	 * @param  {PaletteProvider} provider
	 */
	Palette.prototype.registerProvider = function(provider) {
	  this._providers.push(provider);

	  // postpone init / update until diagram is initialized
	  if (!this._diagramInitialized) {
	    return;
	  }

	  if (!this._container) {
	    this._init();
	  }

	  this._update();
	};


	/**
	 * Returns the palette entries for a given element
	 *
	 * @return {Array<PaletteEntryDescriptor>} list of entries
	 */
	Palette.prototype.getEntries = function() {

	  var entries = {};

	  // loop through all providers and their entries.
	  // group entries by id so that overriding an entry is possible
	  forEach(this._providers, function(provider) {
	    var e = provider.getPaletteEntries();

	    forEach(e, function(entry, id) {
	      entries[id] = entry;
	    });
	  });

	  return entries;
	};


	/**
	 * Initialize
	 */
	Palette.prototype._init = function() {
	  var canvas = this._canvas,
	      eventBus = this._eventBus;

	  var parent = canvas.getContainer(),
	      container = this._container = domify(Palette.HTML_MARKUP),
	      self = this;

	  parent.appendChild(container);

	  delegateEvents.bind(container, ELEMENT_SELECTOR, 'click', function(event) {

	    var target = event.delegateTarget;

	    if (matchesSelector$1(target, TOGGLE_SELECTOR)) {
	      return self.toggle();
	    }

	    self.trigger('click', event);
	  });

	  // prevent drag propagation
	  componentEvent.bind(container, 'mousedown', function(event) {
	    event.stopPropagation();
	  });

	  // prevent drag propagation
	  delegateEvents.bind(container, ENTRY_SELECTOR, 'dragstart', function(event) {
	    self.trigger('dragstart', event);
	  });

	  eventBus.on('canvas.resized', this._layoutChanged, this);

	  eventBus.fire('palette.create', {
	    container: container
	  });
	};

	/**
	 * Update palette state.
	 *
	 * @param  {Object} [state] { open, twoColumn }
	 */
	Palette.prototype._toggleState = function(state) {

	  state = state || {};

	  var parent = this._getParentContainer(),
	      container = this._container;

	  var eventBus = this._eventBus;

	  var twoColumn;

	  var cls = classes(container);

	  if ('twoColumn' in state) {
	    twoColumn = state.twoColumn;
	  } else {
	    twoColumn = this._needsCollapse(parent.clientHeight, this._entries || {});
	  }

	  // always update two column
	  cls.toggle(PALETTE_TWO_COLUMN_CLS, twoColumn);

	  if ('open' in state) {
	    cls.toggle(PALETTE_OPEN_CLS, state.open);
	  }

	  eventBus.fire('palette.changed', {
	    twoColumn: twoColumn,
	    open: this.isOpen()
	  });
	};

	Palette.prototype._update = function() {

	  var entriesContainer = query('.djs-palette-entries', this._container),
	      entries = this._entries = this.getEntries();

	  clear(entriesContainer);

	  forEach(entries, function(entry, id) {

	    var grouping = entry.group || 'default';

	    var container = query('[data-group=' + grouping + ']', entriesContainer);
	    if (!container) {
	      container = domify('<div class="group" data-group="' + grouping + '"></div>');
	      entriesContainer.appendChild(container);
	    }

	    var html = entry.html || (
	      entry.separator ?
	        '<hr class="separator" />' :
	        '<div class="entry" draggable="true"></div>');


	    var control = domify(html);
	    container.appendChild(control);

	    if (!entry.separator) {
	      attr(control, 'data-action', id);

	      if (entry.title) {
	        attr(control, 'title', entry.title);
	      }

	      if (entry.className) {
	        addClasses$1(control, entry.className);
	      }

	      if (entry.imageUrl) {
	        control.appendChild(domify('<img src="' + entry.imageUrl + '">'));
	      }
	    }
	  });

	  // open after update
	  this.open();
	};


	/**
	 * Trigger an action available on the palette
	 *
	 * @param  {String} action
	 * @param  {Event} event
	 */
	Palette.prototype.trigger = function(action, event, autoActivate) {
	  var entries = this._entries,
	      entry,
	      handler,
	      originalEvent,
	      button = event.delegateTarget || event.target;

	  if (!button) {
	    return event.preventDefault();
	  }

	  entry = entries[attr(button, 'data-action')];

	  // when user clicks on the palette and not on an action
	  if (!entry) {
	    return;
	  }

	  handler = entry.action;

	  originalEvent = event.originalEvent || event;

	  // simple action (via callback function)
	  if (isFunction(handler)) {
	    if (action === 'click') {
	      handler(originalEvent, autoActivate);
	    }
	  } else {
	    if (handler[action]) {
	      handler[action](originalEvent, autoActivate);
	    }
	  }

	  // silence other actions
	  event.preventDefault();
	};

	Palette.prototype._layoutChanged = function() {
	  this._toggleState({});
	};

	/**
	 * Do we need to collapse to two columns?
	 *
	 * @param {Number} availableHeight
	 * @param {Object} entries
	 *
	 * @return {Boolean}
	 */
	Palette.prototype._needsCollapse = function(availableHeight, entries) {

	  // top margin + bottom toggle + bottom margin
	  // implementors must override this method if they
	  // change the palette styles
	  var margin = 20 + 10 + 20;

	  var entriesHeight = Object.keys(entries).length * 46;

	  return availableHeight < entriesHeight + margin;
	};

	/**
	 * Close the palette
	 */
	Palette.prototype.close = function() {

	  this._toggleState({
	    open: false,
	    twoColumn: false
	  });
	};


	/**
	 * Open the palette
	 */
	Palette.prototype.open = function() {
	  this._toggleState({ open: true });
	};


	Palette.prototype.toggle = function(open) {
	  if (this.isOpen()) {
	    this.close();
	  } else {
	    this.open();
	  }
	};

	Palette.prototype.isActiveTool = function(tool) {
	  return tool && this._activeTool === tool;
	};

	Palette.prototype.updateToolHighlight = function(name) {
	  var entriesContainer,
	      toolsContainer;

	  if (!this._toolsContainer) {
	    entriesContainer = query('.djs-palette-entries', this._container);

	    this._toolsContainer = query('[data-group=tools]', entriesContainer);
	  }

	  toolsContainer = this._toolsContainer;

	  forEach(toolsContainer.children, function(tool) {
	    var actionName = tool.getAttribute('data-action');

	    if (!actionName) {
	      return;
	    }

	    var toolClasses = classes(tool);

	    actionName = actionName.replace('-tool', '');

	    if (toolClasses.contains('entry') && actionName === name) {
	      toolClasses.add('highlighted-entry');
	    } else {
	      toolClasses.remove('highlighted-entry');
	    }
	  });
	};


	/**
	 * Return true if the palette is opened.
	 *
	 * @example
	 *
	 * palette.open();
	 *
	 * if (palette.isOpen()) {
	 *   // yes, we are open
	 * }
	 *
	 * @return {boolean} true if palette is opened
	 */
	Palette.prototype.isOpen = function() {
	  return classes(this._container).has(PALETTE_OPEN_CLS);
	};

	/**
	 * Get container the palette lives in.
	 *
	 * @return {Element}
	 */
	Palette.prototype._getParentContainer = function() {
	  return this._canvas.getContainer();
	};


	/* markup definition */

	Palette.HTML_MARKUP =
	  '<div class="djs-palette">' +
	    '<div class="djs-palette-entries"></div>' +
	    '<div class="djs-palette-toggle"></div>' +
	  '</div>';


	// helpers //////////////////////

	function addClasses$1(element, classNames) {

	  var classes$1 = classes(element);

	  var actualClassNames = isArray(classNames) ? classNames : classNames.split(/\s+/g);
	  actualClassNames.forEach(function(cls) {
	    classes$1.add(cls);
	  });
	}

	var PaletteModule = {
	  __init__: [ 'palette' ],
	  palette: [ 'type', Palette ]
	};

	var LASSO_TOOL_CURSOR = 'crosshair';


	function LassoTool(
	    eventBus, canvas, dragging,
	    elementRegistry, selection, toolManager) {

	  this._selection = selection;
	  this._dragging = dragging;

	  var self = this;

	  // lasso visuals implementation

	  /**
	  * A helper that realizes the selection box visual
	  */
	  var visuals = {

	    create: function(context) {
	      var container = canvas.getDefaultLayer(),
	          frame;

	      frame = context.frame = create('rect');
	      attr$1(frame, {
	        class: 'djs-lasso-overlay',
	        width:  1,
	        height: 1,
	        x: 0,
	        y: 0
	      });

	      append(container, frame);
	    },

	    update: function(context) {
	      var frame = context.frame,
	          bbox = context.bbox;

	      attr$1(frame, {
	        x: bbox.x,
	        y: bbox.y,
	        width: bbox.width,
	        height: bbox.height
	      });
	    },

	    remove: function(context) {

	      if (context.frame) {
	        remove$1(context.frame);
	      }
	    }
	  };

	  toolManager.registerTool('lasso', {
	    tool: 'lasso.selection',
	    dragging: 'lasso'
	  });

	  eventBus.on('lasso.selection.end', function(event) {
	    var target = event.originalEvent.target;

	    // only reactive on diagram click
	    // on some occasions, event.hover is not set and we have to check if the target is an svg
	    if (!event.hover && !(target instanceof SVGElement)) {
	      return;
	    }

	    eventBus.once('lasso.selection.ended', function() {
	      self.activateLasso(event.originalEvent, true);
	    });
	  });

	  // lasso interaction implementation

	  eventBus.on('lasso.end', function(event) {

	    var bbox = toBBox(event);

	    var elements = elementRegistry.filter(function(element) {
	      return element;
	    });

	    self.select(elements, bbox);
	  });

	  eventBus.on('lasso.start', function(event) {

	    var context = event.context;

	    context.bbox = toBBox(event);
	    visuals.create(context);
	  });

	  eventBus.on('lasso.move', function(event) {

	    var context = event.context;

	    context.bbox = toBBox(event);
	    visuals.update(context);
	  });

	  eventBus.on('lasso.cleanup', function(event) {

	    var context = event.context;

	    visuals.remove(context);
	  });


	  // event integration

	  eventBus.on('element.mousedown', 1500, function(event) {

	    if (hasSecondaryModifier(event)) {
	      self.activateLasso(event.originalEvent);

	      // we've handled the event
	      return true;
	    }
	  });
	}

	LassoTool.$inject = [
	  'eventBus',
	  'canvas',
	  'dragging',
	  'elementRegistry',
	  'selection',
	  'toolManager'
	];


	LassoTool.prototype.activateLasso = function(event, autoActivate) {

	  this._dragging.init(event, 'lasso', {
	    autoActivate: autoActivate,
	    cursor: LASSO_TOOL_CURSOR,
	    data: {
	      context: {}
	    }
	  });
	};

	LassoTool.prototype.activateSelection = function(event) {

	  this._dragging.init(event, 'lasso.selection', {
	    trapClick: false,
	    cursor: LASSO_TOOL_CURSOR,
	    data: {
	      context: {}
	    }
	  });
	};

	LassoTool.prototype.select = function(elements, bbox) {
	  var selectedElements = getEnclosedElements(elements, bbox);

	  this._selection.select(values(selectedElements));
	};

	LassoTool.prototype.toggle = function() {
	  if (this.isActive()) {
	    this._dragging.cancel();
	  } else {
	    this.activateSelection();
	  }
	};

	LassoTool.prototype.isActive = function() {
	  var context = this._dragging.context();

	  return context && /^lasso/.test(context.prefix);
	};



	function toBBox(event) {

	  var start = {

	    x: event.x - event.dx,
	    y: event.y - event.dy
	  };

	  var end = {
	    x: event.x,
	    y: event.y
	  };

	  var bbox;

	  if ((start.x <= end.x && start.y < end.y) ||
	      (start.x < end.x && start.y <= end.y)) {

	    bbox = {
	      x: start.x,
	      y: start.y,
	      width:  end.x - start.x,
	      height: end.y - start.y
	    };
	  } else if ((start.x >= end.x && start.y < end.y) ||
	             (start.x > end.x && start.y <= end.y)) {

	    bbox = {
	      x: end.x,
	      y: start.y,
	      width:  start.x - end.x,
	      height: end.y - start.y
	    };
	  } else if ((start.x <= end.x && start.y > end.y) ||
	             (start.x < end.x && start.y >= end.y)) {

	    bbox = {
	      x: start.x,
	      y: end.y,
	      width:  end.x - start.x,
	      height: start.y - end.y
	    };
	  } else if ((start.x >= end.x && start.y > end.y) ||
	             (start.x > end.x && start.y >= end.y)) {

	    bbox = {
	      x: end.x,
	      y: end.y,
	      width:  start.x - end.x,
	      height: start.y - end.y
	    };
	  } else {

	    bbox = {
	      x: end.x,
	      y: end.y,
	      width:  0,
	      height: 0
	    };
	  }
	  return bbox;
	}

	var LassoToolModule = {
	  __depends__: [
	    ToolManagerModule
	  ],
	  __init__: [ 'lassoTool' ],
	  lassoTool: [ 'type', LassoTool ]
	};

	var HIGH_PRIORITY$e = 1500;
	var HAND_CURSOR = 'grab';


	function HandTool(eventBus, canvas, dragging, injector, toolManager) {
	  this._dragging = dragging;

	  var self = this,
	      keyboard = injector.get('keyboard', false);

	  toolManager.registerTool('hand', {
	    tool: 'hand',
	    dragging: 'hand.move'
	  });

	  eventBus.on('element.mousedown', HIGH_PRIORITY$e, function(event) {
	    if (hasPrimaryModifier(event)) {
	      this.activateMove(event.originalEvent);

	      return false;
	    }
	  }, this);

	  keyboard && keyboard.addListener(HIGH_PRIORITY$e, function(e) {
	    if (!isSpace(e.keyEvent)) {
	      return;
	    }

	    if (self.isActive()) {
	      return;
	    }

	    function activateMove(event) {
	      self.activateMove(event);

	      window.removeEventListener('mousemove', activateMove);
	    }

	    window.addEventListener('mousemove', activateMove);

	    function deactivateMove(e) {
	      if (!isSpace(e.keyEvent)) {
	        return;
	      }

	      window.removeEventListener('mousemove', activateMove);

	      keyboard.removeListener(deactivateMove, 'keyboard.keyup');

	      dragging.cancel();
	    }

	    keyboard.addListener(HIGH_PRIORITY$e, deactivateMove, 'keyboard.keyup');
	  }, 'keyboard.keydown');

	  eventBus.on('hand.end', function(event) {
	    var target = event.originalEvent.target;

	    // only reactive on diagram click
	    // on some occasions, event.hover is not set and we have to check if the target is an svg
	    if (!event.hover && !(target instanceof SVGElement)) {
	      return false;
	    }

	    eventBus.once('hand.ended', function() {
	      this.activateMove(event.originalEvent, { reactivate: true });
	    }, this);

	  }, this);


	  eventBus.on('hand.move.move', function(event) {
	    var scale = canvas.viewbox().scale;

	    canvas.scroll({
	      dx: event.dx * scale,
	      dy: event.dy * scale
	    });
	  });

	  eventBus.on('hand.move.end', function(event) {
	    var context = event.context,
	        reactivate = context.reactivate;

	    // Don't reactivate if the user is using the keyboard keybinding
	    if (!hasPrimaryModifier(event) && reactivate) {

	      eventBus.once('hand.move.ended', function(event) {
	        this.activateHand(event.originalEvent, true, true);
	      }, this);

	    }

	    return false;
	  }, this);

	}

	HandTool.$inject = [
	  'eventBus',
	  'canvas',
	  'dragging',
	  'injector',
	  'toolManager'
	];


	HandTool.prototype.activateMove = function(event, autoActivate, context) {
	  if (typeof autoActivate === 'object') {
	    context = autoActivate;
	    autoActivate = false;
	  }

	  this._dragging.init(event, 'hand.move', {
	    autoActivate: autoActivate,
	    cursor: HAND_CURSOR,
	    data: {
	      context: context || {}
	    }
	  });
	};

	HandTool.prototype.activateHand = function(event, autoActivate, reactivate) {
	  this._dragging.init(event, 'hand', {
	    trapClick: false,
	    autoActivate: autoActivate,
	    cursor: HAND_CURSOR,
	    data: {
	      context: {
	        reactivate: reactivate
	      }
	    }
	  });
	};

	HandTool.prototype.toggle = function() {
	  if (this.isActive()) {
	    this._dragging.cancel();
	  } else {
	    this.activateHand();
	  }
	};

	HandTool.prototype.isActive = function() {
	  var context = this._dragging.context();

	  if (context) {
	    return /^(hand|hand\.move)$/.test(context.prefix);
	  }

	  return false;
	};

	// helpers //////////

	function isSpace(keyEvent) {
	  return isKey(' ', keyEvent);
	}

	var HandToolModule = {
	  __depends__: [
	    ToolManagerModule
	  ],
	  __init__: [ 'handTool' ],
	  handTool: [ 'type', HandTool ]
	};

	var MARKER_OK$4 = 'connect-ok',
	    MARKER_NOT_OK$4 = 'connect-not-ok';

	/**
	 * @class
	 * @constructor
	 *
	 * @param {EventBus} eventBus
	 * @param {Dragging} dragging
	 * @param {Connect} connect
	 * @param {Canvas} canvas
	 * @param {ToolManager} toolManager
	 * @param {Rules} rules
	 */
	function GlobalConnect(
	    eventBus, dragging, connect,
	    canvas, toolManager, rules) {

	  var self = this;

	  this._dragging = dragging;
	  this._rules = rules;

	  toolManager.registerTool('global-connect', {
	    tool: 'global-connect',
	    dragging: 'global-connect.drag'
	  });

	  eventBus.on('global-connect.hover', function(event) {
	    var context = event.context,
	        startTarget = event.hover;

	    var canStartConnect = context.canStartConnect = self.canStartConnect(startTarget);

	    // simply ignore hover
	    if (canStartConnect === null) {
	      return;
	    }

	    context.startTarget = startTarget;

	    canvas.addMarker(startTarget, canStartConnect ? MARKER_OK$4 : MARKER_NOT_OK$4);
	  });


	  eventBus.on([ 'global-connect.out', 'global-connect.cleanup' ], function(event) {
	    var startTarget = event.context.startTarget,
	        canStartConnect = event.context.canStartConnect;

	    if (startTarget) {
	      canvas.removeMarker(startTarget, canStartConnect ? MARKER_OK$4 : MARKER_NOT_OK$4);
	    }
	  });


	  eventBus.on([ 'global-connect.ended' ], function(event) {
	    var context = event.context,
	        startTarget = context.startTarget,
	        startPosition = {
	          x: event.x,
	          y: event.y
	        };

	    var canStartConnect = self.canStartConnect(startTarget);

	    if (!canStartConnect) {
	      return;
	    }

	    eventBus.once('element.out', function() {
	      eventBus.once([ 'connect.ended', 'connect.canceled' ], function() {
	        eventBus.fire('global-connect.drag.ended');
	      });

	      connect.start(null, startTarget, startPosition);
	    });

	    return false;
	  });
	}

	GlobalConnect.$inject = [
	  'eventBus',
	  'dragging',
	  'connect',
	  'canvas',
	  'toolManager',
	  'rules'
	];

	/**
	 * Initiates tool activity.
	 */
	GlobalConnect.prototype.start = function(event) {
	  this._dragging.init(event, 'global-connect', {
	    trapClick: false,
	    data: {
	      context: {}
	    }
	  });
	};

	GlobalConnect.prototype.toggle = function() {
	  if (this.isActive()) {
	    this._dragging.cancel();
	  } else {
	    this.start();
	  }
	};

	GlobalConnect.prototype.isActive = function() {
	  var context = this._dragging.context();

	  return context && /^global-connect/.test(context.prefix);
	};

	/**
	 * Check if source shape can initiate connection.
	 *
	 * @param  {Shape} startTarget
	 * @return {Boolean}
	 */
	GlobalConnect.prototype.canStartConnect = function(startTarget) {
	  return this._rules.allowed('connection.start', { source: startTarget });
	};

	var GlobalConnectModule = {
	  __depends__: [
	    ConnectModule,
	    RulesModule,
	    DraggingModule,
	    ToolManagerModule
	  ],
	  globalConnect: [ 'type', GlobalConnect ]
	};

	/**
	 * A palette provider for BPMN 2.0 elements.
	 */
	function PaletteProvider(
	    palette, create, elementFactory,
	    spaceTool, lassoTool, handTool,
	    globalConnect, translate) {

	  this._palette = palette;
	  this._create = create;
	  this._elementFactory = elementFactory;
	  this._spaceTool = spaceTool;
	  this._lassoTool = lassoTool;
	  this._handTool = handTool;
	  this._globalConnect = globalConnect;
	  this._translate = translate;

	  palette.registerProvider(this);
	}

	PaletteProvider.$inject = [
	  'palette',
	  'create',
	  'elementFactory',
	  'spaceTool',
	  'lassoTool',
	  'handTool',
	  'globalConnect',
	  'translate'
	];


	PaletteProvider.prototype.getPaletteEntries = function(element) {

	  var actions = {},
	      create = this._create,
	      elementFactory = this._elementFactory,
	      spaceTool = this._spaceTool,
	      lassoTool = this._lassoTool,
	      handTool = this._handTool,
	      globalConnect = this._globalConnect,
	      translate = this._translate;

	  function createAction(type, group, className, title, options) {

	    function createListener(event) {
	      var shape = elementFactory.createShape(assign({ type: type }, options));

	      if (options) {
	        shape.businessObject.di.isExpanded = options.isExpanded;
	      }

	      create.start(event, shape);
	    }

	    var shortType = type.replace(/^bpmn:/, '');

	    return {
	      group: group,
	      className: className,
	      title: title || translate('Create {type}', { type: shortType }),
	      action: {
	        dragstart: createListener,
	        click: createListener
	      }
	    };
	  }

	  function createParticipant(event, collapsed) {
	    create.start(event, elementFactory.createParticipantShape(collapsed));
	  }

	  assign(actions, {
	    'hand-tool': {
	      group: 'tools',
	      className: 'bpmn-icon-hand-tool',
	      title: translate('Activate the hand tool'),
	      action: {
	        click: function(event) {
	          handTool.activateHand(event);
	        }
	      }
	    },
	    'lasso-tool': {
	      group: 'tools',
	      className: 'bpmn-icon-lasso-tool',
	      title: translate('Activate the lasso tool'),
	      action: {
	        click: function(event) {
	          lassoTool.activateSelection(event);
	        }
	      }
	    },
	    'space-tool': {
	      group: 'tools',
	      className: 'bpmn-icon-space-tool',
	      title: translate('Activate the create/remove space tool'),
	      action: {
	        click: function(event) {
	          spaceTool.activateSelection(event);
	        }
	      }
	    },
	    'global-connect-tool': {
	      group: 'tools',
	      className: 'bpmn-icon-connection-multi',
	      title: translate('Activate the global connect tool'),
	      action: {
	        click: function(event) {
	          globalConnect.toggle(event);
	        }
	      }
	    },
	    'tool-separator': {
	      group: 'tools',
	      separator: true
	    },
	    'create.start-event': createAction(
	      'bpmn:StartEvent', 'event', 'bpmn-icon-start-event-none',
	      translate('Create StartEvent')
	    ),
	    'create.intermediate-event': createAction(
	      'bpmn:IntermediateThrowEvent', 'event', 'bpmn-icon-intermediate-event-none',
	      translate('Create Intermediate/Boundary Event')
	    ),
	    'create.end-event': createAction(
	      'bpmn:EndEvent', 'event', 'bpmn-icon-end-event-none',
	      translate('Create EndEvent')
	    ),
	    'create.exclusive-gateway': createAction(
	      'bpmn:ExclusiveGateway', 'gateway', 'bpmn-icon-gateway-none',
	      translate('Create Gateway')
	    ),
	    'create.task': createAction(
	      'bpmn:Task', 'activity', 'bpmn-icon-task',
	      translate('Create Task')
	    ),
	    'create.data-object': createAction(
	      'bpmn:DataObjectReference', 'data-object', 'bpmn-icon-data-object',
	      translate('Create DataObjectReference')
	    ),
	    'create.data-store': createAction(
	      'bpmn:DataStoreReference', 'data-store', 'bpmn-icon-data-store',
	      translate('Create DataStoreReference')
	    ),
	    'create.subprocess-expanded': createAction(
	      'bpmn:SubProcess', 'activity', 'bpmn-icon-subprocess-expanded',
	      translate('Create expanded SubProcess'),
	      { isExpanded: true }
	    ),
	    'create.participant-expanded': {
	      group: 'collaboration',
	      className: 'bpmn-icon-participant',
	      title: translate('Create Pool/Participant'),
	      action: {
	        dragstart: createParticipant,
	        click: createParticipant
	      }
	    },
	    'create.group': createAction(
	      'bpmn:Group', 'artifact', 'bpmn-icon-group'
	    ),
	  });

	  return actions;
	};

	var PaletteModule$1 = {
	  __depends__: [
	    PaletteModule,
	    CreateModule,
	    SpaceToolModule,
	    LassoToolModule,
	    HandToolModule,
	    GlobalConnectModule,
	    translate$2
	  ],
	  __init__: [ 'paletteProvider' ],
	  paletteProvider: [ 'type', PaletteProvider ]
	};

	var LOW_PRIORITY$h = 250;


	function BpmnReplacePreview(
	    eventBus, elementRegistry, elementFactory,
	    canvas, previewSupport) {

	  CommandInterceptor.call(this, eventBus);

	  /**
	   * Replace the visuals of all elements in the context which can be replaced
	   *
	   * @param  {Object} context
	   */
	  function replaceVisual(context) {

	    var replacements = context.canExecute.replacements;

	    forEach(replacements, function(replacement) {

	      var id = replacement.oldElementId;

	      var newElement = {
	        type: replacement.newElementType
	      };

	      // if the visual of the element is already replaced
	      if (context.visualReplacements[id]) {
	        return;
	      }

	      var element = elementRegistry.get(id);

	      assign(newElement, { x: element.x, y: element.y });

	      // create a temporary shape
	      var tempShape = elementFactory.createShape(newElement);

	      canvas.addShape(tempShape, element.parent);

	      // select the original SVG element related to the element and hide it
	      var gfx = query('[data-element-id="' + css_escape(element.id) + '"]', context.dragGroup);

	      if (gfx) {
	        attr$1(gfx, { display: 'none' });
	      }

	      // clone the gfx of the temporary shape and add it to the drag group
	      var dragger = previewSupport.addDragger(tempShape, context.dragGroup);

	      context.visualReplacements[id] = dragger;

	      canvas.removeShape(tempShape);
	    });
	  }

	  /**
	   * Restore the original visuals of the previously replaced elements
	   *
	   * @param  {Object} context
	   */
	  function restoreVisual(context) {

	    var visualReplacements = context.visualReplacements;

	    forEach(visualReplacements, function(dragger, id) {

	      var originalGfx = query('[data-element-id="' + css_escape(id) + '"]', context.dragGroup);

	      if (originalGfx) {
	        attr$1(originalGfx, { display: 'inline' });
	      }

	      dragger.remove();

	      if (visualReplacements[id]) {
	        delete visualReplacements[id];
	      }
	    });
	  }

	  eventBus.on('shape.move.move', LOW_PRIORITY$h, function(event) {

	    var context = event.context,
	        canExecute = context.canExecute;

	    if (!context.visualReplacements) {
	      context.visualReplacements = {};
	    }

	    if (canExecute && canExecute.replacements) {
	      replaceVisual(context);
	    } else {
	      restoreVisual(context);
	    }
	  });
	}

	BpmnReplacePreview.$inject = [
	  'eventBus',
	  'elementRegistry',
	  'elementFactory',
	  'canvas',
	  'previewSupport'
	];

	inherits_browser(BpmnReplacePreview, CommandInterceptor);

	var ReplacePreviewModule = {
	  __depends__: [
	    PreviewSupportModule
	  ],
	  __init__: [ 'bpmnReplacePreview' ],
	  bpmnReplacePreview: [ 'type', BpmnReplacePreview ]
	};

	var HIGHER_PRIORITY$2 = 1250;

	var BOUNDARY_TO_HOST_THRESHOLD$1 = 40;

	var TARGET_BOUNDS_PADDING = 20;

	var TARGET_CENTER_PADDING = 20;

	var AXES = [ 'x', 'y' ];

	var abs$7 = Math.abs;

	/**
	 * Snap during connect.
	 *
	 * @param {EventBus} eventBus
	 * @param {Rules} rules
	 */
	function BpmnConnectSnapping(eventBus, rules) {
	  eventBus.on([
	    'connect.hover',
	    'connect.move',
	    'connect.end',
	  ], HIGHER_PRIORITY$2, function(event) {
	    var context = event.context,
	        source = context.source,
	        target = context.target;

	    if (event.originalEvent && isCmd(event.originalEvent)) {
	      return;
	    }

	    if (!context.initialSourcePosition) {
	      context.initialSourcePosition = context.sourcePosition;
	    }

	    var connectionAttrs = rules.allowed('connection.create', {
	      source: source,
	      target: target
	    });

	    if (target && connectionAttrs) {
	      snapInsideTarget(event, target, getTargetBoundsPadding(target));
	    }

	    if (target && isAnyType(connectionAttrs, [
	      'bpmn:Association',
	      'bpmn:DataInputAssociation',
	      'bpmn:DataOutputAssociation',
	      'bpmn:SequenceFlow'
	    ])) {

	      // snap source
	      context.sourcePosition = mid(source);

	      if (isAny(target, ['bpmn:Event', 'bpmn:Gateway'])) {
	        snapToPosition(event, mid(target));
	      }

	      if (is$1(target, 'bpmn:Task')) {
	        snapTargetMidOnCenter(event, target);
	      }

	      if (is$1(source, 'bpmn:BoundaryEvent') && target === source.host) {
	        snapBoundaryEventLoop(event, source, target);
	      }

	    } else if (isType$1(connectionAttrs, 'bpmn:MessageFlow')) {

	      if (is$1(source, 'bpmn:Event')) {

	        // snap source
	        context.sourcePosition = mid(source);
	      }

	      if (is$1(target, 'bpmn:Event')) {

	        // snap target
	        snapToPosition(event, mid(target));
	      }

	    } else {

	      // un-snap source
	      context.sourcePosition = context.initialSourcePosition;
	    }
	  });
	}

	BpmnConnectSnapping.$inject = [
	  'eventBus',
	  'rules'
	];

	function snapInsideTarget(event, target, padding) {

	  AXES.forEach(function(axis) {
	    var matchingTargetDimension = getDimensionForAxis(axis, target),
	        newCoordinate;

	    if (event[axis] < target[axis] + padding) {
	      newCoordinate = target[axis] + padding;
	    } else if (event[axis] > target[axis] + matchingTargetDimension - padding) {
	      newCoordinate = target[axis] + matchingTargetDimension - padding;
	    }

	    if (newCoordinate) {
	      setSnapped(event, axis, newCoordinate);
	    }
	  });
	}

	// snap to target mid if event position in center area
	function snapTargetMidOnCenter(event, target) {

	  var isCenter = every(AXES, function(axis) {
	    var coordinate = event[axis],
	        matchingTargetDimension = getDimensionForAxis(axis, target);

	    return coordinate > target[axis] + TARGET_CENTER_PADDING
	      && coordinate < target[axis] + matchingTargetDimension - TARGET_CENTER_PADDING;
	  });

	  if (isCenter) {
	    snapToPosition(event, mid(target));
	  }

	}

	// snap outside of Boundary Event surroundings
	function snapBoundaryEventLoop(event, source, target) {
	  var sourceMid = mid(source),
	      orientation = getOrientation(sourceMid, target, -10),
	      snappingAxes = [];

	  if (/top|bottom/.test(orientation)) {
	    snappingAxes.push('x');
	  }

	  if (/left|right/.test(orientation)) {
	    snappingAxes.push('y');
	  }

	  snappingAxes.forEach(function(axis) {
	    var coordinate = event[axis], newCoordinate;

	    if (abs$7(coordinate - sourceMid[axis]) < BOUNDARY_TO_HOST_THRESHOLD$1) {
	      if (coordinate > sourceMid[axis]) {
	        newCoordinate = sourceMid[axis] + BOUNDARY_TO_HOST_THRESHOLD$1;
	      }
	      else {
	        newCoordinate = sourceMid[axis] - BOUNDARY_TO_HOST_THRESHOLD$1;
	      }

	      setSnapped(event, axis, newCoordinate);
	    }
	  });
	}

	// helpers //////////

	function snapToPosition(event, position) {
	  setSnapped(event, 'x', position.x);
	  setSnapped(event, 'y', position.y);
	}

	function isType$1(attrs, type) {
	  return attrs && attrs.type === type;
	}

	function isAnyType(attrs, types) {
	  return some(types, function(type) {
	    return isType$1(attrs, type);
	  });
	}

	function getDimensionForAxis(axis, element) {
	  return axis === 'x' ? element.width : element.height;
	}

	function getTargetBoundsPadding(target) {
	  if (is$1(target, 'bpmn:Task')) {
	    return 10;
	  } else {
	    return TARGET_BOUNDS_PADDING;
	  }
	}

	/**
	 * A snap context, containing the (possibly incomplete)
	 * mappings of drop targets (to identify the snapping)
	 * to computed snap points.
	 */
	function SnapContext() {

	  /**
	   * Map<String, SnapPoints> mapping drop targets to
	   * a list of possible snappings.
	   *
	   * @type {Object}
	   */
	  this._targets = {};

	  /**
	   * Map<String, Point> initial positioning of element
	   * regarding various snap directions.
	   *
	   * @type {Object}
	   */
	  this._snapOrigins = {};

	  /**
	   * List of snap locations
	   *
	   * @type {Array<String>}
	   */
	  this._snapLocations = [];

	  /**
	   * Map<String, Array<Point>> of default snapping locations
	   *
	   * @type {Object}
	   */
	  this._defaultSnaps = {};
	}


	SnapContext.prototype.getSnapOrigin = function(snapLocation) {
	  return this._snapOrigins[snapLocation];
	};


	SnapContext.prototype.setSnapOrigin = function(snapLocation, initialValue) {
	  this._snapOrigins[snapLocation] = initialValue;

	  if (this._snapLocations.indexOf(snapLocation) === -1) {
	    this._snapLocations.push(snapLocation);
	  }
	};


	SnapContext.prototype.addDefaultSnap = function(type, point) {

	  var snapValues = this._defaultSnaps[type];

	  if (!snapValues) {
	    snapValues = this._defaultSnaps[type] = [];
	  }

	  snapValues.push(point);
	};

	/**
	 * Return a number of initialized snaps, i.e. snap locations such as
	 * top-left, mid, bottom-right and so forth.
	 *
	 * @return {Array<String>} snapLocations
	 */
	SnapContext.prototype.getSnapLocations = function() {
	  return this._snapLocations;
	};

	/**
	 * Set the snap locations for this context.
	 *
	 * The order of locations determines precedence.
	 *
	 * @param {Array<String>} snapLocations
	 */
	SnapContext.prototype.setSnapLocations = function(snapLocations) {
	  this._snapLocations = snapLocations;
	};

	/**
	 * Get snap points for a given target
	 *
	 * @param {Element|String} target
	 */
	SnapContext.prototype.pointsForTarget = function(target) {

	  var targetId = target.id || target;

	  var snapPoints = this._targets[targetId];

	  if (!snapPoints) {
	    snapPoints = this._targets[targetId] = new SnapPoints();
	    snapPoints.initDefaults(this._defaultSnaps);
	  }

	  return snapPoints;
	};


	/**
	 * Creates the snap points and initializes them with the
	 * given default values.
	 *
	 * @param {Object<String, Array<Point>>} [defaultPoints]
	 */
	function SnapPoints(defaultSnaps) {

	  /**
	   * Map<String, Map<(x|y), Array<Number>>> mapping snap locations,
	   * i.e. top-left, bottom-right, center to actual snap values.
	   *
	   * @type {Object}
	   */
	  this._snapValues = {};
	}

	SnapPoints.prototype.add = function(snapLocation, point) {

	  var snapValues = this._snapValues[snapLocation];

	  if (!snapValues) {
	    snapValues = this._snapValues[snapLocation] = { x: [], y: [] };
	  }

	  if (snapValues.x.indexOf(point.x) === -1) {
	    snapValues.x.push(point.x);
	  }

	  if (snapValues.y.indexOf(point.y) === -1) {
	    snapValues.y.push(point.y);
	  }
	};


	SnapPoints.prototype.snap = function(point, snapLocation, axis, tolerance) {
	  var snappingValues = this._snapValues[snapLocation];

	  return snappingValues && snapTo(point[axis], snappingValues[axis], tolerance);
	};

	/**
	 * Initialize a number of default snapping points.
	 *
	 * @param  {Object} defaultSnaps
	 */
	SnapPoints.prototype.initDefaults = function(defaultSnaps) {

	  var self = this;

	  forEach(defaultSnaps || {}, function(snapPoints, snapLocation) {
	    forEach(snapPoints, function(point) {
	      self.add(snapLocation, point);
	    });
	  });
	};

	var HIGHER_PRIORITY$3 = 1250;


	/**
	 * Snap during create and move.
	 *
	 * @param {EventBus} eventBus
	 * @param {Snapping} snapping
	 */
	function CreateMoveSnapping(eventBus, snapping) {
	  var self = this;

	  eventBus.on([
	    'create.start',
	    'shape.move.start'
	  ], function(event) {
	    self.initSnap(event);
	  });

	  eventBus.on([
	    'create.move',
	    'create.end',
	    'shape.move.move',
	    'shape.move.end'
	  ], HIGHER_PRIORITY$3, function(event) {
	    var context = event.context,
	        shape = context.shape,
	        snapContext = context.snapContext,
	        target = context.target;

	    if (event.originalEvent && isCmd(event.originalEvent)) {
	      return;
	    }

	    if (isSnapped(event) || !target) {
	      return;
	    }

	    var snapPoints = snapContext.pointsForTarget(target);

	    if (!snapPoints.initialized) {
	      snapPoints = self.addSnapTargetPoints(snapPoints, shape, target);

	      snapPoints.initialized = true;
	    }

	    snapping.snap(event, snapPoints);
	  });

	  eventBus.on([
	    'create.cleanup',
	    'shape.move.cleanup'
	  ], function() {
	    snapping.hide();
	  });
	}

	CreateMoveSnapping.$inject = [
	  'eventBus',
	  'snapping'
	];

	CreateMoveSnapping.prototype.initSnap = function(event) {
	  var context = event.context,
	      shape = context.shape,
	      snapContext = context.snapContext;

	  if (!snapContext) {
	    snapContext = context.snapContext = new SnapContext();
	  }

	  var shapeMid = mid(shape, event),
	      shapeTopLeft = {
	        x: shapeMid.x - shape.width / 2,
	        y: shapeMid.y - shape.height / 2
	      },
	      shapeBottomRight = {
	        x: shapeMid.x + shape.width / 2,
	        y: shapeMid.y + shape.height / 2
	      };

	  snapContext.setSnapOrigin('mid', {
	    x: shapeMid.x - event.x,
	    y: shapeMid.y - event.y
	  });

	  // snap labels to mid only
	  if (isLabel$1(shape)) {
	    return snapContext;
	  }

	  snapContext.setSnapOrigin('top-left', {
	    x: shapeTopLeft.x - event.x,
	    y: shapeTopLeft.y - event.y
	  });

	  snapContext.setSnapOrigin('bottom-right', {
	    x: shapeBottomRight.x - event.x,
	    y: shapeBottomRight.y - event.y
	  });

	  return snapContext;
	};

	CreateMoveSnapping.prototype.addSnapTargetPoints = function(snapPoints, shape, target) {
	  var snapTargets = this.getSnapTargets(shape, target);

	  forEach(snapTargets, function(snapTarget) {

	    // handle labels
	    if (isLabel$1(snapTarget)) {

	      if (isLabel$1(shape)) {
	        snapPoints.add('mid', mid(snapTarget));
	      }

	      return;
	    }

	    // handle connections
	    if (isConnection$5(snapTarget)) {

	      // ignore single segment connections
	      if (snapTarget.waypoints.length < 3) {
	        return;
	      }

	      // ignore first and last waypoint
	      var waypoints = snapTarget.waypoints.slice(1, -1);

	      forEach(waypoints, function(waypoint) {
	        snapPoints.add('mid', waypoint);
	      });

	      return;
	    }

	    // handle shapes
	    snapPoints.add('mid', mid(snapTarget));
	  });

	  if (!isNumber(shape.x) || !isNumber(shape.y)) {
	    return snapPoints;
	  }

	  snapPoints.add('mid', mid(shape));

	  return snapPoints;
	};

	CreateMoveSnapping.prototype.getSnapTargets = function(shape, target) {
	  return getChildren$1(target).filter(function(child) {
	    return !isHidden(child);
	  });
	};

	// helpers //////////

	function isConnection$5(element) {
	  return !!element.waypoints;
	}

	function isHidden(element) {
	  return !!element.hidden;
	}

	function isLabel$1(element) {
	  return !!element.labelTarget;
	}

	var HIGH_PRIORITY$f = 1500;


	/**
	 * Snap during create and move.
	 *
	 * @param {BpmnRules} bpmnRules
	 * @param {EventBus} eventBus
	 * @param {Injector} injector
	 */
	function BpmnCreateMoveSnapping(bpmnRules, eventBus, injector) {
	  injector.invoke(CreateMoveSnapping, this);

	  // creating first participant
	  eventBus.on([ 'create.move', 'create.end' ], HIGH_PRIORITY$f, setSnappedIfConstrained);

	  function canAttach(shape, target, position) {
	    return bpmnRules.canAttach([ shape ], target, null, position) === 'attach';
	  }

	  // snap boundary events
	  eventBus.on([
	    'create.move',
	    'create.end',
	    'shape.move.move',
	    'shape.move.end'
	  ], HIGH_PRIORITY$f, function(event) {
	    var context = event.context,
	        target = context.target,
	        shape = context.shape;

	    if (target && canAttach(shape, target, event) && !isSnapped(event)) {
	      snapBoundaryEvent(event, target);
	    }
	  });
	}

	inherits_browser(BpmnCreateMoveSnapping, CreateMoveSnapping);

	BpmnCreateMoveSnapping.$inject = [
	  'bpmnRules',
	  'eventBus',
	  'injector'
	];

	BpmnCreateMoveSnapping.prototype.initSnap = function(event) {
	  var snapContext = CreateMoveSnapping.prototype.initSnap.call(this, event);

	  var shape = event.shape;

	  // snap to docking points
	  forEach(shape.outgoing, function(connection) {
	    var docking = connection.waypoints[0];

	    docking = docking.original || docking;

	    snapContext.setSnapOrigin(connection.id + '-docking', {
	      x: docking.x - event.x,
	      y: docking.y - event.y
	    });
	  });

	  forEach(shape.incoming, function(connection) {
	    var docking = connection.waypoints[connection.waypoints.length - 1];

	    docking = docking.original || docking;

	    snapContext.setSnapOrigin(connection.id + '-docking', {
	      x: docking.x - event.x,
	      y: docking.y - event.y
	    });
	  });

	  if (is$1(shape, 'bpmn:Participant')) {

	    // snap to borders with higher priority
	    snapContext.setSnapLocations([ 'top-left', 'bottom-right', 'mid' ]);
	  }

	  return snapContext;
	};

	BpmnCreateMoveSnapping.prototype.addSnapTargetPoints = function(snapPoints, shape, target) {
	  CreateMoveSnapping.prototype.addSnapTargetPoints.call(this, snapPoints, shape, target);

	  var snapTargets = this.getSnapTargets(shape, target);

	  forEach(snapTargets, function(snapTarget) {

	    // handle TRBL alignment
	    //
	    // * with container elements
	    // * with text annotations
	    if (isContainer(snapTarget) || areAll([ shape, snapTarget ], 'bpmn:TextAnnotation')) {
	      snapPoints.add('top-left', topLeft(snapTarget));
	      snapPoints.add('bottom-right', bottomRight(snapTarget));
	    }
	  });

	  // snap to docking points
	  forEach(shape.incoming, function(connection) {

	    if (!includes(snapTargets, connection.source)) {
	      snapPoints.add('mid', getMid(connection.source));
	    }

	    var docking = connection.waypoints[0];

	    snapPoints.add(connection.id + '-docking', docking.original || docking);
	  });


	  forEach(shape.outgoing, function(connection) {

	    if (!includes(snapTargets, connection.target)) {
	      snapPoints.add('mid', getMid(connection.target));
	    }

	    var docking = connection.waypoints[ connection.waypoints.length - 1 ];

	    snapPoints.add(connection.id + '-docking', docking.original || docking);
	  });

	  // add sequence flow parents as snap targets
	  if (is$1(target, 'bpmn:SequenceFlow')) {
	    snapPoints = this.addSnapTargetPoints(snapPoints, shape, target.parent);
	  }

	  return snapPoints;
	};

	BpmnCreateMoveSnapping.prototype.getSnapTargets = function(shape, target) {
	  return CreateMoveSnapping.prototype.getSnapTargets.call(this, shape, target)
	    .filter(function(snapTarget) {

	      // do not snap to lanes
	      return !is$1(snapTarget, 'bpmn:Lane');
	    });
	};

	// helpers //////////

	function snapBoundaryEvent(event, target) {
	  var targetTRBL = asTRBL(target);

	  var direction = getBoundaryAttachment(event, target);

	  if (/top/.test(direction)) {
	    setSnapped(event, 'y', targetTRBL.top);
	  } else
	  if (/bottom/.test(direction)) {
	    setSnapped(event, 'y', targetTRBL.bottom);
	  }

	  if (/left/.test(direction)) {
	    setSnapped(event, 'x', targetTRBL.left);
	  } else
	  if (/right/.test(direction)) {
	    setSnapped(event, 'x', targetTRBL.right);
	  }
	}

	function areAll(elements, type) {
	  return elements.every(function(el) {
	    return is$1(el, type);
	  });
	}

	function isContainer(element) {
	  if (is$1(element, 'bpmn:SubProcess') && isExpanded(element)) {
	    return true;
	  }

	  return is$1(element, 'bpmn:Participant');
	}


	function setSnappedIfConstrained(event) {
	  var context = event.context,
	      createConstraints = context.createConstraints;

	  if (!createConstraints) {
	    return;
	  }

	  var top = createConstraints.top,
	      right = createConstraints.right,
	      bottom = createConstraints.bottom,
	      left = createConstraints.left;

	  if ((left && left >= event.x) || (right && right <= event.x)) {
	    setSnapped(event, 'x', event.x);
	  }

	  if ((top && top >= event.y) || (bottom && bottom <= event.y)) {
	    setSnapped(event, 'y', event.y);
	  }
	}

	function includes(array, value) {
	  return array.indexOf(value) !== -1;
	}

	var HIGHER_PRIORITY$4 = 1250;


	/**
	 * Snap during resize.
	 *
	 * @param {EventBus} eventBus
	 * @param {Snapping} snapping
	 */
	function ResizeSnapping(eventBus, snapping) {
	  var self = this;

	  eventBus.on([ 'resize.start' ], function(event) {
	    self.initSnap(event);
	  });

	  eventBus.on([
	    'resize.move',
	    'resize.end',
	  ], HIGHER_PRIORITY$4, function(event) {
	    var context = event.context,
	        shape = context.shape,
	        parent = shape.parent,
	        direction = context.direction,
	        snapContext = context.snapContext;

	    if (event.originalEvent && isCmd(event.originalEvent)) {
	      return;
	    }

	    if (isSnapped(event)) {
	      return;
	    }

	    var snapPoints = snapContext.pointsForTarget(parent);

	    if (!snapPoints.initialized) {
	      snapPoints = self.addSnapTargetPoints(snapPoints, shape, parent, direction);

	      snapPoints.initialized = true;
	    }

	    snapping.snap(event, snapPoints);
	  });

	  eventBus.on([ 'resize.cleanup' ], function() {
	    snapping.hide();
	  });
	}

	ResizeSnapping.prototype.initSnap = function(event) {
	  var context = event.context,
	      shape = context.shape,
	      direction = context.direction,
	      snapContext = context.snapContext;

	  if (!snapContext) {
	    snapContext = context.snapContext = new SnapContext();
	  }

	  var snapCorner = getCorner(shape, direction);

	  snapContext.setSnapOrigin('corner', {
	    x: snapCorner.x - event.x,
	    y: snapCorner.y - event.y
	  });

	  return snapContext;
	};

	ResizeSnapping.prototype.addSnapTargetPoints = function(snapPoints, shape, target, direction) {
	  var snapTargets = this.getSnapTargets(shape, target);

	  forEach(snapTargets, function(snapTarget) {
	    snapPoints.add('corner', bottomRight(snapTarget));
	    snapPoints.add('corner', topLeft(snapTarget));
	  });

	  snapPoints.add('corner', getCorner(shape, direction));

	  return snapPoints;
	};

	ResizeSnapping.$inject = [
	  'eventBus',
	  'snapping'
	];

	ResizeSnapping.prototype.getSnapTargets = function(shape, target) {
	  return getChildren$1(target).filter(function(child) {
	    return !isAttached(child, shape)
	      && !isConnection$6(child)
	      && !isHidden$1(child)
	      && !isLabel$2(child);
	  });
	};

	// helpers //////////

	function getCorner(shape, direction) {
	  if (direction === 'nw') {
	    return topLeft(shape);
	  } else if (direction === 'ne') {
	    return topRight(shape);
	  } else if (direction === 'sw') {
	    return bottomLeft(shape);
	  } else {
	    return bottomRight(shape);
	  }
	}

	function isAttached(element, host) {
	  return element.host === host;
	}

	function isConnection$6(element) {
	  return !!element.waypoints;
	}

	function isHidden$1(element) {
	  return !!element.hidden;
	}

	function isLabel$2(element) {
	  return !!element.labelTarget;
	}

	var SNAP_TOLERANCE = 7;

	var SNAP_LINE_HIDE_DELAY = 1000;


	/**
	 * Generic snapping feature.
	 *
	 * @param {EventBus} eventBus
	 * @param {Canvas} canvas
	 */
	function Snapping(canvas) {
	  this._canvas = canvas;

	  // delay hide by 1000 seconds since last snap
	  this._asyncHide = debounce(bind(this.hide, this), SNAP_LINE_HIDE_DELAY);
	}

	Snapping.$inject = [ 'canvas' ];

	/**
	 * Snap an event to given snap points.
	 *
	 * @param {Event} event
	 * @param {SnapPoints} snapPoints
	 */
	Snapping.prototype.snap = function(event, snapPoints) {
	  var context = event.context,
	      snapContext = context.snapContext,
	      snapLocations = snapContext.getSnapLocations();

	  var snapping = {
	    x: isSnapped(event, 'x'),
	    y: isSnapped(event, 'y')
	  };

	  forEach(snapLocations, function(location) {
	    var snapOrigin = snapContext.getSnapOrigin(location);

	    var snapCurrent = {
	      x: event.x + snapOrigin.x,
	      y: event.y + snapOrigin.y
	    };

	    // snap both axis if not snapped already
	    forEach([ 'x', 'y' ], function(axis) {
	      var locationSnapping;

	      if (!snapping[axis]) {
	        locationSnapping = snapPoints.snap(snapCurrent, location, axis, SNAP_TOLERANCE);

	        if (locationSnapping !== undefined) {
	          snapping[axis] = {
	            value: locationSnapping,
	            originValue: locationSnapping - snapOrigin[axis]
	          };
	        }
	      }
	    });

	    // no need to continue snapping
	    if (snapping.x && snapping.y) {
	      return false;
	    }
	  });

	  // show snap lines
	  this.showSnapLine('vertical', snapping.x && snapping.x.value);
	  this.showSnapLine('horizontal', snapping.y && snapping.y.value);

	  // snap event
	  forEach([ 'x', 'y' ], function(axis) {
	    var axisSnapping = snapping[axis];

	    if (isObject(axisSnapping)) {
	      setSnapped(event, axis, axisSnapping.originValue);
	    }
	  });
	};

	Snapping.prototype._createLine = function(orientation) {
	  var root = this._canvas.getLayer('snap');

	  var line = create('path');

	  attr$1(line, { d: 'M0,0 L0,0' });

	  classes$1(line).add('djs-snap-line');

	  append(root, line);

	  return {
	    update: function(position) {

	      if (!isNumber(position)) {
	        attr$1(line, { display: 'none' });
	      } else {
	        if (orientation === 'horizontal') {
	          attr$1(line, {
	            d: 'M-100000,' + position + ' L+100000,' + position,
	            display: ''
	          });
	        } else {
	          attr$1(line, {
	            d: 'M ' + position + ',-100000 L ' + position + ', +100000',
	            display: ''
	          });
	        }
	      }
	    }
	  };
	};

	Snapping.prototype._createSnapLines = function() {
	  this._snapLines = {
	    horizontal: this._createLine('horizontal'),
	    vertical: this._createLine('vertical')
	  };
	};

	Snapping.prototype.showSnapLine = function(orientation, position) {

	  var line = this.getSnapLine(orientation);

	  if (line) {
	    line.update(position);
	  }

	  this._asyncHide();
	};

	Snapping.prototype.getSnapLine = function(orientation) {
	  if (!this._snapLines) {
	    this._createSnapLines();
	  }

	  return this._snapLines[orientation];
	};

	Snapping.prototype.hide = function() {
	  forEach(this._snapLines, function(snapLine) {
	    snapLine.update();
	  });
	};

	var SnappingModule = {
	  __init__: [
	    'createMoveSnapping',
	    'resizeSnapping',
	    'snapping'
	  ],
	  createMoveSnapping: [ 'type', CreateMoveSnapping ],
	  resizeSnapping: [ 'type', ResizeSnapping ],
	  snapping: [ 'type', Snapping ]
	};

	var SnappingModule$1 = {
	  __depends__: [ SnappingModule ],
	  __init__: [
	    'connectSnapping',
	    'createMoveSnapping'
	  ],
	  connectSnapping: [ 'type', BpmnConnectSnapping ],
	  createMoveSnapping: [ 'type', BpmnCreateMoveSnapping ]
	};

	/**
	 * Provides searching infrastructure
	 */
	function SearchPad(canvas, eventBus, overlays, selection) {
	  this._open = false;
	  this._results = [];
	  this._eventMaps = [];

	  this._canvas = canvas;
	  this._eventBus = eventBus;
	  this._overlays = overlays;
	  this._selection = selection;

	  // setup elements
	  this._container = domify(SearchPad.BOX_HTML);
	  this._searchInput = query(SearchPad.INPUT_SELECTOR, this._container);
	  this._resultsContainer = query(SearchPad.RESULTS_CONTAINER_SELECTOR, this._container);

	  // attach search pad
	  this._canvas.getContainer().appendChild(this._container);

	  // cleanup on destroy
	  eventBus.on([ 'canvas.destroy', 'diagram.destroy' ], this.close, this);
	}


	SearchPad.$inject = [
	  'canvas',
	  'eventBus',
	  'overlays',
	  'selection'
	];


	/**
	 * Binds and keeps track of all event listereners
	 */
	SearchPad.prototype._bindEvents = function() {
	  var self = this;

	  function listen(el, selector, type, fn) {
	    self._eventMaps.push({
	      el: el,
	      type: type,
	      listener: delegateEvents.bind(el, selector, type, fn)
	    });
	  }

	  // close search on clicking anywhere outside
	  listen(document, 'html', 'click', function(e) {
	    self.close();
	  });

	  // stop event from propagating and closing search
	  // focus on input
	  listen(this._container, SearchPad.INPUT_SELECTOR, 'click', function(e) {
	    e.stopPropagation();
	    e.delegateTarget.focus();
	  });

	  // preselect result on hover
	  listen(this._container, SearchPad.RESULT_SELECTOR, 'mouseover', function(e) {
	    e.stopPropagation();
	    self._scrollToNode(e.delegateTarget);
	    self._preselect(e.delegateTarget);
	  });

	  // selects desired result on mouse click
	  listen(this._container, SearchPad.RESULT_SELECTOR, 'click', function(e) {
	    e.stopPropagation();
	    self._select(e.delegateTarget);
	  });

	  // prevent cursor in input from going left and right when using up/down to
	  // navigate results
	  listen(this._container, SearchPad.INPUT_SELECTOR, 'keydown', function(e) {
	    // up
	    if (e.keyCode === 38) {
	      e.preventDefault();
	    }

	    // down
	    if (e.keyCode === 40) {
	      e.preventDefault();
	    }
	  });

	  // handle keyboard input
	  listen(this._container, SearchPad.INPUT_SELECTOR, 'keyup', function(e) {
	    // escape
	    if (e.keyCode === 27) {
	      return self.close();
	    }

	    // enter
	    if (e.keyCode === 13) {
	      var selected = self._getCurrentResult();

	      return selected ? self._select(selected) : self.close();
	    }

	    // up
	    if (e.keyCode === 38) {
	      return self._scrollToDirection(true);
	    }

	    // down
	    if (e.keyCode === 40) {
	      return self._scrollToDirection();
	    }

	    // left && right
	    // do not search while navigating text input
	    if (e.keyCode === 37 || e.keyCode === 39) {
	      return;
	    }

	    // anything else
	    self._search(e.delegateTarget.value);
	  });
	};


	/**
	 * Unbinds all previously established listeners
	 */
	SearchPad.prototype._unbindEvents = function() {
	  this._eventMaps.forEach(function(m) {
	    delegateEvents.unbind(m.el, m.type, m.listener);
	  });
	};


	/**
	 * Performs a search for the given pattern.
	 *
	 * @param  {String} pattern
	 */
	SearchPad.prototype._search = function(pattern) {
	  var self = this;

	  this._clearResults();

	  // do not search on empty query
	  if (!pattern || pattern === '') {
	    return;
	  }

	  var searchResults = this._searchProvider.find(pattern);

	  if (!searchResults.length) {
	    return;
	  }

	  // append new results
	  searchResults.forEach(function(result) {
	    var id = result.element.id;
	    var node = self._createResultNode(result, id);
	    self._results[id] = {
	      element: result.element,
	      node: node
	    };
	  });

	  // preselect first result
	  var node = query(SearchPad.RESULT_SELECTOR, this._resultsContainer);
	  this._scrollToNode(node);
	  this._preselect(node);
	};


	/**
	 * Navigate to the previous/next result. Defaults to next result.
	 * @param  {Boolean} previous
	 */
	SearchPad.prototype._scrollToDirection = function(previous) {
	  var selected = this._getCurrentResult();
	  if (!selected) {
	    return;
	  }

	  var node = previous ? selected.previousElementSibling : selected.nextElementSibling;
	  if (node) {
	    this._scrollToNode(node);
	    this._preselect(node);
	  }
	};


	/**
	 * Scroll to the node if it is not visible.
	 *
	 * @param  {Element} node
	 */
	SearchPad.prototype._scrollToNode = function(node) {
	  if (!node || node === this._getCurrentResult()) {
	    return;
	  }

	  var nodeOffset = node.offsetTop;
	  var containerScroll = this._resultsContainer.scrollTop;

	  var bottomScroll = nodeOffset - this._resultsContainer.clientHeight + node.clientHeight;

	  if (nodeOffset < containerScroll) {
	    this._resultsContainer.scrollTop = nodeOffset;
	  } else if (containerScroll < bottomScroll) {
	    this._resultsContainer.scrollTop = bottomScroll;
	  }
	};


	/**
	 * Clears all results data.
	 */
	SearchPad.prototype._clearResults = function() {
	  clear(this._resultsContainer);

	  this._results = [];

	  this._resetOverlay();

	  this._eventBus.fire('searchPad.cleared');
	};


	/**
	 * Get currently selected result.
	 *
	 * @return {Element}
	 */
	SearchPad.prototype._getCurrentResult = function() {
	  return query(SearchPad.RESULT_SELECTED_SELECTOR, this._resultsContainer);
	};


	/**
	 * Create result DOM element within results container
	 * that corresponds to a search result.
	 *
	 * 'result' : one of the elements returned by SearchProvider
	 * 'id' : id attribute value to assign to the new DOM node
	 * return : created DOM element
	 *
	 * @param  {SearchResult} result
	 * @param  {String} id
	 * @return {Element}
	 */
	SearchPad.prototype._createResultNode = function(result, id) {
	  var node = domify(SearchPad.RESULT_HTML);

	  // create only if available
	  if (result.primaryTokens.length > 0) {
	    createInnerTextNode(node, result.primaryTokens, SearchPad.RESULT_PRIMARY_HTML);
	  }

	  // secondary tokens (represent element ID) are allways available
	  createInnerTextNode(node, result.secondaryTokens, SearchPad.RESULT_SECONDARY_HTML);

	  attr(node, SearchPad.RESULT_ID_ATTRIBUTE, id);

	  this._resultsContainer.appendChild(node);

	  return node;
	};


	/**
	 * Register search element provider.
	 *
	 * SearchProvider.find - provides search function over own elements
	 *  (pattern) => [{ text: <String>, element: <Element>}, ...]
	 *
	 * @param  {SearchProvider} provider
	 */
	SearchPad.prototype.registerProvider = function(provider) {
	  this._searchProvider = provider;
	};


	/**
	 * Open search pad.
	 */
	SearchPad.prototype.open = function() {
	  if (!this._searchProvider) {
	    throw new Error('no search provider registered');
	  }

	  if (this.isOpen()) {
	    return;
	  }

	  this._bindEvents();

	  this._open = true;

	  classes(this._container).add('open');

	  this._searchInput.focus();

	  this._eventBus.fire('searchPad.opened');
	};


	/**
	 * Close search pad.
	 */
	SearchPad.prototype.close = function() {
	  if (!this.isOpen()) {
	    return;
	  }

	  this._unbindEvents();

	  this._open = false;

	  classes(this._container).remove('open');

	  this._clearResults();

	  this._searchInput.value = '';
	  this._searchInput.blur();

	  this._resetOverlay();

	  this._eventBus.fire('searchPad.closed');
	};


	/**
	 * Toggles search pad on/off.
	 */
	SearchPad.prototype.toggle = function() {
	  this.isOpen() ? this.close() : this.open();
	};


	/**
	 * Report state of search pad.
	 */
	SearchPad.prototype.isOpen = function() {
	  return this._open;
	};


	/**
	 * Preselect result entry.
	 *
	 * @param  {Element} element
	 */
	SearchPad.prototype._preselect = function(node) {
	  var selectedNode = this._getCurrentResult();

	  // already selected
	  if (node === selectedNode) {
	    return;
	  }

	  // removing preselection from current node
	  if (selectedNode) {
	    classes(selectedNode).remove(SearchPad.RESULT_SELECTED_CLASS);
	  }

	  var id = attr(node, SearchPad.RESULT_ID_ATTRIBUTE);
	  var element = this._results[id].element;

	  classes(node).add(SearchPad.RESULT_SELECTED_CLASS);

	  this._resetOverlay(element);

	  this._centerViewbox(element);

	  this._selection.select(element);

	  this._eventBus.fire('searchPad.preselected', element);
	};


	/**
	 * Select result node.
	 *
	 * @param  {Element} element
	 */
	SearchPad.prototype._select = function(node) {
	  var id = attr(node, SearchPad.RESULT_ID_ATTRIBUTE);
	  var element = this._results[id].element;

	  this.close();

	  this._resetOverlay();

	  this._centerViewbox(element);

	  this._selection.select(element);

	  this._eventBus.fire('searchPad.selected', element);
	};


	/**
	 * Center viewbox on the element middle point.
	 *
	 * @param  {Element} element
	 */
	SearchPad.prototype._centerViewbox = function(element) {
	  var viewbox = this._canvas.viewbox();

	  var box = getBBox(element);

	  var newViewbox = {
	    x: (box.x + box.width/2) - viewbox.outer.width/2,
	    y: (box.y + box.height/2) - viewbox.outer.height/2,
	    width: viewbox.outer.width,
	    height: viewbox.outer.height
	  };

	  this._canvas.viewbox(newViewbox);

	  this._canvas.zoom(viewbox.scale);
	};


	/**
	 * Reset overlay removes and, optionally, set
	 * overlay to a new element.
	 *
	 * @param  {Element} element
	 */
	SearchPad.prototype._resetOverlay = function(element) {
	  if (this._overlayId) {
	    this._overlays.remove(this._overlayId);
	  }

	  if (element) {
	    var box = getBBox(element);
	    var overlay = constructOverlay(box);
	    this._overlayId = this._overlays.add(element, overlay);
	  }
	};


	/**
	 * Construct overlay object for the given bounding box.
	 *
	 * @param  {BoundingBox} box
	 * @return {Object}
	 */
	function constructOverlay(box) {

	  var offset = 6;
	  var w = box.width + offset * 2;
	  var h = box.height + offset * 2;

	  var styles = [
	    'width: '+ w +'px',
	    'height: '+ h + 'px'
	  ].join('; ');

	  return {
	    position: {
	      bottom: h - offset,
	      right: w - offset
	    },
	    show: true,
	    html: '<div style="' + styles + '" class="' + SearchPad.OVERLAY_CLASS + '"></div>'
	  };
	}


	/**
	 * Creates and appends child node from result tokens and HTML template.
	 *
	 * @param  {Element} node
	 * @param  {Array<Object>} tokens
	 * @param  {String} template
	 */
	function createInnerTextNode(parentNode, tokens, template) {
	  var text = createHtmlText(tokens);
	  var childNode = domify(template);
	  childNode.innerHTML = text;
	  parentNode.appendChild(childNode);
	}

	/**
	 * Create internal HTML markup from result tokens.
	 * Caters for highlighting pattern matched tokens.
	 *
	 * @param  {Array<Object>} tokens
	 * @return {String}
	 */
	function createHtmlText(tokens) {
	  var htmlText = '';

	  tokens.forEach(function(t) {
	    if (t.matched) {
	      htmlText += '<strong class="' + SearchPad.RESULT_HIGHLIGHT_CLASS + '">' + escapeHTML(t.matched) + '</strong>';
	    } else {
	      htmlText += escapeHTML(t.normal);
	    }
	  });

	  return htmlText !== '' ? htmlText : null;
	}


	/**
	 * CONSTANTS
	 */
	SearchPad.CONTAINER_SELECTOR = '.djs-search-container';
	SearchPad.INPUT_SELECTOR = '.djs-search-input input';
	SearchPad.RESULTS_CONTAINER_SELECTOR = '.djs-search-results';
	SearchPad.RESULT_SELECTOR = '.djs-search-result';
	SearchPad.RESULT_SELECTED_CLASS = 'djs-search-result-selected';
	SearchPad.RESULT_SELECTED_SELECTOR = '.' + SearchPad.RESULT_SELECTED_CLASS;
	SearchPad.RESULT_ID_ATTRIBUTE = 'data-result-id';
	SearchPad.RESULT_HIGHLIGHT_CLASS = 'djs-search-highlight';
	SearchPad.OVERLAY_CLASS = 'djs-search-overlay';

	SearchPad.BOX_HTML =
	  '<div class="djs-search-container djs-draggable djs-scrollable">' +
	    '<div class="djs-search-input">' +
	      '<input type="text"/>' +
	    '</div>' +
	    '<div class="djs-search-results"></div>' +
	  '</div>';

	SearchPad.RESULT_HTML =
	  '<div class="djs-search-result"></div>';

	SearchPad.RESULT_PRIMARY_HTML =
	  '<div class="djs-search-result-primary"></div>';

	SearchPad.RESULT_SECONDARY_HTML =
	  '<p class="djs-search-result-secondary"></p>';

	var SearchPadModule = {
	  __depends__: [
	    OverlaysModule,
	    SelectionModule
	  ],
	  searchPad: [ 'type', SearchPad ]
	};

	/**
	 * Provides ability to search through BPMN elements
	 */
	function BpmnSearchProvider(elementRegistry, searchPad, canvas) {

	  this._elementRegistry = elementRegistry;
	  this._canvas = canvas;

	  searchPad.registerProvider(this);
	}

	BpmnSearchProvider.$inject = [
	  'elementRegistry',
	  'searchPad',
	  'canvas'
	];


	/**
	 * Finds all elements that match given pattern
	 *
	 * <Result> :
	 *  {
	 *    primaryTokens: <Array<Token>>,
	 *    secondaryTokens: <Array<Token>>,
	 *    element: <Element>
	 *  }
	 *
	 * <Token> :
	 *  {
	 *    normal|matched: <String>
	 *  }
	 *
	 * @param  {String} pattern
	 * @return {Array<Result>}
	 */
	BpmnSearchProvider.prototype.find = function(pattern) {
	  var rootElement = this._canvas.getRootElement();

	  var elements = this._elementRegistry.filter(function(element) {
	    if (element.labelTarget) {
	      return false;
	    }
	    return true;
	  });

	  // do not include root element
	  elements = filter(elements, function(element) {
	    return element !== rootElement;
	  });

	  elements = map(elements, function(element) {
	    return {
	      primaryTokens: matchAndSplit(getLabel(element), pattern),
	      secondaryTokens: matchAndSplit(element.id, pattern),
	      element: element
	    };
	  });

	  // exclude non-matched elements
	  elements = filter(elements, function(element) {
	    return hasMatched(element.primaryTokens) || hasMatched(element.secondaryTokens);
	  });

	  elements = sortBy(elements, function(element) {
	    return getLabel(element.element) + element.element.id;
	  });

	  return elements;
	};


	function hasMatched(tokens) {
	  var matched = filter(tokens, function(t) {
	    return !!t.matched;
	  });

	  return matched.length > 0;
	}


	function matchAndSplit(text, pattern) {
	  var tokens = [],
	      originalText = text;

	  if (!text) {
	    return tokens;
	  }

	  text = text.toLowerCase();
	  pattern = pattern.toLowerCase();

	  var i = text.indexOf(pattern);

	  if (i > -1) {
	    if (i !== 0) {
	      tokens.push({
	        normal: originalText.substr(0, i)
	      });
	    }

	    tokens.push({
	      matched: originalText.substr(i, pattern.length)
	    });

	    if (pattern.length + i < text.length) {
	      tokens.push({
	        normal: originalText.substr(pattern.length + i, text.length)
	      });
	    }
	  } else {
	    tokens.push({
	      normal: originalText
	    });
	  }

	  return tokens;
	}

	var SearchModule = {
	  __depends__: [
	    SearchPadModule
	  ],
	  __init__: [ 'bpmnSearch'],
	  bpmnSearch: [ 'type', BpmnSearchProvider ]
	};

	var initialDiagram =
	  '<?xml version="1.0" encoding="UTF-8"?>' +
	  '<bpmn:definitions xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" ' +
	                    'xmlns:bpmn="http://www.omg.org/spec/BPMN/20100524/MODEL" ' +
	                    'xmlns:bpmndi="http://www.omg.org/spec/BPMN/20100524/DI" ' +
	                    'xmlns:dc="http://www.omg.org/spec/DD/20100524/DC" ' +
	                    'targetNamespace="http://bpmn.io/schema/bpmn" ' +
	                    'id="Definitions_1">' +
	    '<bpmn:process id="Process_1" isExecutable="false">' +
	      '<bpmn:startEvent id="StartEvent_1"/>' +
	    '</bpmn:process>' +
	    '<bpmndi:BPMNDiagram id="BPMNDiagram_1">' +
	      '<bpmndi:BPMNPlane id="BPMNPlane_1" bpmnElement="Process_1">' +
	        '<bpmndi:BPMNShape id="_BPMNShape_StartEvent_2" bpmnElement="StartEvent_1">' +
	          '<dc:Bounds height="36.0" width="36.0" x="173.0" y="102.0"/>' +
	        '</bpmndi:BPMNShape>' +
	      '</bpmndi:BPMNPlane>' +
	    '</bpmndi:BPMNDiagram>' +
	  '</bpmn:definitions>';


	/**
	 * A modeler for BPMN 2.0 diagrams.
	 *
	 *
	 * ## Extending the Modeler
	 *
	 * 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 bpmnModeler = new Modeler({ additionalModules: [ extensionModule ] });
	 * bpmnModeler.importXML(...);
	 * ```
	 *
	 *
	 * ## Customizing / Replacing Components
	 *
	 * You can replace individual diagram components by redefining them in override modules.
	 * This works for all components, including those defined in the core.
	 *
	 * Pass in override modules via the `options.additionalModules` flag like this:
	 *
	 * ```javascript
	 * function CustomContextPadProvider(contextPad) {
	 *
	 *   contextPad.registerProvider(this);
	 *
	 *   this.getContextPadEntries = function(element) {
	 *     // no entries, effectively disable the context pad
	 *     return {};
	 *   };
	 * }
	 *
	 * CustomContextPadProvider.$inject = [ 'contextPad' ];
	 *
	 * var overrideModule = {
	 *   contextPadProvider: [ 'type', CustomContextPadProvider ]
	 * };
	 *
	 * var bpmnModeler = new Modeler({ additionalModules: [ overrideModule ]});
	 * ```
	 *
	 * @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<didi.Module>} [options.modules] a list of modules to override the default modules
	 * @param {Array<didi.Module>} [options.additionalModules] a list of modules to use with the default modules
	 */
	function Modeler(options) {
	  Viewer.call(this, options);

	  // hook ID collection into the modeler
	  this.on('import.parse.complete', function(event) {
	    if (!event.error) {
	      this._collectIds(event.definitions, event.context);
	    }
	  }, this);

	  this.on('diagram.destroy', function() {
	    this.get('moddle').ids.clear();
	  }, this);
	}

	inherits_browser(Modeler, Viewer);

	Modeler.Viewer = Viewer;
	Modeler.NavigatedViewer = NavigatedViewer;

	/**
	 * Create a new diagram to start modeling.
	 *
	 * @param {Function} [done]
	 */
	Modeler.prototype.createDiagram = function(done) {
	  return this.importXML(initialDiagram, done);
	};

	/**
	 * Create a moddle instance, attaching ids to it.
	 *
	 * @param {Object} options
	 */
	Modeler.prototype._createModdle = function(options) {
	  var moddle = Viewer.prototype._createModdle.call(this, options);

	  // attach ids to moddle to be able to track
	  // and validated ids in the BPMN 2.0 XML document
	  // tree
	  moddle.ids = new Ids([ 32, 36, 1 ]);

	  return moddle;
	};

	/**
	 * Collect ids processed during parsing of the
	 * definitions object.
	 *
	 * @param {ModdleElement} definitions
	 * @param {Context} context
	 */
	Modeler.prototype._collectIds = function(definitions, context) {

	  var moddle = definitions.$model,
	      ids = moddle.ids,
	      id;

	  // remove references from previous import
	  ids.clear();

	  for (id in context.elementsById) {
	    ids.claim(id, context.elementsById[id]);
	  }
	};

	Modeler.prototype._interactionModules = [
	  // non-modeling components
	  KeyboardMoveModule,
	  MoveCanvasModule,
	  TouchModule$1,
	  ZoomScrollModule
	];

	Modeler.prototype._modelingModules = [
	  // modeling components
	  AlignElementsModule,
	  AutoPlaceModule,
	  AutoScrollModule,
	  AutoResizeModule,
	  BendpointsModule,
	  ConnectModule,
	  ConnectionPreviewModule,
	  ContextPadModule$1,
	  CopyPasteModule$1,
	  CreateModule,
	  DistributeElementsModule$1,
	  EditorActionsModule$1,
	  GridSnappingModule$1,
	  KeyboardModule$1,
	  KeyboardMoveSelectionModule,
	  LabelEditingModule,
	  ModelingModule,
	  MoveModule,
	  PaletteModule$1,
	  ReplacePreviewModule,
	  ResizeModule,
	  SnappingModule$1,
	  SearchModule
	];


	// modules the modeler is composed of
	//
	// - viewer modules
	// - interaction modules
	// - modeling modules

	Modeler.prototype._modules = [].concat(
	  Modeler.prototype._modules,
	  Modeler.prototype._interactionModules,
	  Modeler.prototype._modelingModules);

	return Modeler;

}));
