UNPKG

1.38 MBJavaScriptView Raw
1/*!
2 * bpmn-js - bpmn-modeler v8.8.2
3 *
4 * Copyright (c) 2014-present, camunda Services GmbH
5 *
6 * Released under the bpmn.io license
7 * http://bpmn.io/license
8 *
9 * Source Code: https://github.com/bpmn-io/bpmn-js
10 *
11 * Date: 2021-10-20
12 */
13(function (global, factory) {
14 typeof exports === 'object' && typeof module !== 'undefined' ? module.exports = factory() :
15 typeof define === 'function' && define.amd ? define(factory) :
16 (global = typeof globalThis !== 'undefined' ? globalThis : global || self, global.BpmnJS = factory());
17}(this, (function () { 'use strict';
18
19 var commonjsGlobal = typeof globalThis !== 'undefined' ? globalThis : typeof window !== 'undefined' ? window : typeof global !== 'undefined' ? global : typeof self !== 'undefined' ? self : {};
20
21 var inherits_browser = {exports: {}};
22
23 if (typeof Object.create === 'function') {
24 // implementation from standard node.js 'util' module
25 inherits_browser.exports = function inherits(ctor, superCtor) {
26 if (superCtor) {
27 ctor.super_ = superCtor;
28 ctor.prototype = Object.create(superCtor.prototype, {
29 constructor: {
30 value: ctor,
31 enumerable: false,
32 writable: true,
33 configurable: true
34 }
35 });
36 }
37 };
38 } else {
39 // old school shim for old browsers
40 inherits_browser.exports = function inherits(ctor, superCtor) {
41 if (superCtor) {
42 ctor.super_ = superCtor;
43 var TempCtor = function () {};
44 TempCtor.prototype = superCtor.prototype;
45 ctor.prototype = new TempCtor();
46 ctor.prototype.constructor = ctor;
47 }
48 };
49 }
50
51 var inherits$1 = inherits_browser.exports;
52
53 function createCommonjsModule(fn, module) {
54 return module = { exports: {} }, fn(module, module.exports), module.exports;
55 }
56
57 var hat_1 = createCommonjsModule(function (module) {
58 var hat = module.exports = function (bits, base) {
59 if (!base) base = 16;
60 if (bits === undefined) bits = 128;
61 if (bits <= 0) return '0';
62
63 var digits = Math.log(Math.pow(2, bits)) / Math.log(base);
64 for (var i = 2; digits === Infinity; i *= 2) {
65 digits = Math.log(Math.pow(2, bits / i)) / Math.log(base) * i;
66 }
67
68 var rem = digits - Math.floor(digits);
69
70 var res = '';
71
72 for (var i = 0; i < Math.floor(digits); i++) {
73 var x = Math.floor(Math.random() * base).toString(base);
74 res = x + res;
75 }
76
77 if (rem) {
78 var b = Math.pow(base, rem);
79 var x = Math.floor(Math.random() * b).toString(base);
80 res = x + res;
81 }
82
83 var parsed = parseInt(res, base);
84 if (parsed !== Infinity && parsed >= Math.pow(2, bits)) {
85 return hat(bits, base)
86 }
87 else return res;
88 };
89
90 hat.rack = function (bits, base, expandBy) {
91 var fn = function (data) {
92 var iters = 0;
93 do {
94 if (iters ++ > 10) {
95 if (expandBy) bits += expandBy;
96 else throw new Error('too many ID collisions, use more bits')
97 }
98
99 var id = hat(bits, base);
100 } while (Object.hasOwnProperty.call(hats, id));
101
102 hats[id] = data;
103 return id;
104 };
105 var hats = fn.hats = {};
106
107 fn.get = function (id) {
108 return fn.hats[id];
109 };
110
111 fn.set = function (id, value) {
112 fn.hats[id] = value;
113 return fn;
114 };
115
116 fn.bits = bits || 128;
117 fn.base = base || 16;
118 return fn;
119 };
120 });
121
122 /**
123 * Create a new id generator / cache instance.
124 *
125 * You may optionally provide a seed that is used internally.
126 *
127 * @param {Seed} seed
128 */
129
130 function Ids(seed) {
131 if (!(this instanceof Ids)) {
132 return new Ids(seed);
133 }
134
135 seed = seed || [128, 36, 1];
136 this._seed = seed.length ? hat_1.rack(seed[0], seed[1], seed[2]) : seed;
137 }
138 /**
139 * Generate a next id.
140 *
141 * @param {Object} [element] element to bind the id to
142 *
143 * @return {String} id
144 */
145
146 Ids.prototype.next = function (element) {
147 return this._seed(element || true);
148 };
149 /**
150 * Generate a next id with a given prefix.
151 *
152 * @param {Object} [element] element to bind the id to
153 *
154 * @return {String} id
155 */
156
157
158 Ids.prototype.nextPrefixed = function (prefix, element) {
159 var id;
160
161 do {
162 id = prefix + this.next(true);
163 } while (this.assigned(id)); // claim {prefix}{random}
164
165
166 this.claim(id, element); // return
167
168 return id;
169 };
170 /**
171 * Manually claim an existing id.
172 *
173 * @param {String} id
174 * @param {String} [element] element the id is claimed by
175 */
176
177
178 Ids.prototype.claim = function (id, element) {
179 this._seed.set(id, element || true);
180 };
181 /**
182 * Returns true if the given id has already been assigned.
183 *
184 * @param {String} id
185 * @return {Boolean}
186 */
187
188
189 Ids.prototype.assigned = function (id) {
190 return this._seed.get(id) || false;
191 };
192 /**
193 * Unclaim an id.
194 *
195 * @param {String} id the id to unclaim
196 */
197
198
199 Ids.prototype.unclaim = function (id) {
200 delete this._seed.hats[id];
201 };
202 /**
203 * Clear all claimed ids.
204 */
205
206
207 Ids.prototype.clear = function () {
208 var hats = this._seed.hats,
209 id;
210
211 for (id in hats) {
212 this.unclaim(id);
213 }
214 };
215
216 /**
217 * Flatten array, one level deep.
218 *
219 * @param {Array<?>} arr
220 *
221 * @return {Array<?>}
222 */
223 function flatten(arr) {
224 return Array.prototype.concat.apply([], arr);
225 }
226
227 var nativeToString = Object.prototype.toString;
228 var nativeHasOwnProperty = Object.prototype.hasOwnProperty;
229 function isUndefined$1(obj) {
230 return obj === undefined;
231 }
232 function isDefined(obj) {
233 return obj !== undefined;
234 }
235 function isNil(obj) {
236 return obj == null;
237 }
238 function isArray$2(obj) {
239 return nativeToString.call(obj) === '[object Array]';
240 }
241 function isObject(obj) {
242 return nativeToString.call(obj) === '[object Object]';
243 }
244 function isNumber(obj) {
245 return nativeToString.call(obj) === '[object Number]';
246 }
247 function isFunction(obj) {
248 var tag = nativeToString.call(obj);
249 return tag === '[object Function]' || tag === '[object AsyncFunction]' || tag === '[object GeneratorFunction]' || tag === '[object AsyncGeneratorFunction]' || tag === '[object Proxy]';
250 }
251 function isString(obj) {
252 return nativeToString.call(obj) === '[object String]';
253 }
254 /**
255 * Ensure collection is an array.
256 *
257 * @param {Object} obj
258 */
259
260 function ensureArray(obj) {
261 if (isArray$2(obj)) {
262 return;
263 }
264
265 throw new Error('must supply array');
266 }
267 /**
268 * Return true, if target owns a property with the given key.
269 *
270 * @param {Object} target
271 * @param {String} key
272 *
273 * @return {Boolean}
274 */
275
276 function has(target, key) {
277 return nativeHasOwnProperty.call(target, key);
278 }
279
280 /**
281 * Find element in collection.
282 *
283 * @param {Array|Object} collection
284 * @param {Function|Object} matcher
285 *
286 * @return {Object}
287 */
288
289 function find(collection, matcher) {
290 matcher = toMatcher(matcher);
291 var match;
292 forEach(collection, function (val, key) {
293 if (matcher(val, key)) {
294 match = val;
295 return false;
296 }
297 });
298 return match;
299 }
300 /**
301 * Find element index in collection.
302 *
303 * @param {Array|Object} collection
304 * @param {Function} matcher
305 *
306 * @return {Object}
307 */
308
309 function findIndex(collection, matcher) {
310 matcher = toMatcher(matcher);
311 var idx = isArray$2(collection) ? -1 : undefined;
312 forEach(collection, function (val, key) {
313 if (matcher(val, key)) {
314 idx = key;
315 return false;
316 }
317 });
318 return idx;
319 }
320 /**
321 * Find element in collection.
322 *
323 * @param {Array|Object} collection
324 * @param {Function} matcher
325 *
326 * @return {Array} result
327 */
328
329 function filter(collection, matcher) {
330 var result = [];
331 forEach(collection, function (val, key) {
332 if (matcher(val, key)) {
333 result.push(val);
334 }
335 });
336 return result;
337 }
338 /**
339 * Iterate over collection; returning something
340 * (non-undefined) will stop iteration.
341 *
342 * @param {Array|Object} collection
343 * @param {Function} iterator
344 *
345 * @return {Object} return result that stopped the iteration
346 */
347
348 function forEach(collection, iterator) {
349 var val, result;
350
351 if (isUndefined$1(collection)) {
352 return;
353 }
354
355 var convertKey = isArray$2(collection) ? toNum : identity;
356
357 for (var key in collection) {
358 if (has(collection, key)) {
359 val = collection[key];
360 result = iterator(val, convertKey(key));
361
362 if (result === false) {
363 return val;
364 }
365 }
366 }
367 }
368 /**
369 * Return collection without element.
370 *
371 * @param {Array} arr
372 * @param {Function} matcher
373 *
374 * @return {Array}
375 */
376
377 function without(arr, matcher) {
378 if (isUndefined$1(arr)) {
379 return [];
380 }
381
382 ensureArray(arr);
383 matcher = toMatcher(matcher);
384 return arr.filter(function (el, idx) {
385 return !matcher(el, idx);
386 });
387 }
388 /**
389 * Reduce collection, returning a single result.
390 *
391 * @param {Object|Array} collection
392 * @param {Function} iterator
393 * @param {Any} result
394 *
395 * @return {Any} result returned from last iterator
396 */
397
398 function reduce(collection, iterator, result) {
399 forEach(collection, function (value, idx) {
400 result = iterator(result, value, idx);
401 });
402 return result;
403 }
404 /**
405 * Return true if every element in the collection
406 * matches the criteria.
407 *
408 * @param {Object|Array} collection
409 * @param {Function} matcher
410 *
411 * @return {Boolean}
412 */
413
414 function every(collection, matcher) {
415 return !!reduce(collection, function (matches, val, key) {
416 return matches && matcher(val, key);
417 }, true);
418 }
419 /**
420 * Return true if some elements in the collection
421 * match the criteria.
422 *
423 * @param {Object|Array} collection
424 * @param {Function} matcher
425 *
426 * @return {Boolean}
427 */
428
429 function some(collection, matcher) {
430 return !!find(collection, matcher);
431 }
432 /**
433 * Transform a collection into another collection
434 * by piping each member through the given fn.
435 *
436 * @param {Object|Array} collection
437 * @param {Function} fn
438 *
439 * @return {Array} transformed collection
440 */
441
442 function map$1(collection, fn) {
443 var result = [];
444 forEach(collection, function (val, key) {
445 result.push(fn(val, key));
446 });
447 return result;
448 }
449 /**
450 * Get the collections keys.
451 *
452 * @param {Object|Array} collection
453 *
454 * @return {Array}
455 */
456
457 function keys(collection) {
458 return collection && Object.keys(collection) || [];
459 }
460 /**
461 * Shorthand for `keys(o).length`.
462 *
463 * @param {Object|Array} collection
464 *
465 * @return {Number}
466 */
467
468 function size(collection) {
469 return keys(collection).length;
470 }
471 /**
472 * Get the values in the collection.
473 *
474 * @param {Object|Array} collection
475 *
476 * @return {Array}
477 */
478
479 function values(collection) {
480 return map$1(collection, function (val) {
481 return val;
482 });
483 }
484 /**
485 * Group collection members by attribute.
486 *
487 * @param {Object|Array} collection
488 * @param {Function} extractor
489 *
490 * @return {Object} map with { attrValue => [ a, b, c ] }
491 */
492
493 function groupBy(collection, extractor) {
494 var grouped = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : {};
495 extractor = toExtractor(extractor);
496 forEach(collection, function (val) {
497 var discriminator = extractor(val) || '_';
498 var group = grouped[discriminator];
499
500 if (!group) {
501 group = grouped[discriminator] = [];
502 }
503
504 group.push(val);
505 });
506 return grouped;
507 }
508 function uniqueBy(extractor) {
509 extractor = toExtractor(extractor);
510 var grouped = {};
511
512 for (var _len = arguments.length, collections = new Array(_len > 1 ? _len - 1 : 0), _key = 1; _key < _len; _key++) {
513 collections[_key - 1] = arguments[_key];
514 }
515
516 forEach(collections, function (c) {
517 return groupBy(c, extractor, grouped);
518 });
519 var result = map$1(grouped, function (val, key) {
520 return val[0];
521 });
522 return result;
523 }
524 var unionBy = uniqueBy;
525 /**
526 * Sort collection by criteria.
527 *
528 * @param {Object|Array} collection
529 * @param {String|Function} extractor
530 *
531 * @return {Array}
532 */
533
534 function sortBy(collection, extractor) {
535 extractor = toExtractor(extractor);
536 var sorted = [];
537 forEach(collection, function (value, key) {
538 var disc = extractor(value, key);
539 var entry = {
540 d: disc,
541 v: value
542 };
543
544 for (var idx = 0; idx < sorted.length; idx++) {
545 var d = sorted[idx].d;
546
547 if (disc < d) {
548 sorted.splice(idx, 0, entry);
549 return;
550 }
551 } // not inserted, append (!)
552
553
554 sorted.push(entry);
555 });
556 return map$1(sorted, function (e) {
557 return e.v;
558 });
559 }
560 /**
561 * Create an object pattern matcher.
562 *
563 * @example
564 *
565 * const matcher = matchPattern({ id: 1 });
566 *
567 * var element = find(elements, matcher);
568 *
569 * @param {Object} pattern
570 *
571 * @return {Function} matcherFn
572 */
573
574 function matchPattern(pattern) {
575 return function (el) {
576 return every(pattern, function (val, key) {
577 return el[key] === val;
578 });
579 };
580 }
581
582 function toExtractor(extractor) {
583 return isFunction(extractor) ? extractor : function (e) {
584 return e[extractor];
585 };
586 }
587
588 function toMatcher(matcher) {
589 return isFunction(matcher) ? matcher : function (e) {
590 return e === matcher;
591 };
592 }
593
594 function identity(arg) {
595 return arg;
596 }
597
598 function toNum(arg) {
599 return Number(arg);
600 }
601
602 /**
603 * Debounce fn, calling it only once if
604 * the given time elapsed between calls.
605 *
606 * @param {Function} fn
607 * @param {Number} timeout
608 *
609 * @return {Function} debounced function
610 */
611 function debounce(fn, timeout) {
612 var timer;
613 var lastArgs;
614 var lastThis;
615 var lastNow;
616
617 function fire() {
618 var now = Date.now();
619 var scheduledDiff = lastNow + timeout - now;
620
621 if (scheduledDiff > 0) {
622 return schedule(scheduledDiff);
623 }
624
625 fn.apply(lastThis, lastArgs);
626 timer = lastNow = lastArgs = lastThis = undefined;
627 }
628
629 function schedule(timeout) {
630 timer = setTimeout(fire, timeout);
631 }
632
633 return function () {
634 lastNow = Date.now();
635
636 for (var _len = arguments.length, args = new Array(_len), _key = 0; _key < _len; _key++) {
637 args[_key] = arguments[_key];
638 }
639
640 lastArgs = args;
641 lastThis = this; // ensure an execution is scheduled
642
643 if (!timer) {
644 schedule(timeout);
645 }
646 };
647 }
648 /**
649 * Bind function against target <this>.
650 *
651 * @param {Function} fn
652 * @param {Object} target
653 *
654 * @return {Function} bound function
655 */
656
657 function bind$2(fn, target) {
658 return fn.bind(target);
659 }
660
661 function _extends() {
662 _extends = Object.assign || function (target) {
663 for (var i = 1; i < arguments.length; i++) {
664 var source = arguments[i];
665
666 for (var key in source) {
667 if (Object.prototype.hasOwnProperty.call(source, key)) {
668 target[key] = source[key];
669 }
670 }
671 }
672
673 return target;
674 };
675
676 return _extends.apply(this, arguments);
677 }
678
679 /**
680 * Convenience wrapper for `Object.assign`.
681 *
682 * @param {Object} target
683 * @param {...Object} others
684 *
685 * @return {Object} the target
686 */
687
688 function assign(target) {
689 for (var _len = arguments.length, others = new Array(_len > 1 ? _len - 1 : 0), _key = 1; _key < _len; _key++) {
690 others[_key - 1] = arguments[_key];
691 }
692
693 return _extends.apply(void 0, [target].concat(others));
694 }
695 /**
696 * Pick given properties from the target object.
697 *
698 * @param {Object} target
699 * @param {Array} properties
700 *
701 * @return {Object} target
702 */
703
704 function pick(target, properties) {
705 var result = {};
706 var obj = Object(target);
707 forEach(properties, function (prop) {
708 if (prop in obj) {
709 result[prop] = target[prop];
710 }
711 });
712 return result;
713 }
714 /**
715 * Pick all target properties, excluding the given ones.
716 *
717 * @param {Object} target
718 * @param {Array} properties
719 *
720 * @return {Object} target
721 */
722
723 function omit(target, properties) {
724 var result = {};
725 var obj = Object(target);
726 forEach(obj, function (prop, key) {
727 if (properties.indexOf(key) === -1) {
728 result[key] = prop;
729 }
730 });
731 return result;
732 }
733
734 /**
735 * Set attribute `name` to `val`, or get attr `name`.
736 *
737 * @param {Element} el
738 * @param {String} name
739 * @param {String} [val]
740 * @api public
741 */
742 function attr$1(el, name, val) {
743 // get
744 if (arguments.length == 2) {
745 return el.getAttribute(name);
746 }
747
748 // remove
749 if (val === null) {
750 return el.removeAttribute(name);
751 }
752
753 // set
754 el.setAttribute(name, val);
755
756 return el;
757 }
758
759 var indexOf$1 = [].indexOf;
760
761 var indexof = function(arr, obj){
762 if (indexOf$1) return arr.indexOf(obj);
763 for (var i = 0; i < arr.length; ++i) {
764 if (arr[i] === obj) return i;
765 }
766 return -1;
767 };
768
769 /**
770 * Taken from https://github.com/component/classes
771 *
772 * Without the component bits.
773 */
774
775 /**
776 * Whitespace regexp.
777 */
778
779 var re$1 = /\s+/;
780
781 /**
782 * toString reference.
783 */
784
785 var toString$1 = Object.prototype.toString;
786
787 /**
788 * Wrap `el` in a `ClassList`.
789 *
790 * @param {Element} el
791 * @return {ClassList}
792 * @api public
793 */
794
795 function classes$1(el) {
796 return new ClassList$1(el);
797 }
798
799 /**
800 * Initialize a new ClassList for `el`.
801 *
802 * @param {Element} el
803 * @api private
804 */
805
806 function ClassList$1(el) {
807 if (!el || !el.nodeType) {
808 throw new Error('A DOM element reference is required');
809 }
810 this.el = el;
811 this.list = el.classList;
812 }
813
814 /**
815 * Add class `name` if not already present.
816 *
817 * @param {String} name
818 * @return {ClassList}
819 * @api public
820 */
821
822 ClassList$1.prototype.add = function (name) {
823 // classList
824 if (this.list) {
825 this.list.add(name);
826 return this;
827 }
828
829 // fallback
830 var arr = this.array();
831 var i = indexof(arr, name);
832 if (!~i) arr.push(name);
833 this.el.className = arr.join(' ');
834 return this;
835 };
836
837 /**
838 * Remove class `name` when present, or
839 * pass a regular expression to remove
840 * any which match.
841 *
842 * @param {String|RegExp} name
843 * @return {ClassList}
844 * @api public
845 */
846
847 ClassList$1.prototype.remove = function (name) {
848 if ('[object RegExp]' == toString$1.call(name)) {
849 return this.removeMatching(name);
850 }
851
852 // classList
853 if (this.list) {
854 this.list.remove(name);
855 return this;
856 }
857
858 // fallback
859 var arr = this.array();
860 var i = indexof(arr, name);
861 if (~i) arr.splice(i, 1);
862 this.el.className = arr.join(' ');
863 return this;
864 };
865
866 /**
867 * Remove all classes matching `re`.
868 *
869 * @param {RegExp} re
870 * @return {ClassList}
871 * @api private
872 */
873
874 ClassList$1.prototype.removeMatching = function (re) {
875 var arr = this.array();
876 for (var i = 0; i < arr.length; i++) {
877 if (re.test(arr[i])) {
878 this.remove(arr[i]);
879 }
880 }
881 return this;
882 };
883
884 /**
885 * Toggle class `name`, can force state via `force`.
886 *
887 * For browsers that support classList, but do not support `force` yet,
888 * the mistake will be detected and corrected.
889 *
890 * @param {String} name
891 * @param {Boolean} force
892 * @return {ClassList}
893 * @api public
894 */
895
896 ClassList$1.prototype.toggle = function (name, force) {
897 // classList
898 if (this.list) {
899 if ('undefined' !== typeof force) {
900 if (force !== this.list.toggle(name, force)) {
901 this.list.toggle(name); // toggle again to correct
902 }
903 } else {
904 this.list.toggle(name);
905 }
906 return this;
907 }
908
909 // fallback
910 if ('undefined' !== typeof force) {
911 if (!force) {
912 this.remove(name);
913 } else {
914 this.add(name);
915 }
916 } else {
917 if (this.has(name)) {
918 this.remove(name);
919 } else {
920 this.add(name);
921 }
922 }
923
924 return this;
925 };
926
927 /**
928 * Return an array of classes.
929 *
930 * @return {Array}
931 * @api public
932 */
933
934 ClassList$1.prototype.array = function () {
935 var className = this.el.getAttribute('class') || '';
936 var str = className.replace(/^\s+|\s+$/g, '');
937 var arr = str.split(re$1);
938 if ('' === arr[0]) arr.shift();
939 return arr;
940 };
941
942 /**
943 * Check if class `name` is present.
944 *
945 * @param {String} name
946 * @return {ClassList}
947 * @api public
948 */
949
950 ClassList$1.prototype.has = ClassList$1.prototype.contains = function (name) {
951 return this.list ? this.list.contains(name) : !!~indexof(this.array(), name);
952 };
953
954 /**
955 * Remove all children from the given element.
956 */
957 function clear$1(el) {
958
959 var c;
960
961 while (el.childNodes.length) {
962 c = el.childNodes[0];
963 el.removeChild(c);
964 }
965
966 return el;
967 }
968
969 var proto = typeof Element !== 'undefined' ? Element.prototype : {};
970 var vendor = proto.matches
971 || proto.matchesSelector
972 || proto.webkitMatchesSelector
973 || proto.mozMatchesSelector
974 || proto.msMatchesSelector
975 || proto.oMatchesSelector;
976
977 var matchesSelector = match;
978
979 /**
980 * Match `el` to `selector`.
981 *
982 * @param {Element} el
983 * @param {String} selector
984 * @return {Boolean}
985 * @api public
986 */
987
988 function match(el, selector) {
989 if (!el || el.nodeType !== 1) return false;
990 if (vendor) return vendor.call(el, selector);
991 var nodes = el.parentNode.querySelectorAll(selector);
992 for (var i = 0; i < nodes.length; i++) {
993 if (nodes[i] == el) return true;
994 }
995 return false;
996 }
997
998 /**
999 * Closest
1000 *
1001 * @param {Element} el
1002 * @param {String} selector
1003 * @param {Boolean} checkYourSelf (optional)
1004 */
1005 function closest (element, selector, checkYourSelf) {
1006 var currentElem = checkYourSelf ? element : element.parentNode;
1007
1008 while (currentElem && currentElem.nodeType !== document.DOCUMENT_NODE && currentElem.nodeType !== document.DOCUMENT_FRAGMENT_NODE) {
1009
1010 if (matchesSelector(currentElem, selector)) {
1011 return currentElem;
1012 }
1013
1014 currentElem = currentElem.parentNode;
1015 }
1016
1017 return matchesSelector(currentElem, selector) ? currentElem : null;
1018 }
1019
1020 var bind = window.addEventListener ? 'addEventListener' : 'attachEvent',
1021 unbind = window.removeEventListener ? 'removeEventListener' : 'detachEvent',
1022 prefix$6 = bind !== 'addEventListener' ? 'on' : '';
1023
1024 /**
1025 * Bind `el` event `type` to `fn`.
1026 *
1027 * @param {Element} el
1028 * @param {String} type
1029 * @param {Function} fn
1030 * @param {Boolean} capture
1031 * @return {Function}
1032 * @api public
1033 */
1034
1035 var bind_1 = function(el, type, fn, capture){
1036 el[bind](prefix$6 + type, fn, capture || false);
1037 return fn;
1038 };
1039
1040 /**
1041 * Unbind `el` event `type`'s callback `fn`.
1042 *
1043 * @param {Element} el
1044 * @param {String} type
1045 * @param {Function} fn
1046 * @param {Boolean} capture
1047 * @return {Function}
1048 * @api public
1049 */
1050
1051 var unbind_1 = function(el, type, fn, capture){
1052 el[unbind](prefix$6 + type, fn, capture || false);
1053 return fn;
1054 };
1055
1056 var componentEvent = {
1057 bind: bind_1,
1058 unbind: unbind_1
1059 };
1060
1061 /**
1062 * Module dependencies.
1063 */
1064
1065 /**
1066 * Delegate event `type` to `selector`
1067 * and invoke `fn(e)`. A callback function
1068 * is returned which may be passed to `.unbind()`.
1069 *
1070 * @param {Element} el
1071 * @param {String} selector
1072 * @param {String} type
1073 * @param {Function} fn
1074 * @param {Boolean} capture
1075 * @return {Function}
1076 * @api public
1077 */
1078
1079 // Some events don't bubble, so we want to bind to the capture phase instead
1080 // when delegating.
1081 var forceCaptureEvents = ['focus', 'blur'];
1082
1083 function bind$1(el, selector, type, fn, capture) {
1084 if (forceCaptureEvents.indexOf(type) !== -1) {
1085 capture = true;
1086 }
1087
1088 return componentEvent.bind(el, type, function (e) {
1089 var target = e.target || e.srcElement;
1090 e.delegateTarget = closest(target, selector, true);
1091 if (e.delegateTarget) {
1092 fn.call(el, e);
1093 }
1094 }, capture);
1095 }
1096
1097 /**
1098 * Unbind event `type`'s callback `fn`.
1099 *
1100 * @param {Element} el
1101 * @param {String} type
1102 * @param {Function} fn
1103 * @param {Boolean} capture
1104 * @api public
1105 */
1106 function unbind$1(el, type, fn, capture) {
1107 if (forceCaptureEvents.indexOf(type) !== -1) {
1108 capture = true;
1109 }
1110
1111 return componentEvent.unbind(el, type, fn, capture);
1112 }
1113
1114 var delegate = {
1115 bind: bind$1,
1116 unbind: unbind$1
1117 };
1118
1119 /**
1120 * Expose `parse`.
1121 */
1122
1123 var domify = parse$1;
1124
1125 /**
1126 * Tests for browser support.
1127 */
1128
1129 var innerHTMLBug = false;
1130 var bugTestDiv;
1131 if (typeof document !== 'undefined') {
1132 bugTestDiv = document.createElement('div');
1133 // Setup
1134 bugTestDiv.innerHTML = ' <link/><table></table><a href="/a">a</a><input type="checkbox"/>';
1135 // Make sure that link elements get serialized correctly by innerHTML
1136 // This requires a wrapper element in IE
1137 innerHTMLBug = !bugTestDiv.getElementsByTagName('link').length;
1138 bugTestDiv = undefined;
1139 }
1140
1141 /**
1142 * Wrap map from jquery.
1143 */
1144
1145 var map = {
1146 legend: [1, '<fieldset>', '</fieldset>'],
1147 tr: [2, '<table><tbody>', '</tbody></table>'],
1148 col: [2, '<table><tbody></tbody><colgroup>', '</colgroup></table>'],
1149 // for script/link/style tags to work in IE6-8, you have to wrap
1150 // in a div with a non-whitespace character in front, ha!
1151 _default: innerHTMLBug ? [1, 'X<div>', '</div>'] : [0, '', '']
1152 };
1153
1154 map.td =
1155 map.th = [3, '<table><tbody><tr>', '</tr></tbody></table>'];
1156
1157 map.option =
1158 map.optgroup = [1, '<select multiple="multiple">', '</select>'];
1159
1160 map.thead =
1161 map.tbody =
1162 map.colgroup =
1163 map.caption =
1164 map.tfoot = [1, '<table>', '</table>'];
1165
1166 map.polyline =
1167 map.ellipse =
1168 map.polygon =
1169 map.circle =
1170 map.text =
1171 map.line =
1172 map.path =
1173 map.rect =
1174 map.g = [1, '<svg xmlns="http://www.w3.org/2000/svg" version="1.1">','</svg>'];
1175
1176 /**
1177 * Parse `html` and return a DOM Node instance, which could be a TextNode,
1178 * HTML DOM Node of some kind (<div> for example), or a DocumentFragment
1179 * instance, depending on the contents of the `html` string.
1180 *
1181 * @param {String} html - HTML string to "domify"
1182 * @param {Document} doc - The `document` instance to create the Node for
1183 * @return {DOMNode} the TextNode, DOM Node, or DocumentFragment instance
1184 * @api private
1185 */
1186
1187 function parse$1(html, doc) {
1188 if ('string' != typeof html) throw new TypeError('String expected');
1189
1190 // default to the global `document` object
1191 if (!doc) doc = document;
1192
1193 // tag name
1194 var m = /<([\w:]+)/.exec(html);
1195 if (!m) return doc.createTextNode(html);
1196
1197 html = html.replace(/^\s+|\s+$/g, ''); // Remove leading/trailing whitespace
1198
1199 var tag = m[1];
1200
1201 // body support
1202 if (tag == 'body') {
1203 var el = doc.createElement('html');
1204 el.innerHTML = html;
1205 return el.removeChild(el.lastChild);
1206 }
1207
1208 // wrap map
1209 var wrap = map[tag] || map._default;
1210 var depth = wrap[0];
1211 var prefix = wrap[1];
1212 var suffix = wrap[2];
1213 var el = doc.createElement('div');
1214 el.innerHTML = prefix + html + suffix;
1215 while (depth--) el = el.lastChild;
1216
1217 // one element
1218 if (el.firstChild == el.lastChild) {
1219 return el.removeChild(el.firstChild);
1220 }
1221
1222 // several elements
1223 var fragment = doc.createDocumentFragment();
1224 while (el.firstChild) {
1225 fragment.appendChild(el.removeChild(el.firstChild));
1226 }
1227
1228 return fragment;
1229 }
1230
1231 function query(selector, el) {
1232 el = el || document;
1233
1234 return el.querySelector(selector);
1235 }
1236
1237 function all(selector, el) {
1238 el = el || document;
1239
1240 return el.querySelectorAll(selector);
1241 }
1242
1243 function remove$2(el) {
1244 el.parentNode && el.parentNode.removeChild(el);
1245 }
1246
1247 function ensureImported(element, target) {
1248
1249 if (element.ownerDocument !== target.ownerDocument) {
1250 try {
1251 // may fail on webkit
1252 return target.ownerDocument.importNode(element, true);
1253 } catch (e) {
1254 // ignore
1255 }
1256 }
1257
1258 return element;
1259 }
1260
1261 /**
1262 * appendTo utility
1263 */
1264
1265 /**
1266 * Append a node to a target element and return the appended node.
1267 *
1268 * @param {SVGElement} element
1269 * @param {SVGElement} target
1270 *
1271 * @return {SVGElement} the appended node
1272 */
1273 function appendTo(element, target) {
1274 return target.appendChild(ensureImported(element, target));
1275 }
1276
1277 /**
1278 * append utility
1279 */
1280
1281 /**
1282 * Append a node to an element
1283 *
1284 * @param {SVGElement} element
1285 * @param {SVGElement} node
1286 *
1287 * @return {SVGElement} the element
1288 */
1289 function append(target, node) {
1290 appendTo(node, target);
1291 return target;
1292 }
1293
1294 /**
1295 * attribute accessor utility
1296 */
1297
1298 var LENGTH_ATTR = 2;
1299
1300 var CSS_PROPERTIES = {
1301 'alignment-baseline': 1,
1302 'baseline-shift': 1,
1303 'clip': 1,
1304 'clip-path': 1,
1305 'clip-rule': 1,
1306 'color': 1,
1307 'color-interpolation': 1,
1308 'color-interpolation-filters': 1,
1309 'color-profile': 1,
1310 'color-rendering': 1,
1311 'cursor': 1,
1312 'direction': 1,
1313 'display': 1,
1314 'dominant-baseline': 1,
1315 'enable-background': 1,
1316 'fill': 1,
1317 'fill-opacity': 1,
1318 'fill-rule': 1,
1319 'filter': 1,
1320 'flood-color': 1,
1321 'flood-opacity': 1,
1322 'font': 1,
1323 'font-family': 1,
1324 'font-size': LENGTH_ATTR,
1325 'font-size-adjust': 1,
1326 'font-stretch': 1,
1327 'font-style': 1,
1328 'font-variant': 1,
1329 'font-weight': 1,
1330 'glyph-orientation-horizontal': 1,
1331 'glyph-orientation-vertical': 1,
1332 'image-rendering': 1,
1333 'kerning': 1,
1334 'letter-spacing': 1,
1335 'lighting-color': 1,
1336 'marker': 1,
1337 'marker-end': 1,
1338 'marker-mid': 1,
1339 'marker-start': 1,
1340 'mask': 1,
1341 'opacity': 1,
1342 'overflow': 1,
1343 'pointer-events': 1,
1344 'shape-rendering': 1,
1345 'stop-color': 1,
1346 'stop-opacity': 1,
1347 'stroke': 1,
1348 'stroke-dasharray': 1,
1349 'stroke-dashoffset': 1,
1350 'stroke-linecap': 1,
1351 'stroke-linejoin': 1,
1352 'stroke-miterlimit': 1,
1353 'stroke-opacity': 1,
1354 'stroke-width': LENGTH_ATTR,
1355 'text-anchor': 1,
1356 'text-decoration': 1,
1357 'text-rendering': 1,
1358 'unicode-bidi': 1,
1359 'visibility': 1,
1360 'word-spacing': 1,
1361 'writing-mode': 1
1362 };
1363
1364
1365 function getAttribute(node, name) {
1366 if (CSS_PROPERTIES[name]) {
1367 return node.style[name];
1368 } else {
1369 return node.getAttributeNS(null, name);
1370 }
1371 }
1372
1373 function setAttribute(node, name, value) {
1374 var hyphenated = name.replace(/([a-z])([A-Z])/g, '$1-$2').toLowerCase();
1375
1376 var type = CSS_PROPERTIES[hyphenated];
1377
1378 if (type) {
1379 // append pixel unit, unless present
1380 if (type === LENGTH_ATTR && typeof value === 'number') {
1381 value = String(value) + 'px';
1382 }
1383
1384 node.style[hyphenated] = value;
1385 } else {
1386 node.setAttributeNS(null, name, value);
1387 }
1388 }
1389
1390 function setAttributes(node, attrs) {
1391
1392 var names = Object.keys(attrs), i, name;
1393
1394 for (i = 0, name; (name = names[i]); i++) {
1395 setAttribute(node, name, attrs[name]);
1396 }
1397 }
1398
1399 /**
1400 * Gets or sets raw attributes on a node.
1401 *
1402 * @param {SVGElement} node
1403 * @param {Object} [attrs]
1404 * @param {String} [name]
1405 * @param {String} [value]
1406 *
1407 * @return {String}
1408 */
1409 function attr(node, name, value) {
1410 if (typeof name === 'string') {
1411 if (value !== undefined) {
1412 setAttribute(node, name, value);
1413 } else {
1414 return getAttribute(node, name);
1415 }
1416 } else {
1417 setAttributes(node, name);
1418 }
1419
1420 return node;
1421 }
1422
1423 /**
1424 * Clear utility
1425 */
1426 function index(arr, obj) {
1427 if (arr.indexOf) {
1428 return arr.indexOf(obj);
1429 }
1430
1431
1432 for (var i = 0; i < arr.length; ++i) {
1433 if (arr[i] === obj) {
1434 return i;
1435 }
1436 }
1437
1438 return -1;
1439 }
1440
1441 var re = /\s+/;
1442
1443 var toString = Object.prototype.toString;
1444
1445 function defined(o) {
1446 return typeof o !== 'undefined';
1447 }
1448
1449 /**
1450 * Wrap `el` in a `ClassList`.
1451 *
1452 * @param {Element} el
1453 * @return {ClassList}
1454 * @api public
1455 */
1456
1457 function classes(el) {
1458 return new ClassList(el);
1459 }
1460
1461 function ClassList(el) {
1462 if (!el || !el.nodeType) {
1463 throw new Error('A DOM element reference is required');
1464 }
1465 this.el = el;
1466 this.list = el.classList;
1467 }
1468
1469 /**
1470 * Add class `name` if not already present.
1471 *
1472 * @param {String} name
1473 * @return {ClassList}
1474 * @api public
1475 */
1476
1477 ClassList.prototype.add = function(name) {
1478
1479 // classList
1480 if (this.list) {
1481 this.list.add(name);
1482 return this;
1483 }
1484
1485 // fallback
1486 var arr = this.array();
1487 var i = index(arr, name);
1488 if (!~i) {
1489 arr.push(name);
1490 }
1491
1492 if (defined(this.el.className.baseVal)) {
1493 this.el.className.baseVal = arr.join(' ');
1494 } else {
1495 this.el.className = arr.join(' ');
1496 }
1497
1498 return this;
1499 };
1500
1501 /**
1502 * Remove class `name` when present, or
1503 * pass a regular expression to remove
1504 * any which match.
1505 *
1506 * @param {String|RegExp} name
1507 * @return {ClassList}
1508 * @api public
1509 */
1510
1511 ClassList.prototype.remove = function(name) {
1512 if ('[object RegExp]' === toString.call(name)) {
1513 return this.removeMatching(name);
1514 }
1515
1516 // classList
1517 if (this.list) {
1518 this.list.remove(name);
1519 return this;
1520 }
1521
1522 // fallback
1523 var arr = this.array();
1524 var i = index(arr, name);
1525 if (~i) {
1526 arr.splice(i, 1);
1527 }
1528 this.el.className.baseVal = arr.join(' ');
1529 return this;
1530 };
1531
1532 /**
1533 * Remove all classes matching `re`.
1534 *
1535 * @param {RegExp} re
1536 * @return {ClassList}
1537 * @api private
1538 */
1539
1540 ClassList.prototype.removeMatching = function(re) {
1541 var arr = this.array();
1542 for (var i = 0; i < arr.length; i++) {
1543 if (re.test(arr[i])) {
1544 this.remove(arr[i]);
1545 }
1546 }
1547 return this;
1548 };
1549
1550 /**
1551 * Toggle class `name`, can force state via `force`.
1552 *
1553 * For browsers that support classList, but do not support `force` yet,
1554 * the mistake will be detected and corrected.
1555 *
1556 * @param {String} name
1557 * @param {Boolean} force
1558 * @return {ClassList}
1559 * @api public
1560 */
1561
1562 ClassList.prototype.toggle = function(name, force) {
1563 // classList
1564 if (this.list) {
1565 if (defined(force)) {
1566 if (force !== this.list.toggle(name, force)) {
1567 this.list.toggle(name); // toggle again to correct
1568 }
1569 } else {
1570 this.list.toggle(name);
1571 }
1572 return this;
1573 }
1574
1575 // fallback
1576 if (defined(force)) {
1577 if (!force) {
1578 this.remove(name);
1579 } else {
1580 this.add(name);
1581 }
1582 } else {
1583 if (this.has(name)) {
1584 this.remove(name);
1585 } else {
1586 this.add(name);
1587 }
1588 }
1589
1590 return this;
1591 };
1592
1593 /**
1594 * Return an array of classes.
1595 *
1596 * @return {Array}
1597 * @api public
1598 */
1599
1600 ClassList.prototype.array = function() {
1601 var className = this.el.getAttribute('class') || '';
1602 var str = className.replace(/^\s+|\s+$/g, '');
1603 var arr = str.split(re);
1604 if ('' === arr[0]) {
1605 arr.shift();
1606 }
1607 return arr;
1608 };
1609
1610 /**
1611 * Check if class `name` is present.
1612 *
1613 * @param {String} name
1614 * @return {ClassList}
1615 * @api public
1616 */
1617
1618 ClassList.prototype.has =
1619 ClassList.prototype.contains = function(name) {
1620 return (
1621 this.list ?
1622 this.list.contains(name) :
1623 !! ~index(this.array(), name)
1624 );
1625 };
1626
1627 function remove$1(element) {
1628 var parent = element.parentNode;
1629
1630 if (parent) {
1631 parent.removeChild(element);
1632 }
1633
1634 return element;
1635 }
1636
1637 /**
1638 * Clear utility
1639 */
1640
1641 /**
1642 * Removes all children from the given element
1643 *
1644 * @param {DOMElement} element
1645 * @return {DOMElement} the element (for chaining)
1646 */
1647 function clear(element) {
1648 var child;
1649
1650 while ((child = element.firstChild)) {
1651 remove$1(child);
1652 }
1653
1654 return element;
1655 }
1656
1657 function clone$1(element) {
1658 return element.cloneNode(true);
1659 }
1660
1661 var ns = {
1662 svg: 'http://www.w3.org/2000/svg'
1663 };
1664
1665 /**
1666 * DOM parsing utility
1667 */
1668
1669 var SVG_START = '<svg xmlns="' + ns.svg + '"';
1670
1671 function parse(svg) {
1672
1673 var unwrap = false;
1674
1675 // ensure we import a valid svg document
1676 if (svg.substring(0, 4) === '<svg') {
1677 if (svg.indexOf(ns.svg) === -1) {
1678 svg = SVG_START + svg.substring(4);
1679 }
1680 } else {
1681 // namespace svg
1682 svg = SVG_START + '>' + svg + '</svg>';
1683 unwrap = true;
1684 }
1685
1686 var parsed = parseDocument(svg);
1687
1688 if (!unwrap) {
1689 return parsed;
1690 }
1691
1692 var fragment = document.createDocumentFragment();
1693
1694 var parent = parsed.firstChild;
1695
1696 while (parent.firstChild) {
1697 fragment.appendChild(parent.firstChild);
1698 }
1699
1700 return fragment;
1701 }
1702
1703 function parseDocument(svg) {
1704
1705 var parser;
1706
1707 // parse
1708 parser = new DOMParser();
1709 parser.async = false;
1710
1711 return parser.parseFromString(svg, 'text/xml');
1712 }
1713
1714 /**
1715 * Create utility for SVG elements
1716 */
1717
1718
1719 /**
1720 * Create a specific type from name or SVG markup.
1721 *
1722 * @param {String} name the name or markup of the element
1723 * @param {Object} [attrs] attributes to set on the element
1724 *
1725 * @returns {SVGElement}
1726 */
1727 function create$1(name, attrs) {
1728 var element;
1729
1730 if (name.charAt(0) === '<') {
1731 element = parse(name).firstChild;
1732 element = document.importNode(element, true);
1733 } else {
1734 element = document.createElementNS(ns.svg, name);
1735 }
1736
1737 if (attrs) {
1738 attr(element, attrs);
1739 }
1740
1741 return element;
1742 }
1743
1744 /**
1745 * Geometry helpers
1746 */
1747
1748 // fake node used to instantiate svg geometry elements
1749 var node = create$1('svg');
1750
1751 function extend$1(object, props) {
1752 var i, k, keys = Object.keys(props);
1753
1754 for (i = 0; (k = keys[i]); i++) {
1755 object[k] = props[k];
1756 }
1757
1758 return object;
1759 }
1760
1761 /**
1762 * Create matrix via args.
1763 *
1764 * @example
1765 *
1766 * createMatrix({ a: 1, b: 1 });
1767 * createMatrix();
1768 * createMatrix(1, 2, 0, 0, 30, 20);
1769 *
1770 * @return {SVGMatrix}
1771 */
1772 function createMatrix(a, b, c, d, e, f) {
1773 var matrix = node.createSVGMatrix();
1774
1775 switch (arguments.length) {
1776 case 0:
1777 return matrix;
1778 case 1:
1779 return extend$1(matrix, a);
1780 case 6:
1781 return extend$1(matrix, {
1782 a: a,
1783 b: b,
1784 c: c,
1785 d: d,
1786 e: e,
1787 f: f
1788 });
1789 }
1790 }
1791
1792 function createTransform(matrix) {
1793 if (matrix) {
1794 return node.createSVGTransformFromMatrix(matrix);
1795 } else {
1796 return node.createSVGTransform();
1797 }
1798 }
1799
1800 /**
1801 * Serialization util
1802 */
1803
1804 var TEXT_ENTITIES = /([&<>]{1})/g;
1805 var ATTR_ENTITIES = /([\n\r"]{1})/g;
1806
1807 var ENTITY_REPLACEMENT = {
1808 '&': '&amp;',
1809 '<': '&lt;',
1810 '>': '&gt;',
1811 '"': '\''
1812 };
1813
1814 function escape$1(str, pattern) {
1815
1816 function replaceFn(match, entity) {
1817 return ENTITY_REPLACEMENT[entity] || entity;
1818 }
1819
1820 return str.replace(pattern, replaceFn);
1821 }
1822
1823 function serialize(node, output) {
1824
1825 var i, len, attrMap, attrNode, childNodes;
1826
1827 switch (node.nodeType) {
1828 // TEXT
1829 case 3:
1830 // replace special XML characters
1831 output.push(escape$1(node.textContent, TEXT_ENTITIES));
1832 break;
1833
1834 // ELEMENT
1835 case 1:
1836 output.push('<', node.tagName);
1837
1838 if (node.hasAttributes()) {
1839 attrMap = node.attributes;
1840 for (i = 0, len = attrMap.length; i < len; ++i) {
1841 attrNode = attrMap.item(i);
1842 output.push(' ', attrNode.name, '="', escape$1(attrNode.value, ATTR_ENTITIES), '"');
1843 }
1844 }
1845
1846 if (node.hasChildNodes()) {
1847 output.push('>');
1848 childNodes = node.childNodes;
1849 for (i = 0, len = childNodes.length; i < len; ++i) {
1850 serialize(childNodes.item(i), output);
1851 }
1852 output.push('</', node.tagName, '>');
1853 } else {
1854 output.push('/>');
1855 }
1856 break;
1857
1858 // COMMENT
1859 case 8:
1860 output.push('<!--', escape$1(node.nodeValue, TEXT_ENTITIES), '-->');
1861 break;
1862
1863 // CDATA
1864 case 4:
1865 output.push('<![CDATA[', node.nodeValue, ']]>');
1866 break;
1867
1868 default:
1869 throw new Error('unable to handle node ' + node.nodeType);
1870 }
1871
1872 return output;
1873 }
1874
1875 /**
1876 * innerHTML like functionality for SVG elements.
1877 * based on innerSVG (https://code.google.com/p/innersvg)
1878 */
1879
1880
1881 function set$1(element, svg) {
1882
1883 var parsed = parse(svg);
1884
1885 // clear element contents
1886 clear(element);
1887
1888 if (!svg) {
1889 return;
1890 }
1891
1892 if (!isFragment(parsed)) {
1893 // extract <svg> from parsed document
1894 parsed = parsed.documentElement;
1895 }
1896
1897 var nodes = slice$1(parsed.childNodes);
1898
1899 // import + append each node
1900 for (var i = 0; i < nodes.length; i++) {
1901 appendTo(nodes[i], element);
1902 }
1903
1904 }
1905
1906 function get$1(element) {
1907 var child = element.firstChild,
1908 output = [];
1909
1910 while (child) {
1911 serialize(child, output);
1912 child = child.nextSibling;
1913 }
1914
1915 return output.join('');
1916 }
1917
1918 function isFragment(node) {
1919 return node.nodeName === '#document-fragment';
1920 }
1921
1922 function innerSVG(element, svg) {
1923
1924 if (svg !== undefined) {
1925
1926 try {
1927 set$1(element, svg);
1928 } catch (e) {
1929 throw new Error('error parsing SVG: ' + e.message);
1930 }
1931
1932 return element;
1933 } else {
1934 return get$1(element);
1935 }
1936 }
1937
1938
1939 function slice$1(arr) {
1940 return Array.prototype.slice.call(arr);
1941 }
1942
1943 /**
1944 * transform accessor utility
1945 */
1946
1947 function wrapMatrix(transformList, transform) {
1948 if (transform instanceof SVGMatrix) {
1949 return transformList.createSVGTransformFromMatrix(transform);
1950 }
1951
1952 return transform;
1953 }
1954
1955
1956 function setTransforms(transformList, transforms) {
1957 var i, t;
1958
1959 transformList.clear();
1960
1961 for (i = 0; (t = transforms[i]); i++) {
1962 transformList.appendItem(wrapMatrix(transformList, t));
1963 }
1964 }
1965
1966 /**
1967 * Get or set the transforms on the given node.
1968 *
1969 * @param {SVGElement} node
1970 * @param {SVGTransform|SVGMatrix|Array<SVGTransform|SVGMatrix>} [transforms]
1971 *
1972 * @return {SVGTransform} the consolidated transform
1973 */
1974 function transform$1(node, transforms) {
1975 var transformList = node.transform.baseVal;
1976
1977 if (transforms) {
1978
1979 if (!Array.isArray(transforms)) {
1980 transforms = [ transforms ];
1981 }
1982
1983 setTransforms(transformList, transforms);
1984 }
1985
1986 return transformList.consolidate();
1987 }
1988
1989 var CLASS_PATTERN = /^class /;
1990
1991 function isClass(fn) {
1992 return CLASS_PATTERN.test(fn.toString());
1993 }
1994
1995 function isArray$1(obj) {
1996 return Object.prototype.toString.call(obj) === '[object Array]';
1997 }
1998
1999 function hasOwnProp(obj, prop) {
2000 return Object.prototype.hasOwnProperty.call(obj, prop);
2001 }
2002
2003 function annotate() {
2004 var args = Array.prototype.slice.call(arguments);
2005
2006 if (args.length === 1 && isArray$1(args[0])) {
2007 args = args[0];
2008 }
2009
2010 var fn = args.pop();
2011
2012 fn.$inject = args;
2013
2014 return fn;
2015 }
2016
2017
2018 // Current limitations:
2019 // - can't put into "function arg" comments
2020 // function /* (no parenthesis like this) */ (){}
2021 // function abc( /* xx (no parenthesis like this) */ a, b) {}
2022 //
2023 // Just put the comment before function or inside:
2024 // /* (((this is fine))) */ function(a, b) {}
2025 // function abc(a) { /* (((this is fine))) */}
2026 //
2027 // - can't reliably auto-annotate constructor; we'll match the
2028 // first constructor(...) pattern found which may be the one
2029 // of a nested class, too.
2030
2031 var CONSTRUCTOR_ARGS = /constructor\s*[^(]*\(\s*([^)]*)\)/m;
2032 var FN_ARGS = /^(?:async )?(?:function\s*)?[^(]*\(\s*([^)]*)\)/m;
2033 var FN_ARG = /\/\*([^*]*)\*\//m;
2034
2035 function parseAnnotations(fn) {
2036
2037 if (typeof fn !== 'function') {
2038 throw new Error('Cannot annotate "' + fn + '". Expected a function!');
2039 }
2040
2041 var match = fn.toString().match(isClass(fn) ? CONSTRUCTOR_ARGS : FN_ARGS);
2042
2043 // may parse class without constructor
2044 if (!match) {
2045 return [];
2046 }
2047
2048 return match[1] && match[1].split(',').map(function(arg) {
2049 match = arg.match(FN_ARG);
2050 return match ? match[1].trim() : arg.trim();
2051 }) || [];
2052 }
2053
2054 function Module() {
2055 var providers = [];
2056
2057 this.factory = function(name, factory) {
2058 providers.push([name, 'factory', factory]);
2059 return this;
2060 };
2061
2062 this.value = function(name, value) {
2063 providers.push([name, 'value', value]);
2064 return this;
2065 };
2066
2067 this.type = function(name, type) {
2068 providers.push([name, 'type', type]);
2069 return this;
2070 };
2071
2072 this.forEach = function(iterator) {
2073 providers.forEach(iterator);
2074 };
2075
2076 }
2077
2078 function Injector(modules, parent) {
2079 parent = parent || {
2080 get: function(name, strict) {
2081 currentlyResolving.push(name);
2082
2083 if (strict === false) {
2084 return null;
2085 } else {
2086 throw error('No provider for "' + name + '"!');
2087 }
2088 }
2089 };
2090
2091 var currentlyResolving = [];
2092 var providers = this._providers = Object.create(parent._providers || null);
2093 var instances = this._instances = Object.create(null);
2094
2095 var self = instances.injector = this;
2096
2097 var error = function(msg) {
2098 var stack = currentlyResolving.join(' -> ');
2099 currentlyResolving.length = 0;
2100 return new Error(stack ? msg + ' (Resolving: ' + stack + ')' : msg);
2101 };
2102
2103 /**
2104 * Return a named service.
2105 *
2106 * @param {String} name
2107 * @param {Boolean} [strict=true] if false, resolve missing services to null
2108 *
2109 * @return {Object}
2110 */
2111 var get = function(name, strict) {
2112 if (!providers[name] && name.indexOf('.') !== -1) {
2113 var parts = name.split('.');
2114 var pivot = get(parts.shift());
2115
2116 while (parts.length) {
2117 pivot = pivot[parts.shift()];
2118 }
2119
2120 return pivot;
2121 }
2122
2123 if (hasOwnProp(instances, name)) {
2124 return instances[name];
2125 }
2126
2127 if (hasOwnProp(providers, name)) {
2128 if (currentlyResolving.indexOf(name) !== -1) {
2129 currentlyResolving.push(name);
2130 throw error('Cannot resolve circular dependency!');
2131 }
2132
2133 currentlyResolving.push(name);
2134 instances[name] = providers[name][0](providers[name][1]);
2135 currentlyResolving.pop();
2136
2137 return instances[name];
2138 }
2139
2140 return parent.get(name, strict);
2141 };
2142
2143 var fnDef = function(fn, locals) {
2144
2145 if (typeof locals === 'undefined') {
2146 locals = {};
2147 }
2148
2149 if (typeof fn !== 'function') {
2150 if (isArray$1(fn)) {
2151 fn = annotate(fn.slice());
2152 } else {
2153 throw new Error('Cannot invoke "' + fn + '". Expected a function!');
2154 }
2155 }
2156
2157 var inject = fn.$inject || parseAnnotations(fn);
2158 var dependencies = inject.map(function(dep) {
2159 if (hasOwnProp(locals, dep)) {
2160 return locals[dep];
2161 } else {
2162 return get(dep);
2163 }
2164 });
2165
2166 return {
2167 fn: fn,
2168 dependencies: dependencies
2169 };
2170 };
2171
2172 var instantiate = function(Type) {
2173 var def = fnDef(Type);
2174
2175 var fn = def.fn,
2176 dependencies = def.dependencies;
2177
2178 // instantiate var args constructor
2179 var Constructor = Function.prototype.bind.apply(fn, [ null ].concat(dependencies));
2180
2181 return new Constructor();
2182 };
2183
2184 var invoke = function(func, context, locals) {
2185 var def = fnDef(func, locals);
2186
2187 var fn = def.fn,
2188 dependencies = def.dependencies;
2189
2190 return fn.apply(context, dependencies);
2191 };
2192
2193
2194 var createPrivateInjectorFactory = function(privateChildInjector) {
2195 return annotate(function(key) {
2196 return privateChildInjector.get(key);
2197 });
2198 };
2199
2200 var createChild = function(modules, forceNewInstances) {
2201 if (forceNewInstances && forceNewInstances.length) {
2202 var fromParentModule = Object.create(null);
2203 var matchedScopes = Object.create(null);
2204
2205 var privateInjectorsCache = [];
2206 var privateChildInjectors = [];
2207 var privateChildFactories = [];
2208
2209 var provider;
2210 var cacheIdx;
2211 var privateChildInjector;
2212 var privateChildInjectorFactory;
2213 for (var name in providers) {
2214 provider = providers[name];
2215
2216 if (forceNewInstances.indexOf(name) !== -1) {
2217 if (provider[2] === 'private') {
2218 cacheIdx = privateInjectorsCache.indexOf(provider[3]);
2219 if (cacheIdx === -1) {
2220 privateChildInjector = provider[3].createChild([], forceNewInstances);
2221 privateChildInjectorFactory = createPrivateInjectorFactory(privateChildInjector);
2222 privateInjectorsCache.push(provider[3]);
2223 privateChildInjectors.push(privateChildInjector);
2224 privateChildFactories.push(privateChildInjectorFactory);
2225 fromParentModule[name] = [privateChildInjectorFactory, name, 'private', privateChildInjector];
2226 } else {
2227 fromParentModule[name] = [privateChildFactories[cacheIdx], name, 'private', privateChildInjectors[cacheIdx]];
2228 }
2229 } else {
2230 fromParentModule[name] = [provider[2], provider[1]];
2231 }
2232 matchedScopes[name] = true;
2233 }
2234
2235 if ((provider[2] === 'factory' || provider[2] === 'type') && provider[1].$scope) {
2236 /* jshint -W083 */
2237 forceNewInstances.forEach(function(scope) {
2238 if (provider[1].$scope.indexOf(scope) !== -1) {
2239 fromParentModule[name] = [provider[2], provider[1]];
2240 matchedScopes[scope] = true;
2241 }
2242 });
2243 }
2244 }
2245
2246 forceNewInstances.forEach(function(scope) {
2247 if (!matchedScopes[scope]) {
2248 throw new Error('No provider for "' + scope + '". Cannot use provider from the parent!');
2249 }
2250 });
2251
2252 modules.unshift(fromParentModule);
2253 }
2254
2255 return new Injector(modules, self);
2256 };
2257
2258 var factoryMap = {
2259 factory: invoke,
2260 type: instantiate,
2261 value: function(value) {
2262 return value;
2263 }
2264 };
2265
2266 modules.forEach(function(module) {
2267
2268 function arrayUnwrap(type, value) {
2269 if (type !== 'value' && isArray$1(value)) {
2270 value = annotate(value.slice());
2271 }
2272
2273 return value;
2274 }
2275
2276 // TODO(vojta): handle wrong inputs (modules)
2277 if (module instanceof Module) {
2278 module.forEach(function(provider) {
2279 var name = provider[0];
2280 var type = provider[1];
2281 var value = provider[2];
2282
2283 providers[name] = [factoryMap[type], arrayUnwrap(type, value), type];
2284 });
2285 } else if (typeof module === 'object') {
2286 if (module.__exports__) {
2287 var clonedModule = Object.keys(module).reduce(function(m, key) {
2288 if (key.substring(0, 2) !== '__') {
2289 m[key] = module[key];
2290 }
2291 return m;
2292 }, Object.create(null));
2293
2294 var privateInjector = new Injector((module.__modules__ || []).concat([clonedModule]), self);
2295 var getFromPrivateInjector = annotate(function(key) {
2296 return privateInjector.get(key);
2297 });
2298 module.__exports__.forEach(function(key) {
2299 providers[key] = [getFromPrivateInjector, key, 'private', privateInjector];
2300 });
2301 } else {
2302 Object.keys(module).forEach(function(name) {
2303 if (module[name][2] === 'private') {
2304 providers[name] = module[name];
2305 return;
2306 }
2307
2308 var type = module[name][0];
2309 var value = module[name][1];
2310
2311 providers[name] = [factoryMap[type], arrayUnwrap(type, value), type];
2312 });
2313 }
2314 }
2315 });
2316
2317 // public API
2318 this.get = get;
2319 this.invoke = invoke;
2320 this.instantiate = instantiate;
2321 this.createChild = createChild;
2322 }
2323
2324 var DEFAULT_RENDER_PRIORITY$1 = 1000;
2325
2326 /**
2327 * The base implementation of shape and connection renderers.
2328 *
2329 * @param {EventBus} eventBus
2330 * @param {number} [renderPriority=1000]
2331 */
2332 function BaseRenderer(eventBus, renderPriority) {
2333 var self = this;
2334
2335 renderPriority = renderPriority || DEFAULT_RENDER_PRIORITY$1;
2336
2337 eventBus.on([ 'render.shape', 'render.connection' ], renderPriority, function(evt, context) {
2338 var type = evt.type,
2339 element = context.element,
2340 visuals = context.gfx;
2341
2342 if (self.canRender(element)) {
2343 if (type === 'render.shape') {
2344 return self.drawShape(visuals, element);
2345 } else {
2346 return self.drawConnection(visuals, element);
2347 }
2348 }
2349 });
2350
2351 eventBus.on([ 'render.getShapePath', 'render.getConnectionPath'], renderPriority, function(evt, element) {
2352 if (self.canRender(element)) {
2353 if (evt.type === 'render.getShapePath') {
2354 return self.getShapePath(element);
2355 } else {
2356 return self.getConnectionPath(element);
2357 }
2358 }
2359 });
2360 }
2361
2362 /**
2363 * Should check whether *this* renderer can render
2364 * the element/connection.
2365 *
2366 * @param {element} element
2367 *
2368 * @returns {boolean}
2369 */
2370 BaseRenderer.prototype.canRender = function() {};
2371
2372 /**
2373 * Provides the shape's snap svg element to be drawn on the `canvas`.
2374 *
2375 * @param {djs.Graphics} visuals
2376 * @param {Shape} shape
2377 *
2378 * @returns {Snap.svg} [returns a Snap.svg paper element ]
2379 */
2380 BaseRenderer.prototype.drawShape = function() {};
2381
2382 /**
2383 * Provides the shape's snap svg element to be drawn on the `canvas`.
2384 *
2385 * @param {djs.Graphics} visuals
2386 * @param {Connection} connection
2387 *
2388 * @returns {Snap.svg} [returns a Snap.svg paper element ]
2389 */
2390 BaseRenderer.prototype.drawConnection = function() {};
2391
2392 /**
2393 * Gets the SVG path of a shape that represents it's visual bounds.
2394 *
2395 * @param {Shape} shape
2396 *
2397 * @return {string} svg path
2398 */
2399 BaseRenderer.prototype.getShapePath = function() {};
2400
2401 /**
2402 * Gets the SVG path of a connection that represents it's visual bounds.
2403 *
2404 * @param {Connection} connection
2405 *
2406 * @return {string} svg path
2407 */
2408 BaseRenderer.prototype.getConnectionPath = function() {};
2409
2410 function componentsToPath(elements) {
2411 return elements.join(',').replace(/,?([A-z]),?/g, '$1');
2412 }
2413
2414 function toSVGPoints(points) {
2415 var result = '';
2416
2417 for (var i = 0, p; (p = points[i]); i++) {
2418 result += p.x + ',' + p.y + ' ';
2419 }
2420
2421 return result;
2422 }
2423
2424 function createLine(points, attrs) {
2425
2426 var line = create$1('polyline');
2427 attr(line, { points: toSVGPoints(points) });
2428
2429 if (attrs) {
2430 attr(line, attrs);
2431 }
2432
2433 return line;
2434 }
2435
2436 function updateLine(gfx, points) {
2437 attr(gfx, { points: toSVGPoints(points) });
2438
2439 return gfx;
2440 }
2441
2442 /**
2443 * Get parent elements.
2444 *
2445 * @param {Array<djs.model.base>} elements
2446 *
2447 * @returns {Array<djs.model.Base>}
2448 */
2449 function getParents$1(elements) {
2450
2451 // find elements that are not children of any other elements
2452 return filter(elements, function(element) {
2453 return !find(elements, function(e) {
2454 return e !== element && getParent$1(element, e);
2455 });
2456 });
2457 }
2458
2459
2460 function getParent$1(element, parent) {
2461 if (!parent) {
2462 return;
2463 }
2464
2465 if (element === parent) {
2466 return parent;
2467 }
2468
2469 if (!element.parent) {
2470 return;
2471 }
2472
2473 return getParent$1(element.parent, parent);
2474 }
2475
2476
2477 /**
2478 * Adds an element to a collection and returns true if the
2479 * element was added.
2480 *
2481 * @param {Array<Object>} elements
2482 * @param {Object} e
2483 * @param {boolean} unique
2484 */
2485 function add$1(elements, e, unique) {
2486 var canAdd = !unique || elements.indexOf(e) === -1;
2487
2488 if (canAdd) {
2489 elements.push(e);
2490 }
2491
2492 return canAdd;
2493 }
2494
2495
2496 /**
2497 * Iterate over each element in a collection, calling the iterator function `fn`
2498 * with (element, index, recursionDepth).
2499 *
2500 * Recurse into all elements that are returned by `fn`.
2501 *
2502 * @param {Object|Array<Object>} elements
2503 * @param {Function} fn iterator function called with (element, index, recursionDepth)
2504 * @param {number} [depth] maximum recursion depth
2505 */
2506 function eachElement(elements, fn, depth) {
2507
2508 depth = depth || 0;
2509
2510 if (!isArray$2(elements)) {
2511 elements = [ elements ];
2512 }
2513
2514 forEach(elements, function(s, i) {
2515 var filter = fn(s, i, depth);
2516
2517 if (isArray$2(filter) && filter.length) {
2518 eachElement(filter, fn, depth + 1);
2519 }
2520 });
2521 }
2522
2523
2524 /**
2525 * Collects self + child elements up to a given depth from a list of elements.
2526 *
2527 * @param {djs.model.Base|Array<djs.model.Base>} elements the elements to select the children from
2528 * @param {boolean} unique whether to return a unique result set (no duplicates)
2529 * @param {number} maxDepth the depth to search through or -1 for infinite
2530 *
2531 * @return {Array<djs.model.Base>} found elements
2532 */
2533 function selfAndChildren(elements, unique, maxDepth) {
2534 var result = [],
2535 processedChildren = [];
2536
2537 eachElement(elements, function(element, i, depth) {
2538 add$1(result, element, unique);
2539
2540 var children = element.children;
2541
2542 // max traversal depth not reached yet
2543 if (maxDepth === -1 || depth < maxDepth) {
2544
2545 // children exist && children not yet processed
2546 if (children && add$1(processedChildren, children, unique)) {
2547 return children;
2548 }
2549 }
2550 });
2551
2552 return result;
2553 }
2554
2555
2556 /**
2557 * Return self + ALL children for a number of elements
2558 *
2559 * @param {Array<djs.model.Base>} elements to query
2560 * @param {boolean} allowDuplicates to allow duplicates in the result set
2561 *
2562 * @return {Array<djs.model.Base>} the collected elements
2563 */
2564 function selfAndAllChildren(elements, allowDuplicates) {
2565 return selfAndChildren(elements, !allowDuplicates, -1);
2566 }
2567
2568
2569 /**
2570 * Gets the the closure for all selected elements,
2571 * their enclosed children and connections.
2572 *
2573 * @param {Array<djs.model.Base>} elements
2574 * @param {boolean} [isTopLevel=true]
2575 * @param {Object} [existingClosure]
2576 *
2577 * @return {Object} newClosure
2578 */
2579 function getClosure(elements, isTopLevel, closure) {
2580
2581 if (isUndefined$1(isTopLevel)) {
2582 isTopLevel = true;
2583 }
2584
2585 if (isObject(isTopLevel)) {
2586 closure = isTopLevel;
2587 isTopLevel = true;
2588 }
2589
2590
2591 closure = closure || {};
2592
2593 var allShapes = copyObject(closure.allShapes),
2594 allConnections = copyObject(closure.allConnections),
2595 enclosedElements = copyObject(closure.enclosedElements),
2596 enclosedConnections = copyObject(closure.enclosedConnections);
2597
2598 var topLevel = copyObject(
2599 closure.topLevel,
2600 isTopLevel && groupBy(elements, function(e) { return e.id; })
2601 );
2602
2603
2604 function handleConnection(c) {
2605 if (topLevel[c.source.id] && topLevel[c.target.id]) {
2606 topLevel[c.id] = [ c ];
2607 }
2608
2609 // not enclosed as a child, but maybe logically
2610 // (connecting two moved elements?)
2611 if (allShapes[c.source.id] && allShapes[c.target.id]) {
2612 enclosedConnections[c.id] = enclosedElements[c.id] = c;
2613 }
2614
2615 allConnections[c.id] = c;
2616 }
2617
2618 function handleElement(element) {
2619
2620 enclosedElements[element.id] = element;
2621
2622 if (element.waypoints) {
2623
2624 // remember connection
2625 enclosedConnections[element.id] = allConnections[element.id] = element;
2626 } else {
2627
2628 // remember shape
2629 allShapes[element.id] = element;
2630
2631 // remember all connections
2632 forEach(element.incoming, handleConnection);
2633
2634 forEach(element.outgoing, handleConnection);
2635
2636 // recurse into children
2637 return element.children;
2638 }
2639 }
2640
2641 eachElement(elements, handleElement);
2642
2643 return {
2644 allShapes: allShapes,
2645 allConnections: allConnections,
2646 topLevel: topLevel,
2647 enclosedConnections: enclosedConnections,
2648 enclosedElements: enclosedElements
2649 };
2650 }
2651
2652 /**
2653 * Returns the surrounding bbox for all elements in
2654 * the array or the element primitive.
2655 *
2656 * @param {Array<djs.model.Shape>|djs.model.Shape} elements
2657 * @param {boolean} stopRecursion
2658 */
2659 function getBBox(elements, stopRecursion) {
2660
2661 stopRecursion = !!stopRecursion;
2662 if (!isArray$2(elements)) {
2663 elements = [elements];
2664 }
2665
2666 var minX,
2667 minY,
2668 maxX,
2669 maxY;
2670
2671 forEach(elements, function(element) {
2672
2673 // If element is a connection the bbox must be computed first
2674 var bbox = element;
2675 if (element.waypoints && !stopRecursion) {
2676 bbox = getBBox(element.waypoints, true);
2677 }
2678
2679 var x = bbox.x,
2680 y = bbox.y,
2681 height = bbox.height || 0,
2682 width = bbox.width || 0;
2683
2684 if (x < minX || minX === undefined) {
2685 minX = x;
2686 }
2687 if (y < minY || minY === undefined) {
2688 minY = y;
2689 }
2690
2691 if ((x + width) > maxX || maxX === undefined) {
2692 maxX = x + width;
2693 }
2694 if ((y + height) > maxY || maxY === undefined) {
2695 maxY = y + height;
2696 }
2697 });
2698
2699 return {
2700 x: minX,
2701 y: minY,
2702 height: maxY - minY,
2703 width: maxX - minX
2704 };
2705 }
2706
2707
2708 /**
2709 * Returns all elements that are enclosed from the bounding box.
2710 *
2711 * * If bbox.(width|height) is not specified the method returns
2712 * all elements with element.x/y > bbox.x/y
2713 * * If only bbox.x or bbox.y is specified, method return all elements with
2714 * e.x > bbox.x or e.y > bbox.y
2715 *
2716 * @param {Array<djs.model.Shape>} elements List of Elements to search through
2717 * @param {djs.model.Shape} bbox the enclosing bbox.
2718 *
2719 * @return {Array<djs.model.Shape>} enclosed elements
2720 */
2721 function getEnclosedElements(elements, bbox) {
2722
2723 var filteredElements = {};
2724
2725 forEach(elements, function(element) {
2726
2727 var e = element;
2728
2729 if (e.waypoints) {
2730 e = getBBox(e);
2731 }
2732
2733 if (!isNumber(bbox.y) && (e.x > bbox.x)) {
2734 filteredElements[element.id] = element;
2735 }
2736 if (!isNumber(bbox.x) && (e.y > bbox.y)) {
2737 filteredElements[element.id] = element;
2738 }
2739 if (e.x > bbox.x && e.y > bbox.y) {
2740 if (isNumber(bbox.width) && isNumber(bbox.height) &&
2741 e.width + e.x < bbox.width + bbox.x &&
2742 e.height + e.y < bbox.height + bbox.y) {
2743
2744 filteredElements[element.id] = element;
2745 } else if (!isNumber(bbox.width) || !isNumber(bbox.height)) {
2746 filteredElements[element.id] = element;
2747 }
2748 }
2749 });
2750
2751 return filteredElements;
2752 }
2753
2754
2755 function getType(element) {
2756
2757 if ('waypoints' in element) {
2758 return 'connection';
2759 }
2760
2761 if ('x' in element) {
2762 return 'shape';
2763 }
2764
2765 return 'root';
2766 }
2767
2768 function isFrameElement$1(element) {
2769
2770 return !!(element && element.isFrame);
2771 }
2772
2773 // helpers ///////////////////////////////
2774
2775 function copyObject(src1, src2) {
2776 return assign({}, src1 || {}, src2 || {});
2777 }
2778
2779 // apply default renderer with lowest possible priority
2780 // so that it only kicks in if noone else could render
2781 var DEFAULT_RENDER_PRIORITY = 1;
2782
2783 /**
2784 * The default renderer used for shapes and connections.
2785 *
2786 * @param {EventBus} eventBus
2787 * @param {Styles} styles
2788 */
2789 function DefaultRenderer(eventBus, styles) {
2790
2791 //
2792 BaseRenderer.call(this, eventBus, DEFAULT_RENDER_PRIORITY);
2793
2794 this.CONNECTION_STYLE = styles.style([ 'no-fill' ], { strokeWidth: 5, stroke: 'fuchsia' });
2795 this.SHAPE_STYLE = styles.style({ fill: 'white', stroke: 'fuchsia', strokeWidth: 2 });
2796 this.FRAME_STYLE = styles.style([ 'no-fill' ], { stroke: 'fuchsia', strokeDasharray: 4, strokeWidth: 2 });
2797 }
2798
2799 inherits$1(DefaultRenderer, BaseRenderer);
2800
2801
2802 DefaultRenderer.prototype.canRender = function() {
2803 return true;
2804 };
2805
2806 DefaultRenderer.prototype.drawShape = function drawShape(visuals, element) {
2807 var rect = create$1('rect');
2808
2809 attr(rect, {
2810 x: 0,
2811 y: 0,
2812 width: element.width || 0,
2813 height: element.height || 0
2814 });
2815
2816 if (isFrameElement$1(element)) {
2817 attr(rect, this.FRAME_STYLE);
2818 } else {
2819 attr(rect, this.SHAPE_STYLE);
2820 }
2821
2822 append(visuals, rect);
2823
2824 return rect;
2825 };
2826
2827 DefaultRenderer.prototype.drawConnection = function drawConnection(visuals, connection) {
2828
2829 var line = createLine(connection.waypoints, this.CONNECTION_STYLE);
2830 append(visuals, line);
2831
2832 return line;
2833 };
2834
2835 DefaultRenderer.prototype.getShapePath = function getShapePath(shape) {
2836
2837 var x = shape.x,
2838 y = shape.y,
2839 width = shape.width,
2840 height = shape.height;
2841
2842 var shapePath = [
2843 ['M', x, y],
2844 ['l', width, 0],
2845 ['l', 0, height],
2846 ['l', -width, 0],
2847 ['z']
2848 ];
2849
2850 return componentsToPath(shapePath);
2851 };
2852
2853 DefaultRenderer.prototype.getConnectionPath = function getConnectionPath(connection) {
2854 var waypoints = connection.waypoints;
2855
2856 var idx, point, connectionPath = [];
2857
2858 for (idx = 0; (point = waypoints[idx]); idx++) {
2859
2860 // take invisible docking into account
2861 // when creating the path
2862 point = point.original || point;
2863
2864 connectionPath.push([ idx === 0 ? 'M' : 'L', point.x, point.y ]);
2865 }
2866
2867 return componentsToPath(connectionPath);
2868 };
2869
2870
2871 DefaultRenderer.$inject = [ 'eventBus', 'styles' ];
2872
2873 /**
2874 * A component that manages shape styles
2875 */
2876 function Styles() {
2877
2878 var defaultTraits = {
2879
2880 'no-fill': {
2881 fill: 'none'
2882 },
2883 'no-border': {
2884 strokeOpacity: 0.0
2885 },
2886 'no-events': {
2887 pointerEvents: 'none'
2888 }
2889 };
2890
2891 var self = this;
2892
2893 /**
2894 * Builds a style definition from a className, a list of traits and an object of additional attributes.
2895 *
2896 * @param {string} className
2897 * @param {Array<string>} traits
2898 * @param {Object} additionalAttrs
2899 *
2900 * @return {Object} the style defintion
2901 */
2902 this.cls = function(className, traits, additionalAttrs) {
2903 var attrs = this.style(traits, additionalAttrs);
2904
2905 return assign(attrs, { 'class': className });
2906 };
2907
2908 /**
2909 * Builds a style definition from a list of traits and an object of additional attributes.
2910 *
2911 * @param {Array<string>} traits
2912 * @param {Object} additionalAttrs
2913 *
2914 * @return {Object} the style defintion
2915 */
2916 this.style = function(traits, additionalAttrs) {
2917
2918 if (!isArray$2(traits) && !additionalAttrs) {
2919 additionalAttrs = traits;
2920 traits = [];
2921 }
2922
2923 var attrs = reduce(traits, function(attrs, t) {
2924 return assign(attrs, defaultTraits[t] || {});
2925 }, {});
2926
2927 return additionalAttrs ? assign(attrs, additionalAttrs) : attrs;
2928 };
2929
2930 this.computeStyle = function(custom, traits, defaultStyles) {
2931 if (!isArray$2(traits)) {
2932 defaultStyles = traits;
2933 traits = [];
2934 }
2935
2936 return self.style(traits || [], assign({}, defaultStyles, custom || {}));
2937 };
2938 }
2939
2940 var DrawModule$1 = {
2941 __init__: [ 'defaultRenderer' ],
2942 defaultRenderer: [ 'type', DefaultRenderer ],
2943 styles: [ 'type', Styles ]
2944 };
2945
2946 /**
2947 * Failsafe remove an element from a collection
2948 *
2949 * @param {Array<Object>} [collection]
2950 * @param {Object} [element]
2951 *
2952 * @return {number} the previous index of the element
2953 */
2954 function remove(collection, element) {
2955
2956 if (!collection || !element) {
2957 return -1;
2958 }
2959
2960 var idx = collection.indexOf(element);
2961
2962 if (idx !== -1) {
2963 collection.splice(idx, 1);
2964 }
2965
2966 return idx;
2967 }
2968
2969 /**
2970 * Fail save add an element to the given connection, ensuring
2971 * it does not yet exist.
2972 *
2973 * @param {Array<Object>} collection
2974 * @param {Object} element
2975 * @param {number} idx
2976 */
2977 function add(collection, element, idx) {
2978
2979 if (!collection || !element) {
2980 return;
2981 }
2982
2983 if (typeof idx !== 'number') {
2984 idx = -1;
2985 }
2986
2987 var currentIdx = collection.indexOf(element);
2988
2989 if (currentIdx !== -1) {
2990
2991 if (currentIdx === idx) {
2992
2993 // nothing to do, position has not changed
2994 return;
2995 } else {
2996
2997 if (idx !== -1) {
2998
2999 // remove from current position
3000 collection.splice(currentIdx, 1);
3001 } else {
3002
3003 // already exists in collection
3004 return;
3005 }
3006 }
3007 }
3008
3009 if (idx !== -1) {
3010
3011 // insert at specified position
3012 collection.splice(idx, 0, element);
3013 } else {
3014
3015 // push to end
3016 collection.push(element);
3017 }
3018 }
3019
3020
3021 /**
3022 * Fail save get the index of an element in a collection.
3023 *
3024 * @param {Array<Object>} collection
3025 * @param {Object} element
3026 *
3027 * @return {number} the index or -1 if collection or element do
3028 * not exist or the element is not contained.
3029 */
3030 function indexOf(collection, element) {
3031
3032 if (!collection || !element) {
3033 return -1;
3034 }
3035
3036 return collection.indexOf(element);
3037 }
3038
3039 /**
3040 * Computes the distance between two points
3041 *
3042 * @param {Point} p
3043 * @param {Point} q
3044 *
3045 * @return {number} distance
3046 */
3047 function pointDistance(a, b) {
3048 if (!a || !b) {
3049 return -1;
3050 }
3051
3052 return Math.sqrt(
3053 Math.pow(a.x - b.x, 2) +
3054 Math.pow(a.y - b.y, 2)
3055 );
3056 }
3057
3058
3059 /**
3060 * Returns true if the point r is on the line between p and q
3061 *
3062 * @param {Point} p
3063 * @param {Point} q
3064 * @param {Point} r
3065 * @param {number} [accuracy=5] accuracy for points on line check (lower is better)
3066 *
3067 * @return {boolean}
3068 */
3069 function pointsOnLine(p, q, r, accuracy) {
3070
3071 if (typeof accuracy === 'undefined') {
3072 accuracy = 5;
3073 }
3074
3075 if (!p || !q || !r) {
3076 return false;
3077 }
3078
3079 var val = (q.x - p.x) * (r.y - p.y) - (q.y - p.y) * (r.x - p.x),
3080 dist = pointDistance(p, q);
3081
3082 // @see http://stackoverflow.com/a/907491/412190
3083 return Math.abs(val / dist) <= accuracy;
3084 }
3085
3086
3087 var ALIGNED_THRESHOLD = 2;
3088
3089 /**
3090 * Check whether two points are horizontally or vertically aligned.
3091 *
3092 * @param {Array<Point>|Point}
3093 * @param {Point}
3094 *
3095 * @return {string|boolean}
3096 */
3097 function pointsAligned(a, b) {
3098 var points;
3099
3100 if (isArray$2(a)) {
3101 points = a;
3102 } else {
3103 points = [ a, b ];
3104 }
3105
3106 if (pointsAlignedHorizontally(points)) {
3107 return 'h';
3108 }
3109
3110 if (pointsAlignedVertically(points)) {
3111 return 'v';
3112 }
3113
3114 return false;
3115 }
3116
3117 function pointsAlignedHorizontally(a, b) {
3118 var points;
3119
3120 if (isArray$2(a)) {
3121 points = a;
3122 } else {
3123 points = [ a, b ];
3124 }
3125
3126 var firstPoint = points.slice().shift();
3127
3128 return every(points, function(point) {
3129 return Math.abs(firstPoint.y - point.y) <= ALIGNED_THRESHOLD;
3130 });
3131 }
3132
3133 function pointsAlignedVertically(a, b) {
3134 var points;
3135
3136 if (isArray$2(a)) {
3137 points = a;
3138 } else {
3139 points = [ a, b ];
3140 }
3141
3142 var firstPoint = points.slice().shift();
3143
3144 return every(points, function(point) {
3145 return Math.abs(firstPoint.x - point.x) <= ALIGNED_THRESHOLD;
3146 });
3147 }
3148
3149
3150
3151 /**
3152 * Returns true if the point p is inside the rectangle rect
3153 *
3154 * @param {Point} p
3155 * @param {Rect} rect
3156 * @param {number} tolerance
3157 *
3158 * @return {boolean}
3159 */
3160 function pointInRect(p, rect, tolerance) {
3161 tolerance = tolerance || 0;
3162
3163 return p.x > rect.x - tolerance &&
3164 p.y > rect.y - tolerance &&
3165 p.x < rect.x + rect.width + tolerance &&
3166 p.y < rect.y + rect.height + tolerance;
3167 }
3168
3169 /**
3170 * Returns a point in the middle of points p and q
3171 *
3172 * @param {Point} p
3173 * @param {Point} q
3174 *
3175 * @return {Point} middle point
3176 */
3177 function getMidPoint(p, q) {
3178 return {
3179 x: Math.round(p.x + ((q.x - p.x) / 2.0)),
3180 y: Math.round(p.y + ((q.y - p.y) / 2.0))
3181 };
3182 }
3183
3184 /**
3185 * This file contains source code adapted from Snap.svg (licensed Apache-2.0).
3186 *
3187 * @see https://github.com/adobe-webplatform/Snap.svg/blob/master/src/path.js
3188 */
3189
3190 /* eslint no-fallthrough: "off" */
3191
3192 var p2s = /,?([a-z]),?/gi,
3193 toFloat = parseFloat,
3194 math = Math,
3195 PI = math.PI,
3196 mmin = math.min,
3197 mmax = math.max,
3198 pow = math.pow,
3199 abs$7 = math.abs,
3200 pathCommand = /([a-z])[\s,]*((-?\d*\.?\d*(?:e[-+]?\d+)?[\s]*,?[\s]*)+)/ig,
3201 pathValues = /(-?\d*\.?\d*(?:e[-+]?\d+)?)[\s]*,?[\s]*/ig;
3202
3203 var isArray = Array.isArray || function(o) { return o instanceof Array; };
3204
3205 function hasProperty(obj, property) {
3206 return Object.prototype.hasOwnProperty.call(obj, property);
3207 }
3208
3209 function clone(obj) {
3210
3211 if (typeof obj == 'function' || Object(obj) !== obj) {
3212 return obj;
3213 }
3214
3215 var res = new obj.constructor;
3216
3217 for (var key in obj) {
3218 if (hasProperty(obj, key)) {
3219 res[key] = clone(obj[key]);
3220 }
3221 }
3222
3223 return res;
3224 }
3225
3226 function repush(array, item) {
3227 for (var i = 0, ii = array.length; i < ii; i++) if (array[i] === item) {
3228 return array.push(array.splice(i, 1)[0]);
3229 }
3230 }
3231
3232 function cacher(f) {
3233
3234 function newf() {
3235
3236 var arg = Array.prototype.slice.call(arguments, 0),
3237 args = arg.join('\u2400'),
3238 cache = newf.cache = newf.cache || {},
3239 count = newf.count = newf.count || [];
3240
3241 if (hasProperty(cache, args)) {
3242 repush(count, args);
3243 return cache[args];
3244 }
3245
3246 count.length >= 1e3 && delete cache[count.shift()];
3247 count.push(args);
3248 cache[args] = f.apply(0, arg);
3249
3250 return cache[args];
3251 }
3252 return newf;
3253 }
3254
3255 function parsePathString(pathString) {
3256
3257 if (!pathString) {
3258 return null;
3259 }
3260
3261 var pth = paths(pathString);
3262
3263 if (pth.arr) {
3264 return clone(pth.arr);
3265 }
3266
3267 var paramCounts = { a: 7, c: 6, h: 1, l: 2, m: 2, q: 4, s: 4, t: 2, v: 1, z: 0 },
3268 data = [];
3269
3270 if (isArray(pathString) && isArray(pathString[0])) { // rough assumption
3271 data = clone(pathString);
3272 }
3273
3274 if (!data.length) {
3275
3276 String(pathString).replace(pathCommand, function(a, b, c) {
3277 var params = [],
3278 name = b.toLowerCase();
3279
3280 c.replace(pathValues, function(a, b) {
3281 b && params.push(+b);
3282 });
3283
3284 if (name == 'm' && params.length > 2) {
3285 data.push([b].concat(params.splice(0, 2)));
3286 name = 'l';
3287 b = b == 'm' ? 'l' : 'L';
3288 }
3289
3290 while (params.length >= paramCounts[name]) {
3291 data.push([b].concat(params.splice(0, paramCounts[name])));
3292 if (!paramCounts[name]) {
3293 break;
3294 }
3295 }
3296 });
3297 }
3298
3299 data.toString = paths.toString;
3300 pth.arr = clone(data);
3301
3302 return data;
3303 }
3304
3305 function paths(ps) {
3306 var p = paths.ps = paths.ps || {};
3307
3308 if (p[ps]) {
3309 p[ps].sleep = 100;
3310 } else {
3311 p[ps] = {
3312 sleep: 100
3313 };
3314 }
3315
3316 setTimeout(function() {
3317 for (var key in p) {
3318 if (hasProperty(p, key) && key != ps) {
3319 p[key].sleep--;
3320 !p[key].sleep && delete p[key];
3321 }
3322 }
3323 });
3324
3325 return p[ps];
3326 }
3327
3328 function rectBBox(x, y, width, height) {
3329
3330 if (arguments.length === 1) {
3331 y = x.y;
3332 width = x.width;
3333 height = x.height;
3334 x = x.x;
3335 }
3336
3337 return {
3338 x: x,
3339 y: y,
3340 width: width,
3341 height: height,
3342 x2: x + width,
3343 y2: y + height
3344 };
3345 }
3346
3347 function pathToString() {
3348 return this.join(',').replace(p2s, '$1');
3349 }
3350
3351 function pathClone(pathArray) {
3352 var res = clone(pathArray);
3353 res.toString = pathToString;
3354 return res;
3355 }
3356
3357 function findDotsAtSegment(p1x, p1y, c1x, c1y, c2x, c2y, p2x, p2y, t) {
3358 var t1 = 1 - t,
3359 t13 = pow(t1, 3),
3360 t12 = pow(t1, 2),
3361 t2 = t * t,
3362 t3 = t2 * t,
3363 x = t13 * p1x + t12 * 3 * t * c1x + t1 * 3 * t * t * c2x + t3 * p2x,
3364 y = t13 * p1y + t12 * 3 * t * c1y + t1 * 3 * t * t * c2y + t3 * p2y;
3365
3366 return {
3367 x: fixError(x),
3368 y: fixError(y)
3369 };
3370 }
3371
3372 function bezierBBox(points) {
3373
3374 var bbox = curveBBox.apply(null, points);
3375
3376 return rectBBox(
3377 bbox.x0,
3378 bbox.y0,
3379 bbox.x1 - bbox.x0,
3380 bbox.y1 - bbox.y0
3381 );
3382 }
3383
3384 function isPointInsideBBox$2(bbox, x, y) {
3385 return x >= bbox.x &&
3386 x <= bbox.x + bbox.width &&
3387 y >= bbox.y &&
3388 y <= bbox.y + bbox.height;
3389 }
3390
3391 function isBBoxIntersect(bbox1, bbox2) {
3392 bbox1 = rectBBox(bbox1);
3393 bbox2 = rectBBox(bbox2);
3394 return isPointInsideBBox$2(bbox2, bbox1.x, bbox1.y)
3395 || isPointInsideBBox$2(bbox2, bbox1.x2, bbox1.y)
3396 || isPointInsideBBox$2(bbox2, bbox1.x, bbox1.y2)
3397 || isPointInsideBBox$2(bbox2, bbox1.x2, bbox1.y2)
3398 || isPointInsideBBox$2(bbox1, bbox2.x, bbox2.y)
3399 || isPointInsideBBox$2(bbox1, bbox2.x2, bbox2.y)
3400 || isPointInsideBBox$2(bbox1, bbox2.x, bbox2.y2)
3401 || isPointInsideBBox$2(bbox1, bbox2.x2, bbox2.y2)
3402 || (bbox1.x < bbox2.x2 && bbox1.x > bbox2.x
3403 || bbox2.x < bbox1.x2 && bbox2.x > bbox1.x)
3404 && (bbox1.y < bbox2.y2 && bbox1.y > bbox2.y
3405 || bbox2.y < bbox1.y2 && bbox2.y > bbox1.y);
3406 }
3407
3408 function base3(t, p1, p2, p3, p4) {
3409 var t1 = -3 * p1 + 9 * p2 - 9 * p3 + 3 * p4,
3410 t2 = t * t1 + 6 * p1 - 12 * p2 + 6 * p3;
3411 return t * t2 - 3 * p1 + 3 * p2;
3412 }
3413
3414 function bezlen(x1, y1, x2, y2, x3, y3, x4, y4, z) {
3415
3416 if (z == null) {
3417 z = 1;
3418 }
3419
3420 z = z > 1 ? 1 : z < 0 ? 0 : z;
3421
3422 var z2 = z / 2,
3423 n = 12,
3424 Tvalues = [-.1252,.1252,-.3678,.3678,-.5873,.5873,-.7699,.7699,-.9041,.9041,-.9816,.9816],
3425 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],
3426 sum = 0;
3427
3428 for (var i = 0; i < n; i++) {
3429 var ct = z2 * Tvalues[i] + z2,
3430 xbase = base3(ct, x1, x2, x3, x4),
3431 ybase = base3(ct, y1, y2, y3, y4),
3432 comb = xbase * xbase + ybase * ybase;
3433
3434 sum += Cvalues[i] * math.sqrt(comb);
3435 }
3436
3437 return z2 * sum;
3438 }
3439
3440
3441 function intersectLines(x1, y1, x2, y2, x3, y3, x4, y4) {
3442
3443 if (
3444 mmax(x1, x2) < mmin(x3, x4) ||
3445 mmin(x1, x2) > mmax(x3, x4) ||
3446 mmax(y1, y2) < mmin(y3, y4) ||
3447 mmin(y1, y2) > mmax(y3, y4)
3448 ) {
3449 return;
3450 }
3451
3452 var nx = (x1 * y2 - y1 * x2) * (x3 - x4) - (x1 - x2) * (x3 * y4 - y3 * x4),
3453 ny = (x1 * y2 - y1 * x2) * (y3 - y4) - (y1 - y2) * (x3 * y4 - y3 * x4),
3454 denominator = (x1 - x2) * (y3 - y4) - (y1 - y2) * (x3 - x4);
3455
3456 if (!denominator) {
3457 return;
3458 }
3459
3460 var px = fixError(nx / denominator),
3461 py = fixError(ny / denominator),
3462 px2 = +px.toFixed(2),
3463 py2 = +py.toFixed(2);
3464
3465 if (
3466 px2 < +mmin(x1, x2).toFixed(2) ||
3467 px2 > +mmax(x1, x2).toFixed(2) ||
3468 px2 < +mmin(x3, x4).toFixed(2) ||
3469 px2 > +mmax(x3, x4).toFixed(2) ||
3470 py2 < +mmin(y1, y2).toFixed(2) ||
3471 py2 > +mmax(y1, y2).toFixed(2) ||
3472 py2 < +mmin(y3, y4).toFixed(2) ||
3473 py2 > +mmax(y3, y4).toFixed(2)
3474 ) {
3475 return;
3476 }
3477
3478 return { x: px, y: py };
3479 }
3480
3481 function fixError(number) {
3482 return Math.round(number * 100000000000) / 100000000000;
3483 }
3484
3485 function findBezierIntersections(bez1, bez2, justCount) {
3486 var bbox1 = bezierBBox(bez1),
3487 bbox2 = bezierBBox(bez2);
3488
3489 if (!isBBoxIntersect(bbox1, bbox2)) {
3490 return justCount ? 0 : [];
3491 }
3492
3493 // As an optimization, lines will have only 1 segment
3494
3495 var l1 = bezlen.apply(0, bez1),
3496 l2 = bezlen.apply(0, bez2),
3497 n1 = isLine(bez1) ? 1 : ~~(l1 / 5) || 1,
3498 n2 = isLine(bez2) ? 1 : ~~(l2 / 5) || 1,
3499 dots1 = [],
3500 dots2 = [],
3501 xy = {},
3502 res = justCount ? 0 : [];
3503
3504 for (var i = 0; i < n1 + 1; i++) {
3505 var p = findDotsAtSegment.apply(0, bez1.concat(i / n1));
3506 dots1.push({ x: p.x, y: p.y, t: i / n1 });
3507 }
3508
3509 for (i = 0; i < n2 + 1; i++) {
3510 p = findDotsAtSegment.apply(0, bez2.concat(i / n2));
3511 dots2.push({ x: p.x, y: p.y, t: i / n2 });
3512 }
3513
3514 for (i = 0; i < n1; i++) {
3515
3516 for (var j = 0; j < n2; j++) {
3517 var di = dots1[i],
3518 di1 = dots1[i + 1],
3519 dj = dots2[j],
3520 dj1 = dots2[j + 1],
3521 ci = abs$7(di1.x - di.x) < .01 ? 'y' : 'x',
3522 cj = abs$7(dj1.x - dj.x) < .01 ? 'y' : 'x',
3523 is = intersectLines(di.x, di.y, di1.x, di1.y, dj.x, dj.y, dj1.x, dj1.y),
3524 key;
3525
3526 if (is) {
3527 key = is.x.toFixed(9) + '#' + is.y.toFixed(9);
3528
3529 if (xy[key]) {
3530 continue;
3531 }
3532
3533 xy[key] = true;
3534
3535 var t1 = di.t + abs$7((is[ci] - di[ci]) / (di1[ci] - di[ci])) * (di1.t - di.t),
3536 t2 = dj.t + abs$7((is[cj] - dj[cj]) / (dj1[cj] - dj[cj])) * (dj1.t - dj.t);
3537
3538 if (t1 >= 0 && t1 <= 1 && t2 >= 0 && t2 <= 1) {
3539
3540 if (justCount) {
3541 res++;
3542 } else {
3543 res.push({
3544 x: is.x,
3545 y: is.y,
3546 t1: t1,
3547 t2: t2
3548 });
3549 }
3550 }
3551 }
3552 }
3553 }
3554
3555 return res;
3556 }
3557
3558
3559 /**
3560 * Find or counts the intersections between two SVG paths.
3561 *
3562 * Returns a number in counting mode and a list of intersections otherwise.
3563 *
3564 * A single intersection entry contains the intersection coordinates (x, y)
3565 * as well as additional information regarding the intersecting segments
3566 * on each path (segment1, segment2) and the relative location of the
3567 * intersection on these segments (t1, t2).
3568 *
3569 * The path may be an SVG path string or a list of path components
3570 * such as `[ [ 'M', 0, 10 ], [ 'L', 20, 0 ] ]`.
3571 *
3572 * @example
3573 *
3574 * var intersections = findPathIntersections(
3575 * 'M0,0L100,100',
3576 * [ [ 'M', 0, 100 ], [ 'L', 100, 0 ] ]
3577 * );
3578 *
3579 * // intersections = [
3580 * // { x: 50, y: 50, segment1: 1, segment2: 1, t1: 0.5, t2: 0.5 }
3581 * // ]
3582 *
3583 * @param {String|Array<PathDef>} path1
3584 * @param {String|Array<PathDef>} path2
3585 * @param {Boolean} [justCount=false]
3586 *
3587 * @return {Array<Intersection>|Number}
3588 */
3589 function findPathIntersections(path1, path2, justCount) {
3590 path1 = pathToCurve(path1);
3591 path2 = pathToCurve(path2);
3592
3593 var x1, y1, x2, y2, x1m, y1m, x2m, y2m, bez1, bez2,
3594 res = justCount ? 0 : [];
3595
3596 for (var i = 0, ii = path1.length; i < ii; i++) {
3597 var pi = path1[i];
3598
3599 if (pi[0] == 'M') {
3600 x1 = x1m = pi[1];
3601 y1 = y1m = pi[2];
3602 } else {
3603
3604 if (pi[0] == 'C') {
3605 bez1 = [x1, y1].concat(pi.slice(1));
3606 x1 = bez1[6];
3607 y1 = bez1[7];
3608 } else {
3609 bez1 = [x1, y1, x1, y1, x1m, y1m, x1m, y1m];
3610 x1 = x1m;
3611 y1 = y1m;
3612 }
3613
3614 for (var j = 0, jj = path2.length; j < jj; j++) {
3615 var pj = path2[j];
3616
3617 if (pj[0] == 'M') {
3618 x2 = x2m = pj[1];
3619 y2 = y2m = pj[2];
3620 } else {
3621
3622 if (pj[0] == 'C') {
3623 bez2 = [x2, y2].concat(pj.slice(1));
3624 x2 = bez2[6];
3625 y2 = bez2[7];
3626 } else {
3627 bez2 = [x2, y2, x2, y2, x2m, y2m, x2m, y2m];
3628 x2 = x2m;
3629 y2 = y2m;
3630 }
3631
3632 var intr = findBezierIntersections(bez1, bez2, justCount);
3633
3634 if (justCount) {
3635 res += intr;
3636 } else {
3637
3638 for (var k = 0, kk = intr.length; k < kk; k++) {
3639 intr[k].segment1 = i;
3640 intr[k].segment2 = j;
3641 intr[k].bez1 = bez1;
3642 intr[k].bez2 = bez2;
3643 }
3644
3645 res = res.concat(intr);
3646 }
3647 }
3648 }
3649 }
3650 }
3651
3652 return res;
3653 }
3654
3655
3656 function pathToAbsolute(pathArray) {
3657 var pth = paths(pathArray);
3658
3659 if (pth.abs) {
3660 return pathClone(pth.abs);
3661 }
3662
3663 if (!isArray(pathArray) || !isArray(pathArray && pathArray[0])) { // rough assumption
3664 pathArray = parsePathString(pathArray);
3665 }
3666
3667 if (!pathArray || !pathArray.length) {
3668 return [['M', 0, 0]];
3669 }
3670
3671 var res = [],
3672 x = 0,
3673 y = 0,
3674 mx = 0,
3675 my = 0,
3676 start = 0,
3677 pa0;
3678
3679 if (pathArray[0][0] == 'M') {
3680 x = +pathArray[0][1];
3681 y = +pathArray[0][2];
3682 mx = x;
3683 my = y;
3684 start++;
3685 res[0] = ['M', x, y];
3686 }
3687
3688 for (var r, pa, i = start, ii = pathArray.length; i < ii; i++) {
3689 res.push(r = []);
3690 pa = pathArray[i];
3691 pa0 = pa[0];
3692
3693 if (pa0 != pa0.toUpperCase()) {
3694 r[0] = pa0.toUpperCase();
3695
3696 switch (r[0]) {
3697 case 'A':
3698 r[1] = pa[1];
3699 r[2] = pa[2];
3700 r[3] = pa[3];
3701 r[4] = pa[4];
3702 r[5] = pa[5];
3703 r[6] = +pa[6] + x;
3704 r[7] = +pa[7] + y;
3705 break;
3706 case 'V':
3707 r[1] = +pa[1] + y;
3708 break;
3709 case 'H':
3710 r[1] = +pa[1] + x;
3711 break;
3712 case 'M':
3713 mx = +pa[1] + x;
3714 my = +pa[2] + y;
3715 default:
3716 for (var j = 1, jj = pa.length; j < jj; j++) {
3717 r[j] = +pa[j] + ((j % 2) ? x : y);
3718 }
3719 }
3720 } else {
3721 for (var k = 0, kk = pa.length; k < kk; k++) {
3722 r[k] = pa[k];
3723 }
3724 }
3725 pa0 = pa0.toUpperCase();
3726
3727 switch (r[0]) {
3728 case 'Z':
3729 x = +mx;
3730 y = +my;
3731 break;
3732 case 'H':
3733 x = r[1];
3734 break;
3735 case 'V':
3736 y = r[1];
3737 break;
3738 case 'M':
3739 mx = r[r.length - 2];
3740 my = r[r.length - 1];
3741 default:
3742 x = r[r.length - 2];
3743 y = r[r.length - 1];
3744 }
3745 }
3746
3747 res.toString = pathToString;
3748 pth.abs = pathClone(res);
3749
3750 return res;
3751 }
3752
3753 function isLine(bez) {
3754 return (
3755 bez[0] === bez[2] &&
3756 bez[1] === bez[3] &&
3757 bez[4] === bez[6] &&
3758 bez[5] === bez[7]
3759 );
3760 }
3761
3762 function lineToCurve(x1, y1, x2, y2) {
3763 return [
3764 x1, y1, x2,
3765 y2, x2, y2
3766 ];
3767 }
3768
3769 function qubicToCurve(x1, y1, ax, ay, x2, y2) {
3770 var _13 = 1 / 3,
3771 _23 = 2 / 3;
3772
3773 return [
3774 _13 * x1 + _23 * ax,
3775 _13 * y1 + _23 * ay,
3776 _13 * x2 + _23 * ax,
3777 _13 * y2 + _23 * ay,
3778 x2,
3779 y2
3780 ];
3781 }
3782
3783 function arcToCurve(x1, y1, rx, ry, angle, large_arc_flag, sweep_flag, x2, y2, recursive) {
3784
3785 // for more information of where this math came from visit:
3786 // http://www.w3.org/TR/SVG11/implnote.html#ArcImplementationNotes
3787 var _120 = PI * 120 / 180,
3788 rad = PI / 180 * (+angle || 0),
3789 res = [],
3790 xy,
3791 rotate = cacher(function(x, y, rad) {
3792 var X = x * math.cos(rad) - y * math.sin(rad),
3793 Y = x * math.sin(rad) + y * math.cos(rad);
3794
3795 return { x: X, y: Y };
3796 });
3797
3798 if (!recursive) {
3799 xy = rotate(x1, y1, -rad);
3800 x1 = xy.x;
3801 y1 = xy.y;
3802 xy = rotate(x2, y2, -rad);
3803 x2 = xy.x;
3804 y2 = xy.y;
3805
3806 var x = (x1 - x2) / 2,
3807 y = (y1 - y2) / 2;
3808
3809 var h = (x * x) / (rx * rx) + (y * y) / (ry * ry);
3810
3811 if (h > 1) {
3812 h = math.sqrt(h);
3813 rx = h * rx;
3814 ry = h * ry;
3815 }
3816
3817 var rx2 = rx * rx,
3818 ry2 = ry * ry,
3819 k = (large_arc_flag == sweep_flag ? -1 : 1) *
3820 math.sqrt(abs$7((rx2 * ry2 - rx2 * y * y - ry2 * x * x) / (rx2 * y * y + ry2 * x * x))),
3821 cx = k * rx * y / ry + (x1 + x2) / 2,
3822 cy = k * -ry * x / rx + (y1 + y2) / 2,
3823 f1 = math.asin(((y1 - cy) / ry).toFixed(9)),
3824 f2 = math.asin(((y2 - cy) / ry).toFixed(9));
3825
3826 f1 = x1 < cx ? PI - f1 : f1;
3827 f2 = x2 < cx ? PI - f2 : f2;
3828 f1 < 0 && (f1 = PI * 2 + f1);
3829 f2 < 0 && (f2 = PI * 2 + f2);
3830
3831 if (sweep_flag && f1 > f2) {
3832 f1 = f1 - PI * 2;
3833 }
3834 if (!sweep_flag && f2 > f1) {
3835 f2 = f2 - PI * 2;
3836 }
3837 } else {
3838 f1 = recursive[0];
3839 f2 = recursive[1];
3840 cx = recursive[2];
3841 cy = recursive[3];
3842 }
3843
3844 var df = f2 - f1;
3845
3846 if (abs$7(df) > _120) {
3847 var f2old = f2,
3848 x2old = x2,
3849 y2old = y2;
3850
3851 f2 = f1 + _120 * (sweep_flag && f2 > f1 ? 1 : -1);
3852 x2 = cx + rx * math.cos(f2);
3853 y2 = cy + ry * math.sin(f2);
3854 res = arcToCurve(x2, y2, rx, ry, angle, 0, sweep_flag, x2old, y2old, [f2, f2old, cx, cy]);
3855 }
3856
3857 df = f2 - f1;
3858
3859 var c1 = math.cos(f1),
3860 s1 = math.sin(f1),
3861 c2 = math.cos(f2),
3862 s2 = math.sin(f2),
3863 t = math.tan(df / 4),
3864 hx = 4 / 3 * rx * t,
3865 hy = 4 / 3 * ry * t,
3866 m1 = [x1, y1],
3867 m2 = [x1 + hx * s1, y1 - hy * c1],
3868 m3 = [x2 + hx * s2, y2 - hy * c2],
3869 m4 = [x2, y2];
3870
3871 m2[0] = 2 * m1[0] - m2[0];
3872 m2[1] = 2 * m1[1] - m2[1];
3873
3874 if (recursive) {
3875 return [m2, m3, m4].concat(res);
3876 } else {
3877 res = [m2, m3, m4].concat(res).join().split(',');
3878 var newres = [];
3879
3880 for (var i = 0, ii = res.length; i < ii; i++) {
3881 newres[i] = i % 2 ? rotate(res[i - 1], res[i], rad).y : rotate(res[i], res[i + 1], rad).x;
3882 }
3883
3884 return newres;
3885 }
3886 }
3887
3888 // Returns bounding box of cubic bezier curve.
3889 // Source: http://blog.hackers-cafe.net/2009/06/how-to-calculate-bezier-curves-bounding.html
3890 // Original version: NISHIO Hirokazu
3891 // Modifications: https://github.com/timo22345
3892 function curveBBox(x0, y0, x1, y1, x2, y2, x3, y3) {
3893 var tvalues = [],
3894 bounds = [[], []],
3895 a, b, c, t, t1, t2, b2ac, sqrtb2ac;
3896
3897 for (var i = 0; i < 2; ++i) {
3898
3899 if (i == 0) {
3900 b = 6 * x0 - 12 * x1 + 6 * x2;
3901 a = -3 * x0 + 9 * x1 - 9 * x2 + 3 * x3;
3902 c = 3 * x1 - 3 * x0;
3903 } else {
3904 b = 6 * y0 - 12 * y1 + 6 * y2;
3905 a = -3 * y0 + 9 * y1 - 9 * y2 + 3 * y3;
3906 c = 3 * y1 - 3 * y0;
3907 }
3908
3909 if (abs$7(a) < 1e-12) {
3910
3911 if (abs$7(b) < 1e-12) {
3912 continue;
3913 }
3914
3915 t = -c / b;
3916
3917 if (0 < t && t < 1) {
3918 tvalues.push(t);
3919 }
3920
3921 continue;
3922 }
3923
3924 b2ac = b * b - 4 * c * a;
3925 sqrtb2ac = math.sqrt(b2ac);
3926
3927 if (b2ac < 0) {
3928 continue;
3929 }
3930
3931 t1 = (-b + sqrtb2ac) / (2 * a);
3932
3933 if (0 < t1 && t1 < 1) {
3934 tvalues.push(t1);
3935 }
3936
3937 t2 = (-b - sqrtb2ac) / (2 * a);
3938
3939 if (0 < t2 && t2 < 1) {
3940 tvalues.push(t2);
3941 }
3942 }
3943
3944 var j = tvalues.length,
3945 jlen = j,
3946 mt;
3947
3948 while (j--) {
3949 t = tvalues[j];
3950 mt = 1 - t;
3951 bounds[0][j] = (mt * mt * mt * x0) + (3 * mt * mt * t * x1) + (3 * mt * t * t * x2) + (t * t * t * x3);
3952 bounds[1][j] = (mt * mt * mt * y0) + (3 * mt * mt * t * y1) + (3 * mt * t * t * y2) + (t * t * t * y3);
3953 }
3954
3955 bounds[0][jlen] = x0;
3956 bounds[1][jlen] = y0;
3957 bounds[0][jlen + 1] = x3;
3958 bounds[1][jlen + 1] = y3;
3959 bounds[0].length = bounds[1].length = jlen + 2;
3960
3961 return {
3962 x0: mmin.apply(0, bounds[0]),
3963 y0: mmin.apply(0, bounds[1]),
3964 x1: mmax.apply(0, bounds[0]),
3965 y1: mmax.apply(0, bounds[1])
3966 };
3967 }
3968
3969 function pathToCurve(path) {
3970
3971 var pth = paths(path);
3972
3973 // return cached curve, if existing
3974 if (pth.curve) {
3975 return pathClone(pth.curve);
3976 }
3977
3978 var curvedPath = pathToAbsolute(path),
3979 attrs = { x: 0, y: 0, bx: 0, by: 0, X: 0, Y: 0, qx: null, qy: null },
3980 processPath = function(path, d, pathCommand) {
3981 var nx, ny;
3982
3983 if (!path) {
3984 return ['C', d.x, d.y, d.x, d.y, d.x, d.y];
3985 }
3986
3987 !(path[0] in { T: 1, Q: 1 }) && (d.qx = d.qy = null);
3988
3989 switch (path[0]) {
3990 case 'M':
3991 d.X = path[1];
3992 d.Y = path[2];
3993 break;
3994 case 'A':
3995 path = ['C'].concat(arcToCurve.apply(0, [d.x, d.y].concat(path.slice(1))));
3996 break;
3997 case 'S':
3998 if (pathCommand == 'C' || pathCommand == 'S') {
3999
4000 // In 'S' case we have to take into account, if the previous command is C/S.
4001 nx = d.x * 2 - d.bx;
4002
4003 // And reflect the previous
4004 ny = d.y * 2 - d.by;
4005
4006 // command's control point relative to the current point.
4007 }
4008 else {
4009
4010 // or some else or nothing
4011 nx = d.x;
4012 ny = d.y;
4013 }
4014 path = ['C', nx, ny].concat(path.slice(1));
4015 break;
4016 case 'T':
4017 if (pathCommand == 'Q' || pathCommand == 'T') {
4018
4019 // In 'T' case we have to take into account, if the previous command is Q/T.
4020 d.qx = d.x * 2 - d.qx;
4021
4022 // And make a reflection similar
4023 d.qy = d.y * 2 - d.qy;
4024
4025 // to case 'S'.
4026 }
4027 else {
4028
4029 // or something else or nothing
4030 d.qx = d.x;
4031 d.qy = d.y;
4032 }
4033 path = ['C'].concat(qubicToCurve(d.x, d.y, d.qx, d.qy, path[1], path[2]));
4034 break;
4035 case 'Q':
4036 d.qx = path[1];
4037 d.qy = path[2];
4038 path = ['C'].concat(qubicToCurve(d.x, d.y, path[1], path[2], path[3], path[4]));
4039 break;
4040 case 'L':
4041 path = ['C'].concat(lineToCurve(d.x, d.y, path[1], path[2]));
4042 break;
4043 case 'H':
4044 path = ['C'].concat(lineToCurve(d.x, d.y, path[1], d.y));
4045 break;
4046 case 'V':
4047 path = ['C'].concat(lineToCurve(d.x, d.y, d.x, path[1]));
4048 break;
4049 case 'Z':
4050 path = ['C'].concat(lineToCurve(d.x, d.y, d.X, d.Y));
4051 break;
4052 }
4053
4054 return path;
4055 },
4056
4057 fixArc = function(pp, i) {
4058
4059 if (pp[i].length > 7) {
4060 pp[i].shift();
4061 var pi = pp[i];
4062
4063 while (pi.length) {
4064 pathCommands[i] = 'A'; // if created multiple C:s, their original seg is saved
4065 pp.splice(i++, 0, ['C'].concat(pi.splice(0, 6)));
4066 }
4067
4068 pp.splice(i, 1);
4069 ii = curvedPath.length;
4070 }
4071 },
4072
4073 pathCommands = [], // path commands of original path p
4074 pfirst = '', // temporary holder for original path command
4075 pathCommand = ''; // holder for previous path command of original path
4076
4077 for (var i = 0, ii = curvedPath.length; i < ii; i++) {
4078 curvedPath[i] && (pfirst = curvedPath[i][0]); // save current path command
4079
4080 if (pfirst != 'C') // C is not saved yet, because it may be result of conversion
4081 {
4082 pathCommands[i] = pfirst; // Save current path command
4083 i && (pathCommand = pathCommands[i - 1]); // Get previous path command pathCommand
4084 }
4085 curvedPath[i] = processPath(curvedPath[i], attrs, pathCommand); // Previous path command is inputted to processPath
4086
4087 if (pathCommands[i] != 'A' && pfirst == 'C') pathCommands[i] = 'C'; // A is the only command
4088 // which may produce multiple C:s
4089 // so we have to make sure that C is also C in original path
4090
4091 fixArc(curvedPath, i); // fixArc adds also the right amount of A:s to pathCommands
4092
4093 var seg = curvedPath[i],
4094 seglen = seg.length;
4095
4096 attrs.x = seg[seglen - 2];
4097 attrs.y = seg[seglen - 1];
4098 attrs.bx = toFloat(seg[seglen - 4]) || attrs.x;
4099 attrs.by = toFloat(seg[seglen - 3]) || attrs.y;
4100 }
4101
4102 // cache curve
4103 pth.curve = pathClone(curvedPath);
4104
4105 return curvedPath;
4106 }
4107
4108 var intersect = findPathIntersections;
4109
4110 function roundBounds(bounds) {
4111 return {
4112 x: Math.round(bounds.x),
4113 y: Math.round(bounds.y),
4114 width: Math.round(bounds.width),
4115 height: Math.round(bounds.height)
4116 };
4117 }
4118
4119
4120 function roundPoint(point) {
4121
4122 return {
4123 x: Math.round(point.x),
4124 y: Math.round(point.y)
4125 };
4126 }
4127
4128
4129 /**
4130 * Convert the given bounds to a { top, left, bottom, right } descriptor.
4131 *
4132 * @param {Bounds|Point} bounds
4133 *
4134 * @return {Object}
4135 */
4136 function asTRBL(bounds) {
4137 return {
4138 top: bounds.y,
4139 right: bounds.x + (bounds.width || 0),
4140 bottom: bounds.y + (bounds.height || 0),
4141 left: bounds.x
4142 };
4143 }
4144
4145
4146 /**
4147 * Convert a { top, left, bottom, right } to an objects bounds.
4148 *
4149 * @param {Object} trbl
4150 *
4151 * @return {Bounds}
4152 */
4153 function asBounds(trbl) {
4154 return {
4155 x: trbl.left,
4156 y: trbl.top,
4157 width: trbl.right - trbl.left,
4158 height: trbl.bottom - trbl.top
4159 };
4160 }
4161
4162
4163 /**
4164 * Get the mid of the given bounds or point.
4165 *
4166 * @param {Bounds|Point} bounds
4167 *
4168 * @return {Point}
4169 */
4170 function getMid(bounds) {
4171 return roundPoint({
4172 x: bounds.x + (bounds.width || 0) / 2,
4173 y: bounds.y + (bounds.height || 0) / 2
4174 });
4175 }
4176
4177
4178 // orientation utils //////////////////////
4179
4180 /**
4181 * Get orientation of the given rectangle with respect to
4182 * the reference rectangle.
4183 *
4184 * A padding (positive or negative) may be passed to influence
4185 * horizontal / vertical orientation and intersection.
4186 *
4187 * @param {Bounds} rect
4188 * @param {Bounds} reference
4189 * @param {Point|number} padding
4190 *
4191 * @return {string} the orientation; one of top, top-left, left, ..., bottom, right or intersect.
4192 */
4193 function getOrientation(rect, reference, padding) {
4194
4195 padding = padding || 0;
4196
4197 // make sure we can use an object, too
4198 // for individual { x, y } padding
4199 if (!isObject(padding)) {
4200 padding = { x: padding, y: padding };
4201 }
4202
4203
4204 var rectOrientation = asTRBL(rect),
4205 referenceOrientation = asTRBL(reference);
4206
4207 var top = rectOrientation.bottom + padding.y <= referenceOrientation.top,
4208 right = rectOrientation.left - padding.x >= referenceOrientation.right,
4209 bottom = rectOrientation.top - padding.y >= referenceOrientation.bottom,
4210 left = rectOrientation.right + padding.x <= referenceOrientation.left;
4211
4212 var vertical = top ? 'top' : (bottom ? 'bottom' : null),
4213 horizontal = left ? 'left' : (right ? 'right' : null);
4214
4215 if (horizontal && vertical) {
4216 return vertical + '-' + horizontal;
4217 } else {
4218 return horizontal || vertical || 'intersect';
4219 }
4220 }
4221
4222
4223 // intersection utils //////////////////////
4224
4225 /**
4226 * Get intersection between an element and a line path.
4227 *
4228 * @param {PathDef} elementPath
4229 * @param {PathDef} linePath
4230 * @param {boolean} cropStart crop from start or end
4231 *
4232 * @return {Point}
4233 */
4234 function getElementLineIntersection(elementPath, linePath, cropStart) {
4235
4236 var intersections = getIntersections(elementPath, linePath);
4237
4238 // recognize intersections
4239 // only one -> choose
4240 // two close together -> choose first
4241 // two or more distinct -> pull out appropriate one
4242 // none -> ok (fallback to point itself)
4243 if (intersections.length === 1) {
4244 return roundPoint(intersections[0]);
4245 } else if (intersections.length === 2 && pointDistance(intersections[0], intersections[1]) < 1) {
4246 return roundPoint(intersections[0]);
4247 } else if (intersections.length > 1) {
4248
4249 // sort by intersections based on connection segment +
4250 // distance from start
4251 intersections = sortBy(intersections, function(i) {
4252 var distance = Math.floor(i.t2 * 100) || 1;
4253
4254 distance = 100 - distance;
4255
4256 distance = (distance < 10 ? '0' : '') + distance;
4257
4258 // create a sort string that makes sure we sort
4259 // line segment ASC + line segment position DESC (for cropStart)
4260 // line segment ASC + line segment position ASC (for cropEnd)
4261 return i.segment2 + '#' + distance;
4262 });
4263
4264 return roundPoint(intersections[cropStart ? 0 : intersections.length - 1]);
4265 }
4266
4267 return null;
4268 }
4269
4270
4271 function getIntersections(a, b) {
4272 return intersect(a, b);
4273 }
4274
4275
4276 function filterRedundantWaypoints(waypoints) {
4277
4278 // alter copy of waypoints, not original
4279 waypoints = waypoints.slice();
4280
4281 var idx = 0,
4282 point,
4283 previousPoint,
4284 nextPoint;
4285
4286 while (waypoints[idx]) {
4287 point = waypoints[idx];
4288 previousPoint = waypoints[idx - 1];
4289 nextPoint = waypoints[idx + 1];
4290
4291 if (pointDistance(point, nextPoint) === 0 ||
4292 pointsOnLine(previousPoint, nextPoint, point)) {
4293
4294 // remove point, if overlapping with {nextPoint}
4295 // or on line with {previousPoint} -> {point} -> {nextPoint}
4296 waypoints.splice(idx, 1);
4297 } else {
4298 idx++;
4299 }
4300 }
4301
4302 return waypoints;
4303 }
4304
4305 function round$b(number, resolution) {
4306 return Math.round(number * resolution) / resolution;
4307 }
4308
4309 function ensurePx(number) {
4310 return isNumber(number) ? number + 'px' : number;
4311 }
4312
4313 function findRoot(element) {
4314 while (element.parent) {
4315 element = element.parent;
4316 }
4317
4318 return element;
4319 }
4320
4321 /**
4322 * Creates a HTML container element for a SVG element with
4323 * the given configuration
4324 *
4325 * @param {Object} options
4326 * @return {HTMLElement} the container element
4327 */
4328 function createContainer(options) {
4329
4330 options = assign({}, { width: '100%', height: '100%' }, options);
4331
4332 var container = options.container || document.body;
4333
4334 // create a <div> around the svg element with the respective size
4335 // this way we can always get the correct container size
4336 // (this is impossible for <svg> elements at the moment)
4337 var parent = document.createElement('div');
4338 parent.setAttribute('class', 'djs-container');
4339
4340 assign(parent.style, {
4341 position: 'relative',
4342 overflow: 'hidden',
4343 width: ensurePx(options.width),
4344 height: ensurePx(options.height)
4345 });
4346
4347 container.appendChild(parent);
4348
4349 return parent;
4350 }
4351
4352 function createGroup(parent, cls, childIndex) {
4353 var group = create$1('g');
4354 classes(group).add(cls);
4355
4356 var index = childIndex !== undefined ? childIndex : parent.childNodes.length - 1;
4357
4358 // must ensure second argument is node or _null_
4359 // cf. https://developer.mozilla.org/en-US/docs/Web/API/Node/insertBefore
4360 parent.insertBefore(group, parent.childNodes[index] || null);
4361
4362 return group;
4363 }
4364
4365 var BASE_LAYER = 'base';
4366 var HIDDEN_MARKER = 'djs-element-hidden';
4367
4368
4369 var REQUIRED_MODEL_ATTRS = {
4370 shape: [ 'x', 'y', 'width', 'height' ],
4371 connection: [ 'waypoints' ]
4372 };
4373
4374 /**
4375 * The main drawing canvas.
4376 *
4377 * @class
4378 * @constructor
4379 *
4380 * @emits Canvas#canvas.init
4381 *
4382 * @param {Object} config
4383 * @param {EventBus} eventBus
4384 * @param {GraphicsFactory} graphicsFactory
4385 * @param {ElementRegistry} elementRegistry
4386 */
4387 function Canvas(config, eventBus, graphicsFactory, elementRegistry) {
4388
4389 this._eventBus = eventBus;
4390 this._elementRegistry = elementRegistry;
4391 this._graphicsFactory = graphicsFactory;
4392
4393 this._init(config || {});
4394 }
4395
4396 Canvas.$inject = [
4397 'config.canvas',
4398 'eventBus',
4399 'graphicsFactory',
4400 'elementRegistry'
4401 ];
4402
4403 /**
4404 * Creates a <svg> element that is wrapped into a <div>.
4405 * This way we are always able to correctly figure out the size of the svg element
4406 * by querying the parent node.
4407
4408 * (It is not possible to get the size of a svg element cross browser @ 2014-04-01)
4409
4410 * <div class="djs-container" style="width: {desired-width}, height: {desired-height}">
4411 * <svg width="100%" height="100%">
4412 * ...
4413 * </svg>
4414 * </div>
4415 */
4416 Canvas.prototype._init = function(config) {
4417
4418 var eventBus = this._eventBus;
4419
4420 // html container
4421 var container = this._container = createContainer(config);
4422
4423 var svg = this._svg = create$1('svg');
4424 attr(svg, { width: '100%', height: '100%' });
4425
4426 append(container, svg);
4427
4428 var viewport = this._viewport = createGroup(svg, 'viewport');
4429
4430 this._layers = {};
4431 this._planes = {};
4432
4433 // debounce canvas.viewbox.changed events
4434 // for smoother diagram interaction
4435 if (config.deferUpdate !== false) {
4436 this._viewboxChanged = debounce(bind$2(this._viewboxChanged, this), 300);
4437 }
4438
4439 eventBus.on('diagram.init', function() {
4440
4441 /**
4442 * An event indicating that the canvas is ready to be drawn on.
4443 *
4444 * @memberOf Canvas
4445 *
4446 * @event canvas.init
4447 *
4448 * @type {Object}
4449 * @property {SVGElement} svg the created svg element
4450 * @property {SVGElement} viewport the direct parent of diagram elements and shapes
4451 */
4452 eventBus.fire('canvas.init', {
4453 svg: svg,
4454 viewport: viewport
4455 });
4456
4457 }, this);
4458
4459 // reset viewbox on shape changes to
4460 // recompute the viewbox
4461 eventBus.on([
4462 'shape.added',
4463 'connection.added',
4464 'shape.removed',
4465 'connection.removed',
4466 'elements.changed'
4467 ], function() {
4468 delete this._cachedViewbox;
4469 }, this);
4470
4471 eventBus.on('diagram.destroy', 500, this._destroy, this);
4472 eventBus.on('diagram.clear', 500, this._clear, this);
4473 };
4474
4475 Canvas.prototype._destroy = function(emit) {
4476 this._eventBus.fire('canvas.destroy', {
4477 svg: this._svg,
4478 viewport: this._viewport
4479 });
4480
4481 var parent = this._container.parentNode;
4482
4483 if (parent) {
4484 parent.removeChild(this._container);
4485 }
4486
4487 delete this._svg;
4488 delete this._container;
4489 delete this._layers;
4490 delete this._planes;
4491 delete this._activePlane;
4492 delete this._viewport;
4493 };
4494
4495 Canvas.prototype._clear = function() {
4496
4497 var self = this;
4498
4499 var allElements = this._elementRegistry.getAll();
4500
4501 // remove all elements
4502 allElements.forEach(function(element) {
4503 var type = getType(element);
4504
4505 if (type === 'root') {
4506 self.setRootElementForPlane(null, self.findPlane(element), true);
4507 } else {
4508 self._removeElement(element, type);
4509 }
4510 });
4511
4512 // remove all planes
4513 this._activePlane = null;
4514 this._planes = {};
4515
4516 // force recomputation of view box
4517 delete this._cachedViewbox;
4518 };
4519
4520 /**
4521 * Returns the default layer on which
4522 * all elements are drawn.
4523 *
4524 * @returns {SVGElement}
4525 */
4526 Canvas.prototype.getDefaultLayer = function() {
4527 return this.getLayer(BASE_LAYER);
4528 };
4529
4530 /**
4531 * Returns a layer that is used to draw elements
4532 * or annotations on it.
4533 *
4534 * Non-existing layers retrieved through this method
4535 * will be created. During creation, the optional index
4536 * may be used to create layers below or above existing layers.
4537 * A layer with a certain index is always created above all
4538 * existing layers with the same index.
4539 *
4540 * @param {string} name
4541 * @param {number} index
4542 *
4543 * @returns {SVGElement}
4544 */
4545 Canvas.prototype.getLayer = function(name, index) {
4546
4547 if (!name) {
4548 throw new Error('must specify a name');
4549 }
4550
4551 var layer = this._layers[name];
4552
4553 if (!layer) {
4554 layer = this._layers[name] = this._createLayer(name, index);
4555 }
4556
4557 // throw an error if layer creation / retrival is
4558 // requested on different index
4559 if (typeof index !== 'undefined' && layer.index !== index) {
4560 throw new Error('layer <' + name + '> already created at index <' + index + '>');
4561 }
4562
4563 return layer.group;
4564 };
4565
4566 /**
4567 * Creates a given layer and returns it.
4568 *
4569 * @param {string} name
4570 * @param {number} [index=0]
4571 *
4572 * @return {Object} layer descriptor with { index, group: SVGGroup }
4573 */
4574 Canvas.prototype._createLayer = function(name, index) {
4575
4576 if (!index) {
4577 index = 0;
4578 }
4579
4580 var childIndex = reduce(this._layers, function(childIndex, layer) {
4581 if (index >= layer.index) {
4582 childIndex++;
4583 }
4584
4585 return childIndex;
4586 }, 0);
4587
4588 return {
4589 group: createGroup(this._viewport, 'layer-' + name, childIndex),
4590 index: index
4591 };
4592
4593 };
4594
4595 /**
4596 * Returns a plane that is used to draw elements on it.
4597 *
4598 * @param {string} name
4599 *
4600 * @return {Object} plane descriptor with { layer, rootElement, name }
4601 */
4602 Canvas.prototype.getPlane = function(name) {
4603 if (!name) {
4604 throw new Error('must specify a name');
4605 }
4606
4607 var plane = this._planes[name];
4608
4609 return plane;
4610 };
4611
4612 /**
4613 * Creates a plane that is used to draw elements on it. If no
4614 * root element is provided, an implicit root will be used.
4615 *
4616 * @param {string} name
4617 * @param {Object|djs.model.Root} [rootElement] optional root element
4618 *
4619 * @return {Object} plane descriptor with { layer, rootElement, name }
4620 */
4621 Canvas.prototype.createPlane = function(name, rootElement) {
4622 if (!name) {
4623 throw new Error('must specify a name');
4624 }
4625
4626 if (this._planes[name]) {
4627 throw new Error('plane ' + name + ' already exists');
4628 }
4629
4630 if (!rootElement) {
4631 rootElement = {
4632 id: '__implicitroot' + name,
4633 children: [],
4634 isImplicit: true
4635 };
4636 }
4637
4638 var svgLayer = this.getLayer(name);
4639 classes(svgLayer).add(HIDDEN_MARKER);
4640
4641 var plane = this._planes[name] = {
4642 layer: svgLayer,
4643 name: name,
4644 rootElement: null
4645 };
4646
4647 this.setRootElementForPlane(rootElement, plane);
4648
4649 return plane;
4650 };
4651
4652 /**
4653 * Sets the active plane and hides the previously active plane.
4654 *
4655 * @param {string|Object} plane
4656 *
4657 * @return {Object} plane descriptor with { layer, rootElement, name }
4658 */
4659 Canvas.prototype.setActivePlane = function(plane) {
4660 if (!plane) {
4661 throw new Error('must specify a plane');
4662 }
4663
4664 if (typeof plane === 'string') {
4665 plane = this.getPlane(plane);
4666 }
4667
4668 // hide previous Plane
4669 if (this._activePlane) {
4670 classes(this._activePlane.layer).add(HIDDEN_MARKER);
4671 }
4672
4673 this._activePlane = plane;
4674
4675 // show current Plane
4676 classes(plane.layer).remove(HIDDEN_MARKER);
4677
4678 if (plane.rootElement) {
4679 this._elementRegistry.updateGraphics(plane.rootElement, this._svg, true);
4680 }
4681
4682 this._eventBus.fire('plane.set', { plane: plane });
4683
4684 return plane;
4685 };
4686
4687 /**
4688 * Returns the currently active layer
4689 *
4690 * @returns {SVGElement}
4691 */
4692
4693 Canvas.prototype.getActiveLayer = function() {
4694 return this.getActivePlane().layer;
4695 };
4696
4697 /**
4698 * Returns the currently active plane.
4699 *
4700 * @return {Object} plane descriptor with { layer, rootElement, name }
4701 */
4702 Canvas.prototype.getActivePlane = function() {
4703 var plane = this._activePlane;
4704 if (!plane) {
4705 plane = this.createPlane(BASE_LAYER);
4706 this.setActivePlane(BASE_LAYER);
4707 }
4708
4709 return plane;
4710 };
4711
4712 /**
4713 * Returns the plane which contains the given element.
4714 *
4715 * @param {string|djs.model.Base} element
4716 *
4717 * @return {Object} plane descriptor with { layer, rootElement, name }
4718 */
4719 Canvas.prototype.findPlane = function(element) {
4720 if (typeof element === 'string') {
4721 element = this._elementRegistry.get(element);
4722 }
4723
4724 var root = findRoot(element);
4725
4726 return find(this._planes, function(plane) {
4727 return plane.rootElement === root;
4728 });
4729 };
4730
4731 /**
4732 * Returns the html element that encloses the
4733 * drawing canvas.
4734 *
4735 * @return {DOMNode}
4736 */
4737 Canvas.prototype.getContainer = function() {
4738 return this._container;
4739 };
4740
4741
4742 // markers //////////////////////
4743
4744 Canvas.prototype._updateMarker = function(element, marker, add) {
4745 var container;
4746
4747 if (!element.id) {
4748 element = this._elementRegistry.get(element);
4749 }
4750
4751 // we need to access all
4752 container = this._elementRegistry._elements[element.id];
4753
4754 if (!container) {
4755 return;
4756 }
4757
4758 forEach([ container.gfx, container.secondaryGfx ], function(gfx) {
4759 if (gfx) {
4760
4761 // invoke either addClass or removeClass based on mode
4762 if (add) {
4763 classes(gfx).add(marker);
4764 } else {
4765 classes(gfx).remove(marker);
4766 }
4767 }
4768 });
4769
4770 /**
4771 * An event indicating that a marker has been updated for an element
4772 *
4773 * @event element.marker.update
4774 * @type {Object}
4775 * @property {djs.model.Element} element the shape
4776 * @property {Object} gfx the graphical representation of the shape
4777 * @property {string} marker
4778 * @property {boolean} add true if the marker was added, false if it got removed
4779 */
4780 this._eventBus.fire('element.marker.update', { element: element, gfx: container.gfx, marker: marker, add: !!add });
4781 };
4782
4783
4784 /**
4785 * Adds a marker to an element (basically a css class).
4786 *
4787 * Fires the element.marker.update event, making it possible to
4788 * integrate extension into the marker life-cycle, too.
4789 *
4790 * @example
4791 * canvas.addMarker('foo', 'some-marker');
4792 *
4793 * var fooGfx = canvas.getGraphics('foo');
4794 *
4795 * fooGfx; // <g class="... some-marker"> ... </g>
4796 *
4797 * @param {string|djs.model.Base} element
4798 * @param {string} marker
4799 */
4800 Canvas.prototype.addMarker = function(element, marker) {
4801 this._updateMarker(element, marker, true);
4802 };
4803
4804
4805 /**
4806 * Remove a marker from an element.
4807 *
4808 * Fires the element.marker.update event, making it possible to
4809 * integrate extension into the marker life-cycle, too.
4810 *
4811 * @param {string|djs.model.Base} element
4812 * @param {string} marker
4813 */
4814 Canvas.prototype.removeMarker = function(element, marker) {
4815 this._updateMarker(element, marker, false);
4816 };
4817
4818 /**
4819 * Check the existence of a marker on element.
4820 *
4821 * @param {string|djs.model.Base} element
4822 * @param {string} marker
4823 */
4824 Canvas.prototype.hasMarker = function(element, marker) {
4825 if (!element.id) {
4826 element = this._elementRegistry.get(element);
4827 }
4828
4829 var gfx = this.getGraphics(element);
4830
4831 return classes(gfx).has(marker);
4832 };
4833
4834 /**
4835 * Toggles a marker on an element.
4836 *
4837 * Fires the element.marker.update event, making it possible to
4838 * integrate extension into the marker life-cycle, too.
4839 *
4840 * @param {string|djs.model.Base} element
4841 * @param {string} marker
4842 */
4843 Canvas.prototype.toggleMarker = function(element, marker) {
4844 if (this.hasMarker(element, marker)) {
4845 this.removeMarker(element, marker);
4846 } else {
4847 this.addMarker(element, marker);
4848 }
4849 };
4850
4851 Canvas.prototype.getRootElement = function() {
4852 var plane = this.getActivePlane();
4853
4854 return plane.rootElement;
4855 };
4856
4857
4858
4859 // root element handling //////////////////////
4860
4861 /**
4862 * Sets a given element as the new root element for the canvas
4863 * and returns the new root element.
4864 *
4865 * @param {Object|djs.model.Root} element
4866 * @param {boolean} [override] whether to override the current root element, if any
4867 *
4868 * @return {Object|djs.model.Root} new root element
4869 */
4870 Canvas.prototype.setRootElement = function(element, override) {
4871 var activePlane = this._activePlane;
4872
4873 if (activePlane) {
4874 return this.setRootElementForPlane(element, activePlane, override);
4875 } else {
4876 var basePlane = this.createPlane(BASE_LAYER, element);
4877
4878 this.setActivePlane(basePlane);
4879
4880 return basePlane.rootElement;
4881 }
4882 };
4883
4884
4885 /**
4886 * Sets a given element as the new root element for the canvas
4887 * and returns the new root element.
4888 *
4889 * @param {Object|djs.model.Root} element
4890 * @param {Object|djs.model.Root} plane
4891 * @param {boolean} [override] whether to override the current root element, if any
4892 *
4893 * @return {Object|djs.model.Root} new root element
4894 */
4895 Canvas.prototype.setRootElementForPlane = function(element, plane, override) {
4896
4897 if (typeof plane === 'string') {
4898 plane = this.getPlane(plane);
4899 }
4900
4901 if (element) {
4902 this._ensureValid('root', element);
4903 }
4904
4905 var currentRoot = plane.rootElement,
4906 elementRegistry = this._elementRegistry,
4907 eventBus = this._eventBus;
4908
4909 if (currentRoot) {
4910 if (!override) {
4911 throw new Error('rootElement already set, need to specify override');
4912 }
4913
4914 // simulate element remove event sequence
4915 eventBus.fire('root.remove', { element: currentRoot });
4916 eventBus.fire('root.removed', { element: currentRoot });
4917
4918 elementRegistry.remove(currentRoot);
4919 }
4920
4921 if (element) {
4922 var gfx = plane.layer;
4923
4924 // resemble element add event sequence
4925 eventBus.fire('root.add', { element: element });
4926
4927 elementRegistry.add(element, gfx);
4928
4929 eventBus.fire('root.added', { element: element, gfx: gfx });
4930
4931 // associate SVG with root element when active
4932 if (plane === this._activePlane) {
4933 this._elementRegistry.updateGraphics(element, this._svg, true);
4934 }
4935 }
4936
4937 plane.rootElement = element;
4938
4939 return element;
4940 };
4941
4942 // add functionality //////////////////////
4943
4944 Canvas.prototype._ensureValid = function(type, element) {
4945 if (!element.id) {
4946 throw new Error('element must have an id');
4947 }
4948
4949 if (this._elementRegistry.get(element.id)) {
4950 throw new Error('element with id ' + element.id + ' already exists');
4951 }
4952
4953 var requiredAttrs = REQUIRED_MODEL_ATTRS[type];
4954
4955 var valid = every(requiredAttrs, function(attr) {
4956 return typeof element[attr] !== 'undefined';
4957 });
4958
4959 if (!valid) {
4960 throw new Error(
4961 'must supply { ' + requiredAttrs.join(', ') + ' } with ' + type);
4962 }
4963 };
4964
4965 Canvas.prototype._setParent = function(element, parent, parentIndex) {
4966 add(parent.children, element, parentIndex);
4967 element.parent = parent;
4968 };
4969
4970 /**
4971 * Adds an element to the canvas.
4972 *
4973 * This wires the parent <-> child relationship between the element and
4974 * a explicitly specified parent or an implicit root element.
4975 *
4976 * During add it emits the events
4977 *
4978 * * <{type}.add> (element, parent)
4979 * * <{type}.added> (element, gfx)
4980 *
4981 * Extensions may hook into these events to perform their magic.
4982 *
4983 * @param {string} type
4984 * @param {Object|djs.model.Base} element
4985 * @param {Object|djs.model.Base} [parent]
4986 * @param {number} [parentIndex]
4987 *
4988 * @return {Object|djs.model.Base} the added element
4989 */
4990 Canvas.prototype._addElement = function(type, element, parent, parentIndex) {
4991
4992 parent = parent || this.getRootElement();
4993
4994 var eventBus = this._eventBus,
4995 graphicsFactory = this._graphicsFactory;
4996
4997 this._ensureValid(type, element);
4998
4999 eventBus.fire(type + '.add', { element: element, parent: parent });
5000
5001 this._setParent(element, parent, parentIndex);
5002
5003 // create graphics
5004 var gfx = graphicsFactory.create(type, element, parentIndex);
5005
5006 this._elementRegistry.add(element, gfx);
5007
5008 // update its visual
5009 graphicsFactory.update(type, element, gfx);
5010
5011 eventBus.fire(type + '.added', { element: element, gfx: gfx });
5012
5013 return element;
5014 };
5015
5016 /**
5017 * Adds a shape to the canvas
5018 *
5019 * @param {Object|djs.model.Shape} shape to add to the diagram
5020 * @param {djs.model.Base} [parent]
5021 * @param {number} [parentIndex]
5022 *
5023 * @return {djs.model.Shape} the added shape
5024 */
5025 Canvas.prototype.addShape = function(shape, parent, parentIndex) {
5026 return this._addElement('shape', shape, parent, parentIndex);
5027 };
5028
5029 /**
5030 * Adds a connection to the canvas
5031 *
5032 * @param {Object|djs.model.Connection} connection to add to the diagram
5033 * @param {djs.model.Base} [parent]
5034 * @param {number} [parentIndex]
5035 *
5036 * @return {djs.model.Connection} the added connection
5037 */
5038 Canvas.prototype.addConnection = function(connection, parent, parentIndex) {
5039 return this._addElement('connection', connection, parent, parentIndex);
5040 };
5041
5042
5043 /**
5044 * Internal remove element
5045 */
5046 Canvas.prototype._removeElement = function(element, type) {
5047
5048 var elementRegistry = this._elementRegistry,
5049 graphicsFactory = this._graphicsFactory,
5050 eventBus = this._eventBus;
5051
5052 element = elementRegistry.get(element.id || element);
5053
5054 if (!element) {
5055
5056 // element was removed already
5057 return;
5058 }
5059
5060 eventBus.fire(type + '.remove', { element: element });
5061
5062 graphicsFactory.remove(element);
5063
5064 // unset parent <-> child relationship
5065 remove(element.parent && element.parent.children, element);
5066 element.parent = null;
5067
5068 eventBus.fire(type + '.removed', { element: element });
5069
5070 elementRegistry.remove(element);
5071
5072 return element;
5073 };
5074
5075
5076 /**
5077 * Removes a shape from the canvas
5078 *
5079 * @param {string|djs.model.Shape} shape or shape id to be removed
5080 *
5081 * @return {djs.model.Shape} the removed shape
5082 */
5083 Canvas.prototype.removeShape = function(shape) {
5084
5085 /**
5086 * An event indicating that a shape is about to be removed from the canvas.
5087 *
5088 * @memberOf Canvas
5089 *
5090 * @event shape.remove
5091 * @type {Object}
5092 * @property {djs.model.Shape} element the shape descriptor
5093 * @property {Object} gfx the graphical representation of the shape
5094 */
5095
5096 /**
5097 * An event indicating that a shape has been removed from the canvas.
5098 *
5099 * @memberOf Canvas
5100 *
5101 * @event shape.removed
5102 * @type {Object}
5103 * @property {djs.model.Shape} element the shape descriptor
5104 * @property {Object} gfx the graphical representation of the shape
5105 */
5106 return this._removeElement(shape, 'shape');
5107 };
5108
5109
5110 /**
5111 * Removes a connection from the canvas
5112 *
5113 * @param {string|djs.model.Connection} connection or connection id to be removed
5114 *
5115 * @return {djs.model.Connection} the removed connection
5116 */
5117 Canvas.prototype.removeConnection = function(connection) {
5118
5119 /**
5120 * An event indicating that a connection is about to be removed from the canvas.
5121 *
5122 * @memberOf Canvas
5123 *
5124 * @event connection.remove
5125 * @type {Object}
5126 * @property {djs.model.Connection} element the connection descriptor
5127 * @property {Object} gfx the graphical representation of the connection
5128 */
5129
5130 /**
5131 * An event indicating that a connection has been removed from the canvas.
5132 *
5133 * @memberOf Canvas
5134 *
5135 * @event connection.removed
5136 * @type {Object}
5137 * @property {djs.model.Connection} element the connection descriptor
5138 * @property {Object} gfx the graphical representation of the connection
5139 */
5140 return this._removeElement(connection, 'connection');
5141 };
5142
5143
5144 /**
5145 * Return the graphical object underlaying a certain diagram element
5146 *
5147 * @param {string|djs.model.Base} element descriptor of the element
5148 * @param {boolean} [secondary=false] whether to return the secondary connected element
5149 *
5150 * @return {SVGElement}
5151 */
5152 Canvas.prototype.getGraphics = function(element, secondary) {
5153 return this._elementRegistry.getGraphics(element, secondary);
5154 };
5155
5156
5157 /**
5158 * Perform a viewbox update via a given change function.
5159 *
5160 * @param {Function} changeFn
5161 */
5162 Canvas.prototype._changeViewbox = function(changeFn) {
5163
5164 // notify others of the upcoming viewbox change
5165 this._eventBus.fire('canvas.viewbox.changing');
5166
5167 // perform actual change
5168 changeFn.apply(this);
5169
5170 // reset the cached viewbox so that
5171 // a new get operation on viewbox or zoom
5172 // triggers a viewbox re-computation
5173 this._cachedViewbox = null;
5174
5175 // notify others of the change; this step
5176 // may or may not be debounced
5177 this._viewboxChanged();
5178 };
5179
5180 Canvas.prototype._viewboxChanged = function() {
5181 this._eventBus.fire('canvas.viewbox.changed', { viewbox: this.viewbox() });
5182 };
5183
5184
5185 /**
5186 * Gets or sets the view box of the canvas, i.e. the
5187 * area that is currently displayed.
5188 *
5189 * The getter may return a cached viewbox (if it is currently
5190 * changing). To force a recomputation, pass `false` as the first argument.
5191 *
5192 * @example
5193 *
5194 * canvas.viewbox({ x: 100, y: 100, width: 500, height: 500 })
5195 *
5196 * // sets the visible area of the diagram to (100|100) -> (600|100)
5197 * // and and scales it according to the diagram width
5198 *
5199 * var viewbox = canvas.viewbox(); // pass `false` to force recomputing the box.
5200 *
5201 * console.log(viewbox);
5202 * // {
5203 * // inner: Dimensions,
5204 * // outer: Dimensions,
5205 * // scale,
5206 * // x, y,
5207 * // width, height
5208 * // }
5209 *
5210 * // if the current diagram is zoomed and scrolled, you may reset it to the
5211 * // default zoom via this method, too:
5212 *
5213 * var zoomedAndScrolledViewbox = canvas.viewbox();
5214 *
5215 * canvas.viewbox({
5216 * x: 0,
5217 * y: 0,
5218 * width: zoomedAndScrolledViewbox.outer.width,
5219 * height: zoomedAndScrolledViewbox.outer.height
5220 * });
5221 *
5222 * @param {Object} [box] the new view box to set
5223 * @param {number} box.x the top left X coordinate of the canvas visible in view box
5224 * @param {number} box.y the top left Y coordinate of the canvas visible in view box
5225 * @param {number} box.width the visible width
5226 * @param {number} box.height
5227 *
5228 * @return {Object} the current view box
5229 */
5230 Canvas.prototype.viewbox = function(box) {
5231
5232 if (box === undefined && this._cachedViewbox) {
5233 return this._cachedViewbox;
5234 }
5235
5236 var viewport = this._viewport,
5237 innerBox,
5238 outerBox = this.getSize(),
5239 matrix,
5240 transform,
5241 scale,
5242 x, y;
5243
5244 if (!box) {
5245
5246 // compute the inner box based on the
5247 // diagrams default layer. This allows us to exclude
5248 // external components, such as overlays
5249 innerBox = this.getDefaultLayer().getBBox();
5250
5251 transform = transform$1(viewport);
5252 matrix = transform ? transform.matrix : createMatrix();
5253 scale = round$b(matrix.a, 1000);
5254
5255 x = round$b(-matrix.e || 0, 1000);
5256 y = round$b(-matrix.f || 0, 1000);
5257
5258 box = this._cachedViewbox = {
5259 x: x ? x / scale : 0,
5260 y: y ? y / scale : 0,
5261 width: outerBox.width / scale,
5262 height: outerBox.height / scale,
5263 scale: scale,
5264 inner: {
5265 width: innerBox.width,
5266 height: innerBox.height,
5267 x: innerBox.x,
5268 y: innerBox.y
5269 },
5270 outer: outerBox
5271 };
5272
5273 return box;
5274 } else {
5275
5276 this._changeViewbox(function() {
5277 scale = Math.min(outerBox.width / box.width, outerBox.height / box.height);
5278
5279 var matrix = this._svg.createSVGMatrix()
5280 .scale(scale)
5281 .translate(-box.x, -box.y);
5282
5283 transform$1(viewport, matrix);
5284 });
5285 }
5286
5287 return box;
5288 };
5289
5290
5291 /**
5292 * Gets or sets the scroll of the canvas.
5293 *
5294 * @param {Object} [delta] the new scroll to apply.
5295 *
5296 * @param {number} [delta.dx]
5297 * @param {number} [delta.dy]
5298 */
5299 Canvas.prototype.scroll = function(delta) {
5300
5301 var node = this._viewport;
5302 var matrix = node.getCTM();
5303
5304 if (delta) {
5305 this._changeViewbox(function() {
5306 delta = assign({ dx: 0, dy: 0 }, delta || {});
5307
5308 matrix = this._svg.createSVGMatrix().translate(delta.dx, delta.dy).multiply(matrix);
5309
5310 setCTM(node, matrix);
5311 });
5312 }
5313
5314 return { x: matrix.e, y: matrix.f };
5315 };
5316
5317 /**
5318 * Scrolls the viewbox to contain the given element.
5319 * Optionally specify a padding to be applied to the edges.
5320 *
5321 * @param {Object} [element] the element to scroll to.
5322 * @param {Object|Number} [padding=100] the padding to be applied. Can also specify top, bottom, left and right.
5323 *
5324 */
5325 Canvas.prototype.scrollToElement = function(element, padding) {
5326 var defaultPadding = 100;
5327
5328 // switch to correct Plane
5329 var targetPlane = this.findPlane(element);
5330 if (targetPlane !== this._activePlane) {
5331 this.setActivePlane(targetPlane);
5332 }
5333
5334 if (!padding) {
5335 padding = {};
5336 }
5337 if (typeof padding === 'number') {
5338 defaultPadding = padding;
5339 }
5340
5341 padding = {
5342 top: padding.top || defaultPadding,
5343 right: padding.right || defaultPadding,
5344 bottom: padding.bottom || defaultPadding,
5345 left: padding.left || defaultPadding
5346 };
5347
5348 var elementBounds = getBBox(element),
5349 elementTrbl = asTRBL(elementBounds),
5350 viewboxBounds = this.viewbox(),
5351 zoom = this.zoom(),
5352 dx, dy;
5353
5354 // shrink viewboxBounds with padding
5355 viewboxBounds.y += padding.top / zoom;
5356 viewboxBounds.x += padding.left / zoom;
5357 viewboxBounds.width -= (padding.right + padding.left) / zoom;
5358 viewboxBounds.height -= (padding.bottom + padding.top) / zoom;
5359
5360 var viewboxTrbl = asTRBL(viewboxBounds);
5361
5362 var canFit = elementBounds.width < viewboxBounds.width && elementBounds.height < viewboxBounds.height;
5363
5364 if (!canFit) {
5365
5366 // top-left when element can't fit
5367 dx = elementBounds.x - viewboxBounds.x;
5368 dy = elementBounds.y - viewboxBounds.y;
5369
5370 } else {
5371
5372 var dRight = Math.max(0, elementTrbl.right - viewboxTrbl.right),
5373 dLeft = Math.min(0, elementTrbl.left - viewboxTrbl.left),
5374 dBottom = Math.max(0, elementTrbl.bottom - viewboxTrbl.bottom),
5375 dTop = Math.min(0, elementTrbl.top - viewboxTrbl.top);
5376
5377 dx = dRight || dLeft;
5378 dy = dBottom || dTop;
5379
5380 }
5381
5382 this.scroll({ dx: -dx * zoom, dy: -dy * zoom });
5383 };
5384
5385 /**
5386 * Gets or sets the current zoom of the canvas, optionally zooming
5387 * to the specified position.
5388 *
5389 * The getter may return a cached zoom level. Call it with `false` as
5390 * the first argument to force recomputation of the current level.
5391 *
5392 * @param {string|number} [newScale] the new zoom level, either a number, i.e. 0.9,
5393 * or `fit-viewport` to adjust the size to fit the current viewport
5394 * @param {string|Point} [center] the reference point { x: .., y: ..} to zoom to, 'auto' to zoom into mid or null
5395 *
5396 * @return {number} the current scale
5397 */
5398 Canvas.prototype.zoom = function(newScale, center) {
5399
5400 if (!newScale) {
5401 return this.viewbox(newScale).scale;
5402 }
5403
5404 if (newScale === 'fit-viewport') {
5405 return this._fitViewport(center);
5406 }
5407
5408 var outer,
5409 matrix;
5410
5411 this._changeViewbox(function() {
5412
5413 if (typeof center !== 'object') {
5414 outer = this.viewbox().outer;
5415
5416 center = {
5417 x: outer.width / 2,
5418 y: outer.height / 2
5419 };
5420 }
5421
5422 matrix = this._setZoom(newScale, center);
5423 });
5424
5425 return round$b(matrix.a, 1000);
5426 };
5427
5428 function setCTM(node, m) {
5429 var mstr = 'matrix(' + m.a + ',' + m.b + ',' + m.c + ',' + m.d + ',' + m.e + ',' + m.f + ')';
5430 node.setAttribute('transform', mstr);
5431 }
5432
5433 Canvas.prototype._fitViewport = function(center) {
5434
5435 var vbox = this.viewbox(),
5436 outer = vbox.outer,
5437 inner = vbox.inner,
5438 newScale,
5439 newViewbox;
5440
5441 // display the complete diagram without zooming in.
5442 // instead of relying on internal zoom, we perform a
5443 // hard reset on the canvas viewbox to realize this
5444 //
5445 // if diagram does not need to be zoomed in, we focus it around
5446 // the diagram origin instead
5447
5448 if (inner.x >= 0 &&
5449 inner.y >= 0 &&
5450 inner.x + inner.width <= outer.width &&
5451 inner.y + inner.height <= outer.height &&
5452 !center) {
5453
5454 newViewbox = {
5455 x: 0,
5456 y: 0,
5457 width: Math.max(inner.width + inner.x, outer.width),
5458 height: Math.max(inner.height + inner.y, outer.height)
5459 };
5460 } else {
5461
5462 newScale = Math.min(1, outer.width / inner.width, outer.height / inner.height);
5463 newViewbox = {
5464 x: inner.x + (center ? inner.width / 2 - outer.width / newScale / 2 : 0),
5465 y: inner.y + (center ? inner.height / 2 - outer.height / newScale / 2 : 0),
5466 width: outer.width / newScale,
5467 height: outer.height / newScale
5468 };
5469 }
5470
5471 this.viewbox(newViewbox);
5472
5473 return this.viewbox(false).scale;
5474 };
5475
5476
5477 Canvas.prototype._setZoom = function(scale, center) {
5478
5479 var svg = this._svg,
5480 viewport = this._viewport;
5481
5482 var matrix = svg.createSVGMatrix();
5483 var point = svg.createSVGPoint();
5484
5485 var centerPoint,
5486 originalPoint,
5487 currentMatrix,
5488 scaleMatrix,
5489 newMatrix;
5490
5491 currentMatrix = viewport.getCTM();
5492
5493 var currentScale = currentMatrix.a;
5494
5495 if (center) {
5496 centerPoint = assign(point, center);
5497
5498 // revert applied viewport transformations
5499 originalPoint = centerPoint.matrixTransform(currentMatrix.inverse());
5500
5501 // create scale matrix
5502 scaleMatrix = matrix
5503 .translate(originalPoint.x, originalPoint.y)
5504 .scale(1 / currentScale * scale)
5505 .translate(-originalPoint.x, -originalPoint.y);
5506
5507 newMatrix = currentMatrix.multiply(scaleMatrix);
5508 } else {
5509 newMatrix = matrix.scale(scale);
5510 }
5511
5512 setCTM(this._viewport, newMatrix);
5513
5514 return newMatrix;
5515 };
5516
5517
5518 /**
5519 * Returns the size of the canvas
5520 *
5521 * @return {Dimensions}
5522 */
5523 Canvas.prototype.getSize = function() {
5524 return {
5525 width: this._container.clientWidth,
5526 height: this._container.clientHeight
5527 };
5528 };
5529
5530
5531 /**
5532 * Return the absolute bounding box for the given element
5533 *
5534 * The absolute bounding box may be used to display overlays in the
5535 * callers (browser) coordinate system rather than the zoomed in/out
5536 * canvas coordinates.
5537 *
5538 * @param {ElementDescriptor} element
5539 * @return {Bounds} the absolute bounding box
5540 */
5541 Canvas.prototype.getAbsoluteBBox = function(element) {
5542 var vbox = this.viewbox();
5543 var bbox;
5544
5545 // connection
5546 // use svg bbox
5547 if (element.waypoints) {
5548 var gfx = this.getGraphics(element);
5549
5550 bbox = gfx.getBBox();
5551 }
5552
5553 // shapes
5554 // use data
5555 else {
5556 bbox = element;
5557 }
5558
5559 var x = bbox.x * vbox.scale - vbox.x * vbox.scale;
5560 var y = bbox.y * vbox.scale - vbox.y * vbox.scale;
5561
5562 var width = bbox.width * vbox.scale;
5563 var height = bbox.height * vbox.scale;
5564
5565 return {
5566 x: x,
5567 y: y,
5568 width: width,
5569 height: height
5570 };
5571 };
5572
5573 /**
5574 * Fires an event in order other modules can react to the
5575 * canvas resizing
5576 */
5577 Canvas.prototype.resized = function() {
5578
5579 // force recomputation of view box
5580 delete this._cachedViewbox;
5581
5582 this._eventBus.fire('canvas.resized');
5583 };
5584
5585 var ELEMENT_ID = 'data-element-id';
5586
5587
5588 /**
5589 * @class
5590 *
5591 * A registry that keeps track of all shapes in the diagram.
5592 */
5593 function ElementRegistry(eventBus) {
5594 this._elements = {};
5595
5596 this._eventBus = eventBus;
5597 }
5598
5599 ElementRegistry.$inject = [ 'eventBus' ];
5600
5601 /**
5602 * Register a pair of (element, gfx, (secondaryGfx)).
5603 *
5604 * @param {djs.model.Base} element
5605 * @param {SVGElement} gfx
5606 * @param {SVGElement} [secondaryGfx] optional other element to register, too
5607 */
5608 ElementRegistry.prototype.add = function(element, gfx, secondaryGfx) {
5609
5610 var id = element.id;
5611
5612 this._validateId(id);
5613
5614 // associate dom node with element
5615 attr(gfx, ELEMENT_ID, id);
5616
5617 if (secondaryGfx) {
5618 attr(secondaryGfx, ELEMENT_ID, id);
5619 }
5620
5621 this._elements[id] = { element: element, gfx: gfx, secondaryGfx: secondaryGfx };
5622 };
5623
5624 /**
5625 * Removes an element from the registry.
5626 *
5627 * @param {djs.model.Base} element
5628 */
5629 ElementRegistry.prototype.remove = function(element) {
5630 var elements = this._elements,
5631 id = element.id || element,
5632 container = id && elements[id];
5633
5634 if (container) {
5635
5636 // unset element id on gfx
5637 attr(container.gfx, ELEMENT_ID, '');
5638
5639 if (container.secondaryGfx) {
5640 attr(container.secondaryGfx, ELEMENT_ID, '');
5641 }
5642
5643 delete elements[id];
5644 }
5645 };
5646
5647 /**
5648 * Update the id of an element
5649 *
5650 * @param {djs.model.Base} element
5651 * @param {string} newId
5652 */
5653 ElementRegistry.prototype.updateId = function(element, newId) {
5654
5655 this._validateId(newId);
5656
5657 if (typeof element === 'string') {
5658 element = this.get(element);
5659 }
5660
5661 this._eventBus.fire('element.updateId', {
5662 element: element,
5663 newId: newId
5664 });
5665
5666 var gfx = this.getGraphics(element),
5667 secondaryGfx = this.getGraphics(element, true);
5668
5669 this.remove(element);
5670
5671 element.id = newId;
5672
5673 this.add(element, gfx, secondaryGfx);
5674 };
5675
5676 /**
5677 * Update the graphics of an element
5678 *
5679 * @param {djs.model.Base} element
5680 * @param {SVGElement} gfx
5681 * @param {boolean} [secondary=false] whether to update the secondary connected element
5682 */
5683 ElementRegistry.prototype.updateGraphics = function(filter, gfx, secondary) {
5684 var id = filter.id || filter;
5685
5686 var container = this._elements[id];
5687
5688 if (secondary) {
5689 container.secondaryGfx = gfx;
5690 } else {
5691 container.gfx = gfx;
5692 }
5693
5694 attr(gfx, ELEMENT_ID, id);
5695
5696 return gfx;
5697 };
5698
5699 /**
5700 * Return the model element for a given id or graphics.
5701 *
5702 * @example
5703 *
5704 * elementRegistry.get('SomeElementId_1');
5705 * elementRegistry.get(gfx);
5706 *
5707 *
5708 * @param {string|SVGElement} filter for selecting the element
5709 *
5710 * @return {djs.model.Base}
5711 */
5712 ElementRegistry.prototype.get = function(filter) {
5713 var id;
5714
5715 if (typeof filter === 'string') {
5716 id = filter;
5717 } else {
5718 id = filter && attr(filter, ELEMENT_ID);
5719 }
5720
5721 var container = this._elements[id];
5722 return container && container.element;
5723 };
5724
5725 /**
5726 * Return all elements that match a given filter function.
5727 *
5728 * @param {Function} fn
5729 *
5730 * @return {Array<djs.model.Base>}
5731 */
5732 ElementRegistry.prototype.filter = function(fn) {
5733
5734 var filtered = [];
5735
5736 this.forEach(function(element, gfx) {
5737 if (fn(element, gfx)) {
5738 filtered.push(element);
5739 }
5740 });
5741
5742 return filtered;
5743 };
5744
5745 /**
5746 * Return the first element that satisfies the provided testing function.
5747 *
5748 * @param {Function} fn
5749 *
5750 * @return {djs.model.Base}
5751 */
5752 ElementRegistry.prototype.find = function(fn) {
5753 var map = this._elements,
5754 keys = Object.keys(map);
5755
5756 for (var i = 0; i < keys.length; i++) {
5757 var id = keys[i],
5758 container = map[id],
5759 element = container.element,
5760 gfx = container.gfx;
5761
5762 if (fn(element, gfx)) {
5763 return element;
5764 }
5765 }
5766 };
5767
5768 /**
5769 * Return all rendered model elements.
5770 *
5771 * @return {Array<djs.model.Base>}
5772 */
5773 ElementRegistry.prototype.getAll = function() {
5774 return this.filter(function(e) { return e; });
5775 };
5776
5777 /**
5778 * Iterate over all diagram elements.
5779 *
5780 * @param {Function} fn
5781 */
5782 ElementRegistry.prototype.forEach = function(fn) {
5783
5784 var map = this._elements;
5785
5786 Object.keys(map).forEach(function(id) {
5787 var container = map[id],
5788 element = container.element,
5789 gfx = container.gfx;
5790
5791 return fn(element, gfx);
5792 });
5793 };
5794
5795 /**
5796 * Return the graphical representation of an element or its id.
5797 *
5798 * @example
5799 * elementRegistry.getGraphics('SomeElementId_1');
5800 * elementRegistry.getGraphics(rootElement); // <g ...>
5801 *
5802 * elementRegistry.getGraphics(rootElement, true); // <svg ...>
5803 *
5804 *
5805 * @param {string|djs.model.Base} filter
5806 * @param {boolean} [secondary=false] whether to return the secondary connected element
5807 *
5808 * @return {SVGElement}
5809 */
5810 ElementRegistry.prototype.getGraphics = function(filter, secondary) {
5811 var id = filter.id || filter;
5812
5813 var container = this._elements[id];
5814 return container && (secondary ? container.secondaryGfx : container.gfx);
5815 };
5816
5817 /**
5818 * Validate the suitability of the given id and signals a problem
5819 * with an exception.
5820 *
5821 * @param {string} id
5822 *
5823 * @throws {Error} if id is empty or already assigned
5824 */
5825 ElementRegistry.prototype._validateId = function(id) {
5826 if (!id) {
5827 throw new Error('element must have an id');
5828 }
5829
5830 if (this._elements[id]) {
5831 throw new Error('element with id ' + id + ' already added');
5832 }
5833 };
5834
5835 var objectRefs = {exports: {}};
5836
5837 var collection = {};
5838
5839 /**
5840 * An empty collection stub. Use {@link RefsCollection.extend} to extend a
5841 * collection with ref semantics.
5842 *
5843 * @class RefsCollection
5844 */
5845
5846 /**
5847 * Extends a collection with {@link Refs} aware methods
5848 *
5849 * @memberof RefsCollection
5850 * @static
5851 *
5852 * @param {Array<Object>} collection
5853 * @param {Refs} refs instance
5854 * @param {Object} property represented by the collection
5855 * @param {Object} target object the collection is attached to
5856 *
5857 * @return {RefsCollection<Object>} the extended array
5858 */
5859 function extend(collection, refs, property, target) {
5860
5861 var inverseProperty = property.inverse;
5862
5863 /**
5864 * Removes the given element from the array and returns it.
5865 *
5866 * @method RefsCollection#remove
5867 *
5868 * @param {Object} element the element to remove
5869 */
5870 Object.defineProperty(collection, 'remove', {
5871 value: function(element) {
5872 var idx = this.indexOf(element);
5873 if (idx !== -1) {
5874 this.splice(idx, 1);
5875
5876 // unset inverse
5877 refs.unset(element, inverseProperty, target);
5878 }
5879
5880 return element;
5881 }
5882 });
5883
5884 /**
5885 * Returns true if the collection contains the given element
5886 *
5887 * @method RefsCollection#contains
5888 *
5889 * @param {Object} element the element to check for
5890 */
5891 Object.defineProperty(collection, 'contains', {
5892 value: function(element) {
5893 return this.indexOf(element) !== -1;
5894 }
5895 });
5896
5897 /**
5898 * Adds an element to the array, unless it exists already (set semantics).
5899 *
5900 * @method RefsCollection#add
5901 *
5902 * @param {Object} element the element to add
5903 * @param {Number} optional index to add element to
5904 * (possibly moving other elements around)
5905 */
5906 Object.defineProperty(collection, 'add', {
5907 value: function(element, idx) {
5908
5909 var currentIdx = this.indexOf(element);
5910
5911 if (typeof idx === 'undefined') {
5912
5913 if (currentIdx !== -1) {
5914 // element already in collection (!)
5915 return;
5916 }
5917
5918 // add to end of array, as no idx is specified
5919 idx = this.length;
5920 }
5921
5922 // handle already in collection
5923 if (currentIdx !== -1) {
5924
5925 // remove element from currentIdx
5926 this.splice(currentIdx, 1);
5927 }
5928
5929 // add element at idx
5930 this.splice(idx, 0, element);
5931
5932 if (currentIdx === -1) {
5933 // set inverse, unless element was
5934 // in collection already
5935 refs.set(element, inverseProperty, target);
5936 }
5937 }
5938 });
5939
5940 // a simple marker, identifying this element
5941 // as being a refs collection
5942 Object.defineProperty(collection, '__refs_collection', {
5943 value: true
5944 });
5945
5946 return collection;
5947 }
5948
5949
5950 function isExtended(collection) {
5951 return collection.__refs_collection === true;
5952 }
5953
5954 collection.extend = extend;
5955
5956 collection.isExtended = isExtended;
5957
5958 var Collection = collection;
5959
5960 function hasOwnProperty$1(e, property) {
5961 return Object.prototype.hasOwnProperty.call(e, property.name || property);
5962 }
5963
5964 function defineCollectionProperty(ref, property, target) {
5965
5966 var collection = Collection.extend(target[property.name] || [], ref, property, target);
5967
5968 Object.defineProperty(target, property.name, {
5969 enumerable: property.enumerable,
5970 value: collection
5971 });
5972
5973 if (collection.length) {
5974
5975 collection.forEach(function(o) {
5976 ref.set(o, property.inverse, target);
5977 });
5978 }
5979 }
5980
5981
5982 function defineProperty$1(ref, property, target) {
5983
5984 var inverseProperty = property.inverse;
5985
5986 var _value = target[property.name];
5987
5988 Object.defineProperty(target, property.name, {
5989 configurable: property.configurable,
5990 enumerable: property.enumerable,
5991
5992 get: function() {
5993 return _value;
5994 },
5995
5996 set: function(value) {
5997
5998 // return if we already performed all changes
5999 if (value === _value) {
6000 return;
6001 }
6002
6003 var old = _value;
6004
6005 // temporary set null
6006 _value = null;
6007
6008 if (old) {
6009 ref.unset(old, inverseProperty, target);
6010 }
6011
6012 // set new value
6013 _value = value;
6014
6015 // set inverse value
6016 ref.set(_value, inverseProperty, target);
6017 }
6018 });
6019
6020 }
6021
6022 /**
6023 * Creates a new references object defining two inversly related
6024 * attribute descriptors a and b.
6025 *
6026 * <p>
6027 * When bound to an object using {@link Refs#bind} the references
6028 * get activated and ensure that add and remove operations are applied
6029 * reversely, too.
6030 * </p>
6031 *
6032 * <p>
6033 * For attributes represented as collections {@link Refs} provides the
6034 * {@link RefsCollection#add}, {@link RefsCollection#remove} and {@link RefsCollection#contains} extensions
6035 * that must be used to properly hook into the inverse change mechanism.
6036 * </p>
6037 *
6038 * @class Refs
6039 *
6040 * @classdesc A bi-directional reference between two attributes.
6041 *
6042 * @param {Refs.AttributeDescriptor} a property descriptor
6043 * @param {Refs.AttributeDescriptor} b property descriptor
6044 *
6045 * @example
6046 *
6047 * var refs = Refs({ name: 'wheels', collection: true, enumerable: true }, { name: 'car' });
6048 *
6049 * var car = { name: 'toyota' };
6050 * var wheels = [{ pos: 'front-left' }, { pos: 'front-right' }];
6051 *
6052 * refs.bind(car, 'wheels');
6053 *
6054 * car.wheels // []
6055 * car.wheels.add(wheels[0]);
6056 * car.wheels.add(wheels[1]);
6057 *
6058 * car.wheels // [{ pos: 'front-left' }, { pos: 'front-right' }]
6059 *
6060 * wheels[0].car // { name: 'toyota' };
6061 * car.wheels.remove(wheels[0]);
6062 *
6063 * wheels[0].car // undefined
6064 */
6065 function Refs$1(a, b) {
6066
6067 if (!(this instanceof Refs$1)) {
6068 return new Refs$1(a, b);
6069 }
6070
6071 // link
6072 a.inverse = b;
6073 b.inverse = a;
6074
6075 this.props = {};
6076 this.props[a.name] = a;
6077 this.props[b.name] = b;
6078 }
6079
6080 /**
6081 * Binds one side of a bi-directional reference to a
6082 * target object.
6083 *
6084 * @memberOf Refs
6085 *
6086 * @param {Object} target
6087 * @param {String} property
6088 */
6089 Refs$1.prototype.bind = function(target, property) {
6090 if (typeof property === 'string') {
6091 if (!this.props[property]) {
6092 throw new Error('no property <' + property + '> in ref');
6093 }
6094 property = this.props[property];
6095 }
6096
6097 if (property.collection) {
6098 defineCollectionProperty(this, property, target);
6099 } else {
6100 defineProperty$1(this, property, target);
6101 }
6102 };
6103
6104 Refs$1.prototype.ensureRefsCollection = function(target, property) {
6105
6106 var collection = target[property.name];
6107
6108 if (!Collection.isExtended(collection)) {
6109 defineCollectionProperty(this, property, target);
6110 }
6111
6112 return collection;
6113 };
6114
6115 Refs$1.prototype.ensureBound = function(target, property) {
6116 if (!hasOwnProperty$1(target, property)) {
6117 this.bind(target, property);
6118 }
6119 };
6120
6121 Refs$1.prototype.unset = function(target, property, value) {
6122
6123 if (target) {
6124 this.ensureBound(target, property);
6125
6126 if (property.collection) {
6127 this.ensureRefsCollection(target, property).remove(value);
6128 } else {
6129 target[property.name] = undefined;
6130 }
6131 }
6132 };
6133
6134 Refs$1.prototype.set = function(target, property, value) {
6135
6136 if (target) {
6137 this.ensureBound(target, property);
6138
6139 if (property.collection) {
6140 this.ensureRefsCollection(target, property).add(value);
6141 } else {
6142 target[property.name] = value;
6143 }
6144 }
6145 };
6146
6147 var refs = Refs$1;
6148
6149 objectRefs.exports = refs;
6150
6151 objectRefs.exports.Collection = collection;
6152
6153 var Refs = objectRefs.exports;
6154
6155 var parentRefs = new Refs({ name: 'children', enumerable: true, collection: true }, { name: 'parent' }),
6156 labelRefs = new Refs({ name: 'labels', enumerable: true, collection: true }, { name: 'labelTarget' }),
6157 attacherRefs = new Refs({ name: 'attachers', collection: true }, { name: 'host' }),
6158 outgoingRefs = new Refs({ name: 'outgoing', collection: true }, { name: 'source' }),
6159 incomingRefs = new Refs({ name: 'incoming', collection: true }, { name: 'target' });
6160
6161 /**
6162 * @namespace djs.model
6163 */
6164
6165 /**
6166 * @memberOf djs.model
6167 */
6168
6169 /**
6170 * The basic graphical representation
6171 *
6172 * @class
6173 *
6174 * @abstract
6175 */
6176 function Base$1() {
6177
6178 /**
6179 * The object that backs up the shape
6180 *
6181 * @name Base#businessObject
6182 * @type Object
6183 */
6184 Object.defineProperty(this, 'businessObject', {
6185 writable: true
6186 });
6187
6188
6189 /**
6190 * Single label support, will mapped to multi label array
6191 *
6192 * @name Base#label
6193 * @type Object
6194 */
6195 Object.defineProperty(this, 'label', {
6196 get: function() {
6197 return this.labels[0];
6198 },
6199 set: function(newLabel) {
6200
6201 var label = this.label,
6202 labels = this.labels;
6203
6204 if (!newLabel && label) {
6205 labels.remove(label);
6206 } else {
6207 labels.add(newLabel, 0);
6208 }
6209 }
6210 });
6211
6212 /**
6213 * The parent shape
6214 *
6215 * @name Base#parent
6216 * @type Shape
6217 */
6218 parentRefs.bind(this, 'parent');
6219
6220 /**
6221 * The list of labels
6222 *
6223 * @name Base#labels
6224 * @type Label
6225 */
6226 labelRefs.bind(this, 'labels');
6227
6228 /**
6229 * The list of outgoing connections
6230 *
6231 * @name Base#outgoing
6232 * @type Array<Connection>
6233 */
6234 outgoingRefs.bind(this, 'outgoing');
6235
6236 /**
6237 * The list of incoming connections
6238 *
6239 * @name Base#incoming
6240 * @type Array<Connection>
6241 */
6242 incomingRefs.bind(this, 'incoming');
6243 }
6244
6245
6246 /**
6247 * A graphical object
6248 *
6249 * @class
6250 * @constructor
6251 *
6252 * @extends Base
6253 */
6254 function Shape() {
6255 Base$1.call(this);
6256
6257 /**
6258 * Indicates frame shapes
6259 *
6260 * @name Shape#isFrame
6261 * @type boolean
6262 */
6263
6264 /**
6265 * The list of children
6266 *
6267 * @name Shape#children
6268 * @type Array<Base>
6269 */
6270 parentRefs.bind(this, 'children');
6271
6272 /**
6273 * @name Shape#host
6274 * @type Shape
6275 */
6276 attacherRefs.bind(this, 'host');
6277
6278 /**
6279 * @name Shape#attachers
6280 * @type Shape
6281 */
6282 attacherRefs.bind(this, 'attachers');
6283 }
6284
6285 inherits$1(Shape, Base$1);
6286
6287
6288 /**
6289 * A root graphical object
6290 *
6291 * @class
6292 * @constructor
6293 *
6294 * @extends Shape
6295 */
6296 function Root() {
6297 Shape.call(this);
6298 }
6299
6300 inherits$1(Root, Shape);
6301
6302
6303 /**
6304 * A label for an element
6305 *
6306 * @class
6307 * @constructor
6308 *
6309 * @extends Shape
6310 */
6311 function Label() {
6312 Shape.call(this);
6313
6314 /**
6315 * The labeled element
6316 *
6317 * @name Label#labelTarget
6318 * @type Base
6319 */
6320 labelRefs.bind(this, 'labelTarget');
6321 }
6322
6323 inherits$1(Label, Shape);
6324
6325
6326 /**
6327 * A connection between two elements
6328 *
6329 * @class
6330 * @constructor
6331 *
6332 * @extends Base
6333 */
6334 function Connection() {
6335 Base$1.call(this);
6336
6337 /**
6338 * The element this connection originates from
6339 *
6340 * @name Connection#source
6341 * @type Base
6342 */
6343 outgoingRefs.bind(this, 'source');
6344
6345 /**
6346 * The element this connection points to
6347 *
6348 * @name Connection#target
6349 * @type Base
6350 */
6351 incomingRefs.bind(this, 'target');
6352 }
6353
6354 inherits$1(Connection, Base$1);
6355
6356
6357 var types$6 = {
6358 connection: Connection,
6359 shape: Shape,
6360 label: Label,
6361 root: Root
6362 };
6363
6364 /**
6365 * Creates a new model element of the specified type
6366 *
6367 * @method create
6368 *
6369 * @example
6370 *
6371 * var shape1 = Model.create('shape', { x: 10, y: 10, width: 100, height: 100 });
6372 * var shape2 = Model.create('shape', { x: 210, y: 210, width: 100, height: 100 });
6373 *
6374 * var connection = Model.create('connection', { waypoints: [ { x: 110, y: 55 }, {x: 210, y: 55 } ] });
6375 *
6376 * @param {string} type lower-cased model name
6377 * @param {Object} attrs attributes to initialize the new model instance with
6378 *
6379 * @return {Base} the new model instance
6380 */
6381 function create(type, attrs) {
6382 var Type = types$6[type];
6383 if (!Type) {
6384 throw new Error('unknown type: <' + type + '>');
6385 }
6386 return assign(new Type(), attrs);
6387 }
6388
6389 /**
6390 * A factory for diagram-js shapes
6391 */
6392 function ElementFactory$1() {
6393 this._uid = 12;
6394 }
6395
6396
6397 ElementFactory$1.prototype.createRoot = function(attrs) {
6398 return this.create('root', attrs);
6399 };
6400
6401 ElementFactory$1.prototype.createLabel = function(attrs) {
6402 return this.create('label', attrs);
6403 };
6404
6405 ElementFactory$1.prototype.createShape = function(attrs) {
6406 return this.create('shape', attrs);
6407 };
6408
6409 ElementFactory$1.prototype.createConnection = function(attrs) {
6410 return this.create('connection', attrs);
6411 };
6412
6413 /**
6414 * Create a model element with the given type and
6415 * a number of pre-set attributes.
6416 *
6417 * @param {string} type
6418 * @param {Object} attrs
6419 * @return {djs.model.Base} the newly created model instance
6420 */
6421 ElementFactory$1.prototype.create = function(type, attrs) {
6422
6423 attrs = assign({}, attrs || {});
6424
6425 if (!attrs.id) {
6426 attrs.id = type + '_' + (this._uid++);
6427 }
6428
6429 return create(type, attrs);
6430 };
6431
6432 var FN_REF = '__fn';
6433
6434 var DEFAULT_PRIORITY$5 = 1000;
6435
6436 var slice = Array.prototype.slice;
6437
6438 /**
6439 * A general purpose event bus.
6440 *
6441 * This component is used to communicate across a diagram instance.
6442 * Other parts of a diagram can use it to listen to and broadcast events.
6443 *
6444 *
6445 * ## Registering for Events
6446 *
6447 * The event bus provides the {@link EventBus#on} and {@link EventBus#once}
6448 * methods to register for events. {@link EventBus#off} can be used to
6449 * remove event registrations. Listeners receive an instance of {@link Event}
6450 * as the first argument. It allows them to hook into the event execution.
6451 *
6452 * ```javascript
6453 *
6454 * // listen for event
6455 * eventBus.on('foo', function(event) {
6456 *
6457 * // access event type
6458 * event.type; // 'foo'
6459 *
6460 * // stop propagation to other listeners
6461 * event.stopPropagation();
6462 *
6463 * // prevent event default
6464 * event.preventDefault();
6465 * });
6466 *
6467 * // listen for event with custom payload
6468 * eventBus.on('bar', function(event, payload) {
6469 * console.log(payload);
6470 * });
6471 *
6472 * // listen for event returning value
6473 * eventBus.on('foobar', function(event) {
6474 *
6475 * // stop event propagation + prevent default
6476 * return false;
6477 *
6478 * // stop event propagation + return custom result
6479 * return {
6480 * complex: 'listening result'
6481 * };
6482 * });
6483 *
6484 *
6485 * // listen with custom priority (default=1000, higher is better)
6486 * eventBus.on('priorityfoo', 1500, function(event) {
6487 * console.log('invoked first!');
6488 * });
6489 *
6490 *
6491 * // listen for event and pass the context (`this`)
6492 * eventBus.on('foobar', function(event) {
6493 * this.foo();
6494 * }, this);
6495 * ```
6496 *
6497 *
6498 * ## Emitting Events
6499 *
6500 * Events can be emitted via the event bus using {@link EventBus#fire}.
6501 *
6502 * ```javascript
6503 *
6504 * // false indicates that the default action
6505 * // was prevented by listeners
6506 * if (eventBus.fire('foo') === false) {
6507 * console.log('default has been prevented!');
6508 * };
6509 *
6510 *
6511 * // custom args + return value listener
6512 * eventBus.on('sum', function(event, a, b) {
6513 * return a + b;
6514 * });
6515 *
6516 * // you can pass custom arguments + retrieve result values.
6517 * var sum = eventBus.fire('sum', 1, 2);
6518 * console.log(sum); // 3
6519 * ```
6520 */
6521 function EventBus() {
6522 this._listeners = {};
6523
6524 // cleanup on destroy on lowest priority to allow
6525 // message passing until the bitter end
6526 this.on('diagram.destroy', 1, this._destroy, this);
6527 }
6528
6529
6530 /**
6531 * Register an event listener for events with the given name.
6532 *
6533 * The callback will be invoked with `event, ...additionalArguments`
6534 * that have been passed to {@link EventBus#fire}.
6535 *
6536 * Returning false from a listener will prevent the events default action
6537 * (if any is specified). To stop an event from being processed further in
6538 * other listeners execute {@link Event#stopPropagation}.
6539 *
6540 * Returning anything but `undefined` from a listener will stop the listener propagation.
6541 *
6542 * @param {string|Array<string>} events
6543 * @param {number} [priority=1000] the priority in which this listener is called, larger is higher
6544 * @param {Function} callback
6545 * @param {Object} [that] Pass context (`this`) to the callback
6546 */
6547 EventBus.prototype.on = function(events, priority, callback, that) {
6548
6549 events = isArray$2(events) ? events : [ events ];
6550
6551 if (isFunction(priority)) {
6552 that = callback;
6553 callback = priority;
6554 priority = DEFAULT_PRIORITY$5;
6555 }
6556
6557 if (!isNumber(priority)) {
6558 throw new Error('priority must be a number');
6559 }
6560
6561 var actualCallback = callback;
6562
6563 if (that) {
6564 actualCallback = bind$2(callback, that);
6565
6566 // make sure we remember and are able to remove
6567 // bound callbacks via {@link #off} using the original
6568 // callback
6569 actualCallback[FN_REF] = callback[FN_REF] || callback;
6570 }
6571
6572 var self = this;
6573
6574 events.forEach(function(e) {
6575 self._addListener(e, {
6576 priority: priority,
6577 callback: actualCallback,
6578 next: null
6579 });
6580 });
6581 };
6582
6583
6584 /**
6585 * Register an event listener that is executed only once.
6586 *
6587 * @param {string} event the event name to register for
6588 * @param {number} [priority=1000] the priority in which this listener is called, larger is higher
6589 * @param {Function} callback the callback to execute
6590 * @param {Object} [that] Pass context (`this`) to the callback
6591 */
6592 EventBus.prototype.once = function(event, priority, callback, that) {
6593 var self = this;
6594
6595 if (isFunction(priority)) {
6596 that = callback;
6597 callback = priority;
6598 priority = DEFAULT_PRIORITY$5;
6599 }
6600
6601 if (!isNumber(priority)) {
6602 throw new Error('priority must be a number');
6603 }
6604
6605 function wrappedCallback() {
6606 wrappedCallback.__isTomb = true;
6607
6608 var result = callback.apply(that, arguments);
6609
6610 self.off(event, wrappedCallback);
6611
6612 return result;
6613 }
6614
6615 // make sure we remember and are able to remove
6616 // bound callbacks via {@link #off} using the original
6617 // callback
6618 wrappedCallback[FN_REF] = callback;
6619
6620 this.on(event, priority, wrappedCallback);
6621 };
6622
6623
6624 /**
6625 * Removes event listeners by event and callback.
6626 *
6627 * If no callback is given, all listeners for a given event name are being removed.
6628 *
6629 * @param {string|Array<string>} events
6630 * @param {Function} [callback]
6631 */
6632 EventBus.prototype.off = function(events, callback) {
6633
6634 events = isArray$2(events) ? events : [ events ];
6635
6636 var self = this;
6637
6638 events.forEach(function(event) {
6639 self._removeListener(event, callback);
6640 });
6641
6642 };
6643
6644
6645 /**
6646 * Create an EventBus event.
6647 *
6648 * @param {Object} data
6649 *
6650 * @return {Object} event, recognized by the eventBus
6651 */
6652 EventBus.prototype.createEvent = function(data) {
6653 var event = new InternalEvent();
6654
6655 event.init(data);
6656
6657 return event;
6658 };
6659
6660
6661 /**
6662 * Fires a named event.
6663 *
6664 * @example
6665 *
6666 * // fire event by name
6667 * events.fire('foo');
6668 *
6669 * // fire event object with nested type
6670 * var event = { type: 'foo' };
6671 * events.fire(event);
6672 *
6673 * // fire event with explicit type
6674 * var event = { x: 10, y: 20 };
6675 * events.fire('element.moved', event);
6676 *
6677 * // pass additional arguments to the event
6678 * events.on('foo', function(event, bar) {
6679 * alert(bar);
6680 * });
6681 *
6682 * events.fire({ type: 'foo' }, 'I am bar!');
6683 *
6684 * @param {string} [name] the optional event name
6685 * @param {Object} [event] the event object
6686 * @param {...Object} additional arguments to be passed to the callback functions
6687 *
6688 * @return {boolean} the events return value, if specified or false if the
6689 * default action was prevented by listeners
6690 */
6691 EventBus.prototype.fire = function(type, data) {
6692 var event,
6693 firstListener,
6694 returnValue,
6695 args;
6696
6697 args = slice.call(arguments);
6698
6699 if (typeof type === 'object') {
6700 data = type;
6701 type = data.type;
6702 }
6703
6704 if (!type) {
6705 throw new Error('no event type specified');
6706 }
6707
6708 firstListener = this._listeners[type];
6709
6710 if (!firstListener) {
6711 return;
6712 }
6713
6714 // we make sure we fire instances of our home made
6715 // events here. We wrap them only once, though
6716 if (data instanceof InternalEvent) {
6717
6718 // we are fine, we alread have an event
6719 event = data;
6720 } else {
6721 event = this.createEvent(data);
6722 }
6723
6724 // ensure we pass the event as the first parameter
6725 args[0] = event;
6726
6727 // original event type (in case we delegate)
6728 var originalType = event.type;
6729
6730 // update event type before delegation
6731 if (type !== originalType) {
6732 event.type = type;
6733 }
6734
6735 try {
6736 returnValue = this._invokeListeners(event, args, firstListener);
6737 } finally {
6738
6739 // reset event type after delegation
6740 if (type !== originalType) {
6741 event.type = originalType;
6742 }
6743 }
6744
6745 // set the return value to false if the event default
6746 // got prevented and no other return value exists
6747 if (returnValue === undefined && event.defaultPrevented) {
6748 returnValue = false;
6749 }
6750
6751 return returnValue;
6752 };
6753
6754
6755 EventBus.prototype.handleError = function(error) {
6756 return this.fire('error', { error: error }) === false;
6757 };
6758
6759
6760 EventBus.prototype._destroy = function() {
6761 this._listeners = {};
6762 };
6763
6764 EventBus.prototype._invokeListeners = function(event, args, listener) {
6765
6766 var returnValue;
6767
6768 while (listener) {
6769
6770 // handle stopped propagation
6771 if (event.cancelBubble) {
6772 break;
6773 }
6774
6775 returnValue = this._invokeListener(event, args, listener);
6776
6777 listener = listener.next;
6778 }
6779
6780 return returnValue;
6781 };
6782
6783 EventBus.prototype._invokeListener = function(event, args, listener) {
6784
6785 var returnValue;
6786
6787 if (listener.callback.__isTomb) {
6788 return returnValue;
6789 }
6790
6791 try {
6792
6793 // returning false prevents the default action
6794 returnValue = invokeFunction(listener.callback, args);
6795
6796 // stop propagation on return value
6797 if (returnValue !== undefined) {
6798 event.returnValue = returnValue;
6799 event.stopPropagation();
6800 }
6801
6802 // prevent default on return false
6803 if (returnValue === false) {
6804 event.preventDefault();
6805 }
6806 } catch (error) {
6807 if (!this.handleError(error)) {
6808 console.error('unhandled error in event listener', error);
6809
6810 throw error;
6811 }
6812 }
6813
6814 return returnValue;
6815 };
6816
6817 /*
6818 * Add new listener with a certain priority to the list
6819 * of listeners (for the given event).
6820 *
6821 * The semantics of listener registration / listener execution are
6822 * first register, first serve: New listeners will always be inserted
6823 * after existing listeners with the same priority.
6824 *
6825 * Example: Inserting two listeners with priority 1000 and 1300
6826 *
6827 * * before: [ 1500, 1500, 1000, 1000 ]
6828 * * after: [ 1500, 1500, (new=1300), 1000, 1000, (new=1000) ]
6829 *
6830 * @param {string} event
6831 * @param {Object} listener { priority, callback }
6832 */
6833 EventBus.prototype._addListener = function(event, newListener) {
6834
6835 var listener = this._getListeners(event),
6836 previousListener;
6837
6838 // no prior listeners
6839 if (!listener) {
6840 this._setListeners(event, newListener);
6841
6842 return;
6843 }
6844
6845 // ensure we order listeners by priority from
6846 // 0 (high) to n > 0 (low)
6847 while (listener) {
6848
6849 if (listener.priority < newListener.priority) {
6850
6851 newListener.next = listener;
6852
6853 if (previousListener) {
6854 previousListener.next = newListener;
6855 } else {
6856 this._setListeners(event, newListener);
6857 }
6858
6859 return;
6860 }
6861
6862 previousListener = listener;
6863 listener = listener.next;
6864 }
6865
6866 // add new listener to back
6867 previousListener.next = newListener;
6868 };
6869
6870
6871 EventBus.prototype._getListeners = function(name) {
6872 return this._listeners[name];
6873 };
6874
6875 EventBus.prototype._setListeners = function(name, listener) {
6876 this._listeners[name] = listener;
6877 };
6878
6879 EventBus.prototype._removeListener = function(event, callback) {
6880
6881 var listener = this._getListeners(event),
6882 nextListener,
6883 previousListener,
6884 listenerCallback;
6885
6886 if (!callback) {
6887
6888 // clear listeners
6889 this._setListeners(event, null);
6890
6891 return;
6892 }
6893
6894 while (listener) {
6895
6896 nextListener = listener.next;
6897
6898 listenerCallback = listener.callback;
6899
6900 if (listenerCallback === callback || listenerCallback[FN_REF] === callback) {
6901 if (previousListener) {
6902 previousListener.next = nextListener;
6903 } else {
6904
6905 // new first listener
6906 this._setListeners(event, nextListener);
6907 }
6908 }
6909
6910 previousListener = listener;
6911 listener = nextListener;
6912 }
6913 };
6914
6915 /**
6916 * A event that is emitted via the event bus.
6917 */
6918 function InternalEvent() { }
6919
6920 InternalEvent.prototype.stopPropagation = function() {
6921 this.cancelBubble = true;
6922 };
6923
6924 InternalEvent.prototype.preventDefault = function() {
6925 this.defaultPrevented = true;
6926 };
6927
6928 InternalEvent.prototype.init = function(data) {
6929 assign(this, data || {});
6930 };
6931
6932
6933 /**
6934 * Invoke function. Be fast...
6935 *
6936 * @param {Function} fn
6937 * @param {Array<Object>} args
6938 *
6939 * @return {Any}
6940 */
6941 function invokeFunction(fn, args) {
6942 return fn.apply(null, args);
6943 }
6944
6945 /**
6946 * SVGs for elements are generated by the {@link GraphicsFactory}.
6947 *
6948 * This utility gives quick access to the important semantic
6949 * parts of an element.
6950 */
6951
6952 /**
6953 * Returns the visual part of a diagram element
6954 *
6955 * @param {Snap<SVGElement>} gfx
6956 *
6957 * @return {Snap<SVGElement>}
6958 */
6959 function getVisual(gfx) {
6960 return gfx.childNodes[0];
6961 }
6962
6963 /**
6964 * Returns the children for a given diagram element.
6965 *
6966 * @param {Snap<SVGElement>} gfx
6967 * @return {Snap<SVGElement>}
6968 */
6969 function getChildren$1(gfx) {
6970 return gfx.parentNode.childNodes[1];
6971 }
6972
6973 /**
6974 * @param {<SVGElement>} element
6975 * @param {number} x
6976 * @param {number} y
6977 * @param {number} angle
6978 * @param {number} amount
6979 */
6980 function transform(gfx, x, y, angle, amount) {
6981 var translate = createTransform();
6982 translate.setTranslate(x, y);
6983
6984 var rotate = createTransform();
6985 rotate.setRotate(angle || 0, 0, 0);
6986
6987 var scale = createTransform();
6988 scale.setScale(amount || 1, amount || 1);
6989
6990 transform$1(gfx, [ translate, rotate, scale ]);
6991 }
6992
6993
6994 /**
6995 * @param {SVGElement} element
6996 * @param {number} x
6997 * @param {number} y
6998 */
6999 function translate$2(gfx, x, y) {
7000 var translate = createTransform();
7001 translate.setTranslate(x, y);
7002
7003 transform$1(gfx, translate);
7004 }
7005
7006
7007 /**
7008 * @param {SVGElement} element
7009 * @param {number} angle
7010 */
7011 function rotate(gfx, angle) {
7012 var rotate = createTransform();
7013 rotate.setRotate(angle, 0, 0);
7014
7015 transform$1(gfx, rotate);
7016 }
7017
7018 /**
7019 * A factory that creates graphical elements
7020 *
7021 * @param {EventBus} eventBus
7022 * @param {ElementRegistry} elementRegistry
7023 */
7024 function GraphicsFactory(eventBus, elementRegistry) {
7025 this._eventBus = eventBus;
7026 this._elementRegistry = elementRegistry;
7027 }
7028
7029 GraphicsFactory.$inject = [ 'eventBus' , 'elementRegistry' ];
7030
7031
7032 GraphicsFactory.prototype._getChildrenContainer = function(element) {
7033
7034 var gfx = this._elementRegistry.getGraphics(element);
7035
7036 var childrenGfx;
7037
7038 // root element
7039 if (!element.parent) {
7040 childrenGfx = gfx;
7041 } else {
7042 childrenGfx = getChildren$1(gfx);
7043 if (!childrenGfx) {
7044 childrenGfx = create$1('g');
7045 classes(childrenGfx).add('djs-children');
7046
7047 append(gfx.parentNode, childrenGfx);
7048 }
7049 }
7050
7051 return childrenGfx;
7052 };
7053
7054 /**
7055 * Clears the graphical representation of the element and returns the
7056 * cleared visual (the <g class="djs-visual" /> element).
7057 */
7058 GraphicsFactory.prototype._clear = function(gfx) {
7059 var visual = getVisual(gfx);
7060
7061 clear$1(visual);
7062
7063 return visual;
7064 };
7065
7066 /**
7067 * Creates a gfx container for shapes and connections
7068 *
7069 * The layout is as follows:
7070 *
7071 * <g class="djs-group">
7072 *
7073 * <!-- the gfx -->
7074 * <g class="djs-element djs-(shape|connection|frame)">
7075 * <g class="djs-visual">
7076 * <!-- the renderer draws in here -->
7077 * </g>
7078 *
7079 * <!-- extensions (overlays, click box, ...) goes here
7080 * </g>
7081 *
7082 * <!-- the gfx child nodes -->
7083 * <g class="djs-children"></g>
7084 * </g>
7085 *
7086 * @param {string} type the type of the element, i.e. shape | connection
7087 * @param {SVGElement} [childrenGfx]
7088 * @param {number} [parentIndex] position to create container in parent
7089 * @param {boolean} [isFrame] is frame element
7090 *
7091 * @return {SVGElement}
7092 */
7093 GraphicsFactory.prototype._createContainer = function(
7094 type, childrenGfx, parentIndex, isFrame
7095 ) {
7096 var outerGfx = create$1('g');
7097 classes(outerGfx).add('djs-group');
7098
7099 // insert node at position
7100 if (typeof parentIndex !== 'undefined') {
7101 prependTo(outerGfx, childrenGfx, childrenGfx.childNodes[parentIndex]);
7102 } else {
7103 append(childrenGfx, outerGfx);
7104 }
7105
7106 var gfx = create$1('g');
7107 classes(gfx).add('djs-element');
7108 classes(gfx).add('djs-' + type);
7109
7110 if (isFrame) {
7111 classes(gfx).add('djs-frame');
7112 }
7113
7114 append(outerGfx, gfx);
7115
7116 // create visual
7117 var visual = create$1('g');
7118 classes(visual).add('djs-visual');
7119
7120 append(gfx, visual);
7121
7122 return gfx;
7123 };
7124
7125 GraphicsFactory.prototype.create = function(type, element, parentIndex) {
7126 var childrenGfx = this._getChildrenContainer(element.parent);
7127 return this._createContainer(type, childrenGfx, parentIndex, isFrameElement$1(element));
7128 };
7129
7130 GraphicsFactory.prototype.updateContainments = function(elements) {
7131
7132 var self = this,
7133 elementRegistry = this._elementRegistry,
7134 parents;
7135
7136 parents = reduce(elements, function(map, e) {
7137
7138 if (e.parent) {
7139 map[e.parent.id] = e.parent;
7140 }
7141
7142 return map;
7143 }, {});
7144
7145 // update all parents of changed and reorganized their children
7146 // in the correct order (as indicated in our model)
7147 forEach(parents, function(parent) {
7148
7149 var children = parent.children;
7150
7151 if (!children) {
7152 return;
7153 }
7154
7155 var childrenGfx = self._getChildrenContainer(parent);
7156
7157 forEach(children.slice().reverse(), function(child) {
7158 var childGfx = elementRegistry.getGraphics(child);
7159
7160 prependTo(childGfx.parentNode, childrenGfx);
7161 });
7162 });
7163 };
7164
7165 GraphicsFactory.prototype.drawShape = function(visual, element) {
7166 var eventBus = this._eventBus;
7167
7168 return eventBus.fire('render.shape', { gfx: visual, element: element });
7169 };
7170
7171 GraphicsFactory.prototype.getShapePath = function(element) {
7172 var eventBus = this._eventBus;
7173
7174 return eventBus.fire('render.getShapePath', element);
7175 };
7176
7177 GraphicsFactory.prototype.drawConnection = function(visual, element) {
7178 var eventBus = this._eventBus;
7179
7180 return eventBus.fire('render.connection', { gfx: visual, element: element });
7181 };
7182
7183 GraphicsFactory.prototype.getConnectionPath = function(waypoints) {
7184 var eventBus = this._eventBus;
7185
7186 return eventBus.fire('render.getConnectionPath', waypoints);
7187 };
7188
7189 GraphicsFactory.prototype.update = function(type, element, gfx) {
7190
7191 // do NOT update root element
7192 if (!element.parent) {
7193 return;
7194 }
7195
7196 var visual = this._clear(gfx);
7197
7198 // redraw
7199 if (type === 'shape') {
7200 this.drawShape(visual, element);
7201
7202 // update positioning
7203 translate$2(gfx, element.x, element.y);
7204 } else
7205 if (type === 'connection') {
7206 this.drawConnection(visual, element);
7207 } else {
7208 throw new Error('unknown type: ' + type);
7209 }
7210
7211 if (element.hidden) {
7212 attr(gfx, 'display', 'none');
7213 } else {
7214 attr(gfx, 'display', 'block');
7215 }
7216 };
7217
7218 GraphicsFactory.prototype.remove = function(element) {
7219 var gfx = this._elementRegistry.getGraphics(element);
7220
7221 // remove
7222 remove$1(gfx.parentNode);
7223 };
7224
7225
7226 // helpers //////////
7227
7228 function prependTo(newNode, parentNode, siblingNode) {
7229 var node = siblingNode || parentNode.firstChild;
7230
7231 // do not prepend node to itself to prevent IE from crashing
7232 // https://github.com/bpmn-io/bpmn-js/issues/746
7233 if (newNode === node) {
7234 return;
7235 }
7236
7237 parentNode.insertBefore(newNode, node);
7238 }
7239
7240 var CoreModule$1 = {
7241 __depends__: [ DrawModule$1 ],
7242 __init__: [ 'canvas' ],
7243 canvas: [ 'type', Canvas ],
7244 elementRegistry: [ 'type', ElementRegistry ],
7245 elementFactory: [ 'type', ElementFactory$1 ],
7246 eventBus: [ 'type', EventBus ],
7247 graphicsFactory: [ 'type', GraphicsFactory ]
7248 };
7249
7250 /**
7251 * Bootstrap an injector from a list of modules, instantiating a number of default components
7252 *
7253 * @ignore
7254 * @param {Array<didi.Module>} bootstrapModules
7255 *
7256 * @return {didi.Injector} a injector to use to access the components
7257 */
7258 function bootstrap(bootstrapModules) {
7259
7260 var modules = [],
7261 components = [];
7262
7263 function hasModule(m) {
7264 return modules.indexOf(m) >= 0;
7265 }
7266
7267 function addModule(m) {
7268 modules.push(m);
7269 }
7270
7271 function visit(m) {
7272 if (hasModule(m)) {
7273 return;
7274 }
7275
7276 (m.__depends__ || []).forEach(visit);
7277
7278 if (hasModule(m)) {
7279 return;
7280 }
7281
7282 addModule(m);
7283
7284 (m.__init__ || []).forEach(function(c) {
7285 components.push(c);
7286 });
7287 }
7288
7289 bootstrapModules.forEach(visit);
7290
7291 var injector = new Injector(modules);
7292
7293 components.forEach(function(c) {
7294
7295 try {
7296
7297 // eagerly resolve component (fn or string)
7298 injector[typeof c === 'string' ? 'get' : 'invoke'](c);
7299 } catch (e) {
7300 console.error('Failed to instantiate component');
7301 console.error(e.stack);
7302
7303 throw e;
7304 }
7305 });
7306
7307 return injector;
7308 }
7309
7310 /**
7311 * Creates an injector from passed options.
7312 *
7313 * @ignore
7314 * @param {Object} options
7315 * @return {didi.Injector}
7316 */
7317 function createInjector(options) {
7318
7319 options = options || {};
7320
7321 var configModule = {
7322 'config': ['value', options]
7323 };
7324
7325 var modules = [ configModule, CoreModule$1 ].concat(options.modules || []);
7326
7327 return bootstrap(modules);
7328 }
7329
7330
7331 /**
7332 * The main diagram-js entry point that bootstraps the diagram with the given
7333 * configuration.
7334 *
7335 * To register extensions with the diagram, pass them as Array<didi.Module> to the constructor.
7336 *
7337 * @class djs.Diagram
7338 * @memberOf djs
7339 * @constructor
7340 *
7341 * @example
7342 *
7343 * <caption>Creating a plug-in that logs whenever a shape is added to the canvas.</caption>
7344 *
7345 * // plug-in implemenentation
7346 * function MyLoggingPlugin(eventBus) {
7347 * eventBus.on('shape.added', function(event) {
7348 * console.log('shape ', event.shape, ' was added to the diagram');
7349 * });
7350 * }
7351 *
7352 * // export as module
7353 * export default {
7354 * __init__: [ 'myLoggingPlugin' ],
7355 * myLoggingPlugin: [ 'type', MyLoggingPlugin ]
7356 * };
7357 *
7358 *
7359 * // instantiate the diagram with the new plug-in
7360 *
7361 * import MyLoggingModule from 'path-to-my-logging-plugin';
7362 *
7363 * var diagram = new Diagram({
7364 * modules: [
7365 * MyLoggingModule
7366 * ]
7367 * });
7368 *
7369 * diagram.invoke([ 'canvas', function(canvas) {
7370 * // add shape to drawing canvas
7371 * canvas.addShape({ x: 10, y: 10 });
7372 * });
7373 *
7374 * // 'shape ... was added to the diagram' logged to console
7375 *
7376 * @param {Object} options
7377 * @param {Array<didi.Module>} [options.modules] external modules to instantiate with the diagram
7378 * @param {didi.Injector} [injector] an (optional) injector to bootstrap the diagram with
7379 */
7380 function Diagram(options, injector) {
7381
7382 // create injector unless explicitly specified
7383 this.injector = injector = injector || createInjector(options);
7384
7385 // API
7386
7387 /**
7388 * Resolves a diagram service
7389 *
7390 * @method Diagram#get
7391 *
7392 * @param {string} name the name of the diagram service to be retrieved
7393 * @param {boolean} [strict=true] if false, resolve missing services to null
7394 */
7395 this.get = injector.get;
7396
7397 /**
7398 * Executes a function into which diagram services are injected
7399 *
7400 * @method Diagram#invoke
7401 *
7402 * @param {Function|Object[]} fn the function to resolve
7403 * @param {Object} locals a number of locals to use to resolve certain dependencies
7404 */
7405 this.invoke = injector.invoke;
7406
7407 // init
7408
7409 // indicate via event
7410
7411
7412 /**
7413 * An event indicating that all plug-ins are loaded.
7414 *
7415 * Use this event to fire other events to interested plug-ins
7416 *
7417 * @memberOf Diagram
7418 *
7419 * @event diagram.init
7420 *
7421 * @example
7422 *
7423 * eventBus.on('diagram.init', function() {
7424 * eventBus.fire('my-custom-event', { foo: 'BAR' });
7425 * });
7426 *
7427 * @type {Object}
7428 */
7429 this.get('eventBus').fire('diagram.init');
7430 }
7431
7432
7433 /**
7434 * Destroys the diagram
7435 *
7436 * @method Diagram#destroy
7437 */
7438 Diagram.prototype.destroy = function() {
7439 this.get('eventBus').fire('diagram.destroy');
7440 };
7441
7442 /**
7443 * Clear the diagram, removing all contents.
7444 */
7445 Diagram.prototype.clear = function() {
7446 this.get('eventBus').fire('diagram.clear');
7447 };
7448
7449 /**
7450 * Moddle base element.
7451 */
7452 function Base() { }
7453
7454 Base.prototype.get = function(name) {
7455 return this.$model.properties.get(this, name);
7456 };
7457
7458 Base.prototype.set = function(name, value) {
7459 this.$model.properties.set(this, name, value);
7460 };
7461
7462 /**
7463 * A model element factory.
7464 *
7465 * @param {Moddle} model
7466 * @param {Properties} properties
7467 */
7468 function Factory(model, properties) {
7469 this.model = model;
7470 this.properties = properties;
7471 }
7472
7473
7474 Factory.prototype.createType = function(descriptor) {
7475
7476 var model = this.model;
7477
7478 var props = this.properties,
7479 prototype = Object.create(Base.prototype);
7480
7481 // initialize default values
7482 forEach(descriptor.properties, function(p) {
7483 if (!p.isMany && p.default !== undefined) {
7484 prototype[p.name] = p.default;
7485 }
7486 });
7487
7488 props.defineModel(prototype, model);
7489 props.defineDescriptor(prototype, descriptor);
7490
7491 var name = descriptor.ns.name;
7492
7493 /**
7494 * The new type constructor
7495 */
7496 function ModdleElement(attrs) {
7497 props.define(this, '$type', { value: name, enumerable: true });
7498 props.define(this, '$attrs', { value: {} });
7499 props.define(this, '$parent', { writable: true });
7500
7501 forEach(attrs, bind$2(function(val, key) {
7502 this.set(key, val);
7503 }, this));
7504 }
7505
7506 ModdleElement.prototype = prototype;
7507
7508 ModdleElement.hasType = prototype.$instanceOf = this.model.hasType;
7509
7510 // static links
7511 props.defineModel(ModdleElement, model);
7512 props.defineDescriptor(ModdleElement, descriptor);
7513
7514 return ModdleElement;
7515 };
7516
7517 /**
7518 * Built-in moddle types
7519 */
7520 var BUILTINS = {
7521 String: true,
7522 Boolean: true,
7523 Integer: true,
7524 Real: true,
7525 Element: true
7526 };
7527
7528 /**
7529 * Converters for built in types from string representations
7530 */
7531 var TYPE_CONVERTERS = {
7532 String: function(s) { return s; },
7533 Boolean: function(s) { return s === 'true'; },
7534 Integer: function(s) { return parseInt(s, 10); },
7535 Real: function(s) { return parseFloat(s); }
7536 };
7537
7538 /**
7539 * Convert a type to its real representation
7540 */
7541 function coerceType(type, value) {
7542
7543 var converter = TYPE_CONVERTERS[type];
7544
7545 if (converter) {
7546 return converter(value);
7547 } else {
7548 return value;
7549 }
7550 }
7551
7552 /**
7553 * Return whether the given type is built-in
7554 */
7555 function isBuiltIn(type) {
7556 return !!BUILTINS[type];
7557 }
7558
7559 /**
7560 * Return whether the given type is simple
7561 */
7562 function isSimple(type) {
7563 return !!TYPE_CONVERTERS[type];
7564 }
7565
7566 /**
7567 * Parses a namespaced attribute name of the form (ns:)localName to an object,
7568 * given a default prefix to assume in case no explicit namespace is given.
7569 *
7570 * @param {String} name
7571 * @param {String} [defaultPrefix] the default prefix to take, if none is present.
7572 *
7573 * @return {Object} the parsed name
7574 */
7575 function parseName(name, defaultPrefix) {
7576 var parts = name.split(/:/),
7577 localName, prefix;
7578
7579 // no prefix (i.e. only local name)
7580 if (parts.length === 1) {
7581 localName = name;
7582 prefix = defaultPrefix;
7583 } else
7584 // prefix + local name
7585 if (parts.length === 2) {
7586 localName = parts[1];
7587 prefix = parts[0];
7588 } else {
7589 throw new Error('expected <prefix:localName> or <localName>, got ' + name);
7590 }
7591
7592 name = (prefix ? prefix + ':' : '') + localName;
7593
7594 return {
7595 name: name,
7596 prefix: prefix,
7597 localName: localName
7598 };
7599 }
7600
7601 /**
7602 * A utility to build element descriptors.
7603 */
7604 function DescriptorBuilder(nameNs) {
7605 this.ns = nameNs;
7606 this.name = nameNs.name;
7607 this.allTypes = [];
7608 this.allTypesByName = {};
7609 this.properties = [];
7610 this.propertiesByName = {};
7611 }
7612
7613
7614 DescriptorBuilder.prototype.build = function() {
7615 return pick(this, [
7616 'ns',
7617 'name',
7618 'allTypes',
7619 'allTypesByName',
7620 'properties',
7621 'propertiesByName',
7622 'bodyProperty',
7623 'idProperty'
7624 ]);
7625 };
7626
7627 /**
7628 * Add property at given index.
7629 *
7630 * @param {Object} p
7631 * @param {Number} [idx]
7632 * @param {Boolean} [validate=true]
7633 */
7634 DescriptorBuilder.prototype.addProperty = function(p, idx, validate) {
7635
7636 if (typeof idx === 'boolean') {
7637 validate = idx;
7638 idx = undefined;
7639 }
7640
7641 this.addNamedProperty(p, validate !== false);
7642
7643 var properties = this.properties;
7644
7645 if (idx !== undefined) {
7646 properties.splice(idx, 0, p);
7647 } else {
7648 properties.push(p);
7649 }
7650 };
7651
7652
7653 DescriptorBuilder.prototype.replaceProperty = function(oldProperty, newProperty, replace) {
7654 var oldNameNs = oldProperty.ns;
7655
7656 var props = this.properties,
7657 propertiesByName = this.propertiesByName,
7658 rename = oldProperty.name !== newProperty.name;
7659
7660 if (oldProperty.isId) {
7661 if (!newProperty.isId) {
7662 throw new Error(
7663 'property <' + newProperty.ns.name + '> must be id property ' +
7664 'to refine <' + oldProperty.ns.name + '>');
7665 }
7666
7667 this.setIdProperty(newProperty, false);
7668 }
7669
7670 if (oldProperty.isBody) {
7671
7672 if (!newProperty.isBody) {
7673 throw new Error(
7674 'property <' + newProperty.ns.name + '> must be body property ' +
7675 'to refine <' + oldProperty.ns.name + '>');
7676 }
7677
7678 // TODO: Check compatibility
7679 this.setBodyProperty(newProperty, false);
7680 }
7681
7682 // validate existence and get location of old property
7683 var idx = props.indexOf(oldProperty);
7684 if (idx === -1) {
7685 throw new Error('property <' + oldNameNs.name + '> not found in property list');
7686 }
7687
7688 // remove old property
7689 props.splice(idx, 1);
7690
7691 // replacing the named property is intentional
7692 //
7693 // * validate only if this is a "rename" operation
7694 // * add at specific index unless we "replace"
7695 //
7696 this.addProperty(newProperty, replace ? undefined : idx, rename);
7697
7698 // make new property available under old name
7699 propertiesByName[oldNameNs.name] = propertiesByName[oldNameNs.localName] = newProperty;
7700 };
7701
7702
7703 DescriptorBuilder.prototype.redefineProperty = function(p, targetPropertyName, replace) {
7704
7705 var nsPrefix = p.ns.prefix;
7706 var parts = targetPropertyName.split('#');
7707
7708 var name = parseName(parts[0], nsPrefix);
7709 var attrName = parseName(parts[1], name.prefix).name;
7710
7711 var redefinedProperty = this.propertiesByName[attrName];
7712 if (!redefinedProperty) {
7713 throw new Error('refined property <' + attrName + '> not found');
7714 } else {
7715 this.replaceProperty(redefinedProperty, p, replace);
7716 }
7717
7718 delete p.redefines;
7719 };
7720
7721 DescriptorBuilder.prototype.addNamedProperty = function(p, validate) {
7722 var ns = p.ns,
7723 propsByName = this.propertiesByName;
7724
7725 if (validate) {
7726 this.assertNotDefined(p, ns.name);
7727 this.assertNotDefined(p, ns.localName);
7728 }
7729
7730 propsByName[ns.name] = propsByName[ns.localName] = p;
7731 };
7732
7733 DescriptorBuilder.prototype.removeNamedProperty = function(p) {
7734 var ns = p.ns,
7735 propsByName = this.propertiesByName;
7736
7737 delete propsByName[ns.name];
7738 delete propsByName[ns.localName];
7739 };
7740
7741 DescriptorBuilder.prototype.setBodyProperty = function(p, validate) {
7742
7743 if (validate && this.bodyProperty) {
7744 throw new Error(
7745 'body property defined multiple times ' +
7746 '(<' + this.bodyProperty.ns.name + '>, <' + p.ns.name + '>)');
7747 }
7748
7749 this.bodyProperty = p;
7750 };
7751
7752 DescriptorBuilder.prototype.setIdProperty = function(p, validate) {
7753
7754 if (validate && this.idProperty) {
7755 throw new Error(
7756 'id property defined multiple times ' +
7757 '(<' + this.idProperty.ns.name + '>, <' + p.ns.name + '>)');
7758 }
7759
7760 this.idProperty = p;
7761 };
7762
7763 DescriptorBuilder.prototype.assertNotDefined = function(p, name) {
7764 var propertyName = p.name,
7765 definedProperty = this.propertiesByName[propertyName];
7766
7767 if (definedProperty) {
7768 throw new Error(
7769 'property <' + propertyName + '> already defined; ' +
7770 'override of <' + definedProperty.definedBy.ns.name + '#' + definedProperty.ns.name + '> by ' +
7771 '<' + p.definedBy.ns.name + '#' + p.ns.name + '> not allowed without redefines');
7772 }
7773 };
7774
7775 DescriptorBuilder.prototype.hasProperty = function(name) {
7776 return this.propertiesByName[name];
7777 };
7778
7779 DescriptorBuilder.prototype.addTrait = function(t, inherited) {
7780
7781 var typesByName = this.allTypesByName,
7782 types = this.allTypes;
7783
7784 var typeName = t.name;
7785
7786 if (typeName in typesByName) {
7787 return;
7788 }
7789
7790 forEach(t.properties, bind$2(function(p) {
7791
7792 // clone property to allow extensions
7793 p = assign({}, p, {
7794 name: p.ns.localName,
7795 inherited: inherited
7796 });
7797
7798 Object.defineProperty(p, 'definedBy', {
7799 value: t
7800 });
7801
7802 var replaces = p.replaces,
7803 redefines = p.redefines;
7804
7805 // add replace/redefine support
7806 if (replaces || redefines) {
7807 this.redefineProperty(p, replaces || redefines, replaces);
7808 } else {
7809 if (p.isBody) {
7810 this.setBodyProperty(p);
7811 }
7812 if (p.isId) {
7813 this.setIdProperty(p);
7814 }
7815 this.addProperty(p);
7816 }
7817 }, this));
7818
7819 types.push(t);
7820 typesByName[typeName] = t;
7821 };
7822
7823 /**
7824 * A registry of Moddle packages.
7825 *
7826 * @param {Array<Package>} packages
7827 * @param {Properties} properties
7828 */
7829 function Registry(packages, properties) {
7830 this.packageMap = {};
7831 this.typeMap = {};
7832
7833 this.packages = [];
7834
7835 this.properties = properties;
7836
7837 forEach(packages, bind$2(this.registerPackage, this));
7838 }
7839
7840
7841 Registry.prototype.getPackage = function(uriOrPrefix) {
7842 return this.packageMap[uriOrPrefix];
7843 };
7844
7845 Registry.prototype.getPackages = function() {
7846 return this.packages;
7847 };
7848
7849
7850 Registry.prototype.registerPackage = function(pkg) {
7851
7852 // copy package
7853 pkg = assign({}, pkg);
7854
7855 var pkgMap = this.packageMap;
7856
7857 ensureAvailable(pkgMap, pkg, 'prefix');
7858 ensureAvailable(pkgMap, pkg, 'uri');
7859
7860 // register types
7861 forEach(pkg.types, bind$2(function(descriptor) {
7862 this.registerType(descriptor, pkg);
7863 }, this));
7864
7865 pkgMap[pkg.uri] = pkgMap[pkg.prefix] = pkg;
7866 this.packages.push(pkg);
7867 };
7868
7869
7870 /**
7871 * Register a type from a specific package with us
7872 */
7873 Registry.prototype.registerType = function(type, pkg) {
7874
7875 type = assign({}, type, {
7876 superClass: (type.superClass || []).slice(),
7877 extends: (type.extends || []).slice(),
7878 properties: (type.properties || []).slice(),
7879 meta: assign((type.meta || {}))
7880 });
7881
7882 var ns = parseName(type.name, pkg.prefix),
7883 name = ns.name,
7884 propertiesByName = {};
7885
7886 // parse properties
7887 forEach(type.properties, bind$2(function(p) {
7888
7889 // namespace property names
7890 var propertyNs = parseName(p.name, ns.prefix),
7891 propertyName = propertyNs.name;
7892
7893 // namespace property types
7894 if (!isBuiltIn(p.type)) {
7895 p.type = parseName(p.type, propertyNs.prefix).name;
7896 }
7897
7898 assign(p, {
7899 ns: propertyNs,
7900 name: propertyName
7901 });
7902
7903 propertiesByName[propertyName] = p;
7904 }, this));
7905
7906 // update ns + name
7907 assign(type, {
7908 ns: ns,
7909 name: name,
7910 propertiesByName: propertiesByName
7911 });
7912
7913 forEach(type.extends, bind$2(function(extendsName) {
7914 var extended = this.typeMap[extendsName];
7915
7916 extended.traits = extended.traits || [];
7917 extended.traits.push(name);
7918 }, this));
7919
7920 // link to package
7921 this.definePackage(type, pkg);
7922
7923 // register
7924 this.typeMap[name] = type;
7925 };
7926
7927
7928 /**
7929 * Traverse the type hierarchy from bottom to top,
7930 * calling iterator with (type, inherited) for all elements in
7931 * the inheritance chain.
7932 *
7933 * @param {Object} nsName
7934 * @param {Function} iterator
7935 * @param {Boolean} [trait=false]
7936 */
7937 Registry.prototype.mapTypes = function(nsName, iterator, trait) {
7938
7939 var type = isBuiltIn(nsName.name) ? { name: nsName.name } : this.typeMap[nsName.name];
7940
7941 var self = this;
7942
7943 /**
7944 * Traverse the selected trait.
7945 *
7946 * @param {String} cls
7947 */
7948 function traverseTrait(cls) {
7949 return traverseSuper(cls, true);
7950 }
7951
7952 /**
7953 * Traverse the selected super type or trait
7954 *
7955 * @param {String} cls
7956 * @param {Boolean} [trait=false]
7957 */
7958 function traverseSuper(cls, trait) {
7959 var parentNs = parseName(cls, isBuiltIn(cls) ? '' : nsName.prefix);
7960 self.mapTypes(parentNs, iterator, trait);
7961 }
7962
7963 if (!type) {
7964 throw new Error('unknown type <' + nsName.name + '>');
7965 }
7966
7967 forEach(type.superClass, trait ? traverseTrait : traverseSuper);
7968
7969 // call iterator with (type, inherited=!trait)
7970 iterator(type, !trait);
7971
7972 forEach(type.traits, traverseTrait);
7973 };
7974
7975
7976 /**
7977 * Returns the effective descriptor for a type.
7978 *
7979 * @param {String} type the namespaced name (ns:localName) of the type
7980 *
7981 * @return {Descriptor} the resulting effective descriptor
7982 */
7983 Registry.prototype.getEffectiveDescriptor = function(name) {
7984
7985 var nsName = parseName(name);
7986
7987 var builder = new DescriptorBuilder(nsName);
7988
7989 this.mapTypes(nsName, function(type, inherited) {
7990 builder.addTrait(type, inherited);
7991 });
7992
7993 var descriptor = builder.build();
7994
7995 // define package link
7996 this.definePackage(descriptor, descriptor.allTypes[descriptor.allTypes.length - 1].$pkg);
7997
7998 return descriptor;
7999 };
8000
8001
8002 Registry.prototype.definePackage = function(target, pkg) {
8003 this.properties.define(target, '$pkg', { value: pkg });
8004 };
8005
8006
8007
8008 ///////// helpers ////////////////////////////
8009
8010 function ensureAvailable(packageMap, pkg, identifierKey) {
8011
8012 var value = pkg[identifierKey];
8013
8014 if (value in packageMap) {
8015 throw new Error('package with ' + identifierKey + ' <' + value + '> already defined');
8016 }
8017 }
8018
8019 /**
8020 * A utility that gets and sets properties of model elements.
8021 *
8022 * @param {Model} model
8023 */
8024 function Properties(model) {
8025 this.model = model;
8026 }
8027
8028
8029 /**
8030 * Sets a named property on the target element.
8031 * If the value is undefined, the property gets deleted.
8032 *
8033 * @param {Object} target
8034 * @param {String} name
8035 * @param {Object} value
8036 */
8037 Properties.prototype.set = function(target, name, value) {
8038
8039 var property = this.model.getPropertyDescriptor(target, name);
8040
8041 var propertyName = property && property.name;
8042
8043 if (isUndefined(value)) {
8044 // unset the property, if the specified value is undefined;
8045 // delete from $attrs (for extensions) or the target itself
8046 if (property) {
8047 delete target[propertyName];
8048 } else {
8049 delete target.$attrs[name];
8050 }
8051 } else {
8052 // set the property, defining well defined properties on the fly
8053 // or simply updating them in target.$attrs (for extensions)
8054 if (property) {
8055 if (propertyName in target) {
8056 target[propertyName] = value;
8057 } else {
8058 defineProperty(target, property, value);
8059 }
8060 } else {
8061 target.$attrs[name] = value;
8062 }
8063 }
8064 };
8065
8066 /**
8067 * Returns the named property of the given element
8068 *
8069 * @param {Object} target
8070 * @param {String} name
8071 *
8072 * @return {Object}
8073 */
8074 Properties.prototype.get = function(target, name) {
8075
8076 var property = this.model.getPropertyDescriptor(target, name);
8077
8078 if (!property) {
8079 return target.$attrs[name];
8080 }
8081
8082 var propertyName = property.name;
8083
8084 // check if access to collection property and lazily initialize it
8085 if (!target[propertyName] && property.isMany) {
8086 defineProperty(target, property, []);
8087 }
8088
8089 return target[propertyName];
8090 };
8091
8092
8093 /**
8094 * Define a property on the target element
8095 *
8096 * @param {Object} target
8097 * @param {String} name
8098 * @param {Object} options
8099 */
8100 Properties.prototype.define = function(target, name, options) {
8101 Object.defineProperty(target, name, options);
8102 };
8103
8104
8105 /**
8106 * Define the descriptor for an element
8107 */
8108 Properties.prototype.defineDescriptor = function(target, descriptor) {
8109 this.define(target, '$descriptor', { value: descriptor });
8110 };
8111
8112 /**
8113 * Define the model for an element
8114 */
8115 Properties.prototype.defineModel = function(target, model) {
8116 this.define(target, '$model', { value: model });
8117 };
8118
8119
8120 function isUndefined(val) {
8121 return typeof val === 'undefined';
8122 }
8123
8124 function defineProperty(target, property, value) {
8125 Object.defineProperty(target, property.name, {
8126 enumerable: !property.isReference,
8127 writable: true,
8128 value: value,
8129 configurable: true
8130 });
8131 }
8132
8133 //// Moddle implementation /////////////////////////////////////////////////
8134
8135 /**
8136 * @class Moddle
8137 *
8138 * A model that can be used to create elements of a specific type.
8139 *
8140 * @example
8141 *
8142 * var Moddle = require('moddle');
8143 *
8144 * var pkg = {
8145 * name: 'mypackage',
8146 * prefix: 'my',
8147 * types: [
8148 * { name: 'Root' }
8149 * ]
8150 * };
8151 *
8152 * var moddle = new Moddle([pkg]);
8153 *
8154 * @param {Array<Package>} packages the packages to contain
8155 */
8156 function Moddle(packages) {
8157
8158 this.properties = new Properties(this);
8159
8160 this.factory = new Factory(this, this.properties);
8161 this.registry = new Registry(packages, this.properties);
8162
8163 this.typeCache = {};
8164 }
8165
8166
8167 /**
8168 * Create an instance of the specified type.
8169 *
8170 * @method Moddle#create
8171 *
8172 * @example
8173 *
8174 * var foo = moddle.create('my:Foo');
8175 * var bar = moddle.create('my:Bar', { id: 'BAR_1' });
8176 *
8177 * @param {String|Object} descriptor the type descriptor or name know to the model
8178 * @param {Object} attrs a number of attributes to initialize the model instance with
8179 * @return {Object} model instance
8180 */
8181 Moddle.prototype.create = function(descriptor, attrs) {
8182 var Type = this.getType(descriptor);
8183
8184 if (!Type) {
8185 throw new Error('unknown type <' + descriptor + '>');
8186 }
8187
8188 return new Type(attrs);
8189 };
8190
8191
8192 /**
8193 * Returns the type representing a given descriptor
8194 *
8195 * @method Moddle#getType
8196 *
8197 * @example
8198 *
8199 * var Foo = moddle.getType('my:Foo');
8200 * var foo = new Foo({ 'id' : 'FOO_1' });
8201 *
8202 * @param {String|Object} descriptor the type descriptor or name know to the model
8203 * @return {Object} the type representing the descriptor
8204 */
8205 Moddle.prototype.getType = function(descriptor) {
8206
8207 var cache = this.typeCache;
8208
8209 var name = isString(descriptor) ? descriptor : descriptor.ns.name;
8210
8211 var type = cache[name];
8212
8213 if (!type) {
8214 descriptor = this.registry.getEffectiveDescriptor(name);
8215 type = cache[name] = this.factory.createType(descriptor);
8216 }
8217
8218 return type;
8219 };
8220
8221
8222 /**
8223 * Creates an any-element type to be used within model instances.
8224 *
8225 * This can be used to create custom elements that lie outside the meta-model.
8226 * The created element contains all the meta-data required to serialize it
8227 * as part of meta-model elements.
8228 *
8229 * @method Moddle#createAny
8230 *
8231 * @example
8232 *
8233 * var foo = moddle.createAny('vendor:Foo', 'http://vendor', {
8234 * value: 'bar'
8235 * });
8236 *
8237 * var container = moddle.create('my:Container', 'http://my', {
8238 * any: [ foo ]
8239 * });
8240 *
8241 * // go ahead and serialize the stuff
8242 *
8243 *
8244 * @param {String} name the name of the element
8245 * @param {String} nsUri the namespace uri of the element
8246 * @param {Object} [properties] a map of properties to initialize the instance with
8247 * @return {Object} the any type instance
8248 */
8249 Moddle.prototype.createAny = function(name, nsUri, properties) {
8250
8251 var nameNs = parseName(name);
8252
8253 var element = {
8254 $type: name,
8255 $instanceOf: function(type) {
8256 return type === this.$type;
8257 }
8258 };
8259
8260 var descriptor = {
8261 name: name,
8262 isGeneric: true,
8263 ns: {
8264 prefix: nameNs.prefix,
8265 localName: nameNs.localName,
8266 uri: nsUri
8267 }
8268 };
8269
8270 this.properties.defineDescriptor(element, descriptor);
8271 this.properties.defineModel(element, this);
8272 this.properties.define(element, '$parent', { enumerable: false, writable: true });
8273 this.properties.define(element, '$instanceOf', { enumerable: false, writable: true });
8274
8275 forEach(properties, function(a, key) {
8276 if (isObject(a) && a.value !== undefined) {
8277 element[a.name] = a.value;
8278 } else {
8279 element[key] = a;
8280 }
8281 });
8282
8283 return element;
8284 };
8285
8286 /**
8287 * Returns a registered package by uri or prefix
8288 *
8289 * @return {Object} the package
8290 */
8291 Moddle.prototype.getPackage = function(uriOrPrefix) {
8292 return this.registry.getPackage(uriOrPrefix);
8293 };
8294
8295 /**
8296 * Returns a snapshot of all known packages
8297 *
8298 * @return {Object} the package
8299 */
8300 Moddle.prototype.getPackages = function() {
8301 return this.registry.getPackages();
8302 };
8303
8304 /**
8305 * Returns the descriptor for an element
8306 */
8307 Moddle.prototype.getElementDescriptor = function(element) {
8308 return element.$descriptor;
8309 };
8310
8311 /**
8312 * Returns true if the given descriptor or instance
8313 * represents the given type.
8314 *
8315 * May be applied to this, if element is omitted.
8316 */
8317 Moddle.prototype.hasType = function(element, type) {
8318 if (type === undefined) {
8319 type = element;
8320 element = this;
8321 }
8322
8323 var descriptor = element.$model.getElementDescriptor(element);
8324
8325 return (type in descriptor.allTypesByName);
8326 };
8327
8328 /**
8329 * Returns the descriptor of an elements named property
8330 */
8331 Moddle.prototype.getPropertyDescriptor = function(element, property) {
8332 return this.getElementDescriptor(element).propertiesByName[property];
8333 };
8334
8335 /**
8336 * Returns a mapped type's descriptor
8337 */
8338 Moddle.prototype.getTypeDescriptor = function(type) {
8339 return this.registry.typeMap[type];
8340 };
8341
8342 var fromCharCode = String.fromCharCode;
8343
8344 var hasOwnProperty = Object.prototype.hasOwnProperty;
8345
8346 var ENTITY_PATTERN = /&#(\d+);|&#x([0-9a-f]+);|&(\w+);/ig;
8347
8348 var ENTITY_MAPPING = {
8349 'amp': '&',
8350 'apos': '\'',
8351 'gt': '>',
8352 'lt': '<',
8353 'quot': '"'
8354 };
8355
8356 // map UPPERCASE variants of supported special chars
8357 Object.keys(ENTITY_MAPPING).forEach(function(k) {
8358 ENTITY_MAPPING[k.toUpperCase()] = ENTITY_MAPPING[k];
8359 });
8360
8361
8362 function replaceEntities(_, d, x, z) {
8363
8364 // reserved names, i.e. &nbsp;
8365 if (z) {
8366 if (hasOwnProperty.call(ENTITY_MAPPING, z)) {
8367 return ENTITY_MAPPING[z];
8368 } else {
8369
8370 // fall back to original value
8371 return '&' + z + ';';
8372 }
8373 }
8374
8375 // decimal encoded char
8376 if (d) {
8377 return fromCharCode(d);
8378 }
8379
8380 // hex encoded char
8381 return fromCharCode(parseInt(x, 16));
8382 }
8383
8384
8385 /**
8386 * A basic entity decoder that can decode a minimal
8387 * sub-set of reserved names (&amp;) as well as
8388 * hex (&#xaaf;) and decimal (&#1231;) encoded characters.
8389 *
8390 * @param {string} str
8391 *
8392 * @return {string} decoded string
8393 */
8394 function decodeEntities(s) {
8395 if (s.length > 3 && s.indexOf('&') !== -1) {
8396 return s.replace(ENTITY_PATTERN, replaceEntities);
8397 }
8398
8399 return s;
8400 }
8401
8402 var XSI_URI = 'http://www.w3.org/2001/XMLSchema-instance';
8403 var XSI_PREFIX = 'xsi';
8404 var XSI_TYPE$1 = 'xsi:type';
8405
8406 var NON_WHITESPACE_OUTSIDE_ROOT_NODE = 'non-whitespace outside of root node';
8407
8408 function error$2(msg) {
8409 return new Error(msg);
8410 }
8411
8412 function missingNamespaceForPrefix(prefix) {
8413 return 'missing namespace for prefix <' + prefix + '>';
8414 }
8415
8416 function getter(getFn) {
8417 return {
8418 'get': getFn,
8419 'enumerable': true
8420 };
8421 }
8422
8423 function cloneNsMatrix(nsMatrix) {
8424 var clone = {}, key;
8425 for (key in nsMatrix) {
8426 clone[key] = nsMatrix[key];
8427 }
8428 return clone;
8429 }
8430
8431 function uriPrefix(prefix) {
8432 return prefix + '$uri';
8433 }
8434
8435 function buildNsMatrix(nsUriToPrefix) {
8436 var nsMatrix = {},
8437 uri,
8438 prefix;
8439
8440 for (uri in nsUriToPrefix) {
8441 prefix = nsUriToPrefix[uri];
8442 nsMatrix[prefix] = prefix;
8443 nsMatrix[uriPrefix(prefix)] = uri;
8444 }
8445
8446 return nsMatrix;
8447 }
8448
8449 function noopGetContext() {
8450 return { 'line': 0, 'column': 0 };
8451 }
8452
8453 function throwFunc(err) {
8454 throw err;
8455 }
8456
8457 /**
8458 * Creates a new parser with the given options.
8459 *
8460 * @constructor
8461 *
8462 * @param {!Object<string, ?>=} options
8463 */
8464 function Parser(options) {
8465
8466 if (!this) {
8467 return new Parser(options);
8468 }
8469
8470 var proxy = options && options['proxy'];
8471
8472 var onText,
8473 onOpenTag,
8474 onCloseTag,
8475 onCDATA,
8476 onError = throwFunc,
8477 onWarning,
8478 onComment,
8479 onQuestion,
8480 onAttention;
8481
8482 var getContext = noopGetContext;
8483
8484 /**
8485 * Do we need to parse the current elements attributes for namespaces?
8486 *
8487 * @type {boolean}
8488 */
8489 var maybeNS = false;
8490
8491 /**
8492 * Do we process namespaces at all?
8493 *
8494 * @type {boolean}
8495 */
8496 var isNamespace = false;
8497
8498 /**
8499 * The caught error returned on parse end
8500 *
8501 * @type {Error}
8502 */
8503 var returnError = null;
8504
8505 /**
8506 * Should we stop parsing?
8507 *
8508 * @type {boolean}
8509 */
8510 var parseStop = false;
8511
8512 /**
8513 * A map of { uri: prefix } used by the parser.
8514 *
8515 * This map will ensure we can normalize prefixes during processing;
8516 * for each uri, only one prefix will be exposed to the handlers.
8517 *
8518 * @type {!Object<string, string>}}
8519 */
8520 var nsUriToPrefix;
8521
8522 /**
8523 * Handle parse error.
8524 *
8525 * @param {string|Error} err
8526 */
8527 function handleError(err) {
8528 if (!(err instanceof Error)) {
8529 err = error$2(err);
8530 }
8531
8532 returnError = err;
8533
8534 onError(err, getContext);
8535 }
8536
8537 /**
8538 * Handle parse error.
8539 *
8540 * @param {string|Error} err
8541 */
8542 function handleWarning(err) {
8543
8544 if (!onWarning) {
8545 return;
8546 }
8547
8548 if (!(err instanceof Error)) {
8549 err = error$2(err);
8550 }
8551
8552 onWarning(err, getContext);
8553 }
8554
8555 /**
8556 * Register parse listener.
8557 *
8558 * @param {string} name
8559 * @param {Function} cb
8560 *
8561 * @return {Parser}
8562 */
8563 this['on'] = function(name, cb) {
8564
8565 if (typeof cb !== 'function') {
8566 throw error$2('required args <name, cb>');
8567 }
8568
8569 switch (name) {
8570 case 'openTag': onOpenTag = cb; break;
8571 case 'text': onText = cb; break;
8572 case 'closeTag': onCloseTag = cb; break;
8573 case 'error': onError = cb; break;
8574 case 'warn': onWarning = cb; break;
8575 case 'cdata': onCDATA = cb; break;
8576 case 'attention': onAttention = cb; break; // <!XXXXX zzzz="eeee">
8577 case 'question': onQuestion = cb; break; // <? .... ?>
8578 case 'comment': onComment = cb; break;
8579 default:
8580 throw error$2('unsupported event: ' + name);
8581 }
8582
8583 return this;
8584 };
8585
8586 /**
8587 * Set the namespace to prefix mapping.
8588 *
8589 * @example
8590 *
8591 * parser.ns({
8592 * 'http://foo': 'foo',
8593 * 'http://bar': 'bar'
8594 * });
8595 *
8596 * @param {!Object<string, string>} nsMap
8597 *
8598 * @return {Parser}
8599 */
8600 this['ns'] = function(nsMap) {
8601
8602 if (typeof nsMap === 'undefined') {
8603 nsMap = {};
8604 }
8605
8606 if (typeof nsMap !== 'object') {
8607 throw error$2('required args <nsMap={}>');
8608 }
8609
8610 var _nsUriToPrefix = {}, k;
8611
8612 for (k in nsMap) {
8613 _nsUriToPrefix[k] = nsMap[k];
8614 }
8615
8616 // FORCE default mapping for schema instance
8617 _nsUriToPrefix[XSI_URI] = XSI_PREFIX;
8618
8619 isNamespace = true;
8620 nsUriToPrefix = _nsUriToPrefix;
8621
8622 return this;
8623 };
8624
8625 /**
8626 * Parse xml string.
8627 *
8628 * @param {string} xml
8629 *
8630 * @return {Error} returnError, if not thrown
8631 */
8632 this['parse'] = function(xml) {
8633 if (typeof xml !== 'string') {
8634 throw error$2('required args <xml=string>');
8635 }
8636
8637 returnError = null;
8638
8639 parse(xml);
8640
8641 getContext = noopGetContext;
8642 parseStop = false;
8643
8644 return returnError;
8645 };
8646
8647 /**
8648 * Stop parsing.
8649 */
8650 this['stop'] = function() {
8651 parseStop = true;
8652 };
8653
8654 /**
8655 * Parse string, invoking configured listeners on element.
8656 *
8657 * @param {string} xml
8658 */
8659 function parse(xml) {
8660 var nsMatrixStack = isNamespace ? [] : null,
8661 nsMatrix = isNamespace ? buildNsMatrix(nsUriToPrefix) : null,
8662 _nsMatrix,
8663 nodeStack = [],
8664 anonymousNsCount = 0,
8665 tagStart = false,
8666 tagEnd = false,
8667 i = 0, j = 0,
8668 x, y, q, w, v,
8669 xmlns,
8670 elementName,
8671 _elementName,
8672 elementProxy
8673 ;
8674
8675 var attrsString = '',
8676 attrsStart = 0,
8677 cachedAttrs // false = parsed with errors, null = needs parsing
8678 ;
8679
8680 /**
8681 * Parse attributes on demand and returns the parsed attributes.
8682 *
8683 * Return semantics: (1) `false` on attribute parse error,
8684 * (2) object hash on extracted attrs.
8685 *
8686 * @return {boolean|Object}
8687 */
8688 function getAttrs() {
8689 if (cachedAttrs !== null) {
8690 return cachedAttrs;
8691 }
8692
8693 var nsUri,
8694 nsUriPrefix,
8695 nsName,
8696 defaultAlias = isNamespace && nsMatrix['xmlns'],
8697 attrList = isNamespace && maybeNS ? [] : null,
8698 i = attrsStart,
8699 s = attrsString,
8700 l = s.length,
8701 hasNewMatrix,
8702 newalias,
8703 value,
8704 alias,
8705 name,
8706 attrs = {},
8707 seenAttrs = {},
8708 skipAttr,
8709 w,
8710 j;
8711
8712 parseAttr:
8713 for (; i < l; i++) {
8714 skipAttr = false;
8715 w = s.charCodeAt(i);
8716
8717 if (w === 32 || (w < 14 && w > 8)) { // WHITESPACE={ \f\n\r\t\v}
8718 continue;
8719 }
8720
8721 // wait for non whitespace character
8722 if (w < 65 || w > 122 || (w > 90 && w < 97)) {
8723 if (w !== 95 && w !== 58) { // char 95"_" 58":"
8724 handleWarning('illegal first char attribute name');
8725 skipAttr = true;
8726 }
8727 }
8728
8729 // parse attribute name
8730 for (j = i + 1; j < l; j++) {
8731 w = s.charCodeAt(j);
8732
8733 if (
8734 w > 96 && w < 123 ||
8735 w > 64 && w < 91 ||
8736 w > 47 && w < 59 ||
8737 w === 46 || // '.'
8738 w === 45 || // '-'
8739 w === 95 // '_'
8740 ) {
8741 continue;
8742 }
8743
8744 // unexpected whitespace
8745 if (w === 32 || (w < 14 && w > 8)) { // WHITESPACE
8746 handleWarning('missing attribute value');
8747 i = j;
8748
8749 continue parseAttr;
8750 }
8751
8752 // expected "="
8753 if (w === 61) { // "=" == 61
8754 break;
8755 }
8756
8757 handleWarning('illegal attribute name char');
8758 skipAttr = true;
8759 }
8760
8761 name = s.substring(i, j);
8762
8763 if (name === 'xmlns:xmlns') {
8764 handleWarning('illegal declaration of xmlns');
8765 skipAttr = true;
8766 }
8767
8768 w = s.charCodeAt(j + 1);
8769
8770 if (w === 34) { // '"'
8771 j = s.indexOf('"', i = j + 2);
8772
8773 if (j === -1) {
8774 j = s.indexOf('\'', i);
8775
8776 if (j !== -1) {
8777 handleWarning('attribute value quote missmatch');
8778 skipAttr = true;
8779 }
8780 }
8781
8782 } else if (w === 39) { // "'"
8783 j = s.indexOf('\'', i = j + 2);
8784
8785 if (j === -1) {
8786 j = s.indexOf('"', i);
8787
8788 if (j !== -1) {
8789 handleWarning('attribute value quote missmatch');
8790 skipAttr = true;
8791 }
8792 }
8793
8794 } else {
8795 handleWarning('missing attribute value quotes');
8796 skipAttr = true;
8797
8798 // skip to next space
8799 for (j = j + 1; j < l; j++) {
8800 w = s.charCodeAt(j + 1);
8801
8802 if (w === 32 || (w < 14 && w > 8)) { // WHITESPACE
8803 break;
8804 }
8805 }
8806
8807 }
8808
8809 if (j === -1) {
8810 handleWarning('missing closing quotes');
8811
8812 j = l;
8813 skipAttr = true;
8814 }
8815
8816 if (!skipAttr) {
8817 value = s.substring(i, j);
8818 }
8819
8820 i = j;
8821
8822 // ensure SPACE follows attribute
8823 // skip illegal content otherwise
8824 // example a="b"c
8825 for (; j + 1 < l; j++) {
8826 w = s.charCodeAt(j + 1);
8827
8828 if (w === 32 || (w < 14 && w > 8)) { // WHITESPACE
8829 break;
8830 }
8831
8832 // FIRST ILLEGAL CHAR
8833 if (i === j) {
8834 handleWarning('illegal character after attribute end');
8835 skipAttr = true;
8836 }
8837 }
8838
8839 // advance cursor to next attribute
8840 i = j + 1;
8841
8842 if (skipAttr) {
8843 continue parseAttr;
8844 }
8845
8846 // check attribute re-declaration
8847 if (name in seenAttrs) {
8848 handleWarning('attribute <' + name + '> already defined');
8849 continue;
8850 }
8851
8852 seenAttrs[name] = true;
8853
8854 if (!isNamespace) {
8855 attrs[name] = value;
8856 continue;
8857 }
8858
8859 // try to extract namespace information
8860 if (maybeNS) {
8861 newalias = (
8862 name === 'xmlns'
8863 ? 'xmlns'
8864 : (name.charCodeAt(0) === 120 && name.substr(0, 6) === 'xmlns:')
8865 ? name.substr(6)
8866 : null
8867 );
8868
8869 // handle xmlns(:alias) assignment
8870 if (newalias !== null) {
8871 nsUri = decodeEntities(value);
8872 nsUriPrefix = uriPrefix(newalias);
8873
8874 alias = nsUriToPrefix[nsUri];
8875
8876 if (!alias) {
8877
8878 // no prefix defined or prefix collision
8879 if (
8880 (newalias === 'xmlns') ||
8881 (nsUriPrefix in nsMatrix && nsMatrix[nsUriPrefix] !== nsUri)
8882 ) {
8883
8884 // alocate free ns prefix
8885 do {
8886 alias = 'ns' + (anonymousNsCount++);
8887 } while (typeof nsMatrix[alias] !== 'undefined');
8888 } else {
8889 alias = newalias;
8890 }
8891
8892 nsUriToPrefix[nsUri] = alias;
8893 }
8894
8895 if (nsMatrix[newalias] !== alias) {
8896 if (!hasNewMatrix) {
8897 nsMatrix = cloneNsMatrix(nsMatrix);
8898 hasNewMatrix = true;
8899 }
8900
8901 nsMatrix[newalias] = alias;
8902 if (newalias === 'xmlns') {
8903 nsMatrix[uriPrefix(alias)] = nsUri;
8904 defaultAlias = alias;
8905 }
8906
8907 nsMatrix[nsUriPrefix] = nsUri;
8908 }
8909
8910 // expose xmlns(:asd)="..." in attributes
8911 attrs[name] = value;
8912 continue;
8913 }
8914
8915 // collect attributes until all namespace
8916 // declarations are processed
8917 attrList.push(name, value);
8918 continue;
8919
8920 } /** end if (maybeNs) */
8921
8922 // handle attributes on element without
8923 // namespace declarations
8924 w = name.indexOf(':');
8925 if (w === -1) {
8926 attrs[name] = value;
8927 continue;
8928 }
8929
8930 // normalize ns attribute name
8931 if (!(nsName = nsMatrix[name.substring(0, w)])) {
8932 handleWarning(missingNamespaceForPrefix(name.substring(0, w)));
8933 continue;
8934 }
8935
8936 name = defaultAlias === nsName
8937 ? name.substr(w + 1)
8938 : nsName + name.substr(w);
8939
8940 // end: normalize ns attribute name
8941
8942 // normalize xsi:type ns attribute value
8943 if (name === XSI_TYPE$1) {
8944 w = value.indexOf(':');
8945
8946 if (w !== -1) {
8947 nsName = value.substring(0, w);
8948
8949 // handle default prefixes, i.e. xs:String gracefully
8950 nsName = nsMatrix[nsName] || nsName;
8951 value = nsName + value.substring(w);
8952 } else {
8953 value = defaultAlias + ':' + value;
8954 }
8955 }
8956
8957 // end: normalize xsi:type ns attribute value
8958
8959 attrs[name] = value;
8960 }
8961
8962
8963 // handle deferred, possibly namespaced attributes
8964 if (maybeNS) {
8965
8966 // normalize captured attributes
8967 for (i = 0, l = attrList.length; i < l; i++) {
8968
8969 name = attrList[i++];
8970 value = attrList[i];
8971
8972 w = name.indexOf(':');
8973
8974 if (w !== -1) {
8975
8976 // normalize ns attribute name
8977 if (!(nsName = nsMatrix[name.substring(0, w)])) {
8978 handleWarning(missingNamespaceForPrefix(name.substring(0, w)));
8979 continue;
8980 }
8981
8982 name = defaultAlias === nsName
8983 ? name.substr(w + 1)
8984 : nsName + name.substr(w);
8985
8986 // end: normalize ns attribute name
8987
8988 // normalize xsi:type ns attribute value
8989 if (name === XSI_TYPE$1) {
8990 w = value.indexOf(':');
8991
8992 if (w !== -1) {
8993 nsName = value.substring(0, w);
8994
8995 // handle default prefixes, i.e. xs:String gracefully
8996 nsName = nsMatrix[nsName] || nsName;
8997 value = nsName + value.substring(w);
8998 } else {
8999 value = defaultAlias + ':' + value;
9000 }
9001 }
9002
9003 // end: normalize xsi:type ns attribute value
9004 }
9005
9006 attrs[name] = value;
9007 }
9008
9009 // end: normalize captured attributes
9010 }
9011
9012 return cachedAttrs = attrs;
9013 }
9014
9015 /**
9016 * Extract the parse context { line, column, part }
9017 * from the current parser position.
9018 *
9019 * @return {Object} parse context
9020 */
9021 function getParseContext() {
9022 var splitsRe = /(\r\n|\r|\n)/g;
9023
9024 var line = 0;
9025 var column = 0;
9026 var startOfLine = 0;
9027 var endOfLine = j;
9028 var match;
9029 var data;
9030
9031 while (i >= startOfLine) {
9032
9033 match = splitsRe.exec(xml);
9034
9035 if (!match) {
9036 break;
9037 }
9038
9039 // end of line = (break idx + break chars)
9040 endOfLine = match[0].length + match.index;
9041
9042 if (endOfLine > i) {
9043 break;
9044 }
9045
9046 // advance to next line
9047 line += 1;
9048
9049 startOfLine = endOfLine;
9050 }
9051
9052 // EOF errors
9053 if (i == -1) {
9054 column = endOfLine;
9055 data = xml.substring(j);
9056 } else
9057
9058 // start errors
9059 if (j === 0) {
9060 data = xml.substring(j, i);
9061 }
9062
9063 // other errors
9064 else {
9065 column = i - startOfLine;
9066 data = (j == -1 ? xml.substring(i) : xml.substring(i, j + 1));
9067 }
9068
9069 return {
9070 'data': data,
9071 'line': line,
9072 'column': column
9073 };
9074 }
9075
9076 getContext = getParseContext;
9077
9078
9079 if (proxy) {
9080 elementProxy = Object.create({}, {
9081 'name': getter(function() {
9082 return elementName;
9083 }),
9084 'originalName': getter(function() {
9085 return _elementName;
9086 }),
9087 'attrs': getter(getAttrs),
9088 'ns': getter(function() {
9089 return nsMatrix;
9090 })
9091 });
9092 }
9093
9094 // actual parse logic
9095 while (j !== -1) {
9096
9097 if (xml.charCodeAt(j) === 60) { // "<"
9098 i = j;
9099 } else {
9100 i = xml.indexOf('<', j);
9101 }
9102
9103 // parse end
9104 if (i === -1) {
9105 if (nodeStack.length) {
9106 return handleError('unexpected end of file');
9107 }
9108
9109 if (j === 0) {
9110 return handleError('missing start tag');
9111 }
9112
9113 if (j < xml.length) {
9114 if (xml.substring(j).trim()) {
9115 handleWarning(NON_WHITESPACE_OUTSIDE_ROOT_NODE);
9116 }
9117 }
9118
9119 return;
9120 }
9121
9122 // parse text
9123 if (j !== i) {
9124
9125 if (nodeStack.length) {
9126 if (onText) {
9127 onText(xml.substring(j, i), decodeEntities, getContext);
9128
9129 if (parseStop) {
9130 return;
9131 }
9132 }
9133 } else {
9134 if (xml.substring(j, i).trim()) {
9135 handleWarning(NON_WHITESPACE_OUTSIDE_ROOT_NODE);
9136
9137 if (parseStop) {
9138 return;
9139 }
9140 }
9141 }
9142 }
9143
9144 w = xml.charCodeAt(i+1);
9145
9146 // parse comments + CDATA
9147 if (w === 33) { // "!"
9148 q = xml.charCodeAt(i+2);
9149
9150 // CDATA section
9151 if (q === 91 && xml.substr(i + 3, 6) === 'CDATA[') { // 91 == "["
9152 j = xml.indexOf(']]>', i);
9153 if (j === -1) {
9154 return handleError('unclosed cdata');
9155 }
9156
9157 if (onCDATA) {
9158 onCDATA(xml.substring(i + 9, j), getContext);
9159 if (parseStop) {
9160 return;
9161 }
9162 }
9163
9164 j += 3;
9165 continue;
9166 }
9167
9168 // comment
9169 if (q === 45 && xml.charCodeAt(i + 3) === 45) { // 45 == "-"
9170 j = xml.indexOf('-->', i);
9171 if (j === -1) {
9172 return handleError('unclosed comment');
9173 }
9174
9175
9176 if (onComment) {
9177 onComment(xml.substring(i + 4, j), decodeEntities, getContext);
9178 if (parseStop) {
9179 return;
9180 }
9181 }
9182
9183 j += 3;
9184 continue;
9185 }
9186 }
9187
9188 // parse question <? ... ?>
9189 if (w === 63) { // "?"
9190 j = xml.indexOf('?>', i);
9191 if (j === -1) {
9192 return handleError('unclosed question');
9193 }
9194
9195 if (onQuestion) {
9196 onQuestion(xml.substring(i, j + 2), getContext);
9197 if (parseStop) {
9198 return;
9199 }
9200 }
9201
9202 j += 2;
9203 continue;
9204 }
9205
9206 // find matching closing tag for attention or standard tags
9207 // for that we must skip through attribute values
9208 // (enclosed in single or double quotes)
9209 for (x = i + 1; ; x++) {
9210 v = xml.charCodeAt(x);
9211 if (isNaN(v)) {
9212 j = -1;
9213 return handleError('unclosed tag');
9214 }
9215
9216 // [10] AttValue ::= '"' ([^<&"] | Reference)* '"' | "'" ([^<&'] | Reference)* "'"
9217 // skips the quoted string
9218 // (double quotes) does not appear in a literal enclosed by (double quotes)
9219 // (single quote) does not appear in a literal enclosed by (single quote)
9220 if (v === 34) { // '"'
9221 q = xml.indexOf('"', x + 1);
9222 x = q !== -1 ? q : x;
9223 } else if (v === 39) { // "'"
9224 q = xml.indexOf("'", x + 1);
9225 x = q !== -1 ? q : x;
9226 } else if (v === 62) { // '>'
9227 j = x;
9228 break;
9229 }
9230 }
9231
9232
9233 // parse attention <! ...>
9234 // previously comment and CDATA have already been parsed
9235 if (w === 33) { // "!"
9236
9237 if (onAttention) {
9238 onAttention(xml.substring(i, j + 1), decodeEntities, getContext);
9239 if (parseStop) {
9240 return;
9241 }
9242 }
9243
9244 j += 1;
9245 continue;
9246 }
9247
9248 // don't process attributes;
9249 // there are none
9250 cachedAttrs = {};
9251
9252 // if (xml.charCodeAt(i+1) === 47) { // </...
9253 if (w === 47) { // </...
9254 tagStart = false;
9255 tagEnd = true;
9256
9257 if (!nodeStack.length) {
9258 return handleError('missing open tag');
9259 }
9260
9261 // verify open <-> close tag match
9262 x = elementName = nodeStack.pop();
9263 q = i + 2 + x.length;
9264
9265 if (xml.substring(i + 2, q) !== x) {
9266 return handleError('closing tag mismatch');
9267 }
9268
9269 // verify chars in close tag
9270 for (; q < j; q++) {
9271 w = xml.charCodeAt(q);
9272
9273 if (w === 32 || (w > 8 && w < 14)) { // \f\n\r\t\v space
9274 continue;
9275 }
9276
9277 return handleError('close tag');
9278 }
9279
9280 } else {
9281 if (xml.charCodeAt(j - 1) === 47) { // .../>
9282 x = elementName = xml.substring(i + 1, j - 1);
9283
9284 tagStart = true;
9285 tagEnd = true;
9286
9287 } else {
9288 x = elementName = xml.substring(i + 1, j);
9289
9290 tagStart = true;
9291 tagEnd = false;
9292 }
9293
9294 if (!(w > 96 && w < 123 || w > 64 && w < 91 || w === 95 || w === 58)) { // char 95"_" 58":"
9295 return handleError('illegal first char nodeName');
9296 }
9297
9298 for (q = 1, y = x.length; q < y; q++) {
9299 w = x.charCodeAt(q);
9300
9301 if (w > 96 && w < 123 || w > 64 && w < 91 || w > 47 && w < 59 || w === 45 || w === 95 || w == 46) {
9302 continue;
9303 }
9304
9305 if (w === 32 || (w < 14 && w > 8)) { // \f\n\r\t\v space
9306 elementName = x.substring(0, q);
9307
9308 // maybe there are attributes
9309 cachedAttrs = null;
9310 break;
9311 }
9312
9313 return handleError('invalid nodeName');
9314 }
9315
9316 if (!tagEnd) {
9317 nodeStack.push(elementName);
9318 }
9319 }
9320
9321 if (isNamespace) {
9322
9323 _nsMatrix = nsMatrix;
9324
9325 if (tagStart) {
9326
9327 // remember old namespace
9328 // unless we're self-closing
9329 if (!tagEnd) {
9330 nsMatrixStack.push(_nsMatrix);
9331 }
9332
9333 if (cachedAttrs === null) {
9334
9335 // quick check, whether there may be namespace
9336 // declarations on the node; if that is the case
9337 // we need to eagerly parse the node attributes
9338 if ((maybeNS = x.indexOf('xmlns', q) !== -1)) {
9339 attrsStart = q;
9340 attrsString = x;
9341
9342 getAttrs();
9343
9344 maybeNS = false;
9345 }
9346 }
9347 }
9348
9349 _elementName = elementName;
9350
9351 w = elementName.indexOf(':');
9352 if (w !== -1) {
9353 xmlns = nsMatrix[elementName.substring(0, w)];
9354
9355 // prefix given; namespace must exist
9356 if (!xmlns) {
9357 return handleError('missing namespace on <' + _elementName + '>');
9358 }
9359
9360 elementName = elementName.substr(w + 1);
9361 } else {
9362 xmlns = nsMatrix['xmlns'];
9363
9364 // if no default namespace is defined,
9365 // we'll import the element as anonymous.
9366 //
9367 // it is up to users to correct that to the document defined
9368 // targetNamespace, or whatever their undersanding of the
9369 // XML spec mandates.
9370 }
9371
9372 // adjust namespace prefixs as configured
9373 if (xmlns) {
9374 elementName = xmlns + ':' + elementName;
9375 }
9376
9377 }
9378
9379 if (tagStart) {
9380 attrsStart = q;
9381 attrsString = x;
9382
9383 if (onOpenTag) {
9384 if (proxy) {
9385 onOpenTag(elementProxy, decodeEntities, tagEnd, getContext);
9386 } else {
9387 onOpenTag(elementName, getAttrs, decodeEntities, tagEnd, getContext);
9388 }
9389
9390 if (parseStop) {
9391 return;
9392 }
9393 }
9394
9395 }
9396
9397 if (tagEnd) {
9398
9399 if (onCloseTag) {
9400 onCloseTag(proxy ? elementProxy : elementName, decodeEntities, tagStart, getContext);
9401
9402 if (parseStop) {
9403 return;
9404 }
9405 }
9406
9407 // restore old namespace
9408 if (isNamespace) {
9409 if (!tagStart) {
9410 nsMatrix = nsMatrixStack.pop();
9411 } else {
9412 nsMatrix = _nsMatrix;
9413 }
9414 }
9415 }
9416
9417 j += 1;
9418 }
9419 } /** end parse */
9420
9421 }
9422
9423 function hasLowerCaseAlias(pkg) {
9424 return pkg.xml && pkg.xml.tagAlias === 'lowerCase';
9425 }
9426
9427 var DEFAULT_NS_MAP = {
9428 'xsi': 'http://www.w3.org/2001/XMLSchema-instance',
9429 'xml': 'http://www.w3.org/XML/1998/namespace'
9430 };
9431
9432 var XSI_TYPE = 'xsi:type';
9433
9434 function serializeFormat(element) {
9435 return element.xml && element.xml.serialize;
9436 }
9437
9438 function serializeAsType(element) {
9439 return serializeFormat(element) === XSI_TYPE;
9440 }
9441
9442 function serializeAsProperty(element) {
9443 return serializeFormat(element) === 'property';
9444 }
9445
9446 function capitalize(str) {
9447 return str.charAt(0).toUpperCase() + str.slice(1);
9448 }
9449
9450 function aliasToName(aliasNs, pkg) {
9451
9452 if (!hasLowerCaseAlias(pkg)) {
9453 return aliasNs.name;
9454 }
9455
9456 return aliasNs.prefix + ':' + capitalize(aliasNs.localName);
9457 }
9458
9459 function prefixedToName(nameNs, pkg) {
9460
9461 var name = nameNs.name,
9462 localName = nameNs.localName;
9463
9464 var typePrefix = pkg.xml && pkg.xml.typePrefix;
9465
9466 if (typePrefix && localName.indexOf(typePrefix) === 0) {
9467 return nameNs.prefix + ':' + localName.slice(typePrefix.length);
9468 } else {
9469 return name;
9470 }
9471 }
9472
9473 function normalizeXsiTypeName(name, model) {
9474
9475 var nameNs = parseName(name);
9476 var pkg = model.getPackage(nameNs.prefix);
9477
9478 return prefixedToName(nameNs, pkg);
9479 }
9480
9481 function error$1(message) {
9482 return new Error(message);
9483 }
9484
9485 /**
9486 * Get the moddle descriptor for a given instance or type.
9487 *
9488 * @param {ModdleElement|Function} element
9489 *
9490 * @return {Object} the moddle descriptor
9491 */
9492 function getModdleDescriptor(element) {
9493 return element.$descriptor;
9494 }
9495
9496
9497 /**
9498 * A parse context.
9499 *
9500 * @class
9501 *
9502 * @param {Object} options
9503 * @param {ElementHandler} options.rootHandler the root handler for parsing a document
9504 * @param {boolean} [options.lax=false] whether or not to ignore invalid elements
9505 */
9506 function Context(options) {
9507
9508 /**
9509 * @property {ElementHandler} rootHandler
9510 */
9511
9512 /**
9513 * @property {Boolean} lax
9514 */
9515
9516 assign(this, options);
9517
9518 this.elementsById = {};
9519 this.references = [];
9520 this.warnings = [];
9521
9522 /**
9523 * Add an unresolved reference.
9524 *
9525 * @param {Object} reference
9526 */
9527 this.addReference = function(reference) {
9528 this.references.push(reference);
9529 };
9530
9531 /**
9532 * Add a processed element.
9533 *
9534 * @param {ModdleElement} element
9535 */
9536 this.addElement = function(element) {
9537
9538 if (!element) {
9539 throw error$1('expected element');
9540 }
9541
9542 var elementsById = this.elementsById;
9543
9544 var descriptor = getModdleDescriptor(element);
9545
9546 var idProperty = descriptor.idProperty,
9547 id;
9548
9549 if (idProperty) {
9550 id = element.get(idProperty.name);
9551
9552 if (id) {
9553
9554 // for QName validation as per http://www.w3.org/TR/REC-xml/#NT-NameChar
9555 if (!/^([a-z][\w-.]*:)?[a-z_][\w-.]*$/i.test(id)) {
9556 throw new Error('illegal ID <' + id + '>');
9557 }
9558
9559 if (elementsById[id]) {
9560 throw error$1('duplicate ID <' + id + '>');
9561 }
9562
9563 elementsById[id] = element;
9564 }
9565 }
9566 };
9567
9568 /**
9569 * Add an import warning.
9570 *
9571 * @param {Object} warning
9572 * @param {String} warning.message
9573 * @param {Error} [warning.error]
9574 */
9575 this.addWarning = function(warning) {
9576 this.warnings.push(warning);
9577 };
9578 }
9579
9580 function BaseHandler() {}
9581
9582 BaseHandler.prototype.handleEnd = function() {};
9583 BaseHandler.prototype.handleText = function() {};
9584 BaseHandler.prototype.handleNode = function() {};
9585
9586
9587 /**
9588 * A simple pass through handler that does nothing except for
9589 * ignoring all input it receives.
9590 *
9591 * This is used to ignore unknown elements and
9592 * attributes.
9593 */
9594 function NoopHandler() { }
9595
9596 NoopHandler.prototype = Object.create(BaseHandler.prototype);
9597
9598 NoopHandler.prototype.handleNode = function() {
9599 return this;
9600 };
9601
9602 function BodyHandler() {}
9603
9604 BodyHandler.prototype = Object.create(BaseHandler.prototype);
9605
9606 BodyHandler.prototype.handleText = function(text) {
9607 this.body = (this.body || '') + text;
9608 };
9609
9610 function ReferenceHandler(property, context) {
9611 this.property = property;
9612 this.context = context;
9613 }
9614
9615 ReferenceHandler.prototype = Object.create(BodyHandler.prototype);
9616
9617 ReferenceHandler.prototype.handleNode = function(node) {
9618
9619 if (this.element) {
9620 throw error$1('expected no sub nodes');
9621 } else {
9622 this.element = this.createReference(node);
9623 }
9624
9625 return this;
9626 };
9627
9628 ReferenceHandler.prototype.handleEnd = function() {
9629 this.element.id = this.body;
9630 };
9631
9632 ReferenceHandler.prototype.createReference = function(node) {
9633 return {
9634 property: this.property.ns.name,
9635 id: ''
9636 };
9637 };
9638
9639 function ValueHandler(propertyDesc, element) {
9640 this.element = element;
9641 this.propertyDesc = propertyDesc;
9642 }
9643
9644 ValueHandler.prototype = Object.create(BodyHandler.prototype);
9645
9646 ValueHandler.prototype.handleEnd = function() {
9647
9648 var value = this.body || '',
9649 element = this.element,
9650 propertyDesc = this.propertyDesc;
9651
9652 value = coerceType(propertyDesc.type, value);
9653
9654 if (propertyDesc.isMany) {
9655 element.get(propertyDesc.name).push(value);
9656 } else {
9657 element.set(propertyDesc.name, value);
9658 }
9659 };
9660
9661
9662 function BaseElementHandler() {}
9663
9664 BaseElementHandler.prototype = Object.create(BodyHandler.prototype);
9665
9666 BaseElementHandler.prototype.handleNode = function(node) {
9667 var parser = this,
9668 element = this.element;
9669
9670 if (!element) {
9671 element = this.element = this.createElement(node);
9672
9673 this.context.addElement(element);
9674 } else {
9675 parser = this.handleChild(node);
9676 }
9677
9678 return parser;
9679 };
9680
9681 /**
9682 * @class Reader.ElementHandler
9683 *
9684 */
9685 function ElementHandler(model, typeName, context) {
9686 this.model = model;
9687 this.type = model.getType(typeName);
9688 this.context = context;
9689 }
9690
9691 ElementHandler.prototype = Object.create(BaseElementHandler.prototype);
9692
9693 ElementHandler.prototype.addReference = function(reference) {
9694 this.context.addReference(reference);
9695 };
9696
9697 ElementHandler.prototype.handleText = function(text) {
9698
9699 var element = this.element,
9700 descriptor = getModdleDescriptor(element),
9701 bodyProperty = descriptor.bodyProperty;
9702
9703 if (!bodyProperty) {
9704 throw error$1('unexpected body text <' + text + '>');
9705 }
9706
9707 BodyHandler.prototype.handleText.call(this, text);
9708 };
9709
9710 ElementHandler.prototype.handleEnd = function() {
9711
9712 var value = this.body,
9713 element = this.element,
9714 descriptor = getModdleDescriptor(element),
9715 bodyProperty = descriptor.bodyProperty;
9716
9717 if (bodyProperty && value !== undefined) {
9718 value = coerceType(bodyProperty.type, value);
9719 element.set(bodyProperty.name, value);
9720 }
9721 };
9722
9723 /**
9724 * Create an instance of the model from the given node.
9725 *
9726 * @param {Element} node the xml node
9727 */
9728 ElementHandler.prototype.createElement = function(node) {
9729 var attributes = node.attributes,
9730 Type = this.type,
9731 descriptor = getModdleDescriptor(Type),
9732 context = this.context,
9733 instance = new Type({}),
9734 model = this.model,
9735 propNameNs;
9736
9737 forEach(attributes, function(value, name) {
9738
9739 var prop = descriptor.propertiesByName[name],
9740 values;
9741
9742 if (prop && prop.isReference) {
9743
9744 if (!prop.isMany) {
9745 context.addReference({
9746 element: instance,
9747 property: prop.ns.name,
9748 id: value
9749 });
9750 } else {
9751
9752 // IDREFS: parse references as whitespace-separated list
9753 values = value.split(' ');
9754
9755 forEach(values, function(v) {
9756 context.addReference({
9757 element: instance,
9758 property: prop.ns.name,
9759 id: v
9760 });
9761 });
9762 }
9763
9764 } else {
9765 if (prop) {
9766 value = coerceType(prop.type, value);
9767 } else
9768 if (name !== 'xmlns') {
9769 propNameNs = parseName(name, descriptor.ns.prefix);
9770
9771 // check whether attribute is defined in a well-known namespace
9772 // if that is the case we emit a warning to indicate potential misuse
9773 if (model.getPackage(propNameNs.prefix)) {
9774
9775 context.addWarning({
9776 message: 'unknown attribute <' + name + '>',
9777 element: instance,
9778 property: name,
9779 value: value
9780 });
9781 }
9782 }
9783
9784 instance.set(name, value);
9785 }
9786 });
9787
9788 return instance;
9789 };
9790
9791 ElementHandler.prototype.getPropertyForNode = function(node) {
9792
9793 var name = node.name;
9794 var nameNs = parseName(name);
9795
9796 var type = this.type,
9797 model = this.model,
9798 descriptor = getModdleDescriptor(type);
9799
9800 var propertyName = nameNs.name,
9801 property = descriptor.propertiesByName[propertyName],
9802 elementTypeName,
9803 elementType;
9804
9805 // search for properties by name first
9806
9807 if (property && !property.isAttr) {
9808
9809 if (serializeAsType(property)) {
9810 elementTypeName = node.attributes[XSI_TYPE];
9811
9812 // xsi type is optional, if it does not exists the
9813 // default type is assumed
9814 if (elementTypeName) {
9815
9816 // take possible type prefixes from XML
9817 // into account, i.e.: xsi:type="t{ActualType}"
9818 elementTypeName = normalizeXsiTypeName(elementTypeName, model);
9819
9820 elementType = model.getType(elementTypeName);
9821
9822 return assign({}, property, {
9823 effectiveType: getModdleDescriptor(elementType).name
9824 });
9825 }
9826 }
9827
9828 // search for properties by name first
9829 return property;
9830 }
9831
9832 var pkg = model.getPackage(nameNs.prefix);
9833
9834 if (pkg) {
9835 elementTypeName = aliasToName(nameNs, pkg);
9836 elementType = model.getType(elementTypeName);
9837
9838 // search for collection members later
9839 property = find(descriptor.properties, function(p) {
9840 return !p.isVirtual && !p.isReference && !p.isAttribute && elementType.hasType(p.type);
9841 });
9842
9843 if (property) {
9844 return assign({}, property, {
9845 effectiveType: getModdleDescriptor(elementType).name
9846 });
9847 }
9848 } else {
9849
9850 // parse unknown element (maybe extension)
9851 property = find(descriptor.properties, function(p) {
9852 return !p.isReference && !p.isAttribute && p.type === 'Element';
9853 });
9854
9855 if (property) {
9856 return property;
9857 }
9858 }
9859
9860 throw error$1('unrecognized element <' + nameNs.name + '>');
9861 };
9862
9863 ElementHandler.prototype.toString = function() {
9864 return 'ElementDescriptor[' + getModdleDescriptor(this.type).name + ']';
9865 };
9866
9867 ElementHandler.prototype.valueHandler = function(propertyDesc, element) {
9868 return new ValueHandler(propertyDesc, element);
9869 };
9870
9871 ElementHandler.prototype.referenceHandler = function(propertyDesc) {
9872 return new ReferenceHandler(propertyDesc, this.context);
9873 };
9874
9875 ElementHandler.prototype.handler = function(type) {
9876 if (type === 'Element') {
9877 return new GenericElementHandler(this.model, type, this.context);
9878 } else {
9879 return new ElementHandler(this.model, type, this.context);
9880 }
9881 };
9882
9883 /**
9884 * Handle the child element parsing
9885 *
9886 * @param {Element} node the xml node
9887 */
9888 ElementHandler.prototype.handleChild = function(node) {
9889 var propertyDesc, type, element, childHandler;
9890
9891 propertyDesc = this.getPropertyForNode(node);
9892 element = this.element;
9893
9894 type = propertyDesc.effectiveType || propertyDesc.type;
9895
9896 if (isSimple(type)) {
9897 return this.valueHandler(propertyDesc, element);
9898 }
9899
9900 if (propertyDesc.isReference) {
9901 childHandler = this.referenceHandler(propertyDesc).handleNode(node);
9902 } else {
9903 childHandler = this.handler(type).handleNode(node);
9904 }
9905
9906 var newElement = childHandler.element;
9907
9908 // child handles may decide to skip elements
9909 // by not returning anything
9910 if (newElement !== undefined) {
9911
9912 if (propertyDesc.isMany) {
9913 element.get(propertyDesc.name).push(newElement);
9914 } else {
9915 element.set(propertyDesc.name, newElement);
9916 }
9917
9918 if (propertyDesc.isReference) {
9919 assign(newElement, {
9920 element: element
9921 });
9922
9923 this.context.addReference(newElement);
9924 } else {
9925
9926 // establish child -> parent relationship
9927 newElement.$parent = element;
9928 }
9929 }
9930
9931 return childHandler;
9932 };
9933
9934 /**
9935 * An element handler that performs special validation
9936 * to ensure the node it gets initialized with matches
9937 * the handlers type (namespace wise).
9938 *
9939 * @param {Moddle} model
9940 * @param {String} typeName
9941 * @param {Context} context
9942 */
9943 function RootElementHandler(model, typeName, context) {
9944 ElementHandler.call(this, model, typeName, context);
9945 }
9946
9947 RootElementHandler.prototype = Object.create(ElementHandler.prototype);
9948
9949 RootElementHandler.prototype.createElement = function(node) {
9950
9951 var name = node.name,
9952 nameNs = parseName(name),
9953 model = this.model,
9954 type = this.type,
9955 pkg = model.getPackage(nameNs.prefix),
9956 typeName = pkg && aliasToName(nameNs, pkg) || name;
9957
9958 // verify the correct namespace if we parse
9959 // the first element in the handler tree
9960 //
9961 // this ensures we don't mistakenly import wrong namespace elements
9962 if (!type.hasType(typeName)) {
9963 throw error$1('unexpected element <' + node.originalName + '>');
9964 }
9965
9966 return ElementHandler.prototype.createElement.call(this, node);
9967 };
9968
9969
9970 function GenericElementHandler(model, typeName, context) {
9971 this.model = model;
9972 this.context = context;
9973 }
9974
9975 GenericElementHandler.prototype = Object.create(BaseElementHandler.prototype);
9976
9977 GenericElementHandler.prototype.createElement = function(node) {
9978
9979 var name = node.name,
9980 ns = parseName(name),
9981 prefix = ns.prefix,
9982 uri = node.ns[prefix + '$uri'],
9983 attributes = node.attributes;
9984
9985 return this.model.createAny(name, uri, attributes);
9986 };
9987
9988 GenericElementHandler.prototype.handleChild = function(node) {
9989
9990 var handler = new GenericElementHandler(this.model, 'Element', this.context).handleNode(node),
9991 element = this.element;
9992
9993 var newElement = handler.element,
9994 children;
9995
9996 if (newElement !== undefined) {
9997 children = element.$children = element.$children || [];
9998 children.push(newElement);
9999
10000 // establish child -> parent relationship
10001 newElement.$parent = element;
10002 }
10003
10004 return handler;
10005 };
10006
10007 GenericElementHandler.prototype.handleEnd = function() {
10008 if (this.body) {
10009 this.element.$body = this.body;
10010 }
10011 };
10012
10013 /**
10014 * A reader for a meta-model
10015 *
10016 * @param {Object} options
10017 * @param {Model} options.model used to read xml files
10018 * @param {Boolean} options.lax whether to make parse errors warnings
10019 */
10020 function Reader(options) {
10021
10022 if (options instanceof Moddle) {
10023 options = {
10024 model: options
10025 };
10026 }
10027
10028 assign(this, { lax: false }, options);
10029 }
10030
10031 /**
10032 * The fromXML result.
10033 *
10034 * @typedef {Object} ParseResult
10035 *
10036 * @property {ModdleElement} rootElement
10037 * @property {Array<Object>} references
10038 * @property {Array<Error>} warnings
10039 * @property {Object} elementsById - a mapping containing each ID -> ModdleElement
10040 */
10041
10042 /**
10043 * The fromXML result.
10044 *
10045 * @typedef {Error} ParseError
10046 *
10047 * @property {Array<Error>} warnings
10048 */
10049
10050 /**
10051 * Parse the given XML into a moddle document tree.
10052 *
10053 * @param {String} xml
10054 * @param {ElementHandler|Object} options or rootHandler
10055 *
10056 * @returns {Promise<ParseResult, ParseError>}
10057 */
10058 Reader.prototype.fromXML = function(xml, options, done) {
10059
10060 var rootHandler = options.rootHandler;
10061
10062 if (options instanceof ElementHandler) {
10063
10064 // root handler passed via (xml, { rootHandler: ElementHandler }, ...)
10065 rootHandler = options;
10066 options = {};
10067 } else {
10068 if (typeof options === 'string') {
10069
10070 // rootHandler passed via (xml, 'someString', ...)
10071 rootHandler = this.handler(options);
10072 options = {};
10073 } else if (typeof rootHandler === 'string') {
10074
10075 // rootHandler passed via (xml, { rootHandler: 'someString' }, ...)
10076 rootHandler = this.handler(rootHandler);
10077 }
10078 }
10079
10080 var model = this.model,
10081 lax = this.lax;
10082
10083 var context = new Context(assign({}, options, { rootHandler: rootHandler })),
10084 parser = new Parser({ proxy: true }),
10085 stack = createStack();
10086
10087 rootHandler.context = context;
10088
10089 // push root handler
10090 stack.push(rootHandler);
10091
10092
10093 /**
10094 * Handle error.
10095 *
10096 * @param {Error} err
10097 * @param {Function} getContext
10098 * @param {boolean} lax
10099 *
10100 * @return {boolean} true if handled
10101 */
10102 function handleError(err, getContext, lax) {
10103
10104 var ctx = getContext();
10105
10106 var line = ctx.line,
10107 column = ctx.column,
10108 data = ctx.data;
10109
10110 // we receive the full context data here,
10111 // for elements trim down the information
10112 // to the tag name, only
10113 if (data.charAt(0) === '<' && data.indexOf(' ') !== -1) {
10114 data = data.slice(0, data.indexOf(' ')) + '>';
10115 }
10116
10117 var message =
10118 'unparsable content ' + (data ? data + ' ' : '') + 'detected\n\t' +
10119 'line: ' + line + '\n\t' +
10120 'column: ' + column + '\n\t' +
10121 'nested error: ' + err.message;
10122
10123 if (lax) {
10124 context.addWarning({
10125 message: message,
10126 error: err
10127 });
10128
10129 return true;
10130 } else {
10131 throw error$1(message);
10132 }
10133 }
10134
10135 function handleWarning(err, getContext) {
10136
10137 // just like handling errors in <lax=true> mode
10138 return handleError(err, getContext, true);
10139 }
10140
10141 /**
10142 * Resolve collected references on parse end.
10143 */
10144 function resolveReferences() {
10145
10146 var elementsById = context.elementsById;
10147 var references = context.references;
10148
10149 var i, r;
10150
10151 for (i = 0; (r = references[i]); i++) {
10152 var element = r.element;
10153 var reference = elementsById[r.id];
10154 var property = getModdleDescriptor(element).propertiesByName[r.property];
10155
10156 if (!reference) {
10157 context.addWarning({
10158 message: 'unresolved reference <' + r.id + '>',
10159 element: r.element,
10160 property: r.property,
10161 value: r.id
10162 });
10163 }
10164
10165 if (property.isMany) {
10166 var collection = element.get(property.name),
10167 idx = collection.indexOf(r);
10168
10169 // we replace an existing place holder (idx != -1) or
10170 // append to the collection instead
10171 if (idx === -1) {
10172 idx = collection.length;
10173 }
10174
10175 if (!reference) {
10176
10177 // remove unresolvable reference
10178 collection.splice(idx, 1);
10179 } else {
10180
10181 // add or update reference in collection
10182 collection[idx] = reference;
10183 }
10184 } else {
10185 element.set(property.name, reference);
10186 }
10187 }
10188 }
10189
10190 function handleClose() {
10191 stack.pop().handleEnd();
10192 }
10193
10194 var PREAMBLE_START_PATTERN = /^<\?xml /i;
10195
10196 var ENCODING_PATTERN = / encoding="([^"]+)"/i;
10197
10198 var UTF_8_PATTERN = /^utf-8$/i;
10199
10200 function handleQuestion(question) {
10201
10202 if (!PREAMBLE_START_PATTERN.test(question)) {
10203 return;
10204 }
10205
10206 var match = ENCODING_PATTERN.exec(question);
10207 var encoding = match && match[1];
10208
10209 if (!encoding || UTF_8_PATTERN.test(encoding)) {
10210 return;
10211 }
10212
10213 context.addWarning({
10214 message:
10215 'unsupported document encoding <' + encoding + '>, ' +
10216 'falling back to UTF-8'
10217 });
10218 }
10219
10220 function handleOpen(node, getContext) {
10221 var handler = stack.peek();
10222
10223 try {
10224 stack.push(handler.handleNode(node));
10225 } catch (err) {
10226
10227 if (handleError(err, getContext, lax)) {
10228 stack.push(new NoopHandler());
10229 }
10230 }
10231 }
10232
10233 function handleCData(text, getContext) {
10234
10235 try {
10236 stack.peek().handleText(text);
10237 } catch (err) {
10238 handleWarning(err, getContext);
10239 }
10240 }
10241
10242 function handleText(text, getContext) {
10243
10244 // strip whitespace only nodes, i.e. before
10245 // <!CDATA[ ... ]> sections and in between tags
10246
10247 if (!text.trim()) {
10248 return;
10249 }
10250
10251 handleCData(text, getContext);
10252 }
10253
10254 var uriMap = model.getPackages().reduce(function(uriMap, p) {
10255 uriMap[p.uri] = p.prefix;
10256
10257 return uriMap;
10258 }, {
10259 'http://www.w3.org/XML/1998/namespace': 'xml' // add default xml ns
10260 });
10261 parser
10262 .ns(uriMap)
10263 .on('openTag', function(obj, decodeStr, selfClosing, getContext) {
10264
10265 // gracefully handle unparsable attributes (attrs=false)
10266 var attrs = obj.attrs || {};
10267
10268 var decodedAttrs = Object.keys(attrs).reduce(function(d, key) {
10269 var value = decodeStr(attrs[key]);
10270
10271 d[key] = value;
10272
10273 return d;
10274 }, {});
10275
10276 var node = {
10277 name: obj.name,
10278 originalName: obj.originalName,
10279 attributes: decodedAttrs,
10280 ns: obj.ns
10281 };
10282
10283 handleOpen(node, getContext);
10284 })
10285 .on('question', handleQuestion)
10286 .on('closeTag', handleClose)
10287 .on('cdata', handleCData)
10288 .on('text', function(text, decodeEntities, getContext) {
10289 handleText(decodeEntities(text), getContext);
10290 })
10291 .on('error', handleError)
10292 .on('warn', handleWarning);
10293
10294 // async XML parsing to make sure the execution environment
10295 // (node or brower) is kept responsive and that certain optimization
10296 // strategies can kick in.
10297 return new Promise(function(resolve, reject) {
10298
10299 var err;
10300
10301 try {
10302 parser.parse(xml);
10303
10304 resolveReferences();
10305 } catch (e) {
10306 err = e;
10307 }
10308
10309 var rootElement = rootHandler.element;
10310
10311 if (!err && !rootElement) {
10312 err = error$1('failed to parse document as <' + rootHandler.type.$descriptor.name + '>');
10313 }
10314
10315 var warnings = context.warnings;
10316 var references = context.references;
10317 var elementsById = context.elementsById;
10318
10319 if (err) {
10320 err.warnings = warnings;
10321
10322 return reject(err);
10323 } else {
10324 return resolve({
10325 rootElement: rootElement,
10326 elementsById: elementsById,
10327 references: references,
10328 warnings: warnings
10329 });
10330 }
10331 });
10332 };
10333
10334 Reader.prototype.handler = function(name) {
10335 return new RootElementHandler(this.model, name);
10336 };
10337
10338
10339 // helpers //////////////////////////
10340
10341 function createStack() {
10342 var stack = [];
10343
10344 Object.defineProperty(stack, 'peek', {
10345 value: function() {
10346 return this[this.length - 1];
10347 }
10348 });
10349
10350 return stack;
10351 }
10352
10353 var XML_PREAMBLE = '<?xml version="1.0" encoding="UTF-8"?>\n';
10354
10355 var ESCAPE_ATTR_CHARS = /<|>|'|"|&|\n\r|\n/g;
10356 var ESCAPE_CHARS = /<|>|&/g;
10357
10358
10359 function Namespaces(parent) {
10360
10361 var prefixMap = {};
10362 var uriMap = {};
10363 var used = {};
10364
10365 var wellknown = [];
10366 var custom = [];
10367
10368 // API
10369
10370 this.byUri = function(uri) {
10371 return uriMap[uri] || (
10372 parent && parent.byUri(uri)
10373 );
10374 };
10375
10376 this.add = function(ns, isWellknown) {
10377
10378 uriMap[ns.uri] = ns;
10379
10380 if (isWellknown) {
10381 wellknown.push(ns);
10382 } else {
10383 custom.push(ns);
10384 }
10385
10386 this.mapPrefix(ns.prefix, ns.uri);
10387 };
10388
10389 this.uriByPrefix = function(prefix) {
10390 return prefixMap[prefix || 'xmlns'];
10391 };
10392
10393 this.mapPrefix = function(prefix, uri) {
10394 prefixMap[prefix || 'xmlns'] = uri;
10395 };
10396
10397 this.getNSKey = function(ns) {
10398 return (ns.prefix !== undefined) ? (ns.uri + '|' + ns.prefix) : ns.uri;
10399 };
10400
10401 this.logUsed = function(ns) {
10402
10403 var uri = ns.uri;
10404 var nsKey = this.getNSKey(ns);
10405
10406 used[nsKey] = this.byUri(uri);
10407
10408 // Inform parent recursively about the usage of this NS
10409 if (parent) {
10410 parent.logUsed(ns);
10411 }
10412 };
10413
10414 this.getUsed = function(ns) {
10415
10416 function isUsed(ns) {
10417 var nsKey = self.getNSKey(ns);
10418
10419 return used[nsKey];
10420 }
10421
10422 var self = this;
10423
10424 var allNs = [].concat(wellknown, custom);
10425
10426 return allNs.filter(isUsed);
10427 };
10428
10429 }
10430
10431 function lower(string) {
10432 return string.charAt(0).toLowerCase() + string.slice(1);
10433 }
10434
10435 function nameToAlias(name, pkg) {
10436 if (hasLowerCaseAlias(pkg)) {
10437 return lower(name);
10438 } else {
10439 return name;
10440 }
10441 }
10442
10443 function inherits(ctor, superCtor) {
10444 ctor.super_ = superCtor;
10445 ctor.prototype = Object.create(superCtor.prototype, {
10446 constructor: {
10447 value: ctor,
10448 enumerable: false,
10449 writable: true,
10450 configurable: true
10451 }
10452 });
10453 }
10454
10455 function nsName(ns) {
10456 if (isString(ns)) {
10457 return ns;
10458 } else {
10459 return (ns.prefix ? ns.prefix + ':' : '') + ns.localName;
10460 }
10461 }
10462
10463 function getNsAttrs(namespaces) {
10464
10465 return namespaces.getUsed().filter(function(ns) {
10466
10467 // do not serialize built in <xml> namespace
10468 return ns.prefix !== 'xml';
10469 }).map(function(ns) {
10470 var name = 'xmlns' + (ns.prefix ? ':' + ns.prefix : '');
10471 return { name: name, value: ns.uri };
10472 });
10473
10474 }
10475
10476 function getElementNs(ns, descriptor) {
10477 if (descriptor.isGeneric) {
10478 return assign({ localName: descriptor.ns.localName }, ns);
10479 } else {
10480 return assign({ localName: nameToAlias(descriptor.ns.localName, descriptor.$pkg) }, ns);
10481 }
10482 }
10483
10484 function getPropertyNs(ns, descriptor) {
10485 return assign({ localName: descriptor.ns.localName }, ns);
10486 }
10487
10488 function getSerializableProperties(element) {
10489 var descriptor = element.$descriptor;
10490
10491 return filter(descriptor.properties, function(p) {
10492 var name = p.name;
10493
10494 if (p.isVirtual) {
10495 return false;
10496 }
10497
10498 // do not serialize defaults
10499 if (!has(element, name)) {
10500 return false;
10501 }
10502
10503 var value = element[name];
10504
10505 // do not serialize default equals
10506 if (value === p.default) {
10507 return false;
10508 }
10509
10510 // do not serialize null properties
10511 if (value === null) {
10512 return false;
10513 }
10514
10515 return p.isMany ? value.length : true;
10516 });
10517 }
10518
10519 var ESCAPE_ATTR_MAP = {
10520 '\n': '#10',
10521 '\n\r': '#10',
10522 '"': '#34',
10523 '\'': '#39',
10524 '<': '#60',
10525 '>': '#62',
10526 '&': '#38'
10527 };
10528
10529 var ESCAPE_MAP = {
10530 '<': 'lt',
10531 '>': 'gt',
10532 '&': 'amp'
10533 };
10534
10535 function escape(str, charPattern, replaceMap) {
10536
10537 // ensure we are handling strings here
10538 str = isString(str) ? str : '' + str;
10539
10540 return str.replace(charPattern, function(s) {
10541 return '&' + replaceMap[s] + ';';
10542 });
10543 }
10544
10545 /**
10546 * Escape a string attribute to not contain any bad values (line breaks, '"', ...)
10547 *
10548 * @param {String} str the string to escape
10549 * @return {String} the escaped string
10550 */
10551 function escapeAttr(str) {
10552 return escape(str, ESCAPE_ATTR_CHARS, ESCAPE_ATTR_MAP);
10553 }
10554
10555 function escapeBody(str) {
10556 return escape(str, ESCAPE_CHARS, ESCAPE_MAP);
10557 }
10558
10559 function filterAttributes(props) {
10560 return filter(props, function(p) { return p.isAttr; });
10561 }
10562
10563 function filterContained(props) {
10564 return filter(props, function(p) { return !p.isAttr; });
10565 }
10566
10567
10568 function ReferenceSerializer(tagName) {
10569 this.tagName = tagName;
10570 }
10571
10572 ReferenceSerializer.prototype.build = function(element) {
10573 this.element = element;
10574 return this;
10575 };
10576
10577 ReferenceSerializer.prototype.serializeTo = function(writer) {
10578 writer
10579 .appendIndent()
10580 .append('<' + this.tagName + '>' + this.element.id + '</' + this.tagName + '>')
10581 .appendNewLine();
10582 };
10583
10584 function BodySerializer() {}
10585
10586 BodySerializer.prototype.serializeValue =
10587 BodySerializer.prototype.serializeTo = function(writer) {
10588 writer.append(
10589 this.escape
10590 ? escapeBody(this.value)
10591 : this.value
10592 );
10593 };
10594
10595 BodySerializer.prototype.build = function(prop, value) {
10596 this.value = value;
10597
10598 if (prop.type === 'String' && value.search(ESCAPE_CHARS) !== -1) {
10599 this.escape = true;
10600 }
10601
10602 return this;
10603 };
10604
10605 function ValueSerializer(tagName) {
10606 this.tagName = tagName;
10607 }
10608
10609 inherits(ValueSerializer, BodySerializer);
10610
10611 ValueSerializer.prototype.serializeTo = function(writer) {
10612
10613 writer
10614 .appendIndent()
10615 .append('<' + this.tagName + '>');
10616
10617 this.serializeValue(writer);
10618
10619 writer
10620 .append('</' + this.tagName + '>')
10621 .appendNewLine();
10622 };
10623
10624 function ElementSerializer(parent, propertyDescriptor) {
10625 this.body = [];
10626 this.attrs = [];
10627
10628 this.parent = parent;
10629 this.propertyDescriptor = propertyDescriptor;
10630 }
10631
10632 ElementSerializer.prototype.build = function(element) {
10633 this.element = element;
10634
10635 var elementDescriptor = element.$descriptor,
10636 propertyDescriptor = this.propertyDescriptor;
10637
10638 var otherAttrs,
10639 properties;
10640
10641 var isGeneric = elementDescriptor.isGeneric;
10642
10643 if (isGeneric) {
10644 otherAttrs = this.parseGeneric(element);
10645 } else {
10646 otherAttrs = this.parseNsAttributes(element);
10647 }
10648
10649 if (propertyDescriptor) {
10650 this.ns = this.nsPropertyTagName(propertyDescriptor);
10651 } else {
10652 this.ns = this.nsTagName(elementDescriptor);
10653 }
10654
10655 // compute tag name
10656 this.tagName = this.addTagName(this.ns);
10657
10658 if (!isGeneric) {
10659 properties = getSerializableProperties(element);
10660
10661 this.parseAttributes(filterAttributes(properties));
10662 this.parseContainments(filterContained(properties));
10663 }
10664
10665 this.parseGenericAttributes(element, otherAttrs);
10666
10667 return this;
10668 };
10669
10670 ElementSerializer.prototype.nsTagName = function(descriptor) {
10671 var effectiveNs = this.logNamespaceUsed(descriptor.ns);
10672 return getElementNs(effectiveNs, descriptor);
10673 };
10674
10675 ElementSerializer.prototype.nsPropertyTagName = function(descriptor) {
10676 var effectiveNs = this.logNamespaceUsed(descriptor.ns);
10677 return getPropertyNs(effectiveNs, descriptor);
10678 };
10679
10680 ElementSerializer.prototype.isLocalNs = function(ns) {
10681 return ns.uri === this.ns.uri;
10682 };
10683
10684 /**
10685 * Get the actual ns attribute name for the given element.
10686 *
10687 * @param {Object} element
10688 * @param {Boolean} [element.inherited=false]
10689 *
10690 * @return {Object} nsName
10691 */
10692 ElementSerializer.prototype.nsAttributeName = function(element) {
10693
10694 var ns;
10695
10696 if (isString(element)) {
10697 ns = parseName(element);
10698 } else {
10699 ns = element.ns;
10700 }
10701
10702 // return just local name for inherited attributes
10703 if (element.inherited) {
10704 return { localName: ns.localName };
10705 }
10706
10707 // parse + log effective ns
10708 var effectiveNs = this.logNamespaceUsed(ns);
10709
10710 // LOG ACTUAL namespace use
10711 this.getNamespaces().logUsed(effectiveNs);
10712
10713 // strip prefix if same namespace like parent
10714 if (this.isLocalNs(effectiveNs)) {
10715 return { localName: ns.localName };
10716 } else {
10717 return assign({ localName: ns.localName }, effectiveNs);
10718 }
10719 };
10720
10721 ElementSerializer.prototype.parseGeneric = function(element) {
10722
10723 var self = this,
10724 body = this.body;
10725
10726 var attributes = [];
10727
10728 forEach(element, function(val, key) {
10729
10730 var nonNsAttr;
10731
10732 if (key === '$body') {
10733 body.push(new BodySerializer().build({ type: 'String' }, val));
10734 } else
10735 if (key === '$children') {
10736 forEach(val, function(child) {
10737 body.push(new ElementSerializer(self).build(child));
10738 });
10739 } else
10740 if (key.indexOf('$') !== 0) {
10741 nonNsAttr = self.parseNsAttribute(element, key, val);
10742
10743 if (nonNsAttr) {
10744 attributes.push({ name: key, value: val });
10745 }
10746 }
10747 });
10748
10749 return attributes;
10750 };
10751
10752 ElementSerializer.prototype.parseNsAttribute = function(element, name, value) {
10753 var model = element.$model;
10754
10755 var nameNs = parseName(name);
10756
10757 var ns;
10758
10759 // parse xmlns:foo="http://foo.bar"
10760 if (nameNs.prefix === 'xmlns') {
10761 ns = { prefix: nameNs.localName, uri: value };
10762 }
10763
10764 // parse xmlns="http://foo.bar"
10765 if (!nameNs.prefix && nameNs.localName === 'xmlns') {
10766 ns = { uri: value };
10767 }
10768
10769 if (!ns) {
10770 return {
10771 name: name,
10772 value: value
10773 };
10774 }
10775
10776 if (model && model.getPackage(value)) {
10777
10778 // register well known namespace
10779 this.logNamespace(ns, true, true);
10780 } else {
10781
10782 // log custom namespace directly as used
10783 var actualNs = this.logNamespaceUsed(ns, true);
10784
10785 this.getNamespaces().logUsed(actualNs);
10786 }
10787 };
10788
10789
10790 /**
10791 * Parse namespaces and return a list of left over generic attributes
10792 *
10793 * @param {Object} element
10794 * @return {Array<Object>}
10795 */
10796 ElementSerializer.prototype.parseNsAttributes = function(element, attrs) {
10797 var self = this;
10798
10799 var genericAttrs = element.$attrs;
10800
10801 var attributes = [];
10802
10803 // parse namespace attributes first
10804 // and log them. push non namespace attributes to a list
10805 // and process them later
10806 forEach(genericAttrs, function(value, name) {
10807
10808 var nonNsAttr = self.parseNsAttribute(element, name, value);
10809
10810 if (nonNsAttr) {
10811 attributes.push(nonNsAttr);
10812 }
10813 });
10814
10815 return attributes;
10816 };
10817
10818 ElementSerializer.prototype.parseGenericAttributes = function(element, attributes) {
10819
10820 var self = this;
10821
10822 forEach(attributes, function(attr) {
10823
10824 // do not serialize xsi:type attribute
10825 // it is set manually based on the actual implementation type
10826 if (attr.name === XSI_TYPE) {
10827 return;
10828 }
10829
10830 try {
10831 self.addAttribute(self.nsAttributeName(attr.name), attr.value);
10832 } catch (e) {
10833 console.warn(
10834 'missing namespace information for ',
10835 attr.name, '=', attr.value, 'on', element,
10836 e);
10837 }
10838 });
10839 };
10840
10841 ElementSerializer.prototype.parseContainments = function(properties) {
10842
10843 var self = this,
10844 body = this.body,
10845 element = this.element;
10846
10847 forEach(properties, function(p) {
10848 var value = element.get(p.name),
10849 isReference = p.isReference,
10850 isMany = p.isMany;
10851
10852 if (!isMany) {
10853 value = [ value ];
10854 }
10855
10856 if (p.isBody) {
10857 body.push(new BodySerializer().build(p, value[0]));
10858 } else
10859 if (isSimple(p.type)) {
10860 forEach(value, function(v) {
10861 body.push(new ValueSerializer(self.addTagName(self.nsPropertyTagName(p))).build(p, v));
10862 });
10863 } else
10864 if (isReference) {
10865 forEach(value, function(v) {
10866 body.push(new ReferenceSerializer(self.addTagName(self.nsPropertyTagName(p))).build(v));
10867 });
10868 } else {
10869
10870 // allow serialization via type
10871 // rather than element name
10872 var asType = serializeAsType(p),
10873 asProperty = serializeAsProperty(p);
10874
10875 forEach(value, function(v) {
10876 var serializer;
10877
10878 if (asType) {
10879 serializer = new TypeSerializer(self, p);
10880 } else
10881 if (asProperty) {
10882 serializer = new ElementSerializer(self, p);
10883 } else {
10884 serializer = new ElementSerializer(self);
10885 }
10886
10887 body.push(serializer.build(v));
10888 });
10889 }
10890 });
10891 };
10892
10893 ElementSerializer.prototype.getNamespaces = function(local) {
10894
10895 var namespaces = this.namespaces,
10896 parent = this.parent,
10897 parentNamespaces;
10898
10899 if (!namespaces) {
10900 parentNamespaces = parent && parent.getNamespaces();
10901
10902 if (local || !parentNamespaces) {
10903 this.namespaces = namespaces = new Namespaces(parentNamespaces);
10904 } else {
10905 namespaces = parentNamespaces;
10906 }
10907 }
10908
10909 return namespaces;
10910 };
10911
10912 ElementSerializer.prototype.logNamespace = function(ns, wellknown, local) {
10913 var namespaces = this.getNamespaces(local);
10914
10915 var nsUri = ns.uri,
10916 nsPrefix = ns.prefix;
10917
10918 var existing = namespaces.byUri(nsUri);
10919
10920 if (!existing || local) {
10921 namespaces.add(ns, wellknown);
10922 }
10923
10924 namespaces.mapPrefix(nsPrefix, nsUri);
10925
10926 return ns;
10927 };
10928
10929 ElementSerializer.prototype.logNamespaceUsed = function(ns, local) {
10930 var element = this.element,
10931 model = element.$model,
10932 namespaces = this.getNamespaces(local);
10933
10934 // ns may be
10935 //
10936 // * prefix only
10937 // * prefix:uri
10938 // * localName only
10939
10940 var prefix = ns.prefix,
10941 uri = ns.uri,
10942 newPrefix, idx,
10943 wellknownUri;
10944
10945 // handle anonymous namespaces (elementForm=unqualified), cf. #23
10946 if (!prefix && !uri) {
10947 return { localName: ns.localName };
10948 }
10949
10950 wellknownUri = DEFAULT_NS_MAP[prefix] || model && (model.getPackage(prefix) || {}).uri;
10951
10952 uri = uri || wellknownUri || namespaces.uriByPrefix(prefix);
10953
10954 if (!uri) {
10955 throw new Error('no namespace uri given for prefix <' + prefix + '>');
10956 }
10957
10958 ns = namespaces.byUri(uri);
10959
10960 if (!ns) {
10961 newPrefix = prefix;
10962 idx = 1;
10963
10964 // find a prefix that is not mapped yet
10965 while (namespaces.uriByPrefix(newPrefix)) {
10966 newPrefix = prefix + '_' + idx++;
10967 }
10968
10969 ns = this.logNamespace({ prefix: newPrefix, uri: uri }, wellknownUri === uri);
10970 }
10971
10972 if (prefix) {
10973 namespaces.mapPrefix(prefix, uri);
10974 }
10975
10976 return ns;
10977 };
10978
10979 ElementSerializer.prototype.parseAttributes = function(properties) {
10980 var self = this,
10981 element = this.element;
10982
10983 forEach(properties, function(p) {
10984
10985 var value = element.get(p.name);
10986
10987 if (p.isReference) {
10988
10989 if (!p.isMany) {
10990 value = value.id;
10991 }
10992 else {
10993 var values = [];
10994 forEach(value, function(v) {
10995 values.push(v.id);
10996 });
10997
10998 // IDREFS is a whitespace-separated list of references.
10999 value = values.join(' ');
11000 }
11001
11002 }
11003
11004 self.addAttribute(self.nsAttributeName(p), value);
11005 });
11006 };
11007
11008 ElementSerializer.prototype.addTagName = function(nsTagName) {
11009 var actualNs = this.logNamespaceUsed(nsTagName);
11010
11011 this.getNamespaces().logUsed(actualNs);
11012
11013 return nsName(nsTagName);
11014 };
11015
11016 ElementSerializer.prototype.addAttribute = function(name, value) {
11017 var attrs = this.attrs;
11018
11019 if (isString(value)) {
11020 value = escapeAttr(value);
11021 }
11022
11023 attrs.push({ name: name, value: value });
11024 };
11025
11026 ElementSerializer.prototype.serializeAttributes = function(writer) {
11027 var attrs = this.attrs,
11028 namespaces = this.namespaces;
11029
11030 if (namespaces) {
11031 attrs = getNsAttrs(namespaces).concat(attrs);
11032 }
11033
11034 forEach(attrs, function(a) {
11035 writer
11036 .append(' ')
11037 .append(nsName(a.name)).append('="').append(a.value).append('"');
11038 });
11039 };
11040
11041 ElementSerializer.prototype.serializeTo = function(writer) {
11042 var firstBody = this.body[0],
11043 indent = firstBody && firstBody.constructor !== BodySerializer;
11044
11045 writer
11046 .appendIndent()
11047 .append('<' + this.tagName);
11048
11049 this.serializeAttributes(writer);
11050
11051 writer.append(firstBody ? '>' : ' />');
11052
11053 if (firstBody) {
11054
11055 if (indent) {
11056 writer
11057 .appendNewLine()
11058 .indent();
11059 }
11060
11061 forEach(this.body, function(b) {
11062 b.serializeTo(writer);
11063 });
11064
11065 if (indent) {
11066 writer
11067 .unindent()
11068 .appendIndent();
11069 }
11070
11071 writer.append('</' + this.tagName + '>');
11072 }
11073
11074 writer.appendNewLine();
11075 };
11076
11077 /**
11078 * A serializer for types that handles serialization of data types
11079 */
11080 function TypeSerializer(parent, propertyDescriptor) {
11081 ElementSerializer.call(this, parent, propertyDescriptor);
11082 }
11083
11084 inherits(TypeSerializer, ElementSerializer);
11085
11086 TypeSerializer.prototype.parseNsAttributes = function(element) {
11087
11088 // extracted attributes
11089 var attributes = ElementSerializer.prototype.parseNsAttributes.call(this, element);
11090
11091 var descriptor = element.$descriptor;
11092
11093 // only serialize xsi:type if necessary
11094 if (descriptor.name === this.propertyDescriptor.type) {
11095 return attributes;
11096 }
11097
11098 var typeNs = this.typeNs = this.nsTagName(descriptor);
11099 this.getNamespaces().logUsed(this.typeNs);
11100
11101 // add xsi:type attribute to represent the elements
11102 // actual type
11103
11104 var pkg = element.$model.getPackage(typeNs.uri),
11105 typePrefix = (pkg.xml && pkg.xml.typePrefix) || '';
11106
11107 this.addAttribute(
11108 this.nsAttributeName(XSI_TYPE),
11109 (typeNs.prefix ? typeNs.prefix + ':' : '') + typePrefix + descriptor.ns.localName
11110 );
11111
11112 return attributes;
11113 };
11114
11115 TypeSerializer.prototype.isLocalNs = function(ns) {
11116 return ns.uri === (this.typeNs || this.ns).uri;
11117 };
11118
11119 function SavingWriter() {
11120 this.value = '';
11121
11122 this.write = function(str) {
11123 this.value += str;
11124 };
11125 }
11126
11127 function FormatingWriter(out, format) {
11128
11129 var indent = [''];
11130
11131 this.append = function(str) {
11132 out.write(str);
11133
11134 return this;
11135 };
11136
11137 this.appendNewLine = function() {
11138 if (format) {
11139 out.write('\n');
11140 }
11141
11142 return this;
11143 };
11144
11145 this.appendIndent = function() {
11146 if (format) {
11147 out.write(indent.join(' '));
11148 }
11149
11150 return this;
11151 };
11152
11153 this.indent = function() {
11154 indent.push('');
11155 return this;
11156 };
11157
11158 this.unindent = function() {
11159 indent.pop();
11160 return this;
11161 };
11162 }
11163
11164 /**
11165 * A writer for meta-model backed document trees
11166 *
11167 * @param {Object} options output options to pass into the writer
11168 */
11169 function Writer(options) {
11170
11171 options = assign({ format: false, preamble: true }, options || {});
11172
11173 function toXML(tree, writer) {
11174 var internalWriter = writer || new SavingWriter();
11175 var formatingWriter = new FormatingWriter(internalWriter, options.format);
11176
11177 if (options.preamble) {
11178 formatingWriter.append(XML_PREAMBLE);
11179 }
11180
11181 new ElementSerializer().build(tree).serializeTo(formatingWriter);
11182
11183 if (!writer) {
11184 return internalWriter.value;
11185 }
11186 }
11187
11188 return {
11189 toXML: toXML
11190 };
11191 }
11192
11193 /**
11194 * A sub class of {@link Moddle} with support for import and export of BPMN 2.0 xml files.
11195 *
11196 * @class BpmnModdle
11197 * @extends Moddle
11198 *
11199 * @param {Object|Array} packages to use for instantiating the model
11200 * @param {Object} [options] additional options to pass over
11201 */
11202 function BpmnModdle(packages, options) {
11203 Moddle.call(this, packages, options);
11204 }
11205
11206 BpmnModdle.prototype = Object.create(Moddle.prototype);
11207
11208 /**
11209 * The fromXML result.
11210 *
11211 * @typedef {Object} ParseResult
11212 *
11213 * @property {ModdleElement} rootElement
11214 * @property {Array<Object>} references
11215 * @property {Array<Error>} warnings
11216 * @property {Object} elementsById - a mapping containing each ID -> ModdleElement
11217 */
11218
11219 /**
11220 * The fromXML error.
11221 *
11222 * @typedef {Error} ParseError
11223 *
11224 * @property {Array<Error>} warnings
11225 */
11226
11227 /**
11228 * Instantiates a BPMN model tree from a given xml string.
11229 *
11230 * @param {String} xmlStr
11231 * @param {String} [typeName='bpmn:Definitions'] name of the root element
11232 * @param {Object} [options] options to pass to the underlying reader
11233 *
11234 * @returns {Promise<ParseResult, ParseError>}
11235 */
11236 BpmnModdle.prototype.fromXML = function(xmlStr, typeName, options) {
11237
11238 if (!isString(typeName)) {
11239 options = typeName;
11240 typeName = 'bpmn:Definitions';
11241 }
11242
11243 var reader = new Reader(assign({ model: this, lax: true }, options));
11244 var rootHandler = reader.handler(typeName);
11245
11246 return reader.fromXML(xmlStr, rootHandler);
11247 };
11248
11249
11250 /**
11251 * The toXML result.
11252 *
11253 * @typedef {Object} SerializationResult
11254 *
11255 * @property {String} xml
11256 */
11257
11258 /**
11259 * Serializes a BPMN 2.0 object tree to XML.
11260 *
11261 * @param {String} element the root element, typically an instance of `bpmn:Definitions`
11262 * @param {Object} [options] to pass to the underlying writer
11263 *
11264 * @returns {Promise<SerializationResult, Error>}
11265 */
11266 BpmnModdle.prototype.toXML = function(element, options) {
11267
11268 var writer = new Writer(options);
11269
11270 return new Promise(function(resolve, reject) {
11271 try {
11272 var result = writer.toXML(element);
11273
11274 return resolve({
11275 xml: result
11276 });
11277 } catch (err) {
11278 return reject(err);
11279 }
11280 });
11281 };
11282
11283 var name = "BPMN20";
11284 var uri = "http://www.omg.org/spec/BPMN/20100524/MODEL";
11285 var prefix = "bpmn";
11286 var associations = [
11287 ];
11288 var types = [
11289 {
11290 name: "Interface",
11291 superClass: [
11292 "RootElement"
11293 ],
11294 properties: [
11295 {
11296 name: "name",
11297 isAttr: true,
11298 type: "String"
11299 },
11300 {
11301 name: "operations",
11302 type: "Operation",
11303 isMany: true
11304 },
11305 {
11306 name: "implementationRef",
11307 isAttr: true,
11308 type: "String"
11309 }
11310 ]
11311 },
11312 {
11313 name: "Operation",
11314 superClass: [
11315 "BaseElement"
11316 ],
11317 properties: [
11318 {
11319 name: "name",
11320 isAttr: true,
11321 type: "String"
11322 },
11323 {
11324 name: "inMessageRef",
11325 type: "Message",
11326 isReference: true
11327 },
11328 {
11329 name: "outMessageRef",
11330 type: "Message",
11331 isReference: true
11332 },
11333 {
11334 name: "errorRef",
11335 type: "Error",
11336 isMany: true,
11337 isReference: true
11338 },
11339 {
11340 name: "implementationRef",
11341 isAttr: true,
11342 type: "String"
11343 }
11344 ]
11345 },
11346 {
11347 name: "EndPoint",
11348 superClass: [
11349 "RootElement"
11350 ]
11351 },
11352 {
11353 name: "Auditing",
11354 superClass: [
11355 "BaseElement"
11356 ]
11357 },
11358 {
11359 name: "GlobalTask",
11360 superClass: [
11361 "CallableElement"
11362 ],
11363 properties: [
11364 {
11365 name: "resources",
11366 type: "ResourceRole",
11367 isMany: true
11368 }
11369 ]
11370 },
11371 {
11372 name: "Monitoring",
11373 superClass: [
11374 "BaseElement"
11375 ]
11376 },
11377 {
11378 name: "Performer",
11379 superClass: [
11380 "ResourceRole"
11381 ]
11382 },
11383 {
11384 name: "Process",
11385 superClass: [
11386 "FlowElementsContainer",
11387 "CallableElement"
11388 ],
11389 properties: [
11390 {
11391 name: "processType",
11392 type: "ProcessType",
11393 isAttr: true
11394 },
11395 {
11396 name: "isClosed",
11397 isAttr: true,
11398 type: "Boolean"
11399 },
11400 {
11401 name: "auditing",
11402 type: "Auditing"
11403 },
11404 {
11405 name: "monitoring",
11406 type: "Monitoring"
11407 },
11408 {
11409 name: "properties",
11410 type: "Property",
11411 isMany: true
11412 },
11413 {
11414 name: "laneSets",
11415 isMany: true,
11416 replaces: "FlowElementsContainer#laneSets",
11417 type: "LaneSet"
11418 },
11419 {
11420 name: "flowElements",
11421 isMany: true,
11422 replaces: "FlowElementsContainer#flowElements",
11423 type: "FlowElement"
11424 },
11425 {
11426 name: "artifacts",
11427 type: "Artifact",
11428 isMany: true
11429 },
11430 {
11431 name: "resources",
11432 type: "ResourceRole",
11433 isMany: true
11434 },
11435 {
11436 name: "correlationSubscriptions",
11437 type: "CorrelationSubscription",
11438 isMany: true
11439 },
11440 {
11441 name: "supports",
11442 type: "Process",
11443 isMany: true,
11444 isReference: true
11445 },
11446 {
11447 name: "definitionalCollaborationRef",
11448 type: "Collaboration",
11449 isAttr: true,
11450 isReference: true
11451 },
11452 {
11453 name: "isExecutable",
11454 isAttr: true,
11455 type: "Boolean"
11456 }
11457 ]
11458 },
11459 {
11460 name: "LaneSet",
11461 superClass: [
11462 "BaseElement"
11463 ],
11464 properties: [
11465 {
11466 name: "lanes",
11467 type: "Lane",
11468 isMany: true
11469 },
11470 {
11471 name: "name",
11472 isAttr: true,
11473 type: "String"
11474 }
11475 ]
11476 },
11477 {
11478 name: "Lane",
11479 superClass: [
11480 "BaseElement"
11481 ],
11482 properties: [
11483 {
11484 name: "name",
11485 isAttr: true,
11486 type: "String"
11487 },
11488 {
11489 name: "partitionElementRef",
11490 type: "BaseElement",
11491 isAttr: true,
11492 isReference: true
11493 },
11494 {
11495 name: "partitionElement",
11496 type: "BaseElement"
11497 },
11498 {
11499 name: "flowNodeRef",
11500 type: "FlowNode",
11501 isMany: true,
11502 isReference: true
11503 },
11504 {
11505 name: "childLaneSet",
11506 type: "LaneSet",
11507 xml: {
11508 serialize: "xsi:type"
11509 }
11510 }
11511 ]
11512 },
11513 {
11514 name: "GlobalManualTask",
11515 superClass: [
11516 "GlobalTask"
11517 ]
11518 },
11519 {
11520 name: "ManualTask",
11521 superClass: [
11522 "Task"
11523 ]
11524 },
11525 {
11526 name: "UserTask",
11527 superClass: [
11528 "Task"
11529 ],
11530 properties: [
11531 {
11532 name: "renderings",
11533 type: "Rendering",
11534 isMany: true
11535 },
11536 {
11537 name: "implementation",
11538 isAttr: true,
11539 type: "String"
11540 }
11541 ]
11542 },
11543 {
11544 name: "Rendering",
11545 superClass: [
11546 "BaseElement"
11547 ]
11548 },
11549 {
11550 name: "HumanPerformer",
11551 superClass: [
11552 "Performer"
11553 ]
11554 },
11555 {
11556 name: "PotentialOwner",
11557 superClass: [
11558 "HumanPerformer"
11559 ]
11560 },
11561 {
11562 name: "GlobalUserTask",
11563 superClass: [
11564 "GlobalTask"
11565 ],
11566 properties: [
11567 {
11568 name: "implementation",
11569 isAttr: true,
11570 type: "String"
11571 },
11572 {
11573 name: "renderings",
11574 type: "Rendering",
11575 isMany: true
11576 }
11577 ]
11578 },
11579 {
11580 name: "Gateway",
11581 isAbstract: true,
11582 superClass: [
11583 "FlowNode"
11584 ],
11585 properties: [
11586 {
11587 name: "gatewayDirection",
11588 type: "GatewayDirection",
11589 "default": "Unspecified",
11590 isAttr: true
11591 }
11592 ]
11593 },
11594 {
11595 name: "EventBasedGateway",
11596 superClass: [
11597 "Gateway"
11598 ],
11599 properties: [
11600 {
11601 name: "instantiate",
11602 "default": false,
11603 isAttr: true,
11604 type: "Boolean"
11605 },
11606 {
11607 name: "eventGatewayType",
11608 type: "EventBasedGatewayType",
11609 isAttr: true,
11610 "default": "Exclusive"
11611 }
11612 ]
11613 },
11614 {
11615 name: "ComplexGateway",
11616 superClass: [
11617 "Gateway"
11618 ],
11619 properties: [
11620 {
11621 name: "activationCondition",
11622 type: "Expression",
11623 xml: {
11624 serialize: "xsi:type"
11625 }
11626 },
11627 {
11628 name: "default",
11629 type: "SequenceFlow",
11630 isAttr: true,
11631 isReference: true
11632 }
11633 ]
11634 },
11635 {
11636 name: "ExclusiveGateway",
11637 superClass: [
11638 "Gateway"
11639 ],
11640 properties: [
11641 {
11642 name: "default",
11643 type: "SequenceFlow",
11644 isAttr: true,
11645 isReference: true
11646 }
11647 ]
11648 },
11649 {
11650 name: "InclusiveGateway",
11651 superClass: [
11652 "Gateway"
11653 ],
11654 properties: [
11655 {
11656 name: "default",
11657 type: "SequenceFlow",
11658 isAttr: true,
11659 isReference: true
11660 }
11661 ]
11662 },
11663 {
11664 name: "ParallelGateway",
11665 superClass: [
11666 "Gateway"
11667 ]
11668 },
11669 {
11670 name: "RootElement",
11671 isAbstract: true,
11672 superClass: [
11673 "BaseElement"
11674 ]
11675 },
11676 {
11677 name: "Relationship",
11678 superClass: [
11679 "BaseElement"
11680 ],
11681 properties: [
11682 {
11683 name: "type",
11684 isAttr: true,
11685 type: "String"
11686 },
11687 {
11688 name: "direction",
11689 type: "RelationshipDirection",
11690 isAttr: true
11691 },
11692 {
11693 name: "source",
11694 isMany: true,
11695 isReference: true,
11696 type: "Element"
11697 },
11698 {
11699 name: "target",
11700 isMany: true,
11701 isReference: true,
11702 type: "Element"
11703 }
11704 ]
11705 },
11706 {
11707 name: "BaseElement",
11708 isAbstract: true,
11709 properties: [
11710 {
11711 name: "id",
11712 isAttr: true,
11713 type: "String",
11714 isId: true
11715 },
11716 {
11717 name: "documentation",
11718 type: "Documentation",
11719 isMany: true
11720 },
11721 {
11722 name: "extensionDefinitions",
11723 type: "ExtensionDefinition",
11724 isMany: true,
11725 isReference: true
11726 },
11727 {
11728 name: "extensionElements",
11729 type: "ExtensionElements"
11730 }
11731 ]
11732 },
11733 {
11734 name: "Extension",
11735 properties: [
11736 {
11737 name: "mustUnderstand",
11738 "default": false,
11739 isAttr: true,
11740 type: "Boolean"
11741 },
11742 {
11743 name: "definition",
11744 type: "ExtensionDefinition",
11745 isAttr: true,
11746 isReference: true
11747 }
11748 ]
11749 },
11750 {
11751 name: "ExtensionDefinition",
11752 properties: [
11753 {
11754 name: "name",
11755 isAttr: true,
11756 type: "String"
11757 },
11758 {
11759 name: "extensionAttributeDefinitions",
11760 type: "ExtensionAttributeDefinition",
11761 isMany: true
11762 }
11763 ]
11764 },
11765 {
11766 name: "ExtensionAttributeDefinition",
11767 properties: [
11768 {
11769 name: "name",
11770 isAttr: true,
11771 type: "String"
11772 },
11773 {
11774 name: "type",
11775 isAttr: true,
11776 type: "String"
11777 },
11778 {
11779 name: "isReference",
11780 "default": false,
11781 isAttr: true,
11782 type: "Boolean"
11783 },
11784 {
11785 name: "extensionDefinition",
11786 type: "ExtensionDefinition",
11787 isAttr: true,
11788 isReference: true
11789 }
11790 ]
11791 },
11792 {
11793 name: "ExtensionElements",
11794 properties: [
11795 {
11796 name: "valueRef",
11797 isAttr: true,
11798 isReference: true,
11799 type: "Element"
11800 },
11801 {
11802 name: "values",
11803 type: "Element",
11804 isMany: true
11805 },
11806 {
11807 name: "extensionAttributeDefinition",
11808 type: "ExtensionAttributeDefinition",
11809 isAttr: true,
11810 isReference: true
11811 }
11812 ]
11813 },
11814 {
11815 name: "Documentation",
11816 superClass: [
11817 "BaseElement"
11818 ],
11819 properties: [
11820 {
11821 name: "text",
11822 type: "String",
11823 isBody: true
11824 },
11825 {
11826 name: "textFormat",
11827 "default": "text/plain",
11828 isAttr: true,
11829 type: "String"
11830 }
11831 ]
11832 },
11833 {
11834 name: "Event",
11835 isAbstract: true,
11836 superClass: [
11837 "FlowNode",
11838 "InteractionNode"
11839 ],
11840 properties: [
11841 {
11842 name: "properties",
11843 type: "Property",
11844 isMany: true
11845 }
11846 ]
11847 },
11848 {
11849 name: "IntermediateCatchEvent",
11850 superClass: [
11851 "CatchEvent"
11852 ]
11853 },
11854 {
11855 name: "IntermediateThrowEvent",
11856 superClass: [
11857 "ThrowEvent"
11858 ]
11859 },
11860 {
11861 name: "EndEvent",
11862 superClass: [
11863 "ThrowEvent"
11864 ]
11865 },
11866 {
11867 name: "StartEvent",
11868 superClass: [
11869 "CatchEvent"
11870 ],
11871 properties: [
11872 {
11873 name: "isInterrupting",
11874 "default": true,
11875 isAttr: true,
11876 type: "Boolean"
11877 }
11878 ]
11879 },
11880 {
11881 name: "ThrowEvent",
11882 isAbstract: true,
11883 superClass: [
11884 "Event"
11885 ],
11886 properties: [
11887 {
11888 name: "dataInputs",
11889 type: "DataInput",
11890 isMany: true
11891 },
11892 {
11893 name: "dataInputAssociations",
11894 type: "DataInputAssociation",
11895 isMany: true
11896 },
11897 {
11898 name: "inputSet",
11899 type: "InputSet"
11900 },
11901 {
11902 name: "eventDefinitions",
11903 type: "EventDefinition",
11904 isMany: true
11905 },
11906 {
11907 name: "eventDefinitionRef",
11908 type: "EventDefinition",
11909 isMany: true,
11910 isReference: true
11911 }
11912 ]
11913 },
11914 {
11915 name: "CatchEvent",
11916 isAbstract: true,
11917 superClass: [
11918 "Event"
11919 ],
11920 properties: [
11921 {
11922 name: "parallelMultiple",
11923 isAttr: true,
11924 type: "Boolean",
11925 "default": false
11926 },
11927 {
11928 name: "dataOutputs",
11929 type: "DataOutput",
11930 isMany: true
11931 },
11932 {
11933 name: "dataOutputAssociations",
11934 type: "DataOutputAssociation",
11935 isMany: true
11936 },
11937 {
11938 name: "outputSet",
11939 type: "OutputSet"
11940 },
11941 {
11942 name: "eventDefinitions",
11943 type: "EventDefinition",
11944 isMany: true
11945 },
11946 {
11947 name: "eventDefinitionRef",
11948 type: "EventDefinition",
11949 isMany: true,
11950 isReference: true
11951 }
11952 ]
11953 },
11954 {
11955 name: "BoundaryEvent",
11956 superClass: [
11957 "CatchEvent"
11958 ],
11959 properties: [
11960 {
11961 name: "cancelActivity",
11962 "default": true,
11963 isAttr: true,
11964 type: "Boolean"
11965 },
11966 {
11967 name: "attachedToRef",
11968 type: "Activity",
11969 isAttr: true,
11970 isReference: true
11971 }
11972 ]
11973 },
11974 {
11975 name: "EventDefinition",
11976 isAbstract: true,
11977 superClass: [
11978 "RootElement"
11979 ]
11980 },
11981 {
11982 name: "CancelEventDefinition",
11983 superClass: [
11984 "EventDefinition"
11985 ]
11986 },
11987 {
11988 name: "ErrorEventDefinition",
11989 superClass: [
11990 "EventDefinition"
11991 ],
11992 properties: [
11993 {
11994 name: "errorRef",
11995 type: "Error",
11996 isAttr: true,
11997 isReference: true
11998 }
11999 ]
12000 },
12001 {
12002 name: "TerminateEventDefinition",
12003 superClass: [
12004 "EventDefinition"
12005 ]
12006 },
12007 {
12008 name: "EscalationEventDefinition",
12009 superClass: [
12010 "EventDefinition"
12011 ],
12012 properties: [
12013 {
12014 name: "escalationRef",
12015 type: "Escalation",
12016 isAttr: true,
12017 isReference: true
12018 }
12019 ]
12020 },
12021 {
12022 name: "Escalation",
12023 properties: [
12024 {
12025 name: "structureRef",
12026 type: "ItemDefinition",
12027 isAttr: true,
12028 isReference: true
12029 },
12030 {
12031 name: "name",
12032 isAttr: true,
12033 type: "String"
12034 },
12035 {
12036 name: "escalationCode",
12037 isAttr: true,
12038 type: "String"
12039 }
12040 ],
12041 superClass: [
12042 "RootElement"
12043 ]
12044 },
12045 {
12046 name: "CompensateEventDefinition",
12047 superClass: [
12048 "EventDefinition"
12049 ],
12050 properties: [
12051 {
12052 name: "waitForCompletion",
12053 isAttr: true,
12054 type: "Boolean",
12055 "default": true
12056 },
12057 {
12058 name: "activityRef",
12059 type: "Activity",
12060 isAttr: true,
12061 isReference: true
12062 }
12063 ]
12064 },
12065 {
12066 name: "TimerEventDefinition",
12067 superClass: [
12068 "EventDefinition"
12069 ],
12070 properties: [
12071 {
12072 name: "timeDate",
12073 type: "Expression",
12074 xml: {
12075 serialize: "xsi:type"
12076 }
12077 },
12078 {
12079 name: "timeCycle",
12080 type: "Expression",
12081 xml: {
12082 serialize: "xsi:type"
12083 }
12084 },
12085 {
12086 name: "timeDuration",
12087 type: "Expression",
12088 xml: {
12089 serialize: "xsi:type"
12090 }
12091 }
12092 ]
12093 },
12094 {
12095 name: "LinkEventDefinition",
12096 superClass: [
12097 "EventDefinition"
12098 ],
12099 properties: [
12100 {
12101 name: "name",
12102 isAttr: true,
12103 type: "String"
12104 },
12105 {
12106 name: "target",
12107 type: "LinkEventDefinition",
12108 isAttr: true,
12109 isReference: true
12110 },
12111 {
12112 name: "source",
12113 type: "LinkEventDefinition",
12114 isMany: true,
12115 isReference: true
12116 }
12117 ]
12118 },
12119 {
12120 name: "MessageEventDefinition",
12121 superClass: [
12122 "EventDefinition"
12123 ],
12124 properties: [
12125 {
12126 name: "messageRef",
12127 type: "Message",
12128 isAttr: true,
12129 isReference: true
12130 },
12131 {
12132 name: "operationRef",
12133 type: "Operation",
12134 isAttr: true,
12135 isReference: true
12136 }
12137 ]
12138 },
12139 {
12140 name: "ConditionalEventDefinition",
12141 superClass: [
12142 "EventDefinition"
12143 ],
12144 properties: [
12145 {
12146 name: "condition",
12147 type: "Expression",
12148 xml: {
12149 serialize: "xsi:type"
12150 }
12151 }
12152 ]
12153 },
12154 {
12155 name: "SignalEventDefinition",
12156 superClass: [
12157 "EventDefinition"
12158 ],
12159 properties: [
12160 {
12161 name: "signalRef",
12162 type: "Signal",
12163 isAttr: true,
12164 isReference: true
12165 }
12166 ]
12167 },
12168 {
12169 name: "Signal",
12170 superClass: [
12171 "RootElement"
12172 ],
12173 properties: [
12174 {
12175 name: "structureRef",
12176 type: "ItemDefinition",
12177 isAttr: true,
12178 isReference: true
12179 },
12180 {
12181 name: "name",
12182 isAttr: true,
12183 type: "String"
12184 }
12185 ]
12186 },
12187 {
12188 name: "ImplicitThrowEvent",
12189 superClass: [
12190 "ThrowEvent"
12191 ]
12192 },
12193 {
12194 name: "DataState",
12195 superClass: [
12196 "BaseElement"
12197 ],
12198 properties: [
12199 {
12200 name: "name",
12201 isAttr: true,
12202 type: "String"
12203 }
12204 ]
12205 },
12206 {
12207 name: "ItemAwareElement",
12208 superClass: [
12209 "BaseElement"
12210 ],
12211 properties: [
12212 {
12213 name: "itemSubjectRef",
12214 type: "ItemDefinition",
12215 isAttr: true,
12216 isReference: true
12217 },
12218 {
12219 name: "dataState",
12220 type: "DataState"
12221 }
12222 ]
12223 },
12224 {
12225 name: "DataAssociation",
12226 superClass: [
12227 "BaseElement"
12228 ],
12229 properties: [
12230 {
12231 name: "sourceRef",
12232 type: "ItemAwareElement",
12233 isMany: true,
12234 isReference: true
12235 },
12236 {
12237 name: "targetRef",
12238 type: "ItemAwareElement",
12239 isReference: true
12240 },
12241 {
12242 name: "transformation",
12243 type: "FormalExpression",
12244 xml: {
12245 serialize: "property"
12246 }
12247 },
12248 {
12249 name: "assignment",
12250 type: "Assignment",
12251 isMany: true
12252 }
12253 ]
12254 },
12255 {
12256 name: "DataInput",
12257 superClass: [
12258 "ItemAwareElement"
12259 ],
12260 properties: [
12261 {
12262 name: "name",
12263 isAttr: true,
12264 type: "String"
12265 },
12266 {
12267 name: "isCollection",
12268 "default": false,
12269 isAttr: true,
12270 type: "Boolean"
12271 },
12272 {
12273 name: "inputSetRef",
12274 type: "InputSet",
12275 isMany: true,
12276 isVirtual: true,
12277 isReference: true
12278 },
12279 {
12280 name: "inputSetWithOptional",
12281 type: "InputSet",
12282 isMany: true,
12283 isVirtual: true,
12284 isReference: true
12285 },
12286 {
12287 name: "inputSetWithWhileExecuting",
12288 type: "InputSet",
12289 isMany: true,
12290 isVirtual: true,
12291 isReference: true
12292 }
12293 ]
12294 },
12295 {
12296 name: "DataOutput",
12297 superClass: [
12298 "ItemAwareElement"
12299 ],
12300 properties: [
12301 {
12302 name: "name",
12303 isAttr: true,
12304 type: "String"
12305 },
12306 {
12307 name: "isCollection",
12308 "default": false,
12309 isAttr: true,
12310 type: "Boolean"
12311 },
12312 {
12313 name: "outputSetRef",
12314 type: "OutputSet",
12315 isMany: true,
12316 isVirtual: true,
12317 isReference: true
12318 },
12319 {
12320 name: "outputSetWithOptional",
12321 type: "OutputSet",
12322 isMany: true,
12323 isVirtual: true,
12324 isReference: true
12325 },
12326 {
12327 name: "outputSetWithWhileExecuting",
12328 type: "OutputSet",
12329 isMany: true,
12330 isVirtual: true,
12331 isReference: true
12332 }
12333 ]
12334 },
12335 {
12336 name: "InputSet",
12337 superClass: [
12338 "BaseElement"
12339 ],
12340 properties: [
12341 {
12342 name: "name",
12343 isAttr: true,
12344 type: "String"
12345 },
12346 {
12347 name: "dataInputRefs",
12348 type: "DataInput",
12349 isMany: true,
12350 isReference: true
12351 },
12352 {
12353 name: "optionalInputRefs",
12354 type: "DataInput",
12355 isMany: true,
12356 isReference: true
12357 },
12358 {
12359 name: "whileExecutingInputRefs",
12360 type: "DataInput",
12361 isMany: true,
12362 isReference: true
12363 },
12364 {
12365 name: "outputSetRefs",
12366 type: "OutputSet",
12367 isMany: true,
12368 isReference: true
12369 }
12370 ]
12371 },
12372 {
12373 name: "OutputSet",
12374 superClass: [
12375 "BaseElement"
12376 ],
12377 properties: [
12378 {
12379 name: "dataOutputRefs",
12380 type: "DataOutput",
12381 isMany: true,
12382 isReference: true
12383 },
12384 {
12385 name: "name",
12386 isAttr: true,
12387 type: "String"
12388 },
12389 {
12390 name: "inputSetRefs",
12391 type: "InputSet",
12392 isMany: true,
12393 isReference: true
12394 },
12395 {
12396 name: "optionalOutputRefs",
12397 type: "DataOutput",
12398 isMany: true,
12399 isReference: true
12400 },
12401 {
12402 name: "whileExecutingOutputRefs",
12403 type: "DataOutput",
12404 isMany: true,
12405 isReference: true
12406 }
12407 ]
12408 },
12409 {
12410 name: "Property",
12411 superClass: [
12412 "ItemAwareElement"
12413 ],
12414 properties: [
12415 {
12416 name: "name",
12417 isAttr: true,
12418 type: "String"
12419 }
12420 ]
12421 },
12422 {
12423 name: "DataInputAssociation",
12424 superClass: [
12425 "DataAssociation"
12426 ]
12427 },
12428 {
12429 name: "DataOutputAssociation",
12430 superClass: [
12431 "DataAssociation"
12432 ]
12433 },
12434 {
12435 name: "InputOutputSpecification",
12436 superClass: [
12437 "BaseElement"
12438 ],
12439 properties: [
12440 {
12441 name: "dataInputs",
12442 type: "DataInput",
12443 isMany: true
12444 },
12445 {
12446 name: "dataOutputs",
12447 type: "DataOutput",
12448 isMany: true
12449 },
12450 {
12451 name: "inputSets",
12452 type: "InputSet",
12453 isMany: true
12454 },
12455 {
12456 name: "outputSets",
12457 type: "OutputSet",
12458 isMany: true
12459 }
12460 ]
12461 },
12462 {
12463 name: "DataObject",
12464 superClass: [
12465 "FlowElement",
12466 "ItemAwareElement"
12467 ],
12468 properties: [
12469 {
12470 name: "isCollection",
12471 "default": false,
12472 isAttr: true,
12473 type: "Boolean"
12474 }
12475 ]
12476 },
12477 {
12478 name: "InputOutputBinding",
12479 properties: [
12480 {
12481 name: "inputDataRef",
12482 type: "InputSet",
12483 isAttr: true,
12484 isReference: true
12485 },
12486 {
12487 name: "outputDataRef",
12488 type: "OutputSet",
12489 isAttr: true,
12490 isReference: true
12491 },
12492 {
12493 name: "operationRef",
12494 type: "Operation",
12495 isAttr: true,
12496 isReference: true
12497 }
12498 ]
12499 },
12500 {
12501 name: "Assignment",
12502 superClass: [
12503 "BaseElement"
12504 ],
12505 properties: [
12506 {
12507 name: "from",
12508 type: "Expression",
12509 xml: {
12510 serialize: "xsi:type"
12511 }
12512 },
12513 {
12514 name: "to",
12515 type: "Expression",
12516 xml: {
12517 serialize: "xsi:type"
12518 }
12519 }
12520 ]
12521 },
12522 {
12523 name: "DataStore",
12524 superClass: [
12525 "RootElement",
12526 "ItemAwareElement"
12527 ],
12528 properties: [
12529 {
12530 name: "name",
12531 isAttr: true,
12532 type: "String"
12533 },
12534 {
12535 name: "capacity",
12536 isAttr: true,
12537 type: "Integer"
12538 },
12539 {
12540 name: "isUnlimited",
12541 "default": true,
12542 isAttr: true,
12543 type: "Boolean"
12544 }
12545 ]
12546 },
12547 {
12548 name: "DataStoreReference",
12549 superClass: [
12550 "ItemAwareElement",
12551 "FlowElement"
12552 ],
12553 properties: [
12554 {
12555 name: "dataStoreRef",
12556 type: "DataStore",
12557 isAttr: true,
12558 isReference: true
12559 }
12560 ]
12561 },
12562 {
12563 name: "DataObjectReference",
12564 superClass: [
12565 "ItemAwareElement",
12566 "FlowElement"
12567 ],
12568 properties: [
12569 {
12570 name: "dataObjectRef",
12571 type: "DataObject",
12572 isAttr: true,
12573 isReference: true
12574 }
12575 ]
12576 },
12577 {
12578 name: "ConversationLink",
12579 superClass: [
12580 "BaseElement"
12581 ],
12582 properties: [
12583 {
12584 name: "sourceRef",
12585 type: "InteractionNode",
12586 isAttr: true,
12587 isReference: true
12588 },
12589 {
12590 name: "targetRef",
12591 type: "InteractionNode",
12592 isAttr: true,
12593 isReference: true
12594 },
12595 {
12596 name: "name",
12597 isAttr: true,
12598 type: "String"
12599 }
12600 ]
12601 },
12602 {
12603 name: "ConversationAssociation",
12604 superClass: [
12605 "BaseElement"
12606 ],
12607 properties: [
12608 {
12609 name: "innerConversationNodeRef",
12610 type: "ConversationNode",
12611 isAttr: true,
12612 isReference: true
12613 },
12614 {
12615 name: "outerConversationNodeRef",
12616 type: "ConversationNode",
12617 isAttr: true,
12618 isReference: true
12619 }
12620 ]
12621 },
12622 {
12623 name: "CallConversation",
12624 superClass: [
12625 "ConversationNode"
12626 ],
12627 properties: [
12628 {
12629 name: "calledCollaborationRef",
12630 type: "Collaboration",
12631 isAttr: true,
12632 isReference: true
12633 },
12634 {
12635 name: "participantAssociations",
12636 type: "ParticipantAssociation",
12637 isMany: true
12638 }
12639 ]
12640 },
12641 {
12642 name: "Conversation",
12643 superClass: [
12644 "ConversationNode"
12645 ]
12646 },
12647 {
12648 name: "SubConversation",
12649 superClass: [
12650 "ConversationNode"
12651 ],
12652 properties: [
12653 {
12654 name: "conversationNodes",
12655 type: "ConversationNode",
12656 isMany: true
12657 }
12658 ]
12659 },
12660 {
12661 name: "ConversationNode",
12662 isAbstract: true,
12663 superClass: [
12664 "InteractionNode",
12665 "BaseElement"
12666 ],
12667 properties: [
12668 {
12669 name: "name",
12670 isAttr: true,
12671 type: "String"
12672 },
12673 {
12674 name: "participantRef",
12675 type: "Participant",
12676 isMany: true,
12677 isReference: true
12678 },
12679 {
12680 name: "messageFlowRefs",
12681 type: "MessageFlow",
12682 isMany: true,
12683 isReference: true
12684 },
12685 {
12686 name: "correlationKeys",
12687 type: "CorrelationKey",
12688 isMany: true
12689 }
12690 ]
12691 },
12692 {
12693 name: "GlobalConversation",
12694 superClass: [
12695 "Collaboration"
12696 ]
12697 },
12698 {
12699 name: "PartnerEntity",
12700 superClass: [
12701 "RootElement"
12702 ],
12703 properties: [
12704 {
12705 name: "name",
12706 isAttr: true,
12707 type: "String"
12708 },
12709 {
12710 name: "participantRef",
12711 type: "Participant",
12712 isMany: true,
12713 isReference: true
12714 }
12715 ]
12716 },
12717 {
12718 name: "PartnerRole",
12719 superClass: [
12720 "RootElement"
12721 ],
12722 properties: [
12723 {
12724 name: "name",
12725 isAttr: true,
12726 type: "String"
12727 },
12728 {
12729 name: "participantRef",
12730 type: "Participant",
12731 isMany: true,
12732 isReference: true
12733 }
12734 ]
12735 },
12736 {
12737 name: "CorrelationProperty",
12738 superClass: [
12739 "RootElement"
12740 ],
12741 properties: [
12742 {
12743 name: "correlationPropertyRetrievalExpression",
12744 type: "CorrelationPropertyRetrievalExpression",
12745 isMany: true
12746 },
12747 {
12748 name: "name",
12749 isAttr: true,
12750 type: "String"
12751 },
12752 {
12753 name: "type",
12754 type: "ItemDefinition",
12755 isAttr: true,
12756 isReference: true
12757 }
12758 ]
12759 },
12760 {
12761 name: "Error",
12762 superClass: [
12763 "RootElement"
12764 ],
12765 properties: [
12766 {
12767 name: "structureRef",
12768 type: "ItemDefinition",
12769 isAttr: true,
12770 isReference: true
12771 },
12772 {
12773 name: "name",
12774 isAttr: true,
12775 type: "String"
12776 },
12777 {
12778 name: "errorCode",
12779 isAttr: true,
12780 type: "String"
12781 }
12782 ]
12783 },
12784 {
12785 name: "CorrelationKey",
12786 superClass: [
12787 "BaseElement"
12788 ],
12789 properties: [
12790 {
12791 name: "correlationPropertyRef",
12792 type: "CorrelationProperty",
12793 isMany: true,
12794 isReference: true
12795 },
12796 {
12797 name: "name",
12798 isAttr: true,
12799 type: "String"
12800 }
12801 ]
12802 },
12803 {
12804 name: "Expression",
12805 superClass: [
12806 "BaseElement"
12807 ],
12808 isAbstract: false,
12809 properties: [
12810 {
12811 name: "body",
12812 isBody: true,
12813 type: "String"
12814 }
12815 ]
12816 },
12817 {
12818 name: "FormalExpression",
12819 superClass: [
12820 "Expression"
12821 ],
12822 properties: [
12823 {
12824 name: "language",
12825 isAttr: true,
12826 type: "String"
12827 },
12828 {
12829 name: "evaluatesToTypeRef",
12830 type: "ItemDefinition",
12831 isAttr: true,
12832 isReference: true
12833 }
12834 ]
12835 },
12836 {
12837 name: "Message",
12838 superClass: [
12839 "RootElement"
12840 ],
12841 properties: [
12842 {
12843 name: "name",
12844 isAttr: true,
12845 type: "String"
12846 },
12847 {
12848 name: "itemRef",
12849 type: "ItemDefinition",
12850 isAttr: true,
12851 isReference: true
12852 }
12853 ]
12854 },
12855 {
12856 name: "ItemDefinition",
12857 superClass: [
12858 "RootElement"
12859 ],
12860 properties: [
12861 {
12862 name: "itemKind",
12863 type: "ItemKind",
12864 isAttr: true
12865 },
12866 {
12867 name: "structureRef",
12868 isAttr: true,
12869 type: "String"
12870 },
12871 {
12872 name: "isCollection",
12873 "default": false,
12874 isAttr: true,
12875 type: "Boolean"
12876 },
12877 {
12878 name: "import",
12879 type: "Import",
12880 isAttr: true,
12881 isReference: true
12882 }
12883 ]
12884 },
12885 {
12886 name: "FlowElement",
12887 isAbstract: true,
12888 superClass: [
12889 "BaseElement"
12890 ],
12891 properties: [
12892 {
12893 name: "name",
12894 isAttr: true,
12895 type: "String"
12896 },
12897 {
12898 name: "auditing",
12899 type: "Auditing"
12900 },
12901 {
12902 name: "monitoring",
12903 type: "Monitoring"
12904 },
12905 {
12906 name: "categoryValueRef",
12907 type: "CategoryValue",
12908 isMany: true,
12909 isReference: true
12910 }
12911 ]
12912 },
12913 {
12914 name: "SequenceFlow",
12915 superClass: [
12916 "FlowElement"
12917 ],
12918 properties: [
12919 {
12920 name: "isImmediate",
12921 isAttr: true,
12922 type: "Boolean"
12923 },
12924 {
12925 name: "conditionExpression",
12926 type: "Expression",
12927 xml: {
12928 serialize: "xsi:type"
12929 }
12930 },
12931 {
12932 name: "sourceRef",
12933 type: "FlowNode",
12934 isAttr: true,
12935 isReference: true
12936 },
12937 {
12938 name: "targetRef",
12939 type: "FlowNode",
12940 isAttr: true,
12941 isReference: true
12942 }
12943 ]
12944 },
12945 {
12946 name: "FlowElementsContainer",
12947 isAbstract: true,
12948 superClass: [
12949 "BaseElement"
12950 ],
12951 properties: [
12952 {
12953 name: "laneSets",
12954 type: "LaneSet",
12955 isMany: true
12956 },
12957 {
12958 name: "flowElements",
12959 type: "FlowElement",
12960 isMany: true
12961 }
12962 ]
12963 },
12964 {
12965 name: "CallableElement",
12966 isAbstract: true,
12967 superClass: [
12968 "RootElement"
12969 ],
12970 properties: [
12971 {
12972 name: "name",
12973 isAttr: true,
12974 type: "String"
12975 },
12976 {
12977 name: "ioSpecification",
12978 type: "InputOutputSpecification",
12979 xml: {
12980 serialize: "property"
12981 }
12982 },
12983 {
12984 name: "supportedInterfaceRef",
12985 type: "Interface",
12986 isMany: true,
12987 isReference: true
12988 },
12989 {
12990 name: "ioBinding",
12991 type: "InputOutputBinding",
12992 isMany: true,
12993 xml: {
12994 serialize: "property"
12995 }
12996 }
12997 ]
12998 },
12999 {
13000 name: "FlowNode",
13001 isAbstract: true,
13002 superClass: [
13003 "FlowElement"
13004 ],
13005 properties: [
13006 {
13007 name: "incoming",
13008 type: "SequenceFlow",
13009 isMany: true,
13010 isReference: true
13011 },
13012 {
13013 name: "outgoing",
13014 type: "SequenceFlow",
13015 isMany: true,
13016 isReference: true
13017 },
13018 {
13019 name: "lanes",
13020 type: "Lane",
13021 isMany: true,
13022 isVirtual: true,
13023 isReference: true
13024 }
13025 ]
13026 },
13027 {
13028 name: "CorrelationPropertyRetrievalExpression",
13029 superClass: [
13030 "BaseElement"
13031 ],
13032 properties: [
13033 {
13034 name: "messagePath",
13035 type: "FormalExpression"
13036 },
13037 {
13038 name: "messageRef",
13039 type: "Message",
13040 isAttr: true,
13041 isReference: true
13042 }
13043 ]
13044 },
13045 {
13046 name: "CorrelationPropertyBinding",
13047 superClass: [
13048 "BaseElement"
13049 ],
13050 properties: [
13051 {
13052 name: "dataPath",
13053 type: "FormalExpression"
13054 },
13055 {
13056 name: "correlationPropertyRef",
13057 type: "CorrelationProperty",
13058 isAttr: true,
13059 isReference: true
13060 }
13061 ]
13062 },
13063 {
13064 name: "Resource",
13065 superClass: [
13066 "RootElement"
13067 ],
13068 properties: [
13069 {
13070 name: "name",
13071 isAttr: true,
13072 type: "String"
13073 },
13074 {
13075 name: "resourceParameters",
13076 type: "ResourceParameter",
13077 isMany: true
13078 }
13079 ]
13080 },
13081 {
13082 name: "ResourceParameter",
13083 superClass: [
13084 "BaseElement"
13085 ],
13086 properties: [
13087 {
13088 name: "name",
13089 isAttr: true,
13090 type: "String"
13091 },
13092 {
13093 name: "isRequired",
13094 isAttr: true,
13095 type: "Boolean"
13096 },
13097 {
13098 name: "type",
13099 type: "ItemDefinition",
13100 isAttr: true,
13101 isReference: true
13102 }
13103 ]
13104 },
13105 {
13106 name: "CorrelationSubscription",
13107 superClass: [
13108 "BaseElement"
13109 ],
13110 properties: [
13111 {
13112 name: "correlationKeyRef",
13113 type: "CorrelationKey",
13114 isAttr: true,
13115 isReference: true
13116 },
13117 {
13118 name: "correlationPropertyBinding",
13119 type: "CorrelationPropertyBinding",
13120 isMany: true
13121 }
13122 ]
13123 },
13124 {
13125 name: "MessageFlow",
13126 superClass: [
13127 "BaseElement"
13128 ],
13129 properties: [
13130 {
13131 name: "name",
13132 isAttr: true,
13133 type: "String"
13134 },
13135 {
13136 name: "sourceRef",
13137 type: "InteractionNode",
13138 isAttr: true,
13139 isReference: true
13140 },
13141 {
13142 name: "targetRef",
13143 type: "InteractionNode",
13144 isAttr: true,
13145 isReference: true
13146 },
13147 {
13148 name: "messageRef",
13149 type: "Message",
13150 isAttr: true,
13151 isReference: true
13152 }
13153 ]
13154 },
13155 {
13156 name: "MessageFlowAssociation",
13157 superClass: [
13158 "BaseElement"
13159 ],
13160 properties: [
13161 {
13162 name: "innerMessageFlowRef",
13163 type: "MessageFlow",
13164 isAttr: true,
13165 isReference: true
13166 },
13167 {
13168 name: "outerMessageFlowRef",
13169 type: "MessageFlow",
13170 isAttr: true,
13171 isReference: true
13172 }
13173 ]
13174 },
13175 {
13176 name: "InteractionNode",
13177 isAbstract: true,
13178 properties: [
13179 {
13180 name: "incomingConversationLinks",
13181 type: "ConversationLink",
13182 isMany: true,
13183 isVirtual: true,
13184 isReference: true
13185 },
13186 {
13187 name: "outgoingConversationLinks",
13188 type: "ConversationLink",
13189 isMany: true,
13190 isVirtual: true,
13191 isReference: true
13192 }
13193 ]
13194 },
13195 {
13196 name: "Participant",
13197 superClass: [
13198 "InteractionNode",
13199 "BaseElement"
13200 ],
13201 properties: [
13202 {
13203 name: "name",
13204 isAttr: true,
13205 type: "String"
13206 },
13207 {
13208 name: "interfaceRef",
13209 type: "Interface",
13210 isMany: true,
13211 isReference: true
13212 },
13213 {
13214 name: "participantMultiplicity",
13215 type: "ParticipantMultiplicity"
13216 },
13217 {
13218 name: "endPointRefs",
13219 type: "EndPoint",
13220 isMany: true,
13221 isReference: true
13222 },
13223 {
13224 name: "processRef",
13225 type: "Process",
13226 isAttr: true,
13227 isReference: true
13228 }
13229 ]
13230 },
13231 {
13232 name: "ParticipantAssociation",
13233 superClass: [
13234 "BaseElement"
13235 ],
13236 properties: [
13237 {
13238 name: "innerParticipantRef",
13239 type: "Participant",
13240 isAttr: true,
13241 isReference: true
13242 },
13243 {
13244 name: "outerParticipantRef",
13245 type: "Participant",
13246 isAttr: true,
13247 isReference: true
13248 }
13249 ]
13250 },
13251 {
13252 name: "ParticipantMultiplicity",
13253 properties: [
13254 {
13255 name: "minimum",
13256 "default": 0,
13257 isAttr: true,
13258 type: "Integer"
13259 },
13260 {
13261 name: "maximum",
13262 "default": 1,
13263 isAttr: true,
13264 type: "Integer"
13265 }
13266 ],
13267 superClass: [
13268 "BaseElement"
13269 ]
13270 },
13271 {
13272 name: "Collaboration",
13273 superClass: [
13274 "RootElement"
13275 ],
13276 properties: [
13277 {
13278 name: "name",
13279 isAttr: true,
13280 type: "String"
13281 },
13282 {
13283 name: "isClosed",
13284 isAttr: true,
13285 type: "Boolean"
13286 },
13287 {
13288 name: "participants",
13289 type: "Participant",
13290 isMany: true
13291 },
13292 {
13293 name: "messageFlows",
13294 type: "MessageFlow",
13295 isMany: true
13296 },
13297 {
13298 name: "artifacts",
13299 type: "Artifact",
13300 isMany: true
13301 },
13302 {
13303 name: "conversations",
13304 type: "ConversationNode",
13305 isMany: true
13306 },
13307 {
13308 name: "conversationAssociations",
13309 type: "ConversationAssociation"
13310 },
13311 {
13312 name: "participantAssociations",
13313 type: "ParticipantAssociation",
13314 isMany: true
13315 },
13316 {
13317 name: "messageFlowAssociations",
13318 type: "MessageFlowAssociation",
13319 isMany: true
13320 },
13321 {
13322 name: "correlationKeys",
13323 type: "CorrelationKey",
13324 isMany: true
13325 },
13326 {
13327 name: "choreographyRef",
13328 type: "Choreography",
13329 isMany: true,
13330 isReference: true
13331 },
13332 {
13333 name: "conversationLinks",
13334 type: "ConversationLink",
13335 isMany: true
13336 }
13337 ]
13338 },
13339 {
13340 name: "ChoreographyActivity",
13341 isAbstract: true,
13342 superClass: [
13343 "FlowNode"
13344 ],
13345 properties: [
13346 {
13347 name: "participantRef",
13348 type: "Participant",
13349 isMany: true,
13350 isReference: true
13351 },
13352 {
13353 name: "initiatingParticipantRef",
13354 type: "Participant",
13355 isAttr: true,
13356 isReference: true
13357 },
13358 {
13359 name: "correlationKeys",
13360 type: "CorrelationKey",
13361 isMany: true
13362 },
13363 {
13364 name: "loopType",
13365 type: "ChoreographyLoopType",
13366 "default": "None",
13367 isAttr: true
13368 }
13369 ]
13370 },
13371 {
13372 name: "CallChoreography",
13373 superClass: [
13374 "ChoreographyActivity"
13375 ],
13376 properties: [
13377 {
13378 name: "calledChoreographyRef",
13379 type: "Choreography",
13380 isAttr: true,
13381 isReference: true
13382 },
13383 {
13384 name: "participantAssociations",
13385 type: "ParticipantAssociation",
13386 isMany: true
13387 }
13388 ]
13389 },
13390 {
13391 name: "SubChoreography",
13392 superClass: [
13393 "ChoreographyActivity",
13394 "FlowElementsContainer"
13395 ],
13396 properties: [
13397 {
13398 name: "artifacts",
13399 type: "Artifact",
13400 isMany: true
13401 }
13402 ]
13403 },
13404 {
13405 name: "ChoreographyTask",
13406 superClass: [
13407 "ChoreographyActivity"
13408 ],
13409 properties: [
13410 {
13411 name: "messageFlowRef",
13412 type: "MessageFlow",
13413 isMany: true,
13414 isReference: true
13415 }
13416 ]
13417 },
13418 {
13419 name: "Choreography",
13420 superClass: [
13421 "Collaboration",
13422 "FlowElementsContainer"
13423 ]
13424 },
13425 {
13426 name: "GlobalChoreographyTask",
13427 superClass: [
13428 "Choreography"
13429 ],
13430 properties: [
13431 {
13432 name: "initiatingParticipantRef",
13433 type: "Participant",
13434 isAttr: true,
13435 isReference: true
13436 }
13437 ]
13438 },
13439 {
13440 name: "TextAnnotation",
13441 superClass: [
13442 "Artifact"
13443 ],
13444 properties: [
13445 {
13446 name: "text",
13447 type: "String"
13448 },
13449 {
13450 name: "textFormat",
13451 "default": "text/plain",
13452 isAttr: true,
13453 type: "String"
13454 }
13455 ]
13456 },
13457 {
13458 name: "Group",
13459 superClass: [
13460 "Artifact"
13461 ],
13462 properties: [
13463 {
13464 name: "categoryValueRef",
13465 type: "CategoryValue",
13466 isAttr: true,
13467 isReference: true
13468 }
13469 ]
13470 },
13471 {
13472 name: "Association",
13473 superClass: [
13474 "Artifact"
13475 ],
13476 properties: [
13477 {
13478 name: "associationDirection",
13479 type: "AssociationDirection",
13480 isAttr: true
13481 },
13482 {
13483 name: "sourceRef",
13484 type: "BaseElement",
13485 isAttr: true,
13486 isReference: true
13487 },
13488 {
13489 name: "targetRef",
13490 type: "BaseElement",
13491 isAttr: true,
13492 isReference: true
13493 }
13494 ]
13495 },
13496 {
13497 name: "Category",
13498 superClass: [
13499 "RootElement"
13500 ],
13501 properties: [
13502 {
13503 name: "categoryValue",
13504 type: "CategoryValue",
13505 isMany: true
13506 },
13507 {
13508 name: "name",
13509 isAttr: true,
13510 type: "String"
13511 }
13512 ]
13513 },
13514 {
13515 name: "Artifact",
13516 isAbstract: true,
13517 superClass: [
13518 "BaseElement"
13519 ]
13520 },
13521 {
13522 name: "CategoryValue",
13523 superClass: [
13524 "BaseElement"
13525 ],
13526 properties: [
13527 {
13528 name: "categorizedFlowElements",
13529 type: "FlowElement",
13530 isMany: true,
13531 isVirtual: true,
13532 isReference: true
13533 },
13534 {
13535 name: "value",
13536 isAttr: true,
13537 type: "String"
13538 }
13539 ]
13540 },
13541 {
13542 name: "Activity",
13543 isAbstract: true,
13544 superClass: [
13545 "FlowNode"
13546 ],
13547 properties: [
13548 {
13549 name: "isForCompensation",
13550 "default": false,
13551 isAttr: true,
13552 type: "Boolean"
13553 },
13554 {
13555 name: "default",
13556 type: "SequenceFlow",
13557 isAttr: true,
13558 isReference: true
13559 },
13560 {
13561 name: "ioSpecification",
13562 type: "InputOutputSpecification",
13563 xml: {
13564 serialize: "property"
13565 }
13566 },
13567 {
13568 name: "boundaryEventRefs",
13569 type: "BoundaryEvent",
13570 isMany: true,
13571 isReference: true
13572 },
13573 {
13574 name: "properties",
13575 type: "Property",
13576 isMany: true
13577 },
13578 {
13579 name: "dataInputAssociations",
13580 type: "DataInputAssociation",
13581 isMany: true
13582 },
13583 {
13584 name: "dataOutputAssociations",
13585 type: "DataOutputAssociation",
13586 isMany: true
13587 },
13588 {
13589 name: "startQuantity",
13590 "default": 1,
13591 isAttr: true,
13592 type: "Integer"
13593 },
13594 {
13595 name: "resources",
13596 type: "ResourceRole",
13597 isMany: true
13598 },
13599 {
13600 name: "completionQuantity",
13601 "default": 1,
13602 isAttr: true,
13603 type: "Integer"
13604 },
13605 {
13606 name: "loopCharacteristics",
13607 type: "LoopCharacteristics"
13608 }
13609 ]
13610 },
13611 {
13612 name: "ServiceTask",
13613 superClass: [
13614 "Task"
13615 ],
13616 properties: [
13617 {
13618 name: "implementation",
13619 isAttr: true,
13620 type: "String"
13621 },
13622 {
13623 name: "operationRef",
13624 type: "Operation",
13625 isAttr: true,
13626 isReference: true
13627 }
13628 ]
13629 },
13630 {
13631 name: "SubProcess",
13632 superClass: [
13633 "Activity",
13634 "FlowElementsContainer",
13635 "InteractionNode"
13636 ],
13637 properties: [
13638 {
13639 name: "triggeredByEvent",
13640 "default": false,
13641 isAttr: true,
13642 type: "Boolean"
13643 },
13644 {
13645 name: "artifacts",
13646 type: "Artifact",
13647 isMany: true
13648 }
13649 ]
13650 },
13651 {
13652 name: "LoopCharacteristics",
13653 isAbstract: true,
13654 superClass: [
13655 "BaseElement"
13656 ]
13657 },
13658 {
13659 name: "MultiInstanceLoopCharacteristics",
13660 superClass: [
13661 "LoopCharacteristics"
13662 ],
13663 properties: [
13664 {
13665 name: "isSequential",
13666 "default": false,
13667 isAttr: true,
13668 type: "Boolean"
13669 },
13670 {
13671 name: "behavior",
13672 type: "MultiInstanceBehavior",
13673 "default": "All",
13674 isAttr: true
13675 },
13676 {
13677 name: "loopCardinality",
13678 type: "Expression",
13679 xml: {
13680 serialize: "xsi:type"
13681 }
13682 },
13683 {
13684 name: "loopDataInputRef",
13685 type: "ItemAwareElement",
13686 isReference: true
13687 },
13688 {
13689 name: "loopDataOutputRef",
13690 type: "ItemAwareElement",
13691 isReference: true
13692 },
13693 {
13694 name: "inputDataItem",
13695 type: "DataInput",
13696 xml: {
13697 serialize: "property"
13698 }
13699 },
13700 {
13701 name: "outputDataItem",
13702 type: "DataOutput",
13703 xml: {
13704 serialize: "property"
13705 }
13706 },
13707 {
13708 name: "complexBehaviorDefinition",
13709 type: "ComplexBehaviorDefinition",
13710 isMany: true
13711 },
13712 {
13713 name: "completionCondition",
13714 type: "Expression",
13715 xml: {
13716 serialize: "xsi:type"
13717 }
13718 },
13719 {
13720 name: "oneBehaviorEventRef",
13721 type: "EventDefinition",
13722 isAttr: true,
13723 isReference: true
13724 },
13725 {
13726 name: "noneBehaviorEventRef",
13727 type: "EventDefinition",
13728 isAttr: true,
13729 isReference: true
13730 }
13731 ]
13732 },
13733 {
13734 name: "StandardLoopCharacteristics",
13735 superClass: [
13736 "LoopCharacteristics"
13737 ],
13738 properties: [
13739 {
13740 name: "testBefore",
13741 "default": false,
13742 isAttr: true,
13743 type: "Boolean"
13744 },
13745 {
13746 name: "loopCondition",
13747 type: "Expression",
13748 xml: {
13749 serialize: "xsi:type"
13750 }
13751 },
13752 {
13753 name: "loopMaximum",
13754 type: "Integer",
13755 isAttr: true
13756 }
13757 ]
13758 },
13759 {
13760 name: "CallActivity",
13761 superClass: [
13762 "Activity",
13763 "InteractionNode"
13764 ],
13765 properties: [
13766 {
13767 name: "calledElement",
13768 type: "String",
13769 isAttr: true
13770 }
13771 ]
13772 },
13773 {
13774 name: "Task",
13775 superClass: [
13776 "Activity",
13777 "InteractionNode"
13778 ]
13779 },
13780 {
13781 name: "SendTask",
13782 superClass: [
13783 "Task"
13784 ],
13785 properties: [
13786 {
13787 name: "implementation",
13788 isAttr: true,
13789 type: "String"
13790 },
13791 {
13792 name: "operationRef",
13793 type: "Operation",
13794 isAttr: true,
13795 isReference: true
13796 },
13797 {
13798 name: "messageRef",
13799 type: "Message",
13800 isAttr: true,
13801 isReference: true
13802 }
13803 ]
13804 },
13805 {
13806 name: "ReceiveTask",
13807 superClass: [
13808 "Task"
13809 ],
13810 properties: [
13811 {
13812 name: "implementation",
13813 isAttr: true,
13814 type: "String"
13815 },
13816 {
13817 name: "instantiate",
13818 "default": false,
13819 isAttr: true,
13820 type: "Boolean"
13821 },
13822 {
13823 name: "operationRef",
13824 type: "Operation",
13825 isAttr: true,
13826 isReference: true
13827 },
13828 {
13829 name: "messageRef",
13830 type: "Message",
13831 isAttr: true,
13832 isReference: true
13833 }
13834 ]
13835 },
13836 {
13837 name: "ScriptTask",
13838 superClass: [
13839 "Task"
13840 ],
13841 properties: [
13842 {
13843 name: "scriptFormat",
13844 isAttr: true,
13845 type: "String"
13846 },
13847 {
13848 name: "script",
13849 type: "String"
13850 }
13851 ]
13852 },
13853 {
13854 name: "BusinessRuleTask",
13855 superClass: [
13856 "Task"
13857 ],
13858 properties: [
13859 {
13860 name: "implementation",
13861 isAttr: true,
13862 type: "String"
13863 }
13864 ]
13865 },
13866 {
13867 name: "AdHocSubProcess",
13868 superClass: [
13869 "SubProcess"
13870 ],
13871 properties: [
13872 {
13873 name: "completionCondition",
13874 type: "Expression",
13875 xml: {
13876 serialize: "xsi:type"
13877 }
13878 },
13879 {
13880 name: "ordering",
13881 type: "AdHocOrdering",
13882 isAttr: true
13883 },
13884 {
13885 name: "cancelRemainingInstances",
13886 "default": true,
13887 isAttr: true,
13888 type: "Boolean"
13889 }
13890 ]
13891 },
13892 {
13893 name: "Transaction",
13894 superClass: [
13895 "SubProcess"
13896 ],
13897 properties: [
13898 {
13899 name: "protocol",
13900 isAttr: true,
13901 type: "String"
13902 },
13903 {
13904 name: "method",
13905 isAttr: true,
13906 type: "String"
13907 }
13908 ]
13909 },
13910 {
13911 name: "GlobalScriptTask",
13912 superClass: [
13913 "GlobalTask"
13914 ],
13915 properties: [
13916 {
13917 name: "scriptLanguage",
13918 isAttr: true,
13919 type: "String"
13920 },
13921 {
13922 name: "script",
13923 isAttr: true,
13924 type: "String"
13925 }
13926 ]
13927 },
13928 {
13929 name: "GlobalBusinessRuleTask",
13930 superClass: [
13931 "GlobalTask"
13932 ],
13933 properties: [
13934 {
13935 name: "implementation",
13936 isAttr: true,
13937 type: "String"
13938 }
13939 ]
13940 },
13941 {
13942 name: "ComplexBehaviorDefinition",
13943 superClass: [
13944 "BaseElement"
13945 ],
13946 properties: [
13947 {
13948 name: "condition",
13949 type: "FormalExpression"
13950 },
13951 {
13952 name: "event",
13953 type: "ImplicitThrowEvent"
13954 }
13955 ]
13956 },
13957 {
13958 name: "ResourceRole",
13959 superClass: [
13960 "BaseElement"
13961 ],
13962 properties: [
13963 {
13964 name: "resourceRef",
13965 type: "Resource",
13966 isReference: true
13967 },
13968 {
13969 name: "resourceParameterBindings",
13970 type: "ResourceParameterBinding",
13971 isMany: true
13972 },
13973 {
13974 name: "resourceAssignmentExpression",
13975 type: "ResourceAssignmentExpression"
13976 },
13977 {
13978 name: "name",
13979 isAttr: true,
13980 type: "String"
13981 }
13982 ]
13983 },
13984 {
13985 name: "ResourceParameterBinding",
13986 properties: [
13987 {
13988 name: "expression",
13989 type: "Expression",
13990 xml: {
13991 serialize: "xsi:type"
13992 }
13993 },
13994 {
13995 name: "parameterRef",
13996 type: "ResourceParameter",
13997 isAttr: true,
13998 isReference: true
13999 }
14000 ],
14001 superClass: [
14002 "BaseElement"
14003 ]
14004 },
14005 {
14006 name: "ResourceAssignmentExpression",
14007 properties: [
14008 {
14009 name: "expression",
14010 type: "Expression",
14011 xml: {
14012 serialize: "xsi:type"
14013 }
14014 }
14015 ],
14016 superClass: [
14017 "BaseElement"
14018 ]
14019 },
14020 {
14021 name: "Import",
14022 properties: [
14023 {
14024 name: "importType",
14025 isAttr: true,
14026 type: "String"
14027 },
14028 {
14029 name: "location",
14030 isAttr: true,
14031 type: "String"
14032 },
14033 {
14034 name: "namespace",
14035 isAttr: true,
14036 type: "String"
14037 }
14038 ]
14039 },
14040 {
14041 name: "Definitions",
14042 superClass: [
14043 "BaseElement"
14044 ],
14045 properties: [
14046 {
14047 name: "name",
14048 isAttr: true,
14049 type: "String"
14050 },
14051 {
14052 name: "targetNamespace",
14053 isAttr: true,
14054 type: "String"
14055 },
14056 {
14057 name: "expressionLanguage",
14058 "default": "http://www.w3.org/1999/XPath",
14059 isAttr: true,
14060 type: "String"
14061 },
14062 {
14063 name: "typeLanguage",
14064 "default": "http://www.w3.org/2001/XMLSchema",
14065 isAttr: true,
14066 type: "String"
14067 },
14068 {
14069 name: "imports",
14070 type: "Import",
14071 isMany: true
14072 },
14073 {
14074 name: "extensions",
14075 type: "Extension",
14076 isMany: true
14077 },
14078 {
14079 name: "rootElements",
14080 type: "RootElement",
14081 isMany: true
14082 },
14083 {
14084 name: "diagrams",
14085 isMany: true,
14086 type: "bpmndi:BPMNDiagram"
14087 },
14088 {
14089 name: "exporter",
14090 isAttr: true,
14091 type: "String"
14092 },
14093 {
14094 name: "relationships",
14095 type: "Relationship",
14096 isMany: true
14097 },
14098 {
14099 name: "exporterVersion",
14100 isAttr: true,
14101 type: "String"
14102 }
14103 ]
14104 }
14105 ];
14106 var enumerations = [
14107 {
14108 name: "ProcessType",
14109 literalValues: [
14110 {
14111 name: "None"
14112 },
14113 {
14114 name: "Public"
14115 },
14116 {
14117 name: "Private"
14118 }
14119 ]
14120 },
14121 {
14122 name: "GatewayDirection",
14123 literalValues: [
14124 {
14125 name: "Unspecified"
14126 },
14127 {
14128 name: "Converging"
14129 },
14130 {
14131 name: "Diverging"
14132 },
14133 {
14134 name: "Mixed"
14135 }
14136 ]
14137 },
14138 {
14139 name: "EventBasedGatewayType",
14140 literalValues: [
14141 {
14142 name: "Parallel"
14143 },
14144 {
14145 name: "Exclusive"
14146 }
14147 ]
14148 },
14149 {
14150 name: "RelationshipDirection",
14151 literalValues: [
14152 {
14153 name: "None"
14154 },
14155 {
14156 name: "Forward"
14157 },
14158 {
14159 name: "Backward"
14160 },
14161 {
14162 name: "Both"
14163 }
14164 ]
14165 },
14166 {
14167 name: "ItemKind",
14168 literalValues: [
14169 {
14170 name: "Physical"
14171 },
14172 {
14173 name: "Information"
14174 }
14175 ]
14176 },
14177 {
14178 name: "ChoreographyLoopType",
14179 literalValues: [
14180 {
14181 name: "None"
14182 },
14183 {
14184 name: "Standard"
14185 },
14186 {
14187 name: "MultiInstanceSequential"
14188 },
14189 {
14190 name: "MultiInstanceParallel"
14191 }
14192 ]
14193 },
14194 {
14195 name: "AssociationDirection",
14196 literalValues: [
14197 {
14198 name: "None"
14199 },
14200 {
14201 name: "One"
14202 },
14203 {
14204 name: "Both"
14205 }
14206 ]
14207 },
14208 {
14209 name: "MultiInstanceBehavior",
14210 literalValues: [
14211 {
14212 name: "None"
14213 },
14214 {
14215 name: "One"
14216 },
14217 {
14218 name: "All"
14219 },
14220 {
14221 name: "Complex"
14222 }
14223 ]
14224 },
14225 {
14226 name: "AdHocOrdering",
14227 literalValues: [
14228 {
14229 name: "Parallel"
14230 },
14231 {
14232 name: "Sequential"
14233 }
14234 ]
14235 }
14236 ];
14237 var xml = {
14238 tagAlias: "lowerCase",
14239 typePrefix: "t"
14240 };
14241 var BpmnPackage = {
14242 name: name,
14243 uri: uri,
14244 prefix: prefix,
14245 associations: associations,
14246 types: types,
14247 enumerations: enumerations,
14248 xml: xml
14249 };
14250
14251 var name$1 = "BPMNDI";
14252 var uri$1 = "http://www.omg.org/spec/BPMN/20100524/DI";
14253 var prefix$1 = "bpmndi";
14254 var types$1 = [
14255 {
14256 name: "BPMNDiagram",
14257 properties: [
14258 {
14259 name: "plane",
14260 type: "BPMNPlane",
14261 redefines: "di:Diagram#rootElement"
14262 },
14263 {
14264 name: "labelStyle",
14265 type: "BPMNLabelStyle",
14266 isMany: true
14267 }
14268 ],
14269 superClass: [
14270 "di:Diagram"
14271 ]
14272 },
14273 {
14274 name: "BPMNPlane",
14275 properties: [
14276 {
14277 name: "bpmnElement",
14278 isAttr: true,
14279 isReference: true,
14280 type: "bpmn:BaseElement",
14281 redefines: "di:DiagramElement#modelElement"
14282 }
14283 ],
14284 superClass: [
14285 "di:Plane"
14286 ]
14287 },
14288 {
14289 name: "BPMNShape",
14290 properties: [
14291 {
14292 name: "bpmnElement",
14293 isAttr: true,
14294 isReference: true,
14295 type: "bpmn:BaseElement",
14296 redefines: "di:DiagramElement#modelElement"
14297 },
14298 {
14299 name: "isHorizontal",
14300 isAttr: true,
14301 type: "Boolean"
14302 },
14303 {
14304 name: "isExpanded",
14305 isAttr: true,
14306 type: "Boolean"
14307 },
14308 {
14309 name: "isMarkerVisible",
14310 isAttr: true,
14311 type: "Boolean"
14312 },
14313 {
14314 name: "label",
14315 type: "BPMNLabel"
14316 },
14317 {
14318 name: "isMessageVisible",
14319 isAttr: true,
14320 type: "Boolean"
14321 },
14322 {
14323 name: "participantBandKind",
14324 type: "ParticipantBandKind",
14325 isAttr: true
14326 },
14327 {
14328 name: "choreographyActivityShape",
14329 type: "BPMNShape",
14330 isAttr: true,
14331 isReference: true
14332 }
14333 ],
14334 superClass: [
14335 "di:LabeledShape"
14336 ]
14337 },
14338 {
14339 name: "BPMNEdge",
14340 properties: [
14341 {
14342 name: "label",
14343 type: "BPMNLabel"
14344 },
14345 {
14346 name: "bpmnElement",
14347 isAttr: true,
14348 isReference: true,
14349 type: "bpmn:BaseElement",
14350 redefines: "di:DiagramElement#modelElement"
14351 },
14352 {
14353 name: "sourceElement",
14354 isAttr: true,
14355 isReference: true,
14356 type: "di:DiagramElement",
14357 redefines: "di:Edge#source"
14358 },
14359 {
14360 name: "targetElement",
14361 isAttr: true,
14362 isReference: true,
14363 type: "di:DiagramElement",
14364 redefines: "di:Edge#target"
14365 },
14366 {
14367 name: "messageVisibleKind",
14368 type: "MessageVisibleKind",
14369 isAttr: true,
14370 "default": "initiating"
14371 }
14372 ],
14373 superClass: [
14374 "di:LabeledEdge"
14375 ]
14376 },
14377 {
14378 name: "BPMNLabel",
14379 properties: [
14380 {
14381 name: "labelStyle",
14382 type: "BPMNLabelStyle",
14383 isAttr: true,
14384 isReference: true,
14385 redefines: "di:DiagramElement#style"
14386 }
14387 ],
14388 superClass: [
14389 "di:Label"
14390 ]
14391 },
14392 {
14393 name: "BPMNLabelStyle",
14394 properties: [
14395 {
14396 name: "font",
14397 type: "dc:Font"
14398 }
14399 ],
14400 superClass: [
14401 "di:Style"
14402 ]
14403 }
14404 ];
14405 var enumerations$1 = [
14406 {
14407 name: "ParticipantBandKind",
14408 literalValues: [
14409 {
14410 name: "top_initiating"
14411 },
14412 {
14413 name: "middle_initiating"
14414 },
14415 {
14416 name: "bottom_initiating"
14417 },
14418 {
14419 name: "top_non_initiating"
14420 },
14421 {
14422 name: "middle_non_initiating"
14423 },
14424 {
14425 name: "bottom_non_initiating"
14426 }
14427 ]
14428 },
14429 {
14430 name: "MessageVisibleKind",
14431 literalValues: [
14432 {
14433 name: "initiating"
14434 },
14435 {
14436 name: "non_initiating"
14437 }
14438 ]
14439 }
14440 ];
14441 var associations$1 = [
14442 ];
14443 var BpmnDiPackage = {
14444 name: name$1,
14445 uri: uri$1,
14446 prefix: prefix$1,
14447 types: types$1,
14448 enumerations: enumerations$1,
14449 associations: associations$1
14450 };
14451
14452 var name$2 = "DC";
14453 var uri$2 = "http://www.omg.org/spec/DD/20100524/DC";
14454 var prefix$2 = "dc";
14455 var types$2 = [
14456 {
14457 name: "Boolean"
14458 },
14459 {
14460 name: "Integer"
14461 },
14462 {
14463 name: "Real"
14464 },
14465 {
14466 name: "String"
14467 },
14468 {
14469 name: "Font",
14470 properties: [
14471 {
14472 name: "name",
14473 type: "String",
14474 isAttr: true
14475 },
14476 {
14477 name: "size",
14478 type: "Real",
14479 isAttr: true
14480 },
14481 {
14482 name: "isBold",
14483 type: "Boolean",
14484 isAttr: true
14485 },
14486 {
14487 name: "isItalic",
14488 type: "Boolean",
14489 isAttr: true
14490 },
14491 {
14492 name: "isUnderline",
14493 type: "Boolean",
14494 isAttr: true
14495 },
14496 {
14497 name: "isStrikeThrough",
14498 type: "Boolean",
14499 isAttr: true
14500 }
14501 ]
14502 },
14503 {
14504 name: "Point",
14505 properties: [
14506 {
14507 name: "x",
14508 type: "Real",
14509 "default": "0",
14510 isAttr: true
14511 },
14512 {
14513 name: "y",
14514 type: "Real",
14515 "default": "0",
14516 isAttr: true
14517 }
14518 ]
14519 },
14520 {
14521 name: "Bounds",
14522 properties: [
14523 {
14524 name: "x",
14525 type: "Real",
14526 "default": "0",
14527 isAttr: true
14528 },
14529 {
14530 name: "y",
14531 type: "Real",
14532 "default": "0",
14533 isAttr: true
14534 },
14535 {
14536 name: "width",
14537 type: "Real",
14538 isAttr: true
14539 },
14540 {
14541 name: "height",
14542 type: "Real",
14543 isAttr: true
14544 }
14545 ]
14546 }
14547 ];
14548 var associations$2 = [
14549 ];
14550 var DcPackage = {
14551 name: name$2,
14552 uri: uri$2,
14553 prefix: prefix$2,
14554 types: types$2,
14555 associations: associations$2
14556 };
14557
14558 var name$3 = "DI";
14559 var uri$3 = "http://www.omg.org/spec/DD/20100524/DI";
14560 var prefix$3 = "di";
14561 var types$3 = [
14562 {
14563 name: "DiagramElement",
14564 isAbstract: true,
14565 properties: [
14566 {
14567 name: "id",
14568 isAttr: true,
14569 isId: true,
14570 type: "String"
14571 },
14572 {
14573 name: "extension",
14574 type: "Extension"
14575 },
14576 {
14577 name: "owningDiagram",
14578 type: "Diagram",
14579 isReadOnly: true,
14580 isVirtual: true,
14581 isReference: true
14582 },
14583 {
14584 name: "owningElement",
14585 type: "DiagramElement",
14586 isReadOnly: true,
14587 isVirtual: true,
14588 isReference: true
14589 },
14590 {
14591 name: "modelElement",
14592 isReadOnly: true,
14593 isVirtual: true,
14594 isReference: true,
14595 type: "Element"
14596 },
14597 {
14598 name: "style",
14599 type: "Style",
14600 isReadOnly: true,
14601 isVirtual: true,
14602 isReference: true
14603 },
14604 {
14605 name: "ownedElement",
14606 type: "DiagramElement",
14607 isReadOnly: true,
14608 isMany: true,
14609 isVirtual: true
14610 }
14611 ]
14612 },
14613 {
14614 name: "Node",
14615 isAbstract: true,
14616 superClass: [
14617 "DiagramElement"
14618 ]
14619 },
14620 {
14621 name: "Edge",
14622 isAbstract: true,
14623 superClass: [
14624 "DiagramElement"
14625 ],
14626 properties: [
14627 {
14628 name: "source",
14629 type: "DiagramElement",
14630 isReadOnly: true,
14631 isVirtual: true,
14632 isReference: true
14633 },
14634 {
14635 name: "target",
14636 type: "DiagramElement",
14637 isReadOnly: true,
14638 isVirtual: true,
14639 isReference: true
14640 },
14641 {
14642 name: "waypoint",
14643 isUnique: false,
14644 isMany: true,
14645 type: "dc:Point",
14646 xml: {
14647 serialize: "xsi:type"
14648 }
14649 }
14650 ]
14651 },
14652 {
14653 name: "Diagram",
14654 isAbstract: true,
14655 properties: [
14656 {
14657 name: "id",
14658 isAttr: true,
14659 isId: true,
14660 type: "String"
14661 },
14662 {
14663 name: "rootElement",
14664 type: "DiagramElement",
14665 isReadOnly: true,
14666 isVirtual: true
14667 },
14668 {
14669 name: "name",
14670 isAttr: true,
14671 type: "String"
14672 },
14673 {
14674 name: "documentation",
14675 isAttr: true,
14676 type: "String"
14677 },
14678 {
14679 name: "resolution",
14680 isAttr: true,
14681 type: "Real"
14682 },
14683 {
14684 name: "ownedStyle",
14685 type: "Style",
14686 isReadOnly: true,
14687 isMany: true,
14688 isVirtual: true
14689 }
14690 ]
14691 },
14692 {
14693 name: "Shape",
14694 isAbstract: true,
14695 superClass: [
14696 "Node"
14697 ],
14698 properties: [
14699 {
14700 name: "bounds",
14701 type: "dc:Bounds"
14702 }
14703 ]
14704 },
14705 {
14706 name: "Plane",
14707 isAbstract: true,
14708 superClass: [
14709 "Node"
14710 ],
14711 properties: [
14712 {
14713 name: "planeElement",
14714 type: "DiagramElement",
14715 subsettedProperty: "DiagramElement-ownedElement",
14716 isMany: true
14717 }
14718 ]
14719 },
14720 {
14721 name: "LabeledEdge",
14722 isAbstract: true,
14723 superClass: [
14724 "Edge"
14725 ],
14726 properties: [
14727 {
14728 name: "ownedLabel",
14729 type: "Label",
14730 isReadOnly: true,
14731 subsettedProperty: "DiagramElement-ownedElement",
14732 isMany: true,
14733 isVirtual: true
14734 }
14735 ]
14736 },
14737 {
14738 name: "LabeledShape",
14739 isAbstract: true,
14740 superClass: [
14741 "Shape"
14742 ],
14743 properties: [
14744 {
14745 name: "ownedLabel",
14746 type: "Label",
14747 isReadOnly: true,
14748 subsettedProperty: "DiagramElement-ownedElement",
14749 isMany: true,
14750 isVirtual: true
14751 }
14752 ]
14753 },
14754 {
14755 name: "Label",
14756 isAbstract: true,
14757 superClass: [
14758 "Node"
14759 ],
14760 properties: [
14761 {
14762 name: "bounds",
14763 type: "dc:Bounds"
14764 }
14765 ]
14766 },
14767 {
14768 name: "Style",
14769 isAbstract: true,
14770 properties: [
14771 {
14772 name: "id",
14773 isAttr: true,
14774 isId: true,
14775 type: "String"
14776 }
14777 ]
14778 },
14779 {
14780 name: "Extension",
14781 properties: [
14782 {
14783 name: "values",
14784 isMany: true,
14785 type: "Element"
14786 }
14787 ]
14788 }
14789 ];
14790 var associations$3 = [
14791 ];
14792 var xml$1 = {
14793 tagAlias: "lowerCase"
14794 };
14795 var DiPackage = {
14796 name: name$3,
14797 uri: uri$3,
14798 prefix: prefix$3,
14799 types: types$3,
14800 associations: associations$3,
14801 xml: xml$1
14802 };
14803
14804 var name$4 = "bpmn.io colors for BPMN";
14805 var uri$4 = "http://bpmn.io/schema/bpmn/biocolor/1.0";
14806 var prefix$4 = "bioc";
14807 var types$4 = [
14808 {
14809 name: "ColoredShape",
14810 "extends": [
14811 "bpmndi:BPMNShape"
14812 ],
14813 properties: [
14814 {
14815 name: "stroke",
14816 isAttr: true,
14817 type: "String"
14818 },
14819 {
14820 name: "fill",
14821 isAttr: true,
14822 type: "String"
14823 }
14824 ]
14825 },
14826 {
14827 name: "ColoredEdge",
14828 "extends": [
14829 "bpmndi:BPMNEdge"
14830 ],
14831 properties: [
14832 {
14833 name: "stroke",
14834 isAttr: true,
14835 type: "String"
14836 },
14837 {
14838 name: "fill",
14839 isAttr: true,
14840 type: "String"
14841 }
14842 ]
14843 }
14844 ];
14845 var enumerations$2 = [
14846 ];
14847 var associations$4 = [
14848 ];
14849 var BiocPackage = {
14850 name: name$4,
14851 uri: uri$4,
14852 prefix: prefix$4,
14853 types: types$4,
14854 enumerations: enumerations$2,
14855 associations: associations$4
14856 };
14857
14858 var name$5 = "BPMN in Color";
14859 var uri$5 = "http://www.omg.org/spec/BPMN/non-normative/color/1.0";
14860 var prefix$5 = "color";
14861 var types$5 = [
14862 {
14863 name: "ColoredLabel",
14864 "extends": [
14865 "bpmndi:BPMNLabel"
14866 ],
14867 properties: [
14868 {
14869 name: "color",
14870 isAttr: true,
14871 type: "String"
14872 }
14873 ]
14874 },
14875 {
14876 name: "ColoredShape",
14877 "extends": [
14878 "bpmndi:BPMNShape"
14879 ],
14880 properties: [
14881 {
14882 name: "background-color",
14883 isAttr: true,
14884 type: "String"
14885 },
14886 {
14887 name: "border-color",
14888 isAttr: true,
14889 type: "String"
14890 }
14891 ]
14892 },
14893 {
14894 name: "ColoredEdge",
14895 "extends": [
14896 "bpmndi:BPMNEdge"
14897 ],
14898 properties: [
14899 {
14900 name: "border-color",
14901 isAttr: true,
14902 type: "String"
14903 }
14904 ]
14905 }
14906 ];
14907 var enumerations$3 = [
14908 ];
14909 var associations$5 = [
14910 ];
14911 var BpmnInColorPackage = {
14912 name: name$5,
14913 uri: uri$5,
14914 prefix: prefix$5,
14915 types: types$5,
14916 enumerations: enumerations$3,
14917 associations: associations$5
14918 };
14919
14920 var packages = {
14921 bpmn: BpmnPackage,
14922 bpmndi: BpmnDiPackage,
14923 dc: DcPackage,
14924 di: DiPackage,
14925 bioc: BiocPackage,
14926 color: BpmnInColorPackage
14927 };
14928
14929 function simple(additionalPackages, options) {
14930 var pks = assign({}, packages, additionalPackages);
14931
14932 return new BpmnModdle(pks, options);
14933 }
14934
14935 function elementToString(e) {
14936 if (!e) {
14937 return '<null>';
14938 }
14939
14940 return '<' + e.$type + (e.id ? ' id="' + e.id : '') + '" />';
14941 }
14942
14943 var diRefs = new Refs(
14944 { name: 'bpmnElement', enumerable: true },
14945 { name: 'di', configurable: true }
14946 );
14947
14948 /**
14949 * Returns true if an element has the given meta-model type
14950 *
14951 * @param {ModdleElement} element
14952 * @param {string} type
14953 *
14954 * @return {boolean}
14955 */
14956 function is$2(element, type) {
14957 return element.$instanceOf(type);
14958 }
14959
14960
14961 /**
14962 * Find a suitable display candidate for definitions where the DI does not
14963 * correctly specify one.
14964 */
14965 function findDisplayCandidate(definitions) {
14966 return find(definitions.rootElements, function(e) {
14967 return is$2(e, 'bpmn:Process') || is$2(e, 'bpmn:Collaboration');
14968 });
14969 }
14970
14971
14972 function BpmnTreeWalker(handler, translate) {
14973
14974 // list of containers already walked
14975 var handledElements = {};
14976
14977 // list of elements to handle deferred to ensure
14978 // prerequisites are drawn
14979 var deferred = [];
14980
14981 // Helpers //////////////////////
14982
14983 function contextual(fn, ctx) {
14984 return function(e) {
14985 fn(e, ctx);
14986 };
14987 }
14988
14989 function handled(element) {
14990 handledElements[element.id] = element;
14991 }
14992
14993 function isHandled(element) {
14994 return handledElements[element.id];
14995 }
14996
14997 function visit(element, ctx) {
14998
14999 var gfx = element.gfx;
15000
15001 // avoid multiple rendering of elements
15002 if (gfx) {
15003 throw new Error(
15004 translate('already rendered {element}', { element: elementToString(element) })
15005 );
15006 }
15007
15008 // call handler
15009 return handler.element(element, ctx);
15010 }
15011
15012 function visitRoot(element, diagram) {
15013 return handler.root(element, diagram);
15014 }
15015
15016 function visitIfDi(element, ctx) {
15017
15018 try {
15019 var gfx = element.di && visit(element, ctx);
15020
15021 handled(element);
15022
15023 return gfx;
15024 } catch (e) {
15025 logError(e.message, { element: element, error: e });
15026
15027 console.error(translate('failed to import {element}', { element: elementToString(element) }));
15028 console.error(e);
15029 }
15030 }
15031
15032 function logError(message, context) {
15033 handler.error(message, context);
15034 }
15035
15036 // DI handling //////////////////////
15037
15038 function registerDi(di) {
15039 var bpmnElement = di.bpmnElement;
15040
15041 if (bpmnElement) {
15042 if (bpmnElement.di) {
15043 logError(
15044 translate('multiple DI elements defined for {element}', {
15045 element: elementToString(bpmnElement)
15046 }),
15047 { element: bpmnElement }
15048 );
15049 } else {
15050 diRefs.bind(bpmnElement, 'di');
15051 bpmnElement.di = di;
15052 }
15053 } else {
15054 logError(
15055 translate('no bpmnElement referenced in {element}', {
15056 element: elementToString(di)
15057 }),
15058 { element: di }
15059 );
15060 }
15061 }
15062
15063 function handleDiagram(diagram) {
15064 handlePlane(diagram.plane);
15065 }
15066
15067 function handlePlane(plane) {
15068 registerDi(plane);
15069
15070 forEach(plane.planeElement, handlePlaneElement);
15071 }
15072
15073 function handlePlaneElement(planeElement) {
15074 registerDi(planeElement);
15075 }
15076
15077
15078 // Semantic handling //////////////////////
15079
15080 /**
15081 * Handle definitions and return the rendered diagram (if any)
15082 *
15083 * @param {ModdleElement} definitions to walk and import
15084 * @param {ModdleElement} [diagram] specific diagram to import and display
15085 *
15086 * @throws {Error} if no diagram to display could be found
15087 */
15088 function handleDefinitions(definitions, diagram) {
15089
15090 // make sure we walk the correct bpmnElement
15091
15092 var diagrams = definitions.diagrams;
15093
15094 if (diagram && diagrams.indexOf(diagram) === -1) {
15095 throw new Error(translate('diagram not part of bpmn:Definitions'));
15096 }
15097
15098 if (!diagram && diagrams && diagrams.length) {
15099 diagram = diagrams[0];
15100 }
15101
15102 // no diagram -> nothing to import
15103 if (!diagram) {
15104 throw new Error(translate('no diagram to display'));
15105 }
15106
15107 // load DI from selected diagram only
15108 handleDiagram(diagram);
15109
15110
15111 var plane = diagram.plane;
15112
15113 if (!plane) {
15114 throw new Error(translate(
15115 'no plane for {element}',
15116 { element: elementToString(diagram) }
15117 ));
15118 }
15119
15120 var rootElement = plane.bpmnElement;
15121
15122 // ensure we default to a suitable display candidate (process or collaboration),
15123 // even if non is specified in DI
15124 if (!rootElement) {
15125 rootElement = findDisplayCandidate(definitions);
15126
15127 if (!rootElement) {
15128 throw new Error(translate('no process or collaboration to display'));
15129 } else {
15130
15131 logError(
15132 translate('correcting missing bpmnElement on {plane} to {rootElement}', {
15133 plane: elementToString(plane),
15134 rootElement: elementToString(rootElement)
15135 })
15136 );
15137
15138 // correct DI on the fly
15139 plane.bpmnElement = rootElement;
15140 registerDi(plane);
15141 }
15142 }
15143
15144
15145 var ctx = visitRoot(rootElement, plane);
15146
15147 if (is$2(rootElement, 'bpmn:Process')) {
15148 handleProcess(rootElement, ctx);
15149 } else if (is$2(rootElement, 'bpmn:Collaboration')) {
15150 handleCollaboration(rootElement);
15151
15152 // force drawing of everything not yet drawn that is part of the target DI
15153 handleUnhandledProcesses(definitions.rootElements, ctx);
15154 } else {
15155 throw new Error(
15156 translate('unsupported bpmnElement for {plane}: {rootElement}', {
15157 plane: elementToString(plane),
15158 rootElement: elementToString(rootElement)
15159 })
15160 );
15161 }
15162
15163 // handle all deferred elements
15164 handleDeferred();
15165 }
15166
15167 function handleDeferred() {
15168
15169 var fn;
15170
15171 // drain deferred until empty
15172 while (deferred.length) {
15173 fn = deferred.shift();
15174
15175 fn();
15176 }
15177 }
15178
15179 function handleProcess(process, context) {
15180 handleFlowElementsContainer(process, context);
15181 handleIoSpecification(process.ioSpecification, context);
15182
15183 handleArtifacts(process.artifacts, context);
15184
15185 // log process handled
15186 handled(process);
15187 }
15188
15189 function handleUnhandledProcesses(rootElements, ctx) {
15190
15191 // walk through all processes that have not yet been drawn and draw them
15192 // if they contain lanes with DI information.
15193 // we do this to pass the free-floating lane test cases in the MIWG test suite
15194 var processes = filter(rootElements, function(e) {
15195 return !isHandled(e) && is$2(e, 'bpmn:Process') && e.laneSets;
15196 });
15197
15198 processes.forEach(contextual(handleProcess, ctx));
15199 }
15200
15201 function handleMessageFlow(messageFlow, context) {
15202 visitIfDi(messageFlow, context);
15203 }
15204
15205 function handleMessageFlows(messageFlows, context) {
15206 forEach(messageFlows, contextual(handleMessageFlow, context));
15207 }
15208
15209 function handleDataAssociation(association, context) {
15210 visitIfDi(association, context);
15211 }
15212
15213 function handleDataInput(dataInput, context) {
15214 visitIfDi(dataInput, context);
15215 }
15216
15217 function handleDataOutput(dataOutput, context) {
15218 visitIfDi(dataOutput, context);
15219 }
15220
15221 function handleArtifact(artifact, context) {
15222
15223 // bpmn:TextAnnotation
15224 // bpmn:Group
15225 // bpmn:Association
15226
15227 visitIfDi(artifact, context);
15228 }
15229
15230 function handleArtifacts(artifacts, context) {
15231
15232 forEach(artifacts, function(e) {
15233 if (is$2(e, 'bpmn:Association')) {
15234 deferred.push(function() {
15235 handleArtifact(e, context);
15236 });
15237 } else {
15238 handleArtifact(e, context);
15239 }
15240 });
15241 }
15242
15243 function handleIoSpecification(ioSpecification, context) {
15244
15245 if (!ioSpecification) {
15246 return;
15247 }
15248
15249 forEach(ioSpecification.dataInputs, contextual(handleDataInput, context));
15250 forEach(ioSpecification.dataOutputs, contextual(handleDataOutput, context));
15251 }
15252
15253 function handleSubProcess(subProcess, context) {
15254 handleFlowElementsContainer(subProcess, context);
15255 handleArtifacts(subProcess.artifacts, context);
15256 }
15257
15258 function handleFlowNode(flowNode, context) {
15259 var childCtx = visitIfDi(flowNode, context);
15260
15261 if (is$2(flowNode, 'bpmn:SubProcess')) {
15262 handleSubProcess(flowNode, childCtx || context);
15263 }
15264
15265 if (is$2(flowNode, 'bpmn:Activity')) {
15266 handleIoSpecification(flowNode.ioSpecification, context);
15267 }
15268
15269 // defer handling of associations
15270 // affected types:
15271 //
15272 // * bpmn:Activity
15273 // * bpmn:ThrowEvent
15274 // * bpmn:CatchEvent
15275 //
15276 deferred.push(function() {
15277 forEach(flowNode.dataInputAssociations, contextual(handleDataAssociation, context));
15278 forEach(flowNode.dataOutputAssociations, contextual(handleDataAssociation, context));
15279 });
15280 }
15281
15282 function handleSequenceFlow(sequenceFlow, context) {
15283 visitIfDi(sequenceFlow, context);
15284 }
15285
15286 function handleDataElement(dataObject, context) {
15287 visitIfDi(dataObject, context);
15288 }
15289
15290 function handleLane(lane, context) {
15291
15292 deferred.push(function() {
15293
15294 var newContext = visitIfDi(lane, context);
15295
15296 if (lane.childLaneSet) {
15297 handleLaneSet(lane.childLaneSet, newContext || context);
15298 }
15299
15300 wireFlowNodeRefs(lane);
15301 });
15302 }
15303
15304 function handleLaneSet(laneSet, context) {
15305 forEach(laneSet.lanes, contextual(handleLane, context));
15306 }
15307
15308 function handleLaneSets(laneSets, context) {
15309 forEach(laneSets, contextual(handleLaneSet, context));
15310 }
15311
15312 function handleFlowElementsContainer(container, context) {
15313 handleFlowElements(container.flowElements, context);
15314
15315 if (container.laneSets) {
15316 handleLaneSets(container.laneSets, context);
15317 }
15318 }
15319
15320 function handleFlowElements(flowElements, context) {
15321 forEach(flowElements, function(e) {
15322 if (is$2(e, 'bpmn:SequenceFlow')) {
15323 deferred.push(function() {
15324 handleSequenceFlow(e, context);
15325 });
15326 } else if (is$2(e, 'bpmn:BoundaryEvent')) {
15327 deferred.unshift(function() {
15328 handleFlowNode(e, context);
15329 });
15330 } else if (is$2(e, 'bpmn:FlowNode')) {
15331 handleFlowNode(e, context);
15332 } else if (is$2(e, 'bpmn:DataObject')) ; else if (is$2(e, 'bpmn:DataStoreReference')) {
15333 handleDataElement(e, context);
15334 } else if (is$2(e, 'bpmn:DataObjectReference')) {
15335 handleDataElement(e, context);
15336 } else {
15337 logError(
15338 translate('unrecognized flowElement {element} in context {context}', {
15339 element: elementToString(e),
15340 context: (context ? elementToString(context.businessObject) : 'null')
15341 }),
15342 { element: e, context: context }
15343 );
15344 }
15345 });
15346 }
15347
15348 function handleParticipant(participant, context) {
15349 var newCtx = visitIfDi(participant, context);
15350
15351 var process = participant.processRef;
15352 if (process) {
15353 handleProcess(process, newCtx || context);
15354 }
15355 }
15356
15357 function handleCollaboration(collaboration) {
15358
15359 forEach(collaboration.participants, contextual(handleParticipant));
15360
15361 handleArtifacts(collaboration.artifacts);
15362
15363 // handle message flows latest in the process
15364 deferred.push(function() {
15365 handleMessageFlows(collaboration.messageFlows);
15366 });
15367 }
15368
15369
15370 function wireFlowNodeRefs(lane) {
15371
15372 // wire the virtual flowNodeRefs <-> relationship
15373 forEach(lane.flowNodeRef, function(flowNode) {
15374 var lanes = flowNode.get('lanes');
15375
15376 if (lanes) {
15377 lanes.push(lane);
15378 }
15379 });
15380 }
15381
15382 // API //////////////////////
15383
15384 return {
15385 handleDeferred: handleDeferred,
15386 handleDefinitions: handleDefinitions,
15387 handleSubProcess: handleSubProcess,
15388 registerDi: registerDi
15389 };
15390 }
15391
15392 /**
15393 * The importBpmnDiagram result.
15394 *
15395 * @typedef {Object} ImportBPMNDiagramResult
15396 *
15397 * @property {Array<string>} warnings
15398 */
15399
15400 /**
15401 * The importBpmnDiagram error.
15402 *
15403 * @typedef {Error} ImportBPMNDiagramError
15404 *
15405 * @property {Array<string>} warnings
15406 */
15407
15408 /**
15409 * Import the definitions into a diagram.
15410 *
15411 * Errors and warnings are reported through the specified callback.
15412 *
15413 * @param {djs.Diagram} diagram
15414 * @param {ModdleElement<Definitions>} definitions
15415 * @param {ModdleElement<BPMNDiagram>} [bpmnDiagram] the diagram to be rendered
15416 * (if not provided, the first one will be rendered)
15417 *
15418 * Returns {Promise<ImportBPMNDiagramResult, ImportBPMNDiagramError>}
15419 */
15420 function importBpmnDiagram(diagram, definitions, bpmnDiagram) {
15421
15422 var importer,
15423 eventBus,
15424 translate;
15425
15426 var error,
15427 warnings = [];
15428
15429 /**
15430 * Walk the diagram semantically, importing (=drawing)
15431 * all elements you encounter.
15432 *
15433 * @param {ModdleElement<Definitions>} definitions
15434 * @param {ModdleElement<BPMNDiagram>} bpmnDiagram
15435 */
15436 function render(definitions, bpmnDiagram) {
15437
15438 var visitor = {
15439
15440 root: function(element) {
15441 return importer.add(element);
15442 },
15443
15444 element: function(element, parentShape) {
15445 return importer.add(element, parentShape);
15446 },
15447
15448 error: function(message, context) {
15449 warnings.push({ message: message, context: context });
15450 }
15451 };
15452
15453 var walker = new BpmnTreeWalker(visitor, translate);
15454
15455 // traverse BPMN 2.0 document model,
15456 // starting at definitions
15457 walker.handleDefinitions(definitions, bpmnDiagram);
15458 }
15459
15460 return new Promise(function(resolve, reject) {
15461 try {
15462 importer = diagram.get('bpmnImporter');
15463 eventBus = diagram.get('eventBus');
15464 translate = diagram.get('translate');
15465
15466 eventBus.fire('import.render.start', { definitions: definitions });
15467
15468 render(definitions, bpmnDiagram);
15469
15470 eventBus.fire('import.render.complete', {
15471 error: error,
15472 warnings: warnings
15473 });
15474
15475 return resolve({ warnings: warnings });
15476 } catch (e) {
15477
15478 e.warnings = warnings;
15479 return reject(e);
15480 }
15481 });
15482 }
15483
15484 // TODO(nikku): remove with future bpmn-js version
15485
15486 /**
15487 * Wraps APIs to check:
15488 *
15489 * 1) If a callback is passed -> Warn users about callback deprecation.
15490 * 2) If Promise class is implemented in current environment.
15491 *
15492 * @private
15493 */
15494 function wrapForCompatibility(api) {
15495
15496 return function() {
15497
15498 if (!window.Promise) {
15499 throw new Error('Promises is not supported in this environment. Please polyfill Promise.');
15500 }
15501
15502 var argLen = arguments.length;
15503 if (argLen >= 1 && isFunction(arguments[argLen - 1])) {
15504
15505 var callback = arguments[argLen - 1];
15506
15507 console.warn(new Error(
15508 'Passing callbacks to ' + api.name + ' is deprecated and will be removed in a future major release. ' +
15509 'Please switch to promises: https://bpmn.io/l/moving-to-promises.html'
15510 ));
15511
15512 var argsWithoutCallback = Array.prototype.slice.call(arguments, 0, -1);
15513
15514 api.apply(this, argsWithoutCallback).then(function(result) {
15515
15516 var firstKey = Object.keys(result)[0];
15517
15518 // The APIs we are wrapping all resolve a single item depending on the API.
15519 // For instance, importXML resolves { warnings } and saveXML returns { xml }.
15520 // That's why we can call the callback with the first item of result.
15521 return callback(null, result[firstKey]);
15522
15523 // Passing a second paramter instead of catch because we don't want to
15524 // catch errors thrown by callback().
15525 }, function(err) {
15526
15527 return callback(err, err.warnings);
15528 });
15529 } else {
15530
15531 return api.apply(this, arguments);
15532 }
15533 };
15534 }
15535
15536 /**
15537 * This file must not be changed or exchanged.
15538 *
15539 * @see http://bpmn.io/license for more information.
15540 */
15541
15542
15543 // inlined ../../resources/logo.svg
15544 var BPMNIO_LOGO_SVG = '<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 14.02 5.57" width="53" height="21" style="vertical-align:middle"><path fill="currentColor" d="M1.88.92v.14c0 .41-.13.68-.4.8.33.14.46.44.46.86v.33c0 .61-.33.95-.95.95H0V0h.95c.65 0 .93.3.93.92zM.63.57v1.06h.24c.24 0 .38-.1.38-.43V.98c0-.28-.1-.4-.32-.4zm0 1.63v1.22h.36c.2 0 .32-.1.32-.39v-.35c0-.37-.12-.48-.4-.48H.63zM4.18.99v.52c0 .64-.31.98-.94.98h-.3V4h-.62V0h.92c.63 0 .94.35.94.99zM2.94.57v1.35h.3c.2 0 .3-.09.3-.37v-.6c0-.29-.1-.38-.3-.38h-.3zm2.89 2.27L6.25 0h.88v4h-.6V1.12L6.1 3.99h-.6l-.46-2.82v2.82h-.55V0h.87zM8.14 1.1V4h-.56V0h.79L9 2.4V0h.56v4h-.64zm2.49 2.29v.6h-.6v-.6zM12.12 1c0-.63.33-1 .95-1 .61 0 .95.37.95 1v2.04c0 .64-.34 1-.95 1-.62 0-.95-.37-.95-1zm.62 2.08c0 .28.13.39.33.39s.32-.1.32-.4V.98c0-.29-.12-.4-.32-.4s-.33.11-.33.4z"/><path fill="currentColor" d="M0 4.53h14.02v1.04H0zM11.08 0h.63v.62h-.63zm.63 4V1h-.63v2.98z"/></svg>';
15545
15546 var BPMNIO_IMG = BPMNIO_LOGO_SVG;
15547
15548 function css(attrs) {
15549 return attrs.join(';');
15550 }
15551
15552 var LINK_STYLES = css([
15553 'color: #404040'
15554 ]);
15555
15556 var LIGHTBOX_STYLES = css([
15557 'z-index: 1001',
15558 'position: fixed',
15559 'top: 0',
15560 'left: 0',
15561 'right: 0',
15562 'bottom: 0'
15563 ]);
15564
15565 var BACKDROP_STYLES = css([
15566 'width: 100%',
15567 'height: 100%',
15568 'background: rgba(40,40,40,0.2)'
15569 ]);
15570
15571 var NOTICE_STYLES = css([
15572 'position: absolute',
15573 'left: 50%',
15574 'top: 40%',
15575 'transform: translate(-50%)',
15576 'width: 260px',
15577 'padding: 10px',
15578 'background: white',
15579 'box-shadow: 0 1px 4px rgba(0,0,0,0.3)',
15580 'font-family: Helvetica, Arial, sans-serif',
15581 'font-size: 14px',
15582 'display: flex',
15583 'line-height: 1.3'
15584 ]);
15585
15586 var LIGHTBOX_MARKUP =
15587 '<div class="bjs-powered-by-lightbox" style="' + LIGHTBOX_STYLES + '">' +
15588 '<div class="backdrop" style="' + BACKDROP_STYLES + '"></div>' +
15589 '<div class="notice" style="' + NOTICE_STYLES + '">' +
15590 '<a href="https://bpmn.io" target="_blank" rel="noopener" style="margin: 15px 20px 15px 10px; align-self: center;' + LINK_STYLES + '">' +
15591 BPMNIO_IMG +
15592 '</a>' +
15593 '<span>' +
15594 'Web-based tooling for BPMN, DMN and CMMN diagrams ' +
15595 'powered by <a href="https://bpmn.io" target="_blank" rel="noopener">bpmn.io</a>.' +
15596 '</span>' +
15597 '</div>' +
15598 '</div>';
15599
15600
15601 var lightbox;
15602
15603 function open() {
15604
15605 if (!lightbox) {
15606 lightbox = domify(LIGHTBOX_MARKUP);
15607
15608 delegate.bind(lightbox, '.backdrop', 'click', function(event) {
15609 document.body.removeChild(lightbox);
15610 });
15611 }
15612
15613 document.body.appendChild(lightbox);
15614 }
15615
15616 /**
15617 * The code in the <project-logo></project-logo> area
15618 * must not be changed.
15619 *
15620 * @see http://bpmn.io/license for more information.
15621 */
15622
15623 /**
15624 * A base viewer for BPMN 2.0 diagrams.
15625 *
15626 * Have a look at {@link Viewer}, {@link NavigatedViewer} or {@link Modeler} for
15627 * bundles that include actual features.
15628 *
15629 * @param {Object} [options] configuration options to pass to the viewer
15630 * @param {DOMElement} [options.container] the container to render the viewer in, defaults to body.
15631 * @param {string|number} [options.width] the width of the viewer
15632 * @param {string|number} [options.height] the height of the viewer
15633 * @param {Object} [options.moddleExtensions] extension packages to provide
15634 * @param {Array<didi.Module>} [options.modules] a list of modules to override the default modules
15635 * @param {Array<didi.Module>} [options.additionalModules] a list of modules to use with the default modules
15636 */
15637 function BaseViewer(options) {
15638
15639 options = assign({}, DEFAULT_OPTIONS, options);
15640
15641 this._moddle = this._createModdle(options);
15642
15643 this._container = this._createContainer(options);
15644
15645 /* <project-logo> */
15646
15647 addProjectLogo(this._container);
15648
15649 /* </project-logo> */
15650
15651 this._init(this._container, this._moddle, options);
15652 }
15653
15654 inherits$1(BaseViewer, Diagram);
15655
15656 /**
15657 * The importXML result.
15658 *
15659 * @typedef {Object} ImportXMLResult
15660 *
15661 * @property {Array<string>} warnings
15662 */
15663
15664 /**
15665 * The importXML error.
15666 *
15667 * @typedef {Error} ImportXMLError
15668 *
15669 * @property {Array<string>} warnings
15670 */
15671
15672 /**
15673 * Parse and render a BPMN 2.0 diagram.
15674 *
15675 * Once finished the viewer reports back the result to the
15676 * provided callback function with (err, warnings).
15677 *
15678 * ## Life-Cycle Events
15679 *
15680 * During import the viewer will fire life-cycle events:
15681 *
15682 * * import.parse.start (about to read model from xml)
15683 * * import.parse.complete (model read; may have worked or not)
15684 * * import.render.start (graphical import start)
15685 * * import.render.complete (graphical import finished)
15686 * * import.done (everything done)
15687 *
15688 * You can use these events to hook into the life-cycle.
15689 *
15690 * @param {string} xml the BPMN 2.0 xml
15691 * @param {ModdleElement<BPMNDiagram>|string} [bpmnDiagram] BPMN diagram or id of diagram to render (if not provided, the first one will be rendered)
15692 *
15693 * Returns {Promise<ImportXMLResult, ImportXMLError>}
15694 */
15695 BaseViewer.prototype.importXML = wrapForCompatibility(function importXML(xml, bpmnDiagram) {
15696
15697 var self = this;
15698
15699 function ParseCompleteEvent(data) {
15700
15701 var event = self.get('eventBus').createEvent(data);
15702
15703 // TODO(nikku): remove with future bpmn-js version
15704 Object.defineProperty(event, 'context', {
15705 enumerable: true,
15706 get: function() {
15707
15708 console.warn(new Error(
15709 'import.parse.complete <context> is deprecated ' +
15710 'and will be removed in future library versions'
15711 ));
15712
15713 return {
15714 warnings: data.warnings,
15715 references: data.references,
15716 elementsById: data.elementsById
15717 };
15718 }
15719 });
15720
15721 return event;
15722 }
15723
15724 return new Promise(function(resolve, reject) {
15725
15726 // hook in pre-parse listeners +
15727 // allow xml manipulation
15728 xml = self._emit('import.parse.start', { xml: xml }) || xml;
15729
15730 self._moddle.fromXML(xml, 'bpmn:Definitions').then(function(result) {
15731 var definitions = result.rootElement;
15732 var references = result.references;
15733 var parseWarnings = result.warnings;
15734 var elementsById = result.elementsById;
15735
15736 // hook in post parse listeners +
15737 // allow definitions manipulation
15738 definitions = self._emit('import.parse.complete', ParseCompleteEvent({
15739 error: null,
15740 definitions: definitions,
15741 elementsById: elementsById,
15742 references: references,
15743 warnings: parseWarnings
15744 })) || definitions;
15745
15746 self.importDefinitions(definitions, bpmnDiagram).then(function(result) {
15747 var allWarnings = [].concat(parseWarnings, result.warnings || []);
15748
15749 self._emit('import.done', { error: null, warnings: allWarnings });
15750
15751 return resolve({ warnings: allWarnings });
15752 }).catch(function(err) {
15753 var allWarnings = [].concat(parseWarnings, err.warnings || []);
15754
15755 self._emit('import.done', { error: err, warnings: allWarnings });
15756
15757 return reject(addWarningsToError(err, allWarnings));
15758 });
15759 }).catch(function(err) {
15760
15761 self._emit('import.parse.complete', {
15762 error: err
15763 });
15764
15765 err = checkValidationError(err);
15766
15767 self._emit('import.done', { error: err, warnings: err.warnings });
15768
15769 return reject(err);
15770 });
15771 });
15772 });
15773
15774 /**
15775 * The importDefinitions result.
15776 *
15777 * @typedef {Object} ImportDefinitionsResult
15778 *
15779 * @property {Array<string>} warnings
15780 */
15781
15782 /**
15783 * The importDefinitions error.
15784 *
15785 * @typedef {Error} ImportDefinitionsError
15786 *
15787 * @property {Array<string>} warnings
15788 */
15789
15790 /**
15791 * Import parsed definitions and render a BPMN 2.0 diagram.
15792 *
15793 * Once finished the viewer reports back the result to the
15794 * provided callback function with (err, warnings).
15795 *
15796 * ## Life-Cycle Events
15797 *
15798 * During import the viewer will fire life-cycle events:
15799 *
15800 * * import.render.start (graphical import start)
15801 * * import.render.complete (graphical import finished)
15802 *
15803 * You can use these events to hook into the life-cycle.
15804 *
15805 * @param {ModdleElement<Definitions>} definitions parsed BPMN 2.0 definitions
15806 * @param {ModdleElement<BPMNDiagram>|string} [bpmnDiagram] BPMN diagram or id of diagram to render (if not provided, the first one will be rendered)
15807 *
15808 * Returns {Promise<ImportDefinitionsResult, ImportDefinitionsError>}
15809 */
15810 BaseViewer.prototype.importDefinitions = wrapForCompatibility(function importDefinitions(definitions, bpmnDiagram) {
15811
15812 var self = this;
15813
15814 return new Promise(function(resolve, reject) {
15815
15816 self._setDefinitions(definitions);
15817
15818 self.open(bpmnDiagram).then(function(result) {
15819
15820 var warnings = result.warnings;
15821
15822 return resolve({ warnings: warnings });
15823 }).catch(function(err) {
15824
15825 return reject(err);
15826 });
15827 });
15828 });
15829
15830 /**
15831 * The open result.
15832 *
15833 * @typedef {Object} OpenResult
15834 *
15835 * @property {Array<string>} warnings
15836 */
15837
15838 /**
15839 * The open error.
15840 *
15841 * @typedef {Error} OpenError
15842 *
15843 * @property {Array<string>} warnings
15844 */
15845
15846 /**
15847 * Open diagram of previously imported XML.
15848 *
15849 * Once finished the viewer reports back the result to the
15850 * provided callback function with (err, warnings).
15851 *
15852 * ## Life-Cycle Events
15853 *
15854 * During switch the viewer will fire life-cycle events:
15855 *
15856 * * import.render.start (graphical import start)
15857 * * import.render.complete (graphical import finished)
15858 *
15859 * You can use these events to hook into the life-cycle.
15860 *
15861 * @param {string|ModdleElement<BPMNDiagram>} [bpmnDiagramOrId] id or the diagram to open
15862 *
15863 * Returns {Promise<OpenResult, OpenError>}
15864 */
15865 BaseViewer.prototype.open = wrapForCompatibility(function open(bpmnDiagramOrId) {
15866
15867 var definitions = this._definitions;
15868 var bpmnDiagram = bpmnDiagramOrId;
15869
15870 var self = this;
15871
15872 return new Promise(function(resolve, reject) {
15873 if (!definitions) {
15874 var err1 = new Error('no XML imported');
15875
15876 return reject(addWarningsToError(err1, []));
15877 }
15878
15879 if (typeof bpmnDiagramOrId === 'string') {
15880 bpmnDiagram = findBPMNDiagram(definitions, bpmnDiagramOrId);
15881
15882 if (!bpmnDiagram) {
15883 var err2 = new Error('BPMNDiagram <' + bpmnDiagramOrId + '> not found');
15884
15885 return reject(addWarningsToError(err2, []));
15886 }
15887 }
15888
15889 // clear existing rendered diagram
15890 // catch synchronous exceptions during #clear()
15891 try {
15892 self.clear();
15893 } catch (error) {
15894
15895 return reject(addWarningsToError(error, []));
15896 }
15897
15898 // perform graphical import
15899 importBpmnDiagram(self, definitions, bpmnDiagram).then(function(result) {
15900
15901 var warnings = result.warnings;
15902
15903 return resolve({ warnings: warnings });
15904 }).catch(function(err) {
15905
15906 return reject(err);
15907 });
15908 });
15909 });
15910
15911 /**
15912 * The saveXML result.
15913 *
15914 * @typedef {Object} SaveXMLResult
15915 *
15916 * @property {string} xml
15917 */
15918
15919 /**
15920 * Export the currently displayed BPMN 2.0 diagram as
15921 * a BPMN 2.0 XML document.
15922 *
15923 * ## Life-Cycle Events
15924 *
15925 * During XML saving the viewer will fire life-cycle events:
15926 *
15927 * * saveXML.start (before serialization)
15928 * * saveXML.serialized (after xml generation)
15929 * * saveXML.done (everything done)
15930 *
15931 * You can use these events to hook into the life-cycle.
15932 *
15933 * @param {Object} [options] export options
15934 * @param {boolean} [options.format=false] output formatted XML
15935 * @param {boolean} [options.preamble=true] output preamble
15936 *
15937 * Returns {Promise<SaveXMLResult, Error>}
15938 */
15939 BaseViewer.prototype.saveXML = wrapForCompatibility(function saveXML(options) {
15940
15941 options = options || {};
15942
15943 var self = this;
15944
15945 var definitions = this._definitions;
15946
15947 return new Promise(function(resolve) {
15948
15949 if (!definitions) {
15950 return resolve({
15951 error: new Error('no definitions loaded')
15952 });
15953 }
15954
15955 // allow to fiddle around with definitions
15956 definitions = self._emit('saveXML.start', {
15957 definitions: definitions
15958 }) || definitions;
15959
15960 self._moddle.toXML(definitions, options).then(function(result) {
15961
15962 var xml = result.xml;
15963
15964 xml = self._emit('saveXML.serialized', {
15965 xml: xml
15966 }) || xml;
15967
15968 return resolve({
15969 xml: xml
15970 });
15971 });
15972 }).catch(function(error) {
15973 return { error: error };
15974 }).then(function(result) {
15975
15976 self._emit('saveXML.done', result);
15977
15978 var error = result.error;
15979
15980 if (error) {
15981 return Promise.reject(error);
15982 }
15983
15984 return result;
15985 });
15986 });
15987
15988 /**
15989 * The saveSVG result.
15990 *
15991 * @typedef {Object} SaveSVGResult
15992 *
15993 * @property {string} svg
15994 */
15995
15996 /**
15997 * Export the currently displayed BPMN 2.0 diagram as
15998 * an SVG image.
15999 *
16000 * ## Life-Cycle Events
16001 *
16002 * During SVG saving the viewer will fire life-cycle events:
16003 *
16004 * * saveSVG.start (before serialization)
16005 * * saveSVG.done (everything done)
16006 *
16007 * You can use these events to hook into the life-cycle.
16008 *
16009 * @param {Object} [options]
16010 *
16011 * Returns {Promise<SaveSVGResult, Error>}
16012 */
16013 BaseViewer.prototype.saveSVG = wrapForCompatibility(function saveSVG(options) {
16014
16015 var self = this;
16016
16017 return new Promise(function(resolve, reject) {
16018
16019 self._emit('saveSVG.start');
16020
16021 var svg, err;
16022
16023 try {
16024 var canvas = self.get('canvas');
16025
16026 var contentNode = canvas.getDefaultLayer(),
16027 defsNode = query('defs', canvas._svg);
16028
16029 var contents = innerSVG(contentNode),
16030 defs = defsNode ? '<defs>' + innerSVG(defsNode) + '</defs>' : '';
16031
16032 var bbox = contentNode.getBBox();
16033
16034 svg =
16035 '<?xml version="1.0" encoding="utf-8"?>\n' +
16036 '<!-- created with bpmn-js / http://bpmn.io -->\n' +
16037 '<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">\n' +
16038 '<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" ' +
16039 'width="' + bbox.width + '" height="' + bbox.height + '" ' +
16040 'viewBox="' + bbox.x + ' ' + bbox.y + ' ' + bbox.width + ' ' + bbox.height + '" version="1.1">' +
16041 defs + contents +
16042 '</svg>';
16043 } catch (e) {
16044 err = e;
16045 }
16046
16047 self._emit('saveSVG.done', {
16048 error: err,
16049 svg: svg
16050 });
16051
16052 if (!err) {
16053 return resolve({ svg: svg });
16054 }
16055
16056 return reject(err);
16057 });
16058 });
16059
16060 /**
16061 * Get a named diagram service.
16062 *
16063 * @example
16064 *
16065 * var elementRegistry = viewer.get('elementRegistry');
16066 * var startEventShape = elementRegistry.get('StartEvent_1');
16067 *
16068 * @param {string} name
16069 *
16070 * @return {Object} diagram service instance
16071 *
16072 * @method BaseViewer#get
16073 */
16074
16075 /**
16076 * Invoke a function in the context of this viewer.
16077 *
16078 * @example
16079 *
16080 * viewer.invoke(function(elementRegistry) {
16081 * var startEventShape = elementRegistry.get('StartEvent_1');
16082 * });
16083 *
16084 * @param {Function} fn to be invoked
16085 *
16086 * @return {Object} the functions return value
16087 *
16088 * @method BaseViewer#invoke
16089 */
16090
16091
16092 BaseViewer.prototype._setDefinitions = function(definitions) {
16093 this._definitions = definitions;
16094 };
16095
16096 BaseViewer.prototype.getModules = function() {
16097 return this._modules;
16098 };
16099
16100 /**
16101 * Remove all drawn elements from the viewer.
16102 *
16103 * After calling this method the viewer can still
16104 * be reused for opening another diagram.
16105 *
16106 * @method BaseViewer#clear
16107 */
16108 BaseViewer.prototype.clear = function() {
16109 if (!this.getDefinitions()) {
16110
16111 // no diagram to clear
16112 return;
16113 }
16114
16115 // remove businessObject#di binding
16116 //
16117 // this is necessary, as we establish the bindings
16118 // in the BpmnTreeWalker (and assume none are given
16119 // on reimport)
16120 this.get('elementRegistry').forEach(function(element) {
16121 var bo = element.businessObject;
16122
16123 if (bo && bo.di) {
16124 delete bo.di;
16125 }
16126 });
16127
16128 // remove drawn elements
16129 Diagram.prototype.clear.call(this);
16130 };
16131
16132 /**
16133 * Destroy the viewer instance and remove all its
16134 * remainders from the document tree.
16135 */
16136 BaseViewer.prototype.destroy = function() {
16137
16138 // diagram destroy
16139 Diagram.prototype.destroy.call(this);
16140
16141 // dom detach
16142 remove$2(this._container);
16143 };
16144
16145 /**
16146 * Register an event listener
16147 *
16148 * Remove a previously added listener via {@link #off(event, callback)}.
16149 *
16150 * @param {string} event
16151 * @param {number} [priority]
16152 * @param {Function} callback
16153 * @param {Object} [that]
16154 */
16155 BaseViewer.prototype.on = function(event, priority, callback, target) {
16156 return this.get('eventBus').on(event, priority, callback, target);
16157 };
16158
16159 /**
16160 * De-register an event listener
16161 *
16162 * @param {string} event
16163 * @param {Function} callback
16164 */
16165 BaseViewer.prototype.off = function(event, callback) {
16166 this.get('eventBus').off(event, callback);
16167 };
16168
16169 BaseViewer.prototype.attachTo = function(parentNode) {
16170
16171 if (!parentNode) {
16172 throw new Error('parentNode required');
16173 }
16174
16175 // ensure we detach from the
16176 // previous, old parent
16177 this.detach();
16178
16179 // unwrap jQuery if provided
16180 if (parentNode.get && parentNode.constructor.prototype.jquery) {
16181 parentNode = parentNode.get(0);
16182 }
16183
16184 if (typeof parentNode === 'string') {
16185 parentNode = query(parentNode);
16186 }
16187
16188 parentNode.appendChild(this._container);
16189
16190 this._emit('attach', {});
16191
16192 this.get('canvas').resized();
16193 };
16194
16195 BaseViewer.prototype.getDefinitions = function() {
16196 return this._definitions;
16197 };
16198
16199 BaseViewer.prototype.detach = function() {
16200
16201 var container = this._container,
16202 parentNode = container.parentNode;
16203
16204 if (!parentNode) {
16205 return;
16206 }
16207
16208 this._emit('detach', {});
16209
16210 parentNode.removeChild(container);
16211 };
16212
16213 BaseViewer.prototype._init = function(container, moddle, options) {
16214
16215 var baseModules = options.modules || this.getModules(),
16216 additionalModules = options.additionalModules || [],
16217 staticModules = [
16218 {
16219 bpmnjs: [ 'value', this ],
16220 moddle: [ 'value', moddle ]
16221 }
16222 ];
16223
16224 var diagramModules = [].concat(staticModules, baseModules, additionalModules);
16225
16226 var diagramOptions = assign(omit(options, [ 'additionalModules' ]), {
16227 canvas: assign({}, options.canvas, { container: container }),
16228 modules: diagramModules
16229 });
16230
16231 // invoke diagram constructor
16232 Diagram.call(this, diagramOptions);
16233
16234 if (options && options.container) {
16235 this.attachTo(options.container);
16236 }
16237 };
16238
16239 /**
16240 * Emit an event on the underlying {@link EventBus}
16241 *
16242 * @param {string} type
16243 * @param {Object} event
16244 *
16245 * @return {Object} event processing result (if any)
16246 */
16247 BaseViewer.prototype._emit = function(type, event) {
16248 return this.get('eventBus').fire(type, event);
16249 };
16250
16251 BaseViewer.prototype._createContainer = function(options) {
16252
16253 var container = domify('<div class="bjs-container"></div>');
16254
16255 assign(container.style, {
16256 width: ensureUnit(options.width),
16257 height: ensureUnit(options.height),
16258 position: options.position
16259 });
16260
16261 return container;
16262 };
16263
16264 BaseViewer.prototype._createModdle = function(options) {
16265 var moddleOptions = assign({}, this._moddleExtensions, options.moddleExtensions);
16266
16267 return new simple(moddleOptions);
16268 };
16269
16270 BaseViewer.prototype._modules = [];
16271
16272 // helpers ///////////////
16273
16274 function addWarningsToError(err, warningsAry) {
16275 err.warnings = warningsAry;
16276 return err;
16277 }
16278
16279 function checkValidationError(err) {
16280
16281 // check if we can help the user by indicating wrong BPMN 2.0 xml
16282 // (in case he or the exporting tool did not get that right)
16283
16284 var pattern = /unparsable content <([^>]+)> detected([\s\S]*)$/;
16285 var match = pattern.exec(err.message);
16286
16287 if (match) {
16288 err.message =
16289 'unparsable content <' + match[1] + '> detected; ' +
16290 'this may indicate an invalid BPMN 2.0 diagram file' + match[2];
16291 }
16292
16293 return err;
16294 }
16295
16296 var DEFAULT_OPTIONS = {
16297 width: '100%',
16298 height: '100%',
16299 position: 'relative'
16300 };
16301
16302
16303 /**
16304 * Ensure the passed argument is a proper unit (defaulting to px)
16305 */
16306 function ensureUnit(val) {
16307 return val + (isNumber(val) ? 'px' : '');
16308 }
16309
16310
16311 /**
16312 * Find BPMNDiagram in definitions by ID
16313 *
16314 * @param {ModdleElement<Definitions>} definitions
16315 * @param {string} diagramId
16316 *
16317 * @return {ModdleElement<BPMNDiagram>|null}
16318 */
16319 function findBPMNDiagram(definitions, diagramId) {
16320 if (!diagramId) {
16321 return null;
16322 }
16323
16324 return find(definitions.diagrams, function(element) {
16325 return element.id === diagramId;
16326 }) || null;
16327 }
16328
16329 /**
16330 * Adds the project logo to the diagram container as
16331 * required by the bpmn.io license.
16332 *
16333 * @see http://bpmn.io/license
16334 *
16335 * @param {Element} container
16336 */
16337 function addProjectLogo(container) {
16338 var img = BPMNIO_IMG;
16339
16340 var linkMarkup =
16341 '<a href="http://bpmn.io" ' +
16342 'target="_blank" ' +
16343 'class="bjs-powered-by" ' +
16344 'title="Powered by bpmn.io" ' +
16345 'style="position: absolute; bottom: 15px; right: 15px; z-index: 100; ' + LINK_STYLES + '">' +
16346 img +
16347 '</a>';
16348
16349 var linkElement = domify(linkMarkup);
16350
16351 container.appendChild(linkElement);
16352
16353 componentEvent.bind(linkElement, 'click', function(event) {
16354 open();
16355
16356 event.preventDefault();
16357 });
16358 }
16359
16360 /* </project-logo> */
16361
16362 /**
16363 * A base modeler for BPMN 2.0 diagrams.
16364 *
16365 * Have a look at {@link Modeler} for a bundle that includes actual features.
16366 *
16367 * @param {Object} [options] configuration options to pass to the viewer
16368 * @param {DOMElement} [options.container] the container to render the viewer in, defaults to body.
16369 * @param {string|number} [options.width] the width of the viewer
16370 * @param {string|number} [options.height] the height of the viewer
16371 * @param {Object} [options.moddleExtensions] extension packages to provide
16372 * @param {Array<didi.Module>} [options.modules] a list of modules to override the default modules
16373 * @param {Array<didi.Module>} [options.additionalModules] a list of modules to use with the default modules
16374 */
16375 function BaseModeler(options) {
16376 BaseViewer.call(this, options);
16377
16378 // hook ID collection into the modeler
16379 this.on('import.parse.complete', function(event) {
16380 if (!event.error) {
16381 this._collectIds(event.definitions, event.elementsById);
16382 }
16383 }, this);
16384
16385 this.on('diagram.destroy', function() {
16386 this.get('moddle').ids.clear();
16387 }, this);
16388 }
16389
16390 inherits$1(BaseModeler, BaseViewer);
16391
16392
16393 /**
16394 * Create a moddle instance, attaching ids to it.
16395 *
16396 * @param {Object} options
16397 */
16398 BaseModeler.prototype._createModdle = function(options) {
16399 var moddle = BaseViewer.prototype._createModdle.call(this, options);
16400
16401 // attach ids to moddle to be able to track
16402 // and validated ids in the BPMN 2.0 XML document
16403 // tree
16404 moddle.ids = new Ids([ 32, 36, 1 ]);
16405
16406 return moddle;
16407 };
16408
16409 /**
16410 * Collect ids processed during parsing of the
16411 * definitions object.
16412 *
16413 * @param {ModdleElement} definitions
16414 * @param {Context} context
16415 */
16416 BaseModeler.prototype._collectIds = function(definitions, elementsById) {
16417
16418 var moddle = definitions.$model,
16419 ids = moddle.ids,
16420 id;
16421
16422 // remove references from previous import
16423 ids.clear();
16424
16425 for (id in elementsById) {
16426 ids.claim(id, elementsById[id]);
16427 }
16428 };
16429
16430 /**
16431 * Is an element of the given BPMN type?
16432 *
16433 * @param {djs.model.Base|ModdleElement} element
16434 * @param {string} type
16435 *
16436 * @return {boolean}
16437 */
16438 function is$1(element, type) {
16439 var bo = getBusinessObject(element);
16440
16441 return bo && (typeof bo.$instanceOf === 'function') && bo.$instanceOf(type);
16442 }
16443
16444
16445 /**
16446 * Return the business object for a given element.
16447 *
16448 * @param {djs.model.Base|ModdleElement} element
16449 *
16450 * @return {ModdleElement}
16451 */
16452 function getBusinessObject(element) {
16453 return (element && element.businessObject) || element;
16454 }
16455
16456 function isExpanded(element) {
16457
16458 if (is$1(element, 'bpmn:CallActivity')) {
16459 return false;
16460 }
16461
16462 if (is$1(element, 'bpmn:SubProcess')) {
16463 return getBusinessObject(element).di && !!getBusinessObject(element).di.isExpanded;
16464 }
16465
16466 if (is$1(element, 'bpmn:Participant')) {
16467 return !!getBusinessObject(element).processRef;
16468 }
16469
16470 return true;
16471 }
16472
16473 function isInterrupting(element) {
16474 return element && getBusinessObject(element).isInterrupting !== false;
16475 }
16476
16477 function isEventSubProcess(element) {
16478 return element && !!getBusinessObject(element).triggeredByEvent;
16479 }
16480
16481 function hasEventDefinition$2(element, eventType) {
16482 var bo = getBusinessObject(element),
16483 hasEventDefinition = false;
16484
16485 if (bo.eventDefinitions) {
16486 forEach(bo.eventDefinitions, function(event) {
16487 if (is$1(event, eventType)) {
16488 hasEventDefinition = true;
16489 }
16490 });
16491 }
16492
16493 return hasEventDefinition;
16494 }
16495
16496 function hasErrorEventDefinition(element) {
16497 return hasEventDefinition$2(element, 'bpmn:ErrorEventDefinition');
16498 }
16499
16500 function hasEscalationEventDefinition(element) {
16501 return hasEventDefinition$2(element, 'bpmn:EscalationEventDefinition');
16502 }
16503
16504 function hasCompensateEventDefinition(element) {
16505 return hasEventDefinition$2(element, 'bpmn:CompensateEventDefinition');
16506 }
16507
16508 function getLabelAttr(semantic) {
16509 if (
16510 is$1(semantic, 'bpmn:FlowElement') ||
16511 is$1(semantic, 'bpmn:Participant') ||
16512 is$1(semantic, 'bpmn:Lane') ||
16513 is$1(semantic, 'bpmn:SequenceFlow') ||
16514 is$1(semantic, 'bpmn:MessageFlow') ||
16515 is$1(semantic, 'bpmn:DataInput') ||
16516 is$1(semantic, 'bpmn:DataOutput')
16517 ) {
16518 return 'name';
16519 }
16520
16521 if (is$1(semantic, 'bpmn:TextAnnotation')) {
16522 return 'text';
16523 }
16524
16525 if (is$1(semantic, 'bpmn:Group')) {
16526 return 'categoryValueRef';
16527 }
16528 }
16529
16530 function getCategoryValue(semantic) {
16531 var categoryValueRef = semantic['categoryValueRef'];
16532
16533 if (!categoryValueRef) {
16534 return '';
16535 }
16536
16537
16538 return categoryValueRef.value || '';
16539 }
16540
16541 function getLabel(element) {
16542 var semantic = element.businessObject,
16543 attr = getLabelAttr(semantic);
16544
16545 if (attr) {
16546
16547 if (attr === 'categoryValueRef') {
16548
16549 return getCategoryValue(semantic);
16550 }
16551
16552 return semantic[attr] || '';
16553 }
16554 }
16555
16556
16557 function setLabel(element, text, isExternal) {
16558 var semantic = element.businessObject,
16559 attr = getLabelAttr(semantic);
16560
16561 if (attr) {
16562
16563 if (attr === 'categoryValueRef') {
16564 semantic['categoryValueRef'].value = text;
16565 } else {
16566 semantic[attr] = text;
16567 }
16568
16569 }
16570
16571 return element;
16572 }
16573
16574 // element utils //////////////////////
16575
16576 /**
16577 * Checks if eventDefinition of the given element matches with semantic type.
16578 *
16579 * @return {boolean} true if element is of the given semantic type
16580 */
16581 function isTypedEvent(event, eventDefinitionType, filter) {
16582
16583 function matches(definition, filter) {
16584 return every(filter, function(val, key) {
16585
16586 // we want a == conversion here, to be able to catch
16587 // undefined == false and friends
16588 /* jshint -W116 */
16589 return definition[key] == val;
16590 });
16591 }
16592
16593 return some(event.eventDefinitions, function(definition) {
16594 return definition.$type === eventDefinitionType && matches(event, filter);
16595 });
16596 }
16597
16598 function isThrowEvent(event) {
16599 return (event.$type === 'bpmn:IntermediateThrowEvent') || (event.$type === 'bpmn:EndEvent');
16600 }
16601
16602 function isCollection(element) {
16603 var dataObject = element.dataObjectRef;
16604
16605 return element.isCollection || (dataObject && dataObject.isCollection);
16606 }
16607
16608 function getDi(element) {
16609 return element.businessObject.di;
16610 }
16611
16612 function getSemantic(element) {
16613 return element.businessObject;
16614 }
16615
16616
16617 // color access //////////////////////
16618
16619 function getFillColor(element, defaultColor) {
16620 var di = getDi(element);
16621
16622 return di.get('color:background-color') || di.get('bioc:fill') || defaultColor || 'white';
16623 }
16624
16625 function getStrokeColor$1(element, defaultColor) {
16626 var di = getDi(element);
16627
16628 return di.get('color:border-color') || di.get('bioc:stroke') || defaultColor || 'black';
16629 }
16630
16631 function getLabelColor(element, defaultColor, defaultStrokeColor) {
16632 var di = getDi(element),
16633 label = di.get('label');
16634
16635 return label && label.get('color:color') || defaultColor ||
16636 getStrokeColor$1(element, defaultStrokeColor);
16637 }
16638
16639 // cropping path customizations //////////////////////
16640
16641 function getCirclePath(shape) {
16642
16643 var cx = shape.x + shape.width / 2,
16644 cy = shape.y + shape.height / 2,
16645 radius = shape.width / 2;
16646
16647 var circlePath = [
16648 ['M', cx, cy],
16649 ['m', 0, -radius],
16650 ['a', radius, radius, 0, 1, 1, 0, 2 * radius],
16651 ['a', radius, radius, 0, 1, 1, 0, -2 * radius],
16652 ['z']
16653 ];
16654
16655 return componentsToPath(circlePath);
16656 }
16657
16658 function getRoundRectPath(shape, borderRadius) {
16659
16660 var x = shape.x,
16661 y = shape.y,
16662 width = shape.width,
16663 height = shape.height;
16664
16665 var roundRectPath = [
16666 ['M', x + borderRadius, y],
16667 ['l', width - borderRadius * 2, 0],
16668 ['a', borderRadius, borderRadius, 0, 0, 1, borderRadius, borderRadius],
16669 ['l', 0, height - borderRadius * 2],
16670 ['a', borderRadius, borderRadius, 0, 0, 1, -borderRadius, borderRadius],
16671 ['l', borderRadius * 2 - width, 0],
16672 ['a', borderRadius, borderRadius, 0, 0, 1, -borderRadius, -borderRadius],
16673 ['l', 0, borderRadius * 2 - height],
16674 ['a', borderRadius, borderRadius, 0, 0, 1, borderRadius, -borderRadius],
16675 ['z']
16676 ];
16677
16678 return componentsToPath(roundRectPath);
16679 }
16680
16681 function getDiamondPath(shape) {
16682
16683 var width = shape.width,
16684 height = shape.height,
16685 x = shape.x,
16686 y = shape.y,
16687 halfWidth = width / 2,
16688 halfHeight = height / 2;
16689
16690 var diamondPath = [
16691 ['M', x + halfWidth, y],
16692 ['l', halfWidth, halfHeight],
16693 ['l', -halfWidth, halfHeight],
16694 ['l', -halfWidth, -halfHeight],
16695 ['z']
16696 ];
16697
16698 return componentsToPath(diamondPath);
16699 }
16700
16701 function getRectPath(shape) {
16702 var x = shape.x,
16703 y = shape.y,
16704 width = shape.width,
16705 height = shape.height;
16706
16707 var rectPath = [
16708 ['M', x, y],
16709 ['l', width, 0],
16710 ['l', 0, height],
16711 ['l', -width, 0],
16712 ['z']
16713 ];
16714
16715 return componentsToPath(rectPath);
16716 }
16717
16718 var RENDERER_IDS = new Ids();
16719
16720 var TASK_BORDER_RADIUS = 10;
16721 var INNER_OUTER_DIST = 3;
16722
16723 var DEFAULT_FILL_OPACITY = .95,
16724 HIGH_FILL_OPACITY = .35;
16725
16726 var ELEMENT_LABEL_DISTANCE$1 = 10;
16727
16728 function BpmnRenderer(
16729 config, eventBus, styles, pathMap,
16730 canvas, textRenderer, priority) {
16731
16732 BaseRenderer.call(this, eventBus, priority);
16733
16734 var defaultFillColor = config && config.defaultFillColor,
16735 defaultStrokeColor = config && config.defaultStrokeColor,
16736 defaultLabelColor = config && config.defaultLabelColor;
16737
16738 var rendererId = RENDERER_IDS.next();
16739
16740 var markers = {};
16741
16742 var computeStyle = styles.computeStyle;
16743
16744 function addMarker(id, options) {
16745 var attrs = assign({
16746 fill: 'black',
16747 strokeWidth: 1,
16748 strokeLinecap: 'round',
16749 strokeDasharray: 'none'
16750 }, options.attrs);
16751
16752 var ref = options.ref || { x: 0, y: 0 };
16753
16754 var scale = options.scale || 1;
16755
16756 // fix for safari / chrome / firefox bug not correctly
16757 // resetting stroke dash array
16758 if (attrs.strokeDasharray === 'none') {
16759 attrs.strokeDasharray = [10000, 1];
16760 }
16761
16762 var marker = create$1('marker');
16763
16764 attr(options.element, attrs);
16765
16766 append(marker, options.element);
16767
16768 attr(marker, {
16769 id: id,
16770 viewBox: '0 0 20 20',
16771 refX: ref.x,
16772 refY: ref.y,
16773 markerWidth: 20 * scale,
16774 markerHeight: 20 * scale,
16775 orient: 'auto'
16776 });
16777
16778 var defs = query('defs', canvas._svg);
16779
16780 if (!defs) {
16781 defs = create$1('defs');
16782
16783 append(canvas._svg, defs);
16784 }
16785
16786 append(defs, marker);
16787
16788 markers[id] = marker;
16789 }
16790
16791 function colorEscape(str) {
16792
16793 // only allow characters and numbers
16794 return str.replace(/[^0-9a-zA-z]+/g, '_');
16795 }
16796
16797 function marker(type, fill, stroke) {
16798 var id = type + '-' + colorEscape(fill) + '-' + colorEscape(stroke) + '-' + rendererId;
16799
16800 if (!markers[id]) {
16801 createMarker(id, type, fill, stroke);
16802 }
16803
16804 return 'url(#' + id + ')';
16805 }
16806
16807 function createMarker(id, type, fill, stroke) {
16808
16809 if (type === 'sequenceflow-end') {
16810 var sequenceflowEnd = create$1('path');
16811 attr(sequenceflowEnd, { d: 'M 1 5 L 11 10 L 1 15 Z' });
16812
16813 addMarker(id, {
16814 element: sequenceflowEnd,
16815 ref: { x: 11, y: 10 },
16816 scale: 0.5,
16817 attrs: {
16818 fill: stroke,
16819 stroke: stroke
16820 }
16821 });
16822 }
16823
16824 if (type === 'messageflow-start') {
16825 var messageflowStart = create$1('circle');
16826 attr(messageflowStart, { cx: 6, cy: 6, r: 3.5 });
16827
16828 addMarker(id, {
16829 element: messageflowStart,
16830 attrs: {
16831 fill: fill,
16832 stroke: stroke
16833 },
16834 ref: { x: 6, y: 6 }
16835 });
16836 }
16837
16838 if (type === 'messageflow-end') {
16839 var messageflowEnd = create$1('path');
16840 attr(messageflowEnd, { d: 'm 1 5 l 0 -3 l 7 3 l -7 3 z' });
16841
16842 addMarker(id, {
16843 element: messageflowEnd,
16844 attrs: {
16845 fill: fill,
16846 stroke: stroke,
16847 strokeLinecap: 'butt'
16848 },
16849 ref: { x: 8.5, y: 5 }
16850 });
16851 }
16852
16853 if (type === 'association-start') {
16854 var associationStart = create$1('path');
16855 attr(associationStart, { d: 'M 11 5 L 1 10 L 11 15' });
16856
16857 addMarker(id, {
16858 element: associationStart,
16859 attrs: {
16860 fill: 'none',
16861 stroke: stroke,
16862 strokeWidth: 1.5
16863 },
16864 ref: { x: 1, y: 10 },
16865 scale: 0.5
16866 });
16867 }
16868
16869 if (type === 'association-end') {
16870 var associationEnd = create$1('path');
16871 attr(associationEnd, { d: 'M 1 5 L 11 10 L 1 15' });
16872
16873 addMarker(id, {
16874 element: associationEnd,
16875 attrs: {
16876 fill: 'none',
16877 stroke: stroke,
16878 strokeWidth: 1.5
16879 },
16880 ref: { x: 12, y: 10 },
16881 scale: 0.5
16882 });
16883 }
16884
16885 if (type === 'conditional-flow-marker') {
16886 var conditionalflowMarker = create$1('path');
16887 attr(conditionalflowMarker, { d: 'M 0 10 L 8 6 L 16 10 L 8 14 Z' });
16888
16889 addMarker(id, {
16890 element: conditionalflowMarker,
16891 attrs: {
16892 fill: fill,
16893 stroke: stroke
16894 },
16895 ref: { x: -1, y: 10 },
16896 scale: 0.5
16897 });
16898 }
16899
16900 if (type === 'conditional-default-flow-marker') {
16901 var conditionaldefaultflowMarker = create$1('path');
16902 attr(conditionaldefaultflowMarker, { d: 'M 6 4 L 10 16' });
16903
16904 addMarker(id, {
16905 element: conditionaldefaultflowMarker,
16906 attrs: {
16907 stroke: stroke
16908 },
16909 ref: { x: 0, y: 10 },
16910 scale: 0.5
16911 });
16912 }
16913 }
16914
16915 function drawCircle(parentGfx, width, height, offset, attrs) {
16916
16917 if (isObject(offset)) {
16918 attrs = offset;
16919 offset = 0;
16920 }
16921
16922 offset = offset || 0;
16923
16924 attrs = computeStyle(attrs, {
16925 stroke: 'black',
16926 strokeWidth: 2,
16927 fill: 'white'
16928 });
16929
16930 if (attrs.fill === 'none') {
16931 delete attrs.fillOpacity;
16932 }
16933
16934 var cx = width / 2,
16935 cy = height / 2;
16936
16937 var circle = create$1('circle');
16938 attr(circle, {
16939 cx: cx,
16940 cy: cy,
16941 r: Math.round((width + height) / 4 - offset)
16942 });
16943 attr(circle, attrs);
16944
16945 append(parentGfx, circle);
16946
16947 return circle;
16948 }
16949
16950 function drawRect(parentGfx, width, height, r, offset, attrs) {
16951
16952 if (isObject(offset)) {
16953 attrs = offset;
16954 offset = 0;
16955 }
16956
16957 offset = offset || 0;
16958
16959 attrs = computeStyle(attrs, {
16960 stroke: 'black',
16961 strokeWidth: 2,
16962 fill: 'white'
16963 });
16964
16965 var rect = create$1('rect');
16966 attr(rect, {
16967 x: offset,
16968 y: offset,
16969 width: width - offset * 2,
16970 height: height - offset * 2,
16971 rx: r,
16972 ry: r
16973 });
16974 attr(rect, attrs);
16975
16976 append(parentGfx, rect);
16977
16978 return rect;
16979 }
16980
16981 function drawDiamond(parentGfx, width, height, attrs) {
16982
16983 var x_2 = width / 2;
16984 var y_2 = height / 2;
16985
16986 var points = [{ x: x_2, y: 0 }, { x: width, y: y_2 }, { x: x_2, y: height }, { x: 0, y: y_2 }];
16987
16988 var pointsString = points.map(function(point) {
16989 return point.x + ',' + point.y;
16990 }).join(' ');
16991
16992 attrs = computeStyle(attrs, {
16993 stroke: 'black',
16994 strokeWidth: 2,
16995 fill: 'white'
16996 });
16997
16998 var polygon = create$1('polygon');
16999 attr(polygon, {
17000 points: pointsString
17001 });
17002 attr(polygon, attrs);
17003
17004 append(parentGfx, polygon);
17005
17006 return polygon;
17007 }
17008
17009 function drawLine(parentGfx, waypoints, attrs) {
17010 attrs = computeStyle(attrs, [ 'no-fill' ], {
17011 stroke: 'black',
17012 strokeWidth: 2,
17013 fill: 'none'
17014 });
17015
17016 var line = createLine(waypoints, attrs);
17017
17018 append(parentGfx, line);
17019
17020 return line;
17021 }
17022
17023 function drawPath(parentGfx, d, attrs) {
17024
17025 attrs = computeStyle(attrs, [ 'no-fill' ], {
17026 strokeWidth: 2,
17027 stroke: 'black'
17028 });
17029
17030 var path = create$1('path');
17031 attr(path, { d: d });
17032 attr(path, attrs);
17033
17034 append(parentGfx, path);
17035
17036 return path;
17037 }
17038
17039 function drawMarker(type, parentGfx, path, attrs) {
17040 return drawPath(parentGfx, path, assign({ 'data-marker': type }, attrs));
17041 }
17042
17043 function as(type) {
17044 return function(parentGfx, element) {
17045 return handlers[type](parentGfx, element);
17046 };
17047 }
17048
17049 function renderer(type) {
17050 return handlers[type];
17051 }
17052
17053 function renderEventContent(element, parentGfx) {
17054
17055 var event = getSemantic(element);
17056 var isThrowing = isThrowEvent(event);
17057
17058 if (event.eventDefinitions && event.eventDefinitions.length>1) {
17059 if (event.parallelMultiple) {
17060 return renderer('bpmn:ParallelMultipleEventDefinition')(parentGfx, element, isThrowing);
17061 }
17062 else {
17063 return renderer('bpmn:MultipleEventDefinition')(parentGfx, element, isThrowing);
17064 }
17065 }
17066
17067 if (isTypedEvent(event, 'bpmn:MessageEventDefinition')) {
17068 return renderer('bpmn:MessageEventDefinition')(parentGfx, element, isThrowing);
17069 }
17070
17071 if (isTypedEvent(event, 'bpmn:TimerEventDefinition')) {
17072 return renderer('bpmn:TimerEventDefinition')(parentGfx, element, isThrowing);
17073 }
17074
17075 if (isTypedEvent(event, 'bpmn:ConditionalEventDefinition')) {
17076 return renderer('bpmn:ConditionalEventDefinition')(parentGfx, element);
17077 }
17078
17079 if (isTypedEvent(event, 'bpmn:SignalEventDefinition')) {
17080 return renderer('bpmn:SignalEventDefinition')(parentGfx, element, isThrowing);
17081 }
17082
17083 if (isTypedEvent(event, 'bpmn:EscalationEventDefinition')) {
17084 return renderer('bpmn:EscalationEventDefinition')(parentGfx, element, isThrowing);
17085 }
17086
17087 if (isTypedEvent(event, 'bpmn:LinkEventDefinition')) {
17088 return renderer('bpmn:LinkEventDefinition')(parentGfx, element, isThrowing);
17089 }
17090
17091 if (isTypedEvent(event, 'bpmn:ErrorEventDefinition')) {
17092 return renderer('bpmn:ErrorEventDefinition')(parentGfx, element, isThrowing);
17093 }
17094
17095 if (isTypedEvent(event, 'bpmn:CancelEventDefinition')) {
17096 return renderer('bpmn:CancelEventDefinition')(parentGfx, element, isThrowing);
17097 }
17098
17099 if (isTypedEvent(event, 'bpmn:CompensateEventDefinition')) {
17100 return renderer('bpmn:CompensateEventDefinition')(parentGfx, element, isThrowing);
17101 }
17102
17103 if (isTypedEvent(event, 'bpmn:TerminateEventDefinition')) {
17104 return renderer('bpmn:TerminateEventDefinition')(parentGfx, element, isThrowing);
17105 }
17106
17107 return null;
17108 }
17109
17110 function renderLabel(parentGfx, label, options) {
17111
17112 options = assign({
17113 size: {
17114 width: 100
17115 }
17116 }, options);
17117
17118 var text = textRenderer.createText(label || '', options);
17119
17120 classes(text).add('djs-label');
17121
17122 append(parentGfx, text);
17123
17124 return text;
17125 }
17126
17127 function renderEmbeddedLabel(parentGfx, element, align) {
17128 var semantic = getSemantic(element);
17129
17130 return renderLabel(parentGfx, semantic.name, {
17131 box: element,
17132 align: align,
17133 padding: 5,
17134 style: {
17135 fill: getLabelColor(element, defaultLabelColor, defaultStrokeColor)
17136 }
17137 });
17138 }
17139
17140 function renderExternalLabel(parentGfx, element) {
17141
17142 var box = {
17143 width: 90,
17144 height: 30,
17145 x: element.width / 2 + element.x,
17146 y: element.height / 2 + element.y
17147 };
17148
17149 return renderLabel(parentGfx, getLabel(element), {
17150 box: box,
17151 fitBox: true,
17152 style: assign(
17153 {},
17154 textRenderer.getExternalStyle(),
17155 {
17156 fill: getLabelColor(element, defaultLabelColor, defaultStrokeColor)
17157 }
17158 )
17159 });
17160 }
17161
17162 function renderLaneLabel(parentGfx, text, element) {
17163 var textBox = renderLabel(parentGfx, text, {
17164 box: {
17165 height: 30,
17166 width: element.height
17167 },
17168 align: 'center-middle',
17169 style: {
17170 fill: getLabelColor(element, defaultLabelColor, defaultStrokeColor)
17171 }
17172 });
17173
17174 var top = -1 * element.height;
17175
17176 transform(textBox, 0, -top, 270);
17177 }
17178
17179 function createPathFromConnection(connection) {
17180 var waypoints = connection.waypoints;
17181
17182 var pathData = 'm ' + waypoints[0].x + ',' + waypoints[0].y;
17183 for (var i = 1; i < waypoints.length; i++) {
17184 pathData += 'L' + waypoints[i].x + ',' + waypoints[i].y + ' ';
17185 }
17186 return pathData;
17187 }
17188
17189 var handlers = this.handlers = {
17190 'bpmn:Event': function(parentGfx, element, attrs) {
17191
17192 if (!('fillOpacity' in attrs)) {
17193 attrs.fillOpacity = DEFAULT_FILL_OPACITY;
17194 }
17195
17196 return drawCircle(parentGfx, element.width, element.height, attrs);
17197 },
17198 'bpmn:StartEvent': function(parentGfx, element) {
17199 var attrs = {
17200 fill: getFillColor(element, defaultFillColor),
17201 stroke: getStrokeColor$1(element, defaultStrokeColor)
17202 };
17203
17204 var semantic = getSemantic(element);
17205
17206 if (!semantic.isInterrupting) {
17207 attrs = {
17208 strokeDasharray: '6',
17209 strokeLinecap: 'round',
17210 fill: getFillColor(element, defaultFillColor),
17211 stroke: getStrokeColor$1(element, defaultStrokeColor)
17212 };
17213 }
17214
17215 var circle = renderer('bpmn:Event')(parentGfx, element, attrs);
17216
17217 renderEventContent(element, parentGfx);
17218
17219 return circle;
17220 },
17221 'bpmn:MessageEventDefinition': function(parentGfx, element, isThrowing) {
17222 var pathData = pathMap.getScaledPath('EVENT_MESSAGE', {
17223 xScaleFactor: 0.9,
17224 yScaleFactor: 0.9,
17225 containerWidth: element.width,
17226 containerHeight: element.height,
17227 position: {
17228 mx: 0.235,
17229 my: 0.315
17230 }
17231 });
17232
17233 var fill = isThrowing ? getStrokeColor$1(element, defaultStrokeColor) : getFillColor(element, defaultFillColor);
17234 var stroke = isThrowing ? getFillColor(element, defaultFillColor) : getStrokeColor$1(element, defaultStrokeColor);
17235
17236 var messagePath = drawPath(parentGfx, pathData, {
17237 strokeWidth: 1,
17238 fill: fill,
17239 stroke: stroke
17240 });
17241
17242 return messagePath;
17243 },
17244 'bpmn:TimerEventDefinition': function(parentGfx, element) {
17245 var circle = drawCircle(parentGfx, element.width, element.height, 0.2 * element.height, {
17246 strokeWidth: 2,
17247 fill: getFillColor(element, defaultFillColor),
17248 stroke: getStrokeColor$1(element, defaultStrokeColor)
17249 });
17250
17251 var pathData = pathMap.getScaledPath('EVENT_TIMER_WH', {
17252 xScaleFactor: 0.75,
17253 yScaleFactor: 0.75,
17254 containerWidth: element.width,
17255 containerHeight: element.height,
17256 position: {
17257 mx: 0.5,
17258 my: 0.5
17259 }
17260 });
17261
17262 drawPath(parentGfx, pathData, {
17263 strokeWidth: 2,
17264 strokeLinecap: 'square',
17265 stroke: getStrokeColor$1(element, defaultStrokeColor)
17266 });
17267
17268 for (var i = 0;i < 12; i++) {
17269
17270 var linePathData = pathMap.getScaledPath('EVENT_TIMER_LINE', {
17271 xScaleFactor: 0.75,
17272 yScaleFactor: 0.75,
17273 containerWidth: element.width,
17274 containerHeight: element.height,
17275 position: {
17276 mx: 0.5,
17277 my: 0.5
17278 }
17279 });
17280
17281 var width = element.width / 2;
17282 var height = element.height / 2;
17283
17284 drawPath(parentGfx, linePathData, {
17285 strokeWidth: 1,
17286 strokeLinecap: 'square',
17287 transform: 'rotate(' + (i * 30) + ',' + height + ',' + width + ')',
17288 stroke: getStrokeColor$1(element, defaultStrokeColor)
17289 });
17290 }
17291
17292 return circle;
17293 },
17294 'bpmn:EscalationEventDefinition': function(parentGfx, event, isThrowing) {
17295 var pathData = pathMap.getScaledPath('EVENT_ESCALATION', {
17296 xScaleFactor: 1,
17297 yScaleFactor: 1,
17298 containerWidth: event.width,
17299 containerHeight: event.height,
17300 position: {
17301 mx: 0.5,
17302 my: 0.2
17303 }
17304 });
17305
17306 var fill = isThrowing ? getStrokeColor$1(event, defaultStrokeColor) : 'none';
17307
17308 return drawPath(parentGfx, pathData, {
17309 strokeWidth: 1,
17310 fill: fill,
17311 stroke: getStrokeColor$1(event, defaultStrokeColor)
17312 });
17313 },
17314 'bpmn:ConditionalEventDefinition': function(parentGfx, event) {
17315 var pathData = pathMap.getScaledPath('EVENT_CONDITIONAL', {
17316 xScaleFactor: 1,
17317 yScaleFactor: 1,
17318 containerWidth: event.width,
17319 containerHeight: event.height,
17320 position: {
17321 mx: 0.5,
17322 my: 0.222
17323 }
17324 });
17325
17326 return drawPath(parentGfx, pathData, {
17327 strokeWidth: 1,
17328 stroke: getStrokeColor$1(event, defaultStrokeColor)
17329 });
17330 },
17331 'bpmn:LinkEventDefinition': function(parentGfx, event, isThrowing) {
17332 var pathData = pathMap.getScaledPath('EVENT_LINK', {
17333 xScaleFactor: 1,
17334 yScaleFactor: 1,
17335 containerWidth: event.width,
17336 containerHeight: event.height,
17337 position: {
17338 mx: 0.57,
17339 my: 0.263
17340 }
17341 });
17342
17343 var fill = isThrowing ? getStrokeColor$1(event, defaultStrokeColor) : 'none';
17344
17345 return drawPath(parentGfx, pathData, {
17346 strokeWidth: 1,
17347 fill: fill,
17348 stroke: getStrokeColor$1(event, defaultStrokeColor)
17349 });
17350 },
17351 'bpmn:ErrorEventDefinition': function(parentGfx, event, isThrowing) {
17352 var pathData = pathMap.getScaledPath('EVENT_ERROR', {
17353 xScaleFactor: 1.1,
17354 yScaleFactor: 1.1,
17355 containerWidth: event.width,
17356 containerHeight: event.height,
17357 position: {
17358 mx: 0.2,
17359 my: 0.722
17360 }
17361 });
17362
17363 var fill = isThrowing ? getStrokeColor$1(event, defaultStrokeColor) : 'none';
17364
17365 return drawPath(parentGfx, pathData, {
17366 strokeWidth: 1,
17367 fill: fill,
17368 stroke: getStrokeColor$1(event, defaultStrokeColor)
17369 });
17370 },
17371 'bpmn:CancelEventDefinition': function(parentGfx, event, isThrowing) {
17372 var pathData = pathMap.getScaledPath('EVENT_CANCEL_45', {
17373 xScaleFactor: 1.0,
17374 yScaleFactor: 1.0,
17375 containerWidth: event.width,
17376 containerHeight: event.height,
17377 position: {
17378 mx: 0.638,
17379 my: -0.055
17380 }
17381 });
17382
17383 var fill = isThrowing ? getStrokeColor$1(event, defaultStrokeColor) : 'none';
17384
17385 var path = drawPath(parentGfx, pathData, {
17386 strokeWidth: 1,
17387 fill: fill,
17388 stroke: getStrokeColor$1(event, defaultStrokeColor)
17389 });
17390
17391 rotate(path, 45);
17392
17393 return path;
17394 },
17395 'bpmn:CompensateEventDefinition': function(parentGfx, event, isThrowing) {
17396 var pathData = pathMap.getScaledPath('EVENT_COMPENSATION', {
17397 xScaleFactor: 1,
17398 yScaleFactor: 1,
17399 containerWidth: event.width,
17400 containerHeight: event.height,
17401 position: {
17402 mx: 0.22,
17403 my: 0.5
17404 }
17405 });
17406
17407 var fill = isThrowing ? getStrokeColor$1(event, defaultStrokeColor) : 'none';
17408
17409 return drawPath(parentGfx, pathData, {
17410 strokeWidth: 1,
17411 fill: fill,
17412 stroke: getStrokeColor$1(event, defaultStrokeColor)
17413 });
17414 },
17415 'bpmn:SignalEventDefinition': function(parentGfx, event, isThrowing) {
17416 var pathData = pathMap.getScaledPath('EVENT_SIGNAL', {
17417 xScaleFactor: 0.9,
17418 yScaleFactor: 0.9,
17419 containerWidth: event.width,
17420 containerHeight: event.height,
17421 position: {
17422 mx: 0.5,
17423 my: 0.2
17424 }
17425 });
17426
17427 var fill = isThrowing ? getStrokeColor$1(event, defaultStrokeColor) : 'none';
17428
17429 return drawPath(parentGfx, pathData, {
17430 strokeWidth: 1,
17431 fill: fill,
17432 stroke: getStrokeColor$1(event, defaultStrokeColor)
17433 });
17434 },
17435 'bpmn:MultipleEventDefinition': function(parentGfx, event, isThrowing) {
17436 var pathData = pathMap.getScaledPath('EVENT_MULTIPLE', {
17437 xScaleFactor: 1.1,
17438 yScaleFactor: 1.1,
17439 containerWidth: event.width,
17440 containerHeight: event.height,
17441 position: {
17442 mx: 0.222,
17443 my: 0.36
17444 }
17445 });
17446
17447 var fill = isThrowing ? getStrokeColor$1(event, defaultStrokeColor) : 'none';
17448
17449 return drawPath(parentGfx, pathData, {
17450 strokeWidth: 1,
17451 fill: fill
17452 });
17453 },
17454 'bpmn:ParallelMultipleEventDefinition': function(parentGfx, event) {
17455 var pathData = pathMap.getScaledPath('EVENT_PARALLEL_MULTIPLE', {
17456 xScaleFactor: 1.2,
17457 yScaleFactor: 1.2,
17458 containerWidth: event.width,
17459 containerHeight: event.height,
17460 position: {
17461 mx: 0.458,
17462 my: 0.194
17463 }
17464 });
17465
17466 return drawPath(parentGfx, pathData, {
17467 strokeWidth: 1,
17468 fill: getStrokeColor$1(event, defaultStrokeColor),
17469 stroke: getStrokeColor$1(event, defaultStrokeColor)
17470 });
17471 },
17472 'bpmn:EndEvent': function(parentGfx, element) {
17473 var circle = renderer('bpmn:Event')(parentGfx, element, {
17474 strokeWidth: 4,
17475 fill: getFillColor(element, defaultFillColor),
17476 stroke: getStrokeColor$1(element, defaultStrokeColor)
17477 });
17478
17479 renderEventContent(element, parentGfx);
17480
17481 return circle;
17482 },
17483 'bpmn:TerminateEventDefinition': function(parentGfx, element) {
17484 var circle = drawCircle(parentGfx, element.width, element.height, 8, {
17485 strokeWidth: 4,
17486 fill: getStrokeColor$1(element, defaultStrokeColor),
17487 stroke: getStrokeColor$1(element, defaultStrokeColor)
17488 });
17489
17490 return circle;
17491 },
17492 'bpmn:IntermediateEvent': function(parentGfx, element) {
17493 var outer = renderer('bpmn:Event')(parentGfx, element, {
17494 strokeWidth: 1,
17495 fill: getFillColor(element, defaultFillColor),
17496 stroke: getStrokeColor$1(element, defaultStrokeColor)
17497 });
17498
17499 /* inner */
17500 drawCircle(parentGfx, element.width, element.height, INNER_OUTER_DIST, {
17501 strokeWidth: 1,
17502 fill: getFillColor(element, 'none'),
17503 stroke: getStrokeColor$1(element, defaultStrokeColor)
17504 });
17505
17506 renderEventContent(element, parentGfx);
17507
17508 return outer;
17509 },
17510 'bpmn:IntermediateCatchEvent': as('bpmn:IntermediateEvent'),
17511 'bpmn:IntermediateThrowEvent': as('bpmn:IntermediateEvent'),
17512
17513 'bpmn:Activity': function(parentGfx, element, attrs) {
17514
17515 attrs = attrs || {};
17516
17517 if (!('fillOpacity' in attrs)) {
17518 attrs.fillOpacity = DEFAULT_FILL_OPACITY;
17519 }
17520
17521 return drawRect(parentGfx, element.width, element.height, TASK_BORDER_RADIUS, attrs);
17522 },
17523
17524 'bpmn:Task': function(parentGfx, element) {
17525 var attrs = {
17526 fill: getFillColor(element, defaultFillColor),
17527 stroke: getStrokeColor$1(element, defaultStrokeColor)
17528 };
17529
17530 var rect = renderer('bpmn:Activity')(parentGfx, element, attrs);
17531
17532 renderEmbeddedLabel(parentGfx, element, 'center-middle');
17533 attachTaskMarkers(parentGfx, element);
17534
17535 return rect;
17536 },
17537 'bpmn:ServiceTask': function(parentGfx, element) {
17538 var task = renderer('bpmn:Task')(parentGfx, element);
17539
17540 var pathDataBG = pathMap.getScaledPath('TASK_TYPE_SERVICE', {
17541 abspos: {
17542 x: 12,
17543 y: 18
17544 }
17545 });
17546
17547 /* service bg */ drawPath(parentGfx, pathDataBG, {
17548 strokeWidth: 1,
17549 fill: getFillColor(element, defaultFillColor),
17550 stroke: getStrokeColor$1(element, defaultStrokeColor)
17551 });
17552
17553 var fillPathData = pathMap.getScaledPath('TASK_TYPE_SERVICE_FILL', {
17554 abspos: {
17555 x: 17.2,
17556 y: 18
17557 }
17558 });
17559
17560 /* service fill */ drawPath(parentGfx, fillPathData, {
17561 strokeWidth: 0,
17562 fill: getFillColor(element, defaultFillColor)
17563 });
17564
17565 var pathData = pathMap.getScaledPath('TASK_TYPE_SERVICE', {
17566 abspos: {
17567 x: 17,
17568 y: 22
17569 }
17570 });
17571
17572 /* service */ drawPath(parentGfx, pathData, {
17573 strokeWidth: 1,
17574 fill: getFillColor(element, defaultFillColor),
17575 stroke: getStrokeColor$1(element, defaultStrokeColor)
17576 });
17577
17578 return task;
17579 },
17580 'bpmn:UserTask': function(parentGfx, element) {
17581 var task = renderer('bpmn:Task')(parentGfx, element);
17582
17583 var x = 15;
17584 var y = 12;
17585
17586 var pathData = pathMap.getScaledPath('TASK_TYPE_USER_1', {
17587 abspos: {
17588 x: x,
17589 y: y
17590 }
17591 });
17592
17593 /* user path */ drawPath(parentGfx, pathData, {
17594 strokeWidth: 0.5,
17595 fill: getFillColor(element, defaultFillColor),
17596 stroke: getStrokeColor$1(element, defaultStrokeColor)
17597 });
17598
17599 var pathData2 = pathMap.getScaledPath('TASK_TYPE_USER_2', {
17600 abspos: {
17601 x: x,
17602 y: y
17603 }
17604 });
17605
17606 /* user2 path */ drawPath(parentGfx, pathData2, {
17607 strokeWidth: 0.5,
17608 fill: getFillColor(element, defaultFillColor),
17609 stroke: getStrokeColor$1(element, defaultStrokeColor)
17610 });
17611
17612 var pathData3 = pathMap.getScaledPath('TASK_TYPE_USER_3', {
17613 abspos: {
17614 x: x,
17615 y: y
17616 }
17617 });
17618
17619 /* user3 path */ drawPath(parentGfx, pathData3, {
17620 strokeWidth: 0.5,
17621 fill: getStrokeColor$1(element, defaultStrokeColor),
17622 stroke: getStrokeColor$1(element, defaultStrokeColor)
17623 });
17624
17625 return task;
17626 },
17627 'bpmn:ManualTask': function(parentGfx, element) {
17628 var task = renderer('bpmn:Task')(parentGfx, element);
17629
17630 var pathData = pathMap.getScaledPath('TASK_TYPE_MANUAL', {
17631 abspos: {
17632 x: 17,
17633 y: 15
17634 }
17635 });
17636
17637 /* manual path */ drawPath(parentGfx, pathData, {
17638 strokeWidth: 0.5, // 0.25,
17639 fill: getFillColor(element, defaultFillColor),
17640 stroke: getStrokeColor$1(element, defaultStrokeColor)
17641 });
17642
17643 return task;
17644 },
17645 'bpmn:SendTask': function(parentGfx, element) {
17646 var task = renderer('bpmn:Task')(parentGfx, element);
17647
17648 var pathData = pathMap.getScaledPath('TASK_TYPE_SEND', {
17649 xScaleFactor: 1,
17650 yScaleFactor: 1,
17651 containerWidth: 21,
17652 containerHeight: 14,
17653 position: {
17654 mx: 0.285,
17655 my: 0.357
17656 }
17657 });
17658
17659 /* send path */ drawPath(parentGfx, pathData, {
17660 strokeWidth: 1,
17661 fill: getStrokeColor$1(element, defaultStrokeColor),
17662 stroke: getFillColor(element, defaultFillColor)
17663 });
17664
17665 return task;
17666 },
17667 'bpmn:ReceiveTask' : function(parentGfx, element) {
17668 var semantic = getSemantic(element);
17669
17670 var task = renderer('bpmn:Task')(parentGfx, element);
17671 var pathData;
17672
17673 if (semantic.instantiate) {
17674 drawCircle(parentGfx, 28, 28, 20 * 0.22, { strokeWidth: 1 });
17675
17676 pathData = pathMap.getScaledPath('TASK_TYPE_INSTANTIATING_SEND', {
17677 abspos: {
17678 x: 7.77,
17679 y: 9.52
17680 }
17681 });
17682 } else {
17683
17684 pathData = pathMap.getScaledPath('TASK_TYPE_SEND', {
17685 xScaleFactor: 0.9,
17686 yScaleFactor: 0.9,
17687 containerWidth: 21,
17688 containerHeight: 14,
17689 position: {
17690 mx: 0.3,
17691 my: 0.4
17692 }
17693 });
17694 }
17695
17696 /* receive path */ drawPath(parentGfx, pathData, {
17697 strokeWidth: 1,
17698 fill: getFillColor(element, defaultFillColor),
17699 stroke: getStrokeColor$1(element, defaultStrokeColor)
17700 });
17701
17702 return task;
17703 },
17704 'bpmn:ScriptTask': function(parentGfx, element) {
17705 var task = renderer('bpmn:Task')(parentGfx, element);
17706
17707 var pathData = pathMap.getScaledPath('TASK_TYPE_SCRIPT', {
17708 abspos: {
17709 x: 15,
17710 y: 20
17711 }
17712 });
17713
17714 /* script path */ drawPath(parentGfx, pathData, {
17715 strokeWidth: 1,
17716 stroke: getStrokeColor$1(element, defaultStrokeColor)
17717 });
17718
17719 return task;
17720 },
17721 'bpmn:BusinessRuleTask': function(parentGfx, element) {
17722 var task = renderer('bpmn:Task')(parentGfx, element);
17723
17724 var headerPathData = pathMap.getScaledPath('TASK_TYPE_BUSINESS_RULE_HEADER', {
17725 abspos: {
17726 x: 8,
17727 y: 8
17728 }
17729 });
17730
17731 var businessHeaderPath = drawPath(parentGfx, headerPathData);
17732 attr(businessHeaderPath, {
17733 strokeWidth: 1,
17734 fill: getFillColor(element, '#aaaaaa'),
17735 stroke: getStrokeColor$1(element, defaultStrokeColor)
17736 });
17737
17738 var headerData = pathMap.getScaledPath('TASK_TYPE_BUSINESS_RULE_MAIN', {
17739 abspos: {
17740 x: 8,
17741 y: 8
17742 }
17743 });
17744
17745 var businessPath = drawPath(parentGfx, headerData);
17746 attr(businessPath, {
17747 strokeWidth: 1,
17748 stroke: getStrokeColor$1(element, defaultStrokeColor)
17749 });
17750
17751 return task;
17752 },
17753 'bpmn:SubProcess': function(parentGfx, element, attrs) {
17754 attrs = assign({
17755 fill: getFillColor(element, defaultFillColor),
17756 stroke: getStrokeColor$1(element, defaultStrokeColor)
17757 }, attrs);
17758
17759 var rect = renderer('bpmn:Activity')(parentGfx, element, attrs);
17760
17761 var expanded = isExpanded(element);
17762
17763 if (isEventSubProcess(element)) {
17764 attr(rect, {
17765 strokeDasharray: '1,2'
17766 });
17767 }
17768
17769 renderEmbeddedLabel(parentGfx, element, expanded ? 'center-top' : 'center-middle');
17770
17771 if (expanded) {
17772 attachTaskMarkers(parentGfx, element);
17773 } else {
17774 attachTaskMarkers(parentGfx, element, ['SubProcessMarker']);
17775 }
17776
17777 return rect;
17778 },
17779 'bpmn:AdHocSubProcess': function(parentGfx, element) {
17780 return renderer('bpmn:SubProcess')(parentGfx, element);
17781 },
17782 'bpmn:Transaction': function(parentGfx, element) {
17783 var outer = renderer('bpmn:SubProcess')(parentGfx, element);
17784
17785 var innerAttrs = styles.style([ 'no-fill', 'no-events' ], {
17786 stroke: getStrokeColor$1(element, defaultStrokeColor)
17787 });
17788
17789 /* inner path */ drawRect(parentGfx, element.width, element.height, TASK_BORDER_RADIUS - 2, INNER_OUTER_DIST, innerAttrs);
17790
17791 return outer;
17792 },
17793 'bpmn:CallActivity': function(parentGfx, element) {
17794 return renderer('bpmn:SubProcess')(parentGfx, element, {
17795 strokeWidth: 5
17796 });
17797 },
17798 'bpmn:Participant': function(parentGfx, element) {
17799
17800 var attrs = {
17801 fillOpacity: DEFAULT_FILL_OPACITY,
17802 fill: getFillColor(element, defaultFillColor),
17803 stroke: getStrokeColor$1(element, defaultStrokeColor)
17804 };
17805
17806 var lane = renderer('bpmn:Lane')(parentGfx, element, attrs);
17807
17808 var expandedPool = isExpanded(element);
17809
17810 if (expandedPool) {
17811 drawLine(parentGfx, [
17812 { x: 30, y: 0 },
17813 { x: 30, y: element.height }
17814 ], {
17815 stroke: getStrokeColor$1(element, defaultStrokeColor)
17816 });
17817 var text = getSemantic(element).name;
17818 renderLaneLabel(parentGfx, text, element);
17819 } else {
17820
17821 // Collapsed pool draw text inline
17822 var text2 = getSemantic(element).name;
17823 renderLabel(parentGfx, text2, {
17824 box: element, align: 'center-middle',
17825 style: {
17826 fill: getLabelColor(element, defaultLabelColor, defaultStrokeColor)
17827 }
17828 });
17829 }
17830
17831 var participantMultiplicity = !!(getSemantic(element).participantMultiplicity);
17832
17833 if (participantMultiplicity) {
17834 renderer('ParticipantMultiplicityMarker')(parentGfx, element);
17835 }
17836
17837 return lane;
17838 },
17839 'bpmn:Lane': function(parentGfx, element, attrs) {
17840 var rect = drawRect(parentGfx, element.width, element.height, 0, assign({
17841 fill: getFillColor(element, defaultFillColor),
17842 fillOpacity: HIGH_FILL_OPACITY,
17843 stroke: getStrokeColor$1(element, defaultStrokeColor)
17844 }, attrs));
17845
17846 var semantic = getSemantic(element);
17847
17848 if (semantic.$type === 'bpmn:Lane') {
17849 var text = semantic.name;
17850 renderLaneLabel(parentGfx, text, element);
17851 }
17852
17853 return rect;
17854 },
17855 'bpmn:InclusiveGateway': function(parentGfx, element) {
17856 var diamond = renderer('bpmn:Gateway')(parentGfx, element);
17857
17858 /* circle path */
17859 drawCircle(parentGfx, element.width, element.height, element.height * 0.24, {
17860 strokeWidth: 2.5,
17861 fill: getFillColor(element, defaultFillColor),
17862 stroke: getStrokeColor$1(element, defaultStrokeColor)
17863 });
17864
17865 return diamond;
17866 },
17867 'bpmn:ExclusiveGateway': function(parentGfx, element) {
17868 var diamond = renderer('bpmn:Gateway')(parentGfx, element);
17869
17870 var pathData = pathMap.getScaledPath('GATEWAY_EXCLUSIVE', {
17871 xScaleFactor: 0.4,
17872 yScaleFactor: 0.4,
17873 containerWidth: element.width,
17874 containerHeight: element.height,
17875 position: {
17876 mx: 0.32,
17877 my: 0.3
17878 }
17879 });
17880
17881 if ((getDi(element).isMarkerVisible)) {
17882 drawPath(parentGfx, pathData, {
17883 strokeWidth: 1,
17884 fill: getStrokeColor$1(element, defaultStrokeColor),
17885 stroke: getStrokeColor$1(element, defaultStrokeColor)
17886 });
17887 }
17888
17889 return diamond;
17890 },
17891 'bpmn:ComplexGateway': function(parentGfx, element) {
17892 var diamond = renderer('bpmn:Gateway')(parentGfx, element);
17893
17894 var pathData = pathMap.getScaledPath('GATEWAY_COMPLEX', {
17895 xScaleFactor: 0.5,
17896 yScaleFactor:0.5,
17897 containerWidth: element.width,
17898 containerHeight: element.height,
17899 position: {
17900 mx: 0.46,
17901 my: 0.26
17902 }
17903 });
17904
17905 /* complex path */ drawPath(parentGfx, pathData, {
17906 strokeWidth: 1,
17907 fill: getStrokeColor$1(element, defaultStrokeColor),
17908 stroke: getStrokeColor$1(element, defaultStrokeColor)
17909 });
17910
17911 return diamond;
17912 },
17913 'bpmn:ParallelGateway': function(parentGfx, element) {
17914 var diamond = renderer('bpmn:Gateway')(parentGfx, element);
17915
17916 var pathData = pathMap.getScaledPath('GATEWAY_PARALLEL', {
17917 xScaleFactor: 0.6,
17918 yScaleFactor:0.6,
17919 containerWidth: element.width,
17920 containerHeight: element.height,
17921 position: {
17922 mx: 0.46,
17923 my: 0.2
17924 }
17925 });
17926
17927 /* parallel path */ drawPath(parentGfx, pathData, {
17928 strokeWidth: 1,
17929 fill: getStrokeColor$1(element, defaultStrokeColor),
17930 stroke: getStrokeColor$1(element, defaultStrokeColor)
17931 });
17932
17933 return diamond;
17934 },
17935 'bpmn:EventBasedGateway': function(parentGfx, element) {
17936
17937 var semantic = getSemantic(element);
17938
17939 var diamond = renderer('bpmn:Gateway')(parentGfx, element);
17940
17941 /* outer circle path */ drawCircle(parentGfx, element.width, element.height, element.height * 0.20, {
17942 strokeWidth: 1,
17943 fill: 'none',
17944 stroke: getStrokeColor$1(element, defaultStrokeColor)
17945 });
17946
17947 var type = semantic.eventGatewayType;
17948 var instantiate = !!semantic.instantiate;
17949
17950 function drawEvent() {
17951
17952 var pathData = pathMap.getScaledPath('GATEWAY_EVENT_BASED', {
17953 xScaleFactor: 0.18,
17954 yScaleFactor: 0.18,
17955 containerWidth: element.width,
17956 containerHeight: element.height,
17957 position: {
17958 mx: 0.36,
17959 my: 0.44
17960 }
17961 });
17962
17963 var attrs = {
17964 strokeWidth: 2,
17965 fill: getFillColor(element, 'none'),
17966 stroke: getStrokeColor$1(element, defaultStrokeColor)
17967 };
17968
17969 /* event path */ drawPath(parentGfx, pathData, attrs);
17970 }
17971
17972 if (type === 'Parallel') {
17973
17974 var pathData = pathMap.getScaledPath('GATEWAY_PARALLEL', {
17975 xScaleFactor: 0.4,
17976 yScaleFactor:0.4,
17977 containerWidth: element.width,
17978 containerHeight: element.height,
17979 position: {
17980 mx: 0.474,
17981 my: 0.296
17982 }
17983 });
17984
17985 var parallelPath = drawPath(parentGfx, pathData);
17986 attr(parallelPath, {
17987 strokeWidth: 1,
17988 fill: 'none'
17989 });
17990 } else if (type === 'Exclusive') {
17991
17992 if (!instantiate) {
17993 var innerCircle = drawCircle(parentGfx, element.width, element.height, element.height * 0.26);
17994 attr(innerCircle, {
17995 strokeWidth: 1,
17996 fill: 'none',
17997 stroke: getStrokeColor$1(element, defaultStrokeColor)
17998 });
17999 }
18000
18001 drawEvent();
18002 }
18003
18004
18005 return diamond;
18006 },
18007 'bpmn:Gateway': function(parentGfx, element) {
18008 var attrs = {
18009 fill: getFillColor(element, defaultFillColor),
18010 fillOpacity: DEFAULT_FILL_OPACITY,
18011 stroke: getStrokeColor$1(element, defaultStrokeColor)
18012 };
18013
18014 return drawDiamond(parentGfx, element.width, element.height, attrs);
18015 },
18016 'bpmn:SequenceFlow': function(parentGfx, element) {
18017 var pathData = createPathFromConnection(element);
18018
18019 var fill = getFillColor(element, defaultFillColor),
18020 stroke = getStrokeColor$1(element, defaultStrokeColor);
18021
18022 var attrs = {
18023 strokeLinejoin: 'round',
18024 markerEnd: marker('sequenceflow-end', fill, stroke),
18025 stroke: getStrokeColor$1(element, defaultStrokeColor)
18026 };
18027
18028 var path = drawPath(parentGfx, pathData, attrs);
18029
18030 var sequenceFlow = getSemantic(element);
18031
18032 var source;
18033
18034 if (element.source) {
18035 source = element.source.businessObject;
18036
18037 // conditional flow marker
18038 if (sequenceFlow.conditionExpression && source.$instanceOf('bpmn:Activity')) {
18039 attr(path, {
18040 markerStart: marker('conditional-flow-marker', fill, stroke)
18041 });
18042 }
18043
18044 // default marker
18045 if (source.default && (source.$instanceOf('bpmn:Gateway') || source.$instanceOf('bpmn:Activity')) &&
18046 source.default === sequenceFlow) {
18047 attr(path, {
18048 markerStart: marker('conditional-default-flow-marker', fill, stroke)
18049 });
18050 }
18051 }
18052
18053 return path;
18054 },
18055 'bpmn:Association': function(parentGfx, element, attrs) {
18056
18057 var semantic = getSemantic(element);
18058
18059 var fill = getFillColor(element, defaultFillColor),
18060 stroke = getStrokeColor$1(element, defaultStrokeColor);
18061
18062 attrs = assign({
18063 strokeDasharray: '0.5, 5',
18064 strokeLinecap: 'round',
18065 strokeLinejoin: 'round',
18066 stroke: getStrokeColor$1(element, defaultStrokeColor)
18067 }, attrs || {});
18068
18069 if (semantic.associationDirection === 'One' ||
18070 semantic.associationDirection === 'Both') {
18071 attrs.markerEnd = marker('association-end', fill, stroke);
18072 }
18073
18074 if (semantic.associationDirection === 'Both') {
18075 attrs.markerStart = marker('association-start', fill, stroke);
18076 }
18077
18078 return drawLine(parentGfx, element.waypoints, attrs);
18079 },
18080 'bpmn:DataInputAssociation': function(parentGfx, element) {
18081 var fill = getFillColor(element, defaultFillColor),
18082 stroke = getStrokeColor$1(element, defaultStrokeColor);
18083
18084 return renderer('bpmn:Association')(parentGfx, element, {
18085 markerEnd: marker('association-end', fill, stroke)
18086 });
18087 },
18088 'bpmn:DataOutputAssociation': function(parentGfx, element) {
18089 var fill = getFillColor(element, defaultFillColor),
18090 stroke = getStrokeColor$1(element, defaultStrokeColor);
18091
18092 return renderer('bpmn:Association')(parentGfx, element, {
18093 markerEnd: marker('association-end', fill, stroke)
18094 });
18095 },
18096 'bpmn:MessageFlow': function(parentGfx, element) {
18097
18098 var semantic = getSemantic(element),
18099 di = getDi(element);
18100
18101 var fill = getFillColor(element, defaultFillColor),
18102 stroke = getStrokeColor$1(element, defaultStrokeColor);
18103
18104 var pathData = createPathFromConnection(element);
18105
18106 var attrs = {
18107 markerEnd: marker('messageflow-end', fill, stroke),
18108 markerStart: marker('messageflow-start', fill, stroke),
18109 strokeDasharray: '10, 12',
18110 strokeLinecap: 'round',
18111 strokeLinejoin: 'round',
18112 strokeWidth: '1.5px',
18113 stroke: getStrokeColor$1(element, defaultStrokeColor)
18114 };
18115
18116 var path = drawPath(parentGfx, pathData, attrs);
18117
18118 if (semantic.messageRef) {
18119 var midPoint = path.getPointAtLength(path.getTotalLength() / 2);
18120
18121 var markerPathData = pathMap.getScaledPath('MESSAGE_FLOW_MARKER', {
18122 abspos: {
18123 x: midPoint.x,
18124 y: midPoint.y
18125 }
18126 });
18127
18128 var messageAttrs = { strokeWidth: 1 };
18129
18130 if (di.messageVisibleKind === 'initiating') {
18131 messageAttrs.fill = 'white';
18132 messageAttrs.stroke = 'black';
18133 } else {
18134 messageAttrs.fill = '#888';
18135 messageAttrs.stroke = 'white';
18136 }
18137
18138 var message = drawPath(parentGfx, markerPathData, messageAttrs);
18139
18140 var labelText = semantic.messageRef.name;
18141 var label = renderLabel(parentGfx, labelText, {
18142 align: 'center-top',
18143 fitBox: true,
18144 style: {
18145 fill: getStrokeColor$1(element, defaultLabelColor)
18146 }
18147 });
18148
18149 var messageBounds = message.getBBox(),
18150 labelBounds = label.getBBox();
18151
18152 var translateX = midPoint.x - labelBounds.width / 2,
18153 translateY = midPoint.y + messageBounds.height / 2 + ELEMENT_LABEL_DISTANCE$1;
18154
18155 transform(label, translateX, translateY, 0);
18156
18157 }
18158
18159 return path;
18160 },
18161 'bpmn:DataObject': function(parentGfx, element) {
18162 var pathData = pathMap.getScaledPath('DATA_OBJECT_PATH', {
18163 xScaleFactor: 1,
18164 yScaleFactor: 1,
18165 containerWidth: element.width,
18166 containerHeight: element.height,
18167 position: {
18168 mx: 0.474,
18169 my: 0.296
18170 }
18171 });
18172
18173 var elementObject = drawPath(parentGfx, pathData, {
18174 fill: getFillColor(element, defaultFillColor),
18175 fillOpacity: DEFAULT_FILL_OPACITY,
18176 stroke: getStrokeColor$1(element, defaultStrokeColor)
18177 });
18178
18179 var semantic = getSemantic(element);
18180
18181 if (isCollection(semantic)) {
18182 renderDataItemCollection(parentGfx, element);
18183 }
18184
18185 return elementObject;
18186 },
18187 'bpmn:DataObjectReference': as('bpmn:DataObject'),
18188 'bpmn:DataInput': function(parentGfx, element) {
18189
18190 var arrowPathData = pathMap.getRawPath('DATA_ARROW');
18191
18192 // page
18193 var elementObject = renderer('bpmn:DataObject')(parentGfx, element);
18194
18195 /* input arrow path */ drawPath(parentGfx, arrowPathData, { strokeWidth: 1 });
18196
18197 return elementObject;
18198 },
18199 'bpmn:DataOutput': function(parentGfx, element) {
18200 var arrowPathData = pathMap.getRawPath('DATA_ARROW');
18201
18202 // page
18203 var elementObject = renderer('bpmn:DataObject')(parentGfx, element);
18204
18205 /* output arrow path */ drawPath(parentGfx, arrowPathData, {
18206 strokeWidth: 1,
18207 fill: 'black'
18208 });
18209
18210 return elementObject;
18211 },
18212 'bpmn:DataStoreReference': function(parentGfx, element) {
18213 var DATA_STORE_PATH = pathMap.getScaledPath('DATA_STORE', {
18214 xScaleFactor: 1,
18215 yScaleFactor: 1,
18216 containerWidth: element.width,
18217 containerHeight: element.height,
18218 position: {
18219 mx: 0,
18220 my: 0.133
18221 }
18222 });
18223
18224 var elementStore = drawPath(parentGfx, DATA_STORE_PATH, {
18225 strokeWidth: 2,
18226 fill: getFillColor(element, defaultFillColor),
18227 fillOpacity: DEFAULT_FILL_OPACITY,
18228 stroke: getStrokeColor$1(element, defaultStrokeColor)
18229 });
18230
18231 return elementStore;
18232 },
18233 'bpmn:BoundaryEvent': function(parentGfx, element) {
18234
18235 var semantic = getSemantic(element),
18236 cancel = semantic.cancelActivity;
18237
18238 var attrs = {
18239 strokeWidth: 1,
18240 fill: getFillColor(element, defaultFillColor),
18241 stroke: getStrokeColor$1(element, defaultStrokeColor)
18242 };
18243
18244 if (!cancel) {
18245 attrs.strokeDasharray = '6';
18246 attrs.strokeLinecap = 'round';
18247 }
18248
18249 // apply fillOpacity
18250 var outerAttrs = assign({}, attrs, {
18251 fillOpacity: 1
18252 });
18253
18254 // apply no-fill
18255 var innerAttrs = assign({}, attrs, {
18256 fill: 'none'
18257 });
18258
18259 var outer = renderer('bpmn:Event')(parentGfx, element, outerAttrs);
18260
18261 /* inner path */ drawCircle(parentGfx, element.width, element.height, INNER_OUTER_DIST, innerAttrs);
18262
18263 renderEventContent(element, parentGfx);
18264
18265 return outer;
18266 },
18267 'bpmn:Group': function(parentGfx, element) {
18268
18269 var group = drawRect(parentGfx, element.width, element.height, TASK_BORDER_RADIUS, {
18270 stroke: getStrokeColor$1(element, defaultStrokeColor),
18271 strokeWidth: 1,
18272 strokeDasharray: '8,3,1,3',
18273 fill: 'none',
18274 pointerEvents: 'none'
18275 });
18276
18277 return group;
18278 },
18279 'label': function(parentGfx, element) {
18280 return renderExternalLabel(parentGfx, element);
18281 },
18282 'bpmn:TextAnnotation': function(parentGfx, element) {
18283 var style = {
18284 'fill': 'none',
18285 'stroke': 'none'
18286 };
18287
18288 var textElement = drawRect(parentGfx, element.width, element.height, 0, 0, style);
18289
18290 var textPathData = pathMap.getScaledPath('TEXT_ANNOTATION', {
18291 xScaleFactor: 1,
18292 yScaleFactor: 1,
18293 containerWidth: element.width,
18294 containerHeight: element.height,
18295 position: {
18296 mx: 0.0,
18297 my: 0.0
18298 }
18299 });
18300
18301 drawPath(parentGfx, textPathData, {
18302 stroke: getStrokeColor$1(element, defaultStrokeColor)
18303 });
18304
18305 var text = getSemantic(element).text || '';
18306 renderLabel(parentGfx, text, {
18307 box: element,
18308 align: 'left-top',
18309 padding: 5,
18310 style: {
18311 fill: getLabelColor(element, defaultLabelColor, defaultStrokeColor)
18312 }
18313 });
18314
18315 return textElement;
18316 },
18317 'ParticipantMultiplicityMarker': function(parentGfx, element) {
18318 var markerPath = pathMap.getScaledPath('MARKER_PARALLEL', {
18319 xScaleFactor: 1,
18320 yScaleFactor: 1,
18321 containerWidth: element.width,
18322 containerHeight: element.height,
18323 position: {
18324 mx: ((element.width / 2) / element.width),
18325 my: (element.height - 15) / element.height
18326 }
18327 });
18328
18329 drawMarker('participant-multiplicity', parentGfx, markerPath, {
18330 strokeWidth: 2,
18331 fill: getFillColor(element, defaultFillColor),
18332 stroke: getStrokeColor$1(element, defaultStrokeColor)
18333 });
18334 },
18335 'SubProcessMarker': function(parentGfx, element) {
18336 var markerRect = drawRect(parentGfx, 14, 14, 0, {
18337 strokeWidth: 1,
18338 fill: getFillColor(element, defaultFillColor),
18339 stroke: getStrokeColor$1(element, defaultStrokeColor)
18340 });
18341
18342 // Process marker is placed in the middle of the box
18343 // therefore fixed values can be used here
18344 translate$2(markerRect, element.width / 2 - 7.5, element.height - 20);
18345
18346 var markerPath = pathMap.getScaledPath('MARKER_SUB_PROCESS', {
18347 xScaleFactor: 1.5,
18348 yScaleFactor: 1.5,
18349 containerWidth: element.width,
18350 containerHeight: element.height,
18351 position: {
18352 mx: (element.width / 2 - 7.5) / element.width,
18353 my: (element.height - 20) / element.height
18354 }
18355 });
18356
18357 drawMarker('sub-process', parentGfx, markerPath, {
18358 fill: getFillColor(element, defaultFillColor),
18359 stroke: getStrokeColor$1(element, defaultStrokeColor)
18360 });
18361 },
18362 'ParallelMarker': function(parentGfx, element, position) {
18363 var markerPath = pathMap.getScaledPath('MARKER_PARALLEL', {
18364 xScaleFactor: 1,
18365 yScaleFactor: 1,
18366 containerWidth: element.width,
18367 containerHeight: element.height,
18368 position: {
18369 mx: ((element.width / 2 + position.parallel) / element.width),
18370 my: (element.height - 20) / element.height
18371 }
18372 });
18373
18374 drawMarker('parallel', parentGfx, markerPath, {
18375 fill: getFillColor(element, defaultFillColor),
18376 stroke: getStrokeColor$1(element, defaultStrokeColor)
18377 });
18378 },
18379 'SequentialMarker': function(parentGfx, element, position) {
18380 var markerPath = pathMap.getScaledPath('MARKER_SEQUENTIAL', {
18381 xScaleFactor: 1,
18382 yScaleFactor: 1,
18383 containerWidth: element.width,
18384 containerHeight: element.height,
18385 position: {
18386 mx: ((element.width / 2 + position.seq) / element.width),
18387 my: (element.height - 19) / element.height
18388 }
18389 });
18390
18391 drawMarker('sequential', parentGfx, markerPath, {
18392 fill: getFillColor(element, defaultFillColor),
18393 stroke: getStrokeColor$1(element, defaultStrokeColor)
18394 });
18395 },
18396 'CompensationMarker': function(parentGfx, element, position) {
18397 var markerMath = pathMap.getScaledPath('MARKER_COMPENSATION', {
18398 xScaleFactor: 1,
18399 yScaleFactor: 1,
18400 containerWidth: element.width,
18401 containerHeight: element.height,
18402 position: {
18403 mx: ((element.width / 2 + position.compensation) / element.width),
18404 my: (element.height - 13) / element.height
18405 }
18406 });
18407
18408 drawMarker('compensation', parentGfx, markerMath, {
18409 strokeWidth: 1,
18410 fill: getFillColor(element, defaultFillColor),
18411 stroke: getStrokeColor$1(element, defaultStrokeColor)
18412 });
18413 },
18414 'LoopMarker': function(parentGfx, element, position) {
18415 var markerPath = pathMap.getScaledPath('MARKER_LOOP', {
18416 xScaleFactor: 1,
18417 yScaleFactor: 1,
18418 containerWidth: element.width,
18419 containerHeight: element.height,
18420 position: {
18421 mx: ((element.width / 2 + position.loop) / element.width),
18422 my: (element.height - 7) / element.height
18423 }
18424 });
18425
18426 drawMarker('loop', parentGfx, markerPath, {
18427 strokeWidth: 1,
18428 fill: getFillColor(element, defaultFillColor),
18429 stroke: getStrokeColor$1(element, defaultStrokeColor),
18430 strokeLinecap: 'round',
18431 strokeMiterlimit: 0.5
18432 });
18433 },
18434 'AdhocMarker': function(parentGfx, element, position) {
18435 var markerPath = pathMap.getScaledPath('MARKER_ADHOC', {
18436 xScaleFactor: 1,
18437 yScaleFactor: 1,
18438 containerWidth: element.width,
18439 containerHeight: element.height,
18440 position: {
18441 mx: ((element.width / 2 + position.adhoc) / element.width),
18442 my: (element.height - 15) / element.height
18443 }
18444 });
18445
18446 drawMarker('adhoc', parentGfx, markerPath, {
18447 strokeWidth: 1,
18448 fill: getStrokeColor$1(element, defaultStrokeColor),
18449 stroke: getStrokeColor$1(element, defaultStrokeColor)
18450 });
18451 }
18452 };
18453
18454 function attachTaskMarkers(parentGfx, element, taskMarkers) {
18455 var obj = getSemantic(element);
18456
18457 var subprocess = taskMarkers && taskMarkers.indexOf('SubProcessMarker') !== -1;
18458 var position;
18459
18460 if (subprocess) {
18461 position = {
18462 seq: -21,
18463 parallel: -22,
18464 compensation: -42,
18465 loop: -18,
18466 adhoc: 10
18467 };
18468 } else {
18469 position = {
18470 seq: -3,
18471 parallel: -6,
18472 compensation: -27,
18473 loop: 0,
18474 adhoc: 10
18475 };
18476 }
18477
18478 forEach(taskMarkers, function(marker) {
18479 renderer(marker)(parentGfx, element, position);
18480 });
18481
18482 if (obj.isForCompensation) {
18483 renderer('CompensationMarker')(parentGfx, element, position);
18484 }
18485
18486 if (obj.$type === 'bpmn:AdHocSubProcess') {
18487 renderer('AdhocMarker')(parentGfx, element, position);
18488 }
18489
18490 var loopCharacteristics = obj.loopCharacteristics,
18491 isSequential = loopCharacteristics && loopCharacteristics.isSequential;
18492
18493 if (loopCharacteristics) {
18494
18495 if (isSequential === undefined) {
18496 renderer('LoopMarker')(parentGfx, element, position);
18497 }
18498
18499 if (isSequential === false) {
18500 renderer('ParallelMarker')(parentGfx, element, position);
18501 }
18502
18503 if (isSequential === true) {
18504 renderer('SequentialMarker')(parentGfx, element, position);
18505 }
18506 }
18507 }
18508
18509 function renderDataItemCollection(parentGfx, element) {
18510
18511 var yPosition = (element.height - 18) / element.height;
18512
18513 var pathData = pathMap.getScaledPath('DATA_OBJECT_COLLECTION_PATH', {
18514 xScaleFactor: 1,
18515 yScaleFactor: 1,
18516 containerWidth: element.width,
18517 containerHeight: element.height,
18518 position: {
18519 mx: 0.33,
18520 my: yPosition
18521 }
18522 });
18523
18524 /* collection path */ drawPath(parentGfx, pathData, {
18525 strokeWidth: 2
18526 });
18527 }
18528
18529
18530 // extension API, use at your own risk
18531 this._drawPath = drawPath;
18532
18533 }
18534
18535
18536 inherits$1(BpmnRenderer, BaseRenderer);
18537
18538 BpmnRenderer.$inject = [
18539 'config.bpmnRenderer',
18540 'eventBus',
18541 'styles',
18542 'pathMap',
18543 'canvas',
18544 'textRenderer'
18545 ];
18546
18547
18548 BpmnRenderer.prototype.canRender = function(element) {
18549 return is$1(element, 'bpmn:BaseElement');
18550 };
18551
18552 BpmnRenderer.prototype.drawShape = function(parentGfx, element) {
18553 var type = element.type;
18554 var h = this.handlers[type];
18555
18556 /* jshint -W040 */
18557 return h(parentGfx, element);
18558 };
18559
18560 BpmnRenderer.prototype.drawConnection = function(parentGfx, element) {
18561 var type = element.type;
18562 var h = this.handlers[type];
18563
18564 /* jshint -W040 */
18565 return h(parentGfx, element);
18566 };
18567
18568 BpmnRenderer.prototype.getShapePath = function(element) {
18569
18570 if (is$1(element, 'bpmn:Event')) {
18571 return getCirclePath(element);
18572 }
18573
18574 if (is$1(element, 'bpmn:Activity')) {
18575 return getRoundRectPath(element, TASK_BORDER_RADIUS);
18576 }
18577
18578 if (is$1(element, 'bpmn:Gateway')) {
18579 return getDiamondPath(element);
18580 }
18581
18582 return getRectPath(element);
18583 };
18584
18585 var DEFAULT_BOX_PADDING = 0;
18586
18587 var DEFAULT_LABEL_SIZE$1 = {
18588 width: 150,
18589 height: 50
18590 };
18591
18592
18593 function parseAlign(align) {
18594
18595 var parts = align.split('-');
18596
18597 return {
18598 horizontal: parts[0] || 'center',
18599 vertical: parts[1] || 'top'
18600 };
18601 }
18602
18603 function parsePadding(padding) {
18604
18605 if (isObject(padding)) {
18606 return assign({ top: 0, left: 0, right: 0, bottom: 0 }, padding);
18607 } else {
18608 return {
18609 top: padding,
18610 left: padding,
18611 right: padding,
18612 bottom: padding
18613 };
18614 }
18615 }
18616
18617 function getTextBBox(text, fakeText) {
18618
18619 fakeText.textContent = text;
18620
18621 var textBBox;
18622
18623 try {
18624 var bbox,
18625 emptyLine = text === '';
18626
18627 // add dummy text, when line is empty to
18628 // determine correct height
18629 fakeText.textContent = emptyLine ? 'dummy' : text;
18630
18631 textBBox = fakeText.getBBox();
18632
18633 // take text rendering related horizontal
18634 // padding into account
18635 bbox = {
18636 width: textBBox.width + textBBox.x * 2,
18637 height: textBBox.height
18638 };
18639
18640 if (emptyLine) {
18641
18642 // correct width
18643 bbox.width = 0;
18644 }
18645
18646 return bbox;
18647 } catch (e) {
18648 return { width: 0, height: 0 };
18649 }
18650 }
18651
18652
18653 /**
18654 * Layout the next line and return the layouted element.
18655 *
18656 * Alters the lines passed.
18657 *
18658 * @param {Array<string>} lines
18659 * @return {Object} the line descriptor, an object { width, height, text }
18660 */
18661 function layoutNext(lines, maxWidth, fakeText) {
18662
18663 var originalLine = lines.shift(),
18664 fitLine = originalLine;
18665
18666 var textBBox;
18667
18668 for (;;) {
18669 textBBox = getTextBBox(fitLine, fakeText);
18670
18671 textBBox.width = fitLine ? textBBox.width : 0;
18672
18673 // try to fit
18674 if (fitLine === ' ' || fitLine === '' || textBBox.width < Math.round(maxWidth) || fitLine.length < 2) {
18675 return fit(lines, fitLine, originalLine, textBBox);
18676 }
18677
18678 fitLine = shortenLine(fitLine, textBBox.width, maxWidth);
18679 }
18680 }
18681
18682 function fit(lines, fitLine, originalLine, textBBox) {
18683 if (fitLine.length < originalLine.length) {
18684 var remainder = originalLine.slice(fitLine.length).trim();
18685
18686 lines.unshift(remainder);
18687 }
18688
18689 return {
18690 width: textBBox.width,
18691 height: textBBox.height,
18692 text: fitLine
18693 };
18694 }
18695
18696 var SOFT_BREAK = '\u00AD';
18697
18698
18699 /**
18700 * Shortens a line based on spacing and hyphens.
18701 * Returns the shortened result on success.
18702 *
18703 * @param {string} line
18704 * @param {number} maxLength the maximum characters of the string
18705 * @return {string} the shortened string
18706 */
18707 function semanticShorten(line, maxLength) {
18708
18709 var parts = line.split(/(\s|-|\u00AD)/g),
18710 part,
18711 shortenedParts = [],
18712 length = 0;
18713
18714 // try to shorten via break chars
18715 if (parts.length > 1) {
18716
18717 while ((part = parts.shift())) {
18718 if (part.length + length < maxLength) {
18719 shortenedParts.push(part);
18720 length += part.length;
18721 } else {
18722
18723 // remove previous part, too if hyphen does not fit anymore
18724 if (part === '-' || part === SOFT_BREAK) {
18725 shortenedParts.pop();
18726 }
18727
18728 break;
18729 }
18730 }
18731 }
18732
18733 var last = shortenedParts[shortenedParts.length - 1];
18734
18735 // translate trailing soft break to actual hyphen
18736 if (last && last === SOFT_BREAK) {
18737 shortenedParts[shortenedParts.length - 1] = '-';
18738 }
18739
18740 return shortenedParts.join('');
18741 }
18742
18743
18744 function shortenLine(line, width, maxWidth) {
18745 var length = Math.max(line.length * (maxWidth / width), 1);
18746
18747 // try to shorten semantically (i.e. based on spaces and hyphens)
18748 var shortenedLine = semanticShorten(line, length);
18749
18750 if (!shortenedLine) {
18751
18752 // force shorten by cutting the long word
18753 shortenedLine = line.slice(0, Math.max(Math.round(length - 1), 1));
18754 }
18755
18756 return shortenedLine;
18757 }
18758
18759
18760 function getHelperSvg() {
18761 var helperSvg = document.getElementById('helper-svg');
18762
18763 if (!helperSvg) {
18764 helperSvg = create$1('svg');
18765
18766 attr(helperSvg, {
18767 id: 'helper-svg',
18768 width: 0,
18769 height: 0,
18770 style: 'visibility: hidden; position: fixed'
18771 });
18772
18773 document.body.appendChild(helperSvg);
18774 }
18775
18776 return helperSvg;
18777 }
18778
18779
18780 /**
18781 * Creates a new label utility
18782 *
18783 * @param {Object} config
18784 * @param {Dimensions} config.size
18785 * @param {number} config.padding
18786 * @param {Object} config.style
18787 * @param {string} config.align
18788 */
18789 function Text(config) {
18790
18791 this._config = assign({}, {
18792 size: DEFAULT_LABEL_SIZE$1,
18793 padding: DEFAULT_BOX_PADDING,
18794 style: {},
18795 align: 'center-top'
18796 }, config || {});
18797 }
18798
18799 /**
18800 * Returns the layouted text as an SVG element.
18801 *
18802 * @param {string} text
18803 * @param {Object} options
18804 *
18805 * @return {SVGElement}
18806 */
18807 Text.prototype.createText = function(text, options) {
18808 return this.layoutText(text, options).element;
18809 };
18810
18811 /**
18812 * Returns a labels layouted dimensions.
18813 *
18814 * @param {string} text to layout
18815 * @param {Object} options
18816 *
18817 * @return {Dimensions}
18818 */
18819 Text.prototype.getDimensions = function(text, options) {
18820 return this.layoutText(text, options).dimensions;
18821 };
18822
18823 /**
18824 * Creates and returns a label and its bounding box.
18825 *
18826 * @method Text#createText
18827 *
18828 * @param {string} text the text to render on the label
18829 * @param {Object} options
18830 * @param {string} options.align how to align in the bounding box.
18831 * Any of { 'center-middle', 'center-top' },
18832 * defaults to 'center-top'.
18833 * @param {string} options.style style to be applied to the text
18834 * @param {boolean} options.fitBox indicates if box will be recalculated to
18835 * fit text
18836 *
18837 * @return {Object} { element, dimensions }
18838 */
18839 Text.prototype.layoutText = function(text, options) {
18840 var box = assign({}, this._config.size, options.box),
18841 style = assign({}, this._config.style, options.style),
18842 align = parseAlign(options.align || this._config.align),
18843 padding = parsePadding(options.padding !== undefined ? options.padding : this._config.padding),
18844 fitBox = options.fitBox || false;
18845
18846 var lineHeight = getLineHeight(style);
18847
18848 // we split text by lines and normalize
18849 // {soft break} + {line break} => { line break }
18850 var lines = text.split(/\u00AD?\r?\n/),
18851 layouted = [];
18852
18853 var maxWidth = box.width - padding.left - padding.right;
18854
18855 // ensure correct rendering by attaching helper text node to invisible SVG
18856 var helperText = create$1('text');
18857 attr(helperText, { x: 0, y: 0 });
18858 attr(helperText, style);
18859
18860 var helperSvg = getHelperSvg();
18861
18862 append(helperSvg, helperText);
18863
18864 while (lines.length) {
18865 layouted.push(layoutNext(lines, maxWidth, helperText));
18866 }
18867
18868 if (align.vertical === 'middle') {
18869 padding.top = padding.bottom = 0;
18870 }
18871
18872 var totalHeight = reduce(layouted, function(sum, line, idx) {
18873 return sum + (lineHeight || line.height);
18874 }, 0) + padding.top + padding.bottom;
18875
18876 var maxLineWidth = reduce(layouted, function(sum, line, idx) {
18877 return line.width > sum ? line.width : sum;
18878 }, 0);
18879
18880 // the y position of the next line
18881 var y = padding.top;
18882
18883 if (align.vertical === 'middle') {
18884 y += (box.height - totalHeight) / 2;
18885 }
18886
18887 // magic number initial offset
18888 y -= (lineHeight || layouted[0].height) / 4;
18889
18890
18891 var textElement = create$1('text');
18892
18893 attr(textElement, style);
18894
18895 // layout each line taking into account that parent
18896 // shape might resize to fit text size
18897 forEach(layouted, function(line) {
18898
18899 var x;
18900
18901 y += (lineHeight || line.height);
18902
18903 switch (align.horizontal) {
18904 case 'left':
18905 x = padding.left;
18906 break;
18907
18908 case 'right':
18909 x = ((fitBox ? maxLineWidth : maxWidth)
18910 - padding.right - line.width);
18911 break;
18912
18913 default:
18914
18915 // aka center
18916 x = Math.max((((fitBox ? maxLineWidth : maxWidth)
18917 - line.width) / 2 + padding.left), 0);
18918 }
18919
18920 var tspan = create$1('tspan');
18921 attr(tspan, { x: x, y: y });
18922
18923 tspan.textContent = line.text;
18924
18925 append(textElement, tspan);
18926 });
18927
18928 remove$1(helperText);
18929
18930 var dimensions = {
18931 width: maxLineWidth,
18932 height: totalHeight
18933 };
18934
18935 return {
18936 dimensions: dimensions,
18937 element: textElement
18938 };
18939 };
18940
18941
18942 function getLineHeight(style) {
18943 if ('fontSize' in style && 'lineHeight' in style) {
18944 return style.lineHeight * parseInt(style.fontSize, 10);
18945 }
18946 }
18947
18948 var DEFAULT_FONT_SIZE = 12;
18949 var LINE_HEIGHT_RATIO = 1.2;
18950
18951 var MIN_TEXT_ANNOTATION_HEIGHT = 30;
18952
18953
18954 function TextRenderer(config) {
18955
18956 var defaultStyle = assign({
18957 fontFamily: 'Arial, sans-serif',
18958 fontSize: DEFAULT_FONT_SIZE,
18959 fontWeight: 'normal',
18960 lineHeight: LINE_HEIGHT_RATIO
18961 }, config && config.defaultStyle || {});
18962
18963 var fontSize = parseInt(defaultStyle.fontSize, 10) - 1;
18964
18965 var externalStyle = assign({}, defaultStyle, {
18966 fontSize: fontSize
18967 }, config && config.externalStyle || {});
18968
18969 var textUtil = new Text({
18970 style: defaultStyle
18971 });
18972
18973 /**
18974 * Get the new bounds of an externally rendered,
18975 * layouted label.
18976 *
18977 * @param {Bounds} bounds
18978 * @param {string} text
18979 *
18980 * @return {Bounds}
18981 */
18982 this.getExternalLabelBounds = function(bounds, text) {
18983
18984 var layoutedDimensions = textUtil.getDimensions(text, {
18985 box: {
18986 width: 90,
18987 height: 30,
18988 x: bounds.width / 2 + bounds.x,
18989 y: bounds.height / 2 + bounds.y
18990 },
18991 style: externalStyle
18992 });
18993
18994 // resize label shape to fit label text
18995 return {
18996 x: Math.round(bounds.x + bounds.width / 2 - layoutedDimensions.width / 2),
18997 y: Math.round(bounds.y),
18998 width: Math.ceil(layoutedDimensions.width),
18999 height: Math.ceil(layoutedDimensions.height)
19000 };
19001
19002 };
19003
19004 /**
19005 * Get the new bounds of text annotation.
19006 *
19007 * @param {Bounds} bounds
19008 * @param {string} text
19009 *
19010 * @return {Bounds}
19011 */
19012 this.getTextAnnotationBounds = function(bounds, text) {
19013
19014 var layoutedDimensions = textUtil.getDimensions(text, {
19015 box: bounds,
19016 style: defaultStyle,
19017 align: 'left-top',
19018 padding: 5
19019 });
19020
19021 return {
19022 x: bounds.x,
19023 y: bounds.y,
19024 width: bounds.width,
19025 height: Math.max(MIN_TEXT_ANNOTATION_HEIGHT, Math.round(layoutedDimensions.height))
19026 };
19027 };
19028
19029 /**
19030 * Create a layouted text element.
19031 *
19032 * @param {string} text
19033 * @param {Object} [options]
19034 *
19035 * @return {SVGElement} rendered text
19036 */
19037 this.createText = function(text, options) {
19038 return textUtil.createText(text, options || {});
19039 };
19040
19041 /**
19042 * Get default text style.
19043 */
19044 this.getDefaultStyle = function() {
19045 return defaultStyle;
19046 };
19047
19048 /**
19049 * Get the external text style.
19050 */
19051 this.getExternalStyle = function() {
19052 return externalStyle;
19053 };
19054
19055 }
19056
19057 TextRenderer.$inject = [
19058 'config.textRenderer'
19059 ];
19060
19061 /**
19062 * Map containing SVG paths needed by BpmnRenderer.
19063 */
19064
19065 function PathMap() {
19066
19067 /**
19068 * Contains a map of path elements
19069 *
19070 * <h1>Path definition</h1>
19071 * A parameterized path is defined like this:
19072 * <pre>
19073 * 'GATEWAY_PARALLEL': {
19074 * d: 'm {mx},{my} {e.x0},0 0,{e.x1} {e.x1},0 0,{e.y0} -{e.x1},0 0,{e.y1} ' +
19075 '-{e.x0},0 0,-{e.y1} -{e.x1},0 0,-{e.y0} {e.x1},0 z',
19076 * height: 17.5,
19077 * width: 17.5,
19078 * heightElements: [2.5, 7.5],
19079 * widthElements: [2.5, 7.5]
19080 * }
19081 * </pre>
19082 * <p>It's important to specify a correct <b>height and width</b> for the path as the scaling
19083 * is based on the ratio between the specified height and width in this object and the
19084 * height and width that is set as scale target (Note x,y coordinates will be scaled with
19085 * individual ratios).</p>
19086 * <p>The '<b>heightElements</b>' and '<b>widthElements</b>' array must contain the values that will be scaled.
19087 * The scaling is based on the computed ratios.
19088 * Coordinates on the y axis should be in the <b>heightElement</b>'s array, they will be scaled using
19089 * the computed ratio coefficient.
19090 * In the parameterized path the scaled values can be accessed through the 'e' object in {} brackets.
19091 * <ul>
19092 * <li>The values for the y axis can be accessed in the path string using {e.y0}, {e.y1}, ....</li>
19093 * <li>The values for the x axis can be accessed in the path string using {e.x0}, {e.x1}, ....</li>
19094 * </ul>
19095 * The numbers x0, x1 respectively y0, y1, ... map to the corresponding array index.
19096 * </p>
19097 */
19098 this.pathMap = {
19099 'EVENT_MESSAGE': {
19100 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}',
19101 height: 36,
19102 width: 36,
19103 heightElements: [6, 14],
19104 widthElements: [10.5, 21]
19105 },
19106 'EVENT_SIGNAL': {
19107 d: 'M {mx},{my} l {e.x0},{e.y0} l -{e.x1},0 Z',
19108 height: 36,
19109 width: 36,
19110 heightElements: [18],
19111 widthElements: [10, 20]
19112 },
19113 'EVENT_ESCALATION': {
19114 d: 'M {mx},{my} l {e.x0},{e.y0} l -{e.x0},-{e.y1} l -{e.x0},{e.y1} Z',
19115 height: 36,
19116 width: 36,
19117 heightElements: [20, 7],
19118 widthElements: [8]
19119 },
19120 'EVENT_CONDITIONAL': {
19121 d: 'M {e.x0},{e.y0} l {e.x1},0 l 0,{e.y2} l -{e.x1},0 Z ' +
19122 'M {e.x2},{e.y3} l {e.x0},0 ' +
19123 'M {e.x2},{e.y4} l {e.x0},0 ' +
19124 'M {e.x2},{e.y5} l {e.x0},0 ' +
19125 'M {e.x2},{e.y6} l {e.x0},0 ' +
19126 'M {e.x2},{e.y7} l {e.x0},0 ' +
19127 'M {e.x2},{e.y8} l {e.x0},0 ',
19128 height: 36,
19129 width: 36,
19130 heightElements: [8.5, 14.5, 18, 11.5, 14.5, 17.5, 20.5, 23.5, 26.5],
19131 widthElements: [10.5, 14.5, 12.5]
19132 },
19133 'EVENT_LINK': {
19134 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',
19135 height: 36,
19136 width: 36,
19137 heightElements: [4.4375, 6.75, 7.8125],
19138 widthElements: [9.84375, 13.5]
19139 },
19140 'EVENT_ERROR': {
19141 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',
19142 height: 36,
19143 width: 36,
19144 heightElements: [0.023, 8.737, 8.151, 16.564, 10.591, 8.714],
19145 widthElements: [0.085, 6.672, 6.97, 4.273, 5.337, 6.636]
19146 },
19147 'EVENT_CANCEL_45': {
19148 d: 'm {mx},{my} -{e.x1},0 0,{e.x0} {e.x1},0 0,{e.y1} {e.x0},0 ' +
19149 '0,-{e.y1} {e.x1},0 0,-{e.y0} -{e.x1},0 0,-{e.y1} -{e.x0},0 z',
19150 height: 36,
19151 width: 36,
19152 heightElements: [4.75, 8.5],
19153 widthElements: [4.75, 8.5]
19154 },
19155 'EVENT_COMPENSATION': {
19156 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',
19157 height: 36,
19158 width: 36,
19159 heightElements: [6.5, 13, 0.4, 6.1],
19160 widthElements: [9, 9.3, 8.7]
19161 },
19162 'EVENT_TIMER_WH': {
19163 d: 'M {mx},{my} l {e.x0},-{e.y0} m -{e.x0},{e.y0} l {e.x1},{e.y1} ',
19164 height: 36,
19165 width: 36,
19166 heightElements: [10, 2],
19167 widthElements: [3, 7]
19168 },
19169 'EVENT_TIMER_LINE': {
19170 d: 'M {mx},{my} ' +
19171 'm {e.x0},{e.y0} l -{e.x1},{e.y1} ',
19172 height: 36,
19173 width: 36,
19174 heightElements: [10, 3],
19175 widthElements: [0, 0]
19176 },
19177 'EVENT_MULTIPLE': {
19178 d:'m {mx},{my} {e.x1},-{e.y0} {e.x1},{e.y0} -{e.x0},{e.y1} -{e.x2},0 z',
19179 height: 36,
19180 width: 36,
19181 heightElements: [6.28099, 12.56199],
19182 widthElements: [3.1405, 9.42149, 12.56198]
19183 },
19184 'EVENT_PARALLEL_MULTIPLE': {
19185 d:'m {mx},{my} {e.x0},0 0,{e.y1} {e.x1},0 0,{e.y0} -{e.x1},0 0,{e.y1} ' +
19186 '-{e.x0},0 0,-{e.y1} -{e.x1},0 0,-{e.y0} {e.x1},0 z',
19187 height: 36,
19188 width: 36,
19189 heightElements: [2.56228, 7.68683],
19190 widthElements: [2.56228, 7.68683]
19191 },
19192 'GATEWAY_EXCLUSIVE': {
19193 d:'m {mx},{my} {e.x0},{e.y0} {e.x1},{e.y0} {e.x2},0 {e.x4},{e.y2} ' +
19194 '{e.x4},{e.y1} {e.x2},0 {e.x1},{e.y3} {e.x0},{e.y3} ' +
19195 '{e.x3},0 {e.x5},{e.y1} {e.x5},{e.y2} {e.x3},0 z',
19196 height: 17.5,
19197 width: 17.5,
19198 heightElements: [8.5, 6.5312, -6.5312, -8.5],
19199 widthElements: [6.5, -6.5, 3, -3, 5, -5]
19200 },
19201 'GATEWAY_PARALLEL': {
19202 d:'m {mx},{my} 0,{e.y1} -{e.x1},0 0,{e.y0} {e.x1},0 0,{e.y1} {e.x0},0 ' +
19203 '0,-{e.y1} {e.x1},0 0,-{e.y0} -{e.x1},0 0,-{e.y1} -{e.x0},0 z',
19204 height: 30,
19205 width: 30,
19206 heightElements: [5, 12.5],
19207 widthElements: [5, 12.5]
19208 },
19209 'GATEWAY_EVENT_BASED': {
19210 d:'m {mx},{my} {e.x0},{e.y0} {e.x0},{e.y1} {e.x1},{e.y2} {e.x2},0 z',
19211 height: 11,
19212 width: 11,
19213 heightElements: [-6, 6, 12, -12],
19214 widthElements: [9, -3, -12]
19215 },
19216 'GATEWAY_COMPLEX': {
19217 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} ' +
19218 '{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} ' +
19219 '{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} ' +
19220 '-{e.x0},{e.y1} 0,-{e.y0} -{e.x3},0 z',
19221 height: 17.125,
19222 width: 17.125,
19223 heightElements: [4.875, 3.4375, 2.125, 3],
19224 widthElements: [3.4375, 2.125, 4.875, 3]
19225 },
19226 'DATA_OBJECT_PATH': {
19227 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',
19228 height: 61,
19229 width: 51,
19230 heightElements: [10, 50, 60],
19231 widthElements: [10, 40, 50, 60]
19232 },
19233 'DATA_OBJECT_COLLECTION_PATH': {
19234 d: 'm{mx},{my} m 3,2 l 0,10 m 3,-10 l 0,10 m 3,-10 l 0,10',
19235 height: 10,
19236 width: 10,
19237 heightElements: [],
19238 widthElements: []
19239 },
19240 'DATA_ARROW': {
19241 d:'m 5,9 9,0 0,-3 5,5 -5,5 0,-3 -9,0 z',
19242 height: 61,
19243 width: 51,
19244 heightElements: [],
19245 widthElements: []
19246 },
19247 'DATA_STORE': {
19248 d:'m {mx},{my} ' +
19249 'l 0,{e.y2} ' +
19250 'c {e.x0},{e.y1} {e.x1},{e.y1} {e.x2},0 ' +
19251 'l 0,-{e.y2} ' +
19252 'c -{e.x0},-{e.y1} -{e.x1},-{e.y1} -{e.x2},0' +
19253 'c {e.x0},{e.y1} {e.x1},{e.y1} {e.x2},0 ' +
19254 'm -{e.x2},{e.y0}' +
19255 'c {e.x0},{e.y1} {e.x1},{e.y1} {e.x2},0' +
19256 'm -{e.x2},{e.y0}' +
19257 'c {e.x0},{e.y1} {e.x1},{e.y1} {e.x2},0',
19258 height: 61,
19259 width: 61,
19260 heightElements: [7, 10, 45],
19261 widthElements: [2, 58, 60]
19262 },
19263 'TEXT_ANNOTATION': {
19264 d: 'm {mx}, {my} m 10,0 l -10,0 l 0,{e.y0} l 10,0',
19265 height: 30,
19266 width: 10,
19267 heightElements: [30],
19268 widthElements: [10]
19269 },
19270 'MARKER_SUB_PROCESS': {
19271 d: 'm{mx},{my} m 7,2 l 0,10 m -5,-5 l 10,0',
19272 height: 10,
19273 width: 10,
19274 heightElements: [],
19275 widthElements: []
19276 },
19277 'MARKER_PARALLEL': {
19278 d: 'm{mx},{my} m 3,2 l 0,10 m 3,-10 l 0,10 m 3,-10 l 0,10',
19279 height: 10,
19280 width: 10,
19281 heightElements: [],
19282 widthElements: []
19283 },
19284 'MARKER_SEQUENTIAL': {
19285 d: 'm{mx},{my} m 0,3 l 10,0 m -10,3 l 10,0 m -10,3 l 10,0',
19286 height: 10,
19287 width: 10,
19288 heightElements: [],
19289 widthElements: []
19290 },
19291 'MARKER_COMPENSATION': {
19292 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',
19293 height: 10,
19294 width: 21,
19295 heightElements: [],
19296 widthElements: []
19297 },
19298 'MARKER_LOOP': {
19299 d: 'm {mx},{my} c 3.526979,0 6.386161,-2.829858 6.386161,-6.320661 0,-3.490806 -2.859182,-6.320661 ' +
19300 '-6.386161,-6.320661 -3.526978,0 -6.38616,2.829855 -6.38616,6.320661 0,1.745402 ' +
19301 '0.714797,3.325567 1.870463,4.469381 0.577834,0.571908 1.265885,1.034728 2.029916,1.35457 ' +
19302 'l -0.718163,-3.909793 m 0.718163,3.909793 -3.885211,0.802902',
19303 height: 13.9,
19304 width: 13.7,
19305 heightElements: [],
19306 widthElements: []
19307 },
19308 'MARKER_ADHOC': {
19309 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 ' +
19310 '3.85579,1.15803 5.76082,1.79107 1.06385,0.34139996 2.24454,0.1438 3.18759,-0.43767 0.61743,-0.33642 ' +
19311 '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 ' +
19312 '-3.6918,1.66181996 -1.24459,0.0927 -2.46671,-0.2491 -3.59505,-0.74812 -1.35789,-0.55965 ' +
19313 '-2.75133,-1.33436996 -4.27027,-1.18121996 -1.37741,0.14601 -2.41842,1.13685996 -3.44288,1.96782996 z',
19314 height: 4,
19315 width: 15,
19316 heightElements: [],
19317 widthElements: []
19318 },
19319 'TASK_TYPE_SEND': {
19320 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}',
19321 height: 14,
19322 width: 21,
19323 heightElements: [6, 14],
19324 widthElements: [10.5, 21]
19325 },
19326 'TASK_TYPE_SCRIPT': {
19327 d: 'm {mx},{my} c 9.966553,-6.27276 -8.000926,-7.91932 2.968968,-14.938 l -8.802728,0 ' +
19328 'c -10.969894,7.01868 6.997585,8.66524 -2.968967,14.938 z ' +
19329 'm -7,-12 l 5,0 ' +
19330 'm -4.5,3 l 4.5,0 ' +
19331 'm -3,3 l 5,0' +
19332 'm -4,3 l 5,0',
19333 height: 15,
19334 width: 12.6,
19335 heightElements: [6, 14],
19336 widthElements: [10.5, 21]
19337 },
19338 'TASK_TYPE_USER_1': {
19339 d: 'm {mx},{my} c 0.909,-0.845 1.594,-2.049 1.594,-3.385 0,-2.554 -1.805,-4.62199999 ' +
19340 '-4.357,-4.62199999 -2.55199998,0 -4.28799998,2.06799999 -4.28799998,4.62199999 0,1.348 ' +
19341 '0.974,2.562 1.89599998,3.405 -0.52899998,0.187 -5.669,2.097 -5.794,4.7560005 v 6.718 ' +
19342 'h 17 v -6.718 c 0,-2.2980005 -5.5279996,-4.5950005 -6.0509996,-4.7760005 z' +
19343 'm -8,6 l 0,5.5 m 11,0 l 0,-5'
19344 },
19345 'TASK_TYPE_USER_2': {
19346 d: 'm {mx},{my} m 2.162,1.009 c 0,2.4470005 -2.158,4.4310005 -4.821,4.4310005 ' +
19347 '-2.66499998,0 -4.822,-1.981 -4.822,-4.4310005 '
19348 },
19349 'TASK_TYPE_USER_3': {
19350 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 ' +
19351 '4.124,0.965 -0.098,-0.57 -0.117,-3.79099999 -4.191,-4.13599999 -3.57499998,0.001 ' +
19352 '-4.20799998,3.36699999 -4.20699998,4.34799999 z'
19353 },
19354 'TASK_TYPE_MANUAL': {
19355 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 ' +
19356 '-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 ' +
19357 '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 ' +
19358 '-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 ' +
19359 '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 ' +
19360 '-10.86,-0.003 -11.0829995,-0.003 -0.022,-0.047 -0.045,-0.094 -0.069,-0.139 0.3939995,-0.319 ' +
19361 '2.0409995,-1.626 2.4149995,-2.017 0.469,-0.4870005 0.519,-1.1650005 0.162,-1.6040005 -0.414,-0.511 ' +
19362 '-0.973,-0.5 -1.48,-0.236 -1.4609995,0.764 -6.5999995,3.6430005 -7.7329995,4.2710005 -0.9,0.499 ' +
19363 '-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 ' +
19364 '-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 ' +
19365 '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 ' +
19366 '-5.824,-0.004 -6.04,-0.004 10e-4,-0.084 0.003,-0.586 10e-4,-0.67 z'
19367 },
19368 'TASK_TYPE_INSTANTIATING_SEND': {
19369 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'
19370 },
19371 'TASK_TYPE_SERVICE': {
19372 d: 'm {mx},{my} v -1.71335 c 0.352326,-0.0705 0.703932,-0.17838 1.047628,-0.32133 ' +
19373 '0.344416,-0.14465 0.665822,-0.32133 0.966377,-0.52145 l 1.19431,1.18005 1.567487,-1.57688 ' +
19374 '-1.195028,-1.18014 c 0.403376,-0.61394 0.683079,-1.29908 0.825447,-2.01824 l 1.622133,-0.01 ' +
19375 'v -2.2196 l -1.636514,0.01 c -0.07333,-0.35153 -0.178319,-0.70024 -0.323564,-1.04372 ' +
19376 '-0.145244,-0.34406 -0.321407,-0.6644 -0.522735,-0.96217 l 1.131035,-1.13631 -1.583305,-1.56293 ' +
19377 '-1.129598,1.13589 c -0.614052,-0.40108 -1.302883,-0.68093 -2.022633,-0.82247 l 0.0093,-1.61852 ' +
19378 '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 ' +
19379 '-0.665102,0.32092 -0.9635006,0.52046 l -1.1698628,-1.15823 -1.5667691,1.5792 1.1684265,1.15669 ' +
19380 'c -0.4026573,0.61283 -0.68308,1.29797 -0.8247287,2.01713 l -1.6588041,0.003 v 2.22174 ' +
19381 'l 1.6724648,-0.006 c 0.073327,0.35077 0.1797598,0.70243 0.3242851,1.04472 0.1452428,0.34448 ' +
19382 '0.3214064,0.6644 0.5227339,0.96066 l -1.1993431,1.19723 1.5840256,1.56011 1.1964668,-1.19348 ' +
19383 'c 0.6140517,0.40346 1.3028827,0.68232 2.0233517,0.82331 l 7.19e-4,1.69892 h 2.226848 z ' +
19384 'm 0.221462,-3.9957 c -1.788948,0.7502 -3.8576,-0.0928 -4.6097055,-1.87438 -0.7521065,-1.78321 ' +
19385 '0.090598,-3.84627 1.8802645,-4.59604 1.78823,-0.74936 3.856881,0.0929 4.608987,1.87437 ' +
19386 '0.752106,1.78165 -0.0906,3.84612 -1.879546,4.59605 z'
19387 },
19388 'TASK_TYPE_SERVICE_FILL': {
19389 d: 'm {mx},{my} c -1.788948,0.7502 -3.8576,-0.0928 -4.6097055,-1.87438 -0.7521065,-1.78321 ' +
19390 '0.090598,-3.84627 1.8802645,-4.59604 1.78823,-0.74936 3.856881,0.0929 4.608987,1.87437 ' +
19391 '0.752106,1.78165 -0.0906,3.84612 -1.879546,4.59605 z'
19392 },
19393 'TASK_TYPE_BUSINESS_RULE_HEADER': {
19394 d: 'm {mx},{my} 0,4 20,0 0,-4 z'
19395 },
19396 'TASK_TYPE_BUSINESS_RULE_MAIN': {
19397 d: 'm {mx},{my} 0,12 20,0 0,-12 z' +
19398 'm 0,8 l 20,0 ' +
19399 'm -13,-4 l 0,8'
19400 },
19401 'MESSAGE_FLOW_MARKER': {
19402 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'
19403 }
19404 };
19405
19406 this.getRawPath = function getRawPath(pathId) {
19407 return this.pathMap[pathId].d;
19408 };
19409
19410 /**
19411 * Scales the path to the given height and width.
19412 * <h1>Use case</h1>
19413 * <p>Use case is to scale the content of elements (event, gateways) based
19414 * on the element bounding box's size.
19415 * </p>
19416 * <h1>Why not transform</h1>
19417 * <p>Scaling a path with transform() will also scale the stroke and IE does not support
19418 * the option 'non-scaling-stroke' to prevent this.
19419 * Also there are use cases where only some parts of a path should be
19420 * scaled.</p>
19421 *
19422 * @param {string} pathId The ID of the path.
19423 * @param {Object} param <p>
19424 * Example param object scales the path to 60% size of the container (data.width, data.height).
19425 * <pre>
19426 * {
19427 * xScaleFactor: 0.6,
19428 * yScaleFactor:0.6,
19429 * containerWidth: data.width,
19430 * containerHeight: data.height,
19431 * position: {
19432 * mx: 0.46,
19433 * my: 0.2,
19434 * }
19435 * }
19436 * </pre>
19437 * <ul>
19438 * <li>targetpathwidth = xScaleFactor * containerWidth</li>
19439 * <li>targetpathheight = yScaleFactor * containerHeight</li>
19440 * <li>Position is used to set the starting coordinate of the path. M is computed:
19441 * <ul>
19442 * <li>position.x * containerWidth</li>
19443 * <li>position.y * containerHeight</li>
19444 * </ul>
19445 * Center of the container <pre> position: {
19446 * mx: 0.5,
19447 * my: 0.5,
19448 * }</pre>
19449 * Upper left corner of the container
19450 * <pre> position: {
19451 * mx: 0.0,
19452 * my: 0.0,
19453 * }</pre>
19454 * </li>
19455 * </ul>
19456 * </p>
19457 *
19458 */
19459 this.getScaledPath = function getScaledPath(pathId, param) {
19460 var rawPath = this.pathMap[pathId];
19461
19462 // positioning
19463 // compute the start point of the path
19464 var mx, my;
19465
19466 if (param.abspos) {
19467 mx = param.abspos.x;
19468 my = param.abspos.y;
19469 } else {
19470 mx = param.containerWidth * param.position.mx;
19471 my = param.containerHeight * param.position.my;
19472 }
19473
19474 var coordinates = {}; // map for the scaled coordinates
19475 if (param.position) {
19476
19477 // path
19478 var heightRatio = (param.containerHeight / rawPath.height) * param.yScaleFactor;
19479 var widthRatio = (param.containerWidth / rawPath.width) * param.xScaleFactor;
19480
19481
19482 // Apply height ratio
19483 for (var heightIndex = 0; heightIndex < rawPath.heightElements.length; heightIndex++) {
19484 coordinates['y' + heightIndex] = rawPath.heightElements[heightIndex] * heightRatio;
19485 }
19486
19487 // Apply width ratio
19488 for (var widthIndex = 0; widthIndex < rawPath.widthElements.length; widthIndex++) {
19489 coordinates['x' + widthIndex] = rawPath.widthElements[widthIndex] * widthRatio;
19490 }
19491 }
19492
19493 // Apply value to raw path
19494 var path = format(
19495 rawPath.d, {
19496 mx: mx,
19497 my: my,
19498 e: coordinates
19499 }
19500 );
19501 return path;
19502 };
19503 }
19504
19505 // helpers //////////////////////
19506
19507 // copied and adjusted from https://github.com/adobe-webplatform/Snap.svg/blob/master/src/svg.js
19508 var tokenRegex = /\{([^{}]+)\}/g,
19509 objNotationRegex = /(?:(?:^|\.)(.+?)(?=\[|\.|$|\()|\[('|")(.+?)\2\])(\(\))?/g; // matches .xxxxx or ["xxxxx"] to run over object properties
19510
19511 function replacer(all, key, obj) {
19512 var res = obj;
19513 key.replace(objNotationRegex, function(all, name, quote, quotedName, isFunc) {
19514 name = name || quotedName;
19515 if (res) {
19516 if (name in res) {
19517 res = res[name];
19518 }
19519 typeof res == 'function' && isFunc && (res = res());
19520 }
19521 });
19522 res = (res == null || res == obj ? all : res) + '';
19523
19524 return res;
19525 }
19526
19527 function format(str, obj) {
19528 return String(str).replace(tokenRegex, function(all, key) {
19529 return replacer(all, key, obj);
19530 });
19531 }
19532
19533 var DrawModule = {
19534 __init__: [ 'bpmnRenderer' ],
19535 bpmnRenderer: [ 'type', BpmnRenderer ],
19536 textRenderer: [ 'type', TextRenderer ],
19537 pathMap: [ 'type', PathMap ]
19538 };
19539
19540 /**
19541 * A simple translation stub to be used for multi-language support
19542 * in diagrams. Can be easily replaced with a more sophisticated
19543 * solution.
19544 *
19545 * @example
19546 *
19547 * // use it inside any diagram component by injecting `translate`.
19548 *
19549 * function MyService(translate) {
19550 * alert(translate('HELLO {you}', { you: 'You!' }));
19551 * }
19552 *
19553 * @param {string} template to interpolate
19554 * @param {Object} [replacements] a map with substitutes
19555 *
19556 * @return {string} the translated string
19557 */
19558 function translate$1(template, replacements) {
19559
19560 replacements = replacements || {};
19561
19562 return template.replace(/{([^}]+)}/g, function(_, key) {
19563 return replacements[key] || '{' + key + '}';
19564 });
19565 }
19566
19567 var translate = {
19568 translate: [ 'value', translate$1 ]
19569 };
19570
19571 var DEFAULT_LABEL_SIZE = {
19572 width: 90,
19573 height: 20
19574 };
19575
19576 var FLOW_LABEL_INDENT = 15;
19577
19578
19579 /**
19580 * Returns true if the given semantic has an external label
19581 *
19582 * @param {BpmnElement} semantic
19583 * @return {boolean} true if has label
19584 */
19585 function isLabelExternal(semantic) {
19586 return is$1(semantic, 'bpmn:Event') ||
19587 is$1(semantic, 'bpmn:Gateway') ||
19588 is$1(semantic, 'bpmn:DataStoreReference') ||
19589 is$1(semantic, 'bpmn:DataObjectReference') ||
19590 is$1(semantic, 'bpmn:DataInput') ||
19591 is$1(semantic, 'bpmn:DataOutput') ||
19592 is$1(semantic, 'bpmn:SequenceFlow') ||
19593 is$1(semantic, 'bpmn:MessageFlow') ||
19594 is$1(semantic, 'bpmn:Group');
19595 }
19596
19597 /**
19598 * Returns true if the given element has an external label
19599 *
19600 * @param {djs.model.shape} element
19601 * @return {boolean} true if has label
19602 */
19603 function hasExternalLabel(element) {
19604 return isLabel$6(element.label);
19605 }
19606
19607 /**
19608 * Get the position for sequence flow labels
19609 *
19610 * @param {Array<Point>} waypoints
19611 * @return {Point} the label position
19612 */
19613 function getFlowLabelPosition(waypoints) {
19614
19615 // get the waypoints mid
19616 var mid = waypoints.length / 2 - 1;
19617
19618 var first = waypoints[Math.floor(mid)];
19619 var second = waypoints[Math.ceil(mid + 0.01)];
19620
19621 // get position
19622 var position = getWaypointsMid(waypoints);
19623
19624 // calculate angle
19625 var angle = Math.atan((second.y - first.y) / (second.x - first.x));
19626
19627 var x = position.x,
19628 y = position.y;
19629
19630 if (Math.abs(angle) < Math.PI / 2) {
19631 y -= FLOW_LABEL_INDENT;
19632 } else {
19633 x += FLOW_LABEL_INDENT;
19634 }
19635
19636 return { x: x, y: y };
19637 }
19638
19639
19640 /**
19641 * Get the middle of a number of waypoints
19642 *
19643 * @param {Array<Point>} waypoints
19644 * @return {Point} the mid point
19645 */
19646 function getWaypointsMid(waypoints) {
19647
19648 var mid = waypoints.length / 2 - 1;
19649
19650 var first = waypoints[Math.floor(mid)];
19651 var second = waypoints[Math.ceil(mid + 0.01)];
19652
19653 return {
19654 x: first.x + (second.x - first.x) / 2,
19655 y: first.y + (second.y - first.y) / 2
19656 };
19657 }
19658
19659
19660 function getExternalLabelMid(element) {
19661
19662 if (element.waypoints) {
19663 return getFlowLabelPosition(element.waypoints);
19664 } else if (is$1(element, 'bpmn:Group')) {
19665 return {
19666 x: element.x + element.width / 2,
19667 y: element.y + DEFAULT_LABEL_SIZE.height / 2
19668 };
19669 } else {
19670 return {
19671 x: element.x + element.width / 2,
19672 y: element.y + element.height + DEFAULT_LABEL_SIZE.height / 2
19673 };
19674 }
19675 }
19676
19677
19678 /**
19679 * Returns the bounds of an elements label, parsed from the elements DI or
19680 * generated from its bounds.
19681 *
19682 * @param {BpmnElement} semantic
19683 * @param {djs.model.Base} element
19684 */
19685 function getExternalLabelBounds(semantic, element) {
19686
19687 var mid,
19688 size,
19689 bounds,
19690 di = semantic.di,
19691 label = di.label;
19692
19693 if (label && label.bounds) {
19694 bounds = label.bounds;
19695
19696 size = {
19697 width: Math.max(DEFAULT_LABEL_SIZE.width, bounds.width),
19698 height: bounds.height
19699 };
19700
19701 mid = {
19702 x: bounds.x + bounds.width / 2,
19703 y: bounds.y + bounds.height / 2
19704 };
19705 } else {
19706
19707 mid = getExternalLabelMid(element);
19708
19709 size = DEFAULT_LABEL_SIZE;
19710 }
19711
19712 return assign({
19713 x: mid.x - size.width / 2,
19714 y: mid.y - size.height / 2
19715 }, size);
19716 }
19717
19718 function isLabel$6(element) {
19719 return element && !!element.labelTarget;
19720 }
19721
19722 function elementData(semantic, attrs) {
19723 return assign({
19724 id: semantic.id,
19725 type: semantic.$type,
19726 businessObject: semantic
19727 }, attrs);
19728 }
19729
19730 function getWaypoints(bo, source, target) {
19731
19732 var waypoints = bo.di.waypoint;
19733
19734 if (!waypoints || waypoints.length < 2) {
19735 return [ getMid(source), getMid(target) ];
19736 }
19737
19738 return waypoints.map(function(p) {
19739 return { x: p.x, y: p.y };
19740 });
19741 }
19742
19743 function notYetDrawn(translate, semantic, refSemantic, property) {
19744 return new Error(translate('element {element} referenced by {referenced}#{property} not yet drawn', {
19745 element: elementToString(refSemantic),
19746 referenced: elementToString(semantic),
19747 property: property
19748 }));
19749 }
19750
19751
19752 /**
19753 * An importer that adds bpmn elements to the canvas
19754 *
19755 * @param {EventBus} eventBus
19756 * @param {Canvas} canvas
19757 * @param {ElementFactory} elementFactory
19758 * @param {ElementRegistry} elementRegistry
19759 * @param {Function} translate
19760 * @param {TextRenderer} textRenderer
19761 */
19762 function BpmnImporter(
19763 eventBus, canvas, elementFactory,
19764 elementRegistry, translate, textRenderer) {
19765
19766 this._eventBus = eventBus;
19767 this._canvas = canvas;
19768 this._elementFactory = elementFactory;
19769 this._elementRegistry = elementRegistry;
19770 this._translate = translate;
19771 this._textRenderer = textRenderer;
19772 }
19773
19774 BpmnImporter.$inject = [
19775 'eventBus',
19776 'canvas',
19777 'elementFactory',
19778 'elementRegistry',
19779 'translate',
19780 'textRenderer'
19781 ];
19782
19783
19784 /**
19785 * Add bpmn element (semantic) to the canvas onto the
19786 * specified parent shape.
19787 */
19788 BpmnImporter.prototype.add = function(semantic, parentElement) {
19789
19790 var di = semantic.di,
19791 element,
19792 translate = this._translate,
19793 hidden;
19794
19795 var parentIndex;
19796
19797 // ROOT ELEMENT
19798 // handle the special case that we deal with a
19799 // invisible root element (process or collaboration)
19800 if (is$1(di, 'bpmndi:BPMNPlane')) {
19801
19802 // add a virtual element (not being drawn)
19803 element = this._elementFactory.createRoot(elementData(semantic));
19804
19805 this._canvas.setRootElement(element);
19806 }
19807
19808 // SHAPE
19809 else if (is$1(di, 'bpmndi:BPMNShape')) {
19810
19811 var collapsed = !isExpanded(semantic),
19812 isFrame = isFrameElement(semantic);
19813 hidden = parentElement && (parentElement.hidden || parentElement.collapsed);
19814
19815 var bounds = semantic.di.bounds;
19816
19817 element = this._elementFactory.createShape(elementData(semantic, {
19818 collapsed: collapsed,
19819 hidden: hidden,
19820 x: Math.round(bounds.x),
19821 y: Math.round(bounds.y),
19822 width: Math.round(bounds.width),
19823 height: Math.round(bounds.height),
19824 isFrame: isFrame
19825 }));
19826
19827 if (is$1(semantic, 'bpmn:BoundaryEvent')) {
19828 this._attachBoundary(semantic, element);
19829 }
19830
19831 // insert lanes behind other flow nodes (cf. #727)
19832 if (is$1(semantic, 'bpmn:Lane')) {
19833 parentIndex = 0;
19834 }
19835
19836 if (is$1(semantic, 'bpmn:DataStoreReference')) {
19837
19838 // check whether data store is inside our outside of its semantic parent
19839 if (!isPointInsideBBox$1(parentElement, getMid(bounds))) {
19840 parentElement = this._canvas.getRootElement();
19841 }
19842 }
19843
19844 this._canvas.addShape(element, parentElement, parentIndex);
19845 }
19846
19847 // CONNECTION
19848 else if (is$1(di, 'bpmndi:BPMNEdge')) {
19849
19850 var source = this._getSource(semantic),
19851 target = this._getTarget(semantic);
19852
19853 hidden = parentElement && (parentElement.hidden || parentElement.collapsed);
19854
19855 element = this._elementFactory.createConnection(elementData(semantic, {
19856 hidden: hidden,
19857 source: source,
19858 target: target,
19859 waypoints: getWaypoints(semantic, source, target)
19860 }));
19861
19862 if (is$1(semantic, 'bpmn:DataAssociation')) {
19863
19864 // render always on top; this ensures DataAssociations
19865 // are rendered correctly across different "hacks" people
19866 // love to model such as cross participant / sub process
19867 // associations
19868 parentElement = null;
19869 }
19870
19871 // insert sequence flows behind other flow nodes (cf. #727)
19872 if (is$1(semantic, 'bpmn:SequenceFlow')) {
19873 parentIndex = 0;
19874 }
19875
19876 this._canvas.addConnection(element, parentElement, parentIndex);
19877 } else {
19878 throw new Error(translate('unknown di {di} for element {semantic}', {
19879 di: elementToString(di),
19880 semantic: elementToString(semantic)
19881 }));
19882 }
19883
19884 // (optional) LABEL
19885 if (isLabelExternal(semantic) && getLabel(element)) {
19886 this.addLabel(semantic, element);
19887 }
19888
19889
19890 this._eventBus.fire('bpmnElement.added', { element: element });
19891
19892 return element;
19893 };
19894
19895
19896 /**
19897 * Attach the boundary element to the given host
19898 *
19899 * @param {ModdleElement} boundarySemantic
19900 * @param {djs.model.Base} boundaryElement
19901 */
19902 BpmnImporter.prototype._attachBoundary = function(boundarySemantic, boundaryElement) {
19903 var translate = this._translate;
19904 var hostSemantic = boundarySemantic.attachedToRef;
19905
19906 if (!hostSemantic) {
19907 throw new Error(translate('missing {semantic}#attachedToRef', {
19908 semantic: elementToString(boundarySemantic)
19909 }));
19910 }
19911
19912 var host = this._elementRegistry.get(hostSemantic.id),
19913 attachers = host && host.attachers;
19914
19915 if (!host) {
19916 throw notYetDrawn(translate, boundarySemantic, hostSemantic, 'attachedToRef');
19917 }
19918
19919 // wire element.host <> host.attachers
19920 boundaryElement.host = host;
19921
19922 if (!attachers) {
19923 host.attachers = attachers = [];
19924 }
19925
19926 if (attachers.indexOf(boundaryElement) === -1) {
19927 attachers.push(boundaryElement);
19928 }
19929 };
19930
19931
19932 /**
19933 * add label for an element
19934 */
19935 BpmnImporter.prototype.addLabel = function(semantic, element) {
19936 var bounds,
19937 text,
19938 label;
19939
19940 bounds = getExternalLabelBounds(semantic, element);
19941
19942 text = getLabel(element);
19943
19944 if (text) {
19945
19946 // get corrected bounds from actual layouted text
19947 bounds = this._textRenderer.getExternalLabelBounds(bounds, text);
19948 }
19949
19950 label = this._elementFactory.createLabel(elementData(semantic, {
19951 id: semantic.id + '_label',
19952 labelTarget: element,
19953 type: 'label',
19954 hidden: element.hidden || !getLabel(element),
19955 x: Math.round(bounds.x),
19956 y: Math.round(bounds.y),
19957 width: Math.round(bounds.width),
19958 height: Math.round(bounds.height)
19959 }));
19960
19961 return this._canvas.addShape(label, element.parent);
19962 };
19963
19964 /**
19965 * Return the drawn connection end based on the given side.
19966 *
19967 * @throws {Error} if the end is not yet drawn
19968 */
19969 BpmnImporter.prototype._getEnd = function(semantic, side) {
19970
19971 var element,
19972 refSemantic,
19973 type = semantic.$type,
19974 translate = this._translate;
19975
19976 refSemantic = semantic[side + 'Ref'];
19977
19978 // handle mysterious isMany DataAssociation#sourceRef
19979 if (side === 'source' && type === 'bpmn:DataInputAssociation') {
19980 refSemantic = refSemantic && refSemantic[0];
19981 }
19982
19983 // fix source / target for DataInputAssociation / DataOutputAssociation
19984 if (side === 'source' && type === 'bpmn:DataOutputAssociation' ||
19985 side === 'target' && type === 'bpmn:DataInputAssociation') {
19986
19987 refSemantic = semantic.$parent;
19988 }
19989
19990 element = refSemantic && this._getElement(refSemantic);
19991
19992 if (element) {
19993 return element;
19994 }
19995
19996 if (refSemantic) {
19997 throw notYetDrawn(translate, semantic, refSemantic, side + 'Ref');
19998 } else {
19999 throw new Error(translate('{semantic}#{side} Ref not specified', {
20000 semantic: elementToString(semantic),
20001 side: side
20002 }));
20003 }
20004 };
20005
20006 BpmnImporter.prototype._getSource = function(semantic) {
20007 return this._getEnd(semantic, 'source');
20008 };
20009
20010 BpmnImporter.prototype._getTarget = function(semantic) {
20011 return this._getEnd(semantic, 'target');
20012 };
20013
20014
20015 BpmnImporter.prototype._getElement = function(semantic) {
20016 return this._elementRegistry.get(semantic.id);
20017 };
20018
20019
20020 // helpers ////////////////////
20021
20022 function isPointInsideBBox$1(bbox, point) {
20023 var x = point.x,
20024 y = point.y;
20025
20026 return x >= bbox.x &&
20027 x <= bbox.x + bbox.width &&
20028 y >= bbox.y &&
20029 y <= bbox.y + bbox.height;
20030 }
20031
20032 function isFrameElement(semantic) {
20033 return is$1(semantic, 'bpmn:Group');
20034 }
20035
20036 var ImportModule = {
20037 __depends__: [
20038 translate
20039 ],
20040 bpmnImporter: [ 'type', BpmnImporter ]
20041 };
20042
20043 var CoreModule = {
20044 __depends__: [
20045 DrawModule,
20046 ImportModule
20047 ]
20048 };
20049
20050 function __stopPropagation(event) {
20051 if (!event || typeof event.stopPropagation !== 'function') {
20052 return;
20053 }
20054
20055 event.stopPropagation();
20056 }
20057
20058
20059 function getOriginal$1(event) {
20060 return event.originalEvent || event.srcEvent;
20061 }
20062
20063
20064 function stopPropagation$1(event, immediate) {
20065 __stopPropagation(event);
20066 __stopPropagation(getOriginal$1(event));
20067 }
20068
20069
20070 function toPoint(event) {
20071
20072 if (event.pointers && event.pointers.length) {
20073 event = event.pointers[0];
20074 }
20075
20076 if (event.touches && event.touches.length) {
20077 event = event.touches[0];
20078 }
20079
20080 return event ? {
20081 x: event.clientX,
20082 y: event.clientY
20083 } : null;
20084 }
20085
20086 function isMac() {
20087 return (/mac/i).test(navigator.platform);
20088 }
20089
20090 function isButton(event, button) {
20091 return (getOriginal$1(event) || event).button === button;
20092 }
20093
20094 function isPrimaryButton(event) {
20095
20096 // button === 0 -> left áka primary mouse button
20097 return isButton(event, 0);
20098 }
20099
20100 function isAuxiliaryButton(event) {
20101
20102 // button === 1 -> auxiliary áka wheel button
20103 return isButton(event, 1);
20104 }
20105
20106 function hasPrimaryModifier(event) {
20107 var originalEvent = getOriginal$1(event) || event;
20108
20109 if (!isPrimaryButton(event)) {
20110 return false;
20111 }
20112
20113 // Use cmd as primary modifier key for mac OS
20114 if (isMac()) {
20115 return originalEvent.metaKey;
20116 } else {
20117 return originalEvent.ctrlKey;
20118 }
20119 }
20120
20121
20122 function hasSecondaryModifier(event) {
20123 var originalEvent = getOriginal$1(event) || event;
20124
20125 return isPrimaryButton(event) && originalEvent.shiftKey;
20126 }
20127
20128 function allowAll(event) { return true; }
20129
20130 function allowPrimaryAndAuxiliary(event) {
20131 return isPrimaryButton(event) || isAuxiliaryButton(event);
20132 }
20133
20134 var LOW_PRIORITY$m = 500;
20135
20136
20137 /**
20138 * A plugin that provides interaction events for diagram elements.
20139 *
20140 * It emits the following events:
20141 *
20142 * * element.click
20143 * * element.contextmenu
20144 * * element.dblclick
20145 * * element.hover
20146 * * element.mousedown
20147 * * element.mousemove
20148 * * element.mouseup
20149 * * element.out
20150 *
20151 * Each event is a tuple { element, gfx, originalEvent }.
20152 *
20153 * Canceling the event via Event#preventDefault()
20154 * prevents the original DOM operation.
20155 *
20156 * @param {EventBus} eventBus
20157 */
20158 function InteractionEvents(eventBus, elementRegistry, styles) {
20159
20160 var self = this;
20161
20162 /**
20163 * Fire an interaction event.
20164 *
20165 * @param {string} type local event name, e.g. element.click.
20166 * @param {DOMEvent} event native event
20167 * @param {djs.model.Base} [element] the diagram element to emit the event on;
20168 * defaults to the event target
20169 */
20170 function fire(type, event, element) {
20171
20172 if (isIgnored(type, event)) {
20173 return;
20174 }
20175
20176 var target, gfx, returnValue;
20177
20178 if (!element) {
20179 target = event.delegateTarget || event.target;
20180
20181 if (target) {
20182 gfx = target;
20183 element = elementRegistry.get(gfx);
20184 }
20185 } else {
20186 gfx = elementRegistry.getGraphics(element);
20187 }
20188
20189 if (!gfx || !element) {
20190 return;
20191 }
20192
20193 returnValue = eventBus.fire(type, {
20194 element: element,
20195 gfx: gfx,
20196 originalEvent: event
20197 });
20198
20199 if (returnValue === false) {
20200 event.stopPropagation();
20201 event.preventDefault();
20202 }
20203 }
20204
20205 // TODO(nikku): document this
20206 var handlers = {};
20207
20208 function mouseHandler(localEventName) {
20209 return handlers[localEventName];
20210 }
20211
20212 function isIgnored(localEventName, event) {
20213
20214 var filter = ignoredFilters[localEventName] || isPrimaryButton;
20215
20216 // only react on left mouse button interactions
20217 // except for interaction events that are enabled
20218 // for secundary mouse button
20219 return !filter(event);
20220 }
20221
20222 var bindings = {
20223 click: 'element.click',
20224 contextmenu: 'element.contextmenu',
20225 dblclick: 'element.dblclick',
20226 mousedown: 'element.mousedown',
20227 mousemove: 'element.mousemove',
20228 mouseover: 'element.hover',
20229 mouseout: 'element.out',
20230 mouseup: 'element.mouseup',
20231 };
20232
20233 var ignoredFilters = {
20234 'element.contextmenu': allowAll,
20235 'element.mousedown': allowPrimaryAndAuxiliary,
20236 'element.mouseup': allowPrimaryAndAuxiliary,
20237 'element.click': allowPrimaryAndAuxiliary,
20238 'element.dblclick': allowPrimaryAndAuxiliary
20239 };
20240
20241
20242 // manual event trigger //////////
20243
20244 /**
20245 * Trigger an interaction event (based on a native dom event)
20246 * on the target shape or connection.
20247 *
20248 * @param {string} eventName the name of the triggered DOM event
20249 * @param {MouseEvent} event
20250 * @param {djs.model.Base} targetElement
20251 */
20252 function triggerMouseEvent(eventName, event, targetElement) {
20253
20254 // i.e. element.mousedown...
20255 var localEventName = bindings[eventName];
20256
20257 if (!localEventName) {
20258 throw new Error('unmapped DOM event name <' + eventName + '>');
20259 }
20260
20261 return fire(localEventName, event, targetElement);
20262 }
20263
20264
20265 var ELEMENT_SELECTOR = 'svg, .djs-element';
20266
20267 // event handling ///////
20268
20269 function registerEvent(node, event, localEvent, ignoredFilter) {
20270
20271 var handler = handlers[localEvent] = function(event) {
20272 fire(localEvent, event);
20273 };
20274
20275 if (ignoredFilter) {
20276 ignoredFilters[localEvent] = ignoredFilter;
20277 }
20278
20279 handler.$delegate = delegate.bind(node, ELEMENT_SELECTOR, event, handler);
20280 }
20281
20282 function unregisterEvent(node, event, localEvent) {
20283
20284 var handler = mouseHandler(localEvent);
20285
20286 if (!handler) {
20287 return;
20288 }
20289
20290 delegate.unbind(node, event, handler.$delegate);
20291 }
20292
20293 function registerEvents(svg) {
20294 forEach(bindings, function(val, key) {
20295 registerEvent(svg, key, val);
20296 });
20297 }
20298
20299 function unregisterEvents(svg) {
20300 forEach(bindings, function(val, key) {
20301 unregisterEvent(svg, key, val);
20302 });
20303 }
20304
20305 eventBus.on('canvas.destroy', function(event) {
20306 unregisterEvents(event.svg);
20307 });
20308
20309 eventBus.on('canvas.init', function(event) {
20310 registerEvents(event.svg);
20311 });
20312
20313
20314 // hit box updating ////////////////
20315
20316 eventBus.on([ 'shape.added', 'connection.added' ], function(event) {
20317 var element = event.element,
20318 gfx = event.gfx;
20319
20320 eventBus.fire('interactionEvents.createHit', { element: element, gfx: gfx });
20321 });
20322
20323 // Update djs-hit on change.
20324 // A low priortity is necessary, because djs-hit of labels has to be updated
20325 // after the label bounds have been updated in the renderer.
20326 eventBus.on([
20327 'shape.changed',
20328 'connection.changed'
20329 ], LOW_PRIORITY$m, function(event) {
20330
20331 var element = event.element,
20332 gfx = event.gfx;
20333
20334 eventBus.fire('interactionEvents.updateHit', { element: element, gfx: gfx });
20335 });
20336
20337 eventBus.on('interactionEvents.createHit', LOW_PRIORITY$m, function(event) {
20338 var element = event.element,
20339 gfx = event.gfx;
20340
20341 self.createDefaultHit(element, gfx);
20342 });
20343
20344 eventBus.on('interactionEvents.updateHit', function(event) {
20345 var element = event.element,
20346 gfx = event.gfx;
20347
20348 self.updateDefaultHit(element, gfx);
20349 });
20350
20351
20352 // hit styles ////////////
20353
20354 var STROKE_HIT_STYLE = createHitStyle('djs-hit djs-hit-stroke');
20355
20356 var CLICK_STROKE_HIT_STYLE = createHitStyle('djs-hit djs-hit-click-stroke');
20357
20358 var ALL_HIT_STYLE = createHitStyle('djs-hit djs-hit-all');
20359
20360 var HIT_TYPES = {
20361 'all': ALL_HIT_STYLE,
20362 'click-stroke': CLICK_STROKE_HIT_STYLE,
20363 'stroke': STROKE_HIT_STYLE
20364 };
20365
20366 function createHitStyle(classNames, attrs) {
20367
20368 attrs = assign({
20369 stroke: 'white',
20370 strokeWidth: 15
20371 }, attrs || {});
20372
20373 return styles.cls(classNames, [ 'no-fill', 'no-border' ], attrs);
20374 }
20375
20376
20377 // style helpers ///////////////
20378
20379 function applyStyle(hit, type) {
20380
20381 var attrs = HIT_TYPES[type];
20382
20383 if (!attrs) {
20384 throw new Error('invalid hit type <' + type + '>');
20385 }
20386
20387 attr(hit, attrs);
20388
20389 return hit;
20390 }
20391
20392 function appendHit(gfx, hit) {
20393 append(gfx, hit);
20394 }
20395
20396
20397 // API
20398
20399 /**
20400 * Remove hints on the given graphics.
20401 *
20402 * @param {SVGElement} gfx
20403 */
20404 this.removeHits = function(gfx) {
20405 var hits = all('.djs-hit', gfx);
20406
20407 forEach(hits, remove$1);
20408 };
20409
20410 /**
20411 * Create default hit for the given element.
20412 *
20413 * @param {djs.model.Base} element
20414 * @param {SVGElement} gfx
20415 *
20416 * @return {SVGElement} created hit
20417 */
20418 this.createDefaultHit = function(element, gfx) {
20419 var waypoints = element.waypoints,
20420 isFrame = element.isFrame,
20421 boxType;
20422
20423 if (waypoints) {
20424 return this.createWaypointsHit(gfx, waypoints);
20425 } else {
20426
20427 boxType = isFrame ? 'stroke' : 'all';
20428
20429 return this.createBoxHit(gfx, boxType, {
20430 width: element.width,
20431 height: element.height
20432 });
20433 }
20434 };
20435
20436 /**
20437 * Create hits for the given waypoints.
20438 *
20439 * @param {SVGElement} gfx
20440 * @param {Array<Point>} waypoints
20441 *
20442 * @return {SVGElement}
20443 */
20444 this.createWaypointsHit = function(gfx, waypoints) {
20445
20446 var hit = createLine(waypoints);
20447
20448 applyStyle(hit, 'stroke');
20449
20450 appendHit(gfx, hit);
20451
20452 return hit;
20453 };
20454
20455 /**
20456 * Create hits for a box.
20457 *
20458 * @param {SVGElement} gfx
20459 * @param {string} hitType
20460 * @param {Object} attrs
20461 *
20462 * @return {SVGElement}
20463 */
20464 this.createBoxHit = function(gfx, type, attrs) {
20465
20466 attrs = assign({
20467 x: 0,
20468 y: 0
20469 }, attrs);
20470
20471 var hit = create$1('rect');
20472
20473 applyStyle(hit, type);
20474
20475 attr(hit, attrs);
20476
20477 appendHit(gfx, hit);
20478
20479 return hit;
20480 };
20481
20482 /**
20483 * Update default hit of the element.
20484 *
20485 * @param {djs.model.Base} element
20486 * @param {SVGElement} gfx
20487 *
20488 * @return {SVGElement} updated hit
20489 */
20490 this.updateDefaultHit = function(element, gfx) {
20491
20492 var hit = query('.djs-hit', gfx);
20493
20494 if (!hit) {
20495 return;
20496 }
20497
20498 if (element.waypoints) {
20499 updateLine(hit, element.waypoints);
20500 } else {
20501 attr(hit, {
20502 width: element.width,
20503 height: element.height
20504 });
20505 }
20506
20507 return hit;
20508 };
20509
20510 this.fire = fire;
20511
20512 this.triggerMouseEvent = triggerMouseEvent;
20513
20514 this.mouseHandler = mouseHandler;
20515
20516 this.registerEvent = registerEvent;
20517 this.unregisterEvent = unregisterEvent;
20518 }
20519
20520
20521 InteractionEvents.$inject = [
20522 'eventBus',
20523 'elementRegistry',
20524 'styles'
20525 ];
20526
20527
20528 /**
20529 * An event indicating that the mouse hovered over an element
20530 *
20531 * @event element.hover
20532 *
20533 * @type {Object}
20534 * @property {djs.model.Base} element
20535 * @property {SVGElement} gfx
20536 * @property {Event} originalEvent
20537 */
20538
20539 /**
20540 * An event indicating that the mouse has left an element
20541 *
20542 * @event element.out
20543 *
20544 * @type {Object}
20545 * @property {djs.model.Base} element
20546 * @property {SVGElement} gfx
20547 * @property {Event} originalEvent
20548 */
20549
20550 /**
20551 * An event indicating that the mouse has clicked an element
20552 *
20553 * @event element.click
20554 *
20555 * @type {Object}
20556 * @property {djs.model.Base} element
20557 * @property {SVGElement} gfx
20558 * @property {Event} originalEvent
20559 */
20560
20561 /**
20562 * An event indicating that the mouse has double clicked an element
20563 *
20564 * @event element.dblclick
20565 *
20566 * @type {Object}
20567 * @property {djs.model.Base} element
20568 * @property {SVGElement} gfx
20569 * @property {Event} originalEvent
20570 */
20571
20572 /**
20573 * An event indicating that the mouse has gone down on an element.
20574 *
20575 * @event element.mousedown
20576 *
20577 * @type {Object}
20578 * @property {djs.model.Base} element
20579 * @property {SVGElement} gfx
20580 * @property {Event} originalEvent
20581 */
20582
20583 /**
20584 * An event indicating that the mouse has gone up on an element.
20585 *
20586 * @event element.mouseup
20587 *
20588 * @type {Object}
20589 * @property {djs.model.Base} element
20590 * @property {SVGElement} gfx
20591 * @property {Event} originalEvent
20592 */
20593
20594 /**
20595 * An event indicating that the context menu action is triggered
20596 * via mouse or touch controls.
20597 *
20598 * @event element.contextmenu
20599 *
20600 * @type {Object}
20601 * @property {djs.model.Base} element
20602 * @property {SVGElement} gfx
20603 * @property {Event} originalEvent
20604 */
20605
20606 var InteractionEventsModule$1 = {
20607 __init__: [ 'interactionEvents' ],
20608 interactionEvents: [ 'type', InteractionEvents ]
20609 };
20610
20611 var LOW_PRIORITY$l = 500;
20612
20613
20614 /**
20615 * @class
20616 *
20617 * A plugin that adds an outline to shapes and connections that may be activated and styled
20618 * via CSS classes.
20619 *
20620 * @param {EventBus} eventBus
20621 * @param {Styles} styles
20622 * @param {ElementRegistry} elementRegistry
20623 */
20624 function Outline(eventBus, styles, elementRegistry) {
20625
20626 this.offset = 6;
20627
20628 var OUTLINE_STYLE = styles.cls('djs-outline', [ 'no-fill' ]);
20629
20630 var self = this;
20631
20632 function createOutline(gfx, bounds) {
20633 var outline = create$1('rect');
20634
20635 attr(outline, assign({
20636 x: 10,
20637 y: 10,
20638 width: 100,
20639 height: 100
20640 }, OUTLINE_STYLE));
20641
20642 append(gfx, outline);
20643
20644 return outline;
20645 }
20646
20647 // A low priortity is necessary, because outlines of labels have to be updated
20648 // after the label bounds have been updated in the renderer.
20649 eventBus.on([ 'shape.added', 'shape.changed' ], LOW_PRIORITY$l, function(event) {
20650 var element = event.element,
20651 gfx = event.gfx;
20652
20653 var outline = query('.djs-outline', gfx);
20654
20655 if (!outline) {
20656 outline = createOutline(gfx);
20657 }
20658
20659 self.updateShapeOutline(outline, element);
20660 });
20661
20662 eventBus.on([ 'connection.added', 'connection.changed' ], function(event) {
20663 var element = event.element,
20664 gfx = event.gfx;
20665
20666 var outline = query('.djs-outline', gfx);
20667
20668 if (!outline) {
20669 outline = createOutline(gfx);
20670 }
20671
20672 self.updateConnectionOutline(outline, element);
20673 });
20674 }
20675
20676
20677 /**
20678 * Updates the outline of a shape respecting the dimension of the
20679 * element and an outline offset.
20680 *
20681 * @param {SVGElement} outline
20682 * @param {djs.model.Base} element
20683 */
20684 Outline.prototype.updateShapeOutline = function(outline, element) {
20685
20686 attr(outline, {
20687 x: -this.offset,
20688 y: -this.offset,
20689 width: element.width + this.offset * 2,
20690 height: element.height + this.offset * 2
20691 });
20692
20693 };
20694
20695
20696 /**
20697 * Updates the outline of a connection respecting the bounding box of
20698 * the connection and an outline offset.
20699 *
20700 * @param {SVGElement} outline
20701 * @param {djs.model.Base} element
20702 */
20703 Outline.prototype.updateConnectionOutline = function(outline, connection) {
20704
20705 var bbox = getBBox(connection);
20706
20707 attr(outline, {
20708 x: bbox.x - this.offset,
20709 y: bbox.y - this.offset,
20710 width: bbox.width + this.offset * 2,
20711 height: bbox.height + this.offset * 2
20712 });
20713
20714 };
20715
20716
20717 Outline.$inject = ['eventBus', 'styles', 'elementRegistry'];
20718
20719 var OutlineModule = {
20720 __init__: [ 'outline' ],
20721 outline: [ 'type', Outline ]
20722 };
20723
20724 /**
20725 * A service that offers the current selection in a diagram.
20726 * Offers the api to control the selection, too.
20727 *
20728 * @class
20729 *
20730 * @param {EventBus} eventBus the event bus
20731 */
20732 function Selection(eventBus, canvas) {
20733
20734 this._eventBus = eventBus;
20735 this._canvas = canvas;
20736
20737 this._selectedElements = [];
20738
20739 var self = this;
20740
20741 eventBus.on([ 'shape.remove', 'connection.remove' ], function(e) {
20742 var element = e.element;
20743 self.deselect(element);
20744 });
20745
20746 eventBus.on([ 'diagram.clear', 'plane.set' ], function(e) {
20747 self.select(null);
20748 });
20749 }
20750
20751 Selection.$inject = [ 'eventBus', 'canvas' ];
20752
20753
20754 Selection.prototype.deselect = function(element) {
20755 var selectedElements = this._selectedElements;
20756
20757 var idx = selectedElements.indexOf(element);
20758
20759 if (idx !== -1) {
20760 var oldSelection = selectedElements.slice();
20761
20762 selectedElements.splice(idx, 1);
20763
20764 this._eventBus.fire('selection.changed', { oldSelection: oldSelection, newSelection: selectedElements });
20765 }
20766 };
20767
20768
20769 Selection.prototype.get = function() {
20770 return this._selectedElements;
20771 };
20772
20773 Selection.prototype.isSelected = function(element) {
20774 return this._selectedElements.indexOf(element) !== -1;
20775 };
20776
20777
20778 /**
20779 * This method selects one or more elements on the diagram.
20780 *
20781 * By passing an additional add parameter you can decide whether or not the element(s)
20782 * should be added to the already existing selection or not.
20783 *
20784 * @method Selection#select
20785 *
20786 * @param {Object|Object[]} elements element or array of elements to be selected
20787 * @param {boolean} [add] whether the element(s) should be appended to the current selection, defaults to false
20788 */
20789 Selection.prototype.select = function(elements, add) {
20790 var selectedElements = this._selectedElements,
20791 oldSelection = selectedElements.slice();
20792
20793 if (!isArray$2(elements)) {
20794 elements = elements ? [ elements ] : [];
20795 }
20796
20797 var canvas = this._canvas;
20798
20799 elements = elements.filter(function(element) {
20800 var plane = canvas.findPlane(element);
20801
20802 return plane === canvas.getActivePlane();
20803 });
20804
20805 // selection may be cleared by passing an empty array or null
20806 // to the method
20807 if (add) {
20808 forEach(elements, function(element) {
20809 if (selectedElements.indexOf(element) !== -1) {
20810
20811 // already selected
20812 return;
20813 } else {
20814 selectedElements.push(element);
20815 }
20816 });
20817 } else {
20818 this._selectedElements = selectedElements = elements.slice();
20819 }
20820
20821 this._eventBus.fire('selection.changed', { oldSelection: oldSelection, newSelection: selectedElements });
20822 };
20823
20824 var MARKER_HOVER = 'hover',
20825 MARKER_SELECTED = 'selected';
20826
20827
20828 /**
20829 * A plugin that adds a visible selection UI to shapes and connections
20830 * by appending the <code>hover</code> and <code>selected</code> classes to them.
20831 *
20832 * @class
20833 *
20834 * Makes elements selectable, too.
20835 *
20836 * @param {EventBus} events
20837 * @param {SelectionService} selection
20838 * @param {Canvas} canvas
20839 */
20840 function SelectionVisuals(events, canvas, selection, styles) {
20841
20842 this._multiSelectionBox = null;
20843
20844 function addMarker(e, cls) {
20845 canvas.addMarker(e, cls);
20846 }
20847
20848 function removeMarker(e, cls) {
20849 canvas.removeMarker(e, cls);
20850 }
20851
20852 events.on('element.hover', function(event) {
20853 addMarker(event.element, MARKER_HOVER);
20854 });
20855
20856 events.on('element.out', function(event) {
20857 removeMarker(event.element, MARKER_HOVER);
20858 });
20859
20860 events.on('selection.changed', function(event) {
20861
20862 function deselect(s) {
20863 removeMarker(s, MARKER_SELECTED);
20864 }
20865
20866 function select(s) {
20867 addMarker(s, MARKER_SELECTED);
20868 }
20869
20870 var oldSelection = event.oldSelection,
20871 newSelection = event.newSelection;
20872
20873 forEach(oldSelection, function(e) {
20874 if (newSelection.indexOf(e) === -1) {
20875 deselect(e);
20876 }
20877 });
20878
20879 forEach(newSelection, function(e) {
20880 if (oldSelection.indexOf(e) === -1) {
20881 select(e);
20882 }
20883 });
20884 });
20885 }
20886
20887 SelectionVisuals.$inject = [
20888 'eventBus',
20889 'canvas',
20890 'selection',
20891 'styles'
20892 ];
20893
20894 function SelectionBehavior(eventBus, selection, canvas, elementRegistry) {
20895
20896 // Select elements on create
20897 eventBus.on('create.end', 500, function(event) {
20898 var context = event.context,
20899 canExecute = context.canExecute,
20900 elements = context.elements,
20901 hints = context.hints || {},
20902 autoSelect = hints.autoSelect;
20903
20904 if (canExecute) {
20905 if (autoSelect === false) {
20906
20907 // Select no elements
20908 return;
20909 }
20910
20911 if (isArray$2(autoSelect)) {
20912 selection.select(autoSelect);
20913 } else {
20914
20915 // Select all elements by default
20916 selection.select(elements.filter(isShown));
20917 }
20918 }
20919 });
20920
20921 // Select connection targets on connect
20922 eventBus.on('connect.end', 500, function(event) {
20923 var context = event.context,
20924 canExecute = context.canExecute,
20925 hover = context.hover;
20926
20927 if (canExecute && hover) {
20928 selection.select(hover);
20929 }
20930 });
20931
20932 // Select shapes on move
20933 eventBus.on('shape.move.end', 500, function(event) {
20934 var previousSelection = event.previousSelection || [];
20935
20936 var shape = elementRegistry.get(event.context.shape.id);
20937
20938 // Always select main shape on move
20939 var isSelected = find(previousSelection, function(selectedShape) {
20940 return shape.id === selectedShape.id;
20941 });
20942
20943 if (!isSelected) {
20944 selection.select(shape);
20945 }
20946 });
20947
20948 // Select elements on click
20949 eventBus.on('element.click', function(event) {
20950
20951 if (!isPrimaryButton(event)) {
20952 return;
20953 }
20954
20955 var element = event.element;
20956
20957 if (element === canvas.getRootElement()) {
20958 element = null;
20959 }
20960
20961 var isSelected = selection.isSelected(element),
20962 isMultiSelect = selection.get().length > 1;
20963
20964 // Add to selection if CTRL or SHIFT pressed
20965 var add = hasPrimaryModifier(event) || hasSecondaryModifier(event);
20966
20967 if (isSelected && isMultiSelect) {
20968 if (add) {
20969
20970 // Deselect element
20971 return selection.deselect(element);
20972 } else {
20973
20974 // Select element only
20975 return selection.select(element);
20976 }
20977 } else if (!isSelected) {
20978
20979 // Select element
20980 selection.select(element, add);
20981 } else {
20982
20983 // Deselect element
20984 selection.deselect(element);
20985 }
20986 });
20987 }
20988
20989 SelectionBehavior.$inject = [
20990 'eventBus',
20991 'selection',
20992 'canvas',
20993 'elementRegistry'
20994 ];
20995
20996
20997 function isShown(element) {
20998 return !element.hidden;
20999 }
21000
21001 var SelectionModule = {
21002 __init__: [ 'selectionVisuals', 'selectionBehavior' ],
21003 __depends__: [
21004 InteractionEventsModule$1,
21005 OutlineModule
21006 ],
21007 selection: [ 'type', Selection ],
21008 selectionVisuals: [ 'type', SelectionVisuals ],
21009 selectionBehavior: [ 'type', SelectionBehavior ]
21010 };
21011
21012 /**
21013 * Util that provides unique IDs.
21014 *
21015 * @class djs.util.IdGenerator
21016 * @constructor
21017 * @memberOf djs.util
21018 *
21019 * The ids can be customized via a given prefix and contain a random value to avoid collisions.
21020 *
21021 * @param {string} prefix a prefix to prepend to generated ids (for better readability)
21022 */
21023 function IdGenerator(prefix) {
21024
21025 this._counter = 0;
21026 this._prefix = (prefix ? prefix + '-' : '') + Math.floor(Math.random() * 1000000000) + '-';
21027 }
21028
21029 /**
21030 * Returns a next unique ID.
21031 *
21032 * @method djs.util.IdGenerator#next
21033 *
21034 * @returns {string} the id
21035 */
21036 IdGenerator.prototype.next = function() {
21037 return this._prefix + (++this._counter);
21038 };
21039
21040 // document wide unique overlay ids
21041 var ids$1 = new IdGenerator('ov');
21042
21043 var LOW_PRIORITY$k = 500;
21044
21045
21046 /**
21047 * A service that allows users to attach overlays to diagram elements.
21048 *
21049 * The overlay service will take care of overlay positioning during updates.
21050 *
21051 * @example
21052 *
21053 * // add a pink badge on the top left of the shape
21054 * overlays.add(someShape, {
21055 * position: {
21056 * top: -5,
21057 * left: -5
21058 * },
21059 * html: '<div style="width: 10px; background: fuchsia; color: white;">0</div>'
21060 * });
21061 *
21062 * // or add via shape id
21063 *
21064 * overlays.add('some-element-id', {
21065 * position: {
21066 * top: -5,
21067 * left: -5
21068 * }
21069 * html: '<div style="width: 10px; background: fuchsia; color: white;">0</div>'
21070 * });
21071 *
21072 * // or add with optional type
21073 *
21074 * overlays.add(someShape, 'badge', {
21075 * position: {
21076 * top: -5,
21077 * left: -5
21078 * }
21079 * html: '<div style="width: 10px; background: fuchsia; color: white;">0</div>'
21080 * });
21081 *
21082 *
21083 * // remove an overlay
21084 *
21085 * var id = overlays.add(...);
21086 * overlays.remove(id);
21087 *
21088 *
21089 * You may configure overlay defaults during tool by providing a `config` module
21090 * with `overlays.defaults` as an entry:
21091 *
21092 * {
21093 * overlays: {
21094 * defaults: {
21095 * show: {
21096 * minZoom: 0.7,
21097 * maxZoom: 5.0
21098 * },
21099 * scale: {
21100 * min: 1
21101 * }
21102 * }
21103 * }
21104 *
21105 * @param {Object} config
21106 * @param {EventBus} eventBus
21107 * @param {Canvas} canvas
21108 * @param {ElementRegistry} elementRegistry
21109 */
21110 function Overlays(config, eventBus, canvas, elementRegistry) {
21111
21112 this._eventBus = eventBus;
21113 this._canvas = canvas;
21114 this._elementRegistry = elementRegistry;
21115
21116 this._ids = ids$1;
21117
21118 this._overlayDefaults = assign({
21119
21120 // no show constraints
21121 show: null,
21122
21123 // always scale
21124 scale: true
21125 }, config && config.defaults);
21126
21127 /**
21128 * Mapping overlayId -> overlay
21129 */
21130 this._overlays = {};
21131
21132 /**
21133 * Mapping elementId -> overlay container
21134 */
21135 this._overlayContainers = [];
21136
21137 // root html element for all overlays
21138 this._overlayRoot = createRoot$1(canvas.getContainer());
21139
21140 this._init();
21141 }
21142
21143
21144 Overlays.$inject = [
21145 'config.overlays',
21146 'eventBus',
21147 'canvas',
21148 'elementRegistry'
21149 ];
21150
21151
21152 /**
21153 * Returns the overlay with the specified id or a list of overlays
21154 * for an element with a given type.
21155 *
21156 * @example
21157 *
21158 * // return the single overlay with the given id
21159 * overlays.get('some-id');
21160 *
21161 * // return all overlays for the shape
21162 * overlays.get({ element: someShape });
21163 *
21164 * // return all overlays on shape with type 'badge'
21165 * overlays.get({ element: someShape, type: 'badge' });
21166 *
21167 * // shape can also be specified as id
21168 * overlays.get({ element: 'element-id', type: 'badge' });
21169 *
21170 *
21171 * @param {Object} search
21172 * @param {string} [search.id]
21173 * @param {string|djs.model.Base} [search.element]
21174 * @param {string} [search.type]
21175 *
21176 * @return {Object|Array<Object>} the overlay(s)
21177 */
21178 Overlays.prototype.get = function(search) {
21179
21180 if (isString(search)) {
21181 search = { id: search };
21182 }
21183
21184 if (isString(search.element)) {
21185 search.element = this._elementRegistry.get(search.element);
21186 }
21187
21188 if (search.element) {
21189 var container = this._getOverlayContainer(search.element, true);
21190
21191 // return a list of overlays when searching by element (+type)
21192 if (container) {
21193 return search.type ? filter(container.overlays, matchPattern({ type: search.type })) : container.overlays.slice();
21194 } else {
21195 return [];
21196 }
21197 } else
21198 if (search.type) {
21199 return filter(this._overlays, matchPattern({ type: search.type }));
21200 } else {
21201
21202 // return single element when searching by id
21203 return search.id ? this._overlays[search.id] : null;
21204 }
21205 };
21206
21207 /**
21208 * Adds a HTML overlay to an element.
21209 *
21210 * @param {string|djs.model.Base} element attach overlay to this shape
21211 * @param {string} [type] optional type to assign to the overlay
21212 * @param {Object} overlay the overlay configuration
21213 *
21214 * @param {string|DOMElement} overlay.html html element to use as an overlay
21215 * @param {Object} [overlay.show] show configuration
21216 * @param {number} [overlay.show.minZoom] minimal zoom level to show the overlay
21217 * @param {number} [overlay.show.maxZoom] maximum zoom level to show the overlay
21218 * @param {Object} overlay.position where to attach the overlay
21219 * @param {number} [overlay.position.left] relative to element bbox left attachment
21220 * @param {number} [overlay.position.top] relative to element bbox top attachment
21221 * @param {number} [overlay.position.bottom] relative to element bbox bottom attachment
21222 * @param {number} [overlay.position.right] relative to element bbox right attachment
21223 * @param {boolean|Object} [overlay.scale=true] false to preserve the same size regardless of
21224 * diagram zoom
21225 * @param {number} [overlay.scale.min]
21226 * @param {number} [overlay.scale.max]
21227 *
21228 * @return {string} id that may be used to reference the overlay for update or removal
21229 */
21230 Overlays.prototype.add = function(element, type, overlay) {
21231
21232 if (isObject(type)) {
21233 overlay = type;
21234 type = null;
21235 }
21236
21237 if (!element.id) {
21238 element = this._elementRegistry.get(element);
21239 }
21240
21241 if (!overlay.position) {
21242 throw new Error('must specifiy overlay position');
21243 }
21244
21245 if (!overlay.html) {
21246 throw new Error('must specifiy overlay html');
21247 }
21248
21249 if (!element) {
21250 throw new Error('invalid element specified');
21251 }
21252
21253 var id = this._ids.next();
21254
21255 overlay = assign({}, this._overlayDefaults, overlay, {
21256 id: id,
21257 type: type,
21258 element: element,
21259 html: overlay.html
21260 });
21261
21262 this._addOverlay(overlay);
21263
21264 return id;
21265 };
21266
21267
21268 /**
21269 * Remove an overlay with the given id or all overlays matching the given filter.
21270 *
21271 * @see Overlays#get for filter options.
21272 *
21273 * @param {string} [id]
21274 * @param {Object} [filter]
21275 */
21276 Overlays.prototype.remove = function(filter) {
21277
21278 var overlays = this.get(filter) || [];
21279
21280 if (!isArray$2(overlays)) {
21281 overlays = [ overlays ];
21282 }
21283
21284 var self = this;
21285
21286 forEach(overlays, function(overlay) {
21287
21288 var container = self._getOverlayContainer(overlay.element, true);
21289
21290 if (overlay) {
21291 remove$2(overlay.html);
21292 remove$2(overlay.htmlContainer);
21293
21294 delete overlay.htmlContainer;
21295 delete overlay.element;
21296
21297 delete self._overlays[overlay.id];
21298 }
21299
21300 if (container) {
21301 var idx = container.overlays.indexOf(overlay);
21302 if (idx !== -1) {
21303 container.overlays.splice(idx, 1);
21304 }
21305 }
21306 });
21307
21308 };
21309
21310
21311 Overlays.prototype.show = function() {
21312 setVisible$1(this._overlayRoot);
21313 };
21314
21315
21316 Overlays.prototype.hide = function() {
21317 setVisible$1(this._overlayRoot, false);
21318 };
21319
21320 Overlays.prototype.clear = function() {
21321 this._overlays = {};
21322
21323 this._overlayContainers = [];
21324
21325 clear$1(this._overlayRoot);
21326 };
21327
21328 Overlays.prototype._updateOverlayContainer = function(container) {
21329 var element = container.element,
21330 html = container.html;
21331
21332 // update container left,top according to the elements x,y coordinates
21333 // this ensures we can attach child elements relative to this container
21334
21335 var x = element.x,
21336 y = element.y;
21337
21338 if (element.waypoints) {
21339 var bbox = getBBox(element);
21340 x = bbox.x;
21341 y = bbox.y;
21342 }
21343
21344 setPosition$1(html, x, y);
21345
21346 attr$1(container.html, 'data-container-id', element.id);
21347 };
21348
21349
21350 Overlays.prototype._updateOverlay = function(overlay) {
21351
21352 var position = overlay.position,
21353 htmlContainer = overlay.htmlContainer,
21354 element = overlay.element;
21355
21356 // update overlay html relative to shape because
21357 // it is already positioned on the element
21358
21359 // update relative
21360 var left = position.left,
21361 top = position.top;
21362
21363 if (position.right !== undefined) {
21364
21365 var width;
21366
21367 if (element.waypoints) {
21368 width = getBBox(element).width;
21369 } else {
21370 width = element.width;
21371 }
21372
21373 left = position.right * -1 + width;
21374 }
21375
21376 if (position.bottom !== undefined) {
21377
21378 var height;
21379
21380 if (element.waypoints) {
21381 height = getBBox(element).height;
21382 } else {
21383 height = element.height;
21384 }
21385
21386 top = position.bottom * -1 + height;
21387 }
21388
21389 setPosition$1(htmlContainer, left || 0, top || 0);
21390 };
21391
21392
21393 Overlays.prototype._createOverlayContainer = function(element) {
21394 var html = domify('<div class="djs-overlays" style="position: absolute" />');
21395
21396 this._overlayRoot.appendChild(html);
21397
21398 var container = {
21399 html: html,
21400 element: element,
21401 overlays: []
21402 };
21403
21404 this._updateOverlayContainer(container);
21405
21406 this._overlayContainers.push(container);
21407
21408 return container;
21409 };
21410
21411
21412 Overlays.prototype._updateRoot = function(viewbox) {
21413 var scale = viewbox.scale || 1;
21414
21415 var matrix = 'matrix(' +
21416 [
21417 scale,
21418 0,
21419 0,
21420 scale,
21421 -1 * viewbox.x * scale,
21422 -1 * viewbox.y * scale
21423 ].join(',') +
21424 ')';
21425
21426 setTransform$1(this._overlayRoot, matrix);
21427 };
21428
21429
21430 Overlays.prototype._getOverlayContainer = function(element, raw) {
21431 var container = find(this._overlayContainers, function(c) {
21432 return c.element === element;
21433 });
21434
21435
21436 if (!container && !raw) {
21437 return this._createOverlayContainer(element);
21438 }
21439
21440 return container;
21441 };
21442
21443
21444 Overlays.prototype._addOverlay = function(overlay) {
21445
21446 var id = overlay.id,
21447 element = overlay.element,
21448 html = overlay.html,
21449 htmlContainer,
21450 overlayContainer;
21451
21452 // unwrap jquery (for those who need it)
21453 if (html.get && html.constructor.prototype.jquery) {
21454 html = html.get(0);
21455 }
21456
21457 // create proper html elements from
21458 // overlay HTML strings
21459 if (isString(html)) {
21460 html = domify(html);
21461 }
21462
21463 overlayContainer = this._getOverlayContainer(element);
21464
21465 htmlContainer = domify('<div class="djs-overlay" data-overlay-id="' + id + '" style="position: absolute">');
21466
21467 htmlContainer.appendChild(html);
21468
21469 if (overlay.type) {
21470 classes$1(htmlContainer).add('djs-overlay-' + overlay.type);
21471 }
21472
21473 var plane = this._canvas.findPlane(element);
21474 var activePlane = this._canvas.getActivePlane();
21475 overlay.plane = plane;
21476 if (plane !== activePlane) {
21477 setVisible$1(htmlContainer, false);
21478 }
21479
21480 overlay.htmlContainer = htmlContainer;
21481
21482 overlayContainer.overlays.push(overlay);
21483 overlayContainer.html.appendChild(htmlContainer);
21484
21485 this._overlays[id] = overlay;
21486
21487 this._updateOverlay(overlay);
21488 this._updateOverlayVisibilty(overlay, this._canvas.viewbox());
21489 };
21490
21491
21492 Overlays.prototype._updateOverlayVisibilty = function(overlay, viewbox) {
21493 var show = overlay.show,
21494 minZoom = show && show.minZoom,
21495 maxZoom = show && show.maxZoom,
21496 htmlContainer = overlay.htmlContainer,
21497 visible = true;
21498
21499 if (show) {
21500 if (
21501 (isDefined(minZoom) && minZoom > viewbox.scale) ||
21502 (isDefined(maxZoom) && maxZoom < viewbox.scale)
21503 ) {
21504 visible = false;
21505 }
21506
21507 setVisible$1(htmlContainer, visible);
21508 }
21509
21510 this._updateOverlayScale(overlay, viewbox);
21511 };
21512
21513
21514 Overlays.prototype._updateOverlayScale = function(overlay, viewbox) {
21515 var shouldScale = overlay.scale,
21516 minScale,
21517 maxScale,
21518 htmlContainer = overlay.htmlContainer;
21519
21520 var scale, transform = '';
21521
21522 if (shouldScale !== true) {
21523
21524 if (shouldScale === false) {
21525 minScale = 1;
21526 maxScale = 1;
21527 } else {
21528 minScale = shouldScale.min;
21529 maxScale = shouldScale.max;
21530 }
21531
21532 if (isDefined(minScale) && viewbox.scale < minScale) {
21533 scale = (1 / viewbox.scale || 1) * minScale;
21534 }
21535
21536 if (isDefined(maxScale) && viewbox.scale > maxScale) {
21537 scale = (1 / viewbox.scale || 1) * maxScale;
21538 }
21539 }
21540
21541 if (isDefined(scale)) {
21542 transform = 'scale(' + scale + ',' + scale + ')';
21543 }
21544
21545 setTransform$1(htmlContainer, transform);
21546 };
21547
21548
21549 Overlays.prototype._updateOverlaysVisibilty = function(viewbox) {
21550
21551 var self = this;
21552
21553 forEach(this._overlays, function(overlay) {
21554 self._updateOverlayVisibilty(overlay, viewbox);
21555 });
21556 };
21557
21558
21559 Overlays.prototype._init = function() {
21560
21561 var eventBus = this._eventBus;
21562
21563 var self = this;
21564
21565
21566 // scroll/zoom integration
21567
21568 function updateViewbox(viewbox) {
21569 self._updateRoot(viewbox);
21570 self._updateOverlaysVisibilty(viewbox);
21571
21572 self.show();
21573 }
21574
21575 eventBus.on('canvas.viewbox.changing', function(event) {
21576 self.hide();
21577 });
21578
21579 eventBus.on('canvas.viewbox.changed', function(event) {
21580 updateViewbox(event.viewbox);
21581 });
21582
21583
21584 // remove integration
21585
21586 eventBus.on([ 'shape.remove', 'connection.remove' ], function(e) {
21587 var element = e.element;
21588 var overlays = self.get({ element: element });
21589
21590 forEach(overlays, function(o) {
21591 self.remove(o.id);
21592 });
21593
21594 var container = self._getOverlayContainer(element);
21595
21596 if (container) {
21597 remove$2(container.html);
21598 var i = self._overlayContainers.indexOf(container);
21599 if (i !== -1) {
21600 self._overlayContainers.splice(i, 1);
21601 }
21602 }
21603 });
21604
21605
21606 // move integration
21607
21608 eventBus.on('element.changed', LOW_PRIORITY$k, function(e) {
21609 var element = e.element;
21610
21611 var container = self._getOverlayContainer(element, true);
21612
21613 if (container) {
21614 forEach(container.overlays, function(overlay) {
21615 self._updateOverlay(overlay);
21616 });
21617
21618 self._updateOverlayContainer(container);
21619 }
21620 });
21621
21622
21623 // marker integration, simply add them on the overlays as classes, too.
21624
21625 eventBus.on('element.marker.update', function(e) {
21626 var container = self._getOverlayContainer(e.element, true);
21627 if (container) {
21628 classes$1(container.html)[e.add ? 'add' : 'remove'](e.marker);
21629 }
21630 });
21631
21632
21633 eventBus.on('plane.set', function(e) {
21634 forEach(self._overlays, function(el) {
21635 setVisible$1(el.htmlContainer, el.plane === e.plane);
21636 });
21637 });
21638
21639 // clear overlays with diagram
21640
21641 eventBus.on('diagram.clear', this.clear, this);
21642 };
21643
21644
21645
21646 // helpers /////////////////////////////
21647
21648 function createRoot$1(parentNode) {
21649 var root = domify(
21650 '<div class="djs-overlay-container" style="position: absolute; width: 0; height: 0;" />'
21651 );
21652
21653 parentNode.insertBefore(root, parentNode.firstChild);
21654
21655 return root;
21656 }
21657
21658 function setPosition$1(el, x, y) {
21659 assign(el.style, { left: x + 'px', top: y + 'px' });
21660 }
21661
21662 function setVisible$1(el, visible) {
21663 el.style.display = visible === false ? 'none' : '';
21664 }
21665
21666 function setTransform$1(el, transform) {
21667
21668 el.style['transform-origin'] = 'top left';
21669
21670 [ '', '-ms-', '-webkit-' ].forEach(function(prefix) {
21671 el.style[prefix + 'transform'] = transform;
21672 });
21673 }
21674
21675 var OverlaysModule = {
21676 __init__: [ 'overlays' ],
21677 overlays: [ 'type', Overlays ]
21678 };
21679
21680 /**
21681 * A viewer for BPMN 2.0 diagrams.
21682 *
21683 * Have a look at {@link NavigatedViewer} or {@link Modeler} for bundles that include
21684 * additional features.
21685 *
21686 *
21687 * ## Extending the Viewer
21688 *
21689 * In order to extend the viewer pass extension modules to bootstrap via the
21690 * `additionalModules` option. An extension module is an object that exposes
21691 * named services.
21692 *
21693 * The following example depicts the integration of a simple
21694 * logging component that integrates with interaction events:
21695 *
21696 *
21697 * ```javascript
21698 *
21699 * // logging component
21700 * function InteractionLogger(eventBus) {
21701 * eventBus.on('element.hover', function(event) {
21702 * console.log()
21703 * })
21704 * }
21705 *
21706 * InteractionLogger.$inject = [ 'eventBus' ]; // minification save
21707 *
21708 * // extension module
21709 * var extensionModule = {
21710 * __init__: [ 'interactionLogger' ],
21711 * interactionLogger: [ 'type', InteractionLogger ]
21712 * };
21713 *
21714 * // extend the viewer
21715 * var bpmnViewer = new Viewer({ additionalModules: [ extensionModule ] });
21716 * bpmnViewer.importXML(...);
21717 * ```
21718 *
21719 * @param {Object} [options] configuration options to pass to the viewer
21720 * @param {DOMElement} [options.container] the container to render the viewer in, defaults to body.
21721 * @param {string|number} [options.width] the width of the viewer
21722 * @param {string|number} [options.height] the height of the viewer
21723 * @param {Object} [options.moddleExtensions] extension packages to provide
21724 * @param {Array<didi.Module>} [options.modules] a list of modules to override the default modules
21725 * @param {Array<didi.Module>} [options.additionalModules] a list of modules to use with the default modules
21726 */
21727 function Viewer(options) {
21728 BaseViewer.call(this, options);
21729 }
21730
21731 inherits$1(Viewer, BaseViewer);
21732
21733 // modules the viewer is composed of
21734 Viewer.prototype._modules = [
21735 CoreModule,
21736 translate,
21737 SelectionModule,
21738 OverlaysModule
21739 ];
21740
21741 // default moddle extensions the viewer is composed of
21742 Viewer.prototype._moddleExtensions = {};
21743
21744 /**
21745 * Returns true if event was triggered with any modifier
21746 * @param {KeyboardEvent} event
21747 */
21748 function hasModifier(event) {
21749 return (event.ctrlKey || event.metaKey || event.shiftKey || event.altKey);
21750 }
21751
21752 /**
21753 * @param {KeyboardEvent} event
21754 */
21755 function isCmd(event) {
21756
21757 // ensure we don't react to AltGr
21758 // (mapped to CTRL + ALT)
21759 if (event.altKey) {
21760 return false;
21761 }
21762
21763 return event.ctrlKey || event.metaKey;
21764 }
21765
21766 /**
21767 * Checks if key pressed is one of provided keys.
21768 *
21769 * @param {string|Array<string>} keys
21770 * @param {KeyboardEvent} event
21771 */
21772 function isKey(keys, event) {
21773 keys = isArray$2(keys) ? keys : [ keys ];
21774
21775 return keys.indexOf(event.key) !== -1 || keys.indexOf(event.keyCode) !== -1;
21776 }
21777
21778 /**
21779 * @param {KeyboardEvent} event
21780 */
21781 function isShift(event) {
21782 return event.shiftKey;
21783 }
21784
21785 var KEYDOWN_EVENT = 'keyboard.keydown',
21786 KEYUP_EVENT = 'keyboard.keyup';
21787
21788 var HANDLE_MODIFIER_ATTRIBUTE = 'input-handle-modified-keys';
21789
21790 var DEFAULT_PRIORITY$4 = 1000;
21791
21792 /**
21793 * A keyboard abstraction that may be activated and
21794 * deactivated by users at will, consuming key events
21795 * and triggering diagram actions.
21796 *
21797 * For keys pressed down, keyboard fires `keyboard.keydown` event.
21798 * The event context contains one field which is `KeyboardEvent` event.
21799 *
21800 * The implementation fires the following key events that allow
21801 * other components to hook into key handling:
21802 *
21803 * - keyboard.bind
21804 * - keyboard.unbind
21805 * - keyboard.init
21806 * - keyboard.destroy
21807 *
21808 * All events contain one field which is node.
21809 *
21810 * A default binding for the keyboard may be specified via the
21811 * `keyboard.bindTo` configuration option.
21812 *
21813 * @param {Config} config
21814 * @param {EventBus} eventBus
21815 */
21816 function Keyboard(config, eventBus) {
21817 var self = this;
21818
21819 this._config = config || {};
21820 this._eventBus = eventBus;
21821
21822 this._keydownHandler = this._keydownHandler.bind(this);
21823 this._keyupHandler = this._keyupHandler.bind(this);
21824
21825 // properly clean dom registrations
21826 eventBus.on('diagram.destroy', function() {
21827 self._fire('destroy');
21828
21829 self.unbind();
21830 });
21831
21832 eventBus.on('diagram.init', function() {
21833 self._fire('init');
21834 });
21835
21836 eventBus.on('attach', function() {
21837 if (config && config.bindTo) {
21838 self.bind(config.bindTo);
21839 }
21840 });
21841
21842 eventBus.on('detach', function() {
21843 self.unbind();
21844 });
21845 }
21846
21847 Keyboard.$inject = [
21848 'config.keyboard',
21849 'eventBus'
21850 ];
21851
21852 Keyboard.prototype._keydownHandler = function(event) {
21853 this._keyHandler(event, KEYDOWN_EVENT);
21854 };
21855
21856 Keyboard.prototype._keyupHandler = function(event) {
21857 this._keyHandler(event, KEYUP_EVENT);
21858 };
21859
21860 Keyboard.prototype._keyHandler = function(event, type) {
21861 var eventBusResult;
21862
21863 if (this._isEventIgnored(event)) {
21864 return;
21865 }
21866
21867 var context = {
21868 keyEvent: event
21869 };
21870
21871 eventBusResult = this._eventBus.fire(type || KEYDOWN_EVENT, context);
21872
21873 if (eventBusResult) {
21874 event.preventDefault();
21875 }
21876 };
21877
21878 Keyboard.prototype._isEventIgnored = function(event) {
21879 return isInput(event.target) && this._isModifiedKeyIgnored(event);
21880 };
21881
21882 Keyboard.prototype._isModifiedKeyIgnored = function(event) {
21883 if (!isCmd(event)) {
21884 return true;
21885 }
21886
21887 var allowedModifiers = this._getAllowedModifiers(event.target);
21888 return !allowedModifiers.includes(event.key);
21889 };
21890
21891 Keyboard.prototype._getAllowedModifiers = function(element) {
21892 var modifierContainer = closest(element, '[' + HANDLE_MODIFIER_ATTRIBUTE + ']', true);
21893
21894 if (!modifierContainer || (this._node && !this._node.contains(modifierContainer))) {
21895 return [];
21896 }
21897
21898 return modifierContainer.getAttribute(HANDLE_MODIFIER_ATTRIBUTE).split(',');
21899 };
21900
21901 Keyboard.prototype.bind = function(node) {
21902
21903 // make sure that the keyboard is only bound once to the DOM
21904 this.unbind();
21905
21906 this._node = node;
21907
21908 // bind key events
21909 componentEvent.bind(node, 'keydown', this._keydownHandler, true);
21910 componentEvent.bind(node, 'keyup', this._keyupHandler, true);
21911
21912 this._fire('bind');
21913 };
21914
21915 Keyboard.prototype.getBinding = function() {
21916 return this._node;
21917 };
21918
21919 Keyboard.prototype.unbind = function() {
21920 var node = this._node;
21921
21922 if (node) {
21923 this._fire('unbind');
21924
21925 // unbind key events
21926 componentEvent.unbind(node, 'keydown', this._keydownHandler, true);
21927 componentEvent.unbind(node, 'keyup', this._keyupHandler, true);
21928 }
21929
21930 this._node = null;
21931 };
21932
21933 Keyboard.prototype._fire = function(event) {
21934 this._eventBus.fire('keyboard.' + event, { node: this._node });
21935 };
21936
21937 /**
21938 * Add a listener function that is notified with `KeyboardEvent` whenever
21939 * the keyboard is bound and the user presses a key. If no priority is
21940 * provided, the default value of 1000 is used.
21941 *
21942 * @param {number} [priority]
21943 * @param {Function} listener
21944 * @param {string} type
21945 */
21946 Keyboard.prototype.addListener = function(priority, listener, type) {
21947 if (isFunction(priority)) {
21948 type = listener;
21949 listener = priority;
21950 priority = DEFAULT_PRIORITY$4;
21951 }
21952
21953 this._eventBus.on(type || KEYDOWN_EVENT, priority, listener);
21954 };
21955
21956 Keyboard.prototype.removeListener = function(listener, type) {
21957 this._eventBus.off(type || KEYDOWN_EVENT, listener);
21958 };
21959
21960 Keyboard.prototype.hasModifier = hasModifier;
21961 Keyboard.prototype.isCmd = isCmd;
21962 Keyboard.prototype.isShift = isShift;
21963 Keyboard.prototype.isKey = isKey;
21964
21965
21966
21967 // helpers ///////
21968
21969 function isInput(target) {
21970 return target && (matchesSelector(target, 'input, textarea') || target.contentEditable === 'true');
21971 }
21972
21973 var LOW_PRIORITY$j = 500;
21974
21975 var KEYCODE_C = 67;
21976 var KEYCODE_V = 86;
21977 var KEYCODE_Y = 89;
21978 var KEYCODE_Z = 90;
21979
21980 var KEYS_COPY = ['c', 'C', KEYCODE_C ];
21981 var KEYS_PASTE = [ 'v', 'V', KEYCODE_V ];
21982 var KEYS_REDO = [ 'y', 'Y', KEYCODE_Y ];
21983 var KEYS_UNDO = [ 'z', 'Z', KEYCODE_Z ];
21984
21985
21986 /**
21987 * Adds default keyboard bindings.
21988 *
21989 * This does not pull in any features will bind only actions that
21990 * have previously been registered against the editorActions component.
21991 *
21992 * @param {EventBus} eventBus
21993 * @param {Keyboard} keyboard
21994 */
21995 function KeyboardBindings(eventBus, keyboard) {
21996
21997 var self = this;
21998
21999 eventBus.on('editorActions.init', LOW_PRIORITY$j, function(event) {
22000
22001 var editorActions = event.editorActions;
22002
22003 self.registerBindings(keyboard, editorActions);
22004 });
22005 }
22006
22007 KeyboardBindings.$inject = [
22008 'eventBus',
22009 'keyboard'
22010 ];
22011
22012
22013 /**
22014 * Register available keyboard bindings.
22015 *
22016 * @param {Keyboard} keyboard
22017 * @param {EditorActions} editorActions
22018 */
22019 KeyboardBindings.prototype.registerBindings = function(keyboard, editorActions) {
22020
22021 /**
22022 * Add keyboard binding if respective editor action
22023 * is registered.
22024 *
22025 * @param {string} action name
22026 * @param {Function} fn that implements the key binding
22027 */
22028 function addListener(action, fn) {
22029
22030 if (editorActions.isRegistered(action)) {
22031 keyboard.addListener(fn);
22032 }
22033 }
22034
22035
22036 // undo
22037 // (CTRL|CMD) + Z
22038 addListener('undo', function(context) {
22039
22040 var event = context.keyEvent;
22041
22042 if (isCmd(event) && !isShift(event) && isKey(KEYS_UNDO, event)) {
22043 editorActions.trigger('undo');
22044
22045 return true;
22046 }
22047 });
22048
22049 // redo
22050 // CTRL + Y
22051 // CMD + SHIFT + Z
22052 addListener('redo', function(context) {
22053
22054 var event = context.keyEvent;
22055
22056 if (isCmd(event) && (isKey(KEYS_REDO, event) || (isKey(KEYS_UNDO, event) && isShift(event)))) {
22057 editorActions.trigger('redo');
22058
22059 return true;
22060 }
22061 });
22062
22063 // copy
22064 // CTRL/CMD + C
22065 addListener('copy', function(context) {
22066
22067 var event = context.keyEvent;
22068
22069 if (isCmd(event) && isKey(KEYS_COPY, event)) {
22070 editorActions.trigger('copy');
22071
22072 return true;
22073 }
22074 });
22075
22076 // paste
22077 // CTRL/CMD + V
22078 addListener('paste', function(context) {
22079
22080 var event = context.keyEvent;
22081
22082 if (isCmd(event) && isKey(KEYS_PASTE, event)) {
22083 editorActions.trigger('paste');
22084
22085 return true;
22086 }
22087 });
22088
22089 // zoom in one step
22090 // CTRL/CMD + +
22091 addListener('stepZoom', function(context) {
22092
22093 var event = context.keyEvent;
22094
22095 // quirk: it has to be triggered by `=` as well to work on international keyboard layout
22096 // cf: https://github.com/bpmn-io/bpmn-js/issues/1362#issuecomment-722989754
22097 if (isKey([ '+', 'Add', '=' ], event) && isCmd(event)) {
22098 editorActions.trigger('stepZoom', { value: 1 });
22099
22100 return true;
22101 }
22102 });
22103
22104 // zoom out one step
22105 // CTRL + -
22106 addListener('stepZoom', function(context) {
22107
22108 var event = context.keyEvent;
22109
22110 if (isKey([ '-', 'Subtract' ], event) && isCmd(event)) {
22111 editorActions.trigger('stepZoom', { value: -1 });
22112
22113 return true;
22114 }
22115 });
22116
22117 // zoom to the default level
22118 // CTRL + 0
22119 addListener('zoom', function(context) {
22120
22121 var event = context.keyEvent;
22122
22123 if (isKey('0', event) && isCmd(event)) {
22124 editorActions.trigger('zoom', { value: 1 });
22125
22126 return true;
22127 }
22128 });
22129
22130 // delete selected element
22131 // DEL
22132 addListener('removeSelection', function(context) {
22133
22134 var event = context.keyEvent;
22135
22136 if (isKey(['Backspace', 'Delete', 'Del' ], event)) {
22137 editorActions.trigger('removeSelection');
22138
22139 return true;
22140 }
22141 });
22142 };
22143
22144 var KeyboardModule$1 = {
22145 __init__: [ 'keyboard', 'keyboardBindings' ],
22146 keyboard: [ 'type', Keyboard ],
22147 keyboardBindings: [ 'type', KeyboardBindings ]
22148 };
22149
22150 var DEFAULT_CONFIG$1 = {
22151 moveSpeed: 50,
22152 moveSpeedAccelerated: 200
22153 };
22154
22155
22156 /**
22157 * A feature that allows users to move the canvas using the keyboard.
22158 *
22159 * @param {Object} config
22160 * @param {number} [config.moveSpeed=50]
22161 * @param {number} [config.moveSpeedAccelerated=200]
22162 * @param {Keyboard} keyboard
22163 * @param {Canvas} canvas
22164 */
22165 function KeyboardMove(
22166 config,
22167 keyboard,
22168 canvas
22169 ) {
22170
22171 var self = this;
22172
22173 this._config = assign({}, DEFAULT_CONFIG$1, config || {});
22174
22175 keyboard.addListener(arrowsListener);
22176
22177
22178 function arrowsListener(context) {
22179
22180 var event = context.keyEvent,
22181 config = self._config;
22182
22183 if (!keyboard.isCmd(event)) {
22184 return;
22185 }
22186
22187 if (keyboard.isKey([
22188 'ArrowLeft', 'Left',
22189 'ArrowUp', 'Up',
22190 'ArrowDown', 'Down',
22191 'ArrowRight', 'Right'
22192 ], event)) {
22193
22194 var speed = (
22195 keyboard.isShift(event) ?
22196 config.moveSpeedAccelerated :
22197 config.moveSpeed
22198 );
22199
22200 var direction;
22201
22202 switch (event.key) {
22203 case 'ArrowLeft':
22204 case 'Left':
22205 direction = 'left';
22206 break;
22207 case 'ArrowUp':
22208 case 'Up':
22209 direction = 'up';
22210 break;
22211 case 'ArrowRight':
22212 case 'Right':
22213 direction = 'right';
22214 break;
22215 case 'ArrowDown':
22216 case 'Down':
22217 direction = 'down';
22218 break;
22219 }
22220
22221 self.moveCanvas({
22222 speed: speed,
22223 direction: direction
22224 });
22225
22226 return true;
22227 }
22228 }
22229
22230 this.moveCanvas = function(opts) {
22231
22232 var dx = 0,
22233 dy = 0,
22234 speed = opts.speed;
22235
22236 var actualSpeed = speed / Math.min(Math.sqrt(canvas.viewbox().scale), 1);
22237
22238 switch (opts.direction) {
22239 case 'left': // Left
22240 dx = actualSpeed;
22241 break;
22242 case 'up': // Up
22243 dy = actualSpeed;
22244 break;
22245 case 'right': // Right
22246 dx = -actualSpeed;
22247 break;
22248 case 'down': // Down
22249 dy = -actualSpeed;
22250 break;
22251 }
22252
22253 canvas.scroll({
22254 dx: dx,
22255 dy: dy
22256 });
22257 };
22258
22259 }
22260
22261
22262 KeyboardMove.$inject = [
22263 'config.keyboardMove',
22264 'keyboard',
22265 'canvas'
22266 ];
22267
22268 var KeyboardMoveModule = {
22269 __depends__: [
22270 KeyboardModule$1
22271 ],
22272 __init__: [ 'keyboardMove' ],
22273 keyboardMove: [ 'type', KeyboardMove ]
22274 };
22275
22276 var CURSOR_CLS_PATTERN = /^djs-cursor-.*$/;
22277
22278
22279 function set(mode) {
22280 var classes = classes$1(document.body);
22281
22282 classes.removeMatching(CURSOR_CLS_PATTERN);
22283
22284 if (mode) {
22285 classes.add('djs-cursor-' + mode);
22286 }
22287 }
22288
22289 function unset() {
22290 set(null);
22291 }
22292
22293 var TRAP_PRIORITY = 5000;
22294
22295 /**
22296 * Installs a click trap that prevents a ghost click following a dragging operation.
22297 *
22298 * @return {Function} a function to immediately remove the installed trap.
22299 */
22300 function install(eventBus, eventName) {
22301
22302 eventName = eventName || 'element.click';
22303
22304 function trap() {
22305 return false;
22306 }
22307
22308 eventBus.once(eventName, TRAP_PRIORITY, trap);
22309
22310 return function() {
22311 eventBus.off(eventName, trap);
22312 };
22313 }
22314
22315 function center(bounds) {
22316 return {
22317 x: bounds.x + (bounds.width / 2),
22318 y: bounds.y + (bounds.height / 2)
22319 };
22320 }
22321
22322
22323 function delta(a, b) {
22324 return {
22325 x: a.x - b.x,
22326 y: a.y - b.y
22327 };
22328 }
22329
22330 var THRESHOLD$1 = 15;
22331
22332
22333 /**
22334 * Move the canvas via mouse.
22335 *
22336 * @param {EventBus} eventBus
22337 * @param {Canvas} canvas
22338 */
22339 function MoveCanvas(eventBus, canvas) {
22340
22341 var context;
22342
22343
22344 // listen for move on element mouse down;
22345 // allow others to hook into the event before us though
22346 // (dragging / element moving will do this)
22347 eventBus.on('element.mousedown', 500, function(e) {
22348 return handleStart(e.originalEvent);
22349 });
22350
22351
22352 function handleMove(event) {
22353
22354 var start = context.start,
22355 button = context.button,
22356 position = toPoint(event),
22357 delta$1 = delta(position, start);
22358
22359 if (!context.dragging && length(delta$1) > THRESHOLD$1) {
22360 context.dragging = true;
22361
22362 if (button === 0) {
22363 install(eventBus);
22364 }
22365
22366 set('grab');
22367 }
22368
22369 if (context.dragging) {
22370
22371 var lastPosition = context.last || context.start;
22372
22373 delta$1 = delta(position, lastPosition);
22374
22375 canvas.scroll({
22376 dx: delta$1.x,
22377 dy: delta$1.y
22378 });
22379
22380 context.last = position;
22381 }
22382
22383 // prevent select
22384 event.preventDefault();
22385 }
22386
22387
22388 function handleEnd(event) {
22389 componentEvent.unbind(document, 'mousemove', handleMove);
22390 componentEvent.unbind(document, 'mouseup', handleEnd);
22391
22392 context = null;
22393
22394 unset();
22395 }
22396
22397 function handleStart(event) {
22398
22399 // event is already handled by '.djs-draggable'
22400 if (closest(event.target, '.djs-draggable')) {
22401 return;
22402 }
22403
22404 var button = event.button;
22405
22406 // reject right mouse button or modifier key
22407 if (button >= 2 || event.ctrlKey || event.shiftKey || event.altKey) {
22408 return;
22409 }
22410
22411 context = {
22412 button: button,
22413 start: toPoint(event)
22414 };
22415
22416 componentEvent.bind(document, 'mousemove', handleMove);
22417 componentEvent.bind(document, 'mouseup', handleEnd);
22418
22419 // we've handled the event
22420 return true;
22421 }
22422
22423 this.isActive = function() {
22424 return !!context;
22425 };
22426
22427 }
22428
22429
22430 MoveCanvas.$inject = [
22431 'eventBus',
22432 'canvas'
22433 ];
22434
22435
22436
22437 // helpers ///////
22438
22439 function length(point) {
22440 return Math.sqrt(Math.pow(point.x, 2) + Math.pow(point.y, 2));
22441 }
22442
22443 var MoveCanvasModule = {
22444 __init__: [ 'moveCanvas' ],
22445 moveCanvas: [ 'type', MoveCanvas ]
22446 };
22447
22448 /**
22449 * Get the logarithm of x with base 10
22450 * @param {Integer} value
22451 */
22452 function log10(x) {
22453 return Math.log(x) / Math.log(10);
22454 }
22455
22456 /**
22457 * Get step size for given range and number of steps.
22458 *
22459 * @param {Object} range
22460 * @param {number} range.min
22461 * @param {number} range.max
22462 */
22463 function getStepSize(range, steps) {
22464
22465 var minLinearRange = log10(range.min),
22466 maxLinearRange = log10(range.max);
22467
22468 var absoluteLinearRange = Math.abs(minLinearRange) + Math.abs(maxLinearRange);
22469
22470 return absoluteLinearRange / steps;
22471 }
22472
22473 function cap(range, scale) {
22474 return Math.max(range.min, Math.min(range.max, scale));
22475 }
22476
22477 var sign = Math.sign || function(n) {
22478 return n >= 0 ? 1 : -1;
22479 };
22480
22481 var RANGE = { min: 0.2, max: 4 },
22482 NUM_STEPS = 10;
22483
22484 var DELTA_THRESHOLD = 0.1;
22485
22486 var DEFAULT_SCALE = 0.75;
22487
22488 /**
22489 * An implementation of zooming and scrolling within the
22490 * {@link Canvas} via the mouse wheel.
22491 *
22492 * Mouse wheel zooming / scrolling may be disabled using
22493 * the {@link toggle(enabled)} method.
22494 *
22495 * @param {Object} [config]
22496 * @param {boolean} [config.enabled=true] default enabled state
22497 * @param {number} [config.scale=.75] scroll sensivity
22498 * @param {EventBus} eventBus
22499 * @param {Canvas} canvas
22500 */
22501 function ZoomScroll(config, eventBus, canvas) {
22502
22503 config = config || {};
22504
22505 this._enabled = false;
22506
22507 this._canvas = canvas;
22508 this._container = canvas._container;
22509
22510 this._handleWheel = bind$2(this._handleWheel, this);
22511
22512 this._totalDelta = 0;
22513 this._scale = config.scale || DEFAULT_SCALE;
22514
22515 var self = this;
22516
22517 eventBus.on('canvas.init', function(e) {
22518 self._init(config.enabled !== false);
22519 });
22520 }
22521
22522 ZoomScroll.$inject = [
22523 'config.zoomScroll',
22524 'eventBus',
22525 'canvas'
22526 ];
22527
22528 ZoomScroll.prototype.scroll = function scroll(delta) {
22529 this._canvas.scroll(delta);
22530 };
22531
22532
22533 ZoomScroll.prototype.reset = function reset() {
22534 this._canvas.zoom('fit-viewport');
22535 };
22536
22537 /**
22538 * Zoom depending on delta.
22539 *
22540 * @param {number} delta
22541 * @param {Object} position
22542 */
22543 ZoomScroll.prototype.zoom = function zoom(delta, position) {
22544
22545 // zoom with half the step size of stepZoom
22546 var stepSize = getStepSize(RANGE, NUM_STEPS * 2);
22547
22548 // add until threshold reached
22549 this._totalDelta += delta;
22550
22551 if (Math.abs(this._totalDelta) > DELTA_THRESHOLD) {
22552 this._zoom(delta, position, stepSize);
22553
22554 // reset
22555 this._totalDelta = 0;
22556 }
22557 };
22558
22559
22560 ZoomScroll.prototype._handleWheel = function handleWheel(event) {
22561
22562 // event is already handled by '.djs-scrollable'
22563 if (closest(event.target, '.djs-scrollable', true)) {
22564 return;
22565 }
22566
22567 var element = this._container;
22568
22569 event.preventDefault();
22570
22571 // pinch to zoom is mapped to wheel + ctrlKey = true
22572 // in modern browsers (!)
22573
22574 var isZoom = event.ctrlKey;
22575
22576 var isHorizontalScroll = event.shiftKey;
22577
22578 var factor = -1 * this._scale,
22579 delta;
22580
22581 if (isZoom) {
22582 factor *= event.deltaMode === 0 ? 0.020 : 0.32;
22583 } else {
22584 factor *= event.deltaMode === 0 ? 1.0 : 16.0;
22585 }
22586
22587 if (isZoom) {
22588 var elementRect = element.getBoundingClientRect();
22589
22590 var offset = {
22591 x: event.clientX - elementRect.left,
22592 y: event.clientY - elementRect.top
22593 };
22594
22595 delta = (
22596 Math.sqrt(
22597 Math.pow(event.deltaY, 2) +
22598 Math.pow(event.deltaX, 2)
22599 ) * sign(event.deltaY) * factor
22600 );
22601
22602 // zoom in relative to diagram {x,y} coordinates
22603 this.zoom(delta, offset);
22604 } else {
22605
22606 if (isHorizontalScroll) {
22607 delta = {
22608 dx: factor * event.deltaY,
22609 dy: 0
22610 };
22611 } else {
22612 delta = {
22613 dx: factor * event.deltaX,
22614 dy: factor * event.deltaY
22615 };
22616 }
22617
22618 this.scroll(delta);
22619 }
22620 };
22621
22622 /**
22623 * Zoom with fixed step size.
22624 *
22625 * @param {number} delta - Zoom delta (1 for zooming in, -1 for out).
22626 * @param {Object} position
22627 */
22628 ZoomScroll.prototype.stepZoom = function stepZoom(delta, position) {
22629
22630 var stepSize = getStepSize(RANGE, NUM_STEPS);
22631
22632 this._zoom(delta, position, stepSize);
22633 };
22634
22635
22636 /**
22637 * Zoom in/out given a step size.
22638 *
22639 * @param {number} delta
22640 * @param {Object} position
22641 * @param {number} stepSize
22642 */
22643 ZoomScroll.prototype._zoom = function(delta, position, stepSize) {
22644 var canvas = this._canvas;
22645
22646 var direction = delta > 0 ? 1 : -1;
22647
22648 var currentLinearZoomLevel = log10(canvas.zoom());
22649
22650 // snap to a proximate zoom step
22651 var newLinearZoomLevel = Math.round(currentLinearZoomLevel / stepSize) * stepSize;
22652
22653 // increase or decrease one zoom step in the given direction
22654 newLinearZoomLevel += stepSize * direction;
22655
22656 // calculate the absolute logarithmic zoom level based on the linear zoom level
22657 // (e.g. 2 for an absolute x2 zoom)
22658 var newLogZoomLevel = Math.pow(10, newLinearZoomLevel);
22659
22660 canvas.zoom(cap(RANGE, newLogZoomLevel), position);
22661 };
22662
22663
22664 /**
22665 * Toggle the zoom scroll ability via mouse wheel.
22666 *
22667 * @param {boolean} [newEnabled] new enabled state
22668 */
22669 ZoomScroll.prototype.toggle = function toggle(newEnabled) {
22670
22671 var element = this._container;
22672 var handleWheel = this._handleWheel;
22673
22674 var oldEnabled = this._enabled;
22675
22676 if (typeof newEnabled === 'undefined') {
22677 newEnabled = !oldEnabled;
22678 }
22679
22680 // only react on actual changes
22681 if (oldEnabled !== newEnabled) {
22682
22683 // add or remove wheel listener based on
22684 // changed enabled state
22685 componentEvent[newEnabled ? 'bind' : 'unbind'](element, 'wheel', handleWheel, false);
22686 }
22687
22688 this._enabled = newEnabled;
22689
22690 return newEnabled;
22691 };
22692
22693
22694 ZoomScroll.prototype._init = function(newEnabled) {
22695 this.toggle(newEnabled);
22696 };
22697
22698 var ZoomScrollModule = {
22699 __init__: [ 'zoomScroll' ],
22700 zoomScroll: [ 'type', ZoomScroll ]
22701 };
22702
22703 /**
22704 * A viewer that includes mouse navigation facilities
22705 *
22706 * @param {Object} options
22707 */
22708 function NavigatedViewer(options) {
22709 Viewer.call(this, options);
22710 }
22711
22712 inherits$1(NavigatedViewer, Viewer);
22713
22714
22715 NavigatedViewer.prototype._navigationModules = [
22716 KeyboardMoveModule,
22717 MoveCanvasModule,
22718 ZoomScrollModule
22719 ];
22720
22721 NavigatedViewer.prototype._modules = [].concat(
22722 Viewer.prototype._modules,
22723 NavigatedViewer.prototype._navigationModules
22724 );
22725
22726 var hammer = {exports: {}};
22727
22728 /*! Hammer.JS - v2.0.7 - 2016-04-22
22729 * http://hammerjs.github.io/
22730 *
22731 * Copyright (c) 2016 Jorik Tangelder;
22732 * Licensed under the MIT license */
22733
22734 (function (module) {
22735 (function(window, document, exportName, undefined$1) {
22736
22737 var VENDOR_PREFIXES = ['', 'webkit', 'Moz', 'MS', 'ms', 'o'];
22738 var TEST_ELEMENT = document.createElement('div');
22739
22740 var TYPE_FUNCTION = 'function';
22741
22742 var round = Math.round;
22743 var abs = Math.abs;
22744 var now = Date.now;
22745
22746 /**
22747 * set a timeout with a given scope
22748 * @param {Function} fn
22749 * @param {Number} timeout
22750 * @param {Object} context
22751 * @returns {number}
22752 */
22753 function setTimeoutContext(fn, timeout, context) {
22754 return setTimeout(bindFn(fn, context), timeout);
22755 }
22756
22757 /**
22758 * if the argument is an array, we want to execute the fn on each entry
22759 * if it aint an array we don't want to do a thing.
22760 * this is used by all the methods that accept a single and array argument.
22761 * @param {*|Array} arg
22762 * @param {String} fn
22763 * @param {Object} [context]
22764 * @returns {Boolean}
22765 */
22766 function invokeArrayArg(arg, fn, context) {
22767 if (Array.isArray(arg)) {
22768 each(arg, context[fn], context);
22769 return true;
22770 }
22771 return false;
22772 }
22773
22774 /**
22775 * walk objects and arrays
22776 * @param {Object} obj
22777 * @param {Function} iterator
22778 * @param {Object} context
22779 */
22780 function each(obj, iterator, context) {
22781 var i;
22782
22783 if (!obj) {
22784 return;
22785 }
22786
22787 if (obj.forEach) {
22788 obj.forEach(iterator, context);
22789 } else if (obj.length !== undefined$1) {
22790 i = 0;
22791 while (i < obj.length) {
22792 iterator.call(context, obj[i], i, obj);
22793 i++;
22794 }
22795 } else {
22796 for (i in obj) {
22797 obj.hasOwnProperty(i) && iterator.call(context, obj[i], i, obj);
22798 }
22799 }
22800 }
22801
22802 /**
22803 * wrap a method with a deprecation warning and stack trace
22804 * @param {Function} method
22805 * @param {String} name
22806 * @param {String} message
22807 * @returns {Function} A new function wrapping the supplied method.
22808 */
22809 function deprecate(method, name, message) {
22810 var deprecationMessage = 'DEPRECATED METHOD: ' + name + '\n' + message + ' AT \n';
22811 return function() {
22812 var e = new Error('get-stack-trace');
22813 var stack = e && e.stack ? e.stack.replace(/^[^\(]+?[\n$]/gm, '')
22814 .replace(/^\s+at\s+/gm, '')
22815 .replace(/^Object.<anonymous>\s*\(/gm, '{anonymous}()@') : 'Unknown Stack Trace';
22816
22817 var log = window.console && (window.console.warn || window.console.log);
22818 if (log) {
22819 log.call(window.console, deprecationMessage, stack);
22820 }
22821 return method.apply(this, arguments);
22822 };
22823 }
22824
22825 /**
22826 * extend object.
22827 * means that properties in dest will be overwritten by the ones in src.
22828 * @param {Object} target
22829 * @param {...Object} objects_to_assign
22830 * @returns {Object} target
22831 */
22832 var assign;
22833 if (typeof Object.assign !== 'function') {
22834 assign = function assign(target) {
22835 if (target === undefined$1 || target === null) {
22836 throw new TypeError('Cannot convert undefined or null to object');
22837 }
22838
22839 var output = Object(target);
22840 for (var index = 1; index < arguments.length; index++) {
22841 var source = arguments[index];
22842 if (source !== undefined$1 && source !== null) {
22843 for (var nextKey in source) {
22844 if (source.hasOwnProperty(nextKey)) {
22845 output[nextKey] = source[nextKey];
22846 }
22847 }
22848 }
22849 }
22850 return output;
22851 };
22852 } else {
22853 assign = Object.assign;
22854 }
22855
22856 /**
22857 * extend object.
22858 * means that properties in dest will be overwritten by the ones in src.
22859 * @param {Object} dest
22860 * @param {Object} src
22861 * @param {Boolean} [merge=false]
22862 * @returns {Object} dest
22863 */
22864 var extend = deprecate(function extend(dest, src, merge) {
22865 var keys = Object.keys(src);
22866 var i = 0;
22867 while (i < keys.length) {
22868 if (!merge || (merge && dest[keys[i]] === undefined$1)) {
22869 dest[keys[i]] = src[keys[i]];
22870 }
22871 i++;
22872 }
22873 return dest;
22874 }, 'extend', 'Use `assign`.');
22875
22876 /**
22877 * merge the values from src in the dest.
22878 * means that properties that exist in dest will not be overwritten by src
22879 * @param {Object} dest
22880 * @param {Object} src
22881 * @returns {Object} dest
22882 */
22883 var merge = deprecate(function merge(dest, src) {
22884 return extend(dest, src, true);
22885 }, 'merge', 'Use `assign`.');
22886
22887 /**
22888 * simple class inheritance
22889 * @param {Function} child
22890 * @param {Function} base
22891 * @param {Object} [properties]
22892 */
22893 function inherit(child, base, properties) {
22894 var baseP = base.prototype,
22895 childP;
22896
22897 childP = child.prototype = Object.create(baseP);
22898 childP.constructor = child;
22899 childP._super = baseP;
22900
22901 if (properties) {
22902 assign(childP, properties);
22903 }
22904 }
22905
22906 /**
22907 * simple function bind
22908 * @param {Function} fn
22909 * @param {Object} context
22910 * @returns {Function}
22911 */
22912 function bindFn(fn, context) {
22913 return function boundFn() {
22914 return fn.apply(context, arguments);
22915 };
22916 }
22917
22918 /**
22919 * let a boolean value also be a function that must return a boolean
22920 * this first item in args will be used as the context
22921 * @param {Boolean|Function} val
22922 * @param {Array} [args]
22923 * @returns {Boolean}
22924 */
22925 function boolOrFn(val, args) {
22926 if (typeof val == TYPE_FUNCTION) {
22927 return val.apply(args ? args[0] || undefined$1 : undefined$1, args);
22928 }
22929 return val;
22930 }
22931
22932 /**
22933 * use the val2 when val1 is undefined
22934 * @param {*} val1
22935 * @param {*} val2
22936 * @returns {*}
22937 */
22938 function ifUndefined(val1, val2) {
22939 return (val1 === undefined$1) ? val2 : val1;
22940 }
22941
22942 /**
22943 * addEventListener with multiple events at once
22944 * @param {EventTarget} target
22945 * @param {String} types
22946 * @param {Function} handler
22947 */
22948 function addEventListeners(target, types, handler) {
22949 each(splitStr(types), function(type) {
22950 target.addEventListener(type, handler, false);
22951 });
22952 }
22953
22954 /**
22955 * removeEventListener with multiple events at once
22956 * @param {EventTarget} target
22957 * @param {String} types
22958 * @param {Function} handler
22959 */
22960 function removeEventListeners(target, types, handler) {
22961 each(splitStr(types), function(type) {
22962 target.removeEventListener(type, handler, false);
22963 });
22964 }
22965
22966 /**
22967 * find if a node is in the given parent
22968 * @method hasParent
22969 * @param {HTMLElement} node
22970 * @param {HTMLElement} parent
22971 * @return {Boolean} found
22972 */
22973 function hasParent(node, parent) {
22974 while (node) {
22975 if (node == parent) {
22976 return true;
22977 }
22978 node = node.parentNode;
22979 }
22980 return false;
22981 }
22982
22983 /**
22984 * small indexOf wrapper
22985 * @param {String} str
22986 * @param {String} find
22987 * @returns {Boolean} found
22988 */
22989 function inStr(str, find) {
22990 return str.indexOf(find) > -1;
22991 }
22992
22993 /**
22994 * split string on whitespace
22995 * @param {String} str
22996 * @returns {Array} words
22997 */
22998 function splitStr(str) {
22999 return str.trim().split(/\s+/g);
23000 }
23001
23002 /**
23003 * find if a array contains the object using indexOf or a simple polyFill
23004 * @param {Array} src
23005 * @param {String} find
23006 * @param {String} [findByKey]
23007 * @return {Boolean|Number} false when not found, or the index
23008 */
23009 function inArray(src, find, findByKey) {
23010 if (src.indexOf && !findByKey) {
23011 return src.indexOf(find);
23012 } else {
23013 var i = 0;
23014 while (i < src.length) {
23015 if ((findByKey && src[i][findByKey] == find) || (!findByKey && src[i] === find)) {
23016 return i;
23017 }
23018 i++;
23019 }
23020 return -1;
23021 }
23022 }
23023
23024 /**
23025 * convert array-like objects to real arrays
23026 * @param {Object} obj
23027 * @returns {Array}
23028 */
23029 function toArray(obj) {
23030 return Array.prototype.slice.call(obj, 0);
23031 }
23032
23033 /**
23034 * unique array with objects based on a key (like 'id') or just by the array's value
23035 * @param {Array} src [{id:1},{id:2},{id:1}]
23036 * @param {String} [key]
23037 * @param {Boolean} [sort=False]
23038 * @returns {Array} [{id:1},{id:2}]
23039 */
23040 function uniqueArray(src, key, sort) {
23041 var results = [];
23042 var values = [];
23043 var i = 0;
23044
23045 while (i < src.length) {
23046 var val = key ? src[i][key] : src[i];
23047 if (inArray(values, val) < 0) {
23048 results.push(src[i]);
23049 }
23050 values[i] = val;
23051 i++;
23052 }
23053
23054 if (sort) {
23055 if (!key) {
23056 results = results.sort();
23057 } else {
23058 results = results.sort(function sortUniqueArray(a, b) {
23059 return a[key] > b[key];
23060 });
23061 }
23062 }
23063
23064 return results;
23065 }
23066
23067 /**
23068 * get the prefixed property
23069 * @param {Object} obj
23070 * @param {String} property
23071 * @returns {String|Undefined} prefixed
23072 */
23073 function prefixed(obj, property) {
23074 var prefix, prop;
23075 var camelProp = property[0].toUpperCase() + property.slice(1);
23076
23077 var i = 0;
23078 while (i < VENDOR_PREFIXES.length) {
23079 prefix = VENDOR_PREFIXES[i];
23080 prop = (prefix) ? prefix + camelProp : property;
23081
23082 if (prop in obj) {
23083 return prop;
23084 }
23085 i++;
23086 }
23087 return undefined$1;
23088 }
23089
23090 /**
23091 * get a unique id
23092 * @returns {number} uniqueId
23093 */
23094 var _uniqueId = 1;
23095 function uniqueId() {
23096 return _uniqueId++;
23097 }
23098
23099 /**
23100 * get the window object of an element
23101 * @param {HTMLElement} element
23102 * @returns {DocumentView|Window}
23103 */
23104 function getWindowForElement(element) {
23105 var doc = element.ownerDocument || element;
23106 return (doc.defaultView || doc.parentWindow || window);
23107 }
23108
23109 var MOBILE_REGEX = /mobile|tablet|ip(ad|hone|od)|android/i;
23110
23111 var SUPPORT_TOUCH = ('ontouchstart' in window);
23112 var SUPPORT_POINTER_EVENTS = prefixed(window, 'PointerEvent') !== undefined$1;
23113 var SUPPORT_ONLY_TOUCH = SUPPORT_TOUCH && MOBILE_REGEX.test(navigator.userAgent);
23114
23115 var INPUT_TYPE_TOUCH = 'touch';
23116 var INPUT_TYPE_PEN = 'pen';
23117 var INPUT_TYPE_MOUSE = 'mouse';
23118 var INPUT_TYPE_KINECT = 'kinect';
23119
23120 var COMPUTE_INTERVAL = 25;
23121
23122 var INPUT_START = 1;
23123 var INPUT_MOVE = 2;
23124 var INPUT_END = 4;
23125 var INPUT_CANCEL = 8;
23126
23127 var DIRECTION_NONE = 1;
23128 var DIRECTION_LEFT = 2;
23129 var DIRECTION_RIGHT = 4;
23130 var DIRECTION_UP = 8;
23131 var DIRECTION_DOWN = 16;
23132
23133 var DIRECTION_HORIZONTAL = DIRECTION_LEFT | DIRECTION_RIGHT;
23134 var DIRECTION_VERTICAL = DIRECTION_UP | DIRECTION_DOWN;
23135 var DIRECTION_ALL = DIRECTION_HORIZONTAL | DIRECTION_VERTICAL;
23136
23137 var PROPS_XY = ['x', 'y'];
23138 var PROPS_CLIENT_XY = ['clientX', 'clientY'];
23139
23140 /**
23141 * create new input type manager
23142 * @param {Manager} manager
23143 * @param {Function} callback
23144 * @returns {Input}
23145 * @constructor
23146 */
23147 function Input(manager, callback) {
23148 var self = this;
23149 this.manager = manager;
23150 this.callback = callback;
23151 this.element = manager.element;
23152 this.target = manager.options.inputTarget;
23153
23154 // smaller wrapper around the handler, for the scope and the enabled state of the manager,
23155 // so when disabled the input events are completely bypassed.
23156 this.domHandler = function(ev) {
23157 if (boolOrFn(manager.options.enable, [manager])) {
23158 self.handler(ev);
23159 }
23160 };
23161
23162 this.init();
23163
23164 }
23165
23166 Input.prototype = {
23167 /**
23168 * should handle the inputEvent data and trigger the callback
23169 * @virtual
23170 */
23171 handler: function() { },
23172
23173 /**
23174 * bind the events
23175 */
23176 init: function() {
23177 this.evEl && addEventListeners(this.element, this.evEl, this.domHandler);
23178 this.evTarget && addEventListeners(this.target, this.evTarget, this.domHandler);
23179 this.evWin && addEventListeners(getWindowForElement(this.element), this.evWin, this.domHandler);
23180 },
23181
23182 /**
23183 * unbind the events
23184 */
23185 destroy: function() {
23186 this.evEl && removeEventListeners(this.element, this.evEl, this.domHandler);
23187 this.evTarget && removeEventListeners(this.target, this.evTarget, this.domHandler);
23188 this.evWin && removeEventListeners(getWindowForElement(this.element), this.evWin, this.domHandler);
23189 }
23190 };
23191
23192 /**
23193 * create new input type manager
23194 * called by the Manager constructor
23195 * @param {Hammer} manager
23196 * @returns {Input}
23197 */
23198 function createInputInstance(manager) {
23199 var Type;
23200 var inputClass = manager.options.inputClass;
23201
23202 if (inputClass) {
23203 Type = inputClass;
23204 } else if (SUPPORT_POINTER_EVENTS) {
23205 Type = PointerEventInput;
23206 } else if (SUPPORT_ONLY_TOUCH) {
23207 Type = TouchInput;
23208 } else if (!SUPPORT_TOUCH) {
23209 Type = MouseInput;
23210 } else {
23211 Type = TouchMouseInput;
23212 }
23213 return new (Type)(manager, inputHandler);
23214 }
23215
23216 /**
23217 * handle input events
23218 * @param {Manager} manager
23219 * @param {String} eventType
23220 * @param {Object} input
23221 */
23222 function inputHandler(manager, eventType, input) {
23223 var pointersLen = input.pointers.length;
23224 var changedPointersLen = input.changedPointers.length;
23225 var isFirst = (eventType & INPUT_START && (pointersLen - changedPointersLen === 0));
23226 var isFinal = (eventType & (INPUT_END | INPUT_CANCEL) && (pointersLen - changedPointersLen === 0));
23227
23228 input.isFirst = !!isFirst;
23229 input.isFinal = !!isFinal;
23230
23231 if (isFirst) {
23232 manager.session = {};
23233 }
23234
23235 // source event is the normalized value of the domEvents
23236 // like 'touchstart, mouseup, pointerdown'
23237 input.eventType = eventType;
23238
23239 // compute scale, rotation etc
23240 computeInputData(manager, input);
23241
23242 // emit secret event
23243 manager.emit('hammer.input', input);
23244
23245 manager.recognize(input);
23246 manager.session.prevInput = input;
23247 }
23248
23249 /**
23250 * extend the data with some usable properties like scale, rotate, velocity etc
23251 * @param {Object} manager
23252 * @param {Object} input
23253 */
23254 function computeInputData(manager, input) {
23255 var session = manager.session;
23256 var pointers = input.pointers;
23257 var pointersLength = pointers.length;
23258
23259 // store the first input to calculate the distance and direction
23260 if (!session.firstInput) {
23261 session.firstInput = simpleCloneInputData(input);
23262 }
23263
23264 // to compute scale and rotation we need to store the multiple touches
23265 if (pointersLength > 1 && !session.firstMultiple) {
23266 session.firstMultiple = simpleCloneInputData(input);
23267 } else if (pointersLength === 1) {
23268 session.firstMultiple = false;
23269 }
23270
23271 var firstInput = session.firstInput;
23272 var firstMultiple = session.firstMultiple;
23273 var offsetCenter = firstMultiple ? firstMultiple.center : firstInput.center;
23274
23275 var center = input.center = getCenter(pointers);
23276 input.timeStamp = now();
23277 input.deltaTime = input.timeStamp - firstInput.timeStamp;
23278
23279 input.angle = getAngle(offsetCenter, center);
23280 input.distance = getDistance(offsetCenter, center);
23281
23282 computeDeltaXY(session, input);
23283 input.offsetDirection = getDirection(input.deltaX, input.deltaY);
23284
23285 var overallVelocity = getVelocity(input.deltaTime, input.deltaX, input.deltaY);
23286 input.overallVelocityX = overallVelocity.x;
23287 input.overallVelocityY = overallVelocity.y;
23288 input.overallVelocity = (abs(overallVelocity.x) > abs(overallVelocity.y)) ? overallVelocity.x : overallVelocity.y;
23289
23290 input.scale = firstMultiple ? getScale(firstMultiple.pointers, pointers) : 1;
23291 input.rotation = firstMultiple ? getRotation(firstMultiple.pointers, pointers) : 0;
23292
23293 input.maxPointers = !session.prevInput ? input.pointers.length : ((input.pointers.length >
23294 session.prevInput.maxPointers) ? input.pointers.length : session.prevInput.maxPointers);
23295
23296 computeIntervalInputData(session, input);
23297
23298 // find the correct target
23299 var target = manager.element;
23300 if (hasParent(input.srcEvent.target, target)) {
23301 target = input.srcEvent.target;
23302 }
23303 input.target = target;
23304 }
23305
23306 function computeDeltaXY(session, input) {
23307 var center = input.center;
23308 var offset = session.offsetDelta || {};
23309 var prevDelta = session.prevDelta || {};
23310 var prevInput = session.prevInput || {};
23311
23312 if (input.eventType === INPUT_START || prevInput.eventType === INPUT_END) {
23313 prevDelta = session.prevDelta = {
23314 x: prevInput.deltaX || 0,
23315 y: prevInput.deltaY || 0
23316 };
23317
23318 offset = session.offsetDelta = {
23319 x: center.x,
23320 y: center.y
23321 };
23322 }
23323
23324 input.deltaX = prevDelta.x + (center.x - offset.x);
23325 input.deltaY = prevDelta.y + (center.y - offset.y);
23326 }
23327
23328 /**
23329 * velocity is calculated every x ms
23330 * @param {Object} session
23331 * @param {Object} input
23332 */
23333 function computeIntervalInputData(session, input) {
23334 var last = session.lastInterval || input,
23335 deltaTime = input.timeStamp - last.timeStamp,
23336 velocity, velocityX, velocityY, direction;
23337
23338 if (input.eventType != INPUT_CANCEL && (deltaTime > COMPUTE_INTERVAL || last.velocity === undefined$1)) {
23339 var deltaX = input.deltaX - last.deltaX;
23340 var deltaY = input.deltaY - last.deltaY;
23341
23342 var v = getVelocity(deltaTime, deltaX, deltaY);
23343 velocityX = v.x;
23344 velocityY = v.y;
23345 velocity = (abs(v.x) > abs(v.y)) ? v.x : v.y;
23346 direction = getDirection(deltaX, deltaY);
23347
23348 session.lastInterval = input;
23349 } else {
23350 // use latest velocity info if it doesn't overtake a minimum period
23351 velocity = last.velocity;
23352 velocityX = last.velocityX;
23353 velocityY = last.velocityY;
23354 direction = last.direction;
23355 }
23356
23357 input.velocity = velocity;
23358 input.velocityX = velocityX;
23359 input.velocityY = velocityY;
23360 input.direction = direction;
23361 }
23362
23363 /**
23364 * create a simple clone from the input used for storage of firstInput and firstMultiple
23365 * @param {Object} input
23366 * @returns {Object} clonedInputData
23367 */
23368 function simpleCloneInputData(input) {
23369 // make a simple copy of the pointers because we will get a reference if we don't
23370 // we only need clientXY for the calculations
23371 var pointers = [];
23372 var i = 0;
23373 while (i < input.pointers.length) {
23374 pointers[i] = {
23375 clientX: round(input.pointers[i].clientX),
23376 clientY: round(input.pointers[i].clientY)
23377 };
23378 i++;
23379 }
23380
23381 return {
23382 timeStamp: now(),
23383 pointers: pointers,
23384 center: getCenter(pointers),
23385 deltaX: input.deltaX,
23386 deltaY: input.deltaY
23387 };
23388 }
23389
23390 /**
23391 * get the center of all the pointers
23392 * @param {Array} pointers
23393 * @return {Object} center contains `x` and `y` properties
23394 */
23395 function getCenter(pointers) {
23396 var pointersLength = pointers.length;
23397
23398 // no need to loop when only one touch
23399 if (pointersLength === 1) {
23400 return {
23401 x: round(pointers[0].clientX),
23402 y: round(pointers[0].clientY)
23403 };
23404 }
23405
23406 var x = 0, y = 0, i = 0;
23407 while (i < pointersLength) {
23408 x += pointers[i].clientX;
23409 y += pointers[i].clientY;
23410 i++;
23411 }
23412
23413 return {
23414 x: round(x / pointersLength),
23415 y: round(y / pointersLength)
23416 };
23417 }
23418
23419 /**
23420 * calculate the velocity between two points. unit is in px per ms.
23421 * @param {Number} deltaTime
23422 * @param {Number} x
23423 * @param {Number} y
23424 * @return {Object} velocity `x` and `y`
23425 */
23426 function getVelocity(deltaTime, x, y) {
23427 return {
23428 x: x / deltaTime || 0,
23429 y: y / deltaTime || 0
23430 };
23431 }
23432
23433 /**
23434 * get the direction between two points
23435 * @param {Number} x
23436 * @param {Number} y
23437 * @return {Number} direction
23438 */
23439 function getDirection(x, y) {
23440 if (x === y) {
23441 return DIRECTION_NONE;
23442 }
23443
23444 if (abs(x) >= abs(y)) {
23445 return x < 0 ? DIRECTION_LEFT : DIRECTION_RIGHT;
23446 }
23447 return y < 0 ? DIRECTION_UP : DIRECTION_DOWN;
23448 }
23449
23450 /**
23451 * calculate the absolute distance between two points
23452 * @param {Object} p1 {x, y}
23453 * @param {Object} p2 {x, y}
23454 * @param {Array} [props] containing x and y keys
23455 * @return {Number} distance
23456 */
23457 function getDistance(p1, p2, props) {
23458 if (!props) {
23459 props = PROPS_XY;
23460 }
23461 var x = p2[props[0]] - p1[props[0]],
23462 y = p2[props[1]] - p1[props[1]];
23463
23464 return Math.sqrt((x * x) + (y * y));
23465 }
23466
23467 /**
23468 * calculate the angle between two coordinates
23469 * @param {Object} p1
23470 * @param {Object} p2
23471 * @param {Array} [props] containing x and y keys
23472 * @return {Number} angle
23473 */
23474 function getAngle(p1, p2, props) {
23475 if (!props) {
23476 props = PROPS_XY;
23477 }
23478 var x = p2[props[0]] - p1[props[0]],
23479 y = p2[props[1]] - p1[props[1]];
23480 return Math.atan2(y, x) * 180 / Math.PI;
23481 }
23482
23483 /**
23484 * calculate the rotation degrees between two pointersets
23485 * @param {Array} start array of pointers
23486 * @param {Array} end array of pointers
23487 * @return {Number} rotation
23488 */
23489 function getRotation(start, end) {
23490 return getAngle(end[1], end[0], PROPS_CLIENT_XY) + getAngle(start[1], start[0], PROPS_CLIENT_XY);
23491 }
23492
23493 /**
23494 * calculate the scale factor between two pointersets
23495 * no scale is 1, and goes down to 0 when pinched together, and bigger when pinched out
23496 * @param {Array} start array of pointers
23497 * @param {Array} end array of pointers
23498 * @return {Number} scale
23499 */
23500 function getScale(start, end) {
23501 return getDistance(end[0], end[1], PROPS_CLIENT_XY) / getDistance(start[0], start[1], PROPS_CLIENT_XY);
23502 }
23503
23504 var MOUSE_INPUT_MAP = {
23505 mousedown: INPUT_START,
23506 mousemove: INPUT_MOVE,
23507 mouseup: INPUT_END
23508 };
23509
23510 var MOUSE_ELEMENT_EVENTS = 'mousedown';
23511 var MOUSE_WINDOW_EVENTS = 'mousemove mouseup';
23512
23513 /**
23514 * Mouse events input
23515 * @constructor
23516 * @extends Input
23517 */
23518 function MouseInput() {
23519 this.evEl = MOUSE_ELEMENT_EVENTS;
23520 this.evWin = MOUSE_WINDOW_EVENTS;
23521
23522 this.pressed = false; // mousedown state
23523
23524 Input.apply(this, arguments);
23525 }
23526
23527 inherit(MouseInput, Input, {
23528 /**
23529 * handle mouse events
23530 * @param {Object} ev
23531 */
23532 handler: function MEhandler(ev) {
23533 var eventType = MOUSE_INPUT_MAP[ev.type];
23534
23535 // on start we want to have the left mouse button down
23536 if (eventType & INPUT_START && ev.button === 0) {
23537 this.pressed = true;
23538 }
23539
23540 if (eventType & INPUT_MOVE && ev.which !== 1) {
23541 eventType = INPUT_END;
23542 }
23543
23544 // mouse must be down
23545 if (!this.pressed) {
23546 return;
23547 }
23548
23549 if (eventType & INPUT_END) {
23550 this.pressed = false;
23551 }
23552
23553 this.callback(this.manager, eventType, {
23554 pointers: [ev],
23555 changedPointers: [ev],
23556 pointerType: INPUT_TYPE_MOUSE,
23557 srcEvent: ev
23558 });
23559 }
23560 });
23561
23562 var POINTER_INPUT_MAP = {
23563 pointerdown: INPUT_START,
23564 pointermove: INPUT_MOVE,
23565 pointerup: INPUT_END,
23566 pointercancel: INPUT_CANCEL,
23567 pointerout: INPUT_CANCEL
23568 };
23569
23570 // in IE10 the pointer types is defined as an enum
23571 var IE10_POINTER_TYPE_ENUM = {
23572 2: INPUT_TYPE_TOUCH,
23573 3: INPUT_TYPE_PEN,
23574 4: INPUT_TYPE_MOUSE,
23575 5: INPUT_TYPE_KINECT // see https://twitter.com/jacobrossi/status/480596438489890816
23576 };
23577
23578 var POINTER_ELEMENT_EVENTS = 'pointerdown';
23579 var POINTER_WINDOW_EVENTS = 'pointermove pointerup pointercancel';
23580
23581 // IE10 has prefixed support, and case-sensitive
23582 if (window.MSPointerEvent && !window.PointerEvent) {
23583 POINTER_ELEMENT_EVENTS = 'MSPointerDown';
23584 POINTER_WINDOW_EVENTS = 'MSPointerMove MSPointerUp MSPointerCancel';
23585 }
23586
23587 /**
23588 * Pointer events input
23589 * @constructor
23590 * @extends Input
23591 */
23592 function PointerEventInput() {
23593 this.evEl = POINTER_ELEMENT_EVENTS;
23594 this.evWin = POINTER_WINDOW_EVENTS;
23595
23596 Input.apply(this, arguments);
23597
23598 this.store = (this.manager.session.pointerEvents = []);
23599 }
23600
23601 inherit(PointerEventInput, Input, {
23602 /**
23603 * handle mouse events
23604 * @param {Object} ev
23605 */
23606 handler: function PEhandler(ev) {
23607 var store = this.store;
23608 var removePointer = false;
23609
23610 var eventTypeNormalized = ev.type.toLowerCase().replace('ms', '');
23611 var eventType = POINTER_INPUT_MAP[eventTypeNormalized];
23612 var pointerType = IE10_POINTER_TYPE_ENUM[ev.pointerType] || ev.pointerType;
23613
23614 var isTouch = (pointerType == INPUT_TYPE_TOUCH);
23615
23616 // get index of the event in the store
23617 var storeIndex = inArray(store, ev.pointerId, 'pointerId');
23618
23619 // start and mouse must be down
23620 if (eventType & INPUT_START && (ev.button === 0 || isTouch)) {
23621 if (storeIndex < 0) {
23622 store.push(ev);
23623 storeIndex = store.length - 1;
23624 }
23625 } else if (eventType & (INPUT_END | INPUT_CANCEL)) {
23626 removePointer = true;
23627 }
23628
23629 // it not found, so the pointer hasn't been down (so it's probably a hover)
23630 if (storeIndex < 0) {
23631 return;
23632 }
23633
23634 // update the event in the store
23635 store[storeIndex] = ev;
23636
23637 this.callback(this.manager, eventType, {
23638 pointers: store,
23639 changedPointers: [ev],
23640 pointerType: pointerType,
23641 srcEvent: ev
23642 });
23643
23644 if (removePointer) {
23645 // remove from the store
23646 store.splice(storeIndex, 1);
23647 }
23648 }
23649 });
23650
23651 var SINGLE_TOUCH_INPUT_MAP = {
23652 touchstart: INPUT_START,
23653 touchmove: INPUT_MOVE,
23654 touchend: INPUT_END,
23655 touchcancel: INPUT_CANCEL
23656 };
23657
23658 var SINGLE_TOUCH_TARGET_EVENTS = 'touchstart';
23659 var SINGLE_TOUCH_WINDOW_EVENTS = 'touchstart touchmove touchend touchcancel';
23660
23661 /**
23662 * Touch events input
23663 * @constructor
23664 * @extends Input
23665 */
23666 function SingleTouchInput() {
23667 this.evTarget = SINGLE_TOUCH_TARGET_EVENTS;
23668 this.evWin = SINGLE_TOUCH_WINDOW_EVENTS;
23669 this.started = false;
23670
23671 Input.apply(this, arguments);
23672 }
23673
23674 inherit(SingleTouchInput, Input, {
23675 handler: function TEhandler(ev) {
23676 var type = SINGLE_TOUCH_INPUT_MAP[ev.type];
23677
23678 // should we handle the touch events?
23679 if (type === INPUT_START) {
23680 this.started = true;
23681 }
23682
23683 if (!this.started) {
23684 return;
23685 }
23686
23687 var touches = normalizeSingleTouches.call(this, ev, type);
23688
23689 // when done, reset the started state
23690 if (type & (INPUT_END | INPUT_CANCEL) && touches[0].length - touches[1].length === 0) {
23691 this.started = false;
23692 }
23693
23694 this.callback(this.manager, type, {
23695 pointers: touches[0],
23696 changedPointers: touches[1],
23697 pointerType: INPUT_TYPE_TOUCH,
23698 srcEvent: ev
23699 });
23700 }
23701 });
23702
23703 /**
23704 * @this {TouchInput}
23705 * @param {Object} ev
23706 * @param {Number} type flag
23707 * @returns {undefined|Array} [all, changed]
23708 */
23709 function normalizeSingleTouches(ev, type) {
23710 var all = toArray(ev.touches);
23711 var changed = toArray(ev.changedTouches);
23712
23713 if (type & (INPUT_END | INPUT_CANCEL)) {
23714 all = uniqueArray(all.concat(changed), 'identifier', true);
23715 }
23716
23717 return [all, changed];
23718 }
23719
23720 var TOUCH_INPUT_MAP = {
23721 touchstart: INPUT_START,
23722 touchmove: INPUT_MOVE,
23723 touchend: INPUT_END,
23724 touchcancel: INPUT_CANCEL
23725 };
23726
23727 var TOUCH_TARGET_EVENTS = 'touchstart touchmove touchend touchcancel';
23728
23729 /**
23730 * Multi-user touch events input
23731 * @constructor
23732 * @extends Input
23733 */
23734 function TouchInput() {
23735 this.evTarget = TOUCH_TARGET_EVENTS;
23736 this.targetIds = {};
23737
23738 Input.apply(this, arguments);
23739 }
23740
23741 inherit(TouchInput, Input, {
23742 handler: function MTEhandler(ev) {
23743 var type = TOUCH_INPUT_MAP[ev.type];
23744 var touches = getTouches.call(this, ev, type);
23745 if (!touches) {
23746 return;
23747 }
23748
23749 this.callback(this.manager, type, {
23750 pointers: touches[0],
23751 changedPointers: touches[1],
23752 pointerType: INPUT_TYPE_TOUCH,
23753 srcEvent: ev
23754 });
23755 }
23756 });
23757
23758 /**
23759 * @this {TouchInput}
23760 * @param {Object} ev
23761 * @param {Number} type flag
23762 * @returns {undefined|Array} [all, changed]
23763 */
23764 function getTouches(ev, type) {
23765 var allTouches = toArray(ev.touches);
23766 var targetIds = this.targetIds;
23767
23768 // when there is only one touch, the process can be simplified
23769 if (type & (INPUT_START | INPUT_MOVE) && allTouches.length === 1) {
23770 targetIds[allTouches[0].identifier] = true;
23771 return [allTouches, allTouches];
23772 }
23773
23774 var i,
23775 targetTouches,
23776 changedTouches = toArray(ev.changedTouches),
23777 changedTargetTouches = [],
23778 target = this.target;
23779
23780 // get target touches from touches
23781 targetTouches = allTouches.filter(function(touch) {
23782 return hasParent(touch.target, target);
23783 });
23784
23785 // collect touches
23786 if (type === INPUT_START) {
23787 i = 0;
23788 while (i < targetTouches.length) {
23789 targetIds[targetTouches[i].identifier] = true;
23790 i++;
23791 }
23792 }
23793
23794 // filter changed touches to only contain touches that exist in the collected target ids
23795 i = 0;
23796 while (i < changedTouches.length) {
23797 if (targetIds[changedTouches[i].identifier]) {
23798 changedTargetTouches.push(changedTouches[i]);
23799 }
23800
23801 // cleanup removed touches
23802 if (type & (INPUT_END | INPUT_CANCEL)) {
23803 delete targetIds[changedTouches[i].identifier];
23804 }
23805 i++;
23806 }
23807
23808 if (!changedTargetTouches.length) {
23809 return;
23810 }
23811
23812 return [
23813 // merge targetTouches with changedTargetTouches so it contains ALL touches, including 'end' and 'cancel'
23814 uniqueArray(targetTouches.concat(changedTargetTouches), 'identifier', true),
23815 changedTargetTouches
23816 ];
23817 }
23818
23819 /**
23820 * Combined touch and mouse input
23821 *
23822 * Touch has a higher priority then mouse, and while touching no mouse events are allowed.
23823 * This because touch devices also emit mouse events while doing a touch.
23824 *
23825 * @constructor
23826 * @extends Input
23827 */
23828
23829 var DEDUP_TIMEOUT = 2500;
23830 var DEDUP_DISTANCE = 25;
23831
23832 function TouchMouseInput() {
23833 Input.apply(this, arguments);
23834
23835 var handler = bindFn(this.handler, this);
23836 this.touch = new TouchInput(this.manager, handler);
23837 this.mouse = new MouseInput(this.manager, handler);
23838
23839 this.primaryTouch = null;
23840 this.lastTouches = [];
23841 }
23842
23843 inherit(TouchMouseInput, Input, {
23844 /**
23845 * handle mouse and touch events
23846 * @param {Hammer} manager
23847 * @param {String} inputEvent
23848 * @param {Object} inputData
23849 */
23850 handler: function TMEhandler(manager, inputEvent, inputData) {
23851 var isTouch = (inputData.pointerType == INPUT_TYPE_TOUCH),
23852 isMouse = (inputData.pointerType == INPUT_TYPE_MOUSE);
23853
23854 if (isMouse && inputData.sourceCapabilities && inputData.sourceCapabilities.firesTouchEvents) {
23855 return;
23856 }
23857
23858 // when we're in a touch event, record touches to de-dupe synthetic mouse event
23859 if (isTouch) {
23860 recordTouches.call(this, inputEvent, inputData);
23861 } else if (isMouse && isSyntheticEvent.call(this, inputData)) {
23862 return;
23863 }
23864
23865 this.callback(manager, inputEvent, inputData);
23866 },
23867
23868 /**
23869 * remove the event listeners
23870 */
23871 destroy: function destroy() {
23872 this.touch.destroy();
23873 this.mouse.destroy();
23874 }
23875 });
23876
23877 function recordTouches(eventType, eventData) {
23878 if (eventType & INPUT_START) {
23879 this.primaryTouch = eventData.changedPointers[0].identifier;
23880 setLastTouch.call(this, eventData);
23881 } else if (eventType & (INPUT_END | INPUT_CANCEL)) {
23882 setLastTouch.call(this, eventData);
23883 }
23884 }
23885
23886 function setLastTouch(eventData) {
23887 var touch = eventData.changedPointers[0];
23888
23889 if (touch.identifier === this.primaryTouch) {
23890 var lastTouch = {x: touch.clientX, y: touch.clientY};
23891 this.lastTouches.push(lastTouch);
23892 var lts = this.lastTouches;
23893 var removeLastTouch = function() {
23894 var i = lts.indexOf(lastTouch);
23895 if (i > -1) {
23896 lts.splice(i, 1);
23897 }
23898 };
23899 setTimeout(removeLastTouch, DEDUP_TIMEOUT);
23900 }
23901 }
23902
23903 function isSyntheticEvent(eventData) {
23904 var x = eventData.srcEvent.clientX, y = eventData.srcEvent.clientY;
23905 for (var i = 0; i < this.lastTouches.length; i++) {
23906 var t = this.lastTouches[i];
23907 var dx = Math.abs(x - t.x), dy = Math.abs(y - t.y);
23908 if (dx <= DEDUP_DISTANCE && dy <= DEDUP_DISTANCE) {
23909 return true;
23910 }
23911 }
23912 return false;
23913 }
23914
23915 var PREFIXED_TOUCH_ACTION = prefixed(TEST_ELEMENT.style, 'touchAction');
23916 var NATIVE_TOUCH_ACTION = PREFIXED_TOUCH_ACTION !== undefined$1;
23917
23918 // magical touchAction value
23919 var TOUCH_ACTION_COMPUTE = 'compute';
23920 var TOUCH_ACTION_AUTO = 'auto';
23921 var TOUCH_ACTION_MANIPULATION = 'manipulation'; // not implemented
23922 var TOUCH_ACTION_NONE = 'none';
23923 var TOUCH_ACTION_PAN_X = 'pan-x';
23924 var TOUCH_ACTION_PAN_Y = 'pan-y';
23925 var TOUCH_ACTION_MAP = getTouchActionProps();
23926
23927 /**
23928 * Touch Action
23929 * sets the touchAction property or uses the js alternative
23930 * @param {Manager} manager
23931 * @param {String} value
23932 * @constructor
23933 */
23934 function TouchAction(manager, value) {
23935 this.manager = manager;
23936 this.set(value);
23937 }
23938
23939 TouchAction.prototype = {
23940 /**
23941 * set the touchAction value on the element or enable the polyfill
23942 * @param {String} value
23943 */
23944 set: function(value) {
23945 // find out the touch-action by the event handlers
23946 if (value == TOUCH_ACTION_COMPUTE) {
23947 value = this.compute();
23948 }
23949
23950 if (NATIVE_TOUCH_ACTION && this.manager.element.style && TOUCH_ACTION_MAP[value]) {
23951 this.manager.element.style[PREFIXED_TOUCH_ACTION] = value;
23952 }
23953 this.actions = value.toLowerCase().trim();
23954 },
23955
23956 /**
23957 * just re-set the touchAction value
23958 */
23959 update: function() {
23960 this.set(this.manager.options.touchAction);
23961 },
23962
23963 /**
23964 * compute the value for the touchAction property based on the recognizer's settings
23965 * @returns {String} value
23966 */
23967 compute: function() {
23968 var actions = [];
23969 each(this.manager.recognizers, function(recognizer) {
23970 if (boolOrFn(recognizer.options.enable, [recognizer])) {
23971 actions = actions.concat(recognizer.getTouchAction());
23972 }
23973 });
23974 return cleanTouchActions(actions.join(' '));
23975 },
23976
23977 /**
23978 * this method is called on each input cycle and provides the preventing of the browser behavior
23979 * @param {Object} input
23980 */
23981 preventDefaults: function(input) {
23982 var srcEvent = input.srcEvent;
23983 var direction = input.offsetDirection;
23984
23985 // if the touch action did prevented once this session
23986 if (this.manager.session.prevented) {
23987 srcEvent.preventDefault();
23988 return;
23989 }
23990
23991 var actions = this.actions;
23992 var hasNone = inStr(actions, TOUCH_ACTION_NONE) && !TOUCH_ACTION_MAP[TOUCH_ACTION_NONE];
23993 var hasPanY = inStr(actions, TOUCH_ACTION_PAN_Y) && !TOUCH_ACTION_MAP[TOUCH_ACTION_PAN_Y];
23994 var hasPanX = inStr(actions, TOUCH_ACTION_PAN_X) && !TOUCH_ACTION_MAP[TOUCH_ACTION_PAN_X];
23995
23996 if (hasNone) {
23997 //do not prevent defaults if this is a tap gesture
23998
23999 var isTapPointer = input.pointers.length === 1;
24000 var isTapMovement = input.distance < 2;
24001 var isTapTouchTime = input.deltaTime < 250;
24002
24003 if (isTapPointer && isTapMovement && isTapTouchTime) {
24004 return;
24005 }
24006 }
24007
24008 if (hasPanX && hasPanY) {
24009 // `pan-x pan-y` means browser handles all scrolling/panning, do not prevent
24010 return;
24011 }
24012
24013 if (hasNone ||
24014 (hasPanY && direction & DIRECTION_HORIZONTAL) ||
24015 (hasPanX && direction & DIRECTION_VERTICAL)) {
24016 return this.preventSrc(srcEvent);
24017 }
24018 },
24019
24020 /**
24021 * call preventDefault to prevent the browser's default behavior (scrolling in most cases)
24022 * @param {Object} srcEvent
24023 */
24024 preventSrc: function(srcEvent) {
24025 this.manager.session.prevented = true;
24026 srcEvent.preventDefault();
24027 }
24028 };
24029
24030 /**
24031 * when the touchActions are collected they are not a valid value, so we need to clean things up. *
24032 * @param {String} actions
24033 * @returns {*}
24034 */
24035 function cleanTouchActions(actions) {
24036 // none
24037 if (inStr(actions, TOUCH_ACTION_NONE)) {
24038 return TOUCH_ACTION_NONE;
24039 }
24040
24041 var hasPanX = inStr(actions, TOUCH_ACTION_PAN_X);
24042 var hasPanY = inStr(actions, TOUCH_ACTION_PAN_Y);
24043
24044 // if both pan-x and pan-y are set (different recognizers
24045 // for different directions, e.g. horizontal pan but vertical swipe?)
24046 // we need none (as otherwise with pan-x pan-y combined none of these
24047 // recognizers will work, since the browser would handle all panning
24048 if (hasPanX && hasPanY) {
24049 return TOUCH_ACTION_NONE;
24050 }
24051
24052 // pan-x OR pan-y
24053 if (hasPanX || hasPanY) {
24054 return hasPanX ? TOUCH_ACTION_PAN_X : TOUCH_ACTION_PAN_Y;
24055 }
24056
24057 // manipulation
24058 if (inStr(actions, TOUCH_ACTION_MANIPULATION)) {
24059 return TOUCH_ACTION_MANIPULATION;
24060 }
24061
24062 return TOUCH_ACTION_AUTO;
24063 }
24064
24065 function getTouchActionProps() {
24066 if (!NATIVE_TOUCH_ACTION) {
24067 return false;
24068 }
24069 var touchMap = {};
24070 var cssSupports = window.CSS && window.CSS.supports;
24071 ['auto', 'manipulation', 'pan-y', 'pan-x', 'pan-x pan-y', 'none'].forEach(function(val) {
24072
24073 // If css.supports is not supported but there is native touch-action assume it supports
24074 // all values. This is the case for IE 10 and 11.
24075 touchMap[val] = cssSupports ? window.CSS.supports('touch-action', val) : true;
24076 });
24077 return touchMap;
24078 }
24079
24080 /**
24081 * Recognizer flow explained; *
24082 * All recognizers have the initial state of POSSIBLE when a input session starts.
24083 * The definition of a input session is from the first input until the last input, with all it's movement in it. *
24084 * Example session for mouse-input: mousedown -> mousemove -> mouseup
24085 *
24086 * On each recognizing cycle (see Manager.recognize) the .recognize() method is executed
24087 * which determines with state it should be.
24088 *
24089 * If the recognizer has the state FAILED, CANCELLED or RECOGNIZED (equals ENDED), it is reset to
24090 * POSSIBLE to give it another change on the next cycle.
24091 *
24092 * Possible
24093 * |
24094 * +-----+---------------+
24095 * | |
24096 * +-----+-----+ |
24097 * | | |
24098 * Failed Cancelled |
24099 * +-------+------+
24100 * | |
24101 * Recognized Began
24102 * |
24103 * Changed
24104 * |
24105 * Ended/Recognized
24106 */
24107 var STATE_POSSIBLE = 1;
24108 var STATE_BEGAN = 2;
24109 var STATE_CHANGED = 4;
24110 var STATE_ENDED = 8;
24111 var STATE_RECOGNIZED = STATE_ENDED;
24112 var STATE_CANCELLED = 16;
24113 var STATE_FAILED = 32;
24114
24115 /**
24116 * Recognizer
24117 * Every recognizer needs to extend from this class.
24118 * @constructor
24119 * @param {Object} options
24120 */
24121 function Recognizer(options) {
24122 this.options = assign({}, this.defaults, options || {});
24123
24124 this.id = uniqueId();
24125
24126 this.manager = null;
24127
24128 // default is enable true
24129 this.options.enable = ifUndefined(this.options.enable, true);
24130
24131 this.state = STATE_POSSIBLE;
24132
24133 this.simultaneous = {};
24134 this.requireFail = [];
24135 }
24136
24137 Recognizer.prototype = {
24138 /**
24139 * @virtual
24140 * @type {Object}
24141 */
24142 defaults: {},
24143
24144 /**
24145 * set options
24146 * @param {Object} options
24147 * @return {Recognizer}
24148 */
24149 set: function(options) {
24150 assign(this.options, options);
24151
24152 // also update the touchAction, in case something changed about the directions/enabled state
24153 this.manager && this.manager.touchAction.update();
24154 return this;
24155 },
24156
24157 /**
24158 * recognize simultaneous with an other recognizer.
24159 * @param {Recognizer} otherRecognizer
24160 * @returns {Recognizer} this
24161 */
24162 recognizeWith: function(otherRecognizer) {
24163 if (invokeArrayArg(otherRecognizer, 'recognizeWith', this)) {
24164 return this;
24165 }
24166
24167 var simultaneous = this.simultaneous;
24168 otherRecognizer = getRecognizerByNameIfManager(otherRecognizer, this);
24169 if (!simultaneous[otherRecognizer.id]) {
24170 simultaneous[otherRecognizer.id] = otherRecognizer;
24171 otherRecognizer.recognizeWith(this);
24172 }
24173 return this;
24174 },
24175
24176 /**
24177 * drop the simultaneous link. it doesnt remove the link on the other recognizer.
24178 * @param {Recognizer} otherRecognizer
24179 * @returns {Recognizer} this
24180 */
24181 dropRecognizeWith: function(otherRecognizer) {
24182 if (invokeArrayArg(otherRecognizer, 'dropRecognizeWith', this)) {
24183 return this;
24184 }
24185
24186 otherRecognizer = getRecognizerByNameIfManager(otherRecognizer, this);
24187 delete this.simultaneous[otherRecognizer.id];
24188 return this;
24189 },
24190
24191 /**
24192 * recognizer can only run when an other is failing
24193 * @param {Recognizer} otherRecognizer
24194 * @returns {Recognizer} this
24195 */
24196 requireFailure: function(otherRecognizer) {
24197 if (invokeArrayArg(otherRecognizer, 'requireFailure', this)) {
24198 return this;
24199 }
24200
24201 var requireFail = this.requireFail;
24202 otherRecognizer = getRecognizerByNameIfManager(otherRecognizer, this);
24203 if (inArray(requireFail, otherRecognizer) === -1) {
24204 requireFail.push(otherRecognizer);
24205 otherRecognizer.requireFailure(this);
24206 }
24207 return this;
24208 },
24209
24210 /**
24211 * drop the requireFailure link. it does not remove the link on the other recognizer.
24212 * @param {Recognizer} otherRecognizer
24213 * @returns {Recognizer} this
24214 */
24215 dropRequireFailure: function(otherRecognizer) {
24216 if (invokeArrayArg(otherRecognizer, 'dropRequireFailure', this)) {
24217 return this;
24218 }
24219
24220 otherRecognizer = getRecognizerByNameIfManager(otherRecognizer, this);
24221 var index = inArray(this.requireFail, otherRecognizer);
24222 if (index > -1) {
24223 this.requireFail.splice(index, 1);
24224 }
24225 return this;
24226 },
24227
24228 /**
24229 * has require failures boolean
24230 * @returns {boolean}
24231 */
24232 hasRequireFailures: function() {
24233 return this.requireFail.length > 0;
24234 },
24235
24236 /**
24237 * if the recognizer can recognize simultaneous with an other recognizer
24238 * @param {Recognizer} otherRecognizer
24239 * @returns {Boolean}
24240 */
24241 canRecognizeWith: function(otherRecognizer) {
24242 return !!this.simultaneous[otherRecognizer.id];
24243 },
24244
24245 /**
24246 * You should use `tryEmit` instead of `emit` directly to check
24247 * that all the needed recognizers has failed before emitting.
24248 * @param {Object} input
24249 */
24250 emit: function(input) {
24251 var self = this;
24252 var state = this.state;
24253
24254 function emit(event) {
24255 self.manager.emit(event, input);
24256 }
24257
24258 // 'panstart' and 'panmove'
24259 if (state < STATE_ENDED) {
24260 emit(self.options.event + stateStr(state));
24261 }
24262
24263 emit(self.options.event); // simple 'eventName' events
24264
24265 if (input.additionalEvent) { // additional event(panleft, panright, pinchin, pinchout...)
24266 emit(input.additionalEvent);
24267 }
24268
24269 // panend and pancancel
24270 if (state >= STATE_ENDED) {
24271 emit(self.options.event + stateStr(state));
24272 }
24273 },
24274
24275 /**
24276 * Check that all the require failure recognizers has failed,
24277 * if true, it emits a gesture event,
24278 * otherwise, setup the state to FAILED.
24279 * @param {Object} input
24280 */
24281 tryEmit: function(input) {
24282 if (this.canEmit()) {
24283 return this.emit(input);
24284 }
24285 // it's failing anyway
24286 this.state = STATE_FAILED;
24287 },
24288
24289 /**
24290 * can we emit?
24291 * @returns {boolean}
24292 */
24293 canEmit: function() {
24294 var i = 0;
24295 while (i < this.requireFail.length) {
24296 if (!(this.requireFail[i].state & (STATE_FAILED | STATE_POSSIBLE))) {
24297 return false;
24298 }
24299 i++;
24300 }
24301 return true;
24302 },
24303
24304 /**
24305 * update the recognizer
24306 * @param {Object} inputData
24307 */
24308 recognize: function(inputData) {
24309 // make a new copy of the inputData
24310 // so we can change the inputData without messing up the other recognizers
24311 var inputDataClone = assign({}, inputData);
24312
24313 // is is enabled and allow recognizing?
24314 if (!boolOrFn(this.options.enable, [this, inputDataClone])) {
24315 this.reset();
24316 this.state = STATE_FAILED;
24317 return;
24318 }
24319
24320 // reset when we've reached the end
24321 if (this.state & (STATE_RECOGNIZED | STATE_CANCELLED | STATE_FAILED)) {
24322 this.state = STATE_POSSIBLE;
24323 }
24324
24325 this.state = this.process(inputDataClone);
24326
24327 // the recognizer has recognized a gesture
24328 // so trigger an event
24329 if (this.state & (STATE_BEGAN | STATE_CHANGED | STATE_ENDED | STATE_CANCELLED)) {
24330 this.tryEmit(inputDataClone);
24331 }
24332 },
24333
24334 /**
24335 * return the state of the recognizer
24336 * the actual recognizing happens in this method
24337 * @virtual
24338 * @param {Object} inputData
24339 * @returns {Const} STATE
24340 */
24341 process: function(inputData) { }, // jshint ignore:line
24342
24343 /**
24344 * return the preferred touch-action
24345 * @virtual
24346 * @returns {Array}
24347 */
24348 getTouchAction: function() { },
24349
24350 /**
24351 * called when the gesture isn't allowed to recognize
24352 * like when another is being recognized or it is disabled
24353 * @virtual
24354 */
24355 reset: function() { }
24356 };
24357
24358 /**
24359 * get a usable string, used as event postfix
24360 * @param {Const} state
24361 * @returns {String} state
24362 */
24363 function stateStr(state) {
24364 if (state & STATE_CANCELLED) {
24365 return 'cancel';
24366 } else if (state & STATE_ENDED) {
24367 return 'end';
24368 } else if (state & STATE_CHANGED) {
24369 return 'move';
24370 } else if (state & STATE_BEGAN) {
24371 return 'start';
24372 }
24373 return '';
24374 }
24375
24376 /**
24377 * direction cons to string
24378 * @param {Const} direction
24379 * @returns {String}
24380 */
24381 function directionStr(direction) {
24382 if (direction == DIRECTION_DOWN) {
24383 return 'down';
24384 } else if (direction == DIRECTION_UP) {
24385 return 'up';
24386 } else if (direction == DIRECTION_LEFT) {
24387 return 'left';
24388 } else if (direction == DIRECTION_RIGHT) {
24389 return 'right';
24390 }
24391 return '';
24392 }
24393
24394 /**
24395 * get a recognizer by name if it is bound to a manager
24396 * @param {Recognizer|String} otherRecognizer
24397 * @param {Recognizer} recognizer
24398 * @returns {Recognizer}
24399 */
24400 function getRecognizerByNameIfManager(otherRecognizer, recognizer) {
24401 var manager = recognizer.manager;
24402 if (manager) {
24403 return manager.get(otherRecognizer);
24404 }
24405 return otherRecognizer;
24406 }
24407
24408 /**
24409 * This recognizer is just used as a base for the simple attribute recognizers.
24410 * @constructor
24411 * @extends Recognizer
24412 */
24413 function AttrRecognizer() {
24414 Recognizer.apply(this, arguments);
24415 }
24416
24417 inherit(AttrRecognizer, Recognizer, {
24418 /**
24419 * @namespace
24420 * @memberof AttrRecognizer
24421 */
24422 defaults: {
24423 /**
24424 * @type {Number}
24425 * @default 1
24426 */
24427 pointers: 1
24428 },
24429
24430 /**
24431 * Used to check if it the recognizer receives valid input, like input.distance > 10.
24432 * @memberof AttrRecognizer
24433 * @param {Object} input
24434 * @returns {Boolean} recognized
24435 */
24436 attrTest: function(input) {
24437 var optionPointers = this.options.pointers;
24438 return optionPointers === 0 || input.pointers.length === optionPointers;
24439 },
24440
24441 /**
24442 * Process the input and return the state for the recognizer
24443 * @memberof AttrRecognizer
24444 * @param {Object} input
24445 * @returns {*} State
24446 */
24447 process: function(input) {
24448 var state = this.state;
24449 var eventType = input.eventType;
24450
24451 var isRecognized = state & (STATE_BEGAN | STATE_CHANGED);
24452 var isValid = this.attrTest(input);
24453
24454 // on cancel input and we've recognized before, return STATE_CANCELLED
24455 if (isRecognized && (eventType & INPUT_CANCEL || !isValid)) {
24456 return state | STATE_CANCELLED;
24457 } else if (isRecognized || isValid) {
24458 if (eventType & INPUT_END) {
24459 return state | STATE_ENDED;
24460 } else if (!(state & STATE_BEGAN)) {
24461 return STATE_BEGAN;
24462 }
24463 return state | STATE_CHANGED;
24464 }
24465 return STATE_FAILED;
24466 }
24467 });
24468
24469 /**
24470 * Pan
24471 * Recognized when the pointer is down and moved in the allowed direction.
24472 * @constructor
24473 * @extends AttrRecognizer
24474 */
24475 function PanRecognizer() {
24476 AttrRecognizer.apply(this, arguments);
24477
24478 this.pX = null;
24479 this.pY = null;
24480 }
24481
24482 inherit(PanRecognizer, AttrRecognizer, {
24483 /**
24484 * @namespace
24485 * @memberof PanRecognizer
24486 */
24487 defaults: {
24488 event: 'pan',
24489 threshold: 10,
24490 pointers: 1,
24491 direction: DIRECTION_ALL
24492 },
24493
24494 getTouchAction: function() {
24495 var direction = this.options.direction;
24496 var actions = [];
24497 if (direction & DIRECTION_HORIZONTAL) {
24498 actions.push(TOUCH_ACTION_PAN_Y);
24499 }
24500 if (direction & DIRECTION_VERTICAL) {
24501 actions.push(TOUCH_ACTION_PAN_X);
24502 }
24503 return actions;
24504 },
24505
24506 directionTest: function(input) {
24507 var options = this.options;
24508 var hasMoved = true;
24509 var distance = input.distance;
24510 var direction = input.direction;
24511 var x = input.deltaX;
24512 var y = input.deltaY;
24513
24514 // lock to axis?
24515 if (!(direction & options.direction)) {
24516 if (options.direction & DIRECTION_HORIZONTAL) {
24517 direction = (x === 0) ? DIRECTION_NONE : (x < 0) ? DIRECTION_LEFT : DIRECTION_RIGHT;
24518 hasMoved = x != this.pX;
24519 distance = Math.abs(input.deltaX);
24520 } else {
24521 direction = (y === 0) ? DIRECTION_NONE : (y < 0) ? DIRECTION_UP : DIRECTION_DOWN;
24522 hasMoved = y != this.pY;
24523 distance = Math.abs(input.deltaY);
24524 }
24525 }
24526 input.direction = direction;
24527 return hasMoved && distance > options.threshold && direction & options.direction;
24528 },
24529
24530 attrTest: function(input) {
24531 return AttrRecognizer.prototype.attrTest.call(this, input) &&
24532 (this.state & STATE_BEGAN || (!(this.state & STATE_BEGAN) && this.directionTest(input)));
24533 },
24534
24535 emit: function(input) {
24536
24537 this.pX = input.deltaX;
24538 this.pY = input.deltaY;
24539
24540 var direction = directionStr(input.direction);
24541
24542 if (direction) {
24543 input.additionalEvent = this.options.event + direction;
24544 }
24545 this._super.emit.call(this, input);
24546 }
24547 });
24548
24549 /**
24550 * Pinch
24551 * Recognized when two or more pointers are moving toward (zoom-in) or away from each other (zoom-out).
24552 * @constructor
24553 * @extends AttrRecognizer
24554 */
24555 function PinchRecognizer() {
24556 AttrRecognizer.apply(this, arguments);
24557 }
24558
24559 inherit(PinchRecognizer, AttrRecognizer, {
24560 /**
24561 * @namespace
24562 * @memberof PinchRecognizer
24563 */
24564 defaults: {
24565 event: 'pinch',
24566 threshold: 0,
24567 pointers: 2
24568 },
24569
24570 getTouchAction: function() {
24571 return [TOUCH_ACTION_NONE];
24572 },
24573
24574 attrTest: function(input) {
24575 return this._super.attrTest.call(this, input) &&
24576 (Math.abs(input.scale - 1) > this.options.threshold || this.state & STATE_BEGAN);
24577 },
24578
24579 emit: function(input) {
24580 if (input.scale !== 1) {
24581 var inOut = input.scale < 1 ? 'in' : 'out';
24582 input.additionalEvent = this.options.event + inOut;
24583 }
24584 this._super.emit.call(this, input);
24585 }
24586 });
24587
24588 /**
24589 * Press
24590 * Recognized when the pointer is down for x ms without any movement.
24591 * @constructor
24592 * @extends Recognizer
24593 */
24594 function PressRecognizer() {
24595 Recognizer.apply(this, arguments);
24596
24597 this._timer = null;
24598 this._input = null;
24599 }
24600
24601 inherit(PressRecognizer, Recognizer, {
24602 /**
24603 * @namespace
24604 * @memberof PressRecognizer
24605 */
24606 defaults: {
24607 event: 'press',
24608 pointers: 1,
24609 time: 251, // minimal time of the pointer to be pressed
24610 threshold: 9 // a minimal movement is ok, but keep it low
24611 },
24612
24613 getTouchAction: function() {
24614 return [TOUCH_ACTION_AUTO];
24615 },
24616
24617 process: function(input) {
24618 var options = this.options;
24619 var validPointers = input.pointers.length === options.pointers;
24620 var validMovement = input.distance < options.threshold;
24621 var validTime = input.deltaTime > options.time;
24622
24623 this._input = input;
24624
24625 // we only allow little movement
24626 // and we've reached an end event, so a tap is possible
24627 if (!validMovement || !validPointers || (input.eventType & (INPUT_END | INPUT_CANCEL) && !validTime)) {
24628 this.reset();
24629 } else if (input.eventType & INPUT_START) {
24630 this.reset();
24631 this._timer = setTimeoutContext(function() {
24632 this.state = STATE_RECOGNIZED;
24633 this.tryEmit();
24634 }, options.time, this);
24635 } else if (input.eventType & INPUT_END) {
24636 return STATE_RECOGNIZED;
24637 }
24638 return STATE_FAILED;
24639 },
24640
24641 reset: function() {
24642 clearTimeout(this._timer);
24643 },
24644
24645 emit: function(input) {
24646 if (this.state !== STATE_RECOGNIZED) {
24647 return;
24648 }
24649
24650 if (input && (input.eventType & INPUT_END)) {
24651 this.manager.emit(this.options.event + 'up', input);
24652 } else {
24653 this._input.timeStamp = now();
24654 this.manager.emit(this.options.event, this._input);
24655 }
24656 }
24657 });
24658
24659 /**
24660 * Rotate
24661 * Recognized when two or more pointer are moving in a circular motion.
24662 * @constructor
24663 * @extends AttrRecognizer
24664 */
24665 function RotateRecognizer() {
24666 AttrRecognizer.apply(this, arguments);
24667 }
24668
24669 inherit(RotateRecognizer, AttrRecognizer, {
24670 /**
24671 * @namespace
24672 * @memberof RotateRecognizer
24673 */
24674 defaults: {
24675 event: 'rotate',
24676 threshold: 0,
24677 pointers: 2
24678 },
24679
24680 getTouchAction: function() {
24681 return [TOUCH_ACTION_NONE];
24682 },
24683
24684 attrTest: function(input) {
24685 return this._super.attrTest.call(this, input) &&
24686 (Math.abs(input.rotation) > this.options.threshold || this.state & STATE_BEGAN);
24687 }
24688 });
24689
24690 /**
24691 * Swipe
24692 * Recognized when the pointer is moving fast (velocity), with enough distance in the allowed direction.
24693 * @constructor
24694 * @extends AttrRecognizer
24695 */
24696 function SwipeRecognizer() {
24697 AttrRecognizer.apply(this, arguments);
24698 }
24699
24700 inherit(SwipeRecognizer, AttrRecognizer, {
24701 /**
24702 * @namespace
24703 * @memberof SwipeRecognizer
24704 */
24705 defaults: {
24706 event: 'swipe',
24707 threshold: 10,
24708 velocity: 0.3,
24709 direction: DIRECTION_HORIZONTAL | DIRECTION_VERTICAL,
24710 pointers: 1
24711 },
24712
24713 getTouchAction: function() {
24714 return PanRecognizer.prototype.getTouchAction.call(this);
24715 },
24716
24717 attrTest: function(input) {
24718 var direction = this.options.direction;
24719 var velocity;
24720
24721 if (direction & (DIRECTION_HORIZONTAL | DIRECTION_VERTICAL)) {
24722 velocity = input.overallVelocity;
24723 } else if (direction & DIRECTION_HORIZONTAL) {
24724 velocity = input.overallVelocityX;
24725 } else if (direction & DIRECTION_VERTICAL) {
24726 velocity = input.overallVelocityY;
24727 }
24728
24729 return this._super.attrTest.call(this, input) &&
24730 direction & input.offsetDirection &&
24731 input.distance > this.options.threshold &&
24732 input.maxPointers == this.options.pointers &&
24733 abs(velocity) > this.options.velocity && input.eventType & INPUT_END;
24734 },
24735
24736 emit: function(input) {
24737 var direction = directionStr(input.offsetDirection);
24738 if (direction) {
24739 this.manager.emit(this.options.event + direction, input);
24740 }
24741
24742 this.manager.emit(this.options.event, input);
24743 }
24744 });
24745
24746 /**
24747 * A tap is ecognized when the pointer is doing a small tap/click. Multiple taps are recognized if they occur
24748 * between the given interval and position. The delay option can be used to recognize multi-taps without firing
24749 * a single tap.
24750 *
24751 * The eventData from the emitted event contains the property `tapCount`, which contains the amount of
24752 * multi-taps being recognized.
24753 * @constructor
24754 * @extends Recognizer
24755 */
24756 function TapRecognizer() {
24757 Recognizer.apply(this, arguments);
24758
24759 // previous time and center,
24760 // used for tap counting
24761 this.pTime = false;
24762 this.pCenter = false;
24763
24764 this._timer = null;
24765 this._input = null;
24766 this.count = 0;
24767 }
24768
24769 inherit(TapRecognizer, Recognizer, {
24770 /**
24771 * @namespace
24772 * @memberof PinchRecognizer
24773 */
24774 defaults: {
24775 event: 'tap',
24776 pointers: 1,
24777 taps: 1,
24778 interval: 300, // max time between the multi-tap taps
24779 time: 250, // max time of the pointer to be down (like finger on the screen)
24780 threshold: 9, // a minimal movement is ok, but keep it low
24781 posThreshold: 10 // a multi-tap can be a bit off the initial position
24782 },
24783
24784 getTouchAction: function() {
24785 return [TOUCH_ACTION_MANIPULATION];
24786 },
24787
24788 process: function(input) {
24789 var options = this.options;
24790
24791 var validPointers = input.pointers.length === options.pointers;
24792 var validMovement = input.distance < options.threshold;
24793 var validTouchTime = input.deltaTime < options.time;
24794
24795 this.reset();
24796
24797 if ((input.eventType & INPUT_START) && (this.count === 0)) {
24798 return this.failTimeout();
24799 }
24800
24801 // we only allow little movement
24802 // and we've reached an end event, so a tap is possible
24803 if (validMovement && validTouchTime && validPointers) {
24804 if (input.eventType != INPUT_END) {
24805 return this.failTimeout();
24806 }
24807
24808 var validInterval = this.pTime ? (input.timeStamp - this.pTime < options.interval) : true;
24809 var validMultiTap = !this.pCenter || getDistance(this.pCenter, input.center) < options.posThreshold;
24810
24811 this.pTime = input.timeStamp;
24812 this.pCenter = input.center;
24813
24814 if (!validMultiTap || !validInterval) {
24815 this.count = 1;
24816 } else {
24817 this.count += 1;
24818 }
24819
24820 this._input = input;
24821
24822 // if tap count matches we have recognized it,
24823 // else it has began recognizing...
24824 var tapCount = this.count % options.taps;
24825 if (tapCount === 0) {
24826 // no failing requirements, immediately trigger the tap event
24827 // or wait as long as the multitap interval to trigger
24828 if (!this.hasRequireFailures()) {
24829 return STATE_RECOGNIZED;
24830 } else {
24831 this._timer = setTimeoutContext(function() {
24832 this.state = STATE_RECOGNIZED;
24833 this.tryEmit();
24834 }, options.interval, this);
24835 return STATE_BEGAN;
24836 }
24837 }
24838 }
24839 return STATE_FAILED;
24840 },
24841
24842 failTimeout: function() {
24843 this._timer = setTimeoutContext(function() {
24844 this.state = STATE_FAILED;
24845 }, this.options.interval, this);
24846 return STATE_FAILED;
24847 },
24848
24849 reset: function() {
24850 clearTimeout(this._timer);
24851 },
24852
24853 emit: function() {
24854 if (this.state == STATE_RECOGNIZED) {
24855 this._input.tapCount = this.count;
24856 this.manager.emit(this.options.event, this._input);
24857 }
24858 }
24859 });
24860
24861 /**
24862 * Simple way to create a manager with a default set of recognizers.
24863 * @param {HTMLElement} element
24864 * @param {Object} [options]
24865 * @constructor
24866 */
24867 function Hammer(element, options) {
24868 options = options || {};
24869 options.recognizers = ifUndefined(options.recognizers, Hammer.defaults.preset);
24870 return new Manager(element, options);
24871 }
24872
24873 /**
24874 * @const {string}
24875 */
24876 Hammer.VERSION = '2.0.7';
24877
24878 /**
24879 * default settings
24880 * @namespace
24881 */
24882 Hammer.defaults = {
24883 /**
24884 * set if DOM events are being triggered.
24885 * But this is slower and unused by simple implementations, so disabled by default.
24886 * @type {Boolean}
24887 * @default false
24888 */
24889 domEvents: false,
24890
24891 /**
24892 * The value for the touchAction property/fallback.
24893 * When set to `compute` it will magically set the correct value based on the added recognizers.
24894 * @type {String}
24895 * @default compute
24896 */
24897 touchAction: TOUCH_ACTION_COMPUTE,
24898
24899 /**
24900 * @type {Boolean}
24901 * @default true
24902 */
24903 enable: true,
24904
24905 /**
24906 * EXPERIMENTAL FEATURE -- can be removed/changed
24907 * Change the parent input target element.
24908 * If Null, then it is being set the to main element.
24909 * @type {Null|EventTarget}
24910 * @default null
24911 */
24912 inputTarget: null,
24913
24914 /**
24915 * force an input class
24916 * @type {Null|Function}
24917 * @default null
24918 */
24919 inputClass: null,
24920
24921 /**
24922 * Default recognizer setup when calling `Hammer()`
24923 * When creating a new Manager these will be skipped.
24924 * @type {Array}
24925 */
24926 preset: [
24927 // RecognizerClass, options, [recognizeWith, ...], [requireFailure, ...]
24928 [RotateRecognizer, {enable: false}],
24929 [PinchRecognizer, {enable: false}, ['rotate']],
24930 [SwipeRecognizer, {direction: DIRECTION_HORIZONTAL}],
24931 [PanRecognizer, {direction: DIRECTION_HORIZONTAL}, ['swipe']],
24932 [TapRecognizer],
24933 [TapRecognizer, {event: 'doubletap', taps: 2}, ['tap']],
24934 [PressRecognizer]
24935 ],
24936
24937 /**
24938 * Some CSS properties can be used to improve the working of Hammer.
24939 * Add them to this method and they will be set when creating a new Manager.
24940 * @namespace
24941 */
24942 cssProps: {
24943 /**
24944 * Disables text selection to improve the dragging gesture. Mainly for desktop browsers.
24945 * @type {String}
24946 * @default 'none'
24947 */
24948 userSelect: 'none',
24949
24950 /**
24951 * Disable the Windows Phone grippers when pressing an element.
24952 * @type {String}
24953 * @default 'none'
24954 */
24955 touchSelect: 'none',
24956
24957 /**
24958 * Disables the default callout shown when you touch and hold a touch target.
24959 * On iOS, when you touch and hold a touch target such as a link, Safari displays
24960 * a callout containing information about the link. This property allows you to disable that callout.
24961 * @type {String}
24962 * @default 'none'
24963 */
24964 touchCallout: 'none',
24965
24966 /**
24967 * Specifies whether zooming is enabled. Used by IE10>
24968 * @type {String}
24969 * @default 'none'
24970 */
24971 contentZooming: 'none',
24972
24973 /**
24974 * Specifies that an entire element should be draggable instead of its contents. Mainly for desktop browsers.
24975 * @type {String}
24976 * @default 'none'
24977 */
24978 userDrag: 'none',
24979
24980 /**
24981 * Overrides the highlight color shown when the user taps a link or a JavaScript
24982 * clickable element in iOS. This property obeys the alpha value, if specified.
24983 * @type {String}
24984 * @default 'rgba(0,0,0,0)'
24985 */
24986 tapHighlightColor: 'rgba(0,0,0,0)'
24987 }
24988 };
24989
24990 var STOP = 1;
24991 var FORCED_STOP = 2;
24992
24993 /**
24994 * Manager
24995 * @param {HTMLElement} element
24996 * @param {Object} [options]
24997 * @constructor
24998 */
24999 function Manager(element, options) {
25000 this.options = assign({}, Hammer.defaults, options || {});
25001
25002 this.options.inputTarget = this.options.inputTarget || element;
25003
25004 this.handlers = {};
25005 this.session = {};
25006 this.recognizers = [];
25007 this.oldCssProps = {};
25008
25009 this.element = element;
25010 this.input = createInputInstance(this);
25011 this.touchAction = new TouchAction(this, this.options.touchAction);
25012
25013 toggleCssProps(this, true);
25014
25015 each(this.options.recognizers, function(item) {
25016 var recognizer = this.add(new (item[0])(item[1]));
25017 item[2] && recognizer.recognizeWith(item[2]);
25018 item[3] && recognizer.requireFailure(item[3]);
25019 }, this);
25020 }
25021
25022 Manager.prototype = {
25023 /**
25024 * set options
25025 * @param {Object} options
25026 * @returns {Manager}
25027 */
25028 set: function(options) {
25029 assign(this.options, options);
25030
25031 // Options that need a little more setup
25032 if (options.touchAction) {
25033 this.touchAction.update();
25034 }
25035 if (options.inputTarget) {
25036 // Clean up existing event listeners and reinitialize
25037 this.input.destroy();
25038 this.input.target = options.inputTarget;
25039 this.input.init();
25040 }
25041 return this;
25042 },
25043
25044 /**
25045 * stop recognizing for this session.
25046 * This session will be discarded, when a new [input]start event is fired.
25047 * When forced, the recognizer cycle is stopped immediately.
25048 * @param {Boolean} [force]
25049 */
25050 stop: function(force) {
25051 this.session.stopped = force ? FORCED_STOP : STOP;
25052 },
25053
25054 /**
25055 * run the recognizers!
25056 * called by the inputHandler function on every movement of the pointers (touches)
25057 * it walks through all the recognizers and tries to detect the gesture that is being made
25058 * @param {Object} inputData
25059 */
25060 recognize: function(inputData) {
25061 var session = this.session;
25062 if (session.stopped) {
25063 return;
25064 }
25065
25066 // run the touch-action polyfill
25067 this.touchAction.preventDefaults(inputData);
25068
25069 var recognizer;
25070 var recognizers = this.recognizers;
25071
25072 // this holds the recognizer that is being recognized.
25073 // so the recognizer's state needs to be BEGAN, CHANGED, ENDED or RECOGNIZED
25074 // if no recognizer is detecting a thing, it is set to `null`
25075 var curRecognizer = session.curRecognizer;
25076
25077 // reset when the last recognizer is recognized
25078 // or when we're in a new session
25079 if (!curRecognizer || (curRecognizer && curRecognizer.state & STATE_RECOGNIZED)) {
25080 curRecognizer = session.curRecognizer = null;
25081 }
25082
25083 var i = 0;
25084 while (i < recognizers.length) {
25085 recognizer = recognizers[i];
25086
25087 // find out if we are allowed try to recognize the input for this one.
25088 // 1. allow if the session is NOT forced stopped (see the .stop() method)
25089 // 2. allow if we still haven't recognized a gesture in this session, or the this recognizer is the one
25090 // that is being recognized.
25091 // 3. allow if the recognizer is allowed to run simultaneous with the current recognized recognizer.
25092 // this can be setup with the `recognizeWith()` method on the recognizer.
25093 if (session.stopped !== FORCED_STOP && ( // 1
25094 !curRecognizer || recognizer == curRecognizer || // 2
25095 recognizer.canRecognizeWith(curRecognizer))) { // 3
25096 recognizer.recognize(inputData);
25097 } else {
25098 recognizer.reset();
25099 }
25100
25101 // if the recognizer has been recognizing the input as a valid gesture, we want to store this one as the
25102 // current active recognizer. but only if we don't already have an active recognizer
25103 if (!curRecognizer && recognizer.state & (STATE_BEGAN | STATE_CHANGED | STATE_ENDED)) {
25104 curRecognizer = session.curRecognizer = recognizer;
25105 }
25106 i++;
25107 }
25108 },
25109
25110 /**
25111 * get a recognizer by its event name.
25112 * @param {Recognizer|String} recognizer
25113 * @returns {Recognizer|Null}
25114 */
25115 get: function(recognizer) {
25116 if (recognizer instanceof Recognizer) {
25117 return recognizer;
25118 }
25119
25120 var recognizers = this.recognizers;
25121 for (var i = 0; i < recognizers.length; i++) {
25122 if (recognizers[i].options.event == recognizer) {
25123 return recognizers[i];
25124 }
25125 }
25126 return null;
25127 },
25128
25129 /**
25130 * add a recognizer to the manager
25131 * existing recognizers with the same event name will be removed
25132 * @param {Recognizer} recognizer
25133 * @returns {Recognizer|Manager}
25134 */
25135 add: function(recognizer) {
25136 if (invokeArrayArg(recognizer, 'add', this)) {
25137 return this;
25138 }
25139
25140 // remove existing
25141 var existing = this.get(recognizer.options.event);
25142 if (existing) {
25143 this.remove(existing);
25144 }
25145
25146 this.recognizers.push(recognizer);
25147 recognizer.manager = this;
25148
25149 this.touchAction.update();
25150 return recognizer;
25151 },
25152
25153 /**
25154 * remove a recognizer by name or instance
25155 * @param {Recognizer|String} recognizer
25156 * @returns {Manager}
25157 */
25158 remove: function(recognizer) {
25159 if (invokeArrayArg(recognizer, 'remove', this)) {
25160 return this;
25161 }
25162
25163 recognizer = this.get(recognizer);
25164
25165 // let's make sure this recognizer exists
25166 if (recognizer) {
25167 var recognizers = this.recognizers;
25168 var index = inArray(recognizers, recognizer);
25169
25170 if (index !== -1) {
25171 recognizers.splice(index, 1);
25172 this.touchAction.update();
25173 }
25174 }
25175
25176 return this;
25177 },
25178
25179 /**
25180 * bind event
25181 * @param {String} events
25182 * @param {Function} handler
25183 * @returns {EventEmitter} this
25184 */
25185 on: function(events, handler) {
25186 if (events === undefined$1) {
25187 return;
25188 }
25189 if (handler === undefined$1) {
25190 return;
25191 }
25192
25193 var handlers = this.handlers;
25194 each(splitStr(events), function(event) {
25195 handlers[event] = handlers[event] || [];
25196 handlers[event].push(handler);
25197 });
25198 return this;
25199 },
25200
25201 /**
25202 * unbind event, leave emit blank to remove all handlers
25203 * @param {String} events
25204 * @param {Function} [handler]
25205 * @returns {EventEmitter} this
25206 */
25207 off: function(events, handler) {
25208 if (events === undefined$1) {
25209 return;
25210 }
25211
25212 var handlers = this.handlers;
25213 each(splitStr(events), function(event) {
25214 if (!handler) {
25215 delete handlers[event];
25216 } else {
25217 handlers[event] && handlers[event].splice(inArray(handlers[event], handler), 1);
25218 }
25219 });
25220 return this;
25221 },
25222
25223 /**
25224 * emit event to the listeners
25225 * @param {String} event
25226 * @param {Object} data
25227 */
25228 emit: function(event, data) {
25229 // we also want to trigger dom events
25230 if (this.options.domEvents) {
25231 triggerDomEvent(event, data);
25232 }
25233
25234 // no handlers, so skip it all
25235 var handlers = this.handlers[event] && this.handlers[event].slice();
25236 if (!handlers || !handlers.length) {
25237 return;
25238 }
25239
25240 data.type = event;
25241 data.preventDefault = function() {
25242 data.srcEvent.preventDefault();
25243 };
25244
25245 var i = 0;
25246 while (i < handlers.length) {
25247 handlers[i](data);
25248 i++;
25249 }
25250 },
25251
25252 /**
25253 * destroy the manager and unbinds all events
25254 * it doesn't unbind dom events, that is the user own responsibility
25255 */
25256 destroy: function() {
25257 this.element && toggleCssProps(this, false);
25258
25259 this.handlers = {};
25260 this.session = {};
25261 this.input.destroy();
25262 this.element = null;
25263 }
25264 };
25265
25266 /**
25267 * add/remove the css properties as defined in manager.options.cssProps
25268 * @param {Manager} manager
25269 * @param {Boolean} add
25270 */
25271 function toggleCssProps(manager, add) {
25272 var element = manager.element;
25273 if (!element.style) {
25274 return;
25275 }
25276 var prop;
25277 each(manager.options.cssProps, function(value, name) {
25278 prop = prefixed(element.style, name);
25279 if (add) {
25280 manager.oldCssProps[prop] = element.style[prop];
25281 element.style[prop] = value;
25282 } else {
25283 element.style[prop] = manager.oldCssProps[prop] || '';
25284 }
25285 });
25286 if (!add) {
25287 manager.oldCssProps = {};
25288 }
25289 }
25290
25291 /**
25292 * trigger dom event
25293 * @param {String} event
25294 * @param {Object} data
25295 */
25296 function triggerDomEvent(event, data) {
25297 var gestureEvent = document.createEvent('Event');
25298 gestureEvent.initEvent(event, true, true);
25299 gestureEvent.gesture = data;
25300 data.target.dispatchEvent(gestureEvent);
25301 }
25302
25303 assign(Hammer, {
25304 INPUT_START: INPUT_START,
25305 INPUT_MOVE: INPUT_MOVE,
25306 INPUT_END: INPUT_END,
25307 INPUT_CANCEL: INPUT_CANCEL,
25308
25309 STATE_POSSIBLE: STATE_POSSIBLE,
25310 STATE_BEGAN: STATE_BEGAN,
25311 STATE_CHANGED: STATE_CHANGED,
25312 STATE_ENDED: STATE_ENDED,
25313 STATE_RECOGNIZED: STATE_RECOGNIZED,
25314 STATE_CANCELLED: STATE_CANCELLED,
25315 STATE_FAILED: STATE_FAILED,
25316
25317 DIRECTION_NONE: DIRECTION_NONE,
25318 DIRECTION_LEFT: DIRECTION_LEFT,
25319 DIRECTION_RIGHT: DIRECTION_RIGHT,
25320 DIRECTION_UP: DIRECTION_UP,
25321 DIRECTION_DOWN: DIRECTION_DOWN,
25322 DIRECTION_HORIZONTAL: DIRECTION_HORIZONTAL,
25323 DIRECTION_VERTICAL: DIRECTION_VERTICAL,
25324 DIRECTION_ALL: DIRECTION_ALL,
25325
25326 Manager: Manager,
25327 Input: Input,
25328 TouchAction: TouchAction,
25329
25330 TouchInput: TouchInput,
25331 MouseInput: MouseInput,
25332 PointerEventInput: PointerEventInput,
25333 TouchMouseInput: TouchMouseInput,
25334 SingleTouchInput: SingleTouchInput,
25335
25336 Recognizer: Recognizer,
25337 AttrRecognizer: AttrRecognizer,
25338 Tap: TapRecognizer,
25339 Pan: PanRecognizer,
25340 Swipe: SwipeRecognizer,
25341 Pinch: PinchRecognizer,
25342 Rotate: RotateRecognizer,
25343 Press: PressRecognizer,
25344
25345 on: addEventListeners,
25346 off: removeEventListeners,
25347 each: each,
25348 merge: merge,
25349 extend: extend,
25350 assign: assign,
25351 inherit: inherit,
25352 bindFn: bindFn,
25353 prefixed: prefixed
25354 });
25355
25356 // this prevents errors when Hammer is loaded in the presence of an AMD
25357 // style loader but by script tag, not by the loader.
25358 var freeGlobal = (typeof window !== 'undefined' ? window : (typeof self !== 'undefined' ? self : {})); // jshint ignore:line
25359 freeGlobal.Hammer = Hammer;
25360
25361 if (typeof undefined$1 === 'function' && undefined$1.amd) {
25362 undefined$1(function() {
25363 return Hammer;
25364 });
25365 } else if (module.exports) {
25366 module.exports = Hammer;
25367 } else {
25368 window[exportName] = Hammer;
25369 }
25370
25371 })(window, document, 'Hammer');
25372 }(hammer));
25373
25374 var Hammer = hammer.exports;
25375
25376 var MIN_ZOOM = 0.2,
25377 MAX_ZOOM = 4;
25378
25379 var mouseEvents = [
25380 'mousedown',
25381 'mouseup',
25382 'mouseover',
25383 'mouseout',
25384 'click',
25385 'dblclick'
25386 ];
25387
25388 function get(service, injector) {
25389 return injector.get(service, false);
25390 }
25391
25392 function stopEvent(event) {
25393
25394 event.preventDefault();
25395
25396 if (typeof event.stopPropagation === 'function') {
25397 event.stopPropagation();
25398 } else if (event.srcEvent && typeof event.srcEvent.stopPropagation === 'function') {
25399
25400 // iPhone & iPad
25401 event.srcEvent.stopPropagation();
25402 }
25403
25404 if (typeof event.stopImmediatePropagation === 'function') {
25405 event.stopImmediatePropagation();
25406 }
25407 }
25408
25409
25410 function createTouchRecognizer(node) {
25411
25412 function stopMouse(event) {
25413
25414 forEach(mouseEvents, function(e) {
25415 componentEvent.bind(node, e, stopEvent, true);
25416 });
25417 }
25418
25419 function allowMouse(event) {
25420 setTimeout(function() {
25421 forEach(mouseEvents, function(e) {
25422 componentEvent.unbind(node, e, stopEvent, true);
25423 });
25424 }, 500);
25425 }
25426
25427 componentEvent.bind(node, 'touchstart', stopMouse, true);
25428 componentEvent.bind(node, 'touchend', allowMouse, true);
25429 componentEvent.bind(node, 'touchcancel', allowMouse, true);
25430
25431 // A touch event recognizer that handles
25432 // touch events only (we know, we can already handle
25433 // mouse events out of the box)
25434
25435 var recognizer = new Hammer.Manager(node, {
25436 inputClass: Hammer.TouchInput,
25437 recognizers: [],
25438 domEvents: true
25439 });
25440
25441
25442 var tap = new Hammer.Tap();
25443 var pan = new Hammer.Pan({ threshold: 10 });
25444 var press = new Hammer.Press();
25445 var pinch = new Hammer.Pinch();
25446
25447 var doubleTap = new Hammer.Tap({ event: 'doubletap', taps: 2 });
25448
25449 pinch.requireFailure(pan);
25450 pinch.requireFailure(press);
25451
25452 recognizer.add([ pan, press, pinch, doubleTap, tap ]);
25453
25454 recognizer.reset = function(force) {
25455 var recognizers = this.recognizers,
25456 session = this.session;
25457
25458 if (session.stopped) {
25459 return;
25460 }
25461
25462 recognizer.stop(force);
25463
25464 setTimeout(function() {
25465 var i, r;
25466 for (i = 0; (r = recognizers[i]); i++) {
25467 r.reset();
25468 r.state = 8; // FAILED STATE
25469 }
25470
25471 session.curRecognizer = null;
25472 }, 0);
25473 };
25474
25475 recognizer.on('hammer.input', function(event) {
25476 if (event.srcEvent.defaultPrevented) {
25477 recognizer.reset(true);
25478 }
25479 });
25480
25481 return recognizer;
25482 }
25483
25484 /**
25485 * A plugin that provides touch events for elements.
25486 *
25487 * @param {EventBus} eventBus
25488 * @param {InteractionEvents} interactionEvents
25489 */
25490 function TouchInteractionEvents(
25491 injector, canvas, eventBus,
25492 elementRegistry, interactionEvents) {
25493
25494 // optional integrations
25495 var dragging = get('dragging', injector),
25496 move = get('move', injector),
25497 contextPad = get('contextPad', injector),
25498 palette = get('palette', injector);
25499
25500 // the touch recognizer
25501 var recognizer;
25502
25503 function handler(type) {
25504
25505 return function(event) {
25506
25507 interactionEvents.fire(type, event);
25508 };
25509 }
25510
25511 function getGfx(target) {
25512 var node = closest(target, 'svg, .djs-element', true);
25513 return node;
25514 }
25515
25516 function initEvents(svg) {
25517
25518 // touch recognizer
25519 recognizer = createTouchRecognizer(svg);
25520
25521 recognizer.on('doubletap', handler('element.dblclick'));
25522
25523 recognizer.on('tap', handler('element.click'));
25524
25525 function startGrabCanvas(event) {
25526
25527 var lx = 0, ly = 0;
25528
25529 function update(e) {
25530
25531 var dx = e.deltaX - lx,
25532 dy = e.deltaY - ly;
25533
25534 canvas.scroll({ dx: dx, dy: dy });
25535
25536 lx = e.deltaX;
25537 ly = e.deltaY;
25538 }
25539
25540 function end(e) {
25541 recognizer.off('panmove', update);
25542 recognizer.off('panend', end);
25543 recognizer.off('pancancel', end);
25544 }
25545
25546 recognizer.on('panmove', update);
25547 recognizer.on('panend', end);
25548 recognizer.on('pancancel', end);
25549 }
25550
25551 function startGrab(event) {
25552
25553 var gfx = getGfx(event.target),
25554 element = gfx && elementRegistry.get(gfx);
25555
25556 // recognizer
25557 if (move && canvas.getRootElement() !== element) {
25558 return move.start(event, element, true);
25559 } else {
25560 startGrabCanvas();
25561 }
25562 }
25563
25564 function startZoom(e) {
25565
25566 var zoom = canvas.zoom(),
25567 mid = e.center;
25568
25569 function update(e) {
25570
25571 var ratio = 1 - (1 - e.scale) / 1.50,
25572 newZoom = Math.max(MIN_ZOOM, Math.min(MAX_ZOOM, ratio * zoom));
25573
25574 canvas.zoom(newZoom, mid);
25575
25576 stopEvent(e);
25577 }
25578
25579 function end(e) {
25580 recognizer.off('pinchmove', update);
25581 recognizer.off('pinchend', end);
25582 recognizer.off('pinchcancel', end);
25583
25584 recognizer.reset(true);
25585 }
25586
25587 recognizer.on('pinchmove', update);
25588 recognizer.on('pinchend', end);
25589 recognizer.on('pinchcancel', end);
25590 }
25591
25592 recognizer.on('panstart', startGrab);
25593 recognizer.on('press', startGrab);
25594
25595 recognizer.on('pinchstart', startZoom);
25596 }
25597
25598 if (dragging) {
25599
25600 // simulate hover during dragging
25601 eventBus.on('drag.move', function(event) {
25602
25603 var originalEvent = event.originalEvent;
25604
25605 if (!originalEvent || originalEvent instanceof MouseEvent) {
25606 return;
25607 }
25608
25609 var position = toPoint(originalEvent);
25610
25611 // this gets really expensive ...
25612 var node = document.elementFromPoint(position.x, position.y),
25613 gfx = getGfx(node),
25614 element = gfx && elementRegistry.get(gfx);
25615
25616 if (element !== event.hover) {
25617 if (event.hover) {
25618 dragging.out(event);
25619 }
25620
25621 if (element) {
25622 dragging.hover({ element: element, gfx: gfx });
25623
25624 event.hover = element;
25625 event.hoverGfx = gfx;
25626 }
25627 }
25628 });
25629 }
25630
25631 if (contextPad) {
25632
25633 eventBus.on('contextPad.create', function(event) {
25634 var node = event.pad.html;
25635
25636 // touch recognizer
25637 var padRecognizer = createTouchRecognizer(node);
25638
25639 padRecognizer.on('panstart', function(event) {
25640 contextPad.trigger('dragstart', event, true);
25641 });
25642
25643 padRecognizer.on('press', function(event) {
25644 contextPad.trigger('dragstart', event, true);
25645 });
25646
25647 padRecognizer.on('tap', function(event) {
25648 contextPad.trigger('click', event);
25649 });
25650 });
25651 }
25652
25653 if (palette) {
25654 eventBus.on('palette.create', function(event) {
25655 var node = event.container;
25656
25657 // touch recognizer
25658 var padRecognizer = createTouchRecognizer(node);
25659
25660 padRecognizer.on('panstart', function(event) {
25661 palette.trigger('dragstart', event, true);
25662 });
25663
25664 padRecognizer.on('press', function(event) {
25665 palette.trigger('dragstart', event, true);
25666 });
25667
25668 padRecognizer.on('tap', function(event) {
25669 palette.trigger('click', event);
25670 });
25671 });
25672 }
25673
25674 eventBus.on('canvas.init', function(event) {
25675 initEvents(event.svg);
25676 });
25677 }
25678
25679
25680 TouchInteractionEvents.$inject = [
25681 'injector',
25682 'canvas',
25683 'eventBus',
25684 'elementRegistry',
25685 'interactionEvents',
25686 'touchFix'
25687 ];
25688
25689 function TouchFix(canvas, eventBus) {
25690
25691 var self = this;
25692
25693 eventBus.on('canvas.init', function(e) {
25694 self.addBBoxMarker(e.svg);
25695 });
25696 }
25697
25698 TouchFix.$inject = [ 'canvas', 'eventBus' ];
25699
25700
25701 /**
25702 * Safari mobile (iOS 7) does not fire touchstart event in <SVG> element
25703 * if there is no shape between 0,0 and viewport elements origin.
25704 *
25705 * So touchstart event is only fired when the <g class="viewport"> element was hit.
25706 * Putting an element over and below the 'viewport' fixes that behavior.
25707 */
25708 TouchFix.prototype.addBBoxMarker = function(svg) {
25709
25710 var markerStyle = {
25711 fill: 'none',
25712 class: 'outer-bound-marker'
25713 };
25714
25715 var rect1 = create$1('rect');
25716 attr(rect1, {
25717 x: -10000,
25718 y: 10000,
25719 width: 10,
25720 height: 10
25721 });
25722 attr(rect1, markerStyle);
25723
25724 append(svg, rect1);
25725
25726 var rect2 = create$1('rect');
25727 attr(rect2, {
25728 x: 10000,
25729 y: 10000,
25730 width: 10,
25731 height: 10
25732 });
25733 attr(rect2, markerStyle);
25734
25735 append(svg, rect2);
25736 };
25737
25738 var TouchModule$1 = {
25739 __depends__: [ InteractionEventsModule$1 ],
25740 __init__: [ 'touchInteractionEvents' ],
25741 touchInteractionEvents: [ 'type', TouchInteractionEvents ],
25742 touchFix: [ 'type', TouchFix ]
25743 };
25744
25745 var TouchModule = {
25746 __depends__: [
25747 TouchModule$1
25748 ]
25749 };
25750
25751 function last(arr) {
25752 return arr && arr[arr.length - 1];
25753 }
25754
25755 function sortTopOrMiddle(element) {
25756 return element.y;
25757 }
25758
25759 function sortLeftOrCenter(element) {
25760 return element.x;
25761 }
25762
25763 /**
25764 * Sorting functions for different types of alignment
25765 *
25766 * @type {Object}
25767 *
25768 * @return {Function}
25769 */
25770 var ALIGNMENT_SORTING = {
25771 left: sortLeftOrCenter,
25772 center: sortLeftOrCenter,
25773 right: function(element) {
25774 return element.x + element.width;
25775 },
25776 top: sortTopOrMiddle,
25777 middle: sortTopOrMiddle,
25778 bottom: function(element) {
25779 return element.y + element.height;
25780 }
25781 };
25782
25783
25784 function AlignElements$1(modeling) {
25785 this._modeling = modeling;
25786 }
25787
25788 AlignElements$1.$inject = [ 'modeling' ];
25789
25790
25791 /**
25792 * Get the relevant "axis" and "dimension" related to the current type of alignment
25793 *
25794 * @param {string} type left|right|center|top|bottom|middle
25795 *
25796 * @return {Object} { axis, dimension }
25797 */
25798 AlignElements$1.prototype._getOrientationDetails = function(type) {
25799 var vertical = [ 'top', 'bottom', 'middle' ],
25800 axis = 'x',
25801 dimension = 'width';
25802
25803 if (vertical.indexOf(type) !== -1) {
25804 axis = 'y';
25805 dimension = 'height';
25806 }
25807
25808 return {
25809 axis: axis,
25810 dimension: dimension
25811 };
25812 };
25813
25814 AlignElements$1.prototype._isType = function(type, types) {
25815 return types.indexOf(type) !== -1;
25816 };
25817
25818 /**
25819 * Get a point on the relevant axis where elements should align to
25820 *
25821 * @param {string} type left|right|center|top|bottom|middle
25822 * @param {Array} sortedElements
25823 *
25824 * @return {Object}
25825 */
25826 AlignElements$1.prototype._alignmentPosition = function(type, sortedElements) {
25827 var orientation = this._getOrientationDetails(type),
25828 axis = orientation.axis,
25829 dimension = orientation.dimension,
25830 alignment = {},
25831 centers = {},
25832 hasSharedCenters = false,
25833 centeredElements,
25834 firstElement,
25835 lastElement;
25836
25837 function getMiddleOrTop(first, last) {
25838 return Math.round((first[axis] + last[axis] + last[dimension]) / 2);
25839 }
25840
25841 if (this._isType(type, [ 'left', 'top' ])) {
25842 alignment[type] = sortedElements[0][axis];
25843
25844 } else if (this._isType(type, [ 'right', 'bottom' ])) {
25845 lastElement = last(sortedElements);
25846
25847 alignment[type] = lastElement[axis] + lastElement[dimension];
25848
25849 } else if (this._isType(type, [ 'center', 'middle' ])) {
25850
25851 // check if there is a center shared by more than one shape
25852 // if not, just take the middle of the range
25853 forEach(sortedElements, function(element) {
25854 var center = element[axis] + Math.round(element[dimension] / 2);
25855
25856 if (centers[center]) {
25857 centers[center].elements.push(element);
25858 } else {
25859 centers[center] = {
25860 elements: [ element ],
25861 center: center
25862 };
25863 }
25864 });
25865
25866 centeredElements = sortBy(centers, function(center) {
25867 if (center.elements.length > 1) {
25868 hasSharedCenters = true;
25869 }
25870
25871 return center.elements.length;
25872 });
25873
25874 if (hasSharedCenters) {
25875 alignment[type] = last(centeredElements).center;
25876
25877 return alignment;
25878 }
25879
25880 firstElement = sortedElements[0];
25881
25882 sortedElements = sortBy(sortedElements, function(element) {
25883 return element[axis] + element[dimension];
25884 });
25885
25886 lastElement = last(sortedElements);
25887
25888 alignment[type] = getMiddleOrTop(firstElement, lastElement);
25889 }
25890
25891 return alignment;
25892 };
25893
25894 /**
25895 * Executes the alignment of a selection of elements
25896 *
25897 * @param {Array} elements
25898 * @param {string} type left|right|center|top|bottom|middle
25899 */
25900 AlignElements$1.prototype.trigger = function(elements, type) {
25901 var modeling = this._modeling;
25902
25903 var filteredElements = filter(elements, function(element) {
25904 return !(element.waypoints || element.host || element.labelTarget);
25905 });
25906
25907 if (filteredElements.length < 2) {
25908 return;
25909 }
25910
25911 var sortFn = ALIGNMENT_SORTING[type];
25912
25913 var sortedElements = sortBy(filteredElements, sortFn);
25914
25915 var alignment = this._alignmentPosition(type, sortedElements);
25916
25917 modeling.alignElements(sortedElements, alignment);
25918 };
25919
25920 var AlignElementsModule = {
25921 __init__: [ 'alignElements' ],
25922 alignElements: [ 'type', AlignElements$1 ]
25923 };
25924
25925 // padding to detect element placement
25926 var PLACEMENT_DETECTION_PAD = 10;
25927
25928 var DEFAULT_DISTANCE = 50;
25929
25930 var DEFAULT_MAX_DISTANCE = 250;
25931
25932
25933 /**
25934 * Get free position starting from given position.
25935 *
25936 * @param {djs.model.Shape} source
25937 * @param {djs.model.Shape} element
25938 * @param {Point} position
25939 * @param {Function} getNextPosition
25940 *
25941 * @return {Point}
25942 */
25943 function findFreePosition(source, element, position, getNextPosition) {
25944 var connectedAtPosition;
25945
25946 while ((connectedAtPosition = getConnectedAtPosition(source, position, element))) {
25947 position = getNextPosition(element, position, connectedAtPosition);
25948 }
25949
25950 return position;
25951 }
25952
25953 /**
25954 * Returns function that returns next position.
25955 *
25956 * @param {Object} nextPositionDirection
25957 * @param {Object} [nextPositionDirection.x]
25958 * @param {Object} [nextPositionDirection.y]
25959 *
25960 * @returns {Function}
25961 */
25962 function generateGetNextPosition(nextPositionDirection) {
25963 return function(element, previousPosition, connectedAtPosition) {
25964 var nextPosition = {
25965 x: previousPosition.x,
25966 y: previousPosition.y
25967 };
25968
25969 [ 'x', 'y' ].forEach(function(axis) {
25970
25971 var nextPositionDirectionForAxis = nextPositionDirection[ axis ];
25972
25973 if (!nextPositionDirectionForAxis) {
25974 return;
25975 }
25976
25977 var dimension = axis === 'x' ? 'width' : 'height';
25978
25979 var margin = nextPositionDirectionForAxis.margin,
25980 minDistance = nextPositionDirectionForAxis.minDistance;
25981
25982 if (margin < 0) {
25983 nextPosition[ axis ] = Math.min(
25984 connectedAtPosition[ axis ] + margin - element[ dimension ] / 2,
25985 previousPosition[ axis ] - minDistance + margin
25986 );
25987 } else {
25988 nextPosition[ axis ] = Math.max(
25989 connectedAtPosition[ axis ] + connectedAtPosition[ dimension ] + margin + element[ dimension ] / 2,
25990 previousPosition[ axis ] + minDistance + margin
25991 );
25992 }
25993 });
25994
25995 return nextPosition;
25996 };
25997 }
25998
25999 /**
26000 * Return target at given position, if defined.
26001 *
26002 * This takes connected elements from host and attachers
26003 * into account, too.
26004 */
26005 function getConnectedAtPosition(source, position, element) {
26006
26007 var bounds = {
26008 x: position.x - (element.width / 2),
26009 y: position.y - (element.height / 2),
26010 width: element.width,
26011 height: element.height
26012 };
26013
26014 var closure = getAutoPlaceClosure(source);
26015
26016 return find(closure, function(target) {
26017
26018 if (target === element) {
26019 return false;
26020 }
26021
26022 var orientation = getOrientation(target, bounds, PLACEMENT_DETECTION_PAD);
26023
26024 return orientation === 'intersect';
26025 });
26026 }
26027
26028 /**
26029 * Compute optimal distance between source and target based on existing connections to and from source.
26030 * Assumes left-to-right and top-to-down modeling.
26031 *
26032 * @param {djs.model.Shape} source
26033 * @param {Object} [hints]
26034 * @param {number} [hints.defaultDistance]
26035 * @param {string} [hints.direction]
26036 * @param {Function} [hints.filter]
26037 * @param {Function} [hints.getWeight]
26038 * @param {number} [hints.maxDistance]
26039 * @param {string} [hints.reference]
26040 *
26041 * @return {number}
26042 */
26043 function getConnectedDistance(source, hints) {
26044 if (!hints) {
26045 hints = {};
26046 }
26047
26048 // targets > sources by default
26049 function getDefaultWeight(connection) {
26050 return connection.source === source ? 1 : -1;
26051 }
26052
26053 var defaultDistance = hints.defaultDistance || DEFAULT_DISTANCE,
26054 direction = hints.direction || 'e',
26055 filter = hints.filter,
26056 getWeight = hints.getWeight || getDefaultWeight,
26057 maxDistance = hints.maxDistance || DEFAULT_MAX_DISTANCE,
26058 reference = hints.reference || 'start';
26059
26060 if (!filter) {
26061 filter = noneFilter;
26062 }
26063
26064 function getDistance(a, b) {
26065 if (direction === 'n') {
26066 if (reference === 'start') {
26067 return asTRBL(a).top - asTRBL(b).bottom;
26068 } else if (reference === 'center') {
26069 return asTRBL(a).top - getMid(b).y;
26070 } else {
26071 return asTRBL(a).top - asTRBL(b).top;
26072 }
26073 } else if (direction === 'w') {
26074 if (reference === 'start') {
26075 return asTRBL(a).left - asTRBL(b).right;
26076 } else if (reference === 'center') {
26077 return asTRBL(a).left - getMid(b).x;
26078 } else {
26079 return asTRBL(a).left - asTRBL(b).left;
26080 }
26081 } else if (direction === 's') {
26082 if (reference === 'start') {
26083 return asTRBL(b).top - asTRBL(a).bottom;
26084 } else if (reference === 'center') {
26085 return getMid(b).y - asTRBL(a).bottom;
26086 } else {
26087 return asTRBL(b).bottom - asTRBL(a).bottom;
26088 }
26089 } else {
26090 if (reference === 'start') {
26091 return asTRBL(b).left - asTRBL(a).right;
26092 } else if (reference === 'center') {
26093 return getMid(b).x - asTRBL(a).right;
26094 } else {
26095 return asTRBL(b).right - asTRBL(a).right;
26096 }
26097 }
26098 }
26099
26100 var sourcesDistances = source.incoming
26101 .filter(filter)
26102 .map(function(connection) {
26103 var weight = getWeight(connection);
26104
26105 var distance = weight < 0
26106 ? getDistance(connection.source, source)
26107 : getDistance(source, connection.source);
26108
26109 return {
26110 id: connection.source.id,
26111 distance: distance,
26112 weight: weight
26113 };
26114 });
26115
26116 var targetsDistances = source.outgoing
26117 .filter(filter)
26118 .map(function(connection) {
26119 var weight = getWeight(connection);
26120
26121 var distance = weight > 0
26122 ? getDistance(source, connection.target)
26123 : getDistance(connection.target, source);
26124
26125 return {
26126 id: connection.target.id,
26127 distance: distance,
26128 weight: weight
26129 };
26130 });
26131
26132 var distances = sourcesDistances.concat(targetsDistances).reduce(function(accumulator, currentValue) {
26133 accumulator[ currentValue.id + '__weight_' + currentValue.weight ] = currentValue;
26134
26135 return accumulator;
26136 }, {});
26137
26138 var distancesGrouped = reduce(distances, function(accumulator, currentValue) {
26139 var distance = currentValue.distance,
26140 weight = currentValue.weight;
26141
26142 if (distance < 0 || distance > maxDistance) {
26143 return accumulator;
26144 }
26145
26146 if (!accumulator[ String(distance) ]) {
26147 accumulator[ String(distance) ] = 0;
26148 }
26149
26150 accumulator[ String(distance) ] += 1 * weight;
26151
26152 if (!accumulator.distance || accumulator[ accumulator.distance ] < accumulator[ String(distance) ]) {
26153 accumulator.distance = distance;
26154 }
26155
26156 return accumulator;
26157 }, {});
26158
26159 return distancesGrouped.distance || defaultDistance;
26160 }
26161
26162 /**
26163 * Returns all connected elements around the given source.
26164 *
26165 * This includes:
26166 *
26167 * - connected elements
26168 * - host connected elements
26169 * - attachers connected elements
26170 *
26171 * @param {djs.model.Shape} source
26172 *
26173 * @return {Array<djs.model.Shape>}
26174 */
26175 function getAutoPlaceClosure(source) {
26176
26177 var allConnected = getConnected(source);
26178
26179 if (source.host) {
26180 allConnected = allConnected.concat(getConnected(source.host));
26181 }
26182
26183 if (source.attachers) {
26184 allConnected = allConnected.concat(source.attachers.reduce(function(shapes, attacher) {
26185 return shapes.concat(getConnected(attacher));
26186 }, []));
26187 }
26188
26189 return allConnected;
26190 }
26191
26192 function getConnected(element) {
26193 return getTargets(element).concat(getSources(element));
26194 }
26195
26196 function getSources(shape) {
26197 return shape.incoming.map(function(connection) {
26198 return connection.source;
26199 });
26200 }
26201
26202 function getTargets(shape) {
26203 return shape.outgoing.map(function(connection) {
26204 return connection.target;
26205 });
26206 }
26207
26208 function noneFilter() {
26209 return true;
26210 }
26211
26212 var LOW_PRIORITY$i = 100;
26213
26214
26215 /**
26216 * A service that places elements connected to existing ones
26217 * to an appropriate position in an _automated_ fashion.
26218 *
26219 * @param {EventBus} eventBus
26220 * @param {Modeling} modeling
26221 */
26222 function AutoPlace$1(eventBus, modeling, canvas) {
26223
26224 eventBus.on('autoPlace', LOW_PRIORITY$i, function(context) {
26225 var shape = context.shape,
26226 source = context.source;
26227
26228 return getNewShapePosition$1(source, shape);
26229 });
26230
26231 eventBus.on('autoPlace.end', function(event) {
26232 canvas.scrollToElement(event.shape);
26233 });
26234
26235 /**
26236 * Append shape to source at appropriate position.
26237 *
26238 * @param {djs.model.Shape} source
26239 * @param {djs.model.Shape} shape
26240 *
26241 * @return {djs.model.Shape} appended shape
26242 */
26243 this.append = function(source, shape, hints) {
26244
26245 eventBus.fire('autoPlace.start', {
26246 source: source,
26247 shape: shape
26248 });
26249
26250 // allow others to provide the position
26251 var position = eventBus.fire('autoPlace', {
26252 source: source,
26253 shape: shape
26254 });
26255
26256 var newShape = modeling.appendShape(source, shape, position, source.parent, hints);
26257
26258 eventBus.fire('autoPlace.end', {
26259 source: source,
26260 shape: newShape
26261 });
26262
26263 return newShape;
26264 };
26265
26266 }
26267
26268 AutoPlace$1.$inject = [
26269 'eventBus',
26270 'modeling',
26271 'canvas'
26272 ];
26273
26274 // helpers //////////
26275
26276 /**
26277 * Find the new position for the target element to
26278 * connect to source.
26279 *
26280 * @param {djs.model.Shape} source
26281 * @param {djs.model.Shape} element
26282 * @param {Object} [hints]
26283 * @param {Object} [hints.defaultDistance]
26284 *
26285 * @returns {Point}
26286 */
26287 function getNewShapePosition$1(source, element, hints) {
26288 if (!hints) {
26289 hints = {};
26290 }
26291
26292 var distance = hints.defaultDistance || DEFAULT_DISTANCE;
26293
26294 var sourceMid = getMid(source),
26295 sourceTrbl = asTRBL(source);
26296
26297 // simply put element right next to source
26298 return {
26299 x: sourceTrbl.right + distance + element.width / 2,
26300 y: sourceMid.y
26301 };
26302 }
26303
26304 /**
26305 * Select element after auto placement.
26306 *
26307 * @param {EventBus} eventBus
26308 * @param {Selection} selection
26309 */
26310 function AutoPlaceSelectionBehavior(eventBus, selection) {
26311
26312 eventBus.on('autoPlace.end', 500, function(e) {
26313 selection.select(e.shape);
26314 });
26315
26316 }
26317
26318 AutoPlaceSelectionBehavior.$inject = [
26319 'eventBus',
26320 'selection'
26321 ];
26322
26323 var AutoPlaceModule$1 = {
26324 __init__: [ 'autoPlaceSelectionBehavior' ],
26325 autoPlace: [ 'type', AutoPlace$1 ],
26326 autoPlaceSelectionBehavior: [ 'type', AutoPlaceSelectionBehavior ]
26327 };
26328
26329 /**
26330 * Return true if element has any of the given types.
26331 *
26332 * @param {djs.model.Base} element
26333 * @param {Array<string>} types
26334 *
26335 * @return {boolean}
26336 */
26337 function isAny(element, types) {
26338 return some(types, function(t) {
26339 return is$1(element, t);
26340 });
26341 }
26342
26343
26344 /**
26345 * Return the parent of the element with any of the given types.
26346 *
26347 * @param {djs.model.Base} element
26348 * @param {string|Array<string>} anyType
26349 *
26350 * @return {djs.model.Base}
26351 */
26352 function getParent(element, anyType) {
26353
26354 if (typeof anyType === 'string') {
26355 anyType = [ anyType ];
26356 }
26357
26358 while ((element = element.parent)) {
26359 if (isAny(element, anyType)) {
26360 return element;
26361 }
26362 }
26363
26364 return null;
26365 }
26366
26367 /**
26368 * Find the new position for the target element to
26369 * connect to source.
26370 *
26371 * @param {djs.model.Shape} source
26372 * @param {djs.model.Shape} element
26373 *
26374 * @return {Point}
26375 */
26376 function getNewShapePosition(source, element) {
26377
26378 if (is$1(element, 'bpmn:TextAnnotation')) {
26379 return getTextAnnotationPosition(source, element);
26380 }
26381
26382 if (isAny(element, [ 'bpmn:DataObjectReference', 'bpmn:DataStoreReference' ])) {
26383 return getDataElementPosition(source, element);
26384 }
26385
26386 if (is$1(element, 'bpmn:FlowNode')) {
26387 return getFlowNodePosition(source, element);
26388 }
26389 }
26390
26391 /**
26392 * Always try to place element right of source;
26393 * compute actual distance from previous nodes in flow.
26394 */
26395 function getFlowNodePosition(source, element) {
26396
26397 var sourceTrbl = asTRBL(source);
26398 var sourceMid = getMid(source);
26399
26400 var horizontalDistance = getConnectedDistance(source, {
26401 filter: function(connection) {
26402 return is$1(connection, 'bpmn:SequenceFlow');
26403 }
26404 });
26405
26406 var margin = 30,
26407 minDistance = 80,
26408 orientation = 'left';
26409
26410 if (is$1(source, 'bpmn:BoundaryEvent')) {
26411 orientation = getOrientation(source, source.host, -25);
26412
26413 if (orientation.indexOf('top') !== -1) {
26414 margin *= -1;
26415 }
26416 }
26417
26418 var position = {
26419 x: sourceTrbl.right + horizontalDistance + element.width / 2,
26420 y: sourceMid.y + getVerticalDistance(orientation, minDistance)
26421 };
26422
26423 var nextPositionDirection = {
26424 y: {
26425 margin: margin,
26426 minDistance: minDistance
26427 }
26428 };
26429
26430 return findFreePosition(source, element, position, generateGetNextPosition(nextPositionDirection));
26431 }
26432
26433
26434 function getVerticalDistance(orientation, minDistance) {
26435 if (orientation.indexOf('top') != -1) {
26436 return -1 * minDistance;
26437 } else if (orientation.indexOf('bottom') != -1) {
26438 return minDistance;
26439 } else {
26440 return 0;
26441 }
26442 }
26443
26444
26445 /**
26446 * Always try to place text annotations top right of source.
26447 */
26448 function getTextAnnotationPosition(source, element) {
26449
26450 var sourceTrbl = asTRBL(source);
26451
26452 var position = {
26453 x: sourceTrbl.right + element.width / 2,
26454 y: sourceTrbl.top - 50 - element.height / 2
26455 };
26456
26457 var nextPositionDirection = {
26458 y: {
26459 margin: -30,
26460 minDistance: 20
26461 }
26462 };
26463
26464 return findFreePosition(source, element, position, generateGetNextPosition(nextPositionDirection));
26465 }
26466
26467
26468 /**
26469 * Always put element bottom right of source.
26470 */
26471 function getDataElementPosition(source, element) {
26472
26473 var sourceTrbl = asTRBL(source);
26474
26475 var position = {
26476 x: sourceTrbl.right - 10 + element.width / 2,
26477 y: sourceTrbl.bottom + 40 + element.width / 2
26478 };
26479
26480 var nextPositionDirection = {
26481 x: {
26482 margin: 30,
26483 minDistance: 30
26484 }
26485 };
26486
26487 return findFreePosition(source, element, position, generateGetNextPosition(nextPositionDirection));
26488 }
26489
26490 /**
26491 * BPMN auto-place behavior.
26492 *
26493 * @param {EventBus} eventBus
26494 */
26495 function AutoPlace(eventBus) {
26496 eventBus.on('autoPlace', function(context) {
26497 var shape = context.shape,
26498 source = context.source;
26499
26500 return getNewShapePosition(source, shape);
26501 });
26502 }
26503
26504 AutoPlace.$inject = [ 'eventBus' ];
26505
26506 var AutoPlaceModule = {
26507 __depends__: [ AutoPlaceModule$1 ],
26508 __init__: [ 'bpmnAutoPlace' ],
26509 bpmnAutoPlace: [ 'type', AutoPlace ]
26510 };
26511
26512 var DEFAULT_PRIORITY$3 = 1000;
26513
26514 /**
26515 * A utility that can be used to plug-in into the command execution for
26516 * extension and/or validation.
26517 *
26518 * @param {EventBus} eventBus
26519 *
26520 * @example
26521 *
26522 * import inherits from 'inherits';
26523 *
26524 * import CommandInterceptor from 'diagram-js/lib/command/CommandInterceptor';
26525 *
26526 * function CommandLogger(eventBus) {
26527 * CommandInterceptor.call(this, eventBus);
26528 *
26529 * this.preExecute(function(event) {
26530 * console.log('command pre-execute', event);
26531 * });
26532 * }
26533 *
26534 * inherits(CommandLogger, CommandInterceptor);
26535 *
26536 */
26537 function CommandInterceptor(eventBus) {
26538 this._eventBus = eventBus;
26539 }
26540
26541 CommandInterceptor.$inject = [ 'eventBus' ];
26542
26543 function unwrapEvent(fn, that) {
26544 return function(event) {
26545 return fn.call(that || null, event.context, event.command, event);
26546 };
26547 }
26548
26549 /**
26550 * Register an interceptor for a command execution
26551 *
26552 * @param {string|Array<string>} [events] list of commands to register on
26553 * @param {string} [hook] command hook, i.e. preExecute, executed to listen on
26554 * @param {number} [priority] the priority on which to hook into the execution
26555 * @param {Function} handlerFn interceptor to be invoked with (event)
26556 * @param {boolean} unwrap if true, unwrap the event and pass (context, command, event) to the
26557 * listener instead
26558 * @param {Object} [that] Pass context (`this`) to the handler function
26559 */
26560 CommandInterceptor.prototype.on = function(events, hook, priority, handlerFn, unwrap, that) {
26561
26562 if (isFunction(hook) || isNumber(hook)) {
26563 that = unwrap;
26564 unwrap = handlerFn;
26565 handlerFn = priority;
26566 priority = hook;
26567 hook = null;
26568 }
26569
26570 if (isFunction(priority)) {
26571 that = unwrap;
26572 unwrap = handlerFn;
26573 handlerFn = priority;
26574 priority = DEFAULT_PRIORITY$3;
26575 }
26576
26577 if (isObject(unwrap)) {
26578 that = unwrap;
26579 unwrap = false;
26580 }
26581
26582 if (!isFunction(handlerFn)) {
26583 throw new Error('handlerFn must be a function');
26584 }
26585
26586 if (!isArray$2(events)) {
26587 events = [ events ];
26588 }
26589
26590 var eventBus = this._eventBus;
26591
26592 forEach(events, function(event) {
26593
26594 // concat commandStack(.event)?(.hook)?
26595 var fullEvent = [ 'commandStack', event, hook ].filter(function(e) { return e; }).join('.');
26596
26597 eventBus.on(fullEvent, priority, unwrap ? unwrapEvent(handlerFn, that) : handlerFn, that);
26598 });
26599 };
26600
26601
26602 var hooks = [
26603 'canExecute',
26604 'preExecute',
26605 'preExecuted',
26606 'execute',
26607 'executed',
26608 'postExecute',
26609 'postExecuted',
26610 'revert',
26611 'reverted'
26612 ];
26613
26614 /*
26615 * Install hook shortcuts
26616 *
26617 * This will generate the CommandInterceptor#(preExecute|...|reverted) methods
26618 * which will in term forward to CommandInterceptor#on.
26619 */
26620 forEach(hooks, function(hook) {
26621
26622 /**
26623 * {canExecute|preExecute|preExecuted|execute|executed|postExecute|postExecuted|revert|reverted}
26624 *
26625 * A named hook for plugging into the command execution
26626 *
26627 * @param {string|Array<string>} [events] list of commands to register on
26628 * @param {number} [priority] the priority on which to hook into the execution
26629 * @param {Function} handlerFn interceptor to be invoked with (event)
26630 * @param {boolean} [unwrap=false] if true, unwrap the event and pass (context, command, event) to the
26631 * listener instead
26632 * @param {Object} [that] Pass context (`this`) to the handler function
26633 */
26634 CommandInterceptor.prototype[hook] = function(events, priority, handlerFn, unwrap, that) {
26635
26636 if (isFunction(events) || isNumber(events)) {
26637 that = unwrap;
26638 unwrap = handlerFn;
26639 handlerFn = priority;
26640 priority = events;
26641 events = null;
26642 }
26643
26644 this.on(events, hook, priority, handlerFn, unwrap, that);
26645 };
26646 });
26647
26648 /**
26649 * An auto resize component that takes care of expanding a parent element
26650 * if child elements are created or moved close the parents edge.
26651 *
26652 * @param {EventBus} eventBus
26653 * @param {ElementRegistry} elementRegistry
26654 * @param {Modeling} modeling
26655 * @param {Rules} rules
26656 */
26657 function AutoResize(eventBus, elementRegistry, modeling, rules) {
26658
26659 CommandInterceptor.call(this, eventBus);
26660
26661 this._elementRegistry = elementRegistry;
26662 this._modeling = modeling;
26663 this._rules = rules;
26664
26665 var self = this;
26666
26667 this.postExecuted([ 'shape.create' ], function(event) {
26668 var context = event.context,
26669 hints = context.hints || {},
26670 shape = context.shape,
26671 parent = context.parent || context.newParent;
26672
26673 if (hints.autoResize === false) {
26674 return;
26675 }
26676
26677 self._expand([ shape ], parent);
26678 });
26679
26680 this.postExecuted([ 'elements.move' ], function(event) {
26681 var context = event.context,
26682 elements = flatten(values(context.closure.topLevel)),
26683 hints = context.hints;
26684
26685 var autoResize = hints ? hints.autoResize : true;
26686
26687 if (autoResize === false) {
26688 return;
26689 }
26690
26691 var expandings = groupBy(elements, function(element) {
26692 return element.parent.id;
26693 });
26694
26695 forEach(expandings, function(elements, parentId) {
26696
26697 // optionally filter elements to be considered when resizing
26698 if (isArray$2(autoResize)) {
26699 elements = elements.filter(function(element) {
26700 return find(autoResize, matchPattern({ id: element.id }));
26701 });
26702 }
26703
26704 self._expand(elements, parentId);
26705 });
26706 });
26707
26708 this.postExecuted([ 'shape.toggleCollapse' ], function(event) {
26709 var context = event.context,
26710 hints = context.hints,
26711 shape = context.shape;
26712
26713 if (hints && hints.autoResize === false) {
26714 return;
26715 }
26716
26717 if (shape.collapsed) {
26718 return;
26719 }
26720
26721 self._expand(shape.children || [], shape);
26722 });
26723
26724 this.postExecuted([ 'shape.resize' ], function(event) {
26725 var context = event.context,
26726 hints = context.hints,
26727 shape = context.shape,
26728 parent = shape.parent;
26729
26730 if (hints && hints.autoResize === false) {
26731 return;
26732 }
26733
26734 if (parent) {
26735 self._expand([ shape ], parent);
26736 }
26737 });
26738
26739 }
26740
26741 AutoResize.$inject = [
26742 'eventBus',
26743 'elementRegistry',
26744 'modeling',
26745 'rules'
26746 ];
26747
26748 inherits$1(AutoResize, CommandInterceptor);
26749
26750
26751 /**
26752 * Calculate the new bounds of the target shape, given
26753 * a number of elements have been moved or added into the parent.
26754 *
26755 * This method considers the current size, the added elements as well as
26756 * the provided padding for the new bounds.
26757 *
26758 * @param {Array<djs.model.Shape>} elements
26759 * @param {djs.model.Shape} target
26760 */
26761 AutoResize.prototype._getOptimalBounds = function(elements, target) {
26762
26763 var offset = this.getOffset(target),
26764 padding = this.getPadding(target);
26765
26766 var elementsTrbl = asTRBL(getBBox(elements)),
26767 targetTrbl = asTRBL(target);
26768
26769 var newTrbl = {};
26770
26771 if (elementsTrbl.top - targetTrbl.top < padding.top) {
26772 newTrbl.top = elementsTrbl.top - offset.top;
26773 }
26774
26775 if (elementsTrbl.left - targetTrbl.left < padding.left) {
26776 newTrbl.left = elementsTrbl.left - offset.left;
26777 }
26778
26779 if (targetTrbl.right - elementsTrbl.right < padding.right) {
26780 newTrbl.right = elementsTrbl.right + offset.right;
26781 }
26782
26783 if (targetTrbl.bottom - elementsTrbl.bottom < padding.bottom) {
26784 newTrbl.bottom = elementsTrbl.bottom + offset.bottom;
26785 }
26786
26787 return asBounds(assign({}, targetTrbl, newTrbl));
26788 };
26789
26790
26791 /**
26792 * Expand the target shape respecting rules, offset and padding
26793 *
26794 * @param {Array<djs.model.Shape>} elements
26795 * @param {djs.model.Shape|string} target|targetId
26796 */
26797 AutoResize.prototype._expand = function(elements, target) {
26798
26799 if (typeof target === 'string') {
26800 target = this._elementRegistry.get(target);
26801 }
26802
26803 var allowed = this._rules.allowed('element.autoResize', {
26804 elements: elements,
26805 target: target
26806 });
26807
26808 if (!allowed) {
26809 return;
26810 }
26811
26812 // calculate the new bounds
26813 var newBounds = this._getOptimalBounds(elements, target);
26814
26815 if (!boundsChanged$1(newBounds, target)) {
26816 return;
26817 }
26818
26819 var resizeDirections = getResizeDirections(pick(target, [ 'x', 'y', 'width', 'height' ]), newBounds);
26820
26821 // resize the parent shape
26822 this.resize(target, newBounds, {
26823 autoResize: resizeDirections
26824 });
26825
26826 var parent = target.parent;
26827
26828 // recursively expand parent elements
26829 if (parent) {
26830 this._expand([ target ], parent);
26831 }
26832 };
26833
26834
26835 /**
26836 * Get the amount to expand the given shape in each direction.
26837 *
26838 * @param {djs.model.Shape} shape
26839 *
26840 * @return {TRBL}
26841 */
26842 AutoResize.prototype.getOffset = function(shape) {
26843 return { top: 60, bottom: 60, left: 100, right: 100 };
26844 };
26845
26846
26847 /**
26848 * Get the activation threshold for each side for which
26849 * resize triggers.
26850 *
26851 * @param {djs.model.Shape} shape
26852 *
26853 * @return {TRBL}
26854 */
26855 AutoResize.prototype.getPadding = function(shape) {
26856 return { top: 2, bottom: 2, left: 15, right: 15 };
26857 };
26858
26859
26860 /**
26861 * Perform the actual resize operation.
26862 *
26863 * @param {djs.model.Shape} shape
26864 * @param {Bounds} newBounds
26865 * @param {Object} [hints]
26866 * @param {string} [hints.autoResize]
26867 */
26868 AutoResize.prototype.resize = function(shape, newBounds, hints) {
26869 this._modeling.resizeShape(shape, newBounds, null, hints);
26870 };
26871
26872
26873 function boundsChanged$1(newBounds, oldBounds) {
26874 return (
26875 newBounds.x !== oldBounds.x ||
26876 newBounds.y !== oldBounds.y ||
26877 newBounds.width !== oldBounds.width ||
26878 newBounds.height !== oldBounds.height
26879 );
26880 }
26881
26882 /**
26883 * Get directions of resize as {n|w|s|e} e.g. "nw".
26884 *
26885 * @param {Bounds} oldBounds
26886 * @param {Bounds} newBounds
26887 *
26888 * @returns {string} Resize directions as {n|w|s|e}.
26889 */
26890 function getResizeDirections(oldBounds, newBounds) {
26891 var directions = '';
26892
26893 oldBounds = asTRBL(oldBounds);
26894 newBounds = asTRBL(newBounds);
26895
26896 if (oldBounds.top > newBounds.top) {
26897 directions = directions.concat('n');
26898 }
26899
26900 if (oldBounds.right < newBounds.right) {
26901 directions = directions.concat('w');
26902 }
26903
26904 if (oldBounds.bottom < newBounds.bottom) {
26905 directions = directions.concat('s');
26906 }
26907
26908 if (oldBounds.left > newBounds.left) {
26909 directions = directions.concat('e');
26910 }
26911
26912 return directions;
26913 }
26914
26915 /**
26916 * Sub class of the AutoResize module which implements a BPMN
26917 * specific resize function.
26918 */
26919 function BpmnAutoResize(injector) {
26920
26921 injector.invoke(AutoResize, this);
26922 }
26923
26924 BpmnAutoResize.$inject = [
26925 'injector'
26926 ];
26927
26928 inherits$1(BpmnAutoResize, AutoResize);
26929
26930
26931 /**
26932 * Resize shapes and lanes.
26933 *
26934 * @param {djs.model.Shape} target
26935 * @param {Bounds} newBounds
26936 * @param {Object} hints
26937 */
26938 BpmnAutoResize.prototype.resize = function(target, newBounds, hints) {
26939
26940 if (is$1(target, 'bpmn:Participant')) {
26941 this._modeling.resizeLane(target, newBounds, null, hints);
26942 } else {
26943 this._modeling.resizeShape(target, newBounds, null, hints);
26944 }
26945 };
26946
26947 /**
26948 * A basic provider that may be extended to implement modeling rules.
26949 *
26950 * Extensions should implement the init method to actually add their custom
26951 * modeling checks. Checks may be added via the #addRule(action, fn) method.
26952 *
26953 * @param {EventBus} eventBus
26954 */
26955 function RuleProvider(eventBus) {
26956 CommandInterceptor.call(this, eventBus);
26957
26958 this.init();
26959 }
26960
26961 RuleProvider.$inject = [ 'eventBus' ];
26962
26963 inherits$1(RuleProvider, CommandInterceptor);
26964
26965
26966 /**
26967 * Adds a modeling rule for the given action, implemented through
26968 * a callback function.
26969 *
26970 * The function will receive the modeling specific action context
26971 * to perform its check. It must return `false` to disallow the
26972 * action from happening or `true` to allow the action.
26973 *
26974 * A rule provider may pass over the evaluation to lower priority
26975 * rules by returning return nothing (or <code>undefined</code>).
26976 *
26977 * @example
26978 *
26979 * ResizableRules.prototype.init = function() {
26980 *
26981 * \/**
26982 * * Return `true`, `false` or nothing to denote
26983 * * _allowed_, _not allowed_ and _continue evaluating_.
26984 * *\/
26985 * this.addRule('shape.resize', function(context) {
26986 *
26987 * var shape = context.shape;
26988 *
26989 * if (!context.newBounds) {
26990 * // check general resizability
26991 * if (!shape.resizable) {
26992 * return false;
26993 * }
26994 *
26995 * // not returning anything (read: undefined)
26996 * // will continue the evaluation of other rules
26997 * // (with lower priority)
26998 * return;
26999 * } else {
27000 * // element must have minimum size of 10*10 points
27001 * return context.newBounds.width > 10 && context.newBounds.height > 10;
27002 * }
27003 * });
27004 * };
27005 *
27006 * @param {string|Array<string>} actions the identifier for the modeling action to check
27007 * @param {number} [priority] the priority at which this rule is being applied
27008 * @param {Function} fn the callback function that performs the actual check
27009 */
27010 RuleProvider.prototype.addRule = function(actions, priority, fn) {
27011
27012 var self = this;
27013
27014 if (typeof actions === 'string') {
27015 actions = [ actions ];
27016 }
27017
27018 actions.forEach(function(action) {
27019
27020 self.canExecute(action, priority, function(context, action, event) {
27021 return fn(context);
27022 }, true);
27023 });
27024 };
27025
27026 /**
27027 * Implement this method to add new rules during provider initialization.
27028 */
27029 RuleProvider.prototype.init = function() {};
27030
27031 /**
27032 * This is a base rule provider for the element.autoResize rule.
27033 */
27034 function AutoResizeProvider(eventBus) {
27035
27036 RuleProvider.call(this, eventBus);
27037
27038 var self = this;
27039
27040 this.addRule('element.autoResize', function(context) {
27041 return self.canResize(context.elements, context.target);
27042 });
27043 }
27044
27045 AutoResizeProvider.$inject = [ 'eventBus' ];
27046
27047 inherits$1(AutoResizeProvider, RuleProvider);
27048
27049 /**
27050 * Needs to be implemented by sub classes to allow actual auto resize
27051 *
27052 * @param {Array<djs.model.Shape>} elements
27053 * @param {djs.model.Shape} target
27054 *
27055 * @return {boolean}
27056 */
27057 AutoResizeProvider.prototype.canResize = function(elements, target) {
27058 return false;
27059 };
27060
27061 /**
27062 * This module is a provider for automatically resizing parent BPMN elements
27063 */
27064 function BpmnAutoResizeProvider(eventBus, modeling) {
27065 AutoResizeProvider.call(this, eventBus);
27066
27067 this._modeling = modeling;
27068 }
27069
27070 inherits$1(BpmnAutoResizeProvider, AutoResizeProvider);
27071
27072 BpmnAutoResizeProvider.$inject = [
27073 'eventBus',
27074 'modeling'
27075 ];
27076
27077
27078 /**
27079 * Check if the given target can be expanded
27080 *
27081 * @param {djs.model.Shape} target
27082 *
27083 * @return {boolean}
27084 */
27085 BpmnAutoResizeProvider.prototype.canResize = function(elements, target) {
27086
27087 if (!is$1(target, 'bpmn:Participant') && !is$1(target, 'bpmn:Lane') && !(is$1(target, 'bpmn:SubProcess'))) {
27088 return false;
27089 }
27090
27091 var canResize = true;
27092
27093 forEach(elements, function(element) {
27094
27095 if (is$1(element, 'bpmn:Lane') || element.labelTarget) {
27096 canResize = false;
27097 return;
27098 }
27099 });
27100
27101 return canResize;
27102 };
27103
27104 var AutoResizeModule = {
27105 __init__: [
27106 'bpmnAutoResize',
27107 'bpmnAutoResizeProvider'
27108 ],
27109 bpmnAutoResize: [ 'type', BpmnAutoResize ],
27110 bpmnAutoResizeProvider: [ 'type', BpmnAutoResizeProvider ]
27111 };
27112
27113 var HIGH_PRIORITY$j = 1500;
27114
27115
27116 /**
27117 * Browsers may swallow certain events (hover, out ...) if users are to
27118 * fast with the mouse.
27119 *
27120 * @see http://stackoverflow.com/questions/7448468/why-cant-i-reliably-capture-a-mouseout-event
27121 *
27122 * The fix implemented in this component ensure that we
27123 *
27124 * 1) have a hover state after a successful drag.move event
27125 * 2) have an out event when dragging leaves an element
27126 *
27127 * @param {ElementRegistry} elementRegistry
27128 * @param {EventBus} eventBus
27129 * @param {Injector} injector
27130 */
27131 function HoverFix(elementRegistry, eventBus, injector) {
27132
27133 var self = this;
27134
27135 var dragging = injector.get('dragging', false);
27136
27137 /**
27138 * Make sure we are god damn hovering!
27139 *
27140 * @param {Event} dragging event
27141 */
27142 function ensureHover(event) {
27143
27144 if (event.hover) {
27145 return;
27146 }
27147
27148 var originalEvent = event.originalEvent;
27149
27150 var gfx = self._findTargetGfx(originalEvent);
27151
27152 var element = gfx && elementRegistry.get(gfx);
27153
27154 if (gfx && element) {
27155
27156 // 1) cancel current mousemove
27157 event.stopPropagation();
27158
27159 // 2) emit fake hover for new target
27160 dragging.hover({ element: element, gfx: gfx });
27161
27162 // 3) re-trigger move event
27163 dragging.move(originalEvent);
27164 }
27165 }
27166
27167
27168 if (dragging) {
27169
27170 /**
27171 * We wait for a specific sequence of events before
27172 * emitting a fake drag.hover event.
27173 *
27174 * Event Sequence:
27175 *
27176 * drag.start
27177 * drag.move >> ensure we are hovering
27178 */
27179 eventBus.on('drag.start', function(event) {
27180
27181 eventBus.once('drag.move', HIGH_PRIORITY$j, function(event) {
27182
27183 ensureHover(event);
27184
27185 });
27186
27187 });
27188 }
27189
27190
27191 /**
27192 * We make sure that element.out is always fired, even if the
27193 * browser swallows an element.out event.
27194 *
27195 * Event sequence:
27196 *
27197 * element.hover
27198 * (element.out >> sometimes swallowed)
27199 * element.hover >> ensure we fired element.out
27200 */
27201 (function() {
27202 var hoverGfx;
27203 var hover;
27204
27205 eventBus.on('element.hover', function(event) {
27206
27207 // (1) remember current hover element
27208 hoverGfx = event.gfx;
27209 hover = event.element;
27210 });
27211
27212 eventBus.on('element.hover', HIGH_PRIORITY$j, function(event) {
27213
27214 // (3) am I on an element still?
27215 if (hover) {
27216
27217 // (4) that is a problem, gotta "simulate the out"
27218 eventBus.fire('element.out', {
27219 element: hover,
27220 gfx: hoverGfx
27221 });
27222 }
27223
27224 });
27225
27226 eventBus.on('element.out', function() {
27227
27228 // (2) unset hover state if we correctly outed us *GG*
27229 hoverGfx = null;
27230 hover = null;
27231 });
27232
27233 })();
27234
27235 this._findTargetGfx = function(event) {
27236 var position,
27237 target;
27238
27239 if (!(event instanceof MouseEvent)) {
27240 return;
27241 }
27242
27243 position = toPoint(event);
27244
27245 // damn expensive operation, ouch!
27246 target = document.elementFromPoint(position.x, position.y);
27247
27248 return getGfx(target);
27249 };
27250
27251 }
27252
27253 HoverFix.$inject = [
27254 'elementRegistry',
27255 'eventBus',
27256 'injector'
27257 ];
27258
27259
27260 // helpers /////////////////////
27261
27262 function getGfx(target) {
27263 return closest(target, 'svg, .djs-element', true);
27264 }
27265
27266 var HoverFixModule = {
27267 __init__: [
27268 'hoverFix'
27269 ],
27270 hoverFix: [ 'type', HoverFix ],
27271 };
27272
27273 var round$a = Math.round;
27274
27275 var DRAG_ACTIVE_CLS = 'djs-drag-active';
27276
27277
27278 function preventDefault$1(event) {
27279 event.preventDefault();
27280 }
27281
27282 function isTouchEvent(event) {
27283
27284 // check for TouchEvent being available first
27285 // (i.e. not available on desktop Firefox)
27286 return typeof TouchEvent !== 'undefined' && event instanceof TouchEvent;
27287 }
27288
27289 function getLength(point) {
27290 return Math.sqrt(Math.pow(point.x, 2) + Math.pow(point.y, 2));
27291 }
27292
27293 /**
27294 * A helper that fires canvas localized drag events and realizes
27295 * the general "drag-and-drop" look and feel.
27296 *
27297 * Calling {@link Dragging#activate} activates dragging on a canvas.
27298 *
27299 * It provides the following:
27300 *
27301 * * emits life cycle events, namespaced with a prefix assigned
27302 * during dragging activation
27303 * * sets and restores the cursor
27304 * * sets and restores the selection if elements still exist
27305 * * ensures there can be only one drag operation active at a time
27306 *
27307 * Dragging may be canceled manually by calling {@link Dragging#cancel}
27308 * or by pressing ESC.
27309 *
27310 *
27311 * ## Life-cycle events
27312 *
27313 * Dragging can be in three different states, off, initialized
27314 * and active.
27315 *
27316 * (1) off: no dragging operation is in progress
27317 * (2) initialized: a new drag operation got initialized but not yet
27318 * started (i.e. because of no initial move)
27319 * (3) started: dragging is in progress
27320 *
27321 * Eventually dragging will be off again after a drag operation has
27322 * been ended or canceled via user click or ESC key press.
27323 *
27324 * To indicate transitions between these states dragging emits generic
27325 * life-cycle events with the `drag.` prefix _and_ events namespaced
27326 * to a prefix choosen by a user during drag initialization.
27327 *
27328 * The following events are emitted (appropriately prefixed) via
27329 * the {@link EventBus}.
27330 *
27331 * * `init`
27332 * * `start`
27333 * * `move`
27334 * * `end`
27335 * * `ended` (dragging already in off state)
27336 * * `cancel` (only if previously started)
27337 * * `canceled` (dragging already in off state, only if previously started)
27338 * * `cleanup`
27339 *
27340 *
27341 * @example
27342 *
27343 * function MyDragComponent(eventBus, dragging) {
27344 *
27345 * eventBus.on('mydrag.start', function(event) {
27346 * console.log('yes, we start dragging');
27347 * });
27348 *
27349 * eventBus.on('mydrag.move', function(event) {
27350 * console.log('canvas local coordinates', event.x, event.y, event.dx, event.dy);
27351 *
27352 * // local drag data is passed with the event
27353 * event.context.foo; // "BAR"
27354 *
27355 * // the original mouse event, too
27356 * event.originalEvent; // MouseEvent(...)
27357 * });
27358 *
27359 * eventBus.on('element.click', function(event) {
27360 * dragging.init(event, 'mydrag', {
27361 * cursor: 'grabbing',
27362 * data: {
27363 * context: {
27364 * foo: "BAR"
27365 * }
27366 * }
27367 * });
27368 * });
27369 * }
27370 */
27371 function Dragging(eventBus, canvas, selection, elementRegistry) {
27372
27373 var defaultOptions = {
27374 threshold: 5,
27375 trapClick: true
27376 };
27377
27378 // the currently active drag operation
27379 // dragging is active as soon as this context exists.
27380 //
27381 // it is visually _active_ only when a context.active flag is set to true.
27382 var context;
27383
27384 /* convert a global event into local coordinates */
27385 function toLocalPoint(globalPosition) {
27386
27387 var viewbox = canvas.viewbox();
27388
27389 var clientRect = canvas._container.getBoundingClientRect();
27390
27391 return {
27392 x: viewbox.x + (globalPosition.x - clientRect.left) / viewbox.scale,
27393 y: viewbox.y + (globalPosition.y - clientRect.top) / viewbox.scale
27394 };
27395 }
27396
27397 // helpers
27398
27399 function fire(type, dragContext) {
27400 dragContext = dragContext || context;
27401
27402 var event = eventBus.createEvent(
27403 assign(
27404 {},
27405 dragContext.payload,
27406 dragContext.data,
27407 { isTouch: dragContext.isTouch }
27408 )
27409 );
27410
27411 // default integration
27412 if (eventBus.fire('drag.' + type, event) === false) {
27413 return false;
27414 }
27415
27416 return eventBus.fire(dragContext.prefix + '.' + type, event);
27417 }
27418
27419 function restoreSelection(previousSelection) {
27420 var existingSelection = previousSelection.filter(function(element) {
27421 return elementRegistry.get(element.id);
27422 });
27423
27424 existingSelection.length && selection.select(existingSelection);
27425 }
27426
27427 // event listeners
27428
27429 function move(event, activate) {
27430 var payload = context.payload,
27431 displacement = context.displacement;
27432
27433 var globalStart = context.globalStart,
27434 globalCurrent = toPoint(event),
27435 globalDelta = delta(globalCurrent, globalStart);
27436
27437 var localStart = context.localStart,
27438 localCurrent = toLocalPoint(globalCurrent),
27439 localDelta = delta(localCurrent, localStart);
27440
27441
27442 // activate context explicitly or once threshold is reached
27443 if (!context.active && (activate || getLength(globalDelta) > context.threshold)) {
27444
27445 // fire start event with original
27446 // starting coordinates
27447
27448 assign(payload, {
27449 x: round$a(localStart.x + displacement.x),
27450 y: round$a(localStart.y + displacement.y),
27451 dx: 0,
27452 dy: 0
27453 }, { originalEvent: event });
27454
27455 if (false === fire('start')) {
27456 return cancel();
27457 }
27458
27459 context.active = true;
27460
27461 // unset selection and remember old selection
27462 // the previous (old) selection will always passed
27463 // with the event via the event.previousSelection property
27464 if (!context.keepSelection) {
27465 payload.previousSelection = selection.get();
27466 selection.select(null);
27467 }
27468
27469 // allow custom cursor
27470 if (context.cursor) {
27471 set(context.cursor);
27472 }
27473
27474 // indicate dragging via marker on root element
27475 canvas.addMarker(canvas.getRootElement(), DRAG_ACTIVE_CLS);
27476 }
27477
27478 stopPropagation$1(event);
27479
27480 if (context.active) {
27481
27482 // update payload with actual coordinates
27483 assign(payload, {
27484 x: round$a(localCurrent.x + displacement.x),
27485 y: round$a(localCurrent.y + displacement.y),
27486 dx: round$a(localDelta.x),
27487 dy: round$a(localDelta.y)
27488 }, { originalEvent: event });
27489
27490 // emit move event
27491 fire('move');
27492 }
27493 }
27494
27495 function end(event) {
27496 var previousContext,
27497 returnValue = true;
27498
27499 if (context.active) {
27500
27501 if (event) {
27502 context.payload.originalEvent = event;
27503
27504 // suppress original event (click, ...)
27505 // because we just ended a drag operation
27506 stopPropagation$1(event);
27507 }
27508
27509 // implementations may stop restoring the
27510 // original state (selections, ...) by preventing the
27511 // end events default action
27512 returnValue = fire('end');
27513 }
27514
27515 if (returnValue === false) {
27516 fire('rejected');
27517 }
27518
27519 previousContext = cleanup(returnValue !== true);
27520
27521 // last event to be fired when all drag operations are done
27522 // at this point in time no drag operation is in progress anymore
27523 fire('ended', previousContext);
27524 }
27525
27526
27527 // cancel active drag operation if the user presses
27528 // the ESC key on the keyboard
27529
27530 function checkCancel(event) {
27531
27532 if (event.which === 27) {
27533 preventDefault$1(event);
27534
27535 cancel();
27536 }
27537 }
27538
27539
27540 // prevent ghost click that might occur after a finished
27541 // drag and drop session
27542
27543 function trapClickAndEnd(event) {
27544
27545 var untrap;
27546
27547 // trap the click in case we are part of an active
27548 // drag operation. This will effectively prevent
27549 // the ghost click that cannot be canceled otherwise.
27550 if (context.active) {
27551
27552 untrap = install(eventBus);
27553
27554 // remove trap after minimal delay
27555 setTimeout(untrap, 400);
27556
27557 // prevent default action (click)
27558 preventDefault$1(event);
27559 }
27560
27561 end(event);
27562 }
27563
27564 function trapTouch(event) {
27565 move(event);
27566 }
27567
27568 // update the drag events hover (djs.model.Base) and hoverGfx (Snap<SVGElement>)
27569 // properties during hover and out and fire {prefix}.hover and {prefix}.out properties
27570 // respectively
27571
27572 function hover(event) {
27573 var payload = context.payload;
27574
27575 payload.hoverGfx = event.gfx;
27576 payload.hover = event.element;
27577
27578 fire('hover');
27579 }
27580
27581 function out(event) {
27582 fire('out');
27583
27584 var payload = context.payload;
27585
27586 payload.hoverGfx = null;
27587 payload.hover = null;
27588 }
27589
27590
27591 // life-cycle methods
27592
27593 function cancel(restore) {
27594 var previousContext;
27595
27596 if (!context) {
27597 return;
27598 }
27599
27600 var wasActive = context.active;
27601
27602 if (wasActive) {
27603 fire('cancel');
27604 }
27605
27606 previousContext = cleanup(restore);
27607
27608 if (wasActive) {
27609
27610 // last event to be fired when all drag operations are done
27611 // at this point in time no drag operation is in progress anymore
27612 fire('canceled', previousContext);
27613 }
27614 }
27615
27616 function cleanup(restore) {
27617 var previousContext,
27618 endDrag;
27619
27620 fire('cleanup');
27621
27622 // reset cursor
27623 unset();
27624
27625 if (context.trapClick) {
27626 endDrag = trapClickAndEnd;
27627 } else {
27628 endDrag = end;
27629 }
27630
27631 // reset dom listeners
27632 componentEvent.unbind(document, 'mousemove', move);
27633
27634 componentEvent.unbind(document, 'dragstart', preventDefault$1);
27635 componentEvent.unbind(document, 'selectstart', preventDefault$1);
27636
27637 componentEvent.unbind(document, 'mousedown', endDrag, true);
27638 componentEvent.unbind(document, 'mouseup', endDrag, true);
27639
27640 componentEvent.unbind(document, 'keyup', checkCancel);
27641
27642 componentEvent.unbind(document, 'touchstart', trapTouch, true);
27643 componentEvent.unbind(document, 'touchcancel', cancel, true);
27644 componentEvent.unbind(document, 'touchmove', move, true);
27645 componentEvent.unbind(document, 'touchend', end, true);
27646
27647 eventBus.off('element.hover', hover);
27648 eventBus.off('element.out', out);
27649
27650 // remove drag marker on root element
27651 canvas.removeMarker(canvas.getRootElement(), DRAG_ACTIVE_CLS);
27652
27653 // restore selection, unless it has changed
27654 var previousSelection = context.payload.previousSelection;
27655
27656 if (restore !== false && previousSelection && !selection.get().length) {
27657 restoreSelection(previousSelection);
27658 }
27659
27660 previousContext = context;
27661
27662 context = null;
27663
27664 return previousContext;
27665 }
27666
27667 /**
27668 * Initialize a drag operation.
27669 *
27670 * If `localPosition` is given, drag events will be emitted
27671 * relative to it.
27672 *
27673 * @param {MouseEvent|TouchEvent} [event]
27674 * @param {Point} [localPosition] actual diagram local position this drag operation should start at
27675 * @param {string} prefix
27676 * @param {Object} [options]
27677 */
27678 function init(event, relativeTo, prefix, options) {
27679
27680 // only one drag operation may be active, at a time
27681 if (context) {
27682 cancel(false);
27683 }
27684
27685 if (typeof relativeTo === 'string') {
27686 options = prefix;
27687 prefix = relativeTo;
27688 relativeTo = null;
27689 }
27690
27691 options = assign({}, defaultOptions, options || {});
27692
27693 var data = options.data || {},
27694 originalEvent,
27695 globalStart,
27696 localStart,
27697 endDrag,
27698 isTouch;
27699
27700 if (options.trapClick) {
27701 endDrag = trapClickAndEnd;
27702 } else {
27703 endDrag = end;
27704 }
27705
27706 if (event) {
27707 originalEvent = getOriginal$1(event) || event;
27708 globalStart = toPoint(event);
27709
27710 stopPropagation$1(event);
27711
27712 // prevent default browser dragging behavior
27713 if (originalEvent.type === 'dragstart') {
27714 preventDefault$1(originalEvent);
27715 }
27716 } else {
27717 originalEvent = null;
27718 globalStart = { x: 0, y: 0 };
27719 }
27720
27721 localStart = toLocalPoint(globalStart);
27722
27723 if (!relativeTo) {
27724 relativeTo = localStart;
27725 }
27726
27727 isTouch = isTouchEvent(originalEvent);
27728
27729 context = assign({
27730 prefix: prefix,
27731 data: data,
27732 payload: {},
27733 globalStart: globalStart,
27734 displacement: delta(relativeTo, localStart),
27735 localStart: localStart,
27736 isTouch: isTouch
27737 }, options);
27738
27739 // skip dom registration if trigger
27740 // is set to manual (during testing)
27741 if (!options.manual) {
27742
27743 // add dom listeners
27744
27745 if (isTouch) {
27746 componentEvent.bind(document, 'touchstart', trapTouch, true);
27747 componentEvent.bind(document, 'touchcancel', cancel, true);
27748 componentEvent.bind(document, 'touchmove', move, true);
27749 componentEvent.bind(document, 'touchend', end, true);
27750 } else {
27751
27752 // assume we use the mouse to interact per default
27753 componentEvent.bind(document, 'mousemove', move);
27754
27755 // prevent default browser drag and text selection behavior
27756 componentEvent.bind(document, 'dragstart', preventDefault$1);
27757 componentEvent.bind(document, 'selectstart', preventDefault$1);
27758
27759 componentEvent.bind(document, 'mousedown', endDrag, true);
27760 componentEvent.bind(document, 'mouseup', endDrag, true);
27761 }
27762
27763 componentEvent.bind(document, 'keyup', checkCancel);
27764
27765 eventBus.on('element.hover', hover);
27766 eventBus.on('element.out', out);
27767 }
27768
27769 fire('init');
27770
27771 if (options.autoActivate) {
27772 move(event, true);
27773 }
27774 }
27775
27776 // cancel on diagram destruction
27777 eventBus.on('diagram.destroy', cancel);
27778
27779
27780 // API
27781
27782 this.init = init;
27783 this.move = move;
27784 this.hover = hover;
27785 this.out = out;
27786 this.end = end;
27787
27788 this.cancel = cancel;
27789
27790 // for introspection
27791
27792 this.context = function() {
27793 return context;
27794 };
27795
27796 this.setOptions = function(options) {
27797 assign(defaultOptions, options);
27798 };
27799 }
27800
27801 Dragging.$inject = [
27802 'eventBus',
27803 'canvas',
27804 'selection',
27805 'elementRegistry'
27806 ];
27807
27808 var DraggingModule = {
27809 __depends__: [
27810 HoverFixModule,
27811 SelectionModule,
27812 ],
27813 dragging: [ 'type', Dragging ],
27814 };
27815
27816 /**
27817 * Initiates canvas scrolling if current cursor point is close to a border.
27818 * Cancelled when current point moves back inside the scrolling borders
27819 * or cancelled manually.
27820 *
27821 * Default options :
27822 * scrollThresholdIn: [ 20, 20, 20, 20 ],
27823 * scrollThresholdOut: [ 0, 0, 0, 0 ],
27824 * scrollRepeatTimeout: 15,
27825 * scrollStep: 10
27826 *
27827 * Threshold order:
27828 * [ left, top, right, bottom ]
27829 */
27830 function AutoScroll(config, eventBus, canvas) {
27831
27832 this._canvas = canvas;
27833
27834 this._opts = assign({
27835 scrollThresholdIn: [ 20, 20, 20, 20 ],
27836 scrollThresholdOut: [ 0, 0, 0, 0 ],
27837 scrollRepeatTimeout: 15,
27838 scrollStep: 10
27839 }, config);
27840
27841 var self = this;
27842
27843 eventBus.on('drag.move', function(e) {
27844 var point = self._toBorderPoint(e);
27845
27846 self.startScroll(point);
27847 });
27848
27849 eventBus.on([ 'drag.cleanup' ], function() {
27850 self.stopScroll();
27851 });
27852 }
27853
27854 AutoScroll.$inject = [
27855 'config.autoScroll',
27856 'eventBus',
27857 'canvas'
27858 ];
27859
27860
27861 /**
27862 * Starts scrolling loop.
27863 * Point is given in global scale in canvas container box plane.
27864 *
27865 * @param {Object} point { x: X, y: Y }
27866 */
27867 AutoScroll.prototype.startScroll = function(point) {
27868
27869 var canvas = this._canvas;
27870 var opts = this._opts;
27871 var self = this;
27872
27873 var clientRect = canvas.getContainer().getBoundingClientRect();
27874
27875 var diff = [
27876 point.x,
27877 point.y,
27878 clientRect.width - point.x,
27879 clientRect.height - point.y
27880 ];
27881
27882 this.stopScroll();
27883
27884 var dx = 0,
27885 dy = 0;
27886
27887 for (var i = 0; i < 4; i++) {
27888 if (between(diff[i], opts.scrollThresholdOut[i], opts.scrollThresholdIn[i])) {
27889 if (i === 0) {
27890 dx = opts.scrollStep;
27891 } else if (i == 1) {
27892 dy = opts.scrollStep;
27893 } else if (i == 2) {
27894 dx = -opts.scrollStep;
27895 } else if (i == 3) {
27896 dy = -opts.scrollStep;
27897 }
27898 }
27899 }
27900
27901 if (dx !== 0 || dy !== 0) {
27902 canvas.scroll({ dx: dx, dy: dy });
27903
27904 this._scrolling = setTimeout(function() {
27905 self.startScroll(point);
27906 }, opts.scrollRepeatTimeout);
27907 }
27908 };
27909
27910 function between(val, start, end) {
27911 if (start < val && val < end) {
27912 return true;
27913 }
27914
27915 return false;
27916 }
27917
27918
27919 /**
27920 * Stops scrolling loop.
27921 */
27922 AutoScroll.prototype.stopScroll = function() {
27923 clearTimeout(this._scrolling);
27924 };
27925
27926
27927 /**
27928 * Overrides defaults options.
27929 *
27930 * @param {Object} options
27931 */
27932 AutoScroll.prototype.setOptions = function(options) {
27933 this._opts = assign({}, this._opts, options);
27934 };
27935
27936
27937 /**
27938 * Converts event to a point in canvas container plane in global scale.
27939 *
27940 * @param {Event} event
27941 * @return {Point}
27942 */
27943 AutoScroll.prototype._toBorderPoint = function(event) {
27944 var clientRect = this._canvas._container.getBoundingClientRect();
27945
27946 var globalPosition = toPoint(event.originalEvent);
27947
27948 return {
27949 x: globalPosition.x - clientRect.left,
27950 y: globalPosition.y - clientRect.top
27951 };
27952 };
27953
27954 var AutoScrollModule = {
27955 __depends__: [
27956 DraggingModule,
27957 ],
27958 __init__: [ 'autoScroll' ],
27959 autoScroll: [ 'type', AutoScroll ]
27960 };
27961
27962 /**
27963 * A service that provides rules for certain diagram actions.
27964 *
27965 * The default implementation will hook into the {@link CommandStack}
27966 * to perform the actual rule evaluation. Make sure to provide the
27967 * `commandStack` service with this module if you plan to use it.
27968 *
27969 * Together with this implementation you may use the {@link RuleProvider}
27970 * to implement your own rule checkers.
27971 *
27972 * This module is ment to be easily replaced, thus the tiny foot print.
27973 *
27974 * @param {Injector} injector
27975 */
27976 function Rules(injector) {
27977 this._commandStack = injector.get('commandStack', false);
27978 }
27979
27980 Rules.$inject = [ 'injector' ];
27981
27982
27983 /**
27984 * Returns whether or not a given modeling action can be executed
27985 * in the specified context.
27986 *
27987 * This implementation will respond with allow unless anyone
27988 * objects.
27989 *
27990 * @param {string} action the action to be checked
27991 * @param {Object} [context] the context to check the action in
27992 *
27993 * @return {boolean} returns true, false or null depending on whether the
27994 * operation is allowed, not allowed or should be ignored.
27995 */
27996 Rules.prototype.allowed = function(action, context) {
27997 var allowed = true;
27998
27999 var commandStack = this._commandStack;
28000
28001 if (commandStack) {
28002 allowed = commandStack.canExecute(action, context);
28003 }
28004
28005 // map undefined to true, i.e. no rules
28006 return allowed === undefined ? true : allowed;
28007 };
28008
28009 var RulesModule$1 = {
28010 __init__: [ 'rules' ],
28011 rules: [ 'type', Rules ]
28012 };
28013
28014 var round$9 = Math.round,
28015 max$6 = Math.max;
28016
28017
28018 function circlePath(center, r) {
28019 var x = center.x,
28020 y = center.y;
28021
28022 return [
28023 ['M', x, y],
28024 ['m', 0, -r],
28025 ['a', r, r, 0, 1, 1, 0, 2 * r],
28026 ['a', r, r, 0, 1, 1, 0, -2 * r],
28027 ['z']
28028 ];
28029 }
28030
28031 function linePath(points) {
28032 var segments = [];
28033
28034 points.forEach(function(p, idx) {
28035 segments.push([ idx === 0 ? 'M' : 'L', p.x, p.y ]);
28036 });
28037
28038 return segments;
28039 }
28040
28041
28042 var INTERSECTION_THRESHOLD$1 = 10;
28043
28044 function getBendpointIntersection(waypoints, reference) {
28045
28046 var i, w;
28047
28048 for (i = 0; (w = waypoints[i]); i++) {
28049
28050 if (pointDistance(w, reference) <= INTERSECTION_THRESHOLD$1) {
28051 return {
28052 point: waypoints[i],
28053 bendpoint: true,
28054 index: i
28055 };
28056 }
28057 }
28058
28059 return null;
28060 }
28061
28062 function getPathIntersection(waypoints, reference) {
28063
28064 var intersections = intersect(circlePath(reference, INTERSECTION_THRESHOLD$1), linePath(waypoints));
28065
28066 var a = intersections[0],
28067 b = intersections[intersections.length - 1],
28068 idx;
28069
28070 if (!a) {
28071
28072 // no intersection
28073 return null;
28074 }
28075
28076 if (a !== b) {
28077
28078 if (a.segment2 !== b.segment2) {
28079
28080 // we use the bendpoint in between both segments
28081 // as the intersection point
28082
28083 idx = max$6(a.segment2, b.segment2) - 1;
28084
28085 return {
28086 point: waypoints[idx],
28087 bendpoint: true,
28088 index: idx
28089 };
28090 }
28091
28092 return {
28093 point: {
28094 x: (round$9(a.x + b.x) / 2),
28095 y: (round$9(a.y + b.y) / 2)
28096 },
28097 index: a.segment2
28098 };
28099 }
28100
28101 return {
28102 point: {
28103 x: round$9(a.x),
28104 y: round$9(a.y)
28105 },
28106 index: a.segment2
28107 };
28108 }
28109
28110 /**
28111 * Returns the closest point on the connection towards a given reference point.
28112 *
28113 * @param {Array<Point>} waypoints
28114 * @param {Point} reference
28115 *
28116 * @return {Object} intersection data (segment, point)
28117 */
28118 function getApproxIntersection(waypoints, reference) {
28119 return getBendpointIntersection(waypoints, reference) || getPathIntersection(waypoints, reference);
28120 }
28121
28122 var BENDPOINT_CLS = 'djs-bendpoint';
28123 var SEGMENT_DRAGGER_CLS = 'djs-segment-dragger';
28124
28125 function toCanvasCoordinates(canvas, event) {
28126
28127 var position = toPoint(event),
28128 clientRect = canvas._container.getBoundingClientRect(),
28129 offset;
28130
28131 // canvas relative position
28132
28133 offset = {
28134 x: clientRect.left,
28135 y: clientRect.top
28136 };
28137
28138 // update actual event payload with canvas relative measures
28139
28140 var viewbox = canvas.viewbox();
28141
28142 return {
28143 x: viewbox.x + (position.x - offset.x) / viewbox.scale,
28144 y: viewbox.y + (position.y - offset.y) / viewbox.scale
28145 };
28146 }
28147
28148 function getConnectionIntersection(canvas, waypoints, event) {
28149 var localPosition = toCanvasCoordinates(canvas, event),
28150 intersection = getApproxIntersection(waypoints, localPosition);
28151
28152 return intersection;
28153 }
28154
28155 function addBendpoint(parentGfx, cls) {
28156 var groupGfx = create$1('g');
28157 classes(groupGfx).add(BENDPOINT_CLS);
28158
28159 append(parentGfx, groupGfx);
28160
28161 var visual = create$1('circle');
28162 attr(visual, {
28163 cx: 0,
28164 cy: 0,
28165 r: 4
28166 });
28167 classes(visual).add('djs-visual');
28168
28169 append(groupGfx, visual);
28170
28171 var hit = create$1('circle');
28172 attr(hit, {
28173 cx: 0,
28174 cy: 0,
28175 r: 10
28176 });
28177 classes(hit).add('djs-hit');
28178
28179 append(groupGfx, hit);
28180
28181 if (cls) {
28182 classes(groupGfx).add(cls);
28183 }
28184
28185 return groupGfx;
28186 }
28187
28188 function createParallelDragger(parentGfx, segmentStart, segmentEnd, alignment) {
28189 var draggerGfx = create$1('g');
28190
28191 append(parentGfx, draggerGfx);
28192
28193 var width = 14,
28194 height = 3,
28195 padding = 11,
28196 hitWidth = calculateHitWidth(segmentStart, segmentEnd, alignment),
28197 hitHeight = height + padding;
28198
28199 var visual = create$1('rect');
28200 attr(visual, {
28201 x: -width / 2,
28202 y: -height / 2,
28203 width: width,
28204 height: height
28205 });
28206 classes(visual).add('djs-visual');
28207
28208 append(draggerGfx, visual);
28209
28210 var hit = create$1('rect');
28211 attr(hit, {
28212 x: -hitWidth / 2,
28213 y: -hitHeight / 2,
28214 width: hitWidth,
28215 height: hitHeight
28216 });
28217 classes(hit).add('djs-hit');
28218
28219 append(draggerGfx, hit);
28220
28221 rotate(draggerGfx, alignment === 'v' ? 90 : 0);
28222
28223 return draggerGfx;
28224 }
28225
28226
28227 function addSegmentDragger(parentGfx, segmentStart, segmentEnd) {
28228
28229 var groupGfx = create$1('g'),
28230 mid = getMidPoint(segmentStart, segmentEnd),
28231 alignment = pointsAligned(segmentStart, segmentEnd);
28232
28233 append(parentGfx, groupGfx);
28234
28235 createParallelDragger(groupGfx, segmentStart, segmentEnd, alignment);
28236
28237 classes(groupGfx).add(SEGMENT_DRAGGER_CLS);
28238 classes(groupGfx).add(alignment === 'h' ? 'horizontal' : 'vertical');
28239
28240 translate$2(groupGfx, mid.x, mid.y);
28241
28242 return groupGfx;
28243 }
28244
28245 /**
28246 * Calculates region for segment move which is 2/3 of the full segment length
28247 * @param {number} segmentLength
28248 *
28249 * @return {number}
28250 */
28251 function calculateSegmentMoveRegion(segmentLength) {
28252 return Math.abs(Math.round(segmentLength * 2 / 3));
28253 }
28254
28255 // helper //////////
28256
28257 function calculateHitWidth(segmentStart, segmentEnd, alignment) {
28258 var segmentLengthXAxis = segmentEnd.x - segmentStart.x,
28259 segmentLengthYAxis = segmentEnd.y - segmentStart.y;
28260
28261 return alignment === 'h' ?
28262 calculateSegmentMoveRegion(segmentLengthXAxis) :
28263 calculateSegmentMoveRegion(segmentLengthYAxis);
28264 }
28265
28266 var css_escape = {exports: {}};
28267
28268 /*! https://mths.be/cssescape v1.5.1 by @mathias | MIT license */
28269
28270 (function (module, exports) {
28271 (function(root, factory) {
28272 // https://github.com/umdjs/umd/blob/master/returnExports.js
28273 {
28274 // For Node.js.
28275 module.exports = factory(root);
28276 }
28277 }(typeof commonjsGlobal != 'undefined' ? commonjsGlobal : commonjsGlobal, function(root) {
28278
28279 if (root.CSS && root.CSS.escape) {
28280 return root.CSS.escape;
28281 }
28282
28283 // https://drafts.csswg.org/cssom/#serialize-an-identifier
28284 var cssEscape = function(value) {
28285 if (arguments.length == 0) {
28286 throw new TypeError('`CSS.escape` requires an argument.');
28287 }
28288 var string = String(value);
28289 var length = string.length;
28290 var index = -1;
28291 var codeUnit;
28292 var result = '';
28293 var firstCodeUnit = string.charCodeAt(0);
28294 while (++index < length) {
28295 codeUnit = string.charCodeAt(index);
28296 // Note: there’s no need to special-case astral symbols, surrogate
28297 // pairs, or lone surrogates.
28298
28299 // If the character is NULL (U+0000), then the REPLACEMENT CHARACTER
28300 // (U+FFFD).
28301 if (codeUnit == 0x0000) {
28302 result += '\uFFFD';
28303 continue;
28304 }
28305
28306 if (
28307 // If the character is in the range [\1-\1F] (U+0001 to U+001F) or is
28308 // U+007F, […]
28309 (codeUnit >= 0x0001 && codeUnit <= 0x001F) || codeUnit == 0x007F ||
28310 // If the character is the first character and is in the range [0-9]
28311 // (U+0030 to U+0039), […]
28312 (index == 0 && codeUnit >= 0x0030 && codeUnit <= 0x0039) ||
28313 // If the character is the second character and is in the range [0-9]
28314 // (U+0030 to U+0039) and the first character is a `-` (U+002D), […]
28315 (
28316 index == 1 &&
28317 codeUnit >= 0x0030 && codeUnit <= 0x0039 &&
28318 firstCodeUnit == 0x002D
28319 )
28320 ) {
28321 // https://drafts.csswg.org/cssom/#escape-a-character-as-code-point
28322 result += '\\' + codeUnit.toString(16) + ' ';
28323 continue;
28324 }
28325
28326 if (
28327 // If the character is the first character and is a `-` (U+002D), and
28328 // there is no second character, […]
28329 index == 0 &&
28330 length == 1 &&
28331 codeUnit == 0x002D
28332 ) {
28333 result += '\\' + string.charAt(index);
28334 continue;
28335 }
28336
28337 // If the character is not handled by one of the above rules and is
28338 // greater than or equal to U+0080, is `-` (U+002D) or `_` (U+005F), or
28339 // is in one of the ranges [0-9] (U+0030 to U+0039), [A-Z] (U+0041 to
28340 // U+005A), or [a-z] (U+0061 to U+007A), […]
28341 if (
28342 codeUnit >= 0x0080 ||
28343 codeUnit == 0x002D ||
28344 codeUnit == 0x005F ||
28345 codeUnit >= 0x0030 && codeUnit <= 0x0039 ||
28346 codeUnit >= 0x0041 && codeUnit <= 0x005A ||
28347 codeUnit >= 0x0061 && codeUnit <= 0x007A
28348 ) {
28349 // the character itself
28350 result += string.charAt(index);
28351 continue;
28352 }
28353
28354 // Otherwise, the escaped character.
28355 // https://drafts.csswg.org/cssom/#escape-a-character
28356 result += '\\' + string.charAt(index);
28357
28358 }
28359 return result;
28360 };
28361
28362 if (!root.CSS) {
28363 root.CSS = {};
28364 }
28365
28366 root.CSS.escape = cssEscape;
28367 return cssEscape;
28368
28369 }));
28370 }(css_escape));
28371
28372 var cssEscape = css_escape.exports;
28373
28374 var HTML_ESCAPE_MAP = {
28375 '&': '&amp;',
28376 '<': '&lt;',
28377 '>': '&gt;',
28378 '"': '&quot;',
28379 '\'': '&#39;'
28380 };
28381
28382 function escapeHTML(str) {
28383 str = '' + str;
28384
28385 return str && str.replace(/[&<>"']/g, function(match) {
28386 return HTML_ESCAPE_MAP[match];
28387 });
28388 }
28389
28390 /**
28391 * A service that adds editable bendpoints to connections.
28392 */
28393 function Bendpoints(
28394 eventBus, canvas, interactionEvents,
28395 bendpointMove, connectionSegmentMove) {
28396
28397 /**
28398 * Returns true if intersection point is inside middle region of segment, adjusted by
28399 * optional threshold
28400 */
28401 function isIntersectionMiddle(intersection, waypoints, treshold) {
28402 var idx = intersection.index,
28403 p = intersection.point,
28404 p0, p1, mid, aligned, xDelta, yDelta;
28405
28406 if (idx <= 0 || intersection.bendpoint) {
28407 return false;
28408 }
28409
28410 p0 = waypoints[idx - 1];
28411 p1 = waypoints[idx];
28412 mid = getMidPoint(p0, p1),
28413 aligned = pointsAligned(p0, p1);
28414 xDelta = Math.abs(p.x - mid.x);
28415 yDelta = Math.abs(p.y - mid.y);
28416
28417 return aligned && xDelta <= treshold && yDelta <= treshold;
28418 }
28419
28420 /**
28421 * Calculates the threshold from a connection's middle which fits the two-third-region
28422 */
28423 function calculateIntersectionThreshold(connection, intersection) {
28424 var waypoints = connection.waypoints,
28425 relevantSegment, alignment, segmentLength, threshold;
28426
28427 if (intersection.index <= 0 || intersection.bendpoint) {
28428 return null;
28429 }
28430
28431 // segment relative to connection intersection
28432 relevantSegment = {
28433 start: waypoints[intersection.index - 1],
28434 end: waypoints[intersection.index]
28435 };
28436
28437 alignment = pointsAligned(relevantSegment.start, relevantSegment.end);
28438
28439 if (!alignment) {
28440 return null;
28441 }
28442
28443 if (alignment === 'h') {
28444 segmentLength = relevantSegment.end.x - relevantSegment.start.x;
28445 } else {
28446 segmentLength = relevantSegment.end.y - relevantSegment.start.y;
28447 }
28448
28449 // calculate threshold relative to 2/3 of segment length
28450 threshold = calculateSegmentMoveRegion(segmentLength) / 2;
28451
28452 return threshold;
28453 }
28454
28455 function activateBendpointMove(event, connection) {
28456 var waypoints = connection.waypoints,
28457 intersection = getConnectionIntersection(canvas, waypoints, event),
28458 threshold;
28459
28460 if (!intersection) {
28461 return;
28462 }
28463
28464 threshold = calculateIntersectionThreshold(connection, intersection);
28465
28466 if (isIntersectionMiddle(intersection, waypoints, threshold)) {
28467 connectionSegmentMove.start(event, connection, intersection.index);
28468 } else {
28469 bendpointMove.start(event, connection, intersection.index, !intersection.bendpoint);
28470 }
28471
28472 // we've handled the event
28473 return true;
28474 }
28475
28476 function bindInteractionEvents(node, eventName, element) {
28477
28478 componentEvent.bind(node, eventName, function(event) {
28479 interactionEvents.triggerMouseEvent(eventName, event, element);
28480 event.stopPropagation();
28481 });
28482 }
28483
28484 function getBendpointsContainer(element, create) {
28485
28486 var layer = canvas.getLayer('overlays'),
28487 gfx = query('.djs-bendpoints[data-element-id="' + cssEscape(element.id) + '"]', layer);
28488
28489 if (!gfx && create) {
28490 gfx = create$1('g');
28491 attr(gfx, { 'data-element-id': element.id });
28492 classes(gfx).add('djs-bendpoints');
28493
28494 append(layer, gfx);
28495
28496 bindInteractionEvents(gfx, 'mousedown', element);
28497 bindInteractionEvents(gfx, 'click', element);
28498 bindInteractionEvents(gfx, 'dblclick', element);
28499 }
28500
28501 return gfx;
28502 }
28503
28504 function getSegmentDragger(idx, parentGfx) {
28505 return query(
28506 '.djs-segment-dragger[data-segment-idx="' + idx + '"]',
28507 parentGfx
28508 );
28509 }
28510
28511 function createBendpoints(gfx, connection) {
28512 connection.waypoints.forEach(function(p, idx) {
28513 var bendpoint = addBendpoint(gfx);
28514
28515 append(gfx, bendpoint);
28516
28517 translate$2(bendpoint, p.x, p.y);
28518 });
28519
28520 // add floating bendpoint
28521 addBendpoint(gfx, 'floating');
28522 }
28523
28524 function createSegmentDraggers(gfx, connection) {
28525
28526 var waypoints = connection.waypoints;
28527
28528 var segmentStart,
28529 segmentEnd,
28530 segmentDraggerGfx;
28531
28532 for (var i = 1; i < waypoints.length; i++) {
28533
28534 segmentStart = waypoints[i - 1];
28535 segmentEnd = waypoints[i];
28536
28537 if (pointsAligned(segmentStart, segmentEnd)) {
28538 segmentDraggerGfx = addSegmentDragger(gfx, segmentStart, segmentEnd);
28539
28540 attr(segmentDraggerGfx, { 'data-segment-idx': i });
28541
28542 bindInteractionEvents(segmentDraggerGfx, 'mousemove', connection);
28543 }
28544 }
28545 }
28546
28547 function clearBendpoints(gfx) {
28548 forEach(all('.' + BENDPOINT_CLS, gfx), function(node) {
28549 remove$1(node);
28550 });
28551 }
28552
28553 function clearSegmentDraggers(gfx) {
28554 forEach(all('.' + SEGMENT_DRAGGER_CLS, gfx), function(node) {
28555 remove$1(node);
28556 });
28557 }
28558
28559 function addHandles(connection) {
28560
28561 var gfx = getBendpointsContainer(connection);
28562
28563 if (!gfx) {
28564 gfx = getBendpointsContainer(connection, true);
28565
28566 createBendpoints(gfx, connection);
28567 createSegmentDraggers(gfx, connection);
28568 }
28569
28570 return gfx;
28571 }
28572
28573 function updateHandles(connection) {
28574
28575 var gfx = getBendpointsContainer(connection);
28576
28577 if (gfx) {
28578 clearSegmentDraggers(gfx);
28579 clearBendpoints(gfx);
28580 createSegmentDraggers(gfx, connection);
28581 createBendpoints(gfx, connection);
28582 }
28583 }
28584
28585 function updateFloatingBendpointPosition(parentGfx, intersection) {
28586 var floating = query('.floating', parentGfx),
28587 point = intersection.point;
28588
28589 if (!floating) {
28590 return;
28591 }
28592
28593 translate$2(floating, point.x, point.y);
28594
28595 }
28596
28597 function updateSegmentDraggerPosition(parentGfx, intersection, waypoints) {
28598
28599 var draggerGfx = getSegmentDragger(intersection.index, parentGfx),
28600 segmentStart = waypoints[intersection.index - 1],
28601 segmentEnd = waypoints[intersection.index],
28602 point = intersection.point,
28603 mid = getMidPoint(segmentStart, segmentEnd),
28604 alignment = pointsAligned(segmentStart, segmentEnd),
28605 draggerVisual, relativePosition;
28606
28607 if (!draggerGfx) {
28608 return;
28609 }
28610
28611 draggerVisual = getDraggerVisual(draggerGfx);
28612
28613 relativePosition = {
28614 x: point.x - mid.x,
28615 y: point.y - mid.y
28616 };
28617
28618 if (alignment === 'v') {
28619
28620 // rotate position
28621 relativePosition = {
28622 x: relativePosition.y,
28623 y: relativePosition.x
28624 };
28625 }
28626
28627 translate$2(draggerVisual, relativePosition.x, relativePosition.y);
28628 }
28629
28630 eventBus.on('connection.changed', function(event) {
28631 updateHandles(event.element);
28632 });
28633
28634 eventBus.on('connection.remove', function(event) {
28635 var gfx = getBendpointsContainer(event.element);
28636
28637 if (gfx) {
28638 remove$1(gfx);
28639 }
28640 });
28641
28642 eventBus.on('element.marker.update', function(event) {
28643
28644 var element = event.element,
28645 bendpointsGfx;
28646
28647 if (!element.waypoints) {
28648 return;
28649 }
28650
28651 bendpointsGfx = addHandles(element);
28652
28653 if (event.add) {
28654 classes(bendpointsGfx).add(event.marker);
28655 } else {
28656 classes(bendpointsGfx).remove(event.marker);
28657 }
28658 });
28659
28660 eventBus.on('element.mousemove', function(event) {
28661
28662 var element = event.element,
28663 waypoints = element.waypoints,
28664 bendpointsGfx,
28665 intersection;
28666
28667 if (waypoints) {
28668 bendpointsGfx = getBendpointsContainer(element, true);
28669
28670 intersection = getConnectionIntersection(canvas, waypoints, event.originalEvent);
28671
28672 if (!intersection) {
28673 return;
28674 }
28675
28676 updateFloatingBendpointPosition(bendpointsGfx, intersection);
28677
28678 if (!intersection.bendpoint) {
28679 updateSegmentDraggerPosition(bendpointsGfx, intersection, waypoints);
28680 }
28681
28682 }
28683 });
28684
28685 eventBus.on('element.mousedown', function(event) {
28686
28687 if (!isPrimaryButton(event)) {
28688 return;
28689 }
28690
28691 var originalEvent = event.originalEvent,
28692 element = event.element;
28693
28694 if (!element.waypoints) {
28695 return;
28696 }
28697
28698 return activateBendpointMove(originalEvent, element);
28699 });
28700
28701 eventBus.on('selection.changed', function(event) {
28702 var newSelection = event.newSelection,
28703 primary = newSelection[0];
28704
28705 if (primary && primary.waypoints) {
28706 addHandles(primary);
28707 }
28708 });
28709
28710 eventBus.on('element.hover', function(event) {
28711 var element = event.element;
28712
28713 if (element.waypoints) {
28714 addHandles(element);
28715 interactionEvents.registerEvent(event.gfx, 'mousemove', 'element.mousemove');
28716 }
28717 });
28718
28719 eventBus.on('element.out', function(event) {
28720 interactionEvents.unregisterEvent(event.gfx, 'mousemove', 'element.mousemove');
28721 });
28722
28723 // update bendpoint container data attribute on element ID change
28724 eventBus.on('element.updateId', function(context) {
28725 var element = context.element,
28726 newId = context.newId;
28727
28728 if (element.waypoints) {
28729 var bendpointContainer = getBendpointsContainer(element);
28730
28731 if (bendpointContainer) {
28732 attr(bendpointContainer, { 'data-element-id': newId });
28733 }
28734 }
28735 });
28736
28737 // API
28738
28739 this.addHandles = addHandles;
28740 this.updateHandles = updateHandles;
28741 this.getBendpointsContainer = getBendpointsContainer;
28742 this.getSegmentDragger = getSegmentDragger;
28743 }
28744
28745 Bendpoints.$inject = [
28746 'eventBus',
28747 'canvas',
28748 'interactionEvents',
28749 'bendpointMove',
28750 'connectionSegmentMove'
28751 ];
28752
28753
28754
28755 // helper /////////////
28756
28757 function getDraggerVisual(draggerGfx) {
28758 return query('.djs-visual', draggerGfx);
28759 }
28760
28761 var round$8 = Math.round;
28762
28763 var RECONNECT_START$1 = 'reconnectStart',
28764 RECONNECT_END$1 = 'reconnectEnd',
28765 UPDATE_WAYPOINTS$1 = 'updateWaypoints';
28766
28767
28768 /**
28769 * Move bendpoints through drag and drop to add/remove bendpoints or reconnect connection.
28770 */
28771 function BendpointMove(injector, eventBus, canvas, dragging, rules, modeling) {
28772 this._injector = injector;
28773
28774 this.start = function(event, connection, bendpointIndex, insert) {
28775 var gfx = canvas.getGraphics(connection),
28776 source = connection.source,
28777 target = connection.target,
28778 waypoints = connection.waypoints,
28779 type;
28780
28781 if (!insert && bendpointIndex === 0) {
28782 type = RECONNECT_START$1;
28783 } else
28784 if (!insert && bendpointIndex === waypoints.length - 1) {
28785 type = RECONNECT_END$1;
28786 } else {
28787 type = UPDATE_WAYPOINTS$1;
28788 }
28789
28790 var command = type === UPDATE_WAYPOINTS$1 ? 'connection.updateWaypoints' : 'connection.reconnect';
28791
28792 var allowed = rules.allowed(command, {
28793 connection: connection,
28794 source: source,
28795 target: target
28796 });
28797
28798 if (allowed === false) {
28799 allowed = rules.allowed(command, {
28800 connection: connection,
28801 source: target,
28802 target: source
28803 });
28804 }
28805
28806 if (allowed === false) {
28807 return;
28808 }
28809
28810 dragging.init(event, 'bendpoint.move', {
28811 data: {
28812 connection: connection,
28813 connectionGfx: gfx,
28814 context: {
28815 allowed: allowed,
28816 bendpointIndex: bendpointIndex,
28817 connection: connection,
28818 source: source,
28819 target: target,
28820 insert: insert,
28821 type: type
28822 }
28823 }
28824 });
28825 };
28826
28827 eventBus.on('bendpoint.move.hover', function(event) {
28828 var context = event.context,
28829 connection = context.connection,
28830 source = connection.source,
28831 target = connection.target,
28832 hover = event.hover,
28833 type = context.type;
28834
28835 // cache hover state
28836 context.hover = hover;
28837
28838 var allowed;
28839
28840 if (!hover) {
28841 return;
28842 }
28843
28844 var command = type === UPDATE_WAYPOINTS$1 ? 'connection.updateWaypoints' : 'connection.reconnect';
28845
28846 allowed = context.allowed = rules.allowed(command, {
28847 connection: connection,
28848 source: type === RECONNECT_START$1 ? hover : source,
28849 target: type === RECONNECT_END$1 ? hover : target
28850 });
28851
28852 if (allowed) {
28853 context.source = type === RECONNECT_START$1 ? hover : source;
28854 context.target = type === RECONNECT_END$1 ? hover : target;
28855
28856 return;
28857 }
28858
28859 if (allowed === false) {
28860 allowed = context.allowed = rules.allowed(command, {
28861 connection: connection,
28862 source: type === RECONNECT_END$1 ? hover : target,
28863 target: type === RECONNECT_START$1 ? hover : source
28864 });
28865 }
28866
28867 if (allowed) {
28868 context.source = type === RECONNECT_END$1 ? hover : target;
28869 context.target = type === RECONNECT_START$1 ? hover : source;
28870 }
28871 });
28872
28873 eventBus.on([ 'bendpoint.move.out', 'bendpoint.move.cleanup' ], function(event) {
28874 var context = event.context,
28875 type = context.type;
28876
28877 context.hover = null;
28878 context.source = null;
28879 context.target = null;
28880
28881 if (type !== UPDATE_WAYPOINTS$1) {
28882 context.allowed = false;
28883 }
28884 });
28885
28886 eventBus.on('bendpoint.move.end', function(event) {
28887 var context = event.context,
28888 allowed = context.allowed,
28889 bendpointIndex = context.bendpointIndex,
28890 connection = context.connection,
28891 insert = context.insert,
28892 newWaypoints = connection.waypoints.slice(),
28893 source = context.source,
28894 target = context.target,
28895 type = context.type,
28896 hints = context.hints || {};
28897
28898 // ensure integer values (important if zoom level was > 1 during move)
28899 var docking = {
28900 x: round$8(event.x),
28901 y: round$8(event.y)
28902 };
28903
28904 if (!allowed) {
28905 return false;
28906 }
28907
28908 if (type === UPDATE_WAYPOINTS$1) {
28909 if (insert) {
28910
28911 // insert new bendpoint
28912 newWaypoints.splice(bendpointIndex, 0, docking);
28913 } else {
28914
28915 // swap previous waypoint with moved one
28916 newWaypoints[bendpointIndex] = docking;
28917 }
28918
28919 // pass hints about actual moved bendpoint
28920 // useful for connection/label layout
28921 hints.bendpointMove = {
28922 insert: insert,
28923 bendpointIndex: bendpointIndex
28924 };
28925
28926 newWaypoints = this.cropWaypoints(connection, newWaypoints);
28927
28928 modeling.updateWaypoints(connection, filterRedundantWaypoints(newWaypoints), hints);
28929 } else {
28930 if (type === RECONNECT_START$1) {
28931 hints.docking = 'source';
28932
28933 if (isReverse$2(context)) {
28934 hints.docking = 'target';
28935
28936 hints.newWaypoints = newWaypoints.reverse();
28937 }
28938 } else if (type === RECONNECT_END$1) {
28939 hints.docking = 'target';
28940
28941 if (isReverse$2(context)) {
28942 hints.docking = 'source';
28943
28944 hints.newWaypoints = newWaypoints.reverse();
28945 }
28946 }
28947
28948 modeling.reconnect(connection, source, target, docking, hints);
28949 }
28950 }, this);
28951 }
28952
28953 BendpointMove.$inject = [
28954 'injector',
28955 'eventBus',
28956 'canvas',
28957 'dragging',
28958 'rules',
28959 'modeling'
28960 ];
28961
28962 BendpointMove.prototype.cropWaypoints = function(connection, newWaypoints) {
28963 var connectionDocking = this._injector.get('connectionDocking', false);
28964
28965 if (!connectionDocking) {
28966 return newWaypoints;
28967 }
28968
28969 var waypoints = connection.waypoints;
28970
28971 connection.waypoints = newWaypoints;
28972
28973 connection.waypoints = connectionDocking.getCroppedWaypoints(connection);
28974
28975 newWaypoints = connection.waypoints;
28976
28977 connection.waypoints = waypoints;
28978
28979 return newWaypoints;
28980 };
28981
28982
28983 // helpers //////////
28984
28985 function isReverse$2(context) {
28986 var hover = context.hover,
28987 source = context.source,
28988 target = context.target,
28989 type = context.type;
28990
28991 if (type === RECONNECT_START$1) {
28992 return hover && target && hover === target && source !== target;
28993 }
28994
28995 if (type === RECONNECT_END$1) {
28996 return hover && source && hover === source && source !== target;
28997 }
28998 }
28999
29000 var RECONNECT_START = 'reconnectStart',
29001 RECONNECT_END = 'reconnectEnd',
29002 UPDATE_WAYPOINTS = 'updateWaypoints';
29003
29004 var MARKER_OK$4 = 'connect-ok',
29005 MARKER_NOT_OK$4 = 'connect-not-ok',
29006 MARKER_CONNECT_HOVER$1 = 'connect-hover',
29007 MARKER_CONNECT_UPDATING$1 = 'djs-updating',
29008 MARKER_ELEMENT_HIDDEN = 'djs-element-hidden';
29009
29010 var HIGH_PRIORITY$i = 1100;
29011
29012 /**
29013 * Preview connection while moving bendpoints.
29014 */
29015 function BendpointMovePreview(bendpointMove, injector, eventBus, canvas) {
29016 this._injector = injector;
29017
29018 var connectionPreview = injector.get('connectionPreview', false);
29019
29020 eventBus.on('bendpoint.move.start', function(event) {
29021 var context = event.context,
29022 bendpointIndex = context.bendpointIndex,
29023 connection = context.connection,
29024 insert = context.insert,
29025 waypoints = connection.waypoints,
29026 newWaypoints = waypoints.slice();
29027
29028 context.waypoints = waypoints;
29029
29030 if (insert) {
29031
29032 // insert placeholder for new bendpoint
29033 newWaypoints.splice(bendpointIndex, 0, { x: event.x, y: event.y });
29034 }
29035
29036 connection.waypoints = newWaypoints;
29037
29038 // add dragger gfx
29039 var draggerGfx = context.draggerGfx = addBendpoint(canvas.getLayer('overlays'));
29040
29041 classes(draggerGfx).add('djs-dragging');
29042
29043 canvas.addMarker(connection, MARKER_ELEMENT_HIDDEN);
29044 canvas.addMarker(connection, MARKER_CONNECT_UPDATING$1);
29045 });
29046
29047 eventBus.on('bendpoint.move.hover', function(event) {
29048 var context = event.context,
29049 allowed = context.allowed,
29050 hover = context.hover,
29051 type = context.type;
29052
29053 if (hover) {
29054 canvas.addMarker(hover, MARKER_CONNECT_HOVER$1);
29055
29056 if (type === UPDATE_WAYPOINTS) {
29057 return;
29058 }
29059
29060 if (allowed) {
29061 canvas.removeMarker(hover, MARKER_NOT_OK$4);
29062 canvas.addMarker(hover, MARKER_OK$4);
29063 } else if (allowed === false) {
29064 canvas.removeMarker(hover, MARKER_OK$4);
29065 canvas.addMarker(hover, MARKER_NOT_OK$4);
29066 }
29067 }
29068 });
29069
29070 eventBus.on([
29071 'bendpoint.move.out',
29072 'bendpoint.move.cleanup'
29073 ], HIGH_PRIORITY$i, function(event) {
29074 var context = event.context,
29075 hover = context.hover,
29076 target = context.target;
29077
29078 if (hover) {
29079 canvas.removeMarker(hover, MARKER_CONNECT_HOVER$1);
29080 canvas.removeMarker(hover, target ? MARKER_OK$4 : MARKER_NOT_OK$4);
29081 }
29082 });
29083
29084 eventBus.on('bendpoint.move.move', function(event) {
29085 var context = event.context,
29086 allowed = context.allowed,
29087 bendpointIndex = context.bendpointIndex,
29088 draggerGfx = context.draggerGfx,
29089 hover = context.hover,
29090 type = context.type,
29091 connection = context.connection,
29092 source = connection.source,
29093 target = connection.target,
29094 newWaypoints = connection.waypoints.slice(),
29095 bendpoint = { x: event.x, y: event.y },
29096 hints = context.hints || {},
29097 drawPreviewHints = {};
29098
29099 if (connectionPreview) {
29100 if (hints.connectionStart) {
29101 drawPreviewHints.connectionStart = hints.connectionStart;
29102 }
29103
29104 if (hints.connectionEnd) {
29105 drawPreviewHints.connectionEnd = hints.connectionEnd;
29106 }
29107
29108
29109 if (type === RECONNECT_START) {
29110 if (isReverse$2(context)) {
29111 drawPreviewHints.connectionEnd = drawPreviewHints.connectionEnd || bendpoint;
29112
29113 drawPreviewHints.source = target;
29114 drawPreviewHints.target = hover || source;
29115
29116 newWaypoints = newWaypoints.reverse();
29117 } else {
29118 drawPreviewHints.connectionStart = drawPreviewHints.connectionStart || bendpoint;
29119
29120 drawPreviewHints.source = hover || source;
29121 drawPreviewHints.target = target;
29122 }
29123 } else if (type === RECONNECT_END) {
29124 if (isReverse$2(context)) {
29125 drawPreviewHints.connectionStart = drawPreviewHints.connectionStart || bendpoint;
29126
29127 drawPreviewHints.source = hover || target;
29128 drawPreviewHints.target = source;
29129
29130 newWaypoints = newWaypoints.reverse();
29131 } else {
29132 drawPreviewHints.connectionEnd = drawPreviewHints.connectionEnd || bendpoint;
29133
29134 drawPreviewHints.source = source;
29135 drawPreviewHints.target = hover || target;
29136 }
29137
29138 } else {
29139 drawPreviewHints.noCropping = true;
29140 drawPreviewHints.noLayout = true;
29141 newWaypoints[ bendpointIndex ] = bendpoint;
29142 }
29143
29144 if (type === UPDATE_WAYPOINTS) {
29145 newWaypoints = bendpointMove.cropWaypoints(connection, newWaypoints);
29146 }
29147
29148 drawPreviewHints.waypoints = newWaypoints;
29149
29150 connectionPreview.drawPreview(context, allowed, drawPreviewHints);
29151 }
29152
29153 translate$2(draggerGfx, event.x, event.y);
29154 }, this);
29155
29156 eventBus.on([
29157 'bendpoint.move.end',
29158 'bendpoint.move.cancel'
29159 ], HIGH_PRIORITY$i, function(event) {
29160 var context = event.context,
29161 connection = context.connection,
29162 draggerGfx = context.draggerGfx,
29163 hover = context.hover,
29164 target = context.target,
29165 waypoints = context.waypoints;
29166
29167 connection.waypoints = waypoints;
29168
29169 // remove dragger gfx
29170 remove$1(draggerGfx);
29171
29172 canvas.removeMarker(connection, MARKER_CONNECT_UPDATING$1);
29173 canvas.removeMarker(connection, MARKER_ELEMENT_HIDDEN);
29174
29175 if (hover) {
29176 canvas.removeMarker(hover, MARKER_OK$4);
29177 canvas.removeMarker(hover, target ? MARKER_OK$4 : MARKER_NOT_OK$4);
29178 }
29179
29180 if (connectionPreview) {
29181 connectionPreview.cleanUp(context);
29182 }
29183 });
29184 }
29185
29186 BendpointMovePreview.$inject = [
29187 'bendpointMove',
29188 'injector',
29189 'eventBus',
29190 'canvas'
29191 ];
29192
29193 var MARKER_CONNECT_HOVER = 'connect-hover',
29194 MARKER_CONNECT_UPDATING = 'djs-updating';
29195
29196
29197 function axisAdd(point, axis, delta) {
29198 return axisSet(point, axis, point[axis] + delta);
29199 }
29200
29201 function axisSet(point, axis, value) {
29202 return {
29203 x: (axis === 'x' ? value : point.x),
29204 y: (axis === 'y' ? value : point.y)
29205 };
29206 }
29207
29208 function axisFenced(position, segmentStart, segmentEnd, axis) {
29209
29210 var maxValue = Math.max(segmentStart[axis], segmentEnd[axis]),
29211 minValue = Math.min(segmentStart[axis], segmentEnd[axis]);
29212
29213 var padding = 20;
29214
29215 var fencedValue = Math.min(Math.max(minValue + padding, position[axis]), maxValue - padding);
29216
29217 return axisSet(segmentStart, axis, fencedValue);
29218 }
29219
29220 function flipAxis(axis) {
29221 return axis === 'x' ? 'y' : 'x';
29222 }
29223
29224 /**
29225 * Get the docking point on the given element.
29226 *
29227 * Compute a reasonable docking, if non exists.
29228 *
29229 * @param {Point} point
29230 * @param {djs.model.Shape} referenceElement
29231 * @param {string} moveAxis (x|y)
29232 *
29233 * @return {Point}
29234 */
29235 function getDocking$2(point, referenceElement, moveAxis) {
29236
29237 var referenceMid,
29238 inverseAxis;
29239
29240 if (point.original) {
29241 return point.original;
29242 } else {
29243 referenceMid = getMid(referenceElement);
29244 inverseAxis = flipAxis(moveAxis);
29245
29246 return axisSet(point, inverseAxis, referenceMid[inverseAxis]);
29247 }
29248 }
29249
29250 /**
29251 * A component that implements moving of bendpoints
29252 */
29253 function ConnectionSegmentMove(
29254 injector, eventBus, canvas,
29255 dragging, graphicsFactory, modeling) {
29256
29257 // optional connection docking integration
29258 var connectionDocking = injector.get('connectionDocking', false);
29259
29260
29261 // API
29262
29263 this.start = function(event, connection, idx) {
29264
29265 var context,
29266 gfx = canvas.getGraphics(connection),
29267 segmentStartIndex = idx - 1,
29268 segmentEndIndex = idx,
29269 waypoints = connection.waypoints,
29270 segmentStart = waypoints[segmentStartIndex],
29271 segmentEnd = waypoints[segmentEndIndex],
29272 intersection = getConnectionIntersection(canvas, waypoints, event),
29273 direction, axis, dragPosition;
29274
29275 direction = pointsAligned(segmentStart, segmentEnd);
29276
29277 // do not move diagonal connection
29278 if (!direction) {
29279 return;
29280 }
29281
29282 // the axis where we are going to move things
29283 axis = direction === 'v' ? 'x' : 'y';
29284
29285 if (segmentStartIndex === 0) {
29286 segmentStart = getDocking$2(segmentStart, connection.source, axis);
29287 }
29288
29289 if (segmentEndIndex === waypoints.length - 1) {
29290 segmentEnd = getDocking$2(segmentEnd, connection.target, axis);
29291 }
29292
29293 if (intersection) {
29294 dragPosition = intersection.point;
29295 } else {
29296
29297 // set to segment center as default
29298 dragPosition = {
29299 x: (segmentStart.x + segmentEnd.x) / 2,
29300 y: (segmentStart.y + segmentEnd.y) / 2
29301 };
29302 }
29303
29304 context = {
29305 connection: connection,
29306 segmentStartIndex: segmentStartIndex,
29307 segmentEndIndex: segmentEndIndex,
29308 segmentStart: segmentStart,
29309 segmentEnd: segmentEnd,
29310 axis: axis,
29311 dragPosition: dragPosition
29312 };
29313
29314 dragging.init(event, dragPosition, 'connectionSegment.move', {
29315 cursor: axis === 'x' ? 'resize-ew' : 'resize-ns',
29316 data: {
29317 connection: connection,
29318 connectionGfx: gfx,
29319 context: context
29320 }
29321 });
29322 };
29323
29324 /**
29325 * Crop connection if connection cropping is provided.
29326 *
29327 * @param {Connection} connection
29328 * @param {Array<Point>} newWaypoints
29329 *
29330 * @return {Array<Point>} cropped connection waypoints
29331 */
29332 function cropConnection(connection, newWaypoints) {
29333
29334 // crop connection, if docking service is provided only
29335 if (!connectionDocking) {
29336 return newWaypoints;
29337 }
29338
29339 var oldWaypoints = connection.waypoints,
29340 croppedWaypoints;
29341
29342 // temporary set new waypoints
29343 connection.waypoints = newWaypoints;
29344
29345 croppedWaypoints = connectionDocking.getCroppedWaypoints(connection);
29346
29347 // restore old waypoints
29348 connection.waypoints = oldWaypoints;
29349
29350 return croppedWaypoints;
29351 }
29352
29353 // DRAGGING IMPLEMENTATION
29354
29355 function redrawConnection(data) {
29356 graphicsFactory.update('connection', data.connection, data.connectionGfx);
29357 }
29358
29359 function updateDragger(context, segmentOffset, event) {
29360
29361 var newWaypoints = context.newWaypoints,
29362 segmentStartIndex = context.segmentStartIndex + segmentOffset,
29363 segmentStart = newWaypoints[segmentStartIndex],
29364 segmentEndIndex = context.segmentEndIndex + segmentOffset,
29365 segmentEnd = newWaypoints[segmentEndIndex],
29366 axis = flipAxis(context.axis);
29367
29368 // make sure the dragger does not move
29369 // outside the connection
29370 var draggerPosition = axisFenced(event, segmentStart, segmentEnd, axis);
29371
29372 // update dragger
29373 translate$2(context.draggerGfx, draggerPosition.x, draggerPosition.y);
29374 }
29375
29376 /**
29377 * Filter waypoints for redundant ones (i.e. on the same axis).
29378 * Returns the filtered waypoints and the offset related to the segment move.
29379 *
29380 * @param {Array<Point>} waypoints
29381 * @param {Integer} segmentStartIndex of moved segment start
29382 *
29383 * @return {Object} { filteredWaypoints, segmentOffset }
29384 */
29385 function filterRedundantWaypoints(waypoints, segmentStartIndex) {
29386
29387 var segmentOffset = 0;
29388
29389 var filteredWaypoints = waypoints.filter(function(r, idx) {
29390 if (pointsOnLine(waypoints[idx - 1], waypoints[idx + 1], r)) {
29391
29392 // remove point and increment offset
29393 segmentOffset = idx <= segmentStartIndex ? segmentOffset - 1 : segmentOffset;
29394 return false;
29395 }
29396
29397 // dont remove point
29398 return true;
29399 });
29400
29401 return {
29402 waypoints: filteredWaypoints,
29403 segmentOffset: segmentOffset
29404 };
29405 }
29406
29407 eventBus.on('connectionSegment.move.start', function(event) {
29408
29409 var context = event.context,
29410 connection = event.connection,
29411 layer = canvas.getLayer('overlays');
29412
29413 context.originalWaypoints = connection.waypoints.slice();
29414
29415 // add dragger gfx
29416 context.draggerGfx = addSegmentDragger(layer, context.segmentStart, context.segmentEnd);
29417 classes(context.draggerGfx).add('djs-dragging');
29418
29419 canvas.addMarker(connection, MARKER_CONNECT_UPDATING);
29420 });
29421
29422 eventBus.on('connectionSegment.move.move', function(event) {
29423
29424 var context = event.context,
29425 connection = context.connection,
29426 segmentStartIndex = context.segmentStartIndex,
29427 segmentEndIndex = context.segmentEndIndex,
29428 segmentStart = context.segmentStart,
29429 segmentEnd = context.segmentEnd,
29430 axis = context.axis;
29431
29432 var newWaypoints = context.originalWaypoints.slice(),
29433 newSegmentStart = axisAdd(segmentStart, axis, event['d' + axis]),
29434 newSegmentEnd = axisAdd(segmentEnd, axis, event['d' + axis]);
29435
29436 // original waypoint count and added / removed
29437 // from start waypoint delta. We use the later
29438 // to retrieve the updated segmentStartIndex / segmentEndIndex
29439 var waypointCount = newWaypoints.length,
29440 segmentOffset = 0;
29441
29442 // move segment start / end by axis delta
29443 newWaypoints[segmentStartIndex] = newSegmentStart;
29444 newWaypoints[segmentEndIndex] = newSegmentEnd;
29445
29446 var sourceToSegmentOrientation,
29447 targetToSegmentOrientation;
29448
29449 // handle first segment
29450 if (segmentStartIndex < 2) {
29451 sourceToSegmentOrientation = getOrientation(connection.source, newSegmentStart);
29452
29453 // first bendpoint, remove first segment if intersecting
29454 if (segmentStartIndex === 1) {
29455
29456 if (sourceToSegmentOrientation === 'intersect') {
29457 newWaypoints.shift();
29458 newWaypoints[0] = newSegmentStart;
29459 segmentOffset--;
29460 }
29461 }
29462
29463 // docking point, add segment if not intersecting anymore
29464 else {
29465 if (sourceToSegmentOrientation !== 'intersect') {
29466 newWaypoints.unshift(segmentStart);
29467 segmentOffset++;
29468 }
29469 }
29470 }
29471
29472 // handle last segment
29473 if (segmentEndIndex > waypointCount - 3) {
29474 targetToSegmentOrientation = getOrientation(connection.target, newSegmentEnd);
29475
29476 // last bendpoint, remove last segment if intersecting
29477 if (segmentEndIndex === waypointCount - 2) {
29478
29479 if (targetToSegmentOrientation === 'intersect') {
29480 newWaypoints.pop();
29481 newWaypoints[newWaypoints.length - 1] = newSegmentEnd;
29482 }
29483 }
29484
29485 // last bendpoint, remove last segment if intersecting
29486 else {
29487 if (targetToSegmentOrientation !== 'intersect') {
29488 newWaypoints.push(segmentEnd);
29489 }
29490 }
29491 }
29492
29493 // update connection waypoints
29494 context.newWaypoints = connection.waypoints = cropConnection(connection, newWaypoints);
29495
29496 // update dragger position
29497 updateDragger(context, segmentOffset, event);
29498
29499 // save segmentOffset in context
29500 context.newSegmentStartIndex = segmentStartIndex + segmentOffset;
29501
29502 // redraw connection
29503 redrawConnection(event);
29504 });
29505
29506 eventBus.on('connectionSegment.move.hover', function(event) {
29507
29508 event.context.hover = event.hover;
29509 canvas.addMarker(event.hover, MARKER_CONNECT_HOVER);
29510 });
29511
29512 eventBus.on([
29513 'connectionSegment.move.out',
29514 'connectionSegment.move.cleanup'
29515 ], function(event) {
29516
29517 // remove connect marker
29518 // if it was added
29519 var hover = event.context.hover;
29520
29521 if (hover) {
29522 canvas.removeMarker(hover, MARKER_CONNECT_HOVER);
29523 }
29524 });
29525
29526 eventBus.on('connectionSegment.move.cleanup', function(event) {
29527
29528 var context = event.context,
29529 connection = context.connection;
29530
29531 // remove dragger gfx
29532 if (context.draggerGfx) {
29533 remove$1(context.draggerGfx);
29534 }
29535
29536 canvas.removeMarker(connection, MARKER_CONNECT_UPDATING);
29537 });
29538
29539 eventBus.on([
29540 'connectionSegment.move.cancel',
29541 'connectionSegment.move.end'
29542 ], function(event) {
29543 var context = event.context,
29544 connection = context.connection;
29545
29546 connection.waypoints = context.originalWaypoints;
29547
29548 redrawConnection(event);
29549 });
29550
29551 eventBus.on('connectionSegment.move.end', function(event) {
29552
29553 var context = event.context,
29554 connection = context.connection,
29555 newWaypoints = context.newWaypoints,
29556 newSegmentStartIndex = context.newSegmentStartIndex;
29557
29558 // ensure we have actual pixel values bendpoint
29559 // coordinates (important when zoom level was > 1 during move)
29560 newWaypoints = newWaypoints.map(function(p) {
29561 return {
29562 original: p.original,
29563 x: Math.round(p.x),
29564 y: Math.round(p.y)
29565 };
29566 });
29567
29568 // apply filter redunant waypoints
29569 var filtered = filterRedundantWaypoints(newWaypoints, newSegmentStartIndex);
29570
29571 // get filtered waypoints
29572 var filteredWaypoints = filtered.waypoints,
29573 croppedWaypoints = cropConnection(connection, filteredWaypoints),
29574 segmentOffset = filtered.segmentOffset;
29575
29576 var hints = {
29577 segmentMove: {
29578 segmentStartIndex: context.segmentStartIndex,
29579 newSegmentStartIndex: newSegmentStartIndex + segmentOffset
29580 }
29581 };
29582
29583 modeling.updateWaypoints(connection, croppedWaypoints, hints);
29584 });
29585 }
29586
29587 ConnectionSegmentMove.$inject = [
29588 'injector',
29589 'eventBus',
29590 'canvas',
29591 'dragging',
29592 'graphicsFactory',
29593 'modeling'
29594 ];
29595
29596 var abs$6 = Math.abs,
29597 round$7 = Math.round;
29598
29599
29600 /**
29601 * Snap value to a collection of reference values.
29602 *
29603 * @param {number} value
29604 * @param {Array<number>} values
29605 * @param {number} [tolerance=10]
29606 *
29607 * @return {number} the value we snapped to or null, if none snapped
29608 */
29609 function snapTo(value, values, tolerance) {
29610 tolerance = tolerance === undefined ? 10 : tolerance;
29611
29612 var idx, snapValue;
29613
29614 for (idx = 0; idx < values.length; idx++) {
29615 snapValue = values[idx];
29616
29617 if (abs$6(snapValue - value) <= tolerance) {
29618 return snapValue;
29619 }
29620 }
29621 }
29622
29623
29624 function topLeft(bounds) {
29625 return {
29626 x: bounds.x,
29627 y: bounds.y
29628 };
29629 }
29630
29631 function bottomRight(bounds) {
29632 return {
29633 x: bounds.x + bounds.width,
29634 y: bounds.y + bounds.height
29635 };
29636 }
29637
29638 function mid$2(bounds, defaultValue) {
29639
29640 if (!bounds || isNaN(bounds.x) || isNaN(bounds.y)) {
29641 return defaultValue;
29642 }
29643
29644 return {
29645 x: round$7(bounds.x + bounds.width / 2),
29646 y: round$7(bounds.y + bounds.height / 2)
29647 };
29648 }
29649
29650
29651 /**
29652 * Retrieve the snap state of the given event.
29653 *
29654 * @param {Event} event
29655 * @param {string} axis
29656 *
29657 * @return {boolean} the snapped state
29658 *
29659 */
29660 function isSnapped(event, axis) {
29661 var snapped = event.snapped;
29662
29663 if (!snapped) {
29664 return false;
29665 }
29666
29667 if (typeof axis === 'string') {
29668 return snapped[axis];
29669 }
29670
29671 return snapped.x && snapped.y;
29672 }
29673
29674
29675 /**
29676 * Set the given event as snapped.
29677 *
29678 * This method may change the x and/or y position of the shape
29679 * from the given event!
29680 *
29681 * @param {Event} event
29682 * @param {string} axis
29683 * @param {number|boolean} value
29684 *
29685 * @return {number} old value
29686 */
29687 function setSnapped(event, axis, value) {
29688 if (typeof axis !== 'string') {
29689 throw new Error('axis must be in [x, y]');
29690 }
29691
29692 if (typeof value !== 'number' && value !== false) {
29693 throw new Error('value must be Number or false');
29694 }
29695
29696 var delta,
29697 previousValue = event[axis];
29698
29699 var snapped = event.snapped = (event.snapped || {});
29700
29701
29702 if (value === false) {
29703 snapped[axis] = false;
29704 } else {
29705 snapped[axis] = true;
29706
29707 delta = value - previousValue;
29708
29709 event[axis] += delta;
29710 event['d' + axis] += delta;
29711 }
29712
29713 return previousValue;
29714 }
29715
29716 /**
29717 * Get children of a shape.
29718 *
29719 * @param {djs.model.Shape} parent
29720 *
29721 * @returns {Array<djs.model.Shape|djs.model.Connection>}
29722 */
29723 function getChildren(parent) {
29724 return parent.children || [];
29725 }
29726
29727 var abs$5= Math.abs,
29728 round$6 = Math.round;
29729
29730 var TOLERANCE = 10;
29731
29732
29733 function BendpointSnapping(eventBus) {
29734
29735 function snapTo(values, value) {
29736
29737 if (isArray$2(values)) {
29738 var i = values.length;
29739
29740 while (i--) if (abs$5(values[i] - value) <= TOLERANCE) {
29741 return values[i];
29742 }
29743 } else {
29744 values = +values;
29745 var rem = value % values;
29746
29747 if (rem < TOLERANCE) {
29748 return value - rem;
29749 }
29750
29751 if (rem > values - TOLERANCE) {
29752 return value - rem + values;
29753 }
29754 }
29755
29756 return value;
29757 }
29758
29759 function mid(element) {
29760 if (element.width) {
29761 return {
29762 x: round$6(element.width / 2 + element.x),
29763 y: round$6(element.height / 2 + element.y)
29764 };
29765 }
29766 }
29767
29768 // connection segment snapping //////////////////////
29769
29770 function getConnectionSegmentSnaps(context) {
29771
29772 var snapPoints = context.snapPoints,
29773 connection = context.connection,
29774 waypoints = connection.waypoints,
29775 segmentStart = context.segmentStart,
29776 segmentStartIndex = context.segmentStartIndex,
29777 segmentEnd = context.segmentEnd,
29778 segmentEndIndex = context.segmentEndIndex,
29779 axis = context.axis;
29780
29781 if (snapPoints) {
29782 return snapPoints;
29783 }
29784
29785 var referenceWaypoints = [
29786 waypoints[segmentStartIndex - 1],
29787 segmentStart,
29788 segmentEnd,
29789 waypoints[segmentEndIndex + 1]
29790 ];
29791
29792 if (segmentStartIndex < 2) {
29793 referenceWaypoints.unshift(mid(connection.source));
29794 }
29795
29796 if (segmentEndIndex > waypoints.length - 3) {
29797 referenceWaypoints.unshift(mid(connection.target));
29798 }
29799
29800 context.snapPoints = snapPoints = { horizontal: [] , vertical: [] };
29801
29802 forEach(referenceWaypoints, function(p) {
29803
29804 // we snap on existing bendpoints only,
29805 // not placeholders that are inserted during add
29806 if (p) {
29807 p = p.original || p;
29808
29809 if (axis === 'y') {
29810 snapPoints.horizontal.push(p.y);
29811 }
29812
29813 if (axis === 'x') {
29814 snapPoints.vertical.push(p.x);
29815 }
29816 }
29817 });
29818
29819 return snapPoints;
29820 }
29821
29822 eventBus.on('connectionSegment.move.move', 1500, function(event) {
29823 var context = event.context,
29824 snapPoints = getConnectionSegmentSnaps(context),
29825 x = event.x,
29826 y = event.y,
29827 sx, sy;
29828
29829 if (!snapPoints) {
29830 return;
29831 }
29832
29833 // snap
29834 sx = snapTo(snapPoints.vertical, x);
29835 sy = snapTo(snapPoints.horizontal, y);
29836
29837
29838 // correction x/y
29839 var cx = (x - sx),
29840 cy = (y - sy);
29841
29842 // update delta
29843 assign(event, {
29844 dx: event.dx - cx,
29845 dy: event.dy - cy,
29846 x: sx,
29847 y: sy
29848 });
29849
29850 // only set snapped if actually snapped
29851 if (cx || snapPoints.vertical.indexOf(x) !== -1) {
29852 setSnapped(event, 'x', sx);
29853 }
29854
29855 if (cy || snapPoints.horizontal.indexOf(y) !== -1) {
29856 setSnapped(event, 'y', sy);
29857 }
29858 });
29859
29860
29861 // bendpoint snapping //////////////////////
29862
29863 function getBendpointSnaps(context) {
29864
29865 var snapPoints = context.snapPoints,
29866 waypoints = context.connection.waypoints,
29867 bendpointIndex = context.bendpointIndex;
29868
29869 if (snapPoints) {
29870 return snapPoints;
29871 }
29872
29873 var referenceWaypoints = [ waypoints[bendpointIndex - 1], waypoints[bendpointIndex + 1] ];
29874
29875 context.snapPoints = snapPoints = { horizontal: [] , vertical: [] };
29876
29877 forEach(referenceWaypoints, function(p) {
29878
29879 // we snap on existing bendpoints only,
29880 // not placeholders that are inserted during add
29881 if (p) {
29882 p = p.original || p;
29883
29884 snapPoints.horizontal.push(p.y);
29885 snapPoints.vertical.push(p.x);
29886 }
29887 });
29888
29889 return snapPoints;
29890 }
29891
29892
29893 eventBus.on([ 'bendpoint.move.move', 'bendpoint.move.end' ], 1500, function(event) {
29894
29895 var context = event.context,
29896 snapPoints = getBendpointSnaps(context),
29897 hover = context.hover,
29898 hoverMid = hover && mid(hover),
29899 x = event.x,
29900 y = event.y,
29901 sx, sy;
29902
29903 if (!snapPoints) {
29904 return;
29905 }
29906
29907 // snap to hover mid
29908 sx = snapTo(hoverMid ? snapPoints.vertical.concat([ hoverMid.x ]) : snapPoints.vertical, x);
29909 sy = snapTo(hoverMid ? snapPoints.horizontal.concat([ hoverMid.y ]) : snapPoints.horizontal, y);
29910
29911 // correction x/y
29912 var cx = (x - sx),
29913 cy = (y - sy);
29914
29915 // update delta
29916 assign(event, {
29917 dx: event.dx - cx,
29918 dy: event.dy - cy,
29919 x: event.x - cx,
29920 y: event.y - cy
29921 });
29922
29923 // only set snapped if actually snapped
29924 if (cx || snapPoints.vertical.indexOf(x) !== -1) {
29925 setSnapped(event, 'x', sx);
29926 }
29927
29928 if (cy || snapPoints.horizontal.indexOf(y) !== -1) {
29929 setSnapped(event, 'y', sy);
29930 }
29931 });
29932 }
29933
29934
29935 BendpointSnapping.$inject = [ 'eventBus' ];
29936
29937 var BendpointsModule = {
29938 __depends__: [
29939 DraggingModule,
29940 RulesModule$1
29941 ],
29942 __init__: [ 'bendpoints', 'bendpointSnapping', 'bendpointMovePreview' ],
29943 bendpoints: [ 'type', Bendpoints ],
29944 bendpointMove: [ 'type', BendpointMove ],
29945 bendpointMovePreview: [ 'type', BendpointMovePreview ],
29946 connectionSegmentMove: [ 'type', ConnectionSegmentMove ],
29947 bendpointSnapping: [ 'type', BendpointSnapping ]
29948 };
29949
29950 function Connect(eventBus, dragging, modeling, rules) {
29951
29952 // rules
29953
29954 function canConnect(source, target) {
29955 return rules.allowed('connection.create', {
29956 source: source,
29957 target: target
29958 });
29959 }
29960
29961 function canConnectReverse(source, target) {
29962 return canConnect(target, source);
29963 }
29964
29965
29966 // event handlers
29967
29968 eventBus.on('connect.hover', function(event) {
29969 var context = event.context,
29970 start = context.start,
29971 hover = event.hover,
29972 canExecute;
29973
29974 // cache hover state
29975 context.hover = hover;
29976
29977 canExecute = context.canExecute = canConnect(start, hover);
29978
29979 // ignore hover
29980 if (isNil(canExecute)) {
29981 return;
29982 }
29983
29984 if (canExecute !== false) {
29985 context.source = start;
29986 context.target = hover;
29987
29988 return;
29989 }
29990
29991 canExecute = context.canExecute = canConnectReverse(start, hover);
29992
29993 // ignore hover
29994 if (isNil(canExecute)) {
29995 return;
29996 }
29997
29998 if (canExecute !== false) {
29999 context.source = hover;
30000 context.target = start;
30001 }
30002 });
30003
30004 eventBus.on([ 'connect.out', 'connect.cleanup' ], function(event) {
30005 var context = event.context;
30006
30007 context.hover = null;
30008 context.source = null;
30009 context.target = null;
30010
30011 context.canExecute = false;
30012 });
30013
30014 eventBus.on('connect.end', function(event) {
30015 var context = event.context,
30016 canExecute = context.canExecute,
30017 connectionStart = context.connectionStart,
30018 connectionEnd = {
30019 x: event.x,
30020 y: event.y
30021 },
30022 source = context.source,
30023 target = context.target;
30024
30025 if (!canExecute) {
30026 return false;
30027 }
30028
30029 var attrs = null,
30030 hints = {
30031 connectionStart: isReverse$1(context) ? connectionEnd : connectionStart,
30032 connectionEnd: isReverse$1(context) ? connectionStart : connectionEnd
30033 };
30034
30035 if (isObject(canExecute)) {
30036 attrs = canExecute;
30037 }
30038
30039 modeling.connect(source, target, attrs, hints);
30040 });
30041
30042
30043 // API
30044
30045 /**
30046 * Start connect operation.
30047 *
30048 * @param {DOMEvent} event
30049 * @param {djs.model.Base} start
30050 * @param {Point} [connectionStart]
30051 * @param {boolean} [autoActivate=false]
30052 */
30053 this.start = function(event, start, connectionStart, autoActivate) {
30054 if (!isObject(connectionStart)) {
30055 autoActivate = connectionStart;
30056 connectionStart = getMid(start);
30057 }
30058
30059 dragging.init(event, 'connect', {
30060 autoActivate: autoActivate,
30061 data: {
30062 shape: start,
30063 context: {
30064 start: start,
30065 connectionStart: connectionStart
30066 }
30067 }
30068 });
30069 };
30070 }
30071
30072 Connect.$inject = [
30073 'eventBus',
30074 'dragging',
30075 'modeling',
30076 'rules'
30077 ];
30078
30079
30080 // helpers //////////
30081
30082 function isReverse$1(context) {
30083 var hover = context.hover,
30084 source = context.source,
30085 target = context.target;
30086
30087 return hover && source && hover === source && source !== target;
30088 }
30089
30090 var HIGH_PRIORITY$h = 1100,
30091 LOW_PRIORITY$h = 900;
30092
30093 var MARKER_OK$3 = 'connect-ok',
30094 MARKER_NOT_OK$3 = 'connect-not-ok';
30095
30096 /**
30097 * Shows connection preview during connect.
30098 *
30099 * @param {didi.Injector} injector
30100 * @param {EventBus} eventBus
30101 * @param {Canvas} canvas
30102 */
30103 function ConnectPreview(injector, eventBus, canvas) {
30104 var connectionPreview = injector.get('connectionPreview', false);
30105
30106 connectionPreview && eventBus.on('connect.move', function(event) {
30107 var context = event.context,
30108 canConnect = context.canExecute,
30109 hover = context.hover,
30110 source = context.source,
30111 start = context.start,
30112 startPosition = context.startPosition,
30113 target = context.target,
30114 connectionStart = context.connectionStart || startPosition,
30115 connectionEnd = context.connectionEnd || {
30116 x: event.x,
30117 y: event.y
30118 },
30119 previewStart = connectionStart,
30120 previewEnd = connectionEnd;
30121
30122 if (isReverse$1(context)) {
30123 previewStart = connectionEnd;
30124 previewEnd = connectionStart;
30125 }
30126
30127 connectionPreview.drawPreview(context, canConnect, {
30128 source: source || start,
30129 target: target || hover,
30130 connectionStart: previewStart,
30131 connectionEnd: previewEnd
30132 });
30133 });
30134
30135 eventBus.on('connect.hover', LOW_PRIORITY$h, function(event) {
30136 var context = event.context,
30137 hover = event.hover,
30138 canExecute = context.canExecute;
30139
30140 // ignore hover
30141 if (canExecute === null) {
30142 return;
30143 }
30144
30145 canvas.addMarker(hover, canExecute ? MARKER_OK$3 : MARKER_NOT_OK$3);
30146 });
30147
30148 eventBus.on([
30149 'connect.out',
30150 'connect.cleanup'
30151 ], HIGH_PRIORITY$h, function(event) {
30152 var hover = event.hover;
30153
30154 if (hover) {
30155 canvas.removeMarker(hover, MARKER_OK$3);
30156 canvas.removeMarker(hover, MARKER_NOT_OK$3);
30157 }
30158 });
30159
30160 connectionPreview && eventBus.on('connect.cleanup', function(event) {
30161 connectionPreview.cleanUp(event.context);
30162 });
30163 }
30164
30165 ConnectPreview.$inject = [
30166 'injector',
30167 'eventBus',
30168 'canvas'
30169 ];
30170
30171 var ConnectModule = {
30172 __depends__: [
30173 SelectionModule,
30174 RulesModule$1,
30175 DraggingModule
30176 ],
30177 __init__: [
30178 'connectPreview'
30179 ],
30180 connect: [ 'type', Connect ],
30181 connectPreview: [ 'type', ConnectPreview ]
30182 };
30183
30184 var MARKER_CONNECTION_PREVIEW = 'djs-connection-preview';
30185
30186 /**
30187 * Draws connection preview. Optionally, this can use layouter and connection docking to draw
30188 * better looking previews.
30189 *
30190 * @param {didi.Injector} injector
30191 * @param {Canvas} canvas
30192 * @param {GraphicsFactory} graphicsFactory
30193 * @param {ElementFactory} elementFactory
30194 */
30195 function ConnectionPreview(
30196 injector,
30197 canvas,
30198 graphicsFactory,
30199 elementFactory
30200 ) {
30201 this._canvas = canvas;
30202 this._graphicsFactory = graphicsFactory;
30203 this._elementFactory = elementFactory;
30204
30205 // optional components
30206 this._connectionDocking = injector.get('connectionDocking', false);
30207 this._layouter = injector.get('layouter', false);
30208 }
30209
30210 ConnectionPreview.$inject = [
30211 'injector',
30212 'canvas',
30213 'graphicsFactory',
30214 'elementFactory'
30215 ];
30216
30217 /**
30218 * Draw connection preview.
30219 *
30220 * Provide at least one of <source, connectionStart> and <target, connectionEnd> to create a preview.
30221 * In the clean up stage, call `connectionPreview#cleanUp` with the context to remove preview.
30222 *
30223 * @param {Object} context
30224 * @param {Object|boolean} canConnect
30225 * @param {Object} hints
30226 * @param {djs.model.shape} [hints.source] source element
30227 * @param {djs.model.shape} [hints.target] target element
30228 * @param {Point} [hints.connectionStart] connection preview start
30229 * @param {Point} [hints.connectionEnd] connection preview end
30230 * @param {Array<Point>} [hints.waypoints] provided waypoints for preview
30231 * @param {boolean} [hints.noLayout] true if preview should not be laid out
30232 * @param {boolean} [hints.noCropping] true if preview should not be cropped
30233 * @param {boolean} [hints.noNoop] true if simple connection should not be drawn
30234 */
30235 ConnectionPreview.prototype.drawPreview = function(context, canConnect, hints) {
30236
30237 hints = hints || {};
30238
30239 var connectionPreviewGfx = context.connectionPreviewGfx,
30240 getConnection = context.getConnection,
30241 source = hints.source,
30242 target = hints.target,
30243 waypoints = hints.waypoints,
30244 connectionStart = hints.connectionStart,
30245 connectionEnd = hints.connectionEnd,
30246 noLayout = hints.noLayout,
30247 noCropping = hints.noCropping,
30248 noNoop = hints.noNoop,
30249 connection;
30250
30251 var self = this;
30252
30253 if (!connectionPreviewGfx) {
30254 connectionPreviewGfx = context.connectionPreviewGfx = this.createConnectionPreviewGfx();
30255 }
30256
30257 clear(connectionPreviewGfx);
30258
30259 if (!getConnection) {
30260 getConnection = context.getConnection = cacheReturnValues(function(canConnect, source, target) {
30261 return self.getConnection(canConnect, source, target);
30262 });
30263 }
30264
30265 if (canConnect) {
30266 connection = getConnection(canConnect, source, target);
30267 }
30268
30269 if (!connection) {
30270 !noNoop && this.drawNoopPreview(connectionPreviewGfx, hints);
30271 return;
30272 }
30273
30274 connection.waypoints = waypoints || [];
30275
30276 // optional layout
30277 if (this._layouter && !noLayout) {
30278 connection.waypoints = this._layouter.layoutConnection(connection, {
30279 source: source,
30280 target: target,
30281 connectionStart: connectionStart,
30282 connectionEnd: connectionEnd,
30283 waypoints: hints.waypoints || connection.waypoints
30284 });
30285 }
30286
30287 // fallback if no waypoints were provided nor created with layouter
30288 if (!connection.waypoints || !connection.waypoints.length) {
30289 connection.waypoints = [
30290 source ? getMid(source) : connectionStart,
30291 target ? getMid(target) : connectionEnd
30292 ];
30293 }
30294
30295 // optional cropping
30296 if (this._connectionDocking && (source || target) && !noCropping) {
30297 connection.waypoints = this._connectionDocking.getCroppedWaypoints(connection, source, target);
30298 }
30299
30300 this._graphicsFactory.drawConnection(connectionPreviewGfx, connection);
30301 };
30302
30303 /**
30304 * Draw simple connection between source and target or provided points.
30305 *
30306 * @param {SVGElement} connectionPreviewGfx container for the connection
30307 * @param {Object} hints
30308 * @param {djs.model.shape} [hints.source] source element
30309 * @param {djs.model.shape} [hints.target] target element
30310 * @param {Point} [hints.connectionStart] required if source is not provided
30311 * @param {Point} [hints.connectionEnd] required if target is not provided
30312 */
30313 ConnectionPreview.prototype.drawNoopPreview = function(connectionPreviewGfx, hints) {
30314 var source = hints.source,
30315 target = hints.target,
30316 start = hints.connectionStart || getMid(source),
30317 end = hints.connectionEnd || getMid(target);
30318
30319 var waypoints = this.cropWaypoints(start, end, source, target);
30320
30321 var connection = this.createNoopConnection(waypoints[0], waypoints[1]);
30322
30323 append(connectionPreviewGfx, connection);
30324 };
30325
30326 /**
30327 * Return cropped waypoints.
30328 *
30329 * @param {Point} start
30330 * @param {Point} end
30331 * @param {djs.model.shape} source
30332 * @param {djs.model.shape} target
30333 *
30334 * @returns {Array}
30335 */
30336 ConnectionPreview.prototype.cropWaypoints = function(start, end, source, target) {
30337 var graphicsFactory = this._graphicsFactory,
30338 sourcePath = source && graphicsFactory.getShapePath(source),
30339 targetPath = target && graphicsFactory.getShapePath(target),
30340 connectionPath = graphicsFactory.getConnectionPath({ waypoints: [ start, end ] });
30341
30342 start = (source && getElementLineIntersection(sourcePath, connectionPath, true)) || start;
30343 end = (target && getElementLineIntersection(targetPath, connectionPath, false)) || end;
30344
30345 return [ start, end ];
30346 };
30347
30348 /**
30349 * Remove connection preview container if it exists.
30350 *
30351 * @param {Object} [context]
30352 * @param {SVGElement} [context.connectionPreviewGfx] preview container
30353 */
30354 ConnectionPreview.prototype.cleanUp = function(context) {
30355 if (context && context.connectionPreviewGfx) {
30356 remove$1(context.connectionPreviewGfx);
30357 }
30358 };
30359
30360 /**
30361 * Get connection that connects source and target.
30362 *
30363 * @param {Object|boolean} canConnect
30364 *
30365 * @returns {djs.model.connection}
30366 */
30367 ConnectionPreview.prototype.getConnection = function(canConnect) {
30368 var attrs = ensureConnectionAttrs(canConnect);
30369
30370 return this._elementFactory.createConnection(attrs);
30371 };
30372
30373
30374 /**
30375 * Add and return preview graphics.
30376 *
30377 * @returns {SVGElement}
30378 */
30379 ConnectionPreview.prototype.createConnectionPreviewGfx = function() {
30380 var gfx = create$1('g');
30381
30382 attr(gfx, {
30383 pointerEvents: 'none'
30384 });
30385
30386 classes(gfx).add(MARKER_CONNECTION_PREVIEW);
30387
30388 append(this._canvas.getActiveLayer(), gfx);
30389
30390 return gfx;
30391 };
30392
30393 /**
30394 * Create and return simple connection.
30395 *
30396 * @param {Point} start
30397 * @param {Point} end
30398 *
30399 * @returns {SVGElement}
30400 */
30401 ConnectionPreview.prototype.createNoopConnection = function(start, end) {
30402 var connection = create$1('polyline');
30403
30404 attr(connection, {
30405 'stroke': '#333',
30406 'strokeDasharray': [ 1 ],
30407 'strokeWidth': 2,
30408 'pointer-events': 'none'
30409 });
30410
30411 attr(connection, { 'points': [ start.x, start.y, end.x, end.y ] });
30412
30413 return connection;
30414 };
30415
30416 // helpers //////////
30417
30418 /**
30419 * Returns function that returns cached return values referenced by stringified first argument.
30420 *
30421 * @param {Function} fn
30422 *
30423 * @return {Function}
30424 */
30425 function cacheReturnValues(fn) {
30426 var returnValues = {};
30427
30428 /**
30429 * Return cached return value referenced by stringified first argument.
30430 *
30431 * @returns {*}
30432 */
30433 return function(firstArgument) {
30434 var key = JSON.stringify(firstArgument);
30435
30436 var returnValue = returnValues[key];
30437
30438 if (!returnValue) {
30439 returnValue = returnValues[key] = fn.apply(null, arguments);
30440 }
30441
30442 return returnValue;
30443 };
30444 }
30445
30446 /**
30447 * Ensure connection attributes is object.
30448 *
30449 * @param {Object|boolean} canConnect
30450 *
30451 * @returns {Object}
30452 */
30453 function ensureConnectionAttrs(canConnect) {
30454 if (isObject(canConnect)) {
30455 return canConnect;
30456 } else {
30457 return {};
30458 }
30459 }
30460
30461 var ConnectionPreviewModule = {
30462 __init__: [ 'connectionPreview' ],
30463 connectionPreview: [ 'type', ConnectionPreview ]
30464 };
30465
30466 var min$3 = Math.min,
30467 max$5 = Math.max;
30468
30469 function preventDefault(e) {
30470 e.preventDefault();
30471 }
30472
30473 function stopPropagation(e) {
30474 e.stopPropagation();
30475 }
30476
30477 function isTextNode(node) {
30478 return node.nodeType === Node.TEXT_NODE;
30479 }
30480
30481 function toArray(nodeList) {
30482 return [].slice.call(nodeList);
30483 }
30484
30485 /**
30486 * Initializes a container for a content editable div.
30487 *
30488 * Structure:
30489 *
30490 * container
30491 * parent
30492 * content
30493 * resize-handle
30494 *
30495 * @param {object} options
30496 * @param {DOMElement} options.container The DOM element to append the contentContainer to
30497 * @param {Function} options.keyHandler Handler for key events
30498 * @param {Function} options.resizeHandler Handler for resize events
30499 */
30500 function TextBox(options) {
30501 this.container = options.container;
30502
30503 this.parent = domify(
30504 '<div class="djs-direct-editing-parent">' +
30505 '<div class="djs-direct-editing-content" contenteditable="true"></div>' +
30506 '</div>'
30507 );
30508
30509 this.content = query('[contenteditable]', this.parent);
30510
30511 this.keyHandler = options.keyHandler || function() {};
30512 this.resizeHandler = options.resizeHandler || function() {};
30513
30514 this.autoResize = bind$2(this.autoResize, this);
30515 this.handlePaste = bind$2(this.handlePaste, this);
30516 }
30517
30518
30519 /**
30520 * Create a text box with the given position, size, style and text content
30521 *
30522 * @param {Object} bounds
30523 * @param {Number} bounds.x absolute x position
30524 * @param {Number} bounds.y absolute y position
30525 * @param {Number} [bounds.width] fixed width value
30526 * @param {Number} [bounds.height] fixed height value
30527 * @param {Number} [bounds.maxWidth] maximum width value
30528 * @param {Number} [bounds.maxHeight] maximum height value
30529 * @param {Number} [bounds.minWidth] minimum width value
30530 * @param {Number} [bounds.minHeight] minimum height value
30531 * @param {Object} [style]
30532 * @param {String} value text content
30533 *
30534 * @return {DOMElement} The created content DOM element
30535 */
30536 TextBox.prototype.create = function(bounds, style, value, options) {
30537 var self = this;
30538
30539 var parent = this.parent,
30540 content = this.content,
30541 container = this.container;
30542
30543 options = this.options = options || {};
30544
30545 style = this.style = style || {};
30546
30547 var parentStyle = pick(style, [
30548 'width',
30549 'height',
30550 'maxWidth',
30551 'maxHeight',
30552 'minWidth',
30553 'minHeight',
30554 'left',
30555 'top',
30556 'backgroundColor',
30557 'position',
30558 'overflow',
30559 'border',
30560 'wordWrap',
30561 'textAlign',
30562 'outline',
30563 'transform'
30564 ]);
30565
30566 assign(parent.style, {
30567 width: bounds.width + 'px',
30568 height: bounds.height + 'px',
30569 maxWidth: bounds.maxWidth + 'px',
30570 maxHeight: bounds.maxHeight + 'px',
30571 minWidth: bounds.minWidth + 'px',
30572 minHeight: bounds.minHeight + 'px',
30573 left: bounds.x + 'px',
30574 top: bounds.y + 'px',
30575 backgroundColor: '#ffffff',
30576 position: 'absolute',
30577 overflow: 'visible',
30578 border: '1px solid #ccc',
30579 boxSizing: 'border-box',
30580 wordWrap: 'normal',
30581 textAlign: 'center',
30582 outline: 'none'
30583 }, parentStyle);
30584
30585 var contentStyle = pick(style, [
30586 'fontFamily',
30587 'fontSize',
30588 'fontWeight',
30589 'lineHeight',
30590 'padding',
30591 'paddingTop',
30592 'paddingRight',
30593 'paddingBottom',
30594 'paddingLeft'
30595 ]);
30596
30597 assign(content.style, {
30598 boxSizing: 'border-box',
30599 width: '100%',
30600 outline: 'none',
30601 wordWrap: 'break-word'
30602 }, contentStyle);
30603
30604 if (options.centerVertically) {
30605 assign(content.style, {
30606 position: 'absolute',
30607 top: '50%',
30608 transform: 'translate(0, -50%)'
30609 }, contentStyle);
30610 }
30611
30612 content.innerText = value;
30613
30614 componentEvent.bind(content, 'keydown', this.keyHandler);
30615 componentEvent.bind(content, 'mousedown', stopPropagation);
30616 componentEvent.bind(content, 'paste', self.handlePaste);
30617
30618 if (options.autoResize) {
30619 componentEvent.bind(content, 'input', this.autoResize);
30620 }
30621
30622 if (options.resizable) {
30623 this.resizable(style);
30624 }
30625
30626 container.appendChild(parent);
30627
30628 // set selection to end of text
30629 this.setSelection(content.lastChild, content.lastChild && content.lastChild.length);
30630
30631 return parent;
30632 };
30633
30634 /**
30635 * Intercept paste events to remove formatting from pasted text.
30636 */
30637 TextBox.prototype.handlePaste = function(e) {
30638 var options = this.options,
30639 style = this.style;
30640
30641 e.preventDefault();
30642
30643 var text;
30644
30645 if (e.clipboardData) {
30646
30647 // Chrome, Firefox, Safari
30648 text = e.clipboardData.getData('text/plain');
30649 } else {
30650
30651 // Internet Explorer
30652 text = window.clipboardData.getData('Text');
30653 }
30654
30655 this.insertText(text);
30656
30657 if (options.autoResize) {
30658 var hasResized = this.autoResize(style);
30659
30660 if (hasResized) {
30661 this.resizeHandler(hasResized);
30662 }
30663 }
30664 };
30665
30666 TextBox.prototype.insertText = function(text) {
30667 text = normalizeEndOfLineSequences(text);
30668
30669 // insertText command not supported by Internet Explorer
30670 var success = document.execCommand('insertText', false, text);
30671
30672 if (success) {
30673 return;
30674 }
30675
30676 this._insertTextIE(text);
30677 };
30678
30679 TextBox.prototype._insertTextIE = function(text) {
30680
30681 // Internet Explorer
30682 var range = this.getSelection(),
30683 startContainer = range.startContainer,
30684 endContainer = range.endContainer,
30685 startOffset = range.startOffset,
30686 endOffset = range.endOffset,
30687 commonAncestorContainer = range.commonAncestorContainer;
30688
30689 var childNodesArray = toArray(commonAncestorContainer.childNodes);
30690
30691 var container,
30692 offset;
30693
30694 if (isTextNode(commonAncestorContainer)) {
30695 var containerTextContent = startContainer.textContent;
30696
30697 startContainer.textContent =
30698 containerTextContent.substring(0, startOffset)
30699 + text
30700 + containerTextContent.substring(endOffset);
30701
30702 container = startContainer;
30703 offset = startOffset + text.length;
30704
30705 } else if (startContainer === this.content && endContainer === this.content) {
30706 var textNode = document.createTextNode(text);
30707
30708 this.content.insertBefore(textNode, childNodesArray[startOffset]);
30709
30710 container = textNode;
30711 offset = textNode.textContent.length;
30712 } else {
30713 var startContainerChildIndex = childNodesArray.indexOf(startContainer),
30714 endContainerChildIndex = childNodesArray.indexOf(endContainer);
30715
30716 childNodesArray.forEach(function(childNode, index) {
30717
30718 if (index === startContainerChildIndex) {
30719 childNode.textContent =
30720 startContainer.textContent.substring(0, startOffset) +
30721 text +
30722 endContainer.textContent.substring(endOffset);
30723 } else if (index > startContainerChildIndex && index <= endContainerChildIndex) {
30724 remove$2(childNode);
30725 }
30726 });
30727
30728 container = startContainer;
30729 offset = startOffset + text.length;
30730 }
30731
30732 if (container && offset !== undefined) {
30733
30734 // is necessary in Internet Explorer
30735 setTimeout(function() {
30736 self.setSelection(container, offset);
30737 });
30738 }
30739 };
30740
30741 /**
30742 * Automatically resize element vertically to fit its content.
30743 */
30744 TextBox.prototype.autoResize = function() {
30745 var parent = this.parent,
30746 content = this.content;
30747
30748 var fontSize = parseInt(this.style.fontSize) || 12;
30749
30750 if (content.scrollHeight > parent.offsetHeight ||
30751 content.scrollHeight < parent.offsetHeight - fontSize) {
30752 var bounds = parent.getBoundingClientRect();
30753
30754 var height = content.scrollHeight;
30755 parent.style.height = height + 'px';
30756
30757 this.resizeHandler({
30758 width: bounds.width,
30759 height: bounds.height,
30760 dx: 0,
30761 dy: height - bounds.height
30762 });
30763 }
30764 };
30765
30766 /**
30767 * Make an element resizable by adding a resize handle.
30768 */
30769 TextBox.prototype.resizable = function() {
30770 var self = this;
30771
30772 var parent = this.parent,
30773 resizeHandle = this.resizeHandle;
30774
30775 var minWidth = parseInt(this.style.minWidth) || 0,
30776 minHeight = parseInt(this.style.minHeight) || 0,
30777 maxWidth = parseInt(this.style.maxWidth) || Infinity,
30778 maxHeight = parseInt(this.style.maxHeight) || Infinity;
30779
30780 if (!resizeHandle) {
30781 resizeHandle = this.resizeHandle = domify(
30782 '<div class="djs-direct-editing-resize-handle"></div>'
30783 );
30784
30785 var startX, startY, startWidth, startHeight;
30786
30787 var onMouseDown = function(e) {
30788 preventDefault(e);
30789 stopPropagation(e);
30790
30791 startX = e.clientX;
30792 startY = e.clientY;
30793
30794 var bounds = parent.getBoundingClientRect();
30795
30796 startWidth = bounds.width;
30797 startHeight = bounds.height;
30798
30799 componentEvent.bind(document, 'mousemove', onMouseMove);
30800 componentEvent.bind(document, 'mouseup', onMouseUp);
30801 };
30802
30803 var onMouseMove = function(e) {
30804 preventDefault(e);
30805 stopPropagation(e);
30806
30807 var newWidth = min$3(max$5(startWidth + e.clientX - startX, minWidth), maxWidth);
30808 var newHeight = min$3(max$5(startHeight + e.clientY - startY, minHeight), maxHeight);
30809
30810 parent.style.width = newWidth + 'px';
30811 parent.style.height = newHeight + 'px';
30812
30813 self.resizeHandler({
30814 width: startWidth,
30815 height: startHeight,
30816 dx: e.clientX - startX,
30817 dy: e.clientY - startY
30818 });
30819 };
30820
30821 var onMouseUp = function(e) {
30822 preventDefault(e);
30823 stopPropagation(e);
30824
30825 componentEvent.unbind(document,'mousemove', onMouseMove, false);
30826 componentEvent.unbind(document, 'mouseup', onMouseUp, false);
30827 };
30828
30829 componentEvent.bind(resizeHandle, 'mousedown', onMouseDown);
30830 }
30831
30832 assign(resizeHandle.style, {
30833 position: 'absolute',
30834 bottom: '0px',
30835 right: '0px',
30836 cursor: 'nwse-resize',
30837 width: '0',
30838 height: '0',
30839 borderTop: (parseInt(this.style.fontSize) / 4 || 3) + 'px solid transparent',
30840 borderRight: (parseInt(this.style.fontSize) / 4 || 3) + 'px solid #ccc',
30841 borderBottom: (parseInt(this.style.fontSize) / 4 || 3) + 'px solid #ccc',
30842 borderLeft: (parseInt(this.style.fontSize) / 4 || 3) + 'px solid transparent'
30843 });
30844
30845 parent.appendChild(resizeHandle);
30846 };
30847
30848
30849 /**
30850 * Clear content and style of the textbox, unbind listeners and
30851 * reset CSS style.
30852 */
30853 TextBox.prototype.destroy = function() {
30854 var parent = this.parent,
30855 content = this.content,
30856 resizeHandle = this.resizeHandle;
30857
30858 // clear content
30859 content.innerText = '';
30860
30861 // clear styles
30862 parent.removeAttribute('style');
30863 content.removeAttribute('style');
30864
30865 componentEvent.unbind(content, 'keydown', this.keyHandler);
30866 componentEvent.unbind(content, 'mousedown', stopPropagation);
30867 componentEvent.unbind(content, 'input', this.autoResize);
30868 componentEvent.unbind(content, 'paste', this.handlePaste);
30869
30870 if (resizeHandle) {
30871 resizeHandle.removeAttribute('style');
30872
30873 remove$2(resizeHandle);
30874 }
30875
30876 remove$2(parent);
30877 };
30878
30879
30880 TextBox.prototype.getValue = function() {
30881 return this.content.innerText.trim();
30882 };
30883
30884
30885 TextBox.prototype.getSelection = function() {
30886 var selection = window.getSelection(),
30887 range = selection.getRangeAt(0);
30888
30889 return range;
30890 };
30891
30892
30893 TextBox.prototype.setSelection = function(container, offset) {
30894 var range = document.createRange();
30895
30896 if (container === null) {
30897 range.selectNodeContents(this.content);
30898 } else {
30899 range.setStart(container, offset);
30900 range.setEnd(container, offset);
30901 }
30902
30903 var selection = window.getSelection();
30904
30905 selection.removeAllRanges();
30906 selection.addRange(range);
30907 };
30908
30909 // helpers //////////
30910
30911 function normalizeEndOfLineSequences(string) {
30912 return string.replace(/\r\n|\r|\n/g, '\n');
30913 }
30914
30915 /**
30916 * A direct editing component that allows users
30917 * to edit an elements text directly in the diagram
30918 *
30919 * @param {EventBus} eventBus the event bus
30920 */
30921 function DirectEditing(eventBus, canvas) {
30922
30923 this._eventBus = eventBus;
30924
30925 this._providers = [];
30926 this._textbox = new TextBox({
30927 container: canvas.getContainer(),
30928 keyHandler: bind$2(this._handleKey, this),
30929 resizeHandler: bind$2(this._handleResize, this)
30930 });
30931 }
30932
30933 DirectEditing.$inject = [ 'eventBus', 'canvas' ];
30934
30935
30936 /**
30937 * Register a direct editing provider
30938
30939 * @param {Object} provider the provider, must expose an #activate(element) method that returns
30940 * an activation context ({ bounds: {x, y, width, height }, text }) if
30941 * direct editing is available for the given element.
30942 * Additionally the provider must expose a #update(element, value) method
30943 * to receive direct editing updates.
30944 */
30945 DirectEditing.prototype.registerProvider = function(provider) {
30946 this._providers.push(provider);
30947 };
30948
30949
30950 /**
30951 * Returns true if direct editing is currently active
30952 *
30953 * @return {Boolean}
30954 */
30955 DirectEditing.prototype.isActive = function() {
30956 return !!this._active;
30957 };
30958
30959
30960 /**
30961 * Cancel direct editing, if it is currently active
30962 */
30963 DirectEditing.prototype.cancel = function() {
30964 if (!this._active) {
30965 return;
30966 }
30967
30968 this._fire('cancel');
30969 this.close();
30970 };
30971
30972
30973 DirectEditing.prototype._fire = function(event, context) {
30974 this._eventBus.fire('directEditing.' + event, context || { active: this._active });
30975 };
30976
30977 DirectEditing.prototype.close = function() {
30978 this._textbox.destroy();
30979
30980 this._fire('deactivate');
30981
30982 this._active = null;
30983
30984 this.resizable = undefined;
30985 };
30986
30987
30988 DirectEditing.prototype.complete = function() {
30989
30990 var active = this._active;
30991
30992 if (!active) {
30993 return;
30994 }
30995
30996 var containerBounds,
30997 previousBounds = active.context.bounds,
30998 newBounds = this.$textbox.getBoundingClientRect(),
30999 newText = this.getValue(),
31000 previousText = active.context.text;
31001
31002 if (
31003 newText !== previousText ||
31004 newBounds.height !== previousBounds.height ||
31005 newBounds.width !== previousBounds.width
31006 ) {
31007 containerBounds = this._textbox.container.getBoundingClientRect();
31008
31009 active.provider.update(active.element, newText, active.context.text, {
31010 x: newBounds.left - containerBounds.left,
31011 y: newBounds.top - containerBounds.top,
31012 width: newBounds.width,
31013 height: newBounds.height
31014 });
31015 }
31016
31017 this._fire('complete');
31018
31019 this.close();
31020 };
31021
31022
31023 DirectEditing.prototype.getValue = function() {
31024 return this._textbox.getValue();
31025 };
31026
31027
31028 DirectEditing.prototype._handleKey = function(e) {
31029
31030 // stop bubble
31031 e.stopPropagation();
31032
31033 var key = e.keyCode || e.charCode;
31034
31035 // ESC
31036 if (key === 27) {
31037 e.preventDefault();
31038 return this.cancel();
31039 }
31040
31041 // Enter
31042 if (key === 13 && !e.shiftKey) {
31043 e.preventDefault();
31044 return this.complete();
31045 }
31046 };
31047
31048
31049 DirectEditing.prototype._handleResize = function(event) {
31050 this._fire('resize', event);
31051 };
31052
31053
31054 /**
31055 * Activate direct editing on the given element
31056 *
31057 * @param {Object} ElementDescriptor the descriptor for a shape or connection
31058 * @return {Boolean} true if the activation was possible
31059 */
31060 DirectEditing.prototype.activate = function(element) {
31061 if (this.isActive()) {
31062 this.cancel();
31063 }
31064
31065 // the direct editing context
31066 var context;
31067
31068 var provider = find(this._providers, function(p) {
31069 return (context = p.activate(element)) ? p : null;
31070 });
31071
31072 // check if activation took place
31073 if (context) {
31074 this.$textbox = this._textbox.create(
31075 context.bounds,
31076 context.style,
31077 context.text,
31078 context.options
31079 );
31080
31081 this._active = {
31082 element: element,
31083 context: context,
31084 provider: provider
31085 };
31086
31087 if (context.options && context.options.resizable) {
31088 this.resizable = true;
31089 }
31090
31091 this._fire('activate');
31092 }
31093
31094 return !!context;
31095 };
31096
31097 var DirectEditingModule = {
31098 __depends__: [
31099 InteractionEventsModule$1
31100 ],
31101 __init__: [ 'directEditing' ],
31102 directEditing: [ 'type', DirectEditing ]
31103 };
31104
31105 var entrySelector = '.entry';
31106
31107 var DEFAULT_PRIORITY$2 = 1000;
31108
31109
31110 /**
31111 * A context pad that displays element specific, contextual actions next
31112 * to a diagram element.
31113 *
31114 * @param {Object} config
31115 * @param {boolean|Object} [config.scale={ min: 1.0, max: 1.5 }]
31116 * @param {number} [config.scale.min]
31117 * @param {number} [config.scale.max]
31118 * @param {EventBus} eventBus
31119 * @param {Overlays} overlays
31120 */
31121 function ContextPad(config, eventBus, overlays) {
31122
31123 this._eventBus = eventBus;
31124 this._overlays = overlays;
31125
31126 var scale = isDefined(config && config.scale) ? config.scale : {
31127 min: 1,
31128 max: 1.5
31129 };
31130
31131 this._overlaysConfig = {
31132 position: {
31133 right: -9,
31134 top: -6
31135 },
31136 scale: scale
31137 };
31138
31139 this._current = null;
31140
31141 this._init();
31142 }
31143
31144 ContextPad.$inject = [
31145 'config.contextPad',
31146 'eventBus',
31147 'overlays'
31148 ];
31149
31150
31151 /**
31152 * Registers events needed for interaction with other components
31153 */
31154 ContextPad.prototype._init = function() {
31155
31156 var eventBus = this._eventBus;
31157
31158 var self = this;
31159
31160 eventBus.on('selection.changed', function(e) {
31161
31162 var selection = e.newSelection;
31163
31164 if (selection.length === 1) {
31165 self.open(selection[0]);
31166 } else {
31167 self.close();
31168 }
31169 });
31170
31171 eventBus.on('elements.delete', function(event) {
31172 var elements = event.elements;
31173
31174 forEach(elements, function(e) {
31175 if (self.isOpen(e)) {
31176 self.close();
31177 }
31178 });
31179 });
31180
31181 eventBus.on('element.changed', function(event) {
31182 var element = event.element,
31183 current = self._current;
31184
31185 // force reopen if element for which we are currently opened changed
31186 if (current && current.element === element) {
31187 self.open(element, true);
31188 }
31189 });
31190 };
31191
31192
31193 /**
31194 * Register a provider with the context pad
31195 *
31196 * @param {number} [priority=1000]
31197 * @param {ContextPadProvider} provider
31198 *
31199 * @example
31200 * const contextPadProvider = {
31201 * getContextPadEntries: function(element) {
31202 * return function(entries) {
31203 * return {
31204 * ...entries,
31205 * 'entry-1': {
31206 * label: 'My Entry',
31207 * action: function() { alert("I have been clicked!"); }
31208 * }
31209 * };
31210 * }
31211 * }
31212 * };
31213 *
31214 * contextPad.registerProvider(800, contextPadProvider);
31215 */
31216 ContextPad.prototype.registerProvider = function(priority, provider) {
31217 if (!provider) {
31218 provider = priority;
31219 priority = DEFAULT_PRIORITY$2;
31220 }
31221
31222 this._eventBus.on('contextPad.getProviders', priority, function(event) {
31223 event.providers.push(provider);
31224 });
31225 };
31226
31227
31228 /**
31229 * Returns the context pad entries for a given element
31230 *
31231 * @param {djs.element.Base} element
31232 *
31233 * @return {Array<ContextPadEntryDescriptor>} list of entries
31234 */
31235 ContextPad.prototype.getEntries = function(element) {
31236 var providers = this._getProviders();
31237
31238 var entries = {};
31239
31240 // loop through all providers and their entries.
31241 // group entries by id so that overriding an entry is possible
31242 forEach(providers, function(provider) {
31243 var entriesOrUpdater = provider.getContextPadEntries(element);
31244
31245 if (isFunction(entriesOrUpdater)) {
31246 entries = entriesOrUpdater(entries);
31247 } else {
31248 forEach(entriesOrUpdater, function(entry, id) {
31249 entries[id] = entry;
31250 });
31251 }
31252 });
31253
31254 return entries;
31255 };
31256
31257
31258 /**
31259 * Trigger an action available on the opened context pad
31260 *
31261 * @param {string} action
31262 * @param {Event} event
31263 * @param {boolean} [autoActivate=false]
31264 */
31265 ContextPad.prototype.trigger = function(action, event, autoActivate) {
31266
31267 var element = this._current.element,
31268 entries = this._current.entries,
31269 entry,
31270 handler,
31271 originalEvent,
31272 button = event.delegateTarget || event.target;
31273
31274 if (!button) {
31275 return event.preventDefault();
31276 }
31277
31278 entry = entries[attr$1(button, 'data-action')];
31279 handler = entry.action;
31280
31281 originalEvent = event.originalEvent || event;
31282
31283 // simple action (via callback function)
31284 if (isFunction(handler)) {
31285 if (action === 'click') {
31286 return handler(originalEvent, element, autoActivate);
31287 }
31288 } else {
31289 if (handler[action]) {
31290 return handler[action](originalEvent, element, autoActivate);
31291 }
31292 }
31293
31294 // silence other actions
31295 event.preventDefault();
31296 };
31297
31298
31299 /**
31300 * Open the context pad for the given element
31301 *
31302 * @param {djs.model.Base} element
31303 * @param {boolean} force if true, force reopening the context pad
31304 */
31305 ContextPad.prototype.open = function(element, force) {
31306 if (!force && this.isOpen(element)) {
31307 return;
31308 }
31309
31310 this.close();
31311 this._updateAndOpen(element);
31312 };
31313
31314 ContextPad.prototype._getProviders = function() {
31315
31316 var event = this._eventBus.createEvent({
31317 type: 'contextPad.getProviders',
31318 providers: []
31319 });
31320
31321 this._eventBus.fire(event);
31322
31323 return event.providers;
31324 };
31325
31326 ContextPad.prototype._updateAndOpen = function(element) {
31327
31328 var entries = this.getEntries(element),
31329 pad = this.getPad(element),
31330 html = pad.html;
31331
31332 forEach(entries, function(entry, id) {
31333 var grouping = entry.group || 'default',
31334 control = domify(entry.html || '<div class="entry" draggable="true"></div>'),
31335 container;
31336
31337 attr$1(control, 'data-action', id);
31338
31339 container = query('[data-group=' + grouping + ']', html);
31340 if (!container) {
31341 container = domify('<div class="group" data-group="' + grouping + '"></div>');
31342 html.appendChild(container);
31343 }
31344
31345 container.appendChild(control);
31346
31347 if (entry.className) {
31348 addClasses$1(control, entry.className);
31349 }
31350
31351 if (entry.title) {
31352 attr$1(control, 'title', entry.title);
31353 }
31354
31355 if (entry.imageUrl) {
31356 control.appendChild(domify('<img src="' + entry.imageUrl + '">'));
31357 }
31358 });
31359
31360 classes$1(html).add('open');
31361
31362 this._current = {
31363 element: element,
31364 pad: pad,
31365 entries: entries
31366 };
31367
31368 this._eventBus.fire('contextPad.open', { current: this._current });
31369 };
31370
31371
31372 ContextPad.prototype.getPad = function(element) {
31373 if (this.isOpen()) {
31374 return this._current.pad;
31375 }
31376
31377 var self = this;
31378
31379 var overlays = this._overlays;
31380
31381 var html = domify('<div class="djs-context-pad"></div>');
31382
31383 var overlaysConfig = assign({
31384 html: html
31385 }, this._overlaysConfig);
31386
31387 delegate.bind(html, entrySelector, 'click', function(event) {
31388 self.trigger('click', event);
31389 });
31390
31391 delegate.bind(html, entrySelector, 'dragstart', function(event) {
31392 self.trigger('dragstart', event);
31393 });
31394
31395 // stop propagation of mouse events
31396 componentEvent.bind(html, 'mousedown', function(event) {
31397 event.stopPropagation();
31398 });
31399
31400 this._overlayId = overlays.add(element, 'context-pad', overlaysConfig);
31401
31402 var pad = overlays.get(this._overlayId);
31403
31404 this._eventBus.fire('contextPad.create', { element: element, pad: pad });
31405
31406 return pad;
31407 };
31408
31409
31410 /**
31411 * Close the context pad
31412 */
31413 ContextPad.prototype.close = function() {
31414 if (!this.isOpen()) {
31415 return;
31416 }
31417
31418 this._overlays.remove(this._overlayId);
31419
31420 this._overlayId = null;
31421
31422 this._eventBus.fire('contextPad.close', { current: this._current });
31423
31424 this._current = null;
31425 };
31426
31427 /**
31428 * Check if pad is open. If element is given, will check
31429 * if pad is opened with given element.
31430 *
31431 * @param {Element} element
31432 * @return {boolean}
31433 */
31434 ContextPad.prototype.isOpen = function(element) {
31435 return !!this._current && (!element ? true : this._current.element === element);
31436 };
31437
31438
31439
31440
31441 // helpers //////////////////////
31442
31443 function addClasses$1(element, classNames) {
31444
31445 var classes = classes$1(element);
31446
31447 var actualClassNames = isArray$2(classNames) ? classNames : classNames.split(/\s+/g);
31448 actualClassNames.forEach(function(cls) {
31449 classes.add(cls);
31450 });
31451 }
31452
31453 var ContextPadModule$1 = {
31454 __depends__: [
31455 InteractionEventsModule$1,
31456 OverlaysModule
31457 ],
31458 contextPad: [ 'type', ContextPad ]
31459 };
31460
31461 var MARKER_TYPES = [
31462 'marker-start',
31463 'marker-mid',
31464 'marker-end'
31465 ];
31466
31467 var NODES_CAN_HAVE_MARKER = [
31468 'circle',
31469 'ellipse',
31470 'line',
31471 'path',
31472 'polygon',
31473 'polyline',
31474 'rect'
31475 ];
31476
31477
31478 /**
31479 * Adds support for previews of moving/resizing elements.
31480 */
31481 function PreviewSupport(elementRegistry, eventBus, canvas, styles) {
31482 this._elementRegistry = elementRegistry;
31483 this._canvas = canvas;
31484 this._styles = styles;
31485
31486 this._clonedMarkers = {};
31487
31488 var self = this;
31489
31490 eventBus.on('drag.cleanup', function() {
31491 forEach(self._clonedMarkers, function(clonedMarker) {
31492 remove$1(clonedMarker);
31493 });
31494
31495 self._clonedMarkers = {};
31496 });
31497 }
31498
31499 PreviewSupport.$inject = [
31500 'elementRegistry',
31501 'eventBus',
31502 'canvas',
31503 'styles'
31504 ];
31505
31506
31507 /**
31508 * Returns graphics of an element.
31509 *
31510 * @param {djs.model.Base} element
31511 *
31512 * @return {SVGElement}
31513 */
31514 PreviewSupport.prototype.getGfx = function(element) {
31515 return this._elementRegistry.getGraphics(element);
31516 };
31517
31518 /**
31519 * Adds a move preview of a given shape to a given svg group.
31520 *
31521 * @param {djs.model.Base} element
31522 * @param {SVGElement} group
31523 * @param {SVGElement} [gfx]
31524 *
31525 * @return {SVGElement} dragger
31526 */
31527 PreviewSupport.prototype.addDragger = function(element, group, gfx) {
31528 gfx = gfx || this.getGfx(element);
31529
31530 var dragger = clone$1(gfx);
31531 var bbox = gfx.getBoundingClientRect();
31532
31533 this._cloneMarkers(getVisual(dragger));
31534
31535 attr(dragger, this._styles.cls('djs-dragger', [], {
31536 x: bbox.top,
31537 y: bbox.left
31538 }));
31539
31540 append(group, dragger);
31541
31542 return dragger;
31543 };
31544
31545 /**
31546 * Adds a resize preview of a given shape to a given svg group.
31547 *
31548 * @param {djs.model.Base} element
31549 * @param {SVGElement} group
31550 *
31551 * @return {SVGElement} frame
31552 */
31553 PreviewSupport.prototype.addFrame = function(shape, group) {
31554
31555 var frame = create$1('rect', {
31556 class: 'djs-resize-overlay',
31557 width: shape.width,
31558 height: shape.height,
31559 x: shape.x,
31560 y: shape.y
31561 });
31562
31563 append(group, frame);
31564
31565 return frame;
31566 };
31567
31568 /**
31569 * Clone all markers referenced by a node and its child nodes.
31570 *
31571 * @param {SVGElement} gfx
31572 */
31573 PreviewSupport.prototype._cloneMarkers = function(gfx) {
31574 var self = this;
31575
31576 if (gfx.childNodes) {
31577
31578 // TODO: use forEach once we drop PhantomJS
31579 for (var i = 0; i < gfx.childNodes.length; i++) {
31580
31581 // recursively clone markers of child nodes
31582 self._cloneMarkers(gfx.childNodes[ i ]);
31583 }
31584 }
31585
31586 if (!canHaveMarker(gfx)) {
31587 return;
31588 }
31589
31590 MARKER_TYPES.forEach(function(markerType) {
31591 if (attr(gfx, markerType)) {
31592 var marker = getMarker(gfx, markerType, self._canvas.getContainer());
31593
31594 self._cloneMarker(gfx, marker, markerType);
31595 }
31596 });
31597 };
31598
31599 /**
31600 * Clone marker referenced by an element.
31601 *
31602 * @param {SVGElement} gfx
31603 * @param {SVGElement} marker
31604 * @param {string} markerType
31605 */
31606 PreviewSupport.prototype._cloneMarker = function(gfx, marker, markerType) {
31607 var markerId = marker.id;
31608
31609 var clonedMarker = this._clonedMarkers[ markerId ];
31610
31611 if (!clonedMarker) {
31612 clonedMarker = clone$1(marker);
31613
31614 var clonedMarkerId = markerId + '-clone';
31615
31616 clonedMarker.id = clonedMarkerId;
31617
31618 classes(clonedMarker)
31619 .add('djs-dragger')
31620 .add('djs-dragger-marker');
31621
31622 this._clonedMarkers[ markerId ] = clonedMarker;
31623
31624 var defs = query('defs', this._canvas._svg);
31625
31626 if (!defs) {
31627 defs = create$1('defs');
31628
31629 append(this._canvas._svg, defs);
31630 }
31631
31632 append(defs, clonedMarker);
31633 }
31634
31635 var reference = idToReference(this._clonedMarkers[ markerId ].id);
31636
31637 attr(gfx, markerType, reference);
31638 };
31639
31640 // helpers //////////
31641
31642 /**
31643 * Get marker of given type referenced by node.
31644 *
31645 * @param {Node} node
31646 * @param {string} markerType
31647 * @param {Node} [parentNode]
31648 *
31649 * @param {Node}
31650 */
31651 function getMarker(node, markerType, parentNode) {
31652 var id = referenceToId(attr(node, markerType));
31653
31654 return query('marker#' + id, parentNode || document);
31655 }
31656
31657 /**
31658 * Get ID of fragment within current document from its functional IRI reference.
31659 * References may use single or double quotes.
31660 *
31661 * @param {string} reference
31662 *
31663 * @returns {string}
31664 */
31665 function referenceToId(reference) {
31666 return reference.match(/url\(['"]?#([^'"]*)['"]?\)/)[1];
31667 }
31668
31669 /**
31670 * Get functional IRI reference for given ID of fragment within current document.
31671 *
31672 * @param {string} id
31673 *
31674 * @returns {string}
31675 */
31676 function idToReference(id) {
31677 return 'url(#' + id + ')';
31678 }
31679
31680 /**
31681 * Check wether node type can have marker attributes.
31682 *
31683 * @param {Node} node
31684 *
31685 * @returns {boolean}
31686 */
31687 function canHaveMarker(node) {
31688 return NODES_CAN_HAVE_MARKER.indexOf(node.nodeName) !== -1;
31689 }
31690
31691 var PreviewSupportModule = {
31692 __init__: [ 'previewSupport' ],
31693 previewSupport: [ 'type', PreviewSupport ]
31694 };
31695
31696 var MARKER_OK$2 = 'drop-ok',
31697 MARKER_NOT_OK$2 = 'drop-not-ok',
31698 MARKER_ATTACH$2 = 'attach-ok',
31699 MARKER_NEW_PARENT$1 = 'new-parent';
31700
31701 var PREFIX = 'create';
31702
31703 var HIGH_PRIORITY$g = 2000;
31704
31705
31706 /**
31707 * Create new elements through drag and drop.
31708 *
31709 * @param {Canvas} canvas
31710 * @param {Dragging} dragging
31711 * @param {EventBus} eventBus
31712 * @param {Modeling} modeling
31713 * @param {Rules} rules
31714 */
31715 function Create(
31716 canvas,
31717 dragging,
31718 eventBus,
31719 modeling,
31720 rules
31721 ) {
31722
31723 // rules //////////
31724
31725 /**
31726 * Check wether elements can be created.
31727 *
31728 * @param {Array<djs.model.Base>} elements
31729 * @param {djs.model.Base} target
31730 * @param {Point} position
31731 * @param {djs.model.Base} [source]
31732 *
31733 * @returns {boolean|null|Object}
31734 */
31735 function canCreate(elements, target, position, source, hints) {
31736 if (!target) {
31737 return false;
31738 }
31739
31740 // ignore child elements and external labels
31741 elements = filter(elements, function(element) {
31742 var labelTarget = element.labelTarget;
31743
31744 return !element.parent && !(isLabel$5(element) && elements.indexOf(labelTarget) !== -1);
31745 });
31746
31747 var shape = find(elements, function(element) {
31748 return !isConnection$b(element);
31749 });
31750
31751 var attach = false,
31752 connect = false,
31753 create = false;
31754
31755 // (1) attaching single shapes
31756 if (isSingleShape(elements)) {
31757 attach = rules.allowed('shape.attach', {
31758 position: position,
31759 shape: shape,
31760 target: target
31761 });
31762 }
31763
31764 if (!attach) {
31765
31766 // (2) creating elements
31767 if (isSingleShape(elements)) {
31768 create = rules.allowed('shape.create', {
31769 position: position,
31770 shape: shape,
31771 source: source,
31772 target: target
31773 });
31774 } else {
31775 create = rules.allowed('elements.create', {
31776 elements: elements,
31777 position: position,
31778 target: target
31779 });
31780 }
31781
31782 }
31783
31784 var connectionTarget = hints.connectionTarget;
31785
31786 // (3) appending single shapes
31787 if (create || attach) {
31788 if (shape && source) {
31789 connect = rules.allowed('connection.create', {
31790 source: connectionTarget === source ? shape : source,
31791 target: connectionTarget === source ? source : shape,
31792 hints: {
31793 targetParent: target,
31794 targetAttach: attach
31795 }
31796 });
31797 }
31798
31799 return {
31800 attach: attach,
31801 connect: connect
31802 };
31803 }
31804
31805 // ignore wether or not elements can be created
31806 if (create === null || attach === null) {
31807 return null;
31808 }
31809
31810 return false;
31811 }
31812
31813 function setMarker(element, marker) {
31814 [ MARKER_ATTACH$2, MARKER_OK$2, MARKER_NOT_OK$2, MARKER_NEW_PARENT$1 ].forEach(function(m) {
31815
31816 if (m === marker) {
31817 canvas.addMarker(element, m);
31818 } else {
31819 canvas.removeMarker(element, m);
31820 }
31821 });
31822 }
31823
31824 // event handling //////////
31825
31826 eventBus.on([ 'create.move', 'create.hover' ], function(event) {
31827 var context = event.context,
31828 elements = context.elements,
31829 hover = event.hover,
31830 source = context.source,
31831 hints = context.hints || {};
31832
31833 if (!hover) {
31834 context.canExecute = false;
31835 context.target = null;
31836
31837 return;
31838 }
31839
31840 ensureConstraints$2(event);
31841
31842 var position = {
31843 x: event.x,
31844 y: event.y
31845 };
31846
31847 var canExecute = context.canExecute = hover && canCreate(elements, hover, position, source, hints);
31848
31849 if (hover && canExecute !== null) {
31850 context.target = hover;
31851
31852 if (canExecute && canExecute.attach) {
31853 setMarker(hover, MARKER_ATTACH$2);
31854 } else {
31855 setMarker(hover, canExecute ? MARKER_NEW_PARENT$1 : MARKER_NOT_OK$2);
31856 }
31857 }
31858 });
31859
31860 eventBus.on([ 'create.end', 'create.out', 'create.cleanup' ], function(event) {
31861 var hover = event.hover;
31862
31863 if (hover) {
31864 setMarker(hover, null);
31865 }
31866 });
31867
31868 eventBus.on('create.end', function(event) {
31869 var context = event.context,
31870 source = context.source,
31871 shape = context.shape,
31872 elements = context.elements,
31873 target = context.target,
31874 canExecute = context.canExecute,
31875 attach = canExecute && canExecute.attach,
31876 connect = canExecute && canExecute.connect,
31877 hints = context.hints || {};
31878
31879 if (canExecute === false || !target) {
31880 return false;
31881 }
31882
31883 ensureConstraints$2(event);
31884
31885 var position = {
31886 x: event.x,
31887 y: event.y
31888 };
31889
31890 if (connect) {
31891 shape = modeling.appendShape(source, shape, position, target, {
31892 attach: attach,
31893 connection: connect === true ? {} : connect,
31894 connectionTarget: hints.connectionTarget
31895 });
31896 } else {
31897 elements = modeling.createElements(elements, position, target, assign({}, hints, {
31898 attach: attach
31899 }));
31900
31901 // update shape
31902 shape = find(elements, function(element) {
31903 return !isConnection$b(element);
31904 });
31905 }
31906
31907 // update elements and shape
31908 assign(context, {
31909 elements: elements,
31910 shape: shape
31911 });
31912
31913 assign(event, {
31914 elements: elements,
31915 shape: shape
31916 });
31917 });
31918
31919 function cancel() {
31920 var context = dragging.context();
31921
31922 if (context && context.prefix === PREFIX) {
31923 dragging.cancel();
31924 }
31925 }
31926
31927 // cancel on <elements.changed> that is not result of <drag.end>
31928 eventBus.on('create.init', function() {
31929 eventBus.on('elements.changed', cancel);
31930
31931 eventBus.once([ 'create.cancel', 'create.end' ], HIGH_PRIORITY$g, function() {
31932 eventBus.off('elements.changed', cancel);
31933 });
31934 });
31935
31936 // API //////////
31937
31938 this.start = function(event, elements, context) {
31939 if (!isArray$2(elements)) {
31940 elements = [ elements ];
31941 }
31942
31943 var shape = find(elements, function(element) {
31944 return !isConnection$b(element);
31945 });
31946
31947 if (!shape) {
31948
31949 // at least one shape is required
31950 return;
31951 }
31952
31953 context = assign({
31954 elements: elements,
31955 hints: {},
31956 shape: shape
31957 }, context || {});
31958
31959 // make sure each element has x and y
31960 forEach(elements, function(element) {
31961 if (!isNumber(element.x)) {
31962 element.x = 0;
31963 }
31964
31965 if (!isNumber(element.y)) {
31966 element.y = 0;
31967 }
31968 });
31969
31970 var bbox = getBBox(elements);
31971
31972 // center elements around cursor
31973 forEach(elements, function(element) {
31974 if (isConnection$b(element)) {
31975 element.waypoints = map$1(element.waypoints, function(waypoint) {
31976 return {
31977 x: waypoint.x - bbox.x - bbox.width / 2,
31978 y: waypoint.y - bbox.y - bbox.height / 2
31979 };
31980 });
31981 }
31982
31983 assign(element, {
31984 x: element.x - bbox.x - bbox.width / 2,
31985 y: element.y - bbox.y - bbox.height / 2
31986 });
31987 });
31988
31989 dragging.init(event, PREFIX, {
31990 cursor: 'grabbing',
31991 autoActivate: true,
31992 data: {
31993 shape: shape,
31994 elements: elements,
31995 context: context
31996 }
31997 });
31998 };
31999 }
32000
32001 Create.$inject = [
32002 'canvas',
32003 'dragging',
32004 'eventBus',
32005 'modeling',
32006 'rules'
32007 ];
32008
32009 // helpers //////////
32010
32011 function ensureConstraints$2(event) {
32012 var context = event.context,
32013 createConstraints = context.createConstraints;
32014
32015 if (!createConstraints) {
32016 return;
32017 }
32018
32019 if (createConstraints.left) {
32020 event.x = Math.max(event.x, createConstraints.left);
32021 }
32022
32023 if (createConstraints.right) {
32024 event.x = Math.min(event.x, createConstraints.right);
32025 }
32026
32027 if (createConstraints.top) {
32028 event.y = Math.max(event.y, createConstraints.top);
32029 }
32030
32031 if (createConstraints.bottom) {
32032 event.y = Math.min(event.y, createConstraints.bottom);
32033 }
32034 }
32035
32036 function isConnection$b(element) {
32037 return !!element.waypoints;
32038 }
32039
32040 function isSingleShape(elements) {
32041 return elements && elements.length === 1 && !isConnection$b(elements[0]);
32042 }
32043
32044 function isLabel$5(element) {
32045 return !!element.labelTarget;
32046 }
32047
32048 var LOW_PRIORITY$g = 750;
32049
32050
32051 function CreatePreview(
32052 canvas,
32053 eventBus,
32054 graphicsFactory,
32055 previewSupport,
32056 styles
32057 ) {
32058 function createDragGroup(elements) {
32059 var dragGroup = create$1('g');
32060
32061 attr(dragGroup, styles.cls('djs-drag-group', [ 'no-events' ]));
32062
32063 var childrenGfx = create$1('g');
32064
32065 elements.forEach(function(element) {
32066
32067 // create graphics
32068 var gfx;
32069
32070 if (element.hidden) {
32071 return;
32072 }
32073
32074 if (element.waypoints) {
32075 gfx = graphicsFactory._createContainer('connection', childrenGfx);
32076
32077 graphicsFactory.drawConnection(getVisual(gfx), element);
32078 } else {
32079 gfx = graphicsFactory._createContainer('shape', childrenGfx);
32080
32081 graphicsFactory.drawShape(getVisual(gfx), element);
32082
32083 translate$2(gfx, element.x, element.y);
32084 }
32085
32086 // add preview
32087 previewSupport.addDragger(element, dragGroup, gfx);
32088 });
32089
32090 return dragGroup;
32091 }
32092
32093 eventBus.on('create.move', LOW_PRIORITY$g, function(event) {
32094
32095 var hover = event.hover,
32096 context = event.context,
32097 elements = context.elements,
32098 dragGroup = context.dragGroup;
32099
32100 // lazily create previews
32101 if (!dragGroup) {
32102 dragGroup = context.dragGroup = createDragGroup(elements);
32103 }
32104
32105 var activeLayer;
32106
32107 if (hover) {
32108 if (!dragGroup.parentNode) {
32109 activeLayer = canvas.getActiveLayer();
32110
32111 append(activeLayer, dragGroup);
32112 }
32113
32114 translate$2(dragGroup, event.x, event.y);
32115 } else {
32116 remove$1(dragGroup);
32117 }
32118 });
32119
32120 eventBus.on('create.cleanup', function(event) {
32121 var context = event.context,
32122 dragGroup = context.dragGroup;
32123
32124 if (dragGroup) {
32125 remove$1(dragGroup);
32126 }
32127 });
32128 }
32129
32130 CreatePreview.$inject = [
32131 'canvas',
32132 'eventBus',
32133 'graphicsFactory',
32134 'previewSupport',
32135 'styles'
32136 ];
32137
32138 var CreateModule = {
32139 __depends__: [
32140 DraggingModule,
32141 PreviewSupportModule,
32142 RulesModule$1,
32143 SelectionModule
32144 ],
32145 __init__: [
32146 'create',
32147 'createPreview'
32148 ],
32149 create: [ 'type', Create ],
32150 createPreview: [ 'type', CreatePreview ]
32151 };
32152
32153 var DATA_REF = 'data-id';
32154
32155 var CLOSE_EVENTS = [
32156 'contextPad.close',
32157 'canvas.viewbox.changing',
32158 'commandStack.changed'
32159 ];
32160
32161 var DEFAULT_PRIORITY$1 = 1000;
32162
32163
32164 /**
32165 * A popup menu that can be used to display a list of actions anywhere in the canvas.
32166 *
32167 * @param {Object} config
32168 * @param {boolean|Object} [config.scale={ min: 1.0, max: 1.5 }]
32169 * @param {number} [config.scale.min]
32170 * @param {number} [config.scale.max]
32171 * @param {EventBus} eventBus
32172 * @param {Canvas} canvas
32173 *
32174 * @class
32175 * @constructor
32176 */
32177 function PopupMenu(config, eventBus, canvas) {
32178
32179 var scale = isDefined(config && config.scale) ? config.scale : {
32180 min: 1,
32181 max: 1.5
32182 };
32183
32184 this._config = {
32185 scale: scale
32186 };
32187
32188 this._eventBus = eventBus;
32189 this._canvas = canvas;
32190 this._providers = {};
32191 this._current = {};
32192 }
32193
32194 PopupMenu.$inject = [
32195 'config.popupMenu',
32196 'eventBus',
32197 'canvas'
32198 ];
32199
32200 /**
32201 * Registers a popup menu provider
32202 *
32203 * @param {string} id
32204 * @param {number} [priority=1000]
32205 * @param {Object} provider
32206 *
32207 * @example
32208 * const popupMenuProvider = {
32209 * getPopupMenuEntries: function(element) {
32210 * return {
32211 * 'entry-1': {
32212 * label: 'My Entry',
32213 * action: function() { alert("I have been clicked!"); }
32214 * }
32215 * }
32216 * }
32217 * };
32218 *
32219 * popupMenu.registerProvider('myMenuID', popupMenuProvider);
32220 */
32221 PopupMenu.prototype.registerProvider = function(id, priority, provider) {
32222 if (!provider) {
32223 provider = priority;
32224 priority = DEFAULT_PRIORITY$1;
32225 }
32226
32227 this._eventBus.on('popupMenu.getProviders.' + id, priority, function(event) {
32228 event.providers.push(provider);
32229 });
32230 };
32231
32232 /**
32233 * Determine if the popup menu has entries.
32234 *
32235 * @return {boolean} true if empty
32236 */
32237 PopupMenu.prototype.isEmpty = function(element, providerId) {
32238 if (!element) {
32239 throw new Error('element parameter is missing');
32240 }
32241
32242 if (!providerId) {
32243 throw new Error('providerId parameter is missing');
32244 }
32245
32246 var providers = this._getProviders(providerId);
32247
32248 if (!providers) {
32249 return true;
32250 }
32251
32252 var entries = this._getEntries(element, providers),
32253 headerEntries = this._getHeaderEntries(element, providers);
32254
32255 var hasEntries = size(entries) > 0,
32256 hasHeaderEntries = headerEntries && size(headerEntries) > 0;
32257
32258 return !hasEntries && !hasHeaderEntries;
32259 };
32260
32261
32262 /**
32263 * Create entries and open popup menu at given position
32264 *
32265 * @param {Object} element
32266 * @param {string} id provider id
32267 * @param {Object} position
32268 *
32269 * @return {Object} popup menu instance
32270 */
32271 PopupMenu.prototype.open = function(element, id, position) {
32272
32273 var providers = this._getProviders(id);
32274
32275 if (!element) {
32276 throw new Error('Element is missing');
32277 }
32278
32279 if (!providers || !providers.length) {
32280 throw new Error('No registered providers for: ' + id);
32281 }
32282
32283 if (!position) {
32284 throw new Error('the position argument is missing');
32285 }
32286
32287 if (this.isOpen()) {
32288 this.close();
32289 }
32290
32291 this._emit('open');
32292
32293 var current = this._current = {
32294 className: id,
32295 element: element,
32296 position: position
32297 };
32298
32299 var entries = this._getEntries(element, providers),
32300 headerEntries = this._getHeaderEntries(element, providers);
32301
32302 current.entries = assign({}, entries, headerEntries);
32303
32304 current.container = this._createContainer();
32305
32306 if (size(headerEntries)) {
32307 current.container.appendChild(
32308 this._createEntries(headerEntries, 'djs-popup-header')
32309 );
32310 }
32311
32312 if (size(entries)) {
32313 current.container.appendChild(
32314 this._createEntries(entries, 'djs-popup-body')
32315 );
32316 }
32317
32318 var canvas = this._canvas,
32319 parent = canvas.getContainer();
32320
32321 this._attachContainer(current.container, parent, position.cursor);
32322 this._bindAutoClose();
32323 };
32324
32325
32326 /**
32327 * Removes the popup menu and unbinds the event handlers.
32328 */
32329 PopupMenu.prototype.close = function() {
32330
32331 if (!this.isOpen()) {
32332 return;
32333 }
32334
32335 this._emit('close');
32336
32337 this._unbindAutoClose();
32338 remove$2(this._current.container);
32339 this._current.container = null;
32340 };
32341
32342
32343 /**
32344 * Determine if an open popup menu exist.
32345 *
32346 * @return {boolean} true if open
32347 */
32348 PopupMenu.prototype.isOpen = function() {
32349 return !!this._current.container;
32350 };
32351
32352
32353 /**
32354 * Trigger an action associated with an entry.
32355 *
32356 * @param {Object} event
32357 *
32358 * @return the result of the action callback, if any
32359 */
32360 PopupMenu.prototype.trigger = function(event) {
32361
32362 // silence other actions
32363 event.preventDefault();
32364
32365 var element = event.delegateTarget || event.target,
32366 entryId = attr$1(element, DATA_REF);
32367
32368 var entry = this._getEntry(entryId);
32369
32370 if (entry.action) {
32371 return entry.action.call(null, event, entry);
32372 }
32373 };
32374
32375 PopupMenu.prototype._getProviders = function(id) {
32376
32377 var event = this._eventBus.createEvent({
32378 type: 'popupMenu.getProviders.' + id,
32379 providers: []
32380 });
32381
32382 this._eventBus.fire(event);
32383
32384 return event.providers;
32385 };
32386
32387 PopupMenu.prototype._getEntries = function(element, providers) {
32388
32389 var entries = {};
32390
32391 forEach(providers, function(provider) {
32392
32393 // handle legacy method
32394 if (!provider.getPopupMenuEntries) {
32395 forEach(provider.getEntries(element), function(entry) {
32396 var id = entry.id;
32397
32398 if (!id) {
32399 throw new Error('every entry must have the id property set');
32400 }
32401
32402 entries[id] = omit(entry, [ 'id' ]);
32403 });
32404
32405 return;
32406 }
32407
32408 var entriesOrUpdater = provider.getPopupMenuEntries(element);
32409
32410 if (isFunction(entriesOrUpdater)) {
32411 entries = entriesOrUpdater(entries);
32412 } else {
32413 forEach(entriesOrUpdater, function(entry, id) {
32414 entries[id] = entry;
32415 });
32416 }
32417 });
32418
32419 return entries;
32420 };
32421
32422 PopupMenu.prototype._getHeaderEntries = function(element, providers) {
32423
32424 var entries = {};
32425
32426 forEach(providers, function(provider) {
32427
32428 // handle legacy method
32429 if (!provider.getPopupMenuHeaderEntries) {
32430 if (!provider.getHeaderEntries) {
32431 return;
32432 }
32433
32434 forEach(provider.getHeaderEntries(element), function(entry) {
32435 var id = entry.id;
32436
32437 if (!id) {
32438 throw new Error('every entry must have the id property set');
32439 }
32440
32441 entries[id] = omit(entry, [ 'id' ]);
32442 });
32443
32444 return;
32445 }
32446
32447 var entriesOrUpdater = provider.getPopupMenuHeaderEntries(element);
32448
32449 if (isFunction(entriesOrUpdater)) {
32450 entries = entriesOrUpdater(entries);
32451 } else {
32452 forEach(entriesOrUpdater, function(entry, id) {
32453 entries[id] = entry;
32454 });
32455 }
32456 });
32457
32458 return entries;
32459
32460
32461 };
32462
32463 /**
32464 * Gets an entry instance (either entry or headerEntry) by id.
32465 *
32466 * @param {string} entryId
32467 *
32468 * @return {Object} entry instance
32469 */
32470 PopupMenu.prototype._getEntry = function(entryId) {
32471
32472 var entry = this._current.entries[entryId];
32473
32474 if (!entry) {
32475 throw new Error('entry not found');
32476 }
32477
32478 return entry;
32479 };
32480
32481 PopupMenu.prototype._emit = function(eventName) {
32482 this._eventBus.fire('popupMenu.' + eventName);
32483 };
32484
32485 /**
32486 * Creates the popup menu container.
32487 *
32488 * @return {Object} a DOM container
32489 */
32490 PopupMenu.prototype._createContainer = function() {
32491 var container = domify('<div class="djs-popup">'),
32492 position = this._current.position,
32493 className = this._current.className;
32494
32495 assign(container.style, {
32496 position: 'absolute',
32497 left: position.x + 'px',
32498 top: position.y + 'px',
32499 visibility: 'hidden'
32500 });
32501
32502 classes$1(container).add(className);
32503
32504 return container;
32505 };
32506
32507
32508 /**
32509 * Attaches the container to the DOM.
32510 *
32511 * @param {Object} container
32512 * @param {Object} parent
32513 */
32514 PopupMenu.prototype._attachContainer = function(container, parent, cursor) {
32515 var self = this;
32516
32517 // Event handler
32518 delegate.bind(container, '.entry' ,'click', function(event) {
32519 self.trigger(event);
32520 });
32521
32522 this._updateScale(container);
32523
32524 // Attach to DOM
32525 parent.appendChild(container);
32526
32527 if (cursor) {
32528 this._assureIsInbounds(container, cursor);
32529 }
32530 };
32531
32532
32533 /**
32534 * Updates popup style.transform with respect to the config and zoom level.
32535 *
32536 * @method _updateScale
32537 *
32538 * @param {Object} container
32539 */
32540 PopupMenu.prototype._updateScale = function(container) {
32541 var zoom = this._canvas.zoom();
32542
32543 var scaleConfig = this._config.scale,
32544 minScale,
32545 maxScale,
32546 scale = zoom;
32547
32548 if (scaleConfig !== true) {
32549
32550 if (scaleConfig === false) {
32551 minScale = 1;
32552 maxScale = 1;
32553 } else {
32554 minScale = scaleConfig.min;
32555 maxScale = scaleConfig.max;
32556 }
32557
32558 if (isDefined(minScale) && zoom < minScale) {
32559 scale = minScale;
32560 }
32561
32562 if (isDefined(maxScale) && zoom > maxScale) {
32563 scale = maxScale;
32564 }
32565
32566 }
32567
32568 setTransform(container, 'scale(' + scale + ')');
32569 };
32570
32571
32572 /**
32573 * Make sure that the menu is always fully shown
32574 *
32575 * @method function
32576 *
32577 * @param {Object} container
32578 * @param {Position} cursor {x, y}
32579 */
32580 PopupMenu.prototype._assureIsInbounds = function(container, cursor) {
32581 var canvas = this._canvas,
32582 clientRect = canvas._container.getBoundingClientRect();
32583
32584 var containerX = container.offsetLeft,
32585 containerY = container.offsetTop,
32586 containerWidth = container.scrollWidth,
32587 containerHeight = container.scrollHeight,
32588 overAxis = {},
32589 left, top;
32590
32591 var cursorPosition = {
32592 x: cursor.x - clientRect.left,
32593 y: cursor.y - clientRect.top
32594 };
32595
32596 if (containerX + containerWidth > clientRect.width) {
32597 overAxis.x = true;
32598 }
32599
32600 if (containerY + containerHeight > clientRect.height) {
32601 overAxis.y = true;
32602 }
32603
32604 if (overAxis.x && overAxis.y) {
32605 left = cursorPosition.x - containerWidth + 'px';
32606 top = cursorPosition.y - containerHeight + 'px';
32607 } else if (overAxis.x) {
32608 left = cursorPosition.x - containerWidth + 'px';
32609 top = cursorPosition.y + 'px';
32610 } else if (overAxis.y && cursorPosition.y < containerHeight) {
32611 left = cursorPosition.x + 'px';
32612 top = 10 + 'px';
32613 } else if (overAxis.y) {
32614 left = cursorPosition.x + 'px';
32615 top = cursorPosition.y - containerHeight + 'px';
32616 }
32617
32618 assign(container.style, { left: left, top: top }, { visibility: 'visible', 'z-index': 1000 });
32619 };
32620
32621
32622 /**
32623 * Creates a list of entries and returns them as a DOM container.
32624 *
32625 * @param {Array<Object>} entries an array of entry objects
32626 * @param {string} className the class name of the entry container
32627 *
32628 * @return {Object} a DOM container
32629 */
32630 PopupMenu.prototype._createEntries = function(entries, className) {
32631
32632 var entriesContainer = domify('<div>'),
32633 self = this;
32634
32635 classes$1(entriesContainer).add(className);
32636
32637 forEach(entries, function(entry, id) {
32638 var entryContainer = self._createEntry(entry, id);
32639 entriesContainer.appendChild(entryContainer);
32640 });
32641
32642 return entriesContainer;
32643 };
32644
32645
32646 /**
32647 * Creates a single entry and returns it as a DOM container.
32648 *
32649 * @param {Object} entry
32650 *
32651 * @return {Object} a DOM container
32652 */
32653 PopupMenu.prototype._createEntry = function(entry, id) {
32654
32655 var entryContainer = domify('<div>'),
32656 entryClasses = classes$1(entryContainer);
32657
32658 entryClasses.add('entry');
32659
32660 if (entry.className) {
32661 entry.className.split(' ').forEach(function(className) {
32662 entryClasses.add(className);
32663 });
32664 }
32665
32666 attr$1(entryContainer, DATA_REF, id);
32667
32668 if (entry.label) {
32669 var label = domify('<span>');
32670 label.textContent = entry.label;
32671 entryContainer.appendChild(label);
32672 }
32673
32674 if (entry.imageUrl) {
32675 entryContainer.appendChild(domify('<img src="' + entry.imageUrl + '" />'));
32676 }
32677
32678 if (entry.active === true) {
32679 entryClasses.add('active');
32680 }
32681
32682 if (entry.disabled === true) {
32683 entryClasses.add('disabled');
32684 }
32685
32686 if (entry.title) {
32687 entryContainer.title = entry.title;
32688 }
32689
32690 return entryContainer;
32691 };
32692
32693
32694 /**
32695 * Set up listener to close popup automatically on certain events.
32696 */
32697 PopupMenu.prototype._bindAutoClose = function() {
32698 this._eventBus.once(CLOSE_EVENTS, this.close, this);
32699 };
32700
32701
32702 /**
32703 * Remove the auto-closing listener.
32704 */
32705 PopupMenu.prototype._unbindAutoClose = function() {
32706 this._eventBus.off(CLOSE_EVENTS, this.close, this);
32707 };
32708
32709
32710
32711 // helpers /////////////////////////////
32712
32713 function setTransform(element, transform) {
32714 element.style['transform-origin'] = 'top left';
32715
32716 [ '', '-ms-', '-webkit-' ].forEach(function(prefix) {
32717 element.style[prefix + 'transform'] = transform;
32718 });
32719 }
32720
32721 var PopupMenuModule$1 = {
32722 __init__: [ 'popupMenu' ],
32723 popupMenu: [ 'type', PopupMenu ]
32724 };
32725
32726 /**
32727 * A clip board stub
32728 */
32729 function Clipboard() {}
32730
32731
32732 Clipboard.prototype.get = function() {
32733 return this._data;
32734 };
32735
32736 Clipboard.prototype.set = function(data) {
32737 this._data = data;
32738 };
32739
32740 Clipboard.prototype.clear = function() {
32741 var data = this._data;
32742
32743 delete this._data;
32744
32745 return data;
32746 };
32747
32748 Clipboard.prototype.isEmpty = function() {
32749 return !this._data;
32750 };
32751
32752 var ClipboardModule = {
32753 clipboard: [ 'type', Clipboard ]
32754 };
32755
32756 function Mouse(eventBus) {
32757 var self = this;
32758
32759 this._lastMoveEvent = null;
32760
32761 function setLastMoveEvent(mousemoveEvent) {
32762 self._lastMoveEvent = mousemoveEvent;
32763 }
32764
32765 eventBus.on('canvas.init', function(context) {
32766 var svg = self._svg = context.svg;
32767
32768 svg.addEventListener('mousemove', setLastMoveEvent);
32769 });
32770
32771 eventBus.on('canvas.destroy', function() {
32772 self._lastMouseEvent = null;
32773
32774 self._svg.removeEventListener('mousemove', setLastMoveEvent);
32775 });
32776 }
32777
32778 Mouse.$inject = [ 'eventBus' ];
32779
32780 Mouse.prototype.getLastMoveEvent = function() {
32781 return this._lastMoveEvent || createMoveEvent(0, 0);
32782 };
32783
32784 // helpers //////////
32785
32786 function createMoveEvent(x, y) {
32787 var event = document.createEvent('MouseEvent');
32788
32789 var screenX = x,
32790 screenY = y,
32791 clientX = x,
32792 clientY = y;
32793
32794 if (event.initMouseEvent) {
32795 event.initMouseEvent(
32796 'mousemove',
32797 true,
32798 true,
32799 window,
32800 0,
32801 screenX,
32802 screenY,
32803 clientX,
32804 clientY,
32805 false,
32806 false,
32807 false,
32808 false,
32809 0,
32810 null
32811 );
32812 }
32813
32814 return event;
32815 }
32816
32817 var MouseModule = {
32818 __init__: [ 'mouse' ],
32819 mouse: [ 'type', Mouse ]
32820 };
32821
32822 /**
32823 * @typedef {Function} <copyPaste.canCopyElements> listener
32824 *
32825 * @param {Object} context
32826 * @param {Array<djs.model.Base>} context.elements
32827 *
32828 * @returns {Array<djs.model.Base>|boolean} - Return elements to be copied or false to disallow
32829 * copying.
32830 */
32831
32832 /**
32833 * @typedef {Function} <copyPaste.copyElement> listener
32834 *
32835 * @param {Object} context
32836 * @param {Object} context.descriptor
32837 * @param {djs.model.Base} context.element
32838 * @param {Array<djs.model.Base>} context.elements
32839 */
32840
32841 /**
32842 * @typedef {Function} <copyPaste.elementsCopied> listener
32843 *
32844 * @param {Object} context
32845 * @param {Object} context.elements
32846 * @param {Object} context.tree
32847 */
32848
32849 /**
32850 * @typedef {Function} <copyPaste.pasteElement> listener
32851 *
32852 * @param {Object} context
32853 * @param {Object} context.cache - Already created elements.
32854 * @param {Object} context.descriptor
32855 */
32856
32857 /**
32858 * @typedef {Function} <copyPaste.pasteElements> listener
32859 *
32860 * @param {Object} context
32861 * @param {Object} context.hints - Add hints before pasting.
32862 */
32863
32864 /**
32865 * Copy and paste elements.
32866 *
32867 * @param {Canvas} canvas
32868 * @param {Create} create
32869 * @param {Clipboard} clipboard
32870 * @param {ElementFactory} elementFactory
32871 * @param {EventBus} eventBus
32872 * @param {Modeling} modeling
32873 * @param {Mouse} mouse
32874 * @param {Rules} rules
32875 */
32876 function CopyPaste(
32877 canvas,
32878 create,
32879 clipboard,
32880 elementFactory,
32881 eventBus,
32882 modeling,
32883 mouse,
32884 rules
32885 ) {
32886
32887 this._canvas = canvas;
32888 this._create = create;
32889 this._clipboard = clipboard;
32890 this._elementFactory = elementFactory;
32891 this._eventBus = eventBus;
32892 this._modeling = modeling;
32893 this._mouse = mouse;
32894 this._rules = rules;
32895
32896 eventBus.on('copyPaste.copyElement', function(context) {
32897 var descriptor = context.descriptor,
32898 element = context.element,
32899 elements = context.elements;
32900
32901 // default priority (priority = 1)
32902 descriptor.priority = 1;
32903
32904 descriptor.id = element.id;
32905
32906 var parentCopied = find(elements, function(e) {
32907 return e === element.parent;
32908 });
32909
32910 // do NOT reference parent if parent wasn't copied
32911 if (parentCopied) {
32912 descriptor.parent = element.parent.id;
32913 }
32914
32915 // attachers (priority = 2)
32916 if (isAttacher$1(element)) {
32917 descriptor.priority = 2;
32918
32919 descriptor.host = element.host.id;
32920 }
32921
32922 // connections (priority = 3)
32923 if (isConnection$a(element)) {
32924 descriptor.priority = 3;
32925
32926 descriptor.source = element.source.id;
32927 descriptor.target = element.target.id;
32928
32929 descriptor.waypoints = copyWaypoints$1(element);
32930 }
32931
32932 // labels (priority = 4)
32933 if (isLabel$4(element)) {
32934 descriptor.priority = 4;
32935
32936 descriptor.labelTarget = element.labelTarget.id;
32937 }
32938
32939 forEach([ 'x', 'y', 'width', 'height' ], function(property) {
32940 if (isNumber(element[ property ])) {
32941 descriptor[ property ] = element[ property ];
32942 }
32943 });
32944
32945 descriptor.hidden = element.hidden;
32946 descriptor.collapsed = element.collapsed;
32947
32948 });
32949
32950 eventBus.on('copyPaste.pasteElements', function(context) {
32951 var hints = context.hints;
32952
32953 assign(hints, {
32954 createElementsBehavior: false
32955 });
32956 });
32957 }
32958
32959 CopyPaste.$inject = [
32960 'canvas',
32961 'create',
32962 'clipboard',
32963 'elementFactory',
32964 'eventBus',
32965 'modeling',
32966 'mouse',
32967 'rules'
32968 ];
32969
32970
32971 /**
32972 * Copy elements.
32973 *
32974 * @param {Array<djs.model.Base>} elements
32975 *
32976 * @returns {Object}
32977 */
32978 CopyPaste.prototype.copy = function(elements) {
32979 var allowed,
32980 tree;
32981
32982 if (!isArray$2(elements)) {
32983 elements = elements ? [ elements ] : [];
32984 }
32985
32986 allowed = this._eventBus.fire('copyPaste.canCopyElements', {
32987 elements: elements
32988 });
32989
32990 if (allowed === false) {
32991 tree = {};
32992 } else {
32993 tree = this.createTree(isArray$2(allowed) ? allowed : elements);
32994 }
32995
32996 // we set an empty tree, selection of elements
32997 // to copy was empty.
32998 this._clipboard.set(tree);
32999
33000 this._eventBus.fire('copyPaste.elementsCopied', {
33001 elements: elements,
33002 tree: tree
33003 });
33004
33005 return tree;
33006 };
33007
33008 /**
33009 * Paste elements.
33010 *
33011 * @param {Object} [context]
33012 * @param {djs.model.base} [context.element] - Parent.
33013 * @param {Point} [context.point] - Position.
33014 * @param {Object} [context.hints] - Hints.
33015 */
33016 CopyPaste.prototype.paste = function(context) {
33017 var tree = this._clipboard.get();
33018
33019 if (this._clipboard.isEmpty()) {
33020 return;
33021 }
33022
33023 var hints = context && context.hints || {};
33024
33025 this._eventBus.fire('copyPaste.pasteElements', {
33026 hints: hints
33027 });
33028
33029 var elements = this._createElements(tree);
33030
33031 // paste directly
33032 if (context && context.element && context.point) {
33033 return this._paste(elements, context.element, context.point, hints);
33034 }
33035
33036 this._create.start(this._mouse.getLastMoveEvent(), elements, {
33037 hints: hints || {}
33038 });
33039 };
33040
33041 /**
33042 * Paste elements directly.
33043 *
33044 * @param {Array<djs.model.Base>} elements
33045 * @param {djs.model.base} target
33046 * @param {Point} position
33047 * @param {Object} [hints]
33048 */
33049 CopyPaste.prototype._paste = function(elements, target, position, hints) {
33050
33051 // make sure each element has x and y
33052 forEach(elements, function(element) {
33053 if (!isNumber(element.x)) {
33054 element.x = 0;
33055 }
33056
33057 if (!isNumber(element.y)) {
33058 element.y = 0;
33059 }
33060 });
33061
33062 var bbox = getBBox(elements);
33063
33064 // center elements around cursor
33065 forEach(elements, function(element) {
33066 if (isConnection$a(element)) {
33067 element.waypoints = map$1(element.waypoints, function(waypoint) {
33068 return {
33069 x: waypoint.x - bbox.x - bbox.width / 2,
33070 y: waypoint.y - bbox.y - bbox.height / 2
33071 };
33072 });
33073 }
33074
33075 assign(element, {
33076 x: element.x - bbox.x - bbox.width / 2,
33077 y: element.y - bbox.y - bbox.height / 2
33078 });
33079 });
33080
33081 return this._modeling.createElements(elements, position, target, assign({}, hints));
33082 };
33083
33084 /**
33085 * Create elements from tree.
33086 */
33087 CopyPaste.prototype._createElements = function(tree) {
33088 var self = this;
33089
33090 var eventBus = this._eventBus;
33091
33092 var cache = {};
33093
33094 var elements = [];
33095
33096 forEach(tree, function(branch, depth) {
33097
33098 // sort by priority
33099 branch = sortBy(branch, 'priority');
33100
33101 forEach(branch, function(descriptor) {
33102
33103 // remove priority
33104 var attrs = assign({}, omit(descriptor, [ 'priority' ]));
33105
33106 if (cache[ descriptor.parent ]) {
33107 attrs.parent = cache[ descriptor.parent ];
33108 } else {
33109 delete attrs.parent;
33110 }
33111
33112 eventBus.fire('copyPaste.pasteElement', {
33113 cache: cache,
33114 descriptor: attrs
33115 });
33116
33117 var element;
33118
33119 if (isConnection$a(attrs)) {
33120 attrs.source = cache[ descriptor.source ];
33121 attrs.target = cache[ descriptor.target ];
33122
33123 element = cache[ descriptor.id ] = self.createConnection(attrs);
33124
33125 elements.push(element);
33126
33127 return;
33128 }
33129
33130 if (isLabel$4(attrs)) {
33131 attrs.labelTarget = cache[ attrs.labelTarget ];
33132
33133 element = cache[ descriptor.id ] = self.createLabel(attrs);
33134
33135 elements.push(element);
33136
33137 return;
33138 }
33139
33140 if (attrs.host) {
33141 attrs.host = cache[ attrs.host ];
33142 }
33143
33144 element = cache[ descriptor.id ] = self.createShape(attrs);
33145
33146 elements.push(element);
33147 });
33148
33149 });
33150
33151 return elements;
33152 };
33153
33154 CopyPaste.prototype.createConnection = function(attrs) {
33155 var connection = this._elementFactory.createConnection(omit(attrs, [ 'id' ]));
33156
33157 return connection;
33158 };
33159
33160 CopyPaste.prototype.createLabel = function(attrs) {
33161 var label = this._elementFactory.createLabel(omit(attrs, [ 'id' ]));
33162
33163 return label;
33164 };
33165
33166 CopyPaste.prototype.createShape = function(attrs) {
33167 var shape = this._elementFactory.createShape(omit(attrs, [ 'id' ]));
33168
33169 return shape;
33170 };
33171
33172 /**
33173 * Check wether element has relations to other elements e.g. attachers, labels and connections.
33174 *
33175 * @param {Object} element
33176 * @param {Array<djs.model.Base>} elements
33177 *
33178 * @returns {boolean}
33179 */
33180 CopyPaste.prototype.hasRelations = function(element, elements) {
33181 var labelTarget,
33182 source,
33183 target;
33184
33185 if (isConnection$a(element)) {
33186 source = find(elements, matchPattern({ id: element.source.id }));
33187 target = find(elements, matchPattern({ id: element.target.id }));
33188
33189 if (!source || !target) {
33190 return false;
33191 }
33192 }
33193
33194 if (isLabel$4(element)) {
33195 labelTarget = find(elements, matchPattern({ id: element.labelTarget.id }));
33196
33197 if (!labelTarget) {
33198 return false;
33199 }
33200 }
33201
33202 return true;
33203 };
33204
33205 /**
33206 * Create a tree-like structure from elements.
33207 *
33208 * @example
33209 * tree: {
33210 * 0: [
33211 * { id: 'Shape_1', priority: 1, ... },
33212 * { id: 'Shape_2', priority: 1, ... },
33213 * { id: 'Connection_1', source: 'Shape_1', target: 'Shape_2', priority: 3, ... },
33214 * ...
33215 * ],
33216 * 1: [
33217 * { id: 'Shape_3', parent: 'Shape1', priority: 1, ... },
33218 * ...
33219 * ]
33220 * };
33221 *
33222 * @param {Array<djs.model.base>} elements
33223 *
33224 * @return {Object}
33225 */
33226 CopyPaste.prototype.createTree = function(elements) {
33227 var rules = this._rules,
33228 self = this;
33229
33230 var tree = {},
33231 elementsData = [];
33232
33233 var parents = getParents$1(elements);
33234
33235 function canCopy(element, elements) {
33236 return rules.allowed('element.copy', {
33237 element: element,
33238 elements: elements
33239 });
33240 }
33241
33242 function addElementData(element, depth) {
33243
33244 // (1) check wether element has already been added
33245 var foundElementData = find(elementsData, function(elementsData) {
33246 return element === elementsData.element;
33247 });
33248
33249 // (2) add element if not already added
33250 if (!foundElementData) {
33251 elementsData.push({
33252 element: element,
33253 depth: depth
33254 });
33255
33256 return;
33257 }
33258
33259 // (3) update depth
33260 if (foundElementData.depth < depth) {
33261 elementsData = removeElementData(foundElementData, elementsData);
33262
33263 elementsData.push({
33264 element: foundElementData.element,
33265 depth: depth
33266 });
33267 }
33268 }
33269
33270 function removeElementData(elementData, elementsData) {
33271 var index = elementsData.indexOf(elementData);
33272
33273 if (index !== -1) {
33274 elementsData.splice(index, 1);
33275 }
33276
33277 return elementsData;
33278 }
33279
33280 // (1) add elements
33281 eachElement(parents, function(element, _index, depth) {
33282
33283 // do NOT add external labels directly
33284 if (isLabel$4(element)) {
33285 return;
33286 }
33287
33288 // always copy external labels
33289 forEach(element.labels, function(label) {
33290 addElementData(label, depth);
33291 });
33292
33293 function addRelatedElements(elements) {
33294 elements && elements.length && forEach(elements, function(element) {
33295
33296 // add external labels
33297 forEach(element.labels, function(label) {
33298 addElementData(label, depth);
33299 });
33300
33301 addElementData(element, depth);
33302 });
33303 }
33304
33305 forEach([ element.attachers, element.incoming, element.outgoing ], addRelatedElements);
33306
33307 addElementData(element, depth);
33308
33309 return element.children;
33310 });
33311
33312 elements = map$1(elementsData, function(elementData) {
33313 return elementData.element;
33314 });
33315
33316 // (2) copy elements
33317 elementsData = map$1(elementsData, function(elementData) {
33318 elementData.descriptor = {};
33319
33320 self._eventBus.fire('copyPaste.copyElement', {
33321 descriptor: elementData.descriptor,
33322 element: elementData.element,
33323 elements: elements
33324 });
33325
33326 return elementData;
33327 });
33328
33329 // (3) sort elements by priority
33330 elementsData = sortBy(elementsData, function(elementData) {
33331 return elementData.descriptor.priority;
33332 });
33333
33334 elements = map$1(elementsData, function(elementData) {
33335 return elementData.element;
33336 });
33337
33338 // (4) create tree
33339 forEach(elementsData, function(elementData) {
33340 var depth = elementData.depth;
33341
33342 if (!self.hasRelations(elementData.element, elements)) {
33343 removeElement(elementData.element, elements);
33344
33345 return;
33346 }
33347
33348 if (!canCopy(elementData.element, elements)) {
33349 removeElement(elementData.element, elements);
33350
33351 return;
33352 }
33353
33354 if (!tree[depth]) {
33355 tree[depth] = [];
33356 }
33357
33358 tree[depth].push(elementData.descriptor);
33359 });
33360
33361 return tree;
33362 };
33363
33364 // helpers //////////
33365
33366 function isAttacher$1(element) {
33367 return !!element.host;
33368 }
33369
33370 function isConnection$a(element) {
33371 return !!element.waypoints;
33372 }
33373
33374 function isLabel$4(element) {
33375 return !!element.labelTarget;
33376 }
33377
33378 function copyWaypoints$1(element) {
33379 return map$1(element.waypoints, function(waypoint) {
33380
33381 waypoint = copyWaypoint$1(waypoint);
33382
33383 if (waypoint.original) {
33384 waypoint.original = copyWaypoint$1(waypoint.original);
33385 }
33386
33387 return waypoint;
33388 });
33389 }
33390
33391 function copyWaypoint$1(waypoint) {
33392 return assign({}, waypoint);
33393 }
33394
33395 function removeElement(element, elements) {
33396 var index = elements.indexOf(element);
33397
33398 if (index === -1) {
33399 return elements;
33400 }
33401
33402 return elements.splice(index, 1);
33403 }
33404
33405 var CopyPasteModule$1 = {
33406 __depends__: [
33407 ClipboardModule,
33408 CreateModule,
33409 MouseModule,
33410 RulesModule$1
33411 ],
33412 __init__: [ 'copyPaste' ],
33413 copyPaste: [ 'type', CopyPaste ]
33414 };
33415
33416 function copyProperties$1(source, target, properties) {
33417 if (!isArray$2(properties)) {
33418 properties = [ properties ];
33419 }
33420
33421 forEach(properties, function(property) {
33422 if (!isUndefined$1(source[property])) {
33423 target[property] = source[property];
33424 }
33425 });
33426 }
33427
33428 function removeProperties(element, properties) {
33429 if (!isArray$2(properties)) {
33430 properties = [ properties ];
33431 }
33432
33433 forEach(properties, function(property) {
33434 if (element[property]) {
33435 delete element[property];
33436 }
33437 });
33438 }
33439
33440 var LOW_PRIORITY$f = 750;
33441
33442
33443 function BpmnCopyPaste(bpmnFactory, eventBus, moddleCopy) {
33444
33445 eventBus.on('copyPaste.copyElement', LOW_PRIORITY$f, function(context) {
33446 var descriptor = context.descriptor,
33447 element = context.element;
33448
33449 var businessObject = descriptor.oldBusinessObject = getBusinessObject(element);
33450
33451 descriptor.type = element.type;
33452
33453 copyProperties$1(businessObject, descriptor, 'name');
33454
33455 descriptor.di = {};
33456
33457 // colors will be set to DI
33458 copyProperties$1(businessObject.di, descriptor.di, [
33459 'fill',
33460 'stroke',
33461 'background-color',
33462 'border-color',
33463 'color'
33464 ]);
33465
33466 copyProperties$1(businessObject.di, descriptor, 'isExpanded');
33467
33468 if (isLabel$3(descriptor)) {
33469 return descriptor;
33470 }
33471
33472 // default sequence flow
33473 if (businessObject.default) {
33474 descriptor.default = businessObject.default.id;
33475 }
33476 });
33477
33478 eventBus.on('moddleCopy.canCopyProperty', function(context) {
33479 var parent = context.parent,
33480 property = context.property,
33481 propertyName = context.propertyName,
33482 bpmnProcess;
33483
33484 if (
33485 propertyName === 'processRef' &&
33486 is$1(parent, 'bpmn:Participant') &&
33487 is$1(property, 'bpmn:Process')
33488 ) {
33489 bpmnProcess = bpmnFactory.create('bpmn:Process');
33490
33491 // return copy of process
33492 return moddleCopy.copyElement(property, bpmnProcess);
33493 }
33494 });
33495
33496 var references;
33497
33498 function resolveReferences(descriptor, cache) {
33499 var businessObject = getBusinessObject(descriptor);
33500
33501 // default sequence flows
33502 if (descriptor.default) {
33503
33504 // relationship cannot be resolved immediately
33505 references[ descriptor.default ] = {
33506 element: businessObject,
33507 property: 'default'
33508 };
33509 }
33510
33511 // boundary events
33512 if (descriptor.host) {
33513
33514 // relationship can be resolved immediately
33515 getBusinessObject(descriptor).attachedToRef = getBusinessObject(cache[ descriptor.host ]);
33516 }
33517
33518 references = omit(references, reduce(references, function(array, reference, key) {
33519 var element = reference.element,
33520 property = reference.property;
33521
33522 if (key === descriptor.id) {
33523 element[ property ] = businessObject;
33524
33525 array.push(descriptor.id);
33526 }
33527
33528 return array;
33529 }, []));
33530 }
33531
33532 eventBus.on('copyPaste.pasteElements', function() {
33533 references = {};
33534 });
33535
33536 eventBus.on('copyPaste.pasteElement', function(context) {
33537 var cache = context.cache,
33538 descriptor = context.descriptor,
33539 oldBusinessObject = descriptor.oldBusinessObject,
33540 newBusinessObject;
33541
33542 // do NOT copy business object if external label
33543 if (isLabel$3(descriptor)) {
33544 descriptor.businessObject = getBusinessObject(cache[ descriptor.labelTarget ]);
33545
33546 return;
33547 }
33548
33549 newBusinessObject = bpmnFactory.create(oldBusinessObject.$type);
33550
33551 descriptor.businessObject = moddleCopy.copyElement(
33552 oldBusinessObject,
33553 newBusinessObject
33554 );
33555
33556 // resolve references e.g. default sequence flow
33557 resolveReferences(descriptor, cache);
33558
33559 copyProperties$1(descriptor, newBusinessObject, [
33560 'isExpanded',
33561 'name'
33562 ]);
33563
33564 removeProperties(descriptor, 'oldBusinessObject');
33565 });
33566
33567 }
33568
33569
33570 BpmnCopyPaste.$inject = [
33571 'bpmnFactory',
33572 'eventBus',
33573 'moddleCopy'
33574 ];
33575
33576 // helpers //////////
33577
33578 function isLabel$3(element) {
33579 return !!element.labelTarget;
33580 }
33581
33582 var DISALLOWED_PROPERTIES = [
33583 'artifacts',
33584 'dataInputAssociations',
33585 'dataOutputAssociations',
33586 'default',
33587 'flowElements',
33588 'lanes',
33589 'incoming',
33590 'outgoing'
33591 ];
33592
33593 /**
33594 * @typedef {Function} <moddleCopy.canCopyProperties> listener
33595 *
33596 * @param {Object} context
33597 * @param {Array<string>} context.propertyNames
33598 * @param {ModdleElement} context.sourceElement
33599 * @param {ModdleElement} context.targetElement
33600 *
33601 * @returns {Array<string>|boolean} - Return properties to be copied or false to disallow
33602 * copying.
33603 */
33604
33605 /**
33606 * @typedef {Function} <moddleCopy.canCopyProperty> listener
33607 *
33608 * @param {Object} context
33609 * @param {ModdleElement} context.parent
33610 * @param {*} context.property
33611 * @param {string} context.propertyName
33612 *
33613 * @returns {*|boolean} - Return copied property or false to disallow
33614 * copying.
33615 */
33616
33617 /**
33618 * @typedef {Function} <moddleCopy.canSetCopiedProperty> listener
33619 *
33620 * @param {Object} context
33621 * @param {ModdleElement} context.parent
33622 * @param {*} context.property
33623 * @param {string} context.propertyName
33624 *
33625 * @returns {boolean} - Return false to disallow
33626 * setting copied property.
33627 */
33628
33629 /**
33630 * Utility for copying model properties from source element to target element.
33631 *
33632 * @param {EventBus} eventBus
33633 * @param {BpmnFactory} bpmnFactory
33634 * @param {BpmnModdle} moddle
33635 */
33636 function ModdleCopy(eventBus, bpmnFactory, moddle) {
33637 this._bpmnFactory = bpmnFactory;
33638 this._eventBus = eventBus;
33639 this._moddle = moddle;
33640
33641 // copy extension elements last
33642 eventBus.on('moddleCopy.canCopyProperties', function(context) {
33643 var propertyNames = context.propertyNames;
33644
33645 if (!propertyNames || !propertyNames.length) {
33646 return;
33647 }
33648
33649 return sortBy(propertyNames, function(propertyName) {
33650 return propertyName === 'extensionElements';
33651 });
33652 });
33653
33654 // default check whether property can be copied
33655 eventBus.on('moddleCopy.canCopyProperty', function(context) {
33656 var parent = context.parent,
33657 parentDescriptor = isObject(parent) && parent.$descriptor,
33658 propertyName = context.propertyName;
33659
33660 if (propertyName && DISALLOWED_PROPERTIES.indexOf(propertyName) !== -1) {
33661
33662 // disallow copying property
33663 return false;
33664 }
33665
33666 if (propertyName &&
33667 parentDescriptor &&
33668 !find(parentDescriptor.properties, matchPattern({ name: propertyName }))) {
33669
33670 // disallow copying property
33671 return false;
33672 }
33673 });
33674
33675 // do NOT allow to copy empty extension elements
33676 eventBus.on('moddleCopy.canSetCopiedProperty', function(context) {
33677 var property = context.property;
33678
33679 if (is(property, 'bpmn:ExtensionElements') && (!property.values || !property.values.length)) {
33680
33681 // disallow setting copied property
33682 return false;
33683 }
33684 });
33685 }
33686
33687 ModdleCopy.$inject = [
33688 'eventBus',
33689 'bpmnFactory',
33690 'moddle'
33691 ];
33692
33693 /**
33694 * Copy model properties of source element to target element.
33695 *
33696 * @param {ModdleElement} sourceElement
33697 * @param {ModdleElement} targetElement
33698 * @param {Array<string>} [propertyNames]
33699 *
33700 * @param {ModdleElement}
33701 */
33702 ModdleCopy.prototype.copyElement = function(sourceElement, targetElement, propertyNames) {
33703 var self = this;
33704
33705 if (propertyNames && !isArray$2(propertyNames)) {
33706 propertyNames = [ propertyNames ];
33707 }
33708
33709 propertyNames = propertyNames || getPropertyNames(sourceElement.$descriptor);
33710
33711 var canCopyProperties = this._eventBus.fire('moddleCopy.canCopyProperties', {
33712 propertyNames: propertyNames,
33713 sourceElement: sourceElement,
33714 targetElement: targetElement
33715 });
33716
33717 if (canCopyProperties === false) {
33718 return targetElement;
33719 }
33720
33721 if (isArray$2(canCopyProperties)) {
33722 propertyNames = canCopyProperties;
33723 }
33724
33725 // copy properties
33726 forEach(propertyNames, function(propertyName) {
33727 var sourceProperty;
33728
33729 if (has(sourceElement, propertyName)) {
33730 sourceProperty = sourceElement.get(propertyName);
33731 }
33732
33733 var copiedProperty = self.copyProperty(sourceProperty, targetElement, propertyName);
33734
33735 var canSetProperty = self._eventBus.fire('moddleCopy.canSetCopiedProperty', {
33736 parent: targetElement,
33737 property: copiedProperty,
33738 propertyName: propertyName
33739 });
33740
33741 if (canSetProperty === false) {
33742 return;
33743 }
33744
33745 if (isDefined(copiedProperty)) {
33746 targetElement.set(propertyName, copiedProperty);
33747 }
33748 });
33749
33750 return targetElement;
33751 };
33752
33753 /**
33754 * Copy model property.
33755 *
33756 * @param {*} property
33757 * @param {ModdleElement} parent
33758 * @param {string} propertyName
33759 *
33760 * @returns {*}
33761 */
33762 ModdleCopy.prototype.copyProperty = function(property, parent, propertyName) {
33763 var self = this;
33764
33765 // allow others to copy property
33766 var copiedProperty = this._eventBus.fire('moddleCopy.canCopyProperty', {
33767 parent: parent,
33768 property: property,
33769 propertyName: propertyName
33770 });
33771
33772 // return if copying is NOT allowed
33773 if (copiedProperty === false) {
33774 return;
33775 }
33776
33777 if (copiedProperty) {
33778 if (isObject(copiedProperty) && copiedProperty.$type && !copiedProperty.$parent) {
33779 copiedProperty.$parent = parent;
33780 }
33781
33782 return copiedProperty;
33783 }
33784
33785 var propertyDescriptor = this._moddle.getPropertyDescriptor(parent, propertyName);
33786
33787 // do NOT copy references
33788 if (propertyDescriptor.isReference) {
33789 return;
33790 }
33791
33792 // copy id
33793 if (propertyDescriptor.isId) {
33794 return this._copyId(property, parent);
33795 }
33796
33797 // copy arrays
33798 if (isArray$2(property)) {
33799 return reduce(property, function(childProperties, childProperty) {
33800
33801 // recursion
33802 copiedProperty = self.copyProperty(childProperty, parent, propertyName);
33803
33804 // copying might NOT be allowed
33805 if (copiedProperty) {
33806 copiedProperty.$parent = parent;
33807
33808 return childProperties.concat(copiedProperty);
33809 }
33810
33811 return childProperties;
33812 }, []);
33813 }
33814
33815 // copy model elements
33816 if (isObject(property) && property.$type) {
33817 if (this._moddle.getElementDescriptor(property).isGeneric) {
33818 return;
33819 }
33820
33821 copiedProperty = self._bpmnFactory.create(property.$type);
33822
33823 copiedProperty.$parent = parent;
33824
33825 // recursion
33826 copiedProperty = self.copyElement(property, copiedProperty);
33827
33828 return copiedProperty;
33829 }
33830
33831 // copy primitive properties
33832 return property;
33833 };
33834
33835 ModdleCopy.prototype._copyId = function(id, element) {
33836
33837 // disallow if already taken
33838 if (this._moddle.ids.assigned(id)) {
33839 return;
33840 } else {
33841
33842 this._moddle.ids.claim(id, element);
33843 return id;
33844 }
33845 };
33846
33847 // helpers //////////
33848
33849 function getPropertyNames(descriptor, keepDefaultProperties) {
33850 return reduce(descriptor.properties, function(properties, property) {
33851
33852 if (keepDefaultProperties && property.default) {
33853 return properties;
33854 }
33855
33856 return properties.concat(property.name);
33857 }, []);
33858 }
33859
33860 function is(element, type) {
33861 return element && (typeof element.$instanceOf === 'function') && element.$instanceOf(type);
33862 }
33863
33864 var CopyPasteModule = {
33865 __depends__: [
33866 CopyPasteModule$1
33867 ],
33868 __init__: [ 'bpmnCopyPaste', 'moddleCopy' ],
33869 bpmnCopyPaste: [ 'type', BpmnCopyPaste ],
33870 moddleCopy: [ 'type', ModdleCopy ]
33871 };
33872
33873 var round$5 = Math.round;
33874
33875 /**
33876 * Service that allow replacing of elements.
33877 */
33878 function Replace(modeling) {
33879
33880 this._modeling = modeling;
33881 }
33882
33883 Replace.$inject = [ 'modeling' ];
33884
33885 /**
33886 * @param {Element} oldElement - Element to be replaced
33887 * @param {Object} newElementData - Containing information about the new element,
33888 * for example the new bounds and type.
33889 * @param {Object} options - Custom options that will be attached to the context. It can be used to inject data
33890 * that is needed in the command chain. For example it could be used in
33891 * eventbus.on('commandStack.shape.replace.postExecute') to change shape attributes after
33892 * shape creation.
33893 */
33894 Replace.prototype.replaceElement = function(oldElement, newElementData, options) {
33895
33896 if (oldElement.waypoints) {
33897
33898 // TODO(nikku): we do not replace connections, yet
33899 return null;
33900 }
33901
33902 var modeling = this._modeling;
33903
33904 var width = newElementData.width || oldElement.width,
33905 height = newElementData.height || oldElement.height,
33906 x = newElementData.x || oldElement.x,
33907 y = newElementData.y || oldElement.y,
33908 centerX = round$5(x + width / 2),
33909 centerY = round$5(y + height / 2);
33910
33911 // modeling API requires center coordinates,
33912 // account for that when handling shape bounds
33913
33914 return modeling.replaceShape(
33915 oldElement,
33916 assign(
33917 {},
33918 newElementData,
33919 {
33920 x: centerX,
33921 y: centerY,
33922 width: width,
33923 height: height
33924 }
33925 ),
33926 options
33927 );
33928 };
33929
33930 var ReplaceModule$1 = {
33931 __init__: [ 'replace' ],
33932 replace: [ 'type', Replace ]
33933 };
33934
33935 function copyProperties(source, target, properties) {
33936 if (!isArray$2(properties)) {
33937 properties = [ properties ];
33938 }
33939
33940 forEach(properties, function(property) {
33941 if (!isUndefined$1(source[property])) {
33942 target[property] = source[property];
33943 }
33944 });
33945 }
33946
33947 var CUSTOM_PROPERTIES = [
33948 'cancelActivity',
33949 'instantiate',
33950 'eventGatewayType',
33951 'triggeredByEvent',
33952 'isInterrupting'
33953 ];
33954
33955
33956 function toggeling(element, target) {
33957
33958 var oldCollapsed = (
33959 element && has(element, 'collapsed') ? element.collapsed : !isExpanded(element)
33960 );
33961
33962 var targetCollapsed;
33963
33964 if (target && (has(target, 'collapsed') || has(target, 'isExpanded'))) {
33965
33966 // property is explicitly set so use it
33967 targetCollapsed = (
33968 has(target, 'collapsed') ? target.collapsed : !target.isExpanded
33969 );
33970 } else {
33971
33972 // keep old state
33973 targetCollapsed = oldCollapsed;
33974 }
33975
33976 if (oldCollapsed !== targetCollapsed) {
33977 element.collapsed = oldCollapsed;
33978 return true;
33979 }
33980
33981 return false;
33982 }
33983
33984
33985
33986 /**
33987 * This module takes care of replacing BPMN elements
33988 */
33989 function BpmnReplace(
33990 bpmnFactory,
33991 elementFactory,
33992 moddleCopy,
33993 modeling,
33994 replace,
33995 rules,
33996 selection
33997 ) {
33998
33999 /**
34000 * Prepares a new business object for the replacement element
34001 * and triggers the replace operation.
34002 *
34003 * @param {djs.model.Base} element
34004 * @param {Object} target
34005 * @param {Object} [hints]
34006 *
34007 * @return {djs.model.Base} the newly created element
34008 */
34009 function replaceElement(element, target, hints) {
34010
34011 hints = hints || {};
34012
34013 var type = target.type,
34014 oldBusinessObject = element.businessObject;
34015
34016 if (isSubProcess(oldBusinessObject)) {
34017 if (type === 'bpmn:SubProcess') {
34018 if (toggeling(element, target)) {
34019
34020 // expanding or collapsing process
34021 modeling.toggleCollapse(element);
34022
34023 return element;
34024 }
34025 }
34026 }
34027
34028 var newBusinessObject = bpmnFactory.create(type);
34029
34030 var newElement = {
34031 type: type,
34032 businessObject: newBusinessObject
34033 };
34034
34035 var elementProps = getPropertyNames(oldBusinessObject.$descriptor),
34036 newElementProps = getPropertyNames(newBusinessObject.$descriptor, true),
34037 copyProps = intersection(elementProps, newElementProps);
34038
34039 // initialize special properties defined in target definition
34040 assign(newBusinessObject, pick(target, CUSTOM_PROPERTIES));
34041
34042 var properties = filter(copyProps, function(propertyName) {
34043
34044 // copying event definitions, unless we replace
34045 if (propertyName === 'eventDefinitions') {
34046 return hasEventDefinition$1(element, target.eventDefinitionType);
34047 }
34048
34049 // retain loop characteristics if the target element
34050 // is not an event sub process
34051 if (propertyName === 'loopCharacteristics') {
34052 return !isEventSubProcess(newBusinessObject);
34053 }
34054
34055 // so the applied properties from 'target' don't get lost
34056 if (has(newBusinessObject, propertyName)) {
34057 return false;
34058 }
34059
34060 if (propertyName === 'processRef' && target.isExpanded === false) {
34061 return false;
34062 }
34063
34064 if (propertyName === 'triggeredByEvent') {
34065 return false;
34066 }
34067
34068 return true;
34069 });
34070
34071 newBusinessObject = moddleCopy.copyElement(
34072 oldBusinessObject,
34073 newBusinessObject,
34074 properties
34075 );
34076
34077 // initialize custom BPMN extensions
34078 if (target.eventDefinitionType) {
34079
34080 // only initialize with new eventDefinition
34081 // if we did not set an event definition yet,
34082 // i.e. because we copied it
34083 if (!hasEventDefinition$1(newBusinessObject, target.eventDefinitionType)) {
34084 newElement.eventDefinitionType = target.eventDefinitionType;
34085 newElement.eventDefinitionAttrs = target.eventDefinitionAttrs;
34086 }
34087 }
34088
34089 if (is$1(oldBusinessObject, 'bpmn:Activity')) {
34090
34091 if (isSubProcess(oldBusinessObject)) {
34092
34093 // no toggeling, so keep old state
34094 newElement.isExpanded = isExpanded(oldBusinessObject);
34095 }
34096
34097 // else if property is explicitly set, use it
34098 else if (target && has(target, 'isExpanded')) {
34099 newElement.isExpanded = target.isExpanded;
34100 }
34101
34102 // TODO: need also to respect min/max Size
34103 // copy size, from an expanded subprocess to an expanded alternative subprocess
34104 // except bpmn:Task, because Task is always expanded
34105 if ((isExpanded(oldBusinessObject) && !is$1(oldBusinessObject, 'bpmn:Task')) && newElement.isExpanded) {
34106 newElement.width = element.width;
34107 newElement.height = element.height;
34108 }
34109 }
34110
34111 // remove children if not expanding sub process
34112 if (isSubProcess(oldBusinessObject) && !isSubProcess(newBusinessObject)) {
34113 hints.moveChildren = false;
34114 }
34115
34116 // transform collapsed/expanded pools
34117 if (is$1(oldBusinessObject, 'bpmn:Participant')) {
34118
34119 // create expanded pool
34120 if (target.isExpanded === true) {
34121 newBusinessObject.processRef = bpmnFactory.create('bpmn:Process');
34122 } else {
34123
34124 // remove children when transforming to collapsed pool
34125 hints.moveChildren = false;
34126 }
34127
34128 // apply same width and default height
34129 newElement.width = element.width;
34130 newElement.height = elementFactory._getDefaultSize(newBusinessObject).height;
34131 }
34132
34133 if (!rules.allowed('shape.resize', { shape: newBusinessObject })) {
34134 newElement.height = elementFactory._getDefaultSize(newBusinessObject).height;
34135 newElement.width = elementFactory._getDefaultSize(newBusinessObject).width;
34136 }
34137
34138 newBusinessObject.name = oldBusinessObject.name;
34139
34140 // retain default flow's reference between inclusive <-> exclusive gateways and activities
34141 if (
34142 isAny(oldBusinessObject, [
34143 'bpmn:ExclusiveGateway',
34144 'bpmn:InclusiveGateway',
34145 'bpmn:Activity'
34146 ]) &&
34147 isAny(newBusinessObject, [
34148 'bpmn:ExclusiveGateway',
34149 'bpmn:InclusiveGateway',
34150 'bpmn:Activity'
34151 ])
34152 ) {
34153 newBusinessObject.default = oldBusinessObject.default;
34154 }
34155
34156 if (
34157 target.host &&
34158 !is$1(oldBusinessObject, 'bpmn:BoundaryEvent') &&
34159 is$1(newBusinessObject, 'bpmn:BoundaryEvent')
34160 ) {
34161 newElement.host = target.host;
34162 }
34163
34164 // The DataStoreReference element is 14px wider than the DataObjectReference element
34165 // This ensures that they stay centered on the x axis when replaced
34166 if (
34167 newElement.type === 'bpmn:DataStoreReference' ||
34168 newElement.type === 'bpmn:DataObjectReference'
34169 ) {
34170 newElement.x = element.x + (element.width - newElement.width) / 2;
34171 }
34172
34173 newElement.di = {};
34174
34175 // colors will be set to DI
34176 copyProperties(oldBusinessObject.di, newElement.di, [
34177 'fill',
34178 'stroke',
34179 'background-color',
34180 'border-color',
34181 'color'
34182 ]);
34183
34184 newElement = replace.replaceElement(element, newElement, hints);
34185
34186 if (hints.select !== false) {
34187 selection.select(newElement);
34188 }
34189
34190 return newElement;
34191 }
34192
34193 this.replaceElement = replaceElement;
34194 }
34195
34196 BpmnReplace.$inject = [
34197 'bpmnFactory',
34198 'elementFactory',
34199 'moddleCopy',
34200 'modeling',
34201 'replace',
34202 'rules',
34203 'selection'
34204 ];
34205
34206
34207 function isSubProcess(bo) {
34208 return is$1(bo, 'bpmn:SubProcess');
34209 }
34210
34211 function hasEventDefinition$1(element, type) {
34212
34213 var bo = getBusinessObject(element);
34214
34215 return type && bo.get('eventDefinitions').some(function(definition) {
34216 return is$1(definition, type);
34217 });
34218 }
34219
34220 /**
34221 * Compute intersection between two arrays.
34222 */
34223 function intersection(a1, a2) {
34224 return a1.filter(function(el) {
34225 return a2.indexOf(el) !== -1;
34226 });
34227 }
34228
34229 var ReplaceModule = {
34230 __depends__: [
34231 CopyPasteModule,
34232 ReplaceModule$1,
34233 SelectionModule
34234 ],
34235 bpmnReplace: [ 'type', BpmnReplace ]
34236 };
34237
34238 /**
34239 * Returns true, if an element is from a different type
34240 * than a target definition. Takes into account the type,
34241 * event definition type and triggeredByEvent property.
34242 *
34243 * @param {djs.model.Base} element
34244 *
34245 * @return {boolean}
34246 */
34247 function isDifferentType(element) {
34248
34249 return function(entry) {
34250 var target = entry.target;
34251
34252 var businessObject = getBusinessObject(element),
34253 eventDefinition = businessObject.eventDefinitions && businessObject.eventDefinitions[0];
34254
34255 var isTypeEqual = businessObject.$type === target.type;
34256
34257 var isEventDefinitionEqual = (
34258 (eventDefinition && eventDefinition.$type) === target.eventDefinitionType
34259 );
34260
34261 var isTriggeredByEventEqual = (
34262 businessObject.triggeredByEvent === target.triggeredByEvent
34263 );
34264
34265 var isExpandedEqual = (
34266 target.isExpanded === undefined ||
34267 target.isExpanded === isExpanded(businessObject)
34268 );
34269
34270 return !isTypeEqual || !isEventDefinitionEqual || !isTriggeredByEventEqual || !isExpandedEqual;
34271 };
34272 }
34273
34274 var START_EVENT = [
34275 {
34276 label: 'Start Event',
34277 actionName: 'replace-with-none-start',
34278 className: 'bpmn-icon-start-event-none',
34279 target: {
34280 type: 'bpmn:StartEvent'
34281 }
34282 },
34283 {
34284 label: 'Intermediate Throw Event',
34285 actionName: 'replace-with-none-intermediate-throwing',
34286 className: 'bpmn-icon-intermediate-event-none',
34287 target: {
34288 type: 'bpmn:IntermediateThrowEvent'
34289 }
34290 },
34291 {
34292 label: 'End Event',
34293 actionName: 'replace-with-none-end',
34294 className: 'bpmn-icon-end-event-none',
34295 target: {
34296 type: 'bpmn:EndEvent'
34297 }
34298 },
34299 {
34300 label: 'Message Start Event',
34301 actionName: 'replace-with-message-start',
34302 className: 'bpmn-icon-start-event-message',
34303 target: {
34304 type: 'bpmn:StartEvent',
34305 eventDefinitionType: 'bpmn:MessageEventDefinition'
34306 }
34307 },
34308 {
34309 label: 'Timer Start Event',
34310 actionName: 'replace-with-timer-start',
34311 className: 'bpmn-icon-start-event-timer',
34312 target: {
34313 type: 'bpmn:StartEvent',
34314 eventDefinitionType: 'bpmn:TimerEventDefinition'
34315 }
34316 },
34317 {
34318 label: 'Conditional Start Event',
34319 actionName: 'replace-with-conditional-start',
34320 className: 'bpmn-icon-start-event-condition',
34321 target: {
34322 type: 'bpmn:StartEvent',
34323 eventDefinitionType: 'bpmn:ConditionalEventDefinition'
34324 }
34325 },
34326 {
34327 label: 'Signal Start Event',
34328 actionName: 'replace-with-signal-start',
34329 className: 'bpmn-icon-start-event-signal',
34330 target: {
34331 type: 'bpmn:StartEvent',
34332 eventDefinitionType: 'bpmn:SignalEventDefinition'
34333 }
34334 }
34335 ];
34336
34337 var START_EVENT_SUB_PROCESS = [
34338 {
34339 label: 'Start Event',
34340 actionName: 'replace-with-none-start',
34341 className: 'bpmn-icon-start-event-none',
34342 target: {
34343 type: 'bpmn:StartEvent'
34344 }
34345 },
34346 {
34347 label: 'Intermediate Throw Event',
34348 actionName: 'replace-with-none-intermediate-throwing',
34349 className: 'bpmn-icon-intermediate-event-none',
34350 target: {
34351 type: 'bpmn:IntermediateThrowEvent'
34352 }
34353 },
34354 {
34355 label: 'End Event',
34356 actionName: 'replace-with-none-end',
34357 className: 'bpmn-icon-end-event-none',
34358 target: {
34359 type: 'bpmn:EndEvent'
34360 }
34361 }
34362 ];
34363
34364 var INTERMEDIATE_EVENT = [
34365 {
34366 label: 'Start Event',
34367 actionName: 'replace-with-none-start',
34368 className: 'bpmn-icon-start-event-none',
34369 target: {
34370 type: 'bpmn:StartEvent'
34371 }
34372 },
34373 {
34374 label: 'Intermediate Throw Event',
34375 actionName: 'replace-with-none-intermediate-throw',
34376 className: 'bpmn-icon-intermediate-event-none',
34377 target: {
34378 type: 'bpmn:IntermediateThrowEvent'
34379 }
34380 },
34381 {
34382 label: 'End Event',
34383 actionName: 'replace-with-none-end',
34384 className: 'bpmn-icon-end-event-none',
34385 target: {
34386 type: 'bpmn:EndEvent'
34387 }
34388 },
34389 {
34390 label: 'Message Intermediate Catch Event',
34391 actionName: 'replace-with-message-intermediate-catch',
34392 className: 'bpmn-icon-intermediate-event-catch-message',
34393 target: {
34394 type: 'bpmn:IntermediateCatchEvent',
34395 eventDefinitionType: 'bpmn:MessageEventDefinition'
34396 }
34397 },
34398 {
34399 label: 'Message Intermediate Throw Event',
34400 actionName: 'replace-with-message-intermediate-throw',
34401 className: 'bpmn-icon-intermediate-event-throw-message',
34402 target: {
34403 type: 'bpmn:IntermediateThrowEvent',
34404 eventDefinitionType: 'bpmn:MessageEventDefinition'
34405 }
34406 },
34407 {
34408 label: 'Timer Intermediate Catch Event',
34409 actionName: 'replace-with-timer-intermediate-catch',
34410 className: 'bpmn-icon-intermediate-event-catch-timer',
34411 target: {
34412 type: 'bpmn:IntermediateCatchEvent',
34413 eventDefinitionType: 'bpmn:TimerEventDefinition'
34414 }
34415 },
34416 {
34417 label: 'Escalation Intermediate Throw Event',
34418 actionName: 'replace-with-escalation-intermediate-throw',
34419 className: 'bpmn-icon-intermediate-event-throw-escalation',
34420 target: {
34421 type: 'bpmn:IntermediateThrowEvent',
34422 eventDefinitionType: 'bpmn:EscalationEventDefinition'
34423 }
34424 },
34425 {
34426 label: 'Conditional Intermediate Catch Event',
34427 actionName: 'replace-with-conditional-intermediate-catch',
34428 className: 'bpmn-icon-intermediate-event-catch-condition',
34429 target: {
34430 type: 'bpmn:IntermediateCatchEvent',
34431 eventDefinitionType: 'bpmn:ConditionalEventDefinition'
34432 }
34433 },
34434 {
34435 label: 'Link Intermediate Catch Event',
34436 actionName: 'replace-with-link-intermediate-catch',
34437 className: 'bpmn-icon-intermediate-event-catch-link',
34438 target: {
34439 type: 'bpmn:IntermediateCatchEvent',
34440 eventDefinitionType: 'bpmn:LinkEventDefinition',
34441 eventDefinitionAttrs: {
34442 name: ''
34443 }
34444 }
34445 },
34446 {
34447 label: 'Link Intermediate Throw Event',
34448 actionName: 'replace-with-link-intermediate-throw',
34449 className: 'bpmn-icon-intermediate-event-throw-link',
34450 target: {
34451 type: 'bpmn:IntermediateThrowEvent',
34452 eventDefinitionType: 'bpmn:LinkEventDefinition',
34453 eventDefinitionAttrs: {
34454 name: ''
34455 }
34456 }
34457 },
34458 {
34459 label: 'Compensation Intermediate Throw Event',
34460 actionName: 'replace-with-compensation-intermediate-throw',
34461 className: 'bpmn-icon-intermediate-event-throw-compensation',
34462 target: {
34463 type: 'bpmn:IntermediateThrowEvent',
34464 eventDefinitionType: 'bpmn:CompensateEventDefinition'
34465 }
34466 },
34467 {
34468 label: 'Signal Intermediate Catch Event',
34469 actionName: 'replace-with-signal-intermediate-catch',
34470 className: 'bpmn-icon-intermediate-event-catch-signal',
34471 target: {
34472 type: 'bpmn:IntermediateCatchEvent',
34473 eventDefinitionType: 'bpmn:SignalEventDefinition'
34474 }
34475 },
34476 {
34477 label: 'Signal Intermediate Throw Event',
34478 actionName: 'replace-with-signal-intermediate-throw',
34479 className: 'bpmn-icon-intermediate-event-throw-signal',
34480 target: {
34481 type: 'bpmn:IntermediateThrowEvent',
34482 eventDefinitionType: 'bpmn:SignalEventDefinition'
34483 }
34484 }
34485 ];
34486
34487 var END_EVENT = [
34488 {
34489 label: 'Start Event',
34490 actionName: 'replace-with-none-start',
34491 className: 'bpmn-icon-start-event-none',
34492 target: {
34493 type: 'bpmn:StartEvent'
34494 }
34495 },
34496 {
34497 label: 'Intermediate Throw Event',
34498 actionName: 'replace-with-none-intermediate-throw',
34499 className: 'bpmn-icon-intermediate-event-none',
34500 target: {
34501 type: 'bpmn:IntermediateThrowEvent'
34502 }
34503 },
34504 {
34505 label: 'End Event',
34506 actionName: 'replace-with-none-end',
34507 className: 'bpmn-icon-end-event-none',
34508 target: {
34509 type: 'bpmn:EndEvent'
34510 }
34511 },
34512 {
34513 label: 'Message End Event',
34514 actionName: 'replace-with-message-end',
34515 className: 'bpmn-icon-end-event-message',
34516 target: {
34517 type: 'bpmn:EndEvent',
34518 eventDefinitionType: 'bpmn:MessageEventDefinition'
34519 }
34520 },
34521 {
34522 label: 'Escalation End Event',
34523 actionName: 'replace-with-escalation-end',
34524 className: 'bpmn-icon-end-event-escalation',
34525 target: {
34526 type: 'bpmn:EndEvent',
34527 eventDefinitionType: 'bpmn:EscalationEventDefinition'
34528 }
34529 },
34530 {
34531 label: 'Error End Event',
34532 actionName: 'replace-with-error-end',
34533 className: 'bpmn-icon-end-event-error',
34534 target: {
34535 type: 'bpmn:EndEvent',
34536 eventDefinitionType: 'bpmn:ErrorEventDefinition'
34537 }
34538 },
34539 {
34540 label: 'Cancel End Event',
34541 actionName: 'replace-with-cancel-end',
34542 className: 'bpmn-icon-end-event-cancel',
34543 target: {
34544 type: 'bpmn:EndEvent',
34545 eventDefinitionType: 'bpmn:CancelEventDefinition'
34546 }
34547 },
34548 {
34549 label: 'Compensation End Event',
34550 actionName: 'replace-with-compensation-end',
34551 className: 'bpmn-icon-end-event-compensation',
34552 target: {
34553 type: 'bpmn:EndEvent',
34554 eventDefinitionType: 'bpmn:CompensateEventDefinition'
34555 }
34556 },
34557 {
34558 label: 'Signal End Event',
34559 actionName: 'replace-with-signal-end',
34560 className: 'bpmn-icon-end-event-signal',
34561 target: {
34562 type: 'bpmn:EndEvent',
34563 eventDefinitionType: 'bpmn:SignalEventDefinition'
34564 }
34565 },
34566 {
34567 label: 'Terminate End Event',
34568 actionName: 'replace-with-terminate-end',
34569 className: 'bpmn-icon-end-event-terminate',
34570 target: {
34571 type: 'bpmn:EndEvent',
34572 eventDefinitionType: 'bpmn:TerminateEventDefinition'
34573 }
34574 }
34575 ];
34576
34577 var GATEWAY = [
34578 {
34579 label: 'Exclusive Gateway',
34580 actionName: 'replace-with-exclusive-gateway',
34581 className: 'bpmn-icon-gateway-xor',
34582 target: {
34583 type: 'bpmn:ExclusiveGateway'
34584 }
34585 },
34586 {
34587 label: 'Parallel Gateway',
34588 actionName: 'replace-with-parallel-gateway',
34589 className: 'bpmn-icon-gateway-parallel',
34590 target: {
34591 type: 'bpmn:ParallelGateway'
34592 }
34593 },
34594 {
34595 label: 'Inclusive Gateway',
34596 actionName: 'replace-with-inclusive-gateway',
34597 className: 'bpmn-icon-gateway-or',
34598 target: {
34599 type: 'bpmn:InclusiveGateway'
34600 }
34601 },
34602 {
34603 label: 'Complex Gateway',
34604 actionName: 'replace-with-complex-gateway',
34605 className: 'bpmn-icon-gateway-complex',
34606 target: {
34607 type: 'bpmn:ComplexGateway'
34608 }
34609 },
34610 {
34611 label: 'Event based Gateway',
34612 actionName: 'replace-with-event-based-gateway',
34613 className: 'bpmn-icon-gateway-eventbased',
34614 target: {
34615 type: 'bpmn:EventBasedGateway',
34616 instantiate: false,
34617 eventGatewayType: 'Exclusive'
34618 }
34619 }
34620
34621 // Gateways deactivated until https://github.com/bpmn-io/bpmn-js/issues/194
34622 // {
34623 // label: 'Event based instantiating Gateway',
34624 // actionName: 'replace-with-exclusive-event-based-gateway',
34625 // className: 'bpmn-icon-exclusive-event-based',
34626 // target: {
34627 // type: 'bpmn:EventBasedGateway'
34628 // },
34629 // options: {
34630 // businessObject: { instantiate: true, eventGatewayType: 'Exclusive' }
34631 // }
34632 // },
34633 // {
34634 // label: 'Parallel Event based instantiating Gateway',
34635 // actionName: 'replace-with-parallel-event-based-instantiate-gateway',
34636 // className: 'bpmn-icon-parallel-event-based-instantiate-gateway',
34637 // target: {
34638 // type: 'bpmn:EventBasedGateway'
34639 // },
34640 // options: {
34641 // businessObject: { instantiate: true, eventGatewayType: 'Parallel' }
34642 // }
34643 // }
34644 ];
34645
34646 var SUBPROCESS_EXPANDED = [
34647 {
34648 label: 'Transaction',
34649 actionName: 'replace-with-transaction',
34650 className: 'bpmn-icon-transaction',
34651 target: {
34652 type: 'bpmn:Transaction',
34653 isExpanded: true
34654 }
34655 },
34656 {
34657 label: 'Event Sub Process',
34658 actionName: 'replace-with-event-subprocess',
34659 className: 'bpmn-icon-event-subprocess-expanded',
34660 target: {
34661 type: 'bpmn:SubProcess',
34662 triggeredByEvent: true,
34663 isExpanded: true
34664 }
34665 },
34666 {
34667 label: 'Sub Process (collapsed)',
34668 actionName: 'replace-with-collapsed-subprocess',
34669 className: 'bpmn-icon-subprocess-collapsed',
34670 target: {
34671 type: 'bpmn:SubProcess',
34672 isExpanded: false
34673 }
34674 }
34675 ];
34676
34677 var TRANSACTION = [
34678 {
34679 label: 'Sub Process',
34680 actionName: 'replace-with-subprocess',
34681 className: 'bpmn-icon-subprocess-expanded',
34682 target: {
34683 type: 'bpmn:SubProcess',
34684 isExpanded: true
34685 }
34686 },
34687 {
34688 label: 'Event Sub Process',
34689 actionName: 'replace-with-event-subprocess',
34690 className: 'bpmn-icon-event-subprocess-expanded',
34691 target: {
34692 type: 'bpmn:SubProcess',
34693 triggeredByEvent: true,
34694 isExpanded: true
34695 }
34696 }
34697 ];
34698
34699 var EVENT_SUB_PROCESS = [
34700 {
34701 label: 'Sub Process',
34702 actionName: 'replace-with-subprocess',
34703 className: 'bpmn-icon-subprocess-expanded',
34704 target: {
34705 type: 'bpmn:SubProcess',
34706 isExpanded: true
34707 }
34708 },
34709 {
34710 label: 'Transaction',
34711 actionName: 'replace-with-transaction',
34712 className: 'bpmn-icon-transaction',
34713 target: {
34714 type: 'bpmn:Transaction',
34715 isExpanded: true
34716 }
34717 }
34718 ];
34719
34720 var TASK = [
34721 {
34722 label: 'Task',
34723 actionName: 'replace-with-task',
34724 className: 'bpmn-icon-task',
34725 target: {
34726 type: 'bpmn:Task'
34727 }
34728 },
34729 {
34730 label: 'Send Task',
34731 actionName: 'replace-with-send-task',
34732 className: 'bpmn-icon-send',
34733 target: {
34734 type: 'bpmn:SendTask'
34735 }
34736 },
34737 {
34738 label: 'Receive Task',
34739 actionName: 'replace-with-receive-task',
34740 className: 'bpmn-icon-receive',
34741 target: {
34742 type: 'bpmn:ReceiveTask'
34743 }
34744 },
34745 {
34746 label: 'User Task',
34747 actionName: 'replace-with-user-task',
34748 className: 'bpmn-icon-user',
34749 target: {
34750 type: 'bpmn:UserTask'
34751 }
34752 },
34753 {
34754 label: 'Manual Task',
34755 actionName: 'replace-with-manual-task',
34756 className: 'bpmn-icon-manual',
34757 target: {
34758 type: 'bpmn:ManualTask'
34759 }
34760 },
34761 {
34762 label: 'Business Rule Task',
34763 actionName: 'replace-with-rule-task',
34764 className: 'bpmn-icon-business-rule',
34765 target: {
34766 type: 'bpmn:BusinessRuleTask'
34767 }
34768 },
34769 {
34770 label: 'Service Task',
34771 actionName: 'replace-with-service-task',
34772 className: 'bpmn-icon-service',
34773 target: {
34774 type: 'bpmn:ServiceTask'
34775 }
34776 },
34777 {
34778 label: 'Script Task',
34779 actionName: 'replace-with-script-task',
34780 className: 'bpmn-icon-script',
34781 target: {
34782 type: 'bpmn:ScriptTask'
34783 }
34784 },
34785 {
34786 label: 'Call Activity',
34787 actionName: 'replace-with-call-activity',
34788 className: 'bpmn-icon-call-activity',
34789 target: {
34790 type: 'bpmn:CallActivity'
34791 }
34792 },
34793 {
34794 label: 'Sub Process (collapsed)',
34795 actionName: 'replace-with-collapsed-subprocess',
34796 className: 'bpmn-icon-subprocess-collapsed',
34797 target: {
34798 type: 'bpmn:SubProcess',
34799 isExpanded: false
34800 }
34801 },
34802 {
34803 label: 'Sub Process (expanded)',
34804 actionName: 'replace-with-expanded-subprocess',
34805 className: 'bpmn-icon-subprocess-expanded',
34806 target: {
34807 type: 'bpmn:SubProcess',
34808 isExpanded: true
34809 }
34810 }
34811 ];
34812
34813 var DATA_OBJECT_REFERENCE = [
34814 {
34815 label: 'Data Store Reference',
34816 actionName: 'replace-with-data-store-reference',
34817 className: 'bpmn-icon-data-store',
34818 target: {
34819 type: 'bpmn:DataStoreReference'
34820 }
34821 }
34822 ];
34823
34824 var DATA_STORE_REFERENCE = [
34825 {
34826 label: 'Data Object Reference',
34827 actionName: 'replace-with-data-object-reference',
34828 className: 'bpmn-icon-data-object',
34829 target: {
34830 type: 'bpmn:DataObjectReference'
34831 }
34832 }
34833 ];
34834
34835 var BOUNDARY_EVENT = [
34836 {
34837 label: 'Message Boundary Event',
34838 actionName: 'replace-with-message-boundary',
34839 className: 'bpmn-icon-intermediate-event-catch-message',
34840 target: {
34841 type: 'bpmn:BoundaryEvent',
34842 eventDefinitionType: 'bpmn:MessageEventDefinition'
34843 }
34844 },
34845 {
34846 label: 'Timer Boundary Event',
34847 actionName: 'replace-with-timer-boundary',
34848 className: 'bpmn-icon-intermediate-event-catch-timer',
34849 target: {
34850 type: 'bpmn:BoundaryEvent',
34851 eventDefinitionType: 'bpmn:TimerEventDefinition'
34852 }
34853 },
34854 {
34855 label: 'Escalation Boundary Event',
34856 actionName: 'replace-with-escalation-boundary',
34857 className: 'bpmn-icon-intermediate-event-catch-escalation',
34858 target: {
34859 type: 'bpmn:BoundaryEvent',
34860 eventDefinitionType: 'bpmn:EscalationEventDefinition'
34861 }
34862 },
34863 {
34864 label: 'Conditional Boundary Event',
34865 actionName: 'replace-with-conditional-boundary',
34866 className: 'bpmn-icon-intermediate-event-catch-condition',
34867 target: {
34868 type: 'bpmn:BoundaryEvent',
34869 eventDefinitionType: 'bpmn:ConditionalEventDefinition'
34870 }
34871 },
34872 {
34873 label: 'Error Boundary Event',
34874 actionName: 'replace-with-error-boundary',
34875 className: 'bpmn-icon-intermediate-event-catch-error',
34876 target: {
34877 type: 'bpmn:BoundaryEvent',
34878 eventDefinitionType: 'bpmn:ErrorEventDefinition'
34879 }
34880 },
34881 {
34882 label: 'Cancel Boundary Event',
34883 actionName: 'replace-with-cancel-boundary',
34884 className: 'bpmn-icon-intermediate-event-catch-cancel',
34885 target: {
34886 type: 'bpmn:BoundaryEvent',
34887 eventDefinitionType: 'bpmn:CancelEventDefinition'
34888 }
34889 },
34890 {
34891 label: 'Signal Boundary Event',
34892 actionName: 'replace-with-signal-boundary',
34893 className: 'bpmn-icon-intermediate-event-catch-signal',
34894 target: {
34895 type: 'bpmn:BoundaryEvent',
34896 eventDefinitionType: 'bpmn:SignalEventDefinition'
34897 }
34898 },
34899 {
34900 label: 'Compensation Boundary Event',
34901 actionName: 'replace-with-compensation-boundary',
34902 className: 'bpmn-icon-intermediate-event-catch-compensation',
34903 target: {
34904 type: 'bpmn:BoundaryEvent',
34905 eventDefinitionType: 'bpmn:CompensateEventDefinition'
34906 }
34907 },
34908 {
34909 label: 'Message Boundary Event (non-interrupting)',
34910 actionName: 'replace-with-non-interrupting-message-boundary',
34911 className: 'bpmn-icon-intermediate-event-catch-non-interrupting-message',
34912 target: {
34913 type: 'bpmn:BoundaryEvent',
34914 eventDefinitionType: 'bpmn:MessageEventDefinition',
34915 cancelActivity: false
34916 }
34917 },
34918 {
34919 label: 'Timer Boundary Event (non-interrupting)',
34920 actionName: 'replace-with-non-interrupting-timer-boundary',
34921 className: 'bpmn-icon-intermediate-event-catch-non-interrupting-timer',
34922 target: {
34923 type: 'bpmn:BoundaryEvent',
34924 eventDefinitionType: 'bpmn:TimerEventDefinition',
34925 cancelActivity: false
34926 }
34927 },
34928 {
34929 label: 'Escalation Boundary Event (non-interrupting)',
34930 actionName: 'replace-with-non-interrupting-escalation-boundary',
34931 className: 'bpmn-icon-intermediate-event-catch-non-interrupting-escalation',
34932 target: {
34933 type: 'bpmn:BoundaryEvent',
34934 eventDefinitionType: 'bpmn:EscalationEventDefinition',
34935 cancelActivity: false
34936 }
34937 },
34938 {
34939 label: 'Conditional Boundary Event (non-interrupting)',
34940 actionName: 'replace-with-non-interrupting-conditional-boundary',
34941 className: 'bpmn-icon-intermediate-event-catch-non-interrupting-condition',
34942 target: {
34943 type: 'bpmn:BoundaryEvent',
34944 eventDefinitionType: 'bpmn:ConditionalEventDefinition',
34945 cancelActivity: false
34946 }
34947 },
34948 {
34949 label: 'Signal Boundary Event (non-interrupting)',
34950 actionName: 'replace-with-non-interrupting-signal-boundary',
34951 className: 'bpmn-icon-intermediate-event-catch-non-interrupting-signal',
34952 target: {
34953 type: 'bpmn:BoundaryEvent',
34954 eventDefinitionType: 'bpmn:SignalEventDefinition',
34955 cancelActivity: false
34956 }
34957 }
34958 ];
34959
34960 var EVENT_SUB_PROCESS_START_EVENT = [
34961 {
34962 label: 'Message Start Event',
34963 actionName: 'replace-with-message-start',
34964 className: 'bpmn-icon-start-event-message',
34965 target: {
34966 type: 'bpmn:StartEvent',
34967 eventDefinitionType: 'bpmn:MessageEventDefinition'
34968 }
34969 },
34970 {
34971 label: 'Timer Start Event',
34972 actionName: 'replace-with-timer-start',
34973 className: 'bpmn-icon-start-event-timer',
34974 target: {
34975 type: 'bpmn:StartEvent',
34976 eventDefinitionType: 'bpmn:TimerEventDefinition'
34977 }
34978 },
34979 {
34980 label: 'Conditional Start Event',
34981 actionName: 'replace-with-conditional-start',
34982 className: 'bpmn-icon-start-event-condition',
34983 target: {
34984 type: 'bpmn:StartEvent',
34985 eventDefinitionType: 'bpmn:ConditionalEventDefinition'
34986 }
34987 },
34988 {
34989 label: 'Signal Start Event',
34990 actionName: 'replace-with-signal-start',
34991 className: 'bpmn-icon-start-event-signal',
34992 target: {
34993 type: 'bpmn:StartEvent',
34994 eventDefinitionType: 'bpmn:SignalEventDefinition'
34995 }
34996 },
34997 {
34998 label: 'Error Start Event',
34999 actionName: 'replace-with-error-start',
35000 className: 'bpmn-icon-start-event-error',
35001 target: {
35002 type: 'bpmn:StartEvent',
35003 eventDefinitionType: 'bpmn:ErrorEventDefinition'
35004 }
35005 },
35006 {
35007 label: 'Escalation Start Event',
35008 actionName: 'replace-with-escalation-start',
35009 className: 'bpmn-icon-start-event-escalation',
35010 target: {
35011 type: 'bpmn:StartEvent',
35012 eventDefinitionType: 'bpmn:EscalationEventDefinition'
35013 }
35014 },
35015 {
35016 label: 'Compensation Start Event',
35017 actionName: 'replace-with-compensation-start',
35018 className: 'bpmn-icon-start-event-compensation',
35019 target: {
35020 type: 'bpmn:StartEvent',
35021 eventDefinitionType: 'bpmn:CompensateEventDefinition'
35022 }
35023 },
35024 {
35025 label: 'Message Start Event (non-interrupting)',
35026 actionName: 'replace-with-non-interrupting-message-start',
35027 className: 'bpmn-icon-start-event-non-interrupting-message',
35028 target: {
35029 type: 'bpmn:StartEvent',
35030 eventDefinitionType: 'bpmn:MessageEventDefinition',
35031 isInterrupting: false
35032 }
35033 },
35034 {
35035 label: 'Timer Start Event (non-interrupting)',
35036 actionName: 'replace-with-non-interrupting-timer-start',
35037 className: 'bpmn-icon-start-event-non-interrupting-timer',
35038 target: {
35039 type: 'bpmn:StartEvent',
35040 eventDefinitionType: 'bpmn:TimerEventDefinition',
35041 isInterrupting: false
35042 }
35043 },
35044 {
35045 label: 'Conditional Start Event (non-interrupting)',
35046 actionName: 'replace-with-non-interrupting-conditional-start',
35047 className: 'bpmn-icon-start-event-non-interrupting-condition',
35048 target: {
35049 type: 'bpmn:StartEvent',
35050 eventDefinitionType: 'bpmn:ConditionalEventDefinition',
35051 isInterrupting: false
35052 }
35053 },
35054 {
35055 label: 'Signal Start Event (non-interrupting)',
35056 actionName: 'replace-with-non-interrupting-signal-start',
35057 className: 'bpmn-icon-start-event-non-interrupting-signal',
35058 target: {
35059 type: 'bpmn:StartEvent',
35060 eventDefinitionType: 'bpmn:SignalEventDefinition',
35061 isInterrupting: false
35062 }
35063 },
35064 {
35065 label: 'Escalation Start Event (non-interrupting)',
35066 actionName: 'replace-with-non-interrupting-escalation-start',
35067 className: 'bpmn-icon-start-event-non-interrupting-escalation',
35068 target: {
35069 type: 'bpmn:StartEvent',
35070 eventDefinitionType: 'bpmn:EscalationEventDefinition',
35071 isInterrupting: false
35072 }
35073 }
35074 ];
35075
35076 var SEQUENCE_FLOW = [
35077 {
35078 label: 'Sequence Flow',
35079 actionName: 'replace-with-sequence-flow',
35080 className: 'bpmn-icon-connection'
35081 },
35082 {
35083 label: 'Default Flow',
35084 actionName: 'replace-with-default-flow',
35085 className: 'bpmn-icon-default-flow'
35086 },
35087 {
35088 label: 'Conditional Flow',
35089 actionName: 'replace-with-conditional-flow',
35090 className: 'bpmn-icon-conditional-flow'
35091 }
35092 ];
35093
35094 var PARTICIPANT = [
35095 {
35096 label: 'Expanded Pool',
35097 actionName: 'replace-with-expanded-pool',
35098 className: 'bpmn-icon-participant',
35099 target: {
35100 type: 'bpmn:Participant',
35101 isExpanded: true
35102 }
35103 },
35104 {
35105 label: function(element) {
35106 var label = 'Empty Pool';
35107
35108 if (element.children && element.children.length) {
35109 label += ' (removes content)';
35110 }
35111
35112 return label;
35113 },
35114 actionName: 'replace-with-collapsed-pool',
35115
35116 // TODO(@janstuemmel): maybe design new icon
35117 className: 'bpmn-icon-lane',
35118 target: {
35119 type: 'bpmn:Participant',
35120 isExpanded: false
35121 }
35122 }
35123 ];
35124
35125 /**
35126 * This module is an element agnostic replace menu provider for the popup menu.
35127 */
35128 function ReplaceMenuProvider(
35129 bpmnFactory, popupMenu, modeling, moddle,
35130 bpmnReplace, rules, translate) {
35131
35132 this._bpmnFactory = bpmnFactory;
35133 this._popupMenu = popupMenu;
35134 this._modeling = modeling;
35135 this._moddle = moddle;
35136 this._bpmnReplace = bpmnReplace;
35137 this._rules = rules;
35138 this._translate = translate;
35139
35140 this.register();
35141 }
35142
35143 ReplaceMenuProvider.$inject = [
35144 'bpmnFactory',
35145 'popupMenu',
35146 'modeling',
35147 'moddle',
35148 'bpmnReplace',
35149 'rules',
35150 'translate'
35151 ];
35152
35153
35154 /**
35155 * Register replace menu provider in the popup menu
35156 */
35157 ReplaceMenuProvider.prototype.register = function() {
35158 this._popupMenu.registerProvider('bpmn-replace', this);
35159 };
35160
35161
35162 /**
35163 * Get all entries from replaceOptions for the given element and apply filters
35164 * on them. Get for example only elements, which are different from the current one.
35165 *
35166 * @param {djs.model.Base} element
35167 *
35168 * @return {Array<Object>} a list of menu entry items
35169 */
35170 ReplaceMenuProvider.prototype.getEntries = function(element) {
35171
35172 var businessObject = element.businessObject;
35173
35174 var rules = this._rules;
35175
35176 var entries;
35177
35178 if (!rules.allowed('shape.replace', { element: element })) {
35179 return [];
35180 }
35181
35182 var differentType = isDifferentType(element);
35183
35184 if (is$1(businessObject, 'bpmn:DataObjectReference')) {
35185 return this._createEntries(element, DATA_OBJECT_REFERENCE);
35186 }
35187
35188 if (is$1(businessObject, 'bpmn:DataStoreReference')) {
35189 return this._createEntries(element, DATA_STORE_REFERENCE);
35190 }
35191
35192 // start events outside sub processes
35193 if (is$1(businessObject, 'bpmn:StartEvent') && !is$1(businessObject.$parent, 'bpmn:SubProcess')) {
35194
35195 entries = filter(START_EVENT, differentType);
35196
35197 return this._createEntries(element, entries);
35198 }
35199
35200 // expanded/collapsed pools
35201 if (is$1(businessObject, 'bpmn:Participant')) {
35202
35203 entries = filter(PARTICIPANT, function(entry) {
35204 return isExpanded(businessObject) !== entry.target.isExpanded;
35205 });
35206
35207 return this._createEntries(element, entries);
35208 }
35209
35210 // start events inside event sub processes
35211 if (is$1(businessObject, 'bpmn:StartEvent') && isEventSubProcess(businessObject.$parent)) {
35212 entries = filter(EVENT_SUB_PROCESS_START_EVENT, function(entry) {
35213
35214 var target = entry.target;
35215
35216 var isInterrupting = target.isInterrupting !== false;
35217
35218 var isInterruptingEqual = getBusinessObject(element).isInterrupting === isInterrupting;
35219
35220 // filters elements which types and event definition are equal but have have different interrupting types
35221 return differentType(entry) || !differentType(entry) && !isInterruptingEqual;
35222
35223 });
35224
35225 return this._createEntries(element, entries);
35226 }
35227
35228 // start events inside sub processes
35229 if (is$1(businessObject, 'bpmn:StartEvent') && !isEventSubProcess(businessObject.$parent)
35230 && is$1(businessObject.$parent, 'bpmn:SubProcess')) {
35231 entries = filter(START_EVENT_SUB_PROCESS, differentType);
35232
35233 return this._createEntries(element, entries);
35234 }
35235
35236 // end events
35237 if (is$1(businessObject, 'bpmn:EndEvent')) {
35238
35239 entries = filter(END_EVENT, function(entry) {
35240 var target = entry.target;
35241
35242 // hide cancel end events outside transactions
35243 if (target.eventDefinitionType == 'bpmn:CancelEventDefinition' && !is$1(businessObject.$parent, 'bpmn:Transaction')) {
35244 return false;
35245 }
35246
35247 return differentType(entry);
35248 });
35249
35250 return this._createEntries(element, entries);
35251 }
35252
35253 // boundary events
35254 if (is$1(businessObject, 'bpmn:BoundaryEvent')) {
35255
35256 entries = filter(BOUNDARY_EVENT, function(entry) {
35257
35258 var target = entry.target;
35259
35260 if (target.eventDefinitionType == 'bpmn:CancelEventDefinition' &&
35261 !is$1(businessObject.attachedToRef, 'bpmn:Transaction')) {
35262 return false;
35263 }
35264 var cancelActivity = target.cancelActivity !== false;
35265
35266 var isCancelActivityEqual = businessObject.cancelActivity == cancelActivity;
35267
35268 return differentType(entry) || !differentType(entry) && !isCancelActivityEqual;
35269 });
35270
35271 return this._createEntries(element, entries);
35272 }
35273
35274 // intermediate events
35275 if (is$1(businessObject, 'bpmn:IntermediateCatchEvent') ||
35276 is$1(businessObject, 'bpmn:IntermediateThrowEvent')) {
35277
35278 entries = filter(INTERMEDIATE_EVENT, differentType);
35279
35280 return this._createEntries(element, entries);
35281 }
35282
35283 // gateways
35284 if (is$1(businessObject, 'bpmn:Gateway')) {
35285
35286 entries = filter(GATEWAY, differentType);
35287
35288 return this._createEntries(element, entries);
35289 }
35290
35291 // transactions
35292 if (is$1(businessObject, 'bpmn:Transaction')) {
35293
35294 entries = filter(TRANSACTION, differentType);
35295
35296 return this._createEntries(element, entries);
35297 }
35298
35299 // expanded event sub processes
35300 if (isEventSubProcess(businessObject) && isExpanded(businessObject)) {
35301
35302 entries = filter(EVENT_SUB_PROCESS, differentType);
35303
35304 return this._createEntries(element, entries);
35305 }
35306
35307 // expanded sub processes
35308 if (is$1(businessObject, 'bpmn:SubProcess') && isExpanded(businessObject)) {
35309
35310 entries = filter(SUBPROCESS_EXPANDED, differentType);
35311
35312 return this._createEntries(element, entries);
35313 }
35314
35315 // collapsed ad hoc sub processes
35316 if (is$1(businessObject, 'bpmn:AdHocSubProcess') && !isExpanded(businessObject)) {
35317
35318 entries = filter(TASK, function(entry) {
35319
35320 var target = entry.target;
35321
35322 var isTargetSubProcess = target.type === 'bpmn:SubProcess';
35323
35324 var isTargetExpanded = target.isExpanded === true;
35325
35326 return isDifferentType(element) && (!isTargetSubProcess || isTargetExpanded);
35327 });
35328
35329 return this._createEntries(element, entries);
35330 }
35331
35332 // sequence flows
35333 if (is$1(businessObject, 'bpmn:SequenceFlow')) {
35334 return this._createSequenceFlowEntries(element, SEQUENCE_FLOW);
35335 }
35336
35337 // flow nodes
35338 if (is$1(businessObject, 'bpmn:FlowNode')) {
35339 entries = filter(TASK, differentType);
35340
35341 // collapsed SubProcess can not be replaced with itself
35342 if (is$1(businessObject, 'bpmn:SubProcess') && !isExpanded(businessObject)) {
35343 entries = filter(entries, function(entry) {
35344 return entry.label !== 'Sub Process (collapsed)';
35345 });
35346 }
35347
35348 return this._createEntries(element, entries);
35349 }
35350
35351 return [];
35352 };
35353
35354
35355 /**
35356 * Get a list of header items for the given element. This includes buttons
35357 * for multi instance markers and for the ad hoc marker.
35358 *
35359 * @param {djs.model.Base} element
35360 *
35361 * @return {Array<Object>} a list of menu entry items
35362 */
35363 ReplaceMenuProvider.prototype.getHeaderEntries = function(element) {
35364
35365 var headerEntries = [];
35366
35367 if (is$1(element, 'bpmn:Activity') && !isEventSubProcess(element)) {
35368 headerEntries = headerEntries.concat(this._getLoopEntries(element));
35369 }
35370
35371 if (is$1(element, 'bpmn:DataObjectReference')) {
35372 headerEntries = headerEntries.concat(this._getDataObjectIsCollection(element));
35373 }
35374
35375 if (is$1(element, 'bpmn:Participant')) {
35376 headerEntries = headerEntries.concat(this._getParticipantMultiplicity(element));
35377 }
35378
35379 if (is$1(element, 'bpmn:SubProcess') &&
35380 !is$1(element, 'bpmn:Transaction') &&
35381 !isEventSubProcess(element)) {
35382 headerEntries.push(this._getAdHocEntry(element));
35383 }
35384
35385 return headerEntries;
35386 };
35387
35388
35389 /**
35390 * Creates an array of menu entry objects for a given element and filters the replaceOptions
35391 * according to a filter function.
35392 *
35393 * @param {djs.model.Base} element
35394 * @param {Object} replaceOptions
35395 *
35396 * @return {Array<Object>} a list of menu items
35397 */
35398 ReplaceMenuProvider.prototype._createEntries = function(element, replaceOptions) {
35399 var menuEntries = [];
35400
35401 var self = this;
35402
35403 forEach(replaceOptions, function(definition) {
35404 var entry = self._createMenuEntry(definition, element);
35405
35406 menuEntries.push(entry);
35407 });
35408
35409 return menuEntries;
35410 };
35411
35412 /**
35413 * Creates an array of menu entry objects for a given sequence flow.
35414 *
35415 * @param {djs.model.Base} element
35416 * @param {Object} replaceOptions
35417
35418 * @return {Array<Object>} a list of menu items
35419 */
35420 ReplaceMenuProvider.prototype._createSequenceFlowEntries = function(element, replaceOptions) {
35421
35422 var businessObject = getBusinessObject(element);
35423
35424 var menuEntries = [];
35425
35426 var modeling = this._modeling,
35427 moddle = this._moddle;
35428
35429 var self = this;
35430
35431 forEach(replaceOptions, function(entry) {
35432
35433 switch (entry.actionName) {
35434 case 'replace-with-default-flow':
35435 if (businessObject.sourceRef.default !== businessObject &&
35436 (is$1(businessObject.sourceRef, 'bpmn:ExclusiveGateway') ||
35437 is$1(businessObject.sourceRef, 'bpmn:InclusiveGateway') ||
35438 is$1(businessObject.sourceRef, 'bpmn:ComplexGateway') ||
35439 is$1(businessObject.sourceRef, 'bpmn:Activity'))) {
35440
35441 menuEntries.push(self._createMenuEntry(entry, element, function() {
35442 modeling.updateProperties(element.source, { default: businessObject });
35443 }));
35444 }
35445 break;
35446 case 'replace-with-conditional-flow':
35447 if (!businessObject.conditionExpression && is$1(businessObject.sourceRef, 'bpmn:Activity')) {
35448
35449 menuEntries.push(self._createMenuEntry(entry, element, function() {
35450 var conditionExpression = moddle.create('bpmn:FormalExpression', { body: '' });
35451
35452 modeling.updateProperties(element, { conditionExpression: conditionExpression });
35453 }));
35454 }
35455 break;
35456 default:
35457
35458 // default flows
35459 if (is$1(businessObject.sourceRef, 'bpmn:Activity') && businessObject.conditionExpression) {
35460 return menuEntries.push(self._createMenuEntry(entry, element, function() {
35461 modeling.updateProperties(element, { conditionExpression: undefined });
35462 }));
35463 }
35464
35465 // conditional flows
35466 if ((is$1(businessObject.sourceRef, 'bpmn:ExclusiveGateway') ||
35467 is$1(businessObject.sourceRef, 'bpmn:InclusiveGateway') ||
35468 is$1(businessObject.sourceRef, 'bpmn:ComplexGateway') ||
35469 is$1(businessObject.sourceRef, 'bpmn:Activity')) &&
35470 businessObject.sourceRef.default === businessObject) {
35471
35472 return menuEntries.push(self._createMenuEntry(entry, element, function() {
35473 modeling.updateProperties(element.source, { default: undefined });
35474 }));
35475 }
35476 }
35477 });
35478
35479 return menuEntries;
35480 };
35481
35482
35483 /**
35484 * Creates and returns a single menu entry item.
35485 *
35486 * @param {Object} definition a single replace options definition object
35487 * @param {djs.model.Base} element
35488 * @param {Function} [action] an action callback function which gets called when
35489 * the menu entry is being triggered.
35490 *
35491 * @return {Object} menu entry item
35492 */
35493 ReplaceMenuProvider.prototype._createMenuEntry = function(definition, element, action) {
35494 var translate = this._translate;
35495 var replaceElement = this._bpmnReplace.replaceElement;
35496
35497 var replaceAction = function() {
35498 return replaceElement(element, definition.target);
35499 };
35500
35501 var label = definition.label;
35502 if (label && typeof label === 'function') {
35503 label = label(element);
35504 }
35505
35506 action = action || replaceAction;
35507
35508 var menuEntry = {
35509 label: translate(label),
35510 className: definition.className,
35511 id: definition.actionName,
35512 action: action
35513 };
35514
35515 return menuEntry;
35516 };
35517
35518 /**
35519 * Get a list of menu items containing buttons for multi instance markers
35520 *
35521 * @param {djs.model.Base} element
35522 *
35523 * @return {Array<Object>} a list of menu items
35524 */
35525 ReplaceMenuProvider.prototype._getLoopEntries = function(element) {
35526
35527 var self = this;
35528 var translate = this._translate;
35529
35530 function toggleLoopEntry(event, entry) {
35531 var loopCharacteristics;
35532
35533 if (entry.active) {
35534 loopCharacteristics = undefined;
35535 } else {
35536 loopCharacteristics = self._moddle.create(entry.options.loopCharacteristics);
35537
35538 if (entry.options.isSequential) {
35539 loopCharacteristics.isSequential = entry.options.isSequential;
35540 }
35541 }
35542 self._modeling.updateProperties(element, { loopCharacteristics: loopCharacteristics });
35543 }
35544
35545 var businessObject = getBusinessObject(element),
35546 loopCharacteristics = businessObject.loopCharacteristics;
35547
35548 var isSequential,
35549 isLoop,
35550 isParallel;
35551
35552 if (loopCharacteristics) {
35553 isSequential = loopCharacteristics.isSequential;
35554 isLoop = loopCharacteristics.isSequential === undefined;
35555 isParallel = loopCharacteristics.isSequential !== undefined && !loopCharacteristics.isSequential;
35556 }
35557
35558
35559 var loopEntries = [
35560 {
35561 id: 'toggle-parallel-mi',
35562 className: 'bpmn-icon-parallel-mi-marker',
35563 title: translate('Parallel Multi Instance'),
35564 active: isParallel,
35565 action: toggleLoopEntry,
35566 options: {
35567 loopCharacteristics: 'bpmn:MultiInstanceLoopCharacteristics',
35568 isSequential: false
35569 }
35570 },
35571 {
35572 id: 'toggle-sequential-mi',
35573 className: 'bpmn-icon-sequential-mi-marker',
35574 title: translate('Sequential Multi Instance'),
35575 active: isSequential,
35576 action: toggleLoopEntry,
35577 options: {
35578 loopCharacteristics: 'bpmn:MultiInstanceLoopCharacteristics',
35579 isSequential: true
35580 }
35581 },
35582 {
35583 id: 'toggle-loop',
35584 className: 'bpmn-icon-loop-marker',
35585 title: translate('Loop'),
35586 active: isLoop,
35587 action: toggleLoopEntry,
35588 options: {
35589 loopCharacteristics: 'bpmn:StandardLoopCharacteristics'
35590 }
35591 }
35592 ];
35593 return loopEntries;
35594 };
35595
35596 /**
35597 * Get a list of menu items containing a button for the collection marker
35598 *
35599 * @param {djs.model.Base} element
35600 *
35601 * @return {Array<Object>} a list of menu items
35602 */
35603 ReplaceMenuProvider.prototype._getDataObjectIsCollection = function(element) {
35604
35605 var self = this;
35606 var translate = this._translate;
35607
35608 function toggleIsCollection(event, entry) {
35609 self._modeling.updateModdleProperties(
35610 element,
35611 dataObject,
35612 { isCollection: !entry.active });
35613 }
35614
35615 var dataObject = element.businessObject.dataObjectRef,
35616 isCollection = dataObject.isCollection;
35617
35618 var dataObjectEntries = [
35619 {
35620 id: 'toggle-is-collection',
35621 className: 'bpmn-icon-parallel-mi-marker',
35622 title: translate('Collection'),
35623 active: isCollection,
35624 action: toggleIsCollection,
35625 }
35626 ];
35627 return dataObjectEntries;
35628 };
35629
35630 /**
35631 * Get a list of menu items containing a button for the participant multiplicity marker
35632 *
35633 * @param {djs.model.Base} element
35634 *
35635 * @return {Array<Object>} a list of menu items
35636 */
35637 ReplaceMenuProvider.prototype._getParticipantMultiplicity = function(element) {
35638
35639 var self = this;
35640 var bpmnFactory = this._bpmnFactory;
35641 var translate = this._translate;
35642
35643 function toggleParticipantMultiplicity(event, entry) {
35644 var isActive = entry.active;
35645 var participantMultiplicity;
35646
35647 if (!isActive) {
35648 participantMultiplicity = bpmnFactory.create('bpmn:ParticipantMultiplicity');
35649 }
35650
35651 self._modeling.updateProperties(
35652 element,
35653 { participantMultiplicity: participantMultiplicity });
35654 }
35655
35656 var participantMultiplicity = element.businessObject.participantMultiplicity;
35657
35658 var participantEntries = [
35659 {
35660 id: 'toggle-participant-multiplicity',
35661 className: 'bpmn-icon-parallel-mi-marker',
35662 title: translate('Participant Multiplicity'),
35663 active: !!participantMultiplicity,
35664 action: toggleParticipantMultiplicity,
35665 }
35666 ];
35667 return participantEntries;
35668 };
35669
35670
35671 /**
35672 * Get the menu items containing a button for the ad hoc marker
35673 *
35674 * @param {djs.model.Base} element
35675 *
35676 * @return {Object} a menu item
35677 */
35678 ReplaceMenuProvider.prototype._getAdHocEntry = function(element) {
35679 var translate = this._translate;
35680 var businessObject = getBusinessObject(element);
35681
35682 var isAdHoc = is$1(businessObject, 'bpmn:AdHocSubProcess');
35683
35684 var replaceElement = this._bpmnReplace.replaceElement;
35685
35686 var adHocEntry = {
35687 id: 'toggle-adhoc',
35688 className: 'bpmn-icon-ad-hoc-marker',
35689 title: translate('Ad-hoc'),
35690 active: isAdHoc,
35691 action: function(event, entry) {
35692 if (isAdHoc) {
35693 return replaceElement(element, { type: 'bpmn:SubProcess' }, {
35694 autoResize: false,
35695 layoutConnection: false
35696 });
35697 } else {
35698 return replaceElement(element, { type: 'bpmn:AdHocSubProcess' }, {
35699 autoResize: false,
35700 layoutConnection: false
35701 });
35702 }
35703 }
35704 };
35705
35706 return adHocEntry;
35707 };
35708
35709 var PopupMenuModule = {
35710 __depends__: [
35711 PopupMenuModule$1,
35712 ReplaceModule
35713 ],
35714 __init__: [ 'replaceMenuProvider' ],
35715 replaceMenuProvider: [ 'type', ReplaceMenuProvider ]
35716 };
35717
35718 var max$4 = Math.max,
35719 min$2 = Math.min;
35720
35721 var DEFAULT_CHILD_BOX_PADDING = 20;
35722
35723
35724 /**
35725 * Substract a TRBL from another
35726 *
35727 * @param {TRBL} trblA
35728 * @param {TRBL} trblB
35729 *
35730 * @return {TRBL}
35731 */
35732 function substractTRBL(trblA, trblB) {
35733 return {
35734 top: trblA.top - trblB.top,
35735 right: trblA.right - trblB.right,
35736 bottom: trblA.bottom - trblB.bottom,
35737 left: trblA.left - trblB.left
35738 };
35739 }
35740
35741 /**
35742 * Resize the given bounds by the specified delta from a given anchor point.
35743 *
35744 * @param {Bounds} bounds the bounding box that should be resized
35745 * @param {string} direction in which the element is resized (nw, ne, se, sw)
35746 * @param {Point} delta of the resize operation
35747 *
35748 * @return {Bounds} resized bounding box
35749 */
35750 function resizeBounds$1(bounds, direction, delta) {
35751 var dx = delta.x,
35752 dy = delta.y;
35753
35754 var newBounds = {
35755 x: bounds.x,
35756 y: bounds.y,
35757 width: bounds.width,
35758 height: bounds.height
35759 };
35760
35761 if (direction.indexOf('n') !== -1) {
35762 newBounds.y = bounds.y + dy;
35763 newBounds.height = bounds.height - dy;
35764 } else if (direction.indexOf('s') !== -1) {
35765 newBounds.height = bounds.height + dy;
35766 }
35767
35768 if (direction.indexOf('e') !== -1) {
35769 newBounds.width = bounds.width + dx;
35770 } else if (direction.indexOf('w') !== -1) {
35771 newBounds.x = bounds.x + dx;
35772 newBounds.width = bounds.width - dx;
35773 }
35774
35775 return newBounds;
35776 }
35777
35778
35779 /**
35780 * Resize the given bounds by applying the passed
35781 * { top, right, bottom, left } delta.
35782 *
35783 * @param {Bounds} bounds
35784 * @param {TRBL} trblResize
35785 *
35786 * @return {Bounds}
35787 */
35788 function resizeTRBL(bounds, resize) {
35789 return {
35790 x: bounds.x + (resize.left || 0),
35791 y: bounds.y + (resize.top || 0),
35792 width: bounds.width - (resize.left || 0) + (resize.right || 0),
35793 height: bounds.height - (resize.top || 0) + (resize.bottom || 0)
35794 };
35795 }
35796
35797
35798 function applyConstraints(attr, trbl, resizeConstraints) {
35799
35800 var value = trbl[attr],
35801 minValue = resizeConstraints.min && resizeConstraints.min[attr],
35802 maxValue = resizeConstraints.max && resizeConstraints.max[attr];
35803
35804 if (isNumber(minValue)) {
35805 value = (/top|left/.test(attr) ? min$2 : max$4)(value, minValue);
35806 }
35807
35808 if (isNumber(maxValue)) {
35809 value = (/top|left/.test(attr) ? max$4 : min$2)(value, maxValue);
35810 }
35811
35812 return value;
35813 }
35814
35815 function ensureConstraints$1(currentBounds, resizeConstraints) {
35816
35817 if (!resizeConstraints) {
35818 return currentBounds;
35819 }
35820
35821 var currentTrbl = asTRBL(currentBounds);
35822
35823 return asBounds({
35824 top: applyConstraints('top', currentTrbl, resizeConstraints),
35825 right: applyConstraints('right', currentTrbl, resizeConstraints),
35826 bottom: applyConstraints('bottom', currentTrbl, resizeConstraints),
35827 left: applyConstraints('left', currentTrbl, resizeConstraints)
35828 });
35829 }
35830
35831
35832 function getMinResizeBounds(direction, currentBounds, minDimensions, childrenBounds) {
35833
35834 var currentBox = asTRBL(currentBounds);
35835
35836 var minBox = {
35837 top: /n/.test(direction) ? currentBox.bottom - minDimensions.height : currentBox.top,
35838 left: /w/.test(direction) ? currentBox.right - minDimensions.width : currentBox.left,
35839 bottom: /s/.test(direction) ? currentBox.top + minDimensions.height : currentBox.bottom,
35840 right: /e/.test(direction) ? currentBox.left + minDimensions.width : currentBox.right
35841 };
35842
35843 var childrenBox = childrenBounds ? asTRBL(childrenBounds) : minBox;
35844
35845 var combinedBox = {
35846 top: min$2(minBox.top, childrenBox.top),
35847 left: min$2(minBox.left, childrenBox.left),
35848 bottom: max$4(minBox.bottom, childrenBox.bottom),
35849 right: max$4(minBox.right, childrenBox.right)
35850 };
35851
35852 return asBounds(combinedBox);
35853 }
35854
35855 function asPadding(mayBePadding, defaultValue) {
35856 if (typeof mayBePadding !== 'undefined') {
35857 return mayBePadding;
35858 } else {
35859 return DEFAULT_CHILD_BOX_PADDING;
35860 }
35861 }
35862
35863 function addPadding$1(bbox, padding) {
35864 var left, right, top, bottom;
35865
35866 if (typeof padding === 'object') {
35867 left = asPadding(padding.left);
35868 right = asPadding(padding.right);
35869 top = asPadding(padding.top);
35870 bottom = asPadding(padding.bottom);
35871 } else {
35872 left = right = top = bottom = asPadding(padding);
35873 }
35874
35875 return {
35876 x: bbox.x - left,
35877 y: bbox.y - top,
35878 width: bbox.width + left + right,
35879 height: bbox.height + top + bottom
35880 };
35881 }
35882
35883
35884 /**
35885 * Is the given element part of the resize
35886 * targets min boundary box?
35887 *
35888 * This is the default implementation which excludes
35889 * connections and labels.
35890 *
35891 * @param {djs.model.Base} element
35892 */
35893 function isBBoxChild(element) {
35894
35895 // exclude connections
35896 if (element.waypoints) {
35897 return false;
35898 }
35899
35900 // exclude labels
35901 if (element.type === 'label') {
35902 return false;
35903 }
35904
35905 return true;
35906 }
35907
35908 /**
35909 * Return children bounding computed from a shapes children
35910 * or a list of prefiltered children.
35911 *
35912 * @param {djs.model.Shape|Array<djs.model.Shape>} shapeOrChildren
35913 * @param {number|Object} padding
35914 *
35915 * @return {Bounds}
35916 */
35917 function computeChildrenBBox(shapeOrChildren, padding) {
35918
35919 var elements;
35920
35921 // compute based on shape
35922 if (shapeOrChildren.length === undefined) {
35923
35924 // grab all the children that are part of the
35925 // parents children box
35926 elements = filter(shapeOrChildren.children, isBBoxChild);
35927
35928 } else {
35929 elements = shapeOrChildren;
35930 }
35931
35932 if (elements.length) {
35933 return addPadding$1(getBBox(elements), padding);
35934 }
35935 }
35936
35937 var abs$4 = Math.abs;
35938
35939
35940 function getTRBLResize(oldBounds, newBounds) {
35941 return substractTRBL(asTRBL(newBounds), asTRBL(oldBounds));
35942 }
35943
35944
35945 var LANE_PARENTS = [
35946 'bpmn:Participant',
35947 'bpmn:Process',
35948 'bpmn:SubProcess'
35949 ];
35950
35951 var LANE_INDENTATION = 30;
35952
35953
35954 /**
35955 * Collect all lane shapes in the given paren
35956 *
35957 * @param {djs.model.Shape} shape
35958 * @param {Array<djs.model.Base>} [collectedShapes]
35959 *
35960 * @return {Array<djs.model.Base>}
35961 */
35962 function collectLanes(shape, collectedShapes) {
35963
35964 collectedShapes = collectedShapes || [];
35965
35966 shape.children.filter(function(s) {
35967 if (is$1(s, 'bpmn:Lane')) {
35968 collectLanes(s, collectedShapes);
35969
35970 collectedShapes.push(s);
35971 }
35972 });
35973
35974 return collectedShapes;
35975 }
35976
35977
35978 /**
35979 * Return the lane children of the given element.
35980 *
35981 * @param {djs.model.Shape} shape
35982 *
35983 * @return {Array<djs.model.Shape>}
35984 */
35985 function getChildLanes(shape) {
35986 return shape.children.filter(function(c) {
35987 return is$1(c, 'bpmn:Lane');
35988 });
35989 }
35990
35991
35992 /**
35993 * Return the root element containing the given lane shape
35994 *
35995 * @param {djs.model.Shape} shape
35996 *
35997 * @return {djs.model.Shape}
35998 */
35999 function getLanesRoot(shape) {
36000 return getParent(shape, LANE_PARENTS) || shape;
36001 }
36002
36003
36004 /**
36005 * Compute the required resize operations for lanes
36006 * adjacent to the given shape, assuming it will be
36007 * resized to the given new bounds.
36008 *
36009 * @param {djs.model.Shape} shape
36010 * @param {Bounds} newBounds
36011 *
36012 * @return {Array<Object>}
36013 */
36014 function computeLanesResize(shape, newBounds) {
36015
36016 var rootElement = getLanesRoot(shape);
36017
36018 var initialShapes = is$1(rootElement, 'bpmn:Process') ? [] : [ rootElement ];
36019
36020 var allLanes = collectLanes(rootElement, initialShapes),
36021 shapeTrbl = asTRBL(shape),
36022 shapeNewTrbl = asTRBL(newBounds),
36023 trblResize = getTRBLResize(shape, newBounds),
36024 resizeNeeded = [];
36025
36026 allLanes.forEach(function(other) {
36027
36028 if (other === shape) {
36029 return;
36030 }
36031
36032 var topResize = 0,
36033 rightResize = trblResize.right,
36034 bottomResize = 0,
36035 leftResize = trblResize.left;
36036
36037 var otherTrbl = asTRBL(other);
36038
36039 if (trblResize.top) {
36040 if (abs$4(otherTrbl.bottom - shapeTrbl.top) < 10) {
36041 bottomResize = shapeNewTrbl.top - otherTrbl.bottom;
36042 }
36043
36044 if (abs$4(otherTrbl.top - shapeTrbl.top) < 5) {
36045 topResize = shapeNewTrbl.top - otherTrbl.top;
36046 }
36047 }
36048
36049 if (trblResize.bottom) {
36050 if (abs$4(otherTrbl.top - shapeTrbl.bottom) < 10) {
36051 topResize = shapeNewTrbl.bottom - otherTrbl.top;
36052 }
36053
36054 if (abs$4(otherTrbl.bottom - shapeTrbl.bottom) < 5) {
36055 bottomResize = shapeNewTrbl.bottom - otherTrbl.bottom;
36056 }
36057 }
36058
36059 if (topResize || rightResize || bottomResize || leftResize) {
36060
36061 resizeNeeded.push({
36062 shape: other,
36063 newBounds: resizeTRBL(other, {
36064 top: topResize,
36065 right: rightResize,
36066 bottom: bottomResize,
36067 left: leftResize
36068 })
36069 });
36070 }
36071
36072 });
36073
36074 return resizeNeeded;
36075 }
36076
36077 /**
36078 * A provider for BPMN 2.0 elements context pad
36079 */
36080 function ContextPadProvider(
36081 config, injector, eventBus,
36082 contextPad, modeling, elementFactory,
36083 connect, create, popupMenu,
36084 canvas, rules, translate) {
36085
36086 config = config || {};
36087
36088 contextPad.registerProvider(this);
36089
36090 this._contextPad = contextPad;
36091
36092 this._modeling = modeling;
36093
36094 this._elementFactory = elementFactory;
36095 this._connect = connect;
36096 this._create = create;
36097 this._popupMenu = popupMenu;
36098 this._canvas = canvas;
36099 this._rules = rules;
36100 this._translate = translate;
36101
36102 if (config.autoPlace !== false) {
36103 this._autoPlace = injector.get('autoPlace', false);
36104 }
36105
36106 eventBus.on('create.end', 250, function(event) {
36107 var context = event.context,
36108 shape = context.shape;
36109
36110 if (!hasPrimaryModifier(event) || !contextPad.isOpen(shape)) {
36111 return;
36112 }
36113
36114 var entries = contextPad.getEntries(shape);
36115
36116 if (entries.replace) {
36117 entries.replace.action.click(event, shape);
36118 }
36119 });
36120 }
36121
36122 ContextPadProvider.$inject = [
36123 'config.contextPad',
36124 'injector',
36125 'eventBus',
36126 'contextPad',
36127 'modeling',
36128 'elementFactory',
36129 'connect',
36130 'create',
36131 'popupMenu',
36132 'canvas',
36133 'rules',
36134 'translate'
36135 ];
36136
36137
36138 ContextPadProvider.prototype.getContextPadEntries = function(element) {
36139
36140 var contextPad = this._contextPad,
36141 modeling = this._modeling,
36142
36143 elementFactory = this._elementFactory,
36144 connect = this._connect,
36145 create = this._create,
36146 popupMenu = this._popupMenu,
36147 canvas = this._canvas,
36148 rules = this._rules,
36149 autoPlace = this._autoPlace,
36150 translate = this._translate;
36151
36152 var actions = {};
36153
36154 if (element.type === 'label') {
36155 return actions;
36156 }
36157
36158 var businessObject = element.businessObject;
36159
36160 function startConnect(event, element) {
36161 connect.start(event, element);
36162 }
36163
36164 function removeElement(e) {
36165 modeling.removeElements([ element ]);
36166 }
36167
36168 function getReplaceMenuPosition(element) {
36169
36170 var Y_OFFSET = 5;
36171
36172 var diagramContainer = canvas.getContainer(),
36173 pad = contextPad.getPad(element).html;
36174
36175 var diagramRect = diagramContainer.getBoundingClientRect(),
36176 padRect = pad.getBoundingClientRect();
36177
36178 var top = padRect.top - diagramRect.top;
36179 var left = padRect.left - diagramRect.left;
36180
36181 var pos = {
36182 x: left,
36183 y: top + padRect.height + Y_OFFSET
36184 };
36185
36186 return pos;
36187 }
36188
36189
36190 /**
36191 * Create an append action
36192 *
36193 * @param {string} type
36194 * @param {string} className
36195 * @param {string} [title]
36196 * @param {Object} [options]
36197 *
36198 * @return {Object} descriptor
36199 */
36200 function appendAction(type, className, title, options) {
36201
36202 if (typeof title !== 'string') {
36203 options = title;
36204 title = translate('Append {type}', { type: type.replace(/^bpmn:/, '') });
36205 }
36206
36207 function appendStart(event, element) {
36208
36209 var shape = elementFactory.createShape(assign({ type: type }, options));
36210 create.start(event, shape, {
36211 source: element
36212 });
36213 }
36214
36215
36216 var append = autoPlace ? function(event, element) {
36217 var shape = elementFactory.createShape(assign({ type: type }, options));
36218
36219 autoPlace.append(element, shape);
36220 } : appendStart;
36221
36222
36223 return {
36224 group: 'model',
36225 className: className,
36226 title: title,
36227 action: {
36228 dragstart: appendStart,
36229 click: append
36230 }
36231 };
36232 }
36233
36234 function splitLaneHandler(count) {
36235
36236 return function(event, element) {
36237
36238 // actual split
36239 modeling.splitLane(element, count);
36240
36241 // refresh context pad after split to
36242 // get rid of split icons
36243 contextPad.open(element, true);
36244 };
36245 }
36246
36247
36248 if (isAny(businessObject, [ 'bpmn:Lane', 'bpmn:Participant' ]) && isExpanded(businessObject)) {
36249
36250 var childLanes = getChildLanes(element);
36251
36252 assign(actions, {
36253 'lane-insert-above': {
36254 group: 'lane-insert-above',
36255 className: 'bpmn-icon-lane-insert-above',
36256 title: translate('Add Lane above'),
36257 action: {
36258 click: function(event, element) {
36259 modeling.addLane(element, 'top');
36260 }
36261 }
36262 }
36263 });
36264
36265 if (childLanes.length < 2) {
36266
36267 if (element.height >= 120) {
36268 assign(actions, {
36269 'lane-divide-two': {
36270 group: 'lane-divide',
36271 className: 'bpmn-icon-lane-divide-two',
36272 title: translate('Divide into two Lanes'),
36273 action: {
36274 click: splitLaneHandler(2)
36275 }
36276 }
36277 });
36278 }
36279
36280 if (element.height >= 180) {
36281 assign(actions, {
36282 'lane-divide-three': {
36283 group: 'lane-divide',
36284 className: 'bpmn-icon-lane-divide-three',
36285 title: translate('Divide into three Lanes'),
36286 action: {
36287 click: splitLaneHandler(3)
36288 }
36289 }
36290 });
36291 }
36292 }
36293
36294 assign(actions, {
36295 'lane-insert-below': {
36296 group: 'lane-insert-below',
36297 className: 'bpmn-icon-lane-insert-below',
36298 title: translate('Add Lane below'),
36299 action: {
36300 click: function(event, element) {
36301 modeling.addLane(element, 'bottom');
36302 }
36303 }
36304 }
36305 });
36306
36307 }
36308
36309 if (is$1(businessObject, 'bpmn:FlowNode')) {
36310
36311 if (is$1(businessObject, 'bpmn:EventBasedGateway')) {
36312
36313 assign(actions, {
36314 'append.receive-task': appendAction(
36315 'bpmn:ReceiveTask',
36316 'bpmn-icon-receive-task',
36317 translate('Append ReceiveTask')
36318 ),
36319 'append.message-intermediate-event': appendAction(
36320 'bpmn:IntermediateCatchEvent',
36321 'bpmn-icon-intermediate-event-catch-message',
36322 translate('Append MessageIntermediateCatchEvent'),
36323 { eventDefinitionType: 'bpmn:MessageEventDefinition' }
36324 ),
36325 'append.timer-intermediate-event': appendAction(
36326 'bpmn:IntermediateCatchEvent',
36327 'bpmn-icon-intermediate-event-catch-timer',
36328 translate('Append TimerIntermediateCatchEvent'),
36329 { eventDefinitionType: 'bpmn:TimerEventDefinition' }
36330 ),
36331 'append.condition-intermediate-event': appendAction(
36332 'bpmn:IntermediateCatchEvent',
36333 'bpmn-icon-intermediate-event-catch-condition',
36334 translate('Append ConditionIntermediateCatchEvent'),
36335 { eventDefinitionType: 'bpmn:ConditionalEventDefinition' }
36336 ),
36337 'append.signal-intermediate-event': appendAction(
36338 'bpmn:IntermediateCatchEvent',
36339 'bpmn-icon-intermediate-event-catch-signal',
36340 translate('Append SignalIntermediateCatchEvent'),
36341 { eventDefinitionType: 'bpmn:SignalEventDefinition' }
36342 )
36343 });
36344 } else
36345
36346 if (isEventType(businessObject, 'bpmn:BoundaryEvent', 'bpmn:CompensateEventDefinition')) {
36347
36348 assign(actions, {
36349 'append.compensation-activity':
36350 appendAction(
36351 'bpmn:Task',
36352 'bpmn-icon-task',
36353 translate('Append compensation activity'),
36354 {
36355 isForCompensation: true
36356 }
36357 )
36358 });
36359 } else
36360
36361 if (!is$1(businessObject, 'bpmn:EndEvent') &&
36362 !businessObject.isForCompensation &&
36363 !isEventType(businessObject, 'bpmn:IntermediateThrowEvent', 'bpmn:LinkEventDefinition') &&
36364 !isEventSubProcess(businessObject)) {
36365
36366 assign(actions, {
36367 'append.end-event': appendAction(
36368 'bpmn:EndEvent',
36369 'bpmn-icon-end-event-none',
36370 translate('Append EndEvent')
36371 ),
36372 'append.gateway': appendAction(
36373 'bpmn:ExclusiveGateway',
36374 'bpmn-icon-gateway-none',
36375 translate('Append Gateway')
36376 ),
36377 'append.append-task': appendAction(
36378 'bpmn:Task',
36379 'bpmn-icon-task',
36380 translate('Append Task')
36381 ),
36382 'append.intermediate-event': appendAction(
36383 'bpmn:IntermediateThrowEvent',
36384 'bpmn-icon-intermediate-event-none',
36385 translate('Append Intermediate/Boundary Event')
36386 )
36387 });
36388 }
36389 }
36390
36391 if (!popupMenu.isEmpty(element, 'bpmn-replace')) {
36392
36393 // Replace menu entry
36394 assign(actions, {
36395 'replace': {
36396 group: 'edit',
36397 className: 'bpmn-icon-screw-wrench',
36398 title: translate('Change type'),
36399 action: {
36400 click: function(event, element) {
36401
36402 var position = assign(getReplaceMenuPosition(element), {
36403 cursor: { x: event.x, y: event.y }
36404 });
36405
36406 popupMenu.open(element, 'bpmn-replace', position);
36407 }
36408 }
36409 }
36410 });
36411 }
36412
36413 if (
36414 isAny(businessObject, [
36415 'bpmn:FlowNode',
36416 'bpmn:InteractionNode',
36417 'bpmn:DataObjectReference',
36418 'bpmn:DataStoreReference',
36419 ])
36420 ) {
36421 assign(actions, {
36422 'append.text-annotation': appendAction(
36423 'bpmn:TextAnnotation',
36424 'bpmn-icon-text-annotation'
36425 ),
36426
36427 'connect': {
36428 group: 'connect',
36429 className: 'bpmn-icon-connection-multi',
36430 title: translate(
36431 'Connect using ' +
36432 (businessObject.isForCompensation
36433 ? ''
36434 : 'Sequence/MessageFlow or ') +
36435 'Association'
36436 ),
36437 action: {
36438 click: startConnect,
36439 dragstart: startConnect,
36440 },
36441 },
36442 });
36443 }
36444
36445 if (is$1(businessObject, 'bpmn:TextAnnotation')) {
36446 assign(actions, {
36447 'connect': {
36448 group: 'connect',
36449 className: 'bpmn-icon-connection-multi',
36450 title: translate('Connect using Association'),
36451 action: {
36452 click: startConnect,
36453 dragstart: startConnect,
36454 },
36455 },
36456 });
36457 }
36458
36459 if (isAny(businessObject, [ 'bpmn:DataObjectReference', 'bpmn:DataStoreReference' ])) {
36460 assign(actions, {
36461 'connect': {
36462 group: 'connect',
36463 className: 'bpmn-icon-connection-multi',
36464 title: translate('Connect using DataInputAssociation'),
36465 action: {
36466 click: startConnect,
36467 dragstart: startConnect
36468 }
36469 }
36470 });
36471 }
36472
36473 if (is$1(businessObject, 'bpmn:Group')) {
36474 assign(actions, {
36475 'append.text-annotation': appendAction('bpmn:TextAnnotation', 'bpmn-icon-text-annotation')
36476 });
36477 }
36478
36479 // delete element entry, only show if allowed by rules
36480 var deleteAllowed = rules.allowed('elements.delete', { elements: [ element ] });
36481
36482 if (isArray$2(deleteAllowed)) {
36483
36484 // was the element returned as a deletion candidate?
36485 deleteAllowed = deleteAllowed[0] === element;
36486 }
36487
36488 if (deleteAllowed) {
36489 assign(actions, {
36490 'delete': {
36491 group: 'edit',
36492 className: 'bpmn-icon-trash',
36493 title: translate('Remove'),
36494 action: {
36495 click: removeElement
36496 }
36497 }
36498 });
36499 }
36500
36501 return actions;
36502 };
36503
36504
36505 // helpers /////////
36506
36507 function isEventType(eventBo, type, definition) {
36508
36509 var isType = eventBo.$instanceOf(type);
36510 var isDefinition = false;
36511
36512 var definitions = eventBo.eventDefinitions || [];
36513 forEach(definitions, function(def) {
36514 if (def.$type === definition) {
36515 isDefinition = true;
36516 }
36517 });
36518
36519 return isType && isDefinition;
36520 }
36521
36522 var ContextPadModule = {
36523 __depends__: [
36524 DirectEditingModule,
36525 ContextPadModule$1,
36526 SelectionModule,
36527 ConnectModule,
36528 CreateModule,
36529 PopupMenuModule
36530 ],
36531 __init__: [ 'contextPadProvider' ],
36532 contextPadProvider: [ 'type', ContextPadProvider ]
36533 };
36534
36535 var AXIS_DIMENSIONS = {
36536 horizontal: [ 'x', 'width' ],
36537 vertical: [ 'y', 'height' ]
36538 };
36539
36540 var THRESHOLD = 5;
36541
36542
36543 /**
36544 * Groups and filters elements and then trigger even distribution.
36545 */
36546 function DistributeElements$1(modeling) {
36547 this._modeling = modeling;
36548
36549 this._filters = [];
36550
36551 // register filter for filtering big elements
36552 this.registerFilter(function(elements, axis, dimension) {
36553 var elementsSize = 0,
36554 numOfShapes = 0,
36555 avgDimension;
36556
36557 forEach(elements, function(element) {
36558 if (element.waypoints || element.labelTarget) {
36559 return;
36560 }
36561
36562 elementsSize += element[dimension];
36563
36564 numOfShapes += 1;
36565 });
36566
36567 avgDimension = Math.round(elementsSize / numOfShapes);
36568
36569 return filter(elements, function(element) {
36570 return element[dimension] < (avgDimension + 50);
36571 });
36572 });
36573
36574 }
36575
36576 DistributeElements$1.$inject = [ 'modeling' ];
36577
36578
36579 /**
36580 * Registers filter functions that allow external parties to filter
36581 * out certain elements.
36582 *
36583 * @param {Function} filterFn
36584 */
36585 DistributeElements$1.prototype.registerFilter = function(filterFn) {
36586 if (typeof filterFn !== 'function') {
36587 throw new Error('the filter has to be a function');
36588 }
36589
36590 this._filters.push(filterFn);
36591 };
36592
36593 /**
36594 * Distributes the elements with a given orientation
36595 *
36596 * @param {Array} elements
36597 * @param {string} orientation
36598 */
36599 DistributeElements$1.prototype.trigger = function(elements, orientation) {
36600 var modeling = this._modeling;
36601
36602 var groups,
36603 distributableElements;
36604
36605 if (elements.length < 3) {
36606 return;
36607 }
36608
36609 this._setOrientation(orientation);
36610
36611 distributableElements = this._filterElements(elements);
36612
36613 groups = this._createGroups(distributableElements);
36614
36615 // nothing to distribute
36616 if (groups.length <= 2) {
36617 return;
36618 }
36619
36620 modeling.distributeElements(groups, this._axis, this._dimension);
36621
36622 return groups;
36623 };
36624
36625 /**
36626 * Filters the elements with provided filters by external parties
36627 *
36628 * @param {Array[Elements]} elements
36629 *
36630 * @return {Array[Elements]}
36631 */
36632 DistributeElements$1.prototype._filterElements = function(elements) {
36633 var filters = this._filters,
36634 axis = this._axis,
36635 dimension = this._dimension,
36636 distributableElements = [].concat(elements);
36637
36638 if (!filters.length) {
36639 return elements;
36640 }
36641
36642 forEach(filters, function(filterFn) {
36643 distributableElements = filterFn(distributableElements, axis, dimension);
36644 });
36645
36646 return distributableElements;
36647 };
36648
36649
36650 /**
36651 * Create range (min, max) groups. Also tries to group elements
36652 * together that share the same range.
36653 *
36654 * @example
36655 * var distributableElements = [
36656 * {
36657 * range: {
36658 * min: 100,
36659 * max: 200
36660 * },
36661 * elements: [ { id: 'shape1', .. }]
36662 * }
36663 * ]
36664 *
36665 * @param {Array} elements
36666 *
36667 * @return {Array[Objects]}
36668 */
36669 DistributeElements$1.prototype._createGroups = function(elements) {
36670 var rangeGroups = [],
36671 self = this,
36672 axis = this._axis,
36673 dimension = this._dimension;
36674
36675 if (!axis) {
36676 throw new Error('must have a defined "axis" and "dimension"');
36677 }
36678
36679 // sort by 'left->right' or 'top->bottom'
36680 var sortedElements = sortBy(elements, axis);
36681
36682 forEach(sortedElements, function(element, idx) {
36683 var elementRange = self._findRange(element, axis, dimension),
36684 range;
36685
36686 var previous = rangeGroups[rangeGroups.length - 1];
36687
36688 if (previous && self._hasIntersection(previous.range, elementRange)) {
36689 rangeGroups[rangeGroups.length - 1].elements.push(element);
36690 } else {
36691 range = { range: elementRange, elements: [ element ] };
36692
36693 rangeGroups.push(range);
36694 }
36695 });
36696
36697 return rangeGroups;
36698 };
36699
36700
36701 /**
36702 * Maps a direction to the according axis and dimension
36703 *
36704 * @param {string} direction 'horizontal' or 'vertical'
36705 */
36706 DistributeElements$1.prototype._setOrientation = function(direction) {
36707 var orientation = AXIS_DIMENSIONS[direction];
36708
36709 this._axis = orientation[0];
36710 this._dimension = orientation[1];
36711 };
36712
36713
36714 /**
36715 * Checks if the two ranges intercept each other
36716 *
36717 * @param {Object} rangeA {min, max}
36718 * @param {Object} rangeB {min, max}
36719 *
36720 * @return {boolean}
36721 */
36722 DistributeElements$1.prototype._hasIntersection = function(rangeA, rangeB) {
36723 return Math.max(rangeA.min, rangeA.max) >= Math.min(rangeB.min, rangeB.max) &&
36724 Math.min(rangeA.min, rangeA.max) <= Math.max(rangeB.min, rangeB.max);
36725 };
36726
36727
36728 /**
36729 * Returns the min and max values for an element
36730 *
36731 * @param {Bounds} element
36732 * @param {string} axis
36733 * @param {string} dimension
36734 *
36735 * @return {{ min: number, max: number }}
36736 */
36737 DistributeElements$1.prototype._findRange = function(element) {
36738 var axis = element[this._axis],
36739 dimension = element[this._dimension];
36740
36741 return {
36742 min: axis + THRESHOLD,
36743 max: axis + dimension - THRESHOLD
36744 };
36745 };
36746
36747 var DistributeElementsModule$1 = {
36748 __init__: [ 'distributeElements' ],
36749 distributeElements: [ 'type', DistributeElements$1 ]
36750 };
36751
36752 /**
36753 * Registers element exclude filters for elements that
36754 * currently do not support distribution.
36755 */
36756 function BpmnDistributeElements(distributeElements) {
36757
36758 distributeElements.registerFilter(function(elements) {
36759 return filter(elements, function(element) {
36760 var cannotDistribute = isAny(element, [
36761 'bpmn:Association',
36762 'bpmn:BoundaryEvent',
36763 'bpmn:DataInputAssociation',
36764 'bpmn:DataOutputAssociation',
36765 'bpmn:Lane',
36766 'bpmn:MessageFlow',
36767 'bpmn:Participant',
36768 'bpmn:SequenceFlow',
36769 'bpmn:TextAnnotation'
36770 ]);
36771
36772 return !(element.labelTarget || cannotDistribute);
36773 });
36774 });
36775 }
36776
36777 BpmnDistributeElements.$inject = [ 'distributeElements' ];
36778
36779 var DistributeElementsModule = {
36780 __depends__: [
36781 DistributeElementsModule$1
36782 ],
36783 __init__: [ 'bpmnDistributeElements' ],
36784 bpmnDistributeElements: [ 'type', BpmnDistributeElements ]
36785 };
36786
36787 var NOT_REGISTERED_ERROR = 'is not a registered action',
36788 IS_REGISTERED_ERROR = 'is already registered';
36789
36790
36791 /**
36792 * An interface that provides access to modeling actions by decoupling
36793 * the one who requests the action to be triggered and the trigger itself.
36794 *
36795 * It's possible to add new actions by registering them with ´registerAction´
36796 * and likewise unregister existing ones with ´unregisterAction´.
36797 *
36798 *
36799 * ## Life-Cycle and configuration
36800 *
36801 * The editor actions will wait for diagram initialization before
36802 * registering default actions _and_ firing an `editorActions.init` event.
36803 *
36804 * Interested parties may listen to the `editorActions.init` event with
36805 * low priority to check, which actions got registered. Other components
36806 * may use the event to register their own actions via `registerAction`.
36807 *
36808 * @param {EventBus} eventBus
36809 * @param {Injector} injector
36810 */
36811 function EditorActions(eventBus, injector) {
36812
36813 // initialize actions
36814 this._actions = {};
36815
36816 var self = this;
36817
36818 eventBus.on('diagram.init', function() {
36819
36820 // all diagram modules got loaded; check which ones
36821 // are available and register the respective default actions
36822 self._registerDefaultActions(injector);
36823
36824 // ask interested parties to register available editor
36825 // actions on diagram initialization
36826 eventBus.fire('editorActions.init', {
36827 editorActions: self
36828 });
36829 });
36830
36831 }
36832
36833 EditorActions.$inject = [
36834 'eventBus',
36835 'injector'
36836 ];
36837
36838 /**
36839 * Register default actions.
36840 *
36841 * @param {Injector} injector
36842 */
36843 EditorActions.prototype._registerDefaultActions = function(injector) {
36844
36845 // (1) retrieve optional components to integrate with
36846
36847 var commandStack = injector.get('commandStack', false);
36848 var modeling = injector.get('modeling', false);
36849 var selection = injector.get('selection', false);
36850 var zoomScroll = injector.get('zoomScroll', false);
36851 var copyPaste = injector.get('copyPaste', false);
36852 var canvas = injector.get('canvas', false);
36853 var rules = injector.get('rules', false);
36854 var keyboardMove = injector.get('keyboardMove', false);
36855 var keyboardMoveSelection = injector.get('keyboardMoveSelection', false);
36856
36857 // (2) check components and register actions
36858
36859 if (commandStack) {
36860 this.register('undo', function() {
36861 commandStack.undo();
36862 });
36863
36864 this.register('redo', function() {
36865 commandStack.redo();
36866 });
36867 }
36868
36869 if (copyPaste && selection) {
36870 this.register('copy', function() {
36871 var selectedElements = selection.get();
36872
36873 copyPaste.copy(selectedElements);
36874 });
36875 }
36876
36877 if (copyPaste) {
36878 this.register('paste', function() {
36879 copyPaste.paste();
36880 });
36881 }
36882
36883 if (zoomScroll) {
36884 this.register('stepZoom', function(opts) {
36885 zoomScroll.stepZoom(opts.value);
36886 });
36887 }
36888
36889 if (canvas) {
36890 this.register('zoom', function(opts) {
36891 canvas.zoom(opts.value);
36892 });
36893 }
36894
36895 if (modeling && selection && rules) {
36896 this.register('removeSelection', function() {
36897
36898 var selectedElements = selection.get();
36899
36900 if (!selectedElements.length) {
36901 return;
36902 }
36903
36904 var allowed = rules.allowed('elements.delete', { elements: selectedElements }),
36905 removableElements;
36906
36907 if (allowed === false) {
36908 return;
36909 }
36910 else if (isArray$2(allowed)) {
36911 removableElements = allowed;
36912 }
36913 else {
36914 removableElements = selectedElements;
36915 }
36916
36917 if (removableElements.length) {
36918 modeling.removeElements(removableElements.slice());
36919 }
36920 });
36921 }
36922
36923 if (keyboardMove) {
36924 this.register('moveCanvas', function(opts) {
36925 keyboardMove.moveCanvas(opts);
36926 });
36927 }
36928
36929 if (keyboardMoveSelection) {
36930 this.register('moveSelection', function(opts) {
36931 keyboardMoveSelection.moveSelection(opts.direction, opts.accelerated);
36932 });
36933 }
36934
36935 };
36936
36937
36938 /**
36939 * Triggers a registered action
36940 *
36941 * @param {string} action
36942 * @param {Object} opts
36943 *
36944 * @return {Unknown} Returns what the registered listener returns
36945 */
36946 EditorActions.prototype.trigger = function(action, opts) {
36947 if (!this._actions[action]) {
36948 throw error(action, NOT_REGISTERED_ERROR);
36949 }
36950
36951 return this._actions[action](opts);
36952 };
36953
36954
36955 /**
36956 * Registers a collections of actions.
36957 * The key of the object will be the name of the action.
36958 *
36959 * @example
36960 * ´´´
36961 * var actions = {
36962 * spaceTool: function() {
36963 * spaceTool.activateSelection();
36964 * },
36965 * lassoTool: function() {
36966 * lassoTool.activateSelection();
36967 * }
36968 * ];
36969 *
36970 * editorActions.register(actions);
36971 *
36972 * editorActions.isRegistered('spaceTool'); // true
36973 * ´´´
36974 *
36975 * @param {Object} actions
36976 */
36977 EditorActions.prototype.register = function(actions, listener) {
36978 var self = this;
36979
36980 if (typeof actions === 'string') {
36981 return this._registerAction(actions, listener);
36982 }
36983
36984 forEach(actions, function(listener, action) {
36985 self._registerAction(action, listener);
36986 });
36987 };
36988
36989 /**
36990 * Registers a listener to an action key
36991 *
36992 * @param {string} action
36993 * @param {Function} listener
36994 */
36995 EditorActions.prototype._registerAction = function(action, listener) {
36996 if (this.isRegistered(action)) {
36997 throw error(action, IS_REGISTERED_ERROR);
36998 }
36999
37000 this._actions[action] = listener;
37001 };
37002
37003 /**
37004 * Unregister an existing action
37005 *
37006 * @param {string} action
37007 */
37008 EditorActions.prototype.unregister = function(action) {
37009 if (!this.isRegistered(action)) {
37010 throw error(action, NOT_REGISTERED_ERROR);
37011 }
37012
37013 this._actions[action] = undefined;
37014 };
37015
37016 /**
37017 * Returns the number of actions that are currently registered
37018 *
37019 * @return {number}
37020 */
37021 EditorActions.prototype.getActions = function() {
37022 return Object.keys(this._actions);
37023 };
37024
37025 /**
37026 * Checks wether the given action is registered
37027 *
37028 * @param {string} action
37029 *
37030 * @return {boolean}
37031 */
37032 EditorActions.prototype.isRegistered = function(action) {
37033 return !!this._actions[action];
37034 };
37035
37036
37037 function error(action, message) {
37038 return new Error(action + ' ' + message);
37039 }
37040
37041 var EditorActionsModule$1 = {
37042 __init__: [ 'editorActions' ],
37043 editorActions: [ 'type', EditorActions ]
37044 };
37045
37046 /**
37047 * Registers and executes BPMN specific editor actions.
37048 *
37049 * @param {Injector} injector
37050 */
37051 function BpmnEditorActions(injector) {
37052 injector.invoke(EditorActions, this);
37053 }
37054
37055 inherits$1(BpmnEditorActions, EditorActions);
37056
37057 BpmnEditorActions.$inject = [
37058 'injector'
37059 ];
37060
37061 /**
37062 * Register default actions.
37063 *
37064 * @param {Injector} injector
37065 */
37066 BpmnEditorActions.prototype._registerDefaultActions = function(injector) {
37067
37068 // (0) invoke super method
37069
37070 EditorActions.prototype._registerDefaultActions.call(this, injector);
37071
37072 // (1) retrieve optional components to integrate with
37073
37074 var canvas = injector.get('canvas', false);
37075 var elementRegistry = injector.get('elementRegistry', false);
37076 var selection = injector.get('selection', false);
37077 var spaceTool = injector.get('spaceTool', false);
37078 var lassoTool = injector.get('lassoTool', false);
37079 var handTool = injector.get('handTool', false);
37080 var globalConnect = injector.get('globalConnect', false);
37081 var distributeElements = injector.get('distributeElements', false);
37082 var alignElements = injector.get('alignElements', false);
37083 var directEditing = injector.get('directEditing', false);
37084 var searchPad = injector.get('searchPad', false);
37085 var modeling = injector.get('modeling', false);
37086
37087 // (2) check components and register actions
37088
37089 if (canvas && elementRegistry && selection) {
37090 this._registerAction('selectElements', function() {
37091
37092 // select all elements except for the invisible
37093 // root element
37094 var rootElement = canvas.getRootElement();
37095
37096 var elements = elementRegistry.filter(function(element) {
37097 return element !== rootElement;
37098 });
37099
37100 selection.select(elements);
37101
37102 return elements;
37103 });
37104 }
37105
37106 if (spaceTool) {
37107 this._registerAction('spaceTool', function() {
37108 spaceTool.toggle();
37109 });
37110 }
37111
37112 if (lassoTool) {
37113 this._registerAction('lassoTool', function() {
37114 lassoTool.toggle();
37115 });
37116 }
37117
37118 if (handTool) {
37119 this._registerAction('handTool', function() {
37120 handTool.toggle();
37121 });
37122 }
37123
37124 if (globalConnect) {
37125 this._registerAction('globalConnectTool', function() {
37126 globalConnect.toggle();
37127 });
37128 }
37129
37130 if (selection && distributeElements) {
37131 this._registerAction('distributeElements', function(opts) {
37132 var currentSelection = selection.get(),
37133 type = opts.type;
37134
37135 if (currentSelection.length) {
37136 distributeElements.trigger(currentSelection, type);
37137 }
37138 });
37139 }
37140
37141 if (selection && alignElements) {
37142 this._registerAction('alignElements', function(opts) {
37143 var currentSelection = selection.get(),
37144 aligneableElements = [],
37145 type = opts.type;
37146
37147 if (currentSelection.length) {
37148 aligneableElements = filter(currentSelection, function(element) {
37149 return !is$1(element, 'bpmn:Lane');
37150 });
37151
37152 alignElements.trigger(aligneableElements, type);
37153 }
37154 });
37155 }
37156
37157 if (selection && modeling) {
37158 this._registerAction('setColor', function(opts) {
37159 var currentSelection = selection.get();
37160
37161 if (currentSelection.length) {
37162 modeling.setColor(currentSelection, opts);
37163 }
37164 });
37165 }
37166
37167 if (selection && directEditing) {
37168 this._registerAction('directEditing', function() {
37169 var currentSelection = selection.get();
37170
37171 if (currentSelection.length) {
37172 directEditing.activate(currentSelection[0]);
37173 }
37174 });
37175 }
37176
37177 if (searchPad) {
37178 this._registerAction('find', function() {
37179 searchPad.toggle();
37180 });
37181 }
37182
37183 if (canvas && modeling) {
37184 this._registerAction('moveToOrigin', function() {
37185 var rootElement = canvas.getRootElement(),
37186 boundingBox,
37187 elements;
37188
37189 if (is$1(rootElement, 'bpmn:Collaboration')) {
37190 elements = elementRegistry.filter(function(element) {
37191 return is$1(element.parent, 'bpmn:Collaboration');
37192 });
37193 } else {
37194 elements = elementRegistry.filter(function(element) {
37195 return element !== rootElement && !is$1(element.parent, 'bpmn:SubProcess');
37196 });
37197 }
37198
37199 boundingBox = getBBox(elements);
37200
37201 modeling.moveElements(
37202 elements,
37203 { x: -boundingBox.x, y: -boundingBox.y },
37204 rootElement
37205 );
37206 });
37207 }
37208
37209 };
37210
37211 var EditorActionsModule = {
37212 __depends__: [
37213 EditorActionsModule$1
37214 ],
37215 editorActions: [ 'type', BpmnEditorActions ]
37216 };
37217
37218 function BpmnGridSnapping(eventBus) {
37219 eventBus.on([
37220 'create.init',
37221 'shape.move.init'
37222 ], function(event) {
37223 var context = event.context,
37224 shape = event.shape;
37225
37226 if (isAny(shape, [
37227 'bpmn:Participant',
37228 'bpmn:SubProcess',
37229 'bpmn:TextAnnotation'
37230 ])) {
37231 if (!context.gridSnappingContext) {
37232 context.gridSnappingContext = {};
37233 }
37234
37235 context.gridSnappingContext.snapLocation = 'top-left';
37236 }
37237 });
37238 }
37239
37240 BpmnGridSnapping.$inject = [ 'eventBus' ];
37241
37242 var SPACING = 10;
37243
37244 function quantize(value, quantum, fn) {
37245 if (!fn) {
37246 fn = 'round';
37247 }
37248
37249 return Math[ fn ](value / quantum) * quantum;
37250 }
37251
37252 var LOWER_PRIORITY = 1200;
37253 var LOW_PRIORITY$e = 800;
37254
37255 /**
37256 * Basic grid snapping that covers connecting, creating, moving, resizing shapes, moving bendpoints
37257 * and connection segments.
37258 */
37259 function GridSnapping(elementRegistry, eventBus, config) {
37260
37261 var active = !config || config.active !== false;
37262
37263 this._eventBus = eventBus;
37264
37265 var self = this;
37266
37267 eventBus.on('diagram.init', LOW_PRIORITY$e, function() {
37268 self.setActive(active);
37269 });
37270
37271 eventBus.on([
37272 'create.move',
37273 'create.end',
37274 'bendpoint.move.move',
37275 'bendpoint.move.end',
37276 'connect.move',
37277 'connect.end',
37278 'connectionSegment.move.move',
37279 'connectionSegment.move.end',
37280 'resize.move',
37281 'resize.end',
37282 'shape.move.move',
37283 'shape.move.end'
37284 ], LOWER_PRIORITY, function(event) {
37285 var originalEvent = event.originalEvent;
37286
37287 if (!self.active || (originalEvent && isCmd(originalEvent))) {
37288 return;
37289 }
37290
37291 var context = event.context,
37292 gridSnappingContext = context.gridSnappingContext;
37293
37294 if (!gridSnappingContext) {
37295 gridSnappingContext = context.gridSnappingContext = {};
37296 }
37297
37298 [ 'x', 'y' ].forEach(function(axis) {
37299 var options = {};
37300
37301 // allow snapping with offset
37302 var snapOffset = getSnapOffset(event, axis, elementRegistry);
37303
37304 if (snapOffset) {
37305 options.offset = snapOffset;
37306 }
37307
37308 // allow snapping with min and max
37309 var snapConstraints = getSnapConstraints(event, axis);
37310
37311 if (snapConstraints) {
37312 assign(options, snapConstraints);
37313 }
37314
37315 if (!isSnapped(event, axis)) {
37316 self.snapEvent(event, axis, options);
37317 }
37318 });
37319 });
37320 }
37321
37322 /**
37323 * Snap an events x or y with optional min, max and offset.
37324 *
37325 * @param {Object} event
37326 * @param {string} axis
37327 * @param {number} [options.min]
37328 * @param {number} [options.max]
37329 * @param {number} [options.offset]
37330 */
37331 GridSnapping.prototype.snapEvent = function(event, axis, options) {
37332 var snappedValue = this.snapValue(event[ axis ], options);
37333
37334 setSnapped(event, axis, snappedValue);
37335 };
37336
37337 /**
37338 * Expose grid spacing for third parties (i.e. extensions).
37339 *
37340 * @return {number} spacing of grid dots
37341 */
37342 GridSnapping.prototype.getGridSpacing = function() {
37343 return SPACING;
37344 };
37345
37346 /**
37347 * Snap value with optional min, max and offset.
37348 *
37349 * @param {number} value
37350 * @param {Object} options
37351 * @param {number} [options.min]
37352 * @param {number} [options.max]
37353 * @param {number} [options.offset]
37354 */
37355 GridSnapping.prototype.snapValue = function(value, options) {
37356 var offset = 0;
37357
37358 if (options && options.offset) {
37359 offset = options.offset;
37360 }
37361
37362 value += offset;
37363
37364 value = quantize(value, SPACING);
37365
37366 var min, max;
37367
37368 if (options && options.min) {
37369 min = options.min;
37370
37371 if (isNumber(min)) {
37372 min = quantize(min + offset, SPACING, 'ceil');
37373
37374 value = Math.max(value, min);
37375 }
37376 }
37377
37378 if (options && options.max) {
37379 max = options.max;
37380
37381 if (isNumber(max)) {
37382 max = quantize(max + offset, SPACING, 'floor');
37383
37384 value = Math.min(value, max);
37385 }
37386 }
37387
37388 value -= offset;
37389
37390 return value;
37391 };
37392
37393 GridSnapping.prototype.isActive = function() {
37394 return this.active;
37395 };
37396
37397 GridSnapping.prototype.setActive = function(active) {
37398 this.active = active;
37399
37400 this._eventBus.fire('gridSnapping.toggle', { active: active });
37401 };
37402
37403 GridSnapping.prototype.toggleActive = function() {
37404 this.setActive(!this.active);
37405 };
37406
37407 GridSnapping.$inject = [
37408 'elementRegistry',
37409 'eventBus',
37410 'config.gridSnapping'
37411 ];
37412
37413 // helpers //////////
37414
37415 /**
37416 * Get minimum and maximum snap constraints.
37417 * Constraints are cached.
37418 *
37419 * @param {Object} event
37420 * @param {Object} event.context
37421 * @param {string} axis
37422 *
37423 * @returns {boolean|Object}
37424 */
37425 function getSnapConstraints(event, axis) {
37426 var context = event.context,
37427 createConstraints = context.createConstraints,
37428 resizeConstraints = context.resizeConstraints || {},
37429 gridSnappingContext = context.gridSnappingContext,
37430 snapConstraints = gridSnappingContext.snapConstraints;
37431
37432 // cache snap constraints
37433 if (snapConstraints && snapConstraints[ axis ]) {
37434 return snapConstraints[ axis ];
37435 }
37436
37437 if (!snapConstraints) {
37438 snapConstraints = gridSnappingContext.snapConstraints = {};
37439 }
37440
37441 if (!snapConstraints[ axis ]) {
37442 snapConstraints[ axis ] = {};
37443 }
37444
37445 var direction = context.direction;
37446
37447 // create
37448 if (createConstraints) {
37449 if (isHorizontal$3(axis)) {
37450 snapConstraints.x.min = createConstraints.left;
37451 snapConstraints.x.max = createConstraints.right;
37452 } else {
37453 snapConstraints.y.min = createConstraints.top;
37454 snapConstraints.y.max = createConstraints.bottom;
37455 }
37456 }
37457
37458 // resize
37459 var minResizeConstraints = resizeConstraints.min,
37460 maxResizeConstraints = resizeConstraints.max;
37461
37462 if (minResizeConstraints) {
37463 if (isHorizontal$3(axis)) {
37464
37465 if (isWest(direction)) {
37466 snapConstraints.x.max = minResizeConstraints.left;
37467 } else {
37468 snapConstraints.x.min = minResizeConstraints.right;
37469 }
37470
37471 } else {
37472
37473 if (isNorth(direction)) {
37474 snapConstraints.y.max = minResizeConstraints.top;
37475 } else {
37476 snapConstraints.y.min = minResizeConstraints.bottom;
37477 }
37478
37479 }
37480 }
37481
37482 if (maxResizeConstraints) {
37483 if (isHorizontal$3(axis)) {
37484
37485 if (isWest(direction)) {
37486 snapConstraints.x.min = maxResizeConstraints.left;
37487 } else {
37488 snapConstraints.x.max = maxResizeConstraints.right;
37489 }
37490
37491 } else {
37492
37493 if (isNorth(direction)) {
37494 snapConstraints.y.min = maxResizeConstraints.top;
37495 } else {
37496 snapConstraints.y.max = maxResizeConstraints.bottom;
37497 }
37498
37499 }
37500 }
37501
37502 return snapConstraints[ axis ];
37503 }
37504
37505 /**
37506 * Get snap offset.
37507 * Offset is cached.
37508 *
37509 * @param {Object} event
37510 * @param {string} axis
37511 * @param {ElementRegistry} elementRegistry
37512 *
37513 * @returns {number}
37514 */
37515 function getSnapOffset(event, axis, elementRegistry) {
37516 var context = event.context,
37517 shape = event.shape,
37518 gridSnappingContext = context.gridSnappingContext,
37519 snapLocation = gridSnappingContext.snapLocation,
37520 snapOffset = gridSnappingContext.snapOffset;
37521
37522 // cache snap offset
37523 if (snapOffset && isNumber(snapOffset[ axis ])) {
37524 return snapOffset[ axis ];
37525 }
37526
37527 if (!snapOffset) {
37528 snapOffset = gridSnappingContext.snapOffset = {};
37529 }
37530
37531 if (!isNumber(snapOffset[ axis ])) {
37532 snapOffset[ axis ] = 0;
37533 }
37534
37535 if (!shape) {
37536 return snapOffset[ axis ];
37537 }
37538
37539 if (!elementRegistry.get(shape.id)) {
37540
37541 if (isHorizontal$3(axis)) {
37542 snapOffset[ axis ] += shape[ axis ] + shape.width / 2;
37543 } else {
37544 snapOffset[ axis ] += shape[ axis ] + shape.height / 2;
37545 }
37546 }
37547
37548 if (!snapLocation) {
37549 return snapOffset[ axis ];
37550 }
37551
37552 if (axis === 'x') {
37553 if (/left/.test(snapLocation)) {
37554 snapOffset[ axis ] -= shape.width / 2;
37555 } else if (/right/.test(snapLocation)) {
37556 snapOffset[ axis ] += shape.width / 2;
37557 }
37558 } else {
37559 if (/top/.test(snapLocation)) {
37560 snapOffset[ axis ] -= shape.height / 2;
37561 } else if (/bottom/.test(snapLocation)) {
37562 snapOffset[ axis ] += shape.height / 2;
37563 }
37564 }
37565
37566 return snapOffset[ axis ];
37567 }
37568
37569 function isHorizontal$3(axis) {
37570 return axis === 'x';
37571 }
37572
37573 function isNorth(direction) {
37574 return direction.indexOf('n') !== -1;
37575 }
37576
37577 function isWest(direction) {
37578 return direction.indexOf('w') !== -1;
37579 }
37580
37581 /**
37582 * Integrates resizing with grid snapping.
37583 */
37584 function ResizeBehavior$1(eventBus, gridSnapping) {
37585 CommandInterceptor.call(this, eventBus);
37586
37587 this._gridSnapping = gridSnapping;
37588
37589 var self = this;
37590
37591 this.preExecute('shape.resize', function(event) {
37592 var context = event.context,
37593 hints = context.hints || {},
37594 autoResize = hints.autoResize;
37595
37596 if (!autoResize) {
37597 return;
37598 }
37599
37600 var shape = context.shape,
37601 newBounds = context.newBounds;
37602
37603 if (isString(autoResize)) {
37604 context.newBounds = self.snapComplex(newBounds, autoResize);
37605 } else {
37606 context.newBounds = self.snapSimple(shape, newBounds);
37607 }
37608 });
37609 }
37610
37611 ResizeBehavior$1.$inject = [
37612 'eventBus',
37613 'gridSnapping',
37614 'modeling'
37615 ];
37616
37617 inherits$1(ResizeBehavior$1, CommandInterceptor);
37618
37619 /**
37620 * Snap width and height in relation to center.
37621 *
37622 * @param {djs.model.shape} shape
37623 * @param {Bounds} newBounds
37624 *
37625 * @returns {Bounds} Snapped bounds.
37626 */
37627 ResizeBehavior$1.prototype.snapSimple = function(shape, newBounds) {
37628 var gridSnapping = this._gridSnapping;
37629
37630 newBounds.width = gridSnapping.snapValue(newBounds.width, {
37631 min: newBounds.width
37632 });
37633
37634 newBounds.height = gridSnapping.snapValue(newBounds.height, {
37635 min: newBounds.height
37636 });
37637
37638 newBounds.x = shape.x + (shape.width / 2) - (newBounds.width / 2);
37639 newBounds.y = shape.y + (shape.height / 2) - (newBounds.height / 2);
37640
37641 return newBounds;
37642 };
37643
37644 /**
37645 * Snap x, y, width and height according to given directions.
37646 *
37647 * @param {Bounds} newBounds
37648 * @param {string} directions - Directions as {n|w|s|e}.
37649 *
37650 * @returns {Bounds} Snapped bounds.
37651 */
37652 ResizeBehavior$1.prototype.snapComplex = function(newBounds, directions) {
37653 if (/w|e/.test(directions)) {
37654 newBounds = this.snapHorizontally(newBounds, directions);
37655 }
37656
37657 if (/n|s/.test(directions)) {
37658 newBounds = this.snapVertically(newBounds, directions);
37659 }
37660
37661 return newBounds;
37662 };
37663
37664 /**
37665 * Snap in one or both directions horizontally.
37666 *
37667 * @param {Bounds} newBounds
37668 * @param {string} directions - Directions as {n|w|s|e}.
37669 *
37670 * @returns {Bounds} Snapped bounds.
37671 */
37672 ResizeBehavior$1.prototype.snapHorizontally = function(newBounds, directions) {
37673 var gridSnapping = this._gridSnapping,
37674 west = /w/.test(directions),
37675 east = /e/.test(directions);
37676
37677 var snappedNewBounds = {};
37678
37679 snappedNewBounds.width = gridSnapping.snapValue(newBounds.width, {
37680 min: newBounds.width
37681 });
37682
37683 if (east) {
37684
37685 // handle <we>
37686 if (west) {
37687 snappedNewBounds.x = gridSnapping.snapValue(newBounds.x, {
37688 max: newBounds.x
37689 });
37690
37691 snappedNewBounds.width += gridSnapping.snapValue(newBounds.x - snappedNewBounds.x, {
37692 min: newBounds.x - snappedNewBounds.x
37693 });
37694 }
37695
37696 // handle <e>
37697 else {
37698 newBounds.x = newBounds.x + newBounds.width - snappedNewBounds.width;
37699 }
37700 }
37701
37702 // assign snapped x and width
37703 assign(newBounds, snappedNewBounds);
37704
37705 return newBounds;
37706 };
37707
37708 /**
37709 * Snap in one or both directions vertically.
37710 *
37711 * @param {Bounds} newBounds
37712 * @param {string} directions - Directions as {n|w|s|e}.
37713 *
37714 * @returns {Bounds} Snapped bounds.
37715 */
37716 ResizeBehavior$1.prototype.snapVertically = function(newBounds, directions) {
37717 var gridSnapping = this._gridSnapping,
37718 north = /n/.test(directions),
37719 south = /s/.test(directions);
37720
37721 var snappedNewBounds = {};
37722
37723 snappedNewBounds.height = gridSnapping.snapValue(newBounds.height, {
37724 min: newBounds.height
37725 });
37726
37727 if (north) {
37728
37729 // handle <ns>
37730 if (south) {
37731 snappedNewBounds.y = gridSnapping.snapValue(newBounds.y, {
37732 max: newBounds.y
37733 });
37734
37735 snappedNewBounds.height += gridSnapping.snapValue(newBounds.y - snappedNewBounds.y, {
37736 min: newBounds.y - snappedNewBounds.y
37737 });
37738 }
37739
37740 // handle <n>
37741 else {
37742 newBounds.y = newBounds.y + newBounds.height - snappedNewBounds.height;
37743 }
37744 }
37745
37746 // assign snapped y and height
37747 assign(newBounds, snappedNewBounds);
37748
37749 return newBounds;
37750 };
37751
37752 var HIGH_PRIORITY$f = 2000;
37753
37754 /**
37755 * Integrates space tool with grid snapping.
37756 */
37757 function SpaceToolBehavior$1(eventBus, gridSnapping) {
37758 eventBus.on([
37759 'spaceTool.move',
37760 'spaceTool.end'
37761 ], HIGH_PRIORITY$f, function(event) {
37762 var context = event.context;
37763
37764 if (!context.initialized) {
37765 return;
37766 }
37767
37768 var axis = context.axis;
37769
37770 var snapped;
37771
37772 if (axis === 'x') {
37773
37774 // snap delta x to multiple of 10
37775 snapped = gridSnapping.snapValue(event.dx);
37776
37777 event.x = event.x + snapped - event.dx;
37778 event.dx = snapped;
37779 } else {
37780
37781 // snap delta y to multiple of 10
37782 snapped = gridSnapping.snapValue(event.dy);
37783
37784 event.y = event.y + snapped - event.dy;
37785 event.dy = snapped;
37786 }
37787 });
37788 }
37789
37790 SpaceToolBehavior$1.$inject = [
37791 'eventBus',
37792 'gridSnapping'
37793 ];
37794
37795 var GridSnappingBehaviorModule$1 = {
37796 __init__: [
37797 'gridSnappingResizeBehavior',
37798 'gridSnappingSpaceToolBehavior'
37799 ],
37800 gridSnappingResizeBehavior: [ 'type', ResizeBehavior$1 ],
37801 gridSnappingSpaceToolBehavior: [ 'type', SpaceToolBehavior$1 ]
37802 };
37803
37804 var GridSnappingModule$1 = {
37805 __depends__: [ GridSnappingBehaviorModule$1 ],
37806 __init__: [ 'gridSnapping' ],
37807 gridSnapping: [ 'type', GridSnapping ]
37808 };
37809
37810 var HIGH_PRIORITY$e = 2000;
37811
37812
37813 function AutoPlaceBehavior(eventBus, gridSnapping) {
37814 eventBus.on('autoPlace', HIGH_PRIORITY$e, function(context) {
37815 var source = context.source,
37816 sourceMid = getMid(source),
37817 shape = context.shape;
37818
37819 var position = getNewShapePosition(source, shape);
37820
37821 [ 'x', 'y' ].forEach(function(axis) {
37822 var options = {};
37823
37824 // do not snap if x/y equal
37825 if (position[ axis ] === sourceMid[ axis ]) {
37826 return;
37827 }
37828
37829 if (position[ axis ] > sourceMid[ axis ]) {
37830 options.min = position[ axis ];
37831 } else {
37832 options.max = position[ axis ];
37833 }
37834
37835 if (is$1(shape, 'bpmn:TextAnnotation')) {
37836
37837 if (isHorizontal$2(axis)) {
37838 options.offset = -shape.width / 2;
37839 } else {
37840 options.offset = -shape.height / 2;
37841 }
37842
37843 }
37844
37845 position[ axis ] = gridSnapping.snapValue(position[ axis ], options);
37846
37847 });
37848
37849 // must be returned to be considered by auto place
37850 return position;
37851 });
37852 }
37853
37854 AutoPlaceBehavior.$inject = [
37855 'eventBus',
37856 'gridSnapping'
37857 ];
37858
37859 // helpers //////////
37860
37861 function isHorizontal$2(axis) {
37862 return axis === 'x';
37863 }
37864
37865 var HIGHER_PRIORITY$4 = 1750;
37866
37867
37868 function CreateParticipantBehavior$1(canvas, eventBus, gridSnapping) {
37869 eventBus.on([
37870 'create.start',
37871 'shape.move.start'
37872 ], HIGHER_PRIORITY$4, function(event) {
37873 var context = event.context,
37874 shape = context.shape,
37875 rootElement = canvas.getRootElement();
37876
37877 if (!is$1(shape, 'bpmn:Participant') ||
37878 !is$1(rootElement, 'bpmn:Process') ||
37879 !rootElement.children.length) {
37880 return;
37881 }
37882
37883 var createConstraints = context.createConstraints;
37884
37885 if (!createConstraints) {
37886 return;
37887 }
37888
37889 shape.width = gridSnapping.snapValue(shape.width, { min: shape.width });
37890 shape.height = gridSnapping.snapValue(shape.height, { min: shape.height });
37891 });
37892 }
37893
37894 CreateParticipantBehavior$1.$inject = [
37895 'canvas',
37896 'eventBus',
37897 'gridSnapping'
37898 ];
37899
37900 var HIGH_PRIORITY$d = 3000;
37901
37902
37903 /**
37904 * Snaps connections with Manhattan layout.
37905 */
37906 function LayoutConnectionBehavior(eventBus, gridSnapping, modeling) {
37907 CommandInterceptor.call(this, eventBus);
37908
37909 this._gridSnapping = gridSnapping;
37910
37911 var self = this;
37912
37913 this.postExecuted([
37914 'connection.create',
37915 'connection.layout'
37916 ], HIGH_PRIORITY$d, function(event) {
37917 var context = event.context,
37918 connection = context.connection,
37919 hints = context.hints || {},
37920 waypoints = connection.waypoints;
37921
37922 if (hints.connectionStart || hints.connectionEnd || hints.createElementsBehavior === false) {
37923 return;
37924 }
37925
37926 if (!hasMiddleSegments(waypoints)) {
37927 return;
37928 }
37929
37930 modeling.updateWaypoints(connection, self.snapMiddleSegments(waypoints));
37931 });
37932 }
37933
37934 LayoutConnectionBehavior.$inject = [
37935 'eventBus',
37936 'gridSnapping',
37937 'modeling'
37938 ];
37939
37940 inherits$1(LayoutConnectionBehavior, CommandInterceptor);
37941
37942 /**
37943 * Snap middle segments of a given connection.
37944 *
37945 * @param {Array<Point>} waypoints
37946 *
37947 * @returns {Array<Point>}
37948 */
37949 LayoutConnectionBehavior.prototype.snapMiddleSegments = function(waypoints) {
37950 var gridSnapping = this._gridSnapping,
37951 snapped;
37952
37953 waypoints = waypoints.slice();
37954
37955 for (var i = 1; i < waypoints.length - 2; i++) {
37956
37957 snapped = snapSegment(gridSnapping, waypoints[i], waypoints[i + 1]);
37958
37959 waypoints[i] = snapped[0];
37960 waypoints[i + 1] = snapped[1];
37961 }
37962
37963 return waypoints;
37964 };
37965
37966
37967 // helpers //////////
37968
37969 /**
37970 * Check whether a connection has a middle segments.
37971 *
37972 * @param {Array} waypoints
37973 *
37974 * @returns {boolean}
37975 */
37976 function hasMiddleSegments(waypoints) {
37977 return waypoints.length > 3;
37978 }
37979
37980 /**
37981 * Check whether an alignment is horizontal.
37982 *
37983 * @param {string} aligned
37984 *
37985 * @returns {boolean}
37986 */
37987 function horizontallyAligned(aligned) {
37988 return aligned === 'h';
37989 }
37990
37991 /**
37992 * Check whether an alignment is vertical.
37993 *
37994 * @param {string} aligned
37995 *
37996 * @returns {boolean}
37997 */
37998 function verticallyAligned(aligned) {
37999 return aligned === 'v';
38000 }
38001
38002 /**
38003 * Get middle segments from a given connection.
38004 *
38005 * @param {Array} waypoints
38006 *
38007 * @returns {Array}
38008 */
38009 function snapSegment(gridSnapping, segmentStart, segmentEnd) {
38010
38011 var aligned = pointsAligned(segmentStart, segmentEnd);
38012
38013 var snapped = {};
38014
38015 if (horizontallyAligned(aligned)) {
38016
38017 // snap horizontally
38018 snapped.y = gridSnapping.snapValue(segmentStart.y);
38019 }
38020
38021 if (verticallyAligned(aligned)) {
38022
38023 // snap vertically
38024 snapped.x = gridSnapping.snapValue(segmentStart.x);
38025 }
38026
38027 if ('x' in snapped || 'y' in snapped) {
38028 segmentStart = assign({}, segmentStart, snapped);
38029 segmentEnd = assign({}, segmentEnd, snapped);
38030 }
38031
38032 return [ segmentStart, segmentEnd ];
38033 }
38034
38035 var GridSnappingBehaviorModule = {
38036 __init__: [
38037 'gridSnappingAutoPlaceBehavior',
38038 'gridSnappingCreateParticipantBehavior',
38039 'gridSnappingLayoutConnectionBehavior',
38040 ],
38041 gridSnappingAutoPlaceBehavior: [ 'type', AutoPlaceBehavior ],
38042 gridSnappingCreateParticipantBehavior: [ 'type', CreateParticipantBehavior$1 ],
38043 gridSnappingLayoutConnectionBehavior: [ 'type', LayoutConnectionBehavior ]
38044 };
38045
38046 var GridSnappingModule = {
38047 __depends__: [
38048 GridSnappingModule$1,
38049 GridSnappingBehaviorModule
38050 ],
38051 __init__: [ 'bpmnGridSnapping' ],
38052 bpmnGridSnapping: [ 'type', BpmnGridSnapping ]
38053 };
38054
38055 var LABEL_WIDTH = 30,
38056 LABEL_HEIGHT = 30;
38057
38058
38059 /**
38060 * BPMN-specific hit zones and interaction fixes.
38061 *
38062 * @param {EventBus} eventBus
38063 * @param {InteractionEvents} interactionEvents
38064 */
38065 function BpmnInteractionEvents(eventBus, interactionEvents) {
38066
38067 this._interactionEvents = interactionEvents;
38068
38069 var self = this;
38070
38071 eventBus.on([
38072 'interactionEvents.createHit',
38073 'interactionEvents.updateHit'
38074 ], function(context) {
38075 var element = context.element,
38076 gfx = context.gfx;
38077
38078 if (is$1(element, 'bpmn:Lane')) {
38079 return self.createParticipantHit(element, gfx);
38080 } else
38081
38082 if (is$1(element, 'bpmn:Participant')) {
38083 if (isExpanded(element)) {
38084 return self.createParticipantHit(element, gfx);
38085 } else {
38086 return self.createDefaultHit(element, gfx);
38087 }
38088 } else
38089
38090 if (is$1(element, 'bpmn:SubProcess')) {
38091 if (isExpanded(element)) {
38092 return self.createSubProcessHit(element, gfx);
38093 } else {
38094 return self.createDefaultHit(element, gfx);
38095 }
38096 }
38097 });
38098
38099 }
38100
38101 BpmnInteractionEvents.$inject = [
38102 'eventBus',
38103 'interactionEvents'
38104 ];
38105
38106
38107 BpmnInteractionEvents.prototype.createDefaultHit = function(element, gfx) {
38108 this._interactionEvents.removeHits(gfx);
38109
38110 this._interactionEvents.createDefaultHit(element, gfx);
38111
38112 // indicate that we created a hit
38113 return true;
38114 };
38115
38116 BpmnInteractionEvents.prototype.createParticipantHit = function(element, gfx) {
38117
38118 // remove existing hits
38119 this._interactionEvents.removeHits(gfx);
38120
38121 // add outline hit
38122 this._interactionEvents.createBoxHit(gfx, 'click-stroke', {
38123 width: element.width,
38124 height: element.height
38125 });
38126
38127 // add label hit
38128 this._interactionEvents.createBoxHit(gfx, 'all', {
38129 width: LABEL_WIDTH,
38130 height: element.height
38131 });
38132
38133 // indicate that we created a hit
38134 return true;
38135 };
38136
38137 BpmnInteractionEvents.prototype.createSubProcessHit = function(element, gfx) {
38138
38139 // remove existing hits
38140 this._interactionEvents.removeHits(gfx);
38141
38142 // add outline hit
38143 this._interactionEvents.createBoxHit(gfx, 'click-stroke', {
38144 width: element.width,
38145 height: element.height
38146 });
38147
38148 // add label hit
38149 this._interactionEvents.createBoxHit(gfx, 'all', {
38150 width: element.width,
38151 height: LABEL_HEIGHT
38152 });
38153
38154 // indicate that we created a hit
38155 return true;
38156 };
38157
38158 var InteractionEventsModule = {
38159 __init__: [ 'bpmnInteractionEvents' ],
38160 bpmnInteractionEvents: [ 'type', BpmnInteractionEvents ]
38161 };
38162
38163 /**
38164 * BPMN 2.0 specific keyboard bindings.
38165 *
38166 * @param {Injector} injector
38167 */
38168 function BpmnKeyboardBindings(injector) {
38169 injector.invoke(KeyboardBindings, this);
38170 }
38171
38172 inherits$1(BpmnKeyboardBindings, KeyboardBindings);
38173
38174 BpmnKeyboardBindings.$inject = [
38175 'injector'
38176 ];
38177
38178
38179 /**
38180 * Register available keyboard bindings.
38181 *
38182 * @param {Keyboard} keyboard
38183 * @param {EditorActions} editorActions
38184 */
38185 BpmnKeyboardBindings.prototype.registerBindings = function(keyboard, editorActions) {
38186
38187 // inherit default bindings
38188 KeyboardBindings.prototype.registerBindings.call(this, keyboard, editorActions);
38189
38190 /**
38191 * Add keyboard binding if respective editor action
38192 * is registered.
38193 *
38194 * @param {string} action name
38195 * @param {Function} fn that implements the key binding
38196 */
38197 function addListener(action, fn) {
38198
38199 if (editorActions.isRegistered(action)) {
38200 keyboard.addListener(fn);
38201 }
38202 }
38203
38204 // select all elements
38205 // CTRL + A
38206 addListener('selectElements', function(context) {
38207
38208 var event = context.keyEvent;
38209
38210 if (keyboard.isKey(['a', 'A'], event) && keyboard.isCmd(event)) {
38211 editorActions.trigger('selectElements');
38212
38213 return true;
38214 }
38215 });
38216
38217 // search labels
38218 // CTRL + F
38219 addListener('find', function(context) {
38220
38221 var event = context.keyEvent;
38222
38223 if (keyboard.isKey(['f', 'F'], event) && keyboard.isCmd(event)) {
38224 editorActions.trigger('find');
38225
38226 return true;
38227 }
38228 });
38229
38230 // activate space tool
38231 // S
38232 addListener('spaceTool', function(context) {
38233
38234 var event = context.keyEvent;
38235
38236 if (keyboard.hasModifier(event)) {
38237 return;
38238 }
38239
38240 if (keyboard.isKey(['s', 'S'], event)) {
38241 editorActions.trigger('spaceTool');
38242
38243 return true;
38244 }
38245 });
38246
38247 // activate lasso tool
38248 // L
38249 addListener('lassoTool', function(context) {
38250
38251 var event = context.keyEvent;
38252
38253 if (keyboard.hasModifier(event)) {
38254 return;
38255 }
38256
38257 if (keyboard.isKey(['l', 'L'], event)) {
38258 editorActions.trigger('lassoTool');
38259
38260 return true;
38261 }
38262 });
38263
38264 // activate hand tool
38265 // H
38266 addListener('handTool', function(context) {
38267
38268 var event = context.keyEvent;
38269
38270 if (keyboard.hasModifier(event)) {
38271 return;
38272 }
38273
38274 if (keyboard.isKey(['h', 'H'], event)) {
38275 editorActions.trigger('handTool');
38276
38277 return true;
38278 }
38279 });
38280
38281 // activate global connect tool
38282 // C
38283 addListener('globalConnectTool', function(context) {
38284
38285 var event = context.keyEvent;
38286
38287 if (keyboard.hasModifier(event)) {
38288 return;
38289 }
38290
38291 if (keyboard.isKey(['c', 'C'], event)) {
38292 editorActions.trigger('globalConnectTool');
38293
38294 return true;
38295 }
38296 });
38297
38298 // activate direct editing
38299 // E
38300 addListener('directEditing', function(context) {
38301
38302 var event = context.keyEvent;
38303
38304 if (keyboard.hasModifier(event)) {
38305 return;
38306 }
38307
38308 if (keyboard.isKey(['e', 'E'], event)) {
38309 editorActions.trigger('directEditing');
38310
38311 return true;
38312 }
38313 });
38314
38315 };
38316
38317 var KeyboardModule = {
38318 __depends__: [
38319 KeyboardModule$1
38320 ],
38321 __init__: [ 'keyboardBindings' ],
38322 keyboardBindings: [ 'type', BpmnKeyboardBindings ]
38323 };
38324
38325 var DEFAULT_CONFIG = {
38326 moveSpeed: 1,
38327 moveSpeedAccelerated: 10
38328 };
38329
38330 var HIGHER_PRIORITY$3 = 1500;
38331
38332 var LEFT = 'left';
38333 var UP = 'up';
38334 var RIGHT = 'right';
38335 var DOWN = 'down';
38336
38337 var KEY_TO_DIRECTION = {
38338 ArrowLeft: LEFT,
38339 Left: LEFT,
38340 ArrowUp: UP,
38341 Up: UP,
38342 ArrowRight: RIGHT,
38343 Right: RIGHT,
38344 ArrowDown: DOWN,
38345 Down: DOWN
38346 };
38347
38348 var DIRECTIONS_DELTA = {
38349 left: function(speed) {
38350 return {
38351 x: -speed,
38352 y: 0
38353 };
38354 },
38355 up: function(speed) {
38356 return {
38357 x: 0,
38358 y: -speed
38359 };
38360 },
38361 right: function(speed) {
38362 return {
38363 x: speed,
38364 y: 0
38365 };
38366 },
38367 down: function(speed) {
38368 return {
38369 x: 0,
38370 y: speed
38371 };
38372 }
38373 };
38374
38375
38376 /**
38377 * Enables to move selection with keyboard arrows.
38378 * Use with Shift for modified speed (default=1, with Shift=10).
38379 * Pressed Cmd/Ctrl turns the feature off.
38380 *
38381 * @param {Object} config
38382 * @param {number} [config.moveSpeed=1]
38383 * @param {number} [config.moveSpeedAccelerated=10]
38384 * @param {Keyboard} keyboard
38385 * @param {Modeling} modeling
38386 * @param {Selection} selection
38387 */
38388 function KeyboardMoveSelection(
38389 config,
38390 keyboard,
38391 modeling,
38392 rules,
38393 selection
38394 ) {
38395
38396 var self = this;
38397
38398 this._config = assign({}, DEFAULT_CONFIG, config || {});
38399
38400 keyboard.addListener(HIGHER_PRIORITY$3, function(event) {
38401
38402 var keyEvent = event.keyEvent;
38403
38404 var direction = KEY_TO_DIRECTION[keyEvent.key];
38405
38406 if (!direction) {
38407 return;
38408 }
38409
38410 if (keyboard.isCmd(keyEvent)) {
38411 return;
38412 }
38413
38414 var accelerated = keyboard.isShift(keyEvent);
38415
38416 self.moveSelection(direction, accelerated);
38417
38418 return true;
38419 });
38420
38421
38422 /**
38423 * Move selected elements in the given direction,
38424 * optionally specifying accelerated movement.
38425 *
38426 * @param {string} direction
38427 * @param {boolean} [accelerated=false]
38428 */
38429 this.moveSelection = function(direction, accelerated) {
38430
38431 var selectedElements = selection.get();
38432
38433 if (!selectedElements.length) {
38434 return;
38435 }
38436
38437 var speed = this._config[
38438 accelerated ?
38439 'moveSpeedAccelerated' :
38440 'moveSpeed'
38441 ];
38442
38443 var delta = DIRECTIONS_DELTA[direction](speed);
38444
38445 var canMove = rules.allowed('elements.move', {
38446 shapes: selectedElements
38447 });
38448
38449 if (canMove) {
38450 modeling.moveElements(selectedElements, delta);
38451 }
38452 };
38453
38454 }
38455
38456 KeyboardMoveSelection.$inject = [
38457 'config.keyboardMoveSelection',
38458 'keyboard',
38459 'modeling',
38460 'rules',
38461 'selection'
38462 ];
38463
38464 var KeyboardMoveSelectionModule = {
38465 __depends__: [
38466 KeyboardModule$1,
38467 SelectionModule
38468 ],
38469 __init__: [
38470 'keyboardMoveSelection'
38471 ],
38472 keyboardMoveSelection: [ 'type', KeyboardMoveSelection ]
38473 };
38474
38475 /**
38476 * Adds change support to the diagram, including
38477 *
38478 * <ul>
38479 * <li>redrawing shapes and connections on change</li>
38480 * </ul>
38481 *
38482 * @param {EventBus} eventBus
38483 * @param {Canvas} canvas
38484 * @param {ElementRegistry} elementRegistry
38485 * @param {GraphicsFactory} graphicsFactory
38486 */
38487 function ChangeSupport(
38488 eventBus, canvas, elementRegistry,
38489 graphicsFactory) {
38490
38491
38492 // redraw shapes / connections on change
38493
38494 eventBus.on('element.changed', function(event) {
38495
38496 var element = event.element;
38497
38498 // element might have been deleted and replaced by new element with same ID
38499 // thus check for parent of element except for root element
38500 if (element.parent || element === canvas.getRootElement()) {
38501 event.gfx = elementRegistry.getGraphics(element);
38502 }
38503
38504 // shape + gfx may have been deleted
38505 if (!event.gfx) {
38506 return;
38507 }
38508
38509 eventBus.fire(getType(element) + '.changed', event);
38510 });
38511
38512 eventBus.on('elements.changed', function(event) {
38513
38514 var elements = event.elements;
38515
38516 elements.forEach(function(e) {
38517 eventBus.fire('element.changed', { element: e });
38518 });
38519
38520 graphicsFactory.updateContainments(elements);
38521 });
38522
38523 eventBus.on('shape.changed', function(event) {
38524 graphicsFactory.update('shape', event.element, event.gfx);
38525 });
38526
38527 eventBus.on('connection.changed', function(event) {
38528 graphicsFactory.update('connection', event.element, event.gfx);
38529 });
38530 }
38531
38532 ChangeSupport.$inject = [
38533 'eventBus',
38534 'canvas',
38535 'elementRegistry',
38536 'graphicsFactory'
38537 ];
38538
38539 var ChangeSupportModule = {
38540 __init__: [ 'changeSupport'],
38541 changeSupport: [ 'type', ChangeSupport ]
38542 };
38543
38544 var DEFAULT_MIN_WIDTH = 10;
38545
38546
38547 /**
38548 * A component that provides resizing of shapes on the canvas.
38549 *
38550 * The following components are part of shape resize:
38551 *
38552 * * adding resize handles,
38553 * * creating a visual during resize
38554 * * checking resize rules
38555 * * committing a change once finished
38556 *
38557 *
38558 * ## Customizing
38559 *
38560 * It's possible to customize the resizing behaviour by intercepting 'resize.start'
38561 * and providing the following parameters through the 'context':
38562 *
38563 * * minDimensions ({ width, height }): minimum shape dimensions
38564 *
38565 * * childrenBoxPadding ({ left, top, bottom, right } || number):
38566 * gap between the minimum bounding box and the container
38567 *
38568 * f.ex:
38569 *
38570 * ```javascript
38571 * eventBus.on('resize.start', 1500, function(event) {
38572 * var context = event.context,
38573 *
38574 * context.minDimensions = { width: 140, height: 120 };
38575 *
38576 * // Passing general padding
38577 * context.childrenBoxPadding = 30;
38578 *
38579 * // Passing padding to a specific side
38580 * context.childrenBoxPadding.left = 20;
38581 * });
38582 * ```
38583 */
38584 function Resize(eventBus, rules, modeling, dragging) {
38585
38586 this._dragging = dragging;
38587 this._rules = rules;
38588
38589 var self = this;
38590
38591
38592 /**
38593 * Handle resize move by specified delta.
38594 *
38595 * @param {Object} context
38596 * @param {Point} delta
38597 */
38598 function handleMove(context, delta) {
38599
38600 var shape = context.shape,
38601 direction = context.direction,
38602 resizeConstraints = context.resizeConstraints,
38603 newBounds;
38604
38605 context.delta = delta;
38606
38607 newBounds = resizeBounds$1(shape, direction, delta);
38608
38609 // ensure constraints during resize
38610 context.newBounds = ensureConstraints$1(newBounds, resizeConstraints);
38611
38612 // update + cache executable state
38613 context.canExecute = self.canResize(context);
38614 }
38615
38616 /**
38617 * Handle resize start.
38618 *
38619 * @param {Object} context
38620 */
38621 function handleStart(context) {
38622
38623 var resizeConstraints = context.resizeConstraints,
38624
38625 // evaluate minBounds for backwards compatibility
38626 minBounds = context.minBounds;
38627
38628 if (resizeConstraints !== undefined) {
38629 return;
38630 }
38631
38632 if (minBounds === undefined) {
38633 minBounds = self.computeMinResizeBox(context);
38634 }
38635
38636 context.resizeConstraints = {
38637 min: asTRBL(minBounds)
38638 };
38639 }
38640
38641 /**
38642 * Handle resize end.
38643 *
38644 * @param {Object} context
38645 */
38646 function handleEnd(context) {
38647 var shape = context.shape,
38648 canExecute = context.canExecute,
38649 newBounds = context.newBounds;
38650
38651 if (canExecute) {
38652
38653 // ensure we have actual pixel values for new bounds
38654 // (important when zoom level was > 1 during move)
38655 newBounds = roundBounds(newBounds);
38656
38657 if (!boundsChanged(shape, newBounds)) {
38658
38659 // no resize necessary
38660 return;
38661 }
38662
38663 // perform the actual resize
38664 modeling.resizeShape(shape, newBounds);
38665 }
38666 }
38667
38668
38669 eventBus.on('resize.start', function(event) {
38670 handleStart(event.context);
38671 });
38672
38673 eventBus.on('resize.move', function(event) {
38674 var delta = {
38675 x: event.dx,
38676 y: event.dy
38677 };
38678
38679 handleMove(event.context, delta);
38680 });
38681
38682 eventBus.on('resize.end', function(event) {
38683 handleEnd(event.context);
38684 });
38685
38686 }
38687
38688
38689 Resize.prototype.canResize = function(context) {
38690 var rules = this._rules;
38691
38692 var ctx = pick(context, [ 'newBounds', 'shape', 'delta', 'direction' ]);
38693
38694 return rules.allowed('shape.resize', ctx);
38695 };
38696
38697 /**
38698 * Activate a resize operation.
38699 *
38700 * You may specify additional contextual information and must specify a
38701 * resize direction during activation of the resize event.
38702 *
38703 * @param {MouseEvent} event
38704 * @param {djs.model.Shape} shape
38705 * @param {Object|string} contextOrDirection
38706 */
38707 Resize.prototype.activate = function(event, shape, contextOrDirection) {
38708 var dragging = this._dragging,
38709 context,
38710 direction;
38711
38712 if (typeof contextOrDirection === 'string') {
38713 contextOrDirection = {
38714 direction: contextOrDirection
38715 };
38716 }
38717
38718 context = assign({ shape: shape }, contextOrDirection);
38719
38720 direction = context.direction;
38721
38722 if (!direction) {
38723 throw new Error('must provide a direction (n|w|s|e|nw|se|ne|sw)');
38724 }
38725
38726 dragging.init(event, getReferencePoint$1(shape, direction), 'resize', {
38727 autoActivate: true,
38728 cursor: getCursor(direction),
38729 data: {
38730 shape: shape,
38731 context: context
38732 }
38733 });
38734 };
38735
38736 Resize.prototype.computeMinResizeBox = function(context) {
38737 var shape = context.shape,
38738 direction = context.direction,
38739 minDimensions,
38740 childrenBounds;
38741
38742 minDimensions = context.minDimensions || {
38743 width: DEFAULT_MIN_WIDTH,
38744 height: DEFAULT_MIN_WIDTH
38745 };
38746
38747 // get children bounds
38748 childrenBounds = computeChildrenBBox(shape, context.childrenBoxPadding);
38749
38750 // get correct minimum bounds from given resize direction
38751 // basically ensures that the minBounds is max(childrenBounds, minDimensions)
38752 return getMinResizeBounds(direction, shape, minDimensions, childrenBounds);
38753 };
38754
38755
38756 Resize.$inject = [
38757 'eventBus',
38758 'rules',
38759 'modeling',
38760 'dragging'
38761 ];
38762
38763 // helpers //////////
38764
38765 function boundsChanged(shape, newBounds) {
38766 return shape.x !== newBounds.x ||
38767 shape.y !== newBounds.y ||
38768 shape.width !== newBounds.width ||
38769 shape.height !== newBounds.height;
38770 }
38771
38772 function getReferencePoint$1(shape, direction) {
38773 var mid = getMid(shape),
38774 trbl = asTRBL(shape);
38775
38776 var referencePoint = {
38777 x: mid.x,
38778 y: mid.y
38779 };
38780
38781 if (direction.indexOf('n') !== -1) {
38782 referencePoint.y = trbl.top;
38783 } else if (direction.indexOf('s') !== -1) {
38784 referencePoint.y = trbl.bottom;
38785 }
38786
38787 if (direction.indexOf('e') !== -1) {
38788 referencePoint.x = trbl.right;
38789 } else if (direction.indexOf('w') !== -1) {
38790 referencePoint.x = trbl.left;
38791 }
38792
38793 return referencePoint;
38794 }
38795
38796 function getCursor(direction) {
38797 var prefix = 'resize-';
38798
38799 if (direction === 'n' || direction === 's') {
38800 return prefix + 'ns';
38801 } else if (direction === 'e' || direction === 'w') {
38802 return prefix + 'ew';
38803 } else if (direction === 'nw' || direction === 'se') {
38804 return prefix + 'nwse';
38805 } else {
38806 return prefix + 'nesw';
38807 }
38808 }
38809
38810 var MARKER_RESIZING$1 = 'djs-resizing',
38811 MARKER_RESIZE_NOT_OK = 'resize-not-ok';
38812
38813 var LOW_PRIORITY$d = 500;
38814
38815
38816 /**
38817 * Provides previews for resizing shapes when resizing.
38818 *
38819 * @param {EventBus} eventBus
38820 * @param {Canvas} canvas
38821 * @param {PreviewSupport} previewSupport
38822 */
38823 function ResizePreview(eventBus, canvas, previewSupport) {
38824
38825 /**
38826 * Update resizer frame.
38827 *
38828 * @param {Object} context
38829 */
38830 function updateFrame(context) {
38831
38832 var shape = context.shape,
38833 bounds = context.newBounds,
38834 frame = context.frame;
38835
38836 if (!frame) {
38837 frame = context.frame = previewSupport.addFrame(shape, canvas.getActiveLayer());
38838
38839 canvas.addMarker(shape, MARKER_RESIZING$1);
38840 }
38841
38842 if (bounds.width > 5) {
38843 attr(frame, { x: bounds.x, width: bounds.width });
38844 }
38845
38846 if (bounds.height > 5) {
38847 attr(frame, { y: bounds.y, height: bounds.height });
38848 }
38849
38850 if (context.canExecute) {
38851 classes(frame).remove(MARKER_RESIZE_NOT_OK);
38852 } else {
38853 classes(frame).add(MARKER_RESIZE_NOT_OK);
38854 }
38855 }
38856
38857 /**
38858 * Remove resizer frame.
38859 *
38860 * @param {Object} context
38861 */
38862 function removeFrame(context) {
38863 var shape = context.shape,
38864 frame = context.frame;
38865
38866 if (frame) {
38867 remove$1(context.frame);
38868 }
38869
38870 canvas.removeMarker(shape, MARKER_RESIZING$1);
38871 }
38872
38873 // add and update previews
38874 eventBus.on('resize.move', LOW_PRIORITY$d, function(event) {
38875 updateFrame(event.context);
38876 });
38877
38878 // remove previews
38879 eventBus.on('resize.cleanup', function(event) {
38880 removeFrame(event.context);
38881 });
38882
38883 }
38884
38885 ResizePreview.$inject = [
38886 'eventBus',
38887 'canvas',
38888 'previewSupport'
38889 ];
38890
38891 var HANDLE_OFFSET = -6,
38892 HANDLE_SIZE = 4,
38893 HANDLE_HIT_SIZE = 20;
38894
38895 var CLS_RESIZER = 'djs-resizer';
38896
38897 var directions = [ 'n', 'w', 's', 'e', 'nw', 'ne', 'se', 'sw' ];
38898
38899
38900 /**
38901 * This component is responsible for adding resize handles.
38902 *
38903 * @param {EventBus} eventBus
38904 * @param {Canvas} canvas
38905 * @param {Selection} selection
38906 * @param {Resize} resize
38907 */
38908 function ResizeHandles(eventBus, canvas, selection, resize) {
38909
38910 this._resize = resize;
38911 this._canvas = canvas;
38912
38913 var self = this;
38914
38915 eventBus.on('selection.changed', function(e) {
38916 var newSelection = e.newSelection;
38917
38918 // remove old selection markers
38919 self.removeResizers();
38920
38921 // add new selection markers ONLY if single selection
38922 if (newSelection.length === 1) {
38923 forEach(newSelection, bind$2(self.addResizer, self));
38924 }
38925 });
38926
38927 eventBus.on('shape.changed', function(e) {
38928 var shape = e.element;
38929
38930 if (selection.isSelected(shape)) {
38931 self.removeResizers();
38932
38933 self.addResizer(shape);
38934 }
38935 });
38936 }
38937
38938
38939 ResizeHandles.prototype.makeDraggable = function(element, gfx, direction) {
38940 var resize = this._resize;
38941
38942 function startResize(event) {
38943
38944 // only trigger on left mouse button
38945 if (isPrimaryButton(event)) {
38946 resize.activate(event, element, direction);
38947 }
38948 }
38949
38950 componentEvent.bind(gfx, 'mousedown', startResize);
38951 componentEvent.bind(gfx, 'touchstart', startResize);
38952 };
38953
38954
38955 ResizeHandles.prototype._createResizer = function(element, x, y, direction) {
38956 var resizersParent = this._getResizersParent();
38957
38958 var offset = getHandleOffset(direction);
38959
38960 var group = create$1('g');
38961
38962 classes(group).add(CLS_RESIZER);
38963 classes(group).add(CLS_RESIZER + '-' + element.id);
38964 classes(group).add(CLS_RESIZER + '-' + direction);
38965
38966 append(resizersParent, group);
38967
38968 var visual = create$1('rect');
38969
38970 attr(visual, {
38971 x: -HANDLE_SIZE / 2 + offset.x,
38972 y: -HANDLE_SIZE / 2 + offset.y,
38973 width: HANDLE_SIZE,
38974 height: HANDLE_SIZE
38975 });
38976
38977 classes(visual).add(CLS_RESIZER + '-visual');
38978
38979 append(group, visual);
38980
38981 var hit = create$1('rect');
38982
38983 attr(hit, {
38984 x: -HANDLE_HIT_SIZE / 2 + offset.x,
38985 y: -HANDLE_HIT_SIZE / 2 + offset.y,
38986 width: HANDLE_HIT_SIZE,
38987 height: HANDLE_HIT_SIZE
38988 });
38989
38990 classes(hit).add(CLS_RESIZER + '-hit');
38991
38992 append(group, hit);
38993
38994 transform(group, x, y);
38995
38996 return group;
38997 };
38998
38999 ResizeHandles.prototype.createResizer = function(element, direction) {
39000 var point = getReferencePoint$1(element, direction);
39001
39002 var resizer = this._createResizer(element, point.x, point.y, direction);
39003
39004 this.makeDraggable(element, resizer, direction);
39005 };
39006
39007 // resize handles implementation ///////////////////////////////
39008
39009 /**
39010 * Add resizers for a given element.
39011 *
39012 * @param {djs.model.Shape} shape
39013 */
39014 ResizeHandles.prototype.addResizer = function(shape) {
39015 var self = this;
39016
39017 var resize = this._resize;
39018
39019 if (!resize.canResize({ shape: shape })) {
39020 return;
39021 }
39022
39023 forEach(directions, function(direction) {
39024 self.createResizer(shape, direction);
39025 });
39026 };
39027
39028 /**
39029 * Remove all resizers
39030 */
39031 ResizeHandles.prototype.removeResizers = function() {
39032 var resizersParent = this._getResizersParent();
39033
39034 clear(resizersParent);
39035 };
39036
39037 ResizeHandles.prototype._getResizersParent = function() {
39038 return this._canvas.getLayer('resizers');
39039 };
39040
39041 ResizeHandles.$inject = [
39042 'eventBus',
39043 'canvas',
39044 'selection',
39045 'resize'
39046 ];
39047
39048 // helpers //////////
39049
39050 function getHandleOffset(direction) {
39051 var offset = {
39052 x: 0,
39053 y: 0
39054 };
39055
39056 if (direction.indexOf('e') !== -1) {
39057 offset.x = -HANDLE_OFFSET;
39058 } else if (direction.indexOf('w') !== -1) {
39059 offset.x = HANDLE_OFFSET;
39060 }
39061
39062 if (direction.indexOf('s') !== -1) {
39063 offset.y = -HANDLE_OFFSET;
39064 } else if (direction.indexOf('n') !== -1) {
39065 offset.y = HANDLE_OFFSET;
39066 }
39067
39068 return offset;
39069 }
39070
39071 var ResizeModule = {
39072 __depends__: [
39073 RulesModule$1,
39074 DraggingModule,
39075 PreviewSupportModule
39076 ],
39077 __init__: [
39078 'resize',
39079 'resizePreview',
39080 'resizeHandles'
39081 ],
39082 resize: [ 'type', Resize ],
39083 resizePreview: [ 'type', ResizePreview ],
39084 resizeHandles: [ 'type', ResizeHandles ]
39085 };
39086
39087 /**
39088 * Creates a new bpmn:CategoryValue inside a new bpmn:Category
39089 *
39090 * @param {ModdleElement} definitions
39091 * @param {BpmnFactory} bpmnFactory
39092 *
39093 * @return {ModdleElement} categoryValue.
39094 */
39095 function createCategoryValue(definitions, bpmnFactory) {
39096 var categoryValue = bpmnFactory.create('bpmn:CategoryValue'),
39097 category = bpmnFactory.create('bpmn:Category', {
39098 categoryValue: [ categoryValue ]
39099 });
39100
39101 // add to correct place
39102 add(definitions.get('rootElements'), category);
39103 getBusinessObject(category).$parent = definitions;
39104 getBusinessObject(categoryValue).$parent = category;
39105
39106 return categoryValue;
39107
39108 }
39109
39110 function LabelEditingProvider(
39111 eventBus, bpmnFactory, canvas, directEditing,
39112 modeling, resizeHandles, textRenderer) {
39113
39114 this._bpmnFactory = bpmnFactory;
39115 this._canvas = canvas;
39116 this._modeling = modeling;
39117 this._textRenderer = textRenderer;
39118
39119 directEditing.registerProvider(this);
39120
39121 // listen to dblclick on non-root elements
39122 eventBus.on('element.dblclick', function(event) {
39123 activateDirectEdit(event.element, true);
39124 });
39125
39126 // complete on followup canvas operation
39127 eventBus.on([
39128 'autoPlace.start',
39129 'canvas.viewbox.changing',
39130 'drag.init',
39131 'element.mousedown',
39132 'popupMenu.open'
39133 ], function(event) {
39134
39135 if (directEditing.isActive()) {
39136 directEditing.complete();
39137 }
39138 });
39139
39140 // cancel on command stack changes
39141 eventBus.on([ 'commandStack.changed' ], function(e) {
39142 if (directEditing.isActive()) {
39143 directEditing.cancel();
39144 }
39145 });
39146
39147
39148 eventBus.on('directEditing.activate', function(event) {
39149 resizeHandles.removeResizers();
39150 });
39151
39152 eventBus.on('create.end', 500, function(event) {
39153
39154 var context = event.context,
39155 element = context.shape,
39156 canExecute = event.context.canExecute,
39157 isTouch = event.isTouch;
39158
39159 // TODO(nikku): we need to find a way to support the
39160 // direct editing on mobile devices; right now this will
39161 // break for desworkflowediting on mobile devices
39162 // as it breaks the user interaction workflow
39163
39164 // TODO(nre): we should temporarily focus the edited element
39165 // here and release the focused viewport after the direct edit
39166 // operation is finished
39167 if (isTouch) {
39168 return;
39169 }
39170
39171 if (!canExecute) {
39172 return;
39173 }
39174
39175 if (context.hints && context.hints.createElementsBehavior === false) {
39176 return;
39177 }
39178
39179 activateDirectEdit(element);
39180 });
39181
39182 eventBus.on('autoPlace.end', 500, function(event) {
39183 activateDirectEdit(event.shape);
39184 });
39185
39186
39187 function activateDirectEdit(element, force) {
39188 if (force ||
39189 isAny(element, [ 'bpmn:Task', 'bpmn:TextAnnotation', 'bpmn:Group' ]) ||
39190 isCollapsedSubProcess(element)) {
39191
39192 directEditing.activate(element);
39193 }
39194 }
39195
39196 }
39197
39198 LabelEditingProvider.$inject = [
39199 'eventBus',
39200 'bpmnFactory',
39201 'canvas',
39202 'directEditing',
39203 'modeling',
39204 'resizeHandles',
39205 'textRenderer'
39206 ];
39207
39208
39209 /**
39210 * Activate direct editing for activities and text annotations.
39211 *
39212 * @param {djs.model.Base} element
39213 *
39214 * @return {Object} an object with properties bounds (position and size), text and options
39215 */
39216 LabelEditingProvider.prototype.activate = function(element) {
39217
39218 // text
39219 var text = getLabel(element);
39220
39221 if (text === undefined) {
39222 return;
39223 }
39224
39225 var context = {
39226 text: text
39227 };
39228
39229 // bounds
39230 var bounds = this.getEditingBBox(element);
39231
39232 assign(context, bounds);
39233
39234 var options = {};
39235
39236 // tasks
39237 if (
39238 isAny(element, [
39239 'bpmn:Task',
39240 'bpmn:Participant',
39241 'bpmn:Lane',
39242 'bpmn:CallActivity'
39243 ]) ||
39244 isCollapsedSubProcess(element)
39245 ) {
39246 assign(options, {
39247 centerVertically: true
39248 });
39249 }
39250
39251 // external labels
39252 if (isLabelExternal(element)) {
39253 assign(options, {
39254 autoResize: true
39255 });
39256 }
39257
39258 // text annotations
39259 if (is$1(element, 'bpmn:TextAnnotation')) {
39260 assign(options, {
39261 resizable: true,
39262 autoResize: true
39263 });
39264 }
39265
39266 assign(context, {
39267 options: options
39268 });
39269
39270 return context;
39271 };
39272
39273
39274 /**
39275 * Get the editing bounding box based on the element's size and position
39276 *
39277 * @param {djs.model.Base} element
39278 *
39279 * @return {Object} an object containing information about position
39280 * and size (fixed or minimum and/or maximum)
39281 */
39282 LabelEditingProvider.prototype.getEditingBBox = function(element) {
39283 var canvas = this._canvas;
39284
39285 var target = element.label || element;
39286
39287 var bbox = canvas.getAbsoluteBBox(target);
39288
39289 var mid = {
39290 x: bbox.x + bbox.width / 2,
39291 y: bbox.y + bbox.height / 2
39292 };
39293
39294 // default position
39295 var bounds = { x: bbox.x, y: bbox.y };
39296
39297 var zoom = canvas.zoom();
39298
39299 var defaultStyle = this._textRenderer.getDefaultStyle(),
39300 externalStyle = this._textRenderer.getExternalStyle();
39301
39302 // take zoom into account
39303 var externalFontSize = externalStyle.fontSize * zoom,
39304 externalLineHeight = externalStyle.lineHeight,
39305 defaultFontSize = defaultStyle.fontSize * zoom,
39306 defaultLineHeight = defaultStyle.lineHeight;
39307
39308 var style = {
39309 fontFamily: this._textRenderer.getDefaultStyle().fontFamily,
39310 fontWeight: this._textRenderer.getDefaultStyle().fontWeight
39311 };
39312
39313 // adjust for expanded pools AND lanes
39314 if (is$1(element, 'bpmn:Lane') || isExpandedPool(element)) {
39315
39316 assign(bounds, {
39317 width: bbox.height,
39318 height: 30 * zoom,
39319 x: bbox.x - bbox.height / 2 + (15 * zoom),
39320 y: mid.y - (30 * zoom) / 2
39321 });
39322
39323 assign(style, {
39324 fontSize: defaultFontSize + 'px',
39325 lineHeight: defaultLineHeight,
39326 paddingTop: (7 * zoom) + 'px',
39327 paddingBottom: (7 * zoom) + 'px',
39328 paddingLeft: (5 * zoom) + 'px',
39329 paddingRight: (5 * zoom) + 'px',
39330 transform: 'rotate(-90deg)'
39331 });
39332 }
39333
39334
39335 // internal labels for tasks and collapsed call activities,
39336 // sub processes and participants
39337 if (isAny(element, [ 'bpmn:Task', 'bpmn:CallActivity']) ||
39338 isCollapsedPool(element) ||
39339 isCollapsedSubProcess(element)) {
39340
39341 assign(bounds, {
39342 width: bbox.width,
39343 height: bbox.height
39344 });
39345
39346 assign(style, {
39347 fontSize: defaultFontSize + 'px',
39348 lineHeight: defaultLineHeight,
39349 paddingTop: (7 * zoom) + 'px',
39350 paddingBottom: (7 * zoom) + 'px',
39351 paddingLeft: (5 * zoom) + 'px',
39352 paddingRight: (5 * zoom) + 'px'
39353 });
39354 }
39355
39356
39357 // internal labels for expanded sub processes
39358 if (isExpandedSubProcess$1(element)) {
39359 assign(bounds, {
39360 width: bbox.width,
39361 x: bbox.x
39362 });
39363
39364 assign(style, {
39365 fontSize: defaultFontSize + 'px',
39366 lineHeight: defaultLineHeight,
39367 paddingTop: (7 * zoom) + 'px',
39368 paddingBottom: (7 * zoom) + 'px',
39369 paddingLeft: (5 * zoom) + 'px',
39370 paddingRight: (5 * zoom) + 'px'
39371 });
39372 }
39373
39374 var width = 90 * zoom,
39375 paddingTop = 7 * zoom,
39376 paddingBottom = 4 * zoom;
39377
39378 // external labels for events, data elements, gateways, groups and connections
39379 if (target.labelTarget) {
39380 assign(bounds, {
39381 width: width,
39382 height: bbox.height + paddingTop + paddingBottom,
39383 x: mid.x - width / 2,
39384 y: bbox.y - paddingTop
39385 });
39386
39387 assign(style, {
39388 fontSize: externalFontSize + 'px',
39389 lineHeight: externalLineHeight,
39390 paddingTop: paddingTop + 'px',
39391 paddingBottom: paddingBottom + 'px'
39392 });
39393 }
39394
39395 // external label not yet created
39396 if (isLabelExternal(target)
39397 && !hasExternalLabel(target)
39398 && !isLabel$6(target)) {
39399
39400 var externalLabelMid = getExternalLabelMid(element);
39401
39402 var absoluteBBox = canvas.getAbsoluteBBox({
39403 x: externalLabelMid.x,
39404 y: externalLabelMid.y,
39405 width: 0,
39406 height: 0
39407 });
39408
39409 var height = externalFontSize + paddingTop + paddingBottom;
39410
39411 assign(bounds, {
39412 width: width,
39413 height: height,
39414 x: absoluteBBox.x - width / 2,
39415 y: absoluteBBox.y - height / 2
39416 });
39417
39418 assign(style, {
39419 fontSize: externalFontSize + 'px',
39420 lineHeight: externalLineHeight,
39421 paddingTop: paddingTop + 'px',
39422 paddingBottom: paddingBottom + 'px'
39423 });
39424 }
39425
39426 // text annotations
39427 if (is$1(element, 'bpmn:TextAnnotation')) {
39428 assign(bounds, {
39429 width: bbox.width,
39430 height: bbox.height,
39431 minWidth: 30 * zoom,
39432 minHeight: 10 * zoom
39433 });
39434
39435 assign(style, {
39436 textAlign: 'left',
39437 paddingTop: (5 * zoom) + 'px',
39438 paddingBottom: (7 * zoom) + 'px',
39439 paddingLeft: (7 * zoom) + 'px',
39440 paddingRight: (5 * zoom) + 'px',
39441 fontSize: defaultFontSize + 'px',
39442 lineHeight: defaultLineHeight
39443 });
39444 }
39445
39446 return { bounds: bounds, style: style };
39447 };
39448
39449
39450 LabelEditingProvider.prototype.update = function(
39451 element, newLabel,
39452 activeContextText, bounds) {
39453
39454 var newBounds,
39455 bbox;
39456
39457 if (is$1(element, 'bpmn:TextAnnotation')) {
39458
39459 bbox = this._canvas.getAbsoluteBBox(element);
39460
39461 newBounds = {
39462 x: element.x,
39463 y: element.y,
39464 width: element.width / bbox.width * bounds.width,
39465 height: element.height / bbox.height * bounds.height
39466 };
39467 }
39468
39469 if (is$1(element, 'bpmn:Group')) {
39470
39471 var businessObject = getBusinessObject(element);
39472
39473 // initialize categoryValue if not existing
39474 if (!businessObject.categoryValueRef) {
39475
39476 var rootElement = this._canvas.getRootElement(),
39477 definitions = getBusinessObject(rootElement).$parent;
39478
39479 var categoryValue = createCategoryValue(definitions, this._bpmnFactory);
39480
39481 getBusinessObject(element).categoryValueRef = categoryValue;
39482 }
39483
39484 }
39485
39486 if (isEmptyText$1(newLabel)) {
39487 newLabel = null;
39488 }
39489
39490 this._modeling.updateLabel(element, newLabel, newBounds);
39491 };
39492
39493
39494
39495 // helpers //////////////////////
39496
39497 function isCollapsedSubProcess(element) {
39498 return is$1(element, 'bpmn:SubProcess') && !isExpanded(element);
39499 }
39500
39501 function isExpandedSubProcess$1(element) {
39502 return is$1(element, 'bpmn:SubProcess') && isExpanded(element);
39503 }
39504
39505 function isCollapsedPool(element) {
39506 return is$1(element, 'bpmn:Participant') && !isExpanded(element);
39507 }
39508
39509 function isExpandedPool(element) {
39510 return is$1(element, 'bpmn:Participant') && isExpanded(element);
39511 }
39512
39513 function isEmptyText$1(label) {
39514 return !label || !label.trim();
39515 }
39516
39517 var MARKER_HIDDEN = 'djs-element-hidden',
39518 MARKER_LABEL_HIDDEN = 'djs-label-hidden';
39519
39520
39521 function LabelEditingPreview(
39522 eventBus, canvas, elementRegistry,
39523 pathMap) {
39524
39525 var self = this;
39526
39527 var defaultLayer = canvas.getDefaultLayer();
39528
39529 var element, absoluteElementBBox, gfx;
39530
39531 eventBus.on('directEditing.activate', function(context) {
39532 var activeProvider = context.active;
39533
39534 element = activeProvider.element.label || activeProvider.element;
39535
39536 // text annotation
39537 if (is$1(element, 'bpmn:TextAnnotation')) {
39538 absoluteElementBBox = canvas.getAbsoluteBBox(element);
39539
39540 gfx = create$1('g');
39541
39542 var textPathData = pathMap.getScaledPath('TEXT_ANNOTATION', {
39543 xScaleFactor: 1,
39544 yScaleFactor: 1,
39545 containerWidth: element.width,
39546 containerHeight: element.height,
39547 position: {
39548 mx: 0.0,
39549 my: 0.0
39550 }
39551 });
39552
39553 var path = self.path = create$1('path');
39554
39555 attr(path, {
39556 d: textPathData,
39557 strokeWidth: 2,
39558 stroke: getStrokeColor(element)
39559 });
39560
39561 append(gfx, path);
39562
39563 append(defaultLayer, gfx);
39564
39565 translate$2(gfx, element.x, element.y);
39566 }
39567
39568 if (is$1(element, 'bpmn:TextAnnotation') ||
39569 element.labelTarget) {
39570 canvas.addMarker(element, MARKER_HIDDEN);
39571 } else if (is$1(element, 'bpmn:Task') ||
39572 is$1(element, 'bpmn:CallActivity') ||
39573 is$1(element, 'bpmn:SubProcess') ||
39574 is$1(element, 'bpmn:Participant')) {
39575 canvas.addMarker(element, MARKER_LABEL_HIDDEN);
39576 }
39577 });
39578
39579 eventBus.on('directEditing.resize', function(context) {
39580
39581 // text annotation
39582 if (is$1(element, 'bpmn:TextAnnotation')) {
39583 var height = context.height,
39584 dy = context.dy;
39585
39586 var newElementHeight = Math.max(element.height / absoluteElementBBox.height * (height + dy), 0);
39587
39588 var textPathData = pathMap.getScaledPath('TEXT_ANNOTATION', {
39589 xScaleFactor: 1,
39590 yScaleFactor: 1,
39591 containerWidth: element.width,
39592 containerHeight: newElementHeight,
39593 position: {
39594 mx: 0.0,
39595 my: 0.0
39596 }
39597 });
39598
39599 attr(self.path, {
39600 d: textPathData
39601 });
39602 }
39603 });
39604
39605 eventBus.on([ 'directEditing.complete', 'directEditing.cancel' ], function(context) {
39606 var activeProvider = context.active;
39607
39608 if (activeProvider) {
39609 canvas.removeMarker(activeProvider.element.label || activeProvider.element, MARKER_HIDDEN);
39610 canvas.removeMarker(element, MARKER_LABEL_HIDDEN);
39611 }
39612
39613 element = undefined;
39614 absoluteElementBBox = undefined;
39615
39616 if (gfx) {
39617 remove$1(gfx);
39618
39619 gfx = undefined;
39620 }
39621 });
39622 }
39623
39624 LabelEditingPreview.$inject = [
39625 'eventBus',
39626 'canvas',
39627 'elementRegistry',
39628 'pathMap'
39629 ];
39630
39631
39632 // helpers ///////////////////
39633
39634 function getStrokeColor(element, defaultColor) {
39635 var bo = getBusinessObject(element);
39636
39637 return bo.di.get('stroke') || defaultColor || 'black';
39638 }
39639
39640 var LabelEditingModule = {
39641 __depends__: [
39642 ChangeSupportModule,
39643 ResizeModule,
39644 DirectEditingModule
39645 ],
39646 __init__: [
39647 'labelEditingProvider',
39648 'labelEditingPreview'
39649 ],
39650 labelEditingProvider: [ 'type', LabelEditingProvider ],
39651 labelEditingPreview: [ 'type', LabelEditingPreview ]
39652 };
39653
39654 var ALIGNMENTS = [
39655 'top',
39656 'bottom',
39657 'left',
39658 'right'
39659 ];
39660
39661 var ELEMENT_LABEL_DISTANCE = 10;
39662
39663 /**
39664 * A component that makes sure that external labels are added
39665 * together with respective elements and properly updated (DI wise)
39666 * during move.
39667 *
39668 * @param {EventBus} eventBus
39669 * @param {Modeling} modeling
39670 */
39671 function AdaptiveLabelPositioningBehavior(eventBus, modeling) {
39672
39673 CommandInterceptor.call(this, eventBus);
39674
39675 this.postExecuted([
39676 'connection.create',
39677 'connection.layout',
39678 'connection.updateWaypoints'
39679 ], function(event) {
39680 var context = event.context,
39681 connection = context.connection,
39682 source = connection.source,
39683 target = connection.target,
39684 hints = context.hints || {};
39685
39686 if (hints.createElementsBehavior !== false) {
39687 checkLabelAdjustment(source);
39688 checkLabelAdjustment(target);
39689 }
39690 });
39691
39692
39693 this.postExecuted([
39694 'label.create'
39695 ], function(event) {
39696 var context = event.context,
39697 shape = context.shape,
39698 hints = context.hints || {};
39699
39700 if (hints.createElementsBehavior !== false) {
39701 checkLabelAdjustment(shape.labelTarget);
39702 }
39703 });
39704
39705
39706 this.postExecuted([
39707 'elements.create'
39708 ], function(event) {
39709 var context = event.context,
39710 elements = context.elements,
39711 hints = context.hints || {};
39712
39713 if (hints.createElementsBehavior !== false) {
39714 elements.forEach(function(element) {
39715 checkLabelAdjustment(element);
39716 });
39717 }
39718 });
39719
39720 function checkLabelAdjustment(element) {
39721
39722 // skip non-existing labels
39723 if (!hasExternalLabel(element)) {
39724 return;
39725 }
39726
39727 var optimalPosition = getOptimalPosition(element);
39728
39729 // no optimal position found
39730 if (!optimalPosition) {
39731 return;
39732 }
39733
39734 adjustLabelPosition(element, optimalPosition);
39735 }
39736
39737 function adjustLabelPosition(element, orientation) {
39738
39739 var elementMid = getMid(element),
39740 label = element.label,
39741 labelMid = getMid(label);
39742
39743 // ignore labels that are being created
39744 if (!label.parent) {
39745 return;
39746 }
39747
39748 var elementTrbl = asTRBL(element);
39749
39750 var newLabelMid;
39751
39752 switch (orientation) {
39753 case 'top':
39754 newLabelMid = {
39755 x: elementMid.x,
39756 y: elementTrbl.top - ELEMENT_LABEL_DISTANCE - label.height / 2
39757 };
39758
39759 break;
39760
39761 case 'left':
39762
39763 newLabelMid = {
39764 x: elementTrbl.left - ELEMENT_LABEL_DISTANCE - label.width / 2,
39765 y: elementMid.y
39766 };
39767
39768 break;
39769
39770 case 'bottom':
39771
39772 newLabelMid = {
39773 x: elementMid.x,
39774 y: elementTrbl.bottom + ELEMENT_LABEL_DISTANCE + label.height / 2
39775 };
39776
39777 break;
39778
39779 case 'right':
39780
39781 newLabelMid = {
39782 x: elementTrbl.right + ELEMENT_LABEL_DISTANCE + label.width / 2,
39783 y: elementMid.y
39784 };
39785
39786 break;
39787 }
39788
39789 var delta$1 = delta(newLabelMid, labelMid);
39790
39791 modeling.moveShape(label, delta$1);
39792 }
39793
39794 }
39795
39796 inherits$1(AdaptiveLabelPositioningBehavior, CommandInterceptor);
39797
39798 AdaptiveLabelPositioningBehavior.$inject = [
39799 'eventBus',
39800 'modeling'
39801 ];
39802
39803
39804 // helpers //////////////////////
39805
39806 /**
39807 * Return alignments which are taken by a boundary's host element
39808 *
39809 * @param {Shape} element
39810 *
39811 * @return {Array<string>}
39812 */
39813 function getTakenHostAlignments(element) {
39814
39815 var hostElement = element.host,
39816 elementMid = getMid(element),
39817 hostOrientation = getOrientation(elementMid, hostElement);
39818
39819 var freeAlignments;
39820
39821 // check whether there is a multi-orientation, e.g. 'top-left'
39822 if (hostOrientation.indexOf('-') >= 0) {
39823 freeAlignments = hostOrientation.split('-');
39824 } else {
39825 freeAlignments = [ hostOrientation ];
39826 }
39827
39828 var takenAlignments = ALIGNMENTS.filter(function(alignment) {
39829
39830 return freeAlignments.indexOf(alignment) === -1;
39831 });
39832
39833 return takenAlignments;
39834
39835 }
39836
39837 /**
39838 * Return alignments which are taken by related connections
39839 *
39840 * @param {Shape} element
39841 *
39842 * @return {Array<string>}
39843 */
39844 function getTakenConnectionAlignments(element) {
39845
39846 var elementMid = getMid(element);
39847
39848 var takenAlignments = [].concat(
39849 element.incoming.map(function(c) {
39850 return c.waypoints[c.waypoints.length - 2 ];
39851 }),
39852 element.outgoing.map(function(c) {
39853 return c.waypoints[1];
39854 })
39855 ).map(function(point) {
39856 return getApproximateOrientation(elementMid, point);
39857 });
39858
39859 return takenAlignments;
39860 }
39861
39862 /**
39863 * Return the optimal label position around an element
39864 * or _undefined_, if none was found.
39865 *
39866 * @param {Shape} element
39867 *
39868 * @return {string} positioning identifier
39869 */
39870 function getOptimalPosition(element) {
39871
39872 var labelMid = getMid(element.label);
39873
39874 var elementMid = getMid(element);
39875
39876 var labelOrientation = getApproximateOrientation(elementMid, labelMid);
39877
39878 if (!isAligned(labelOrientation)) {
39879 return;
39880 }
39881
39882 var takenAlignments = getTakenConnectionAlignments(element);
39883
39884 if (element.host) {
39885 var takenHostAlignments = getTakenHostAlignments(element);
39886
39887 takenAlignments = takenAlignments.concat(takenHostAlignments);
39888 }
39889
39890 var freeAlignments = ALIGNMENTS.filter(function(alignment) {
39891
39892 return takenAlignments.indexOf(alignment) === -1;
39893 });
39894
39895 // NOTHING TO DO; label already aligned a.O.K.
39896 if (freeAlignments.indexOf(labelOrientation) !== -1) {
39897 return;
39898 }
39899
39900 return freeAlignments[0];
39901 }
39902
39903 function getApproximateOrientation(p0, p1) {
39904 return getOrientation(p1, p0, 5);
39905 }
39906
39907 function isAligned(orientation) {
39908 return ALIGNMENTS.indexOf(orientation) !== -1;
39909 }
39910
39911 function AppendBehavior(eventBus, elementFactory, bpmnRules) {
39912
39913 CommandInterceptor.call(this, eventBus);
39914
39915 // assign correct shape position unless already set
39916
39917 this.preExecute('shape.append', function(context) {
39918
39919 var source = context.source,
39920 shape = context.shape;
39921
39922 if (!context.position) {
39923
39924 if (is$1(shape, 'bpmn:TextAnnotation')) {
39925 context.position = {
39926 x: source.x + source.width / 2 + 75,
39927 y: source.y - (50) - shape.height / 2
39928 };
39929 } else {
39930 context.position = {
39931 x: source.x + source.width + 80 + shape.width / 2,
39932 y: source.y + source.height / 2
39933 };
39934 }
39935 }
39936 }, true);
39937 }
39938
39939 inherits$1(AppendBehavior, CommandInterceptor);
39940
39941 AppendBehavior.$inject = [
39942 'eventBus',
39943 'elementFactory',
39944 'bpmnRules'
39945 ];
39946
39947 function AssociationBehavior(injector, modeling) {
39948 injector.invoke(CommandInterceptor, this);
39949
39950 this.postExecute('shape.move', function(context) {
39951 var newParent = context.newParent,
39952 shape = context.shape;
39953
39954 var associations = filter(shape.incoming.concat(shape.outgoing), function(connection) {
39955 return is$1(connection, 'bpmn:Association');
39956 });
39957
39958 forEach(associations, function(association) {
39959 modeling.moveConnection(association, { x: 0, y: 0 }, newParent);
39960 });
39961 }, true);
39962 }
39963
39964 inherits$1(AssociationBehavior, CommandInterceptor);
39965
39966 AssociationBehavior.$inject = [
39967 'injector',
39968 'modeling'
39969 ];
39970
39971 var LOW_PRIORITY$c = 500;
39972
39973
39974 /**
39975 * Replace intermediate event with boundary event when creating or moving results in attached event.
39976 */
39977 function AttachEventBehavior(bpmnReplace, injector) {
39978 injector.invoke(CommandInterceptor, this);
39979
39980 this._bpmnReplace = bpmnReplace;
39981
39982 var self = this;
39983
39984 this.postExecuted('elements.create', LOW_PRIORITY$c, function(context) {
39985 var elements = context.elements;
39986
39987 elements = elements.filter(function(shape) {
39988 var host = shape.host;
39989
39990 return shouldReplace$1(shape, host);
39991 });
39992
39993 if (elements.length !== 1) {
39994 return;
39995 }
39996
39997 elements.map(function(element) {
39998 return elements.indexOf(element);
39999 }).forEach(function(index) {
40000 var host = elements[ index ];
40001
40002 context.elements[ index ] = self.replaceShape(elements[ index ], host);
40003 });
40004 }, true);
40005
40006
40007 this.preExecute('elements.move', LOW_PRIORITY$c, function(context) {
40008 var shapes = context.shapes,
40009 host = context.newHost;
40010
40011 if (shapes.length !== 1) {
40012 return;
40013 }
40014
40015 var shape = shapes[0];
40016
40017 if (shouldReplace$1(shape, host)) {
40018 context.shapes = [ self.replaceShape(shape, host) ];
40019 }
40020 }, true);
40021 }
40022
40023 AttachEventBehavior.$inject = [
40024 'bpmnReplace',
40025 'injector'
40026 ];
40027
40028 inherits$1(AttachEventBehavior, CommandInterceptor);
40029
40030 AttachEventBehavior.prototype.replaceShape = function(shape, host) {
40031 var eventDefinition = getEventDefinition$1(shape);
40032
40033 var boundaryEvent = {
40034 type: 'bpmn:BoundaryEvent',
40035 host: host
40036 };
40037
40038 if (eventDefinition) {
40039 boundaryEvent.eventDefinitionType = eventDefinition.$type;
40040 }
40041
40042 return this._bpmnReplace.replaceElement(shape, boundaryEvent, { layoutConnection: false });
40043 };
40044
40045
40046 // helpers //////////
40047
40048 function getEventDefinition$1(element) {
40049 var businessObject = getBusinessObject(element),
40050 eventDefinitions = businessObject.eventDefinitions;
40051
40052 return eventDefinitions && eventDefinitions[0];
40053 }
40054
40055 function shouldReplace$1(shape, host) {
40056 return !isLabel$6(shape) &&
40057 isAny(shape, [ 'bpmn:IntermediateThrowEvent', 'bpmn:IntermediateCatchEvent' ]) && !!host;
40058 }
40059
40060 var HIGH_PRIORITY$c = 2000;
40061
40062
40063 /**
40064 * BPMN specific boundary event behavior
40065 */
40066 function BoundaryEventBehavior(eventBus, moddle, modeling) {
40067
40068 CommandInterceptor.call(this, eventBus);
40069
40070 function getBoundaryEvents(element) {
40071 return filter(element.attachers, function(attacher) {
40072 return is$1(attacher, 'bpmn:BoundaryEvent');
40073 });
40074 }
40075
40076 // remove after connecting to event-based gateway
40077 this.postExecute('connection.create', function(event) {
40078 var source = event.context.source,
40079 target = event.context.target,
40080 boundaryEvents = getBoundaryEvents(target);
40081
40082 if (
40083 is$1(source, 'bpmn:EventBasedGateway') &&
40084 is$1(target, 'bpmn:ReceiveTask') &&
40085 boundaryEvents.length > 0
40086 ) {
40087 modeling.removeElements(boundaryEvents);
40088 }
40089
40090 });
40091
40092 // remove after replacing connected gateway with event-based gateway
40093 this.postExecute('connection.reconnect', function(event) {
40094 var oldSource = event.context.oldSource,
40095 newSource = event.context.newSource;
40096
40097 if (is$1(oldSource, 'bpmn:Gateway') &&
40098 is$1(newSource, 'bpmn:EventBasedGateway')) {
40099 forEach(newSource.outgoing, function(connection) {
40100 var target = connection.target,
40101 attachedboundaryEvents = getBoundaryEvents(target);
40102
40103 if (is$1(target, 'bpmn:ReceiveTask') &&
40104 attachedboundaryEvents.length > 0) {
40105 modeling.removeElements(attachedboundaryEvents);
40106 }
40107 });
40108 }
40109 });
40110
40111 // copy reference to root element on replace
40112 eventBus.on('moddleCopy.canCopyProperty', HIGH_PRIORITY$c, function(context) {
40113 var parent = context.parent,
40114 property = context.property,
40115 propertyName = context.propertyName;
40116
40117 var propertyDescriptor = moddle.getPropertyDescriptor(parent, propertyName);
40118
40119 if (propertyDescriptor && propertyDescriptor.isReference && is$1(property, 'bpmn:RootElement')) {
40120 parent.set(propertyName, property);
40121 }
40122 });
40123 }
40124
40125 BoundaryEventBehavior.$inject = [
40126 'eventBus',
40127 'moddle',
40128 'modeling'
40129 ];
40130
40131 inherits$1(BoundaryEventBehavior, CommandInterceptor);
40132
40133 var LOW_PRIORITY$b = 500;
40134
40135
40136 /**
40137 * Add referenced root elements (error, escalation, message, signal) if they don't exist.
40138 * Copy referenced root elements on copy & paste.
40139 */
40140 function RootElementReferenceBehavior(
40141 bpmnjs, eventBus, injector, moddleCopy, bpmnFactory
40142 ) {
40143 injector.invoke(CommandInterceptor, this);
40144
40145 function canHaveRootElementReference(element) {
40146 return isAny(element, [ 'bpmn:ReceiveTask', 'bpmn:SendTask' ]) ||
40147 hasAnyEventDefinition(element, [
40148 'bpmn:ErrorEventDefinition',
40149 'bpmn:EscalationEventDefinition',
40150 'bpmn:MessageEventDefinition',
40151 'bpmn:SignalEventDefinition'
40152 ]);
40153 }
40154
40155 function hasRootElement(rootElement) {
40156 var definitions = bpmnjs.getDefinitions(),
40157 rootElements = definitions.get('rootElements');
40158
40159 return !!find(rootElements, matchPattern({ id: rootElement.id }));
40160 }
40161
40162 function getRootElementReferencePropertyName(eventDefinition) {
40163 if (is$1(eventDefinition, 'bpmn:ErrorEventDefinition')) {
40164 return 'errorRef';
40165 } else if (is$1(eventDefinition, 'bpmn:EscalationEventDefinition')) {
40166 return 'escalationRef';
40167 } else if (is$1(eventDefinition, 'bpmn:MessageEventDefinition')) {
40168 return 'messageRef';
40169 } else if (is$1(eventDefinition, 'bpmn:SignalEventDefinition')) {
40170 return 'signalRef';
40171 }
40172 }
40173
40174 function getRootElement(businessObject) {
40175 if (isAny(businessObject, [ 'bpmn:ReceiveTask', 'bpmn:SendTask' ])) {
40176 return businessObject.get('messageRef');
40177 }
40178
40179 var eventDefinitions = businessObject.get('eventDefinitions'),
40180 eventDefinition = eventDefinitions[ 0 ];
40181
40182 return eventDefinition.get(getRootElementReferencePropertyName(eventDefinition));
40183 }
40184
40185 function setRootElement(businessObject, rootElement) {
40186 if (isAny(businessObject, [ 'bpmn:ReceiveTask', 'bpmn:SendTask' ])) {
40187 return businessObject.set('messageRef', rootElement);
40188 }
40189
40190 var eventDefinitions = businessObject.get('eventDefinitions'),
40191 eventDefinition = eventDefinitions[ 0 ];
40192
40193 return eventDefinition.set(getRootElementReferencePropertyName(eventDefinition), rootElement);
40194 }
40195
40196 // create shape
40197 this.executed('shape.create', function(context) {
40198 var shape = context.shape;
40199
40200 if (!canHaveRootElementReference(shape)) {
40201 return;
40202 }
40203
40204 var businessObject = getBusinessObject(shape),
40205 rootElement = getRootElement(businessObject),
40206 rootElements;
40207
40208 if (rootElement && !hasRootElement(rootElement)) {
40209 rootElements = bpmnjs.getDefinitions().get('rootElements');
40210
40211 // add root element
40212 add(rootElements, rootElement);
40213
40214 context.addedRootElement = rootElement;
40215 }
40216 }, true);
40217
40218 this.reverted('shape.create', function(context) {
40219 var addedRootElement = context.addedRootElement;
40220
40221 if (!addedRootElement) {
40222 return;
40223 }
40224
40225 var rootElements = bpmnjs.getDefinitions().get('rootElements');
40226
40227 // remove root element
40228 remove(rootElements, addedRootElement);
40229 }, true);
40230
40231 eventBus.on('copyPaste.copyElement', function(context) {
40232 var descriptor = context.descriptor,
40233 element = context.element;
40234
40235 if (!canHaveRootElementReference(element)) {
40236 return;
40237 }
40238
40239 var businessObject = getBusinessObject(element),
40240 rootElement = getRootElement(businessObject);
40241
40242 if (rootElement) {
40243 descriptor.referencedRootElement = rootElement;
40244 }
40245 });
40246
40247 eventBus.on('copyPaste.pasteElement', LOW_PRIORITY$b, function(context) {
40248 var descriptor = context.descriptor,
40249 businessObject = descriptor.businessObject;
40250
40251 if (!canHaveRootElementReference(businessObject)) {
40252 return;
40253 }
40254
40255 var referencedRootElement = descriptor.referencedRootElement;
40256
40257 if (!referencedRootElement) {
40258 return;
40259 }
40260
40261 if (!hasRootElement(referencedRootElement)) {
40262 referencedRootElement = moddleCopy.copyElement(
40263 referencedRootElement,
40264 bpmnFactory.create(referencedRootElement.$type)
40265 );
40266 }
40267
40268 setRootElement(businessObject, referencedRootElement);
40269 });
40270 }
40271
40272 RootElementReferenceBehavior.$inject = [
40273 'bpmnjs',
40274 'eventBus',
40275 'injector',
40276 'moddleCopy',
40277 'bpmnFactory'
40278 ];
40279
40280 inherits$1(RootElementReferenceBehavior, CommandInterceptor);
40281
40282 // helpers //////////
40283
40284 function hasAnyEventDefinition(element, types) {
40285 if (!isArray$2(types)) {
40286 types = [ types ];
40287 }
40288
40289 return some(types, function(type) {
40290 return hasEventDefinition$2(element, type);
40291 });
40292 }
40293
40294 function CreateBehavior(injector) {
40295 injector.invoke(CommandInterceptor, this);
40296
40297 this.preExecute('shape.create', 1500, function(event) {
40298 var context = event.context,
40299 parent = context.parent,
40300 shape = context.shape;
40301
40302 if (is$1(parent, 'bpmn:Lane') && !is$1(shape, 'bpmn:Lane')) {
40303 context.parent = getParent(parent, 'bpmn:Participant');
40304 }
40305 });
40306
40307 }
40308
40309
40310 CreateBehavior.$inject = [ 'injector' ];
40311
40312 inherits$1(CreateBehavior, CommandInterceptor);
40313
40314 var HIGH_PRIORITY$b = 1500;
40315 var HIGHEST_PRIORITY = 2000;
40316
40317
40318 /**
40319 * Correct hover targets in certain situations to improve diagram interaction.
40320 *
40321 * @param {ElementRegistry} elementRegistry
40322 * @param {EventBus} eventBus
40323 * @param {Canvas} canvas
40324 */
40325 function FixHoverBehavior(elementRegistry, eventBus, canvas) {
40326
40327 eventBus.on([
40328 'create.hover',
40329 'create.move',
40330 'create.out',
40331 'create.end',
40332 'shape.move.hover',
40333 'shape.move.move',
40334 'shape.move.out',
40335 'shape.move.end'
40336 ], HIGH_PRIORITY$b, function(event) {
40337 var context = event.context,
40338 shape = context.shape || event.shape,
40339 hover = event.hover;
40340
40341 // ensure elements are not dropped onto a bpmn:Lane but onto
40342 // the underlying bpmn:Participant
40343 if (is$1(hover, 'bpmn:Lane') && !isAny(shape, [ 'bpmn:Lane', 'bpmn:Participant' ])) {
40344 event.hover = getLanesRoot(hover);
40345 event.hoverGfx = elementRegistry.getGraphics(event.hover);
40346 }
40347
40348 var rootElement = canvas.getRootElement();
40349
40350 // ensure bpmn:Group and label elements are dropped
40351 // always onto the root
40352 if (hover !== rootElement && (shape.labelTarget || is$1(shape, 'bpmn:Group'))) {
40353 event.hover = rootElement;
40354 event.hoverGfx = elementRegistry.getGraphics(event.hover);
40355 }
40356 });
40357
40358 eventBus.on([
40359 'connect.hover',
40360 'connect.out',
40361 'connect.end',
40362 'connect.cleanup',
40363 'global-connect.hover',
40364 'global-connect.out',
40365 'global-connect.end',
40366 'global-connect.cleanup'
40367 ], HIGH_PRIORITY$b, function(event) {
40368 var hover = event.hover;
40369
40370 // ensure connections start/end on bpmn:Participant,
40371 // not the underlying bpmn:Lane
40372 if (is$1(hover, 'bpmn:Lane')) {
40373 event.hover = getLanesRoot(hover) || hover;
40374 event.hoverGfx = elementRegistry.getGraphics(event.hover);
40375 }
40376 });
40377
40378
40379 eventBus.on([
40380 'bendpoint.move.hover'
40381 ], HIGH_PRIORITY$b, function(event) {
40382 var context = event.context,
40383 hover = event.hover,
40384 type = context.type;
40385
40386 // ensure reconnect start/end on bpmn:Participant,
40387 // not the underlying bpmn:Lane
40388 if (is$1(hover, 'bpmn:Lane') && /reconnect/.test(type)) {
40389 event.hover = getLanesRoot(hover) || hover;
40390 event.hoverGfx = elementRegistry.getGraphics(event.hover);
40391 }
40392 });
40393
40394
40395 eventBus.on([
40396 'connect.start'
40397 ], HIGH_PRIORITY$b, function(event) {
40398 var context = event.context,
40399 start = context.start;
40400
40401 // ensure connect start on bpmn:Participant,
40402 // not the underlying bpmn:Lane
40403 if (is$1(start, 'bpmn:Lane')) {
40404 context.start = getLanesRoot(start) || start;
40405 }
40406 });
40407
40408
40409 // allow movement of participants from lanes
40410 eventBus.on('shape.move.start', HIGHEST_PRIORITY, function(event) {
40411 var shape = event.shape;
40412
40413 if (is$1(shape, 'bpmn:Lane')) {
40414 event.shape = getLanesRoot(shape) || shape;
40415 }
40416 });
40417
40418 }
40419
40420 FixHoverBehavior.$inject = [
40421 'elementRegistry',
40422 'eventBus',
40423 'canvas'
40424 ];
40425
40426 /**
40427 * BPMN specific create data object behavior
40428 */
40429 function CreateDataObjectBehavior(eventBus, bpmnFactory, moddle) {
40430
40431 CommandInterceptor.call(this, eventBus);
40432
40433 this.preExecute('shape.create', function(event) {
40434
40435 var context = event.context,
40436 shape = context.shape;
40437
40438 if (is$1(shape, 'bpmn:DataObjectReference') && shape.type !== 'label') {
40439
40440 // create a DataObject every time a DataObjectReference is created
40441 var dataObject = bpmnFactory.create('bpmn:DataObject');
40442
40443 // set the reference to the DataObject
40444 shape.businessObject.dataObjectRef = dataObject;
40445 }
40446 });
40447
40448 }
40449
40450 CreateDataObjectBehavior.$inject = [
40451 'eventBus',
40452 'bpmnFactory',
40453 'moddle'
40454 ];
40455
40456 inherits$1(CreateDataObjectBehavior, CommandInterceptor);
40457
40458 var HORIZONTAL_PARTICIPANT_PADDING = 20,
40459 VERTICAL_PARTICIPANT_PADDING = 20;
40460
40461 var PARTICIPANT_BORDER_WIDTH = 30;
40462
40463 var HIGH_PRIORITY$a = 2000;
40464
40465
40466 /**
40467 * BPMN-specific behavior for creating participants.
40468 */
40469 function CreateParticipantBehavior(canvas, eventBus, modeling) {
40470 CommandInterceptor.call(this, eventBus);
40471
40472 // fit participant
40473 eventBus.on([
40474 'create.start',
40475 'shape.move.start'
40476 ], HIGH_PRIORITY$a, function(event) {
40477 var context = event.context,
40478 shape = context.shape,
40479 rootElement = canvas.getRootElement();
40480
40481 if (!is$1(shape, 'bpmn:Participant') ||
40482 !is$1(rootElement, 'bpmn:Process') ||
40483 !rootElement.children.length) {
40484 return;
40485 }
40486
40487 // ignore connections, groups and labels
40488 var children = rootElement.children.filter(function(element) {
40489 return !is$1(element, 'bpmn:Group') &&
40490 !isLabel$6(element) &&
40491 !isConnection$9(element);
40492 });
40493
40494 // ensure for available children to calculate bounds
40495 if (!children.length) {
40496 return;
40497 }
40498
40499 var childrenBBox = getBBox(children);
40500
40501 var participantBounds = getParticipantBounds(shape, childrenBBox);
40502
40503 // assign width and height
40504 assign(shape, participantBounds);
40505
40506 // assign create constraints
40507 context.createConstraints = getParticipantCreateConstraints(shape, childrenBBox);
40508 });
40509
40510 // force hovering process when creating first participant
40511 eventBus.on('create.start', HIGH_PRIORITY$a, function(event) {
40512 var context = event.context,
40513 shape = context.shape,
40514 rootElement = canvas.getRootElement(),
40515 rootElementGfx = canvas.getGraphics(rootElement);
40516
40517 function ensureHoveringProcess(event) {
40518 event.element = rootElement;
40519 event.gfx = rootElementGfx;
40520 }
40521
40522 if (is$1(shape, 'bpmn:Participant') && is$1(rootElement, 'bpmn:Process')) {
40523 eventBus.on('element.hover', HIGH_PRIORITY$a, ensureHoveringProcess);
40524
40525 eventBus.once('create.cleanup', function() {
40526 eventBus.off('element.hover', ensureHoveringProcess);
40527 });
40528 }
40529 });
40530
40531 function ensureCollaboration(context) {
40532 var parent = context.parent,
40533 collaboration;
40534
40535 var rootElement = canvas.getRootElement();
40536
40537 if (is$1(rootElement, 'bpmn:Collaboration')) {
40538 collaboration = rootElement;
40539 } else {
40540
40541 // update root element by making collaboration
40542 collaboration = modeling.makeCollaboration();
40543
40544 // re-use process when creating first participant
40545 context.process = parent;
40546 }
40547
40548 context.parent = collaboration;
40549 }
40550
40551 // turn process into collaboration before adding participant
40552 this.preExecute('shape.create', function(context) {
40553 var parent = context.parent,
40554 shape = context.shape;
40555
40556 if (is$1(shape, 'bpmn:Participant') && is$1(parent, 'bpmn:Process')) {
40557 ensureCollaboration(context);
40558 }
40559 }, true);
40560
40561 this.execute('shape.create', function(context) {
40562 var process = context.process,
40563 shape = context.shape;
40564
40565 if (process) {
40566 context.oldProcessRef = shape.businessObject.processRef;
40567
40568 // re-use process when creating first participant
40569 shape.businessObject.processRef = process.businessObject;
40570 }
40571 }, true);
40572
40573 this.revert('shape.create', function(context) {
40574 var process = context.process,
40575 shape = context.shape;
40576
40577 if (process) {
40578
40579 // re-use process when creating first participant
40580 shape.businessObject.processRef = context.oldProcessRef;
40581 }
40582 }, true);
40583
40584 this.postExecute('shape.create', function(context) {
40585 var process = context.process,
40586 shape = context.shape;
40587
40588 if (process) {
40589
40590 // move children from process to participant
40591 var processChildren = process.children.slice();
40592
40593 modeling.moveElements(processChildren, { x: 0, y: 0 }, shape);
40594 }
40595
40596 }, true);
40597
40598 // turn process into collaboration when creating participants
40599 this.preExecute('elements.create', HIGH_PRIORITY$a, function(context) {
40600 var elements = context.elements,
40601 parent = context.parent,
40602 participant;
40603
40604 var hasParticipants = findParticipant(elements);
40605
40606 if (hasParticipants && is$1(parent, 'bpmn:Process')) {
40607 ensureCollaboration(context);
40608
40609 participant = findParticipant(elements);
40610
40611 context.oldProcessRef = participant.businessObject.processRef;
40612
40613 // re-use process when creating first participant
40614 participant.businessObject.processRef = parent.businessObject;
40615 }
40616 }, true);
40617
40618 this.revert('elements.create', function(context) {
40619 var elements = context.elements,
40620 process = context.process,
40621 participant;
40622
40623 if (process) {
40624 participant = findParticipant(elements);
40625
40626 // re-use process when creating first participant
40627 participant.businessObject.processRef = context.oldProcessRef;
40628 }
40629 }, true);
40630
40631 this.postExecute('elements.create', function(context) {
40632 var elements = context.elements,
40633 process = context.process,
40634 participant;
40635
40636 if (process) {
40637 participant = findParticipant(elements);
40638
40639 // move children from process to first participant
40640 var processChildren = process.children.slice();
40641
40642 modeling.moveElements(processChildren, { x: 0, y: 0 }, participant);
40643 }
40644
40645 }, true);
40646
40647 }
40648
40649 CreateParticipantBehavior.$inject = [
40650 'canvas',
40651 'eventBus',
40652 'modeling'
40653 ];
40654
40655 inherits$1(CreateParticipantBehavior, CommandInterceptor);
40656
40657 // helpers //////////
40658
40659 function getParticipantBounds(shape, childrenBBox) {
40660 childrenBBox = {
40661 width: childrenBBox.width + HORIZONTAL_PARTICIPANT_PADDING * 2 + PARTICIPANT_BORDER_WIDTH,
40662 height: childrenBBox.height + VERTICAL_PARTICIPANT_PADDING * 2
40663 };
40664
40665 var width = Math.max(shape.width, childrenBBox.width),
40666 height = Math.max(shape.height, childrenBBox.height);
40667
40668 return {
40669 x: -width / 2,
40670 y: -height / 2,
40671 width: width,
40672 height: height
40673 };
40674 }
40675
40676 function getParticipantCreateConstraints(shape, childrenBBox) {
40677 childrenBBox = asTRBL(childrenBBox);
40678
40679 return {
40680 bottom: childrenBBox.top + shape.height / 2 - VERTICAL_PARTICIPANT_PADDING,
40681 left: childrenBBox.right - shape.width / 2 + HORIZONTAL_PARTICIPANT_PADDING,
40682 top: childrenBBox.bottom - shape.height / 2 + VERTICAL_PARTICIPANT_PADDING,
40683 right: childrenBBox.left + shape.width / 2 - HORIZONTAL_PARTICIPANT_PADDING - PARTICIPANT_BORDER_WIDTH
40684 };
40685 }
40686
40687 function isConnection$9(element) {
40688 return !!element.waypoints;
40689 }
40690
40691 function findParticipant(elements) {
40692 return find(elements, function(element) {
40693 return is$1(element, 'bpmn:Participant');
40694 });
40695 }
40696
40697 var TARGET_REF_PLACEHOLDER_NAME = '__targetRef_placeholder';
40698
40699
40700 /**
40701 * This behavior makes sure we always set a fake
40702 * DataInputAssociation#targetRef as demanded by the BPMN 2.0
40703 * XSD schema.
40704 *
40705 * The reference is set to a bpmn:Property{ name: '__targetRef_placeholder' }
40706 * which is created on the fly and cleaned up afterwards if not needed
40707 * anymore.
40708 *
40709 * @param {EventBus} eventBus
40710 * @param {BpmnFactory} bpmnFactory
40711 */
40712 function DataInputAssociationBehavior(eventBus, bpmnFactory) {
40713
40714 CommandInterceptor.call(this, eventBus);
40715
40716
40717 this.executed([
40718 'connection.create',
40719 'connection.delete',
40720 'connection.move',
40721 'connection.reconnect'
40722 ], ifDataInputAssociation(fixTargetRef));
40723
40724 this.reverted([
40725 'connection.create',
40726 'connection.delete',
40727 'connection.move',
40728 'connection.reconnect'
40729 ], ifDataInputAssociation(fixTargetRef));
40730
40731
40732 function usesTargetRef(element, targetRef, removedConnection) {
40733
40734 var inputAssociations = element.get('dataInputAssociations');
40735
40736 return find(inputAssociations, function(association) {
40737 return association !== removedConnection &&
40738 association.targetRef === targetRef;
40739 });
40740 }
40741
40742 function getTargetRef(element, create) {
40743
40744 var properties = element.get('properties');
40745
40746 var targetRefProp = find(properties, function(p) {
40747 return p.name === TARGET_REF_PLACEHOLDER_NAME;
40748 });
40749
40750 if (!targetRefProp && create) {
40751 targetRefProp = bpmnFactory.create('bpmn:Property', {
40752 name: TARGET_REF_PLACEHOLDER_NAME
40753 });
40754
40755 add(properties, targetRefProp);
40756 }
40757
40758 return targetRefProp;
40759 }
40760
40761 function cleanupTargetRef(element, connection) {
40762
40763 var targetRefProp = getTargetRef(element);
40764
40765 if (!targetRefProp) {
40766 return;
40767 }
40768
40769 if (!usesTargetRef(element, targetRefProp, connection)) {
40770 remove(element.get('properties'), targetRefProp);
40771 }
40772 }
40773
40774 /**
40775 * Make sure targetRef is set to a valid property or
40776 * `null` if the connection is detached.
40777 *
40778 * @param {Event} event
40779 */
40780 function fixTargetRef(event) {
40781
40782 var context = event.context,
40783 connection = context.connection,
40784 connectionBo = connection.businessObject,
40785 target = connection.target,
40786 targetBo = target && target.businessObject,
40787 newTarget = context.newTarget,
40788 newTargetBo = newTarget && newTarget.businessObject,
40789 oldTarget = context.oldTarget || context.target,
40790 oldTargetBo = oldTarget && oldTarget.businessObject;
40791
40792 var dataAssociation = connection.businessObject,
40793 targetRefProp;
40794
40795 if (oldTargetBo && oldTargetBo !== targetBo) {
40796 cleanupTargetRef(oldTargetBo, connectionBo);
40797 }
40798
40799 if (newTargetBo && newTargetBo !== targetBo) {
40800 cleanupTargetRef(newTargetBo, connectionBo);
40801 }
40802
40803 if (targetBo) {
40804 targetRefProp = getTargetRef(targetBo, true);
40805 dataAssociation.targetRef = targetRefProp;
40806 } else {
40807 dataAssociation.targetRef = null;
40808 }
40809 }
40810 }
40811
40812 DataInputAssociationBehavior.$inject = [
40813 'eventBus',
40814 'bpmnFactory'
40815 ];
40816
40817 inherits$1(DataInputAssociationBehavior, CommandInterceptor);
40818
40819
40820 /**
40821 * Only call the given function when the event
40822 * touches a bpmn:DataInputAssociation.
40823 *
40824 * @param {Function} fn
40825 * @return {Function}
40826 */
40827 function ifDataInputAssociation(fn) {
40828
40829 return function(event) {
40830 var context = event.context,
40831 connection = context.connection;
40832
40833 if (is$1(connection, 'bpmn:DataInputAssociation')) {
40834 return fn(event);
40835 }
40836 };
40837 }
40838
40839 function UpdateSemanticParentHandler(bpmnUpdater) {
40840 this._bpmnUpdater = bpmnUpdater;
40841 }
40842
40843 UpdateSemanticParentHandler.$inject = [ 'bpmnUpdater' ];
40844
40845
40846 UpdateSemanticParentHandler.prototype.execute = function(context) {
40847 var dataStoreBo = context.dataStoreBo,
40848 newSemanticParent = context.newSemanticParent,
40849 newDiParent = context.newDiParent;
40850
40851 context.oldSemanticParent = dataStoreBo.$parent;
40852 context.oldDiParent = dataStoreBo.di.$parent;
40853
40854 // update semantic parent
40855 this._bpmnUpdater.updateSemanticParent(dataStoreBo, newSemanticParent);
40856
40857 // update DI parent
40858 this._bpmnUpdater.updateDiParent(dataStoreBo.di, newDiParent);
40859 };
40860
40861 UpdateSemanticParentHandler.prototype.revert = function(context) {
40862 var dataStoreBo = context.dataStoreBo,
40863 oldSemanticParent = context.oldSemanticParent,
40864 oldDiParent = context.oldDiParent;
40865
40866 // update semantic parent
40867 this._bpmnUpdater.updateSemanticParent(dataStoreBo, oldSemanticParent);
40868
40869 // update DI parent
40870 this._bpmnUpdater.updateDiParent(dataStoreBo.di, oldDiParent);
40871 };
40872
40873 /**
40874 * BPMN specific data store behavior
40875 */
40876 function DataStoreBehavior(
40877 canvas, commandStack, elementRegistry,
40878 eventBus) {
40879
40880 CommandInterceptor.call(this, eventBus);
40881
40882 commandStack.registerHandler('dataStore.updateContainment', UpdateSemanticParentHandler);
40883
40884 function getFirstParticipantWithProcessRef() {
40885 return elementRegistry.filter(function(element) {
40886 return is$1(element, 'bpmn:Participant') && getBusinessObject(element).processRef;
40887 })[0];
40888 }
40889
40890 function getDataStores(element) {
40891 return element.children.filter(function(child) {
40892 return is$1(child, 'bpmn:DataStoreReference') && !child.labelTarget;
40893 });
40894 }
40895
40896 function updateDataStoreParent(dataStore, newDataStoreParent) {
40897 var dataStoreBo = dataStore.businessObject || dataStore;
40898
40899 newDataStoreParent = newDataStoreParent || getFirstParticipantWithProcessRef();
40900
40901 if (newDataStoreParent) {
40902 var newDataStoreParentBo = newDataStoreParent.businessObject || newDataStoreParent;
40903
40904 commandStack.execute('dataStore.updateContainment', {
40905 dataStoreBo: dataStoreBo,
40906 newSemanticParent: newDataStoreParentBo.processRef || newDataStoreParentBo,
40907 newDiParent: newDataStoreParentBo.di
40908 });
40909 }
40910 }
40911
40912
40913 // disable auto-resize for data stores
40914 this.preExecute('shape.create', function(event) {
40915
40916 var context = event.context,
40917 shape = context.shape;
40918
40919 if (is$1(shape, 'bpmn:DataStoreReference') &&
40920 shape.type !== 'label') {
40921
40922 if (!context.hints) {
40923 context.hints = {};
40924 }
40925
40926 // prevent auto resizing
40927 context.hints.autoResize = false;
40928 }
40929 });
40930
40931
40932 // disable auto-resize for data stores
40933 this.preExecute('elements.move', function(event) {
40934 var context = event.context,
40935 shapes = context.shapes;
40936
40937 var dataStoreReferences = shapes.filter(function(shape) {
40938 return is$1(shape, 'bpmn:DataStoreReference');
40939 });
40940
40941 if (dataStoreReferences.length) {
40942 if (!context.hints) {
40943 context.hints = {};
40944 }
40945
40946 // prevent auto resizing for data store references
40947 context.hints.autoResize = shapes.filter(function(shape) {
40948 return !is$1(shape, 'bpmn:DataStoreReference');
40949 });
40950 }
40951 });
40952
40953
40954 // update parent on data store created
40955 this.postExecute('shape.create', function(event) {
40956 var context = event.context,
40957 shape = context.shape,
40958 parent = shape.parent;
40959
40960
40961 if (is$1(shape, 'bpmn:DataStoreReference') &&
40962 shape.type !== 'label' &&
40963 is$1(parent, 'bpmn:Collaboration')) {
40964
40965 updateDataStoreParent(shape);
40966 }
40967 });
40968
40969
40970 // update parent on data store moved
40971 this.postExecute('shape.move', function(event) {
40972 var context = event.context,
40973 shape = context.shape,
40974 oldParent = context.oldParent,
40975 parent = shape.parent;
40976
40977 if (is$1(oldParent, 'bpmn:Collaboration')) {
40978
40979 // do nothing if not necessary
40980 return;
40981 }
40982
40983 if (is$1(shape, 'bpmn:DataStoreReference') &&
40984 shape.type !== 'label' &&
40985 is$1(parent, 'bpmn:Collaboration')) {
40986
40987 var participant = is$1(oldParent, 'bpmn:Participant') ?
40988 oldParent :
40989 getAncestor(oldParent, 'bpmn:Participant');
40990
40991 updateDataStoreParent(shape, participant);
40992 }
40993 });
40994
40995
40996 // update data store parents on participant or subprocess deleted
40997 this.postExecute('shape.delete', function(event) {
40998 var context = event.context,
40999 shape = context.shape,
41000 rootElement = canvas.getRootElement();
41001
41002 if (isAny(shape, [ 'bpmn:Participant', 'bpmn:SubProcess' ])
41003 && is$1(rootElement, 'bpmn:Collaboration')) {
41004 getDataStores(rootElement)
41005 .filter(function(dataStore) {
41006 return isDescendant(dataStore, shape);
41007 })
41008 .forEach(function(dataStore) {
41009 updateDataStoreParent(dataStore);
41010 });
41011 }
41012 });
41013
41014 // update data store parents on collaboration -> process
41015 this.postExecute('canvas.updateRoot', function(event) {
41016 var context = event.context,
41017 oldRoot = context.oldRoot,
41018 newRoot = context.newRoot;
41019
41020 var dataStores = getDataStores(oldRoot);
41021
41022 dataStores.forEach(function(dataStore) {
41023
41024 if (is$1(newRoot, 'bpmn:Process')) {
41025 updateDataStoreParent(dataStore, newRoot);
41026 }
41027
41028 });
41029 });
41030 }
41031
41032 DataStoreBehavior.$inject = [
41033 'canvas',
41034 'commandStack',
41035 'elementRegistry',
41036 'eventBus',
41037 ];
41038
41039 inherits$1(DataStoreBehavior, CommandInterceptor);
41040
41041
41042 // helpers //////////
41043
41044 function isDescendant(descendant, ancestor) {
41045 var descendantBo = descendant.businessObject || descendant,
41046 ancestorBo = ancestor.businessObject || ancestor;
41047
41048 while (descendantBo.$parent) {
41049 if (descendantBo.$parent === ancestorBo.processRef || ancestorBo) {
41050 return true;
41051 }
41052
41053 descendantBo = descendantBo.$parent;
41054 }
41055
41056 return false;
41057 }
41058
41059 function getAncestor(element, type) {
41060
41061 while (element.parent) {
41062 if (is$1(element.parent, type)) {
41063 return element.parent;
41064 }
41065
41066 element = element.parent;
41067 }
41068 }
41069
41070 var LOW_PRIORITY$a = 500;
41071
41072
41073 /**
41074 * BPMN specific delete lane behavior
41075 */
41076 function DeleteLaneBehavior(eventBus, modeling, spaceTool) {
41077
41078 CommandInterceptor.call(this, eventBus);
41079
41080
41081 function compensateLaneDelete(shape, oldParent) {
41082
41083 var siblings = getChildLanes(oldParent);
41084
41085 var topAffected = [];
41086 var bottomAffected = [];
41087
41088 eachElement(siblings, function(element) {
41089
41090 if (element.y > shape.y) {
41091 bottomAffected.push(element);
41092 } else {
41093 topAffected.push(element);
41094 }
41095
41096 return element.children;
41097 });
41098
41099 if (!siblings.length) {
41100 return;
41101 }
41102
41103 var offset;
41104
41105 if (bottomAffected.length && topAffected.length) {
41106 offset = shape.height / 2;
41107 } else {
41108 offset = shape.height;
41109 }
41110
41111 var topAdjustments,
41112 bottomAdjustments;
41113
41114 if (topAffected.length) {
41115 topAdjustments = spaceTool.calculateAdjustments(
41116 topAffected, 'y', offset, shape.y - 10);
41117
41118 spaceTool.makeSpace(
41119 topAdjustments.movingShapes,
41120 topAdjustments.resizingShapes,
41121 { x: 0, y: offset }, 's');
41122 }
41123
41124 if (bottomAffected.length) {
41125 bottomAdjustments = spaceTool.calculateAdjustments(
41126 bottomAffected, 'y', -offset, shape.y + shape.height + 10);
41127
41128 spaceTool.makeSpace(
41129 bottomAdjustments.movingShapes,
41130 bottomAdjustments.resizingShapes,
41131 { x: 0, y: -offset }, 'n');
41132 }
41133 }
41134
41135
41136 /**
41137 * Adjust sizes of other lanes after lane deletion
41138 */
41139 this.postExecuted('shape.delete', LOW_PRIORITY$a, function(event) {
41140
41141 var context = event.context,
41142 hints = context.hints,
41143 shape = context.shape,
41144 oldParent = context.oldParent;
41145
41146 // only compensate lane deletes
41147 if (!is$1(shape, 'bpmn:Lane')) {
41148 return;
41149 }
41150
41151 // compensate root deletes only
41152 if (hints && hints.nested) {
41153 return;
41154 }
41155
41156 compensateLaneDelete(shape, oldParent);
41157 });
41158 }
41159
41160 DeleteLaneBehavior.$inject = [
41161 'eventBus',
41162 'modeling',
41163 'spaceTool'
41164 ];
41165
41166 inherits$1(DeleteLaneBehavior, CommandInterceptor);
41167
41168 var LOW_PRIORITY$9 = 500;
41169
41170
41171 /**
41172 * Replace boundary event with intermediate event when creating or moving results in detached event.
41173 */
41174 function DetachEventBehavior(bpmnReplace, injector) {
41175 injector.invoke(CommandInterceptor, this);
41176
41177 this._bpmnReplace = bpmnReplace;
41178
41179 var self = this;
41180
41181 this.postExecuted('elements.create', LOW_PRIORITY$9, function(context) {
41182 var elements = context.elements;
41183
41184 elements.filter(function(shape) {
41185 var host = shape.host;
41186
41187 return shouldReplace(shape, host);
41188 }).map(function(shape) {
41189 return elements.indexOf(shape);
41190 }).forEach(function(index) {
41191 context.elements[ index ] = self.replaceShape(elements[ index ]);
41192 });
41193 }, true);
41194
41195 this.preExecute('elements.move', LOW_PRIORITY$9, function(context) {
41196 var shapes = context.shapes,
41197 newHost = context.newHost;
41198
41199 shapes.forEach(function(shape, index) {
41200 var host = shape.host;
41201
41202 if (shouldReplace(shape, includes$6(shapes, host) ? host : newHost)) {
41203 shapes[ index ] = self.replaceShape(shape);
41204 }
41205 });
41206 }, true);
41207 }
41208
41209 DetachEventBehavior.$inject = [
41210 'bpmnReplace',
41211 'injector'
41212 ];
41213
41214 inherits$1(DetachEventBehavior, CommandInterceptor);
41215
41216 DetachEventBehavior.prototype.replaceShape = function(shape) {
41217 var eventDefinition = getEventDefinition(shape),
41218 intermediateEvent;
41219
41220 if (eventDefinition) {
41221 intermediateEvent = {
41222 type: 'bpmn:IntermediateCatchEvent',
41223 eventDefinitionType: eventDefinition.$type
41224 };
41225 } else {
41226 intermediateEvent = {
41227 type: 'bpmn:IntermediateThrowEvent'
41228 };
41229 }
41230
41231 return this._bpmnReplace.replaceElement(shape, intermediateEvent, { layoutConnection: false });
41232 };
41233
41234
41235 // helpers //////////
41236
41237 function getEventDefinition(element) {
41238 var businessObject = getBusinessObject(element),
41239 eventDefinitions = businessObject.eventDefinitions;
41240
41241 return eventDefinitions && eventDefinitions[0];
41242 }
41243
41244 function shouldReplace(shape, host) {
41245 return !isLabel$6(shape) && is$1(shape, 'bpmn:BoundaryEvent') && !host;
41246 }
41247
41248 function includes$6(array, item) {
41249 return array.indexOf(item) !== -1;
41250 }
41251
41252 function DropOnFlowBehavior(eventBus, bpmnRules, modeling) {
41253
41254 CommandInterceptor.call(this, eventBus);
41255
41256 /**
41257 * Reconnect start / end of a connection after
41258 * dropping an element on a flow.
41259 */
41260
41261 function insertShape(shape, targetFlow, positionOrBounds) {
41262 var waypoints = targetFlow.waypoints,
41263 waypointsBefore,
41264 waypointsAfter,
41265 dockingPoint,
41266 source,
41267 target,
41268 incomingConnection,
41269 outgoingConnection,
41270 oldOutgoing = shape.outgoing.slice(),
41271 oldIncoming = shape.incoming.slice();
41272
41273 var mid;
41274
41275 if (isNumber(positionOrBounds.width)) {
41276 mid = getMid(positionOrBounds);
41277 } else {
41278 mid = positionOrBounds;
41279 }
41280
41281 var intersection = getApproxIntersection(waypoints, mid);
41282
41283 if (intersection) {
41284 waypointsBefore = waypoints.slice(0, intersection.index);
41285 waypointsAfter = waypoints.slice(intersection.index + (intersection.bendpoint ? 1 : 0));
41286
41287 // due to inaccuracy intersection might have been found
41288 if (!waypointsBefore.length || !waypointsAfter.length) {
41289 return;
41290 }
41291
41292 dockingPoint = intersection.bendpoint ? waypoints[intersection.index] : mid;
41293
41294 // if last waypointBefore is inside shape's bounds, ignore docking point
41295 if (!isPointInsideBBox(shape, waypointsBefore[waypointsBefore.length-1])) {
41296 waypointsBefore.push(copy(dockingPoint));
41297 }
41298
41299 // if first waypointAfter is inside shape's bounds, ignore docking point
41300 if (!isPointInsideBBox(shape, waypointsAfter[0])) {
41301 waypointsAfter.unshift(copy(dockingPoint));
41302 }
41303 }
41304
41305 source = targetFlow.source;
41306 target = targetFlow.target;
41307
41308 if (bpmnRules.canConnect(source, shape, targetFlow)) {
41309
41310 // reconnect source -> inserted shape
41311 modeling.reconnectEnd(targetFlow, shape, waypointsBefore || mid);
41312
41313 incomingConnection = targetFlow;
41314 }
41315
41316 if (bpmnRules.canConnect(shape, target, targetFlow)) {
41317
41318 if (!incomingConnection) {
41319
41320 // reconnect inserted shape -> end
41321 modeling.reconnectStart(targetFlow, shape, waypointsAfter || mid);
41322
41323 outgoingConnection = targetFlow;
41324 } else {
41325 outgoingConnection = modeling.connect(
41326 shape, target, { type: targetFlow.type, waypoints: waypointsAfter }
41327 );
41328 }
41329 }
41330
41331 var duplicateConnections = [].concat(
41332
41333 incomingConnection && filter(oldIncoming, function(connection) {
41334 return connection.source === incomingConnection.source;
41335 }) || [],
41336
41337 outgoingConnection && filter(oldOutgoing, function(connection) {
41338 return connection.target === outgoingConnection.target;
41339 }) || []
41340 );
41341
41342 if (duplicateConnections.length) {
41343 modeling.removeElements(duplicateConnections);
41344 }
41345 }
41346
41347 this.preExecute('elements.move', function(context) {
41348
41349 var newParent = context.newParent,
41350 shapes = context.shapes,
41351 delta = context.delta,
41352 shape = shapes[0];
41353
41354 if (!shape || !newParent) {
41355 return;
41356 }
41357
41358 // if the new parent is a connection,
41359 // change it to the new parent's parent
41360 if (newParent && newParent.waypoints) {
41361 context.newParent = newParent = newParent.parent;
41362 }
41363
41364 var shapeMid = getMid(shape);
41365 var newShapeMid = {
41366 x: shapeMid.x + delta.x,
41367 y: shapeMid.y + delta.y
41368 };
41369
41370 // find a connection which intersects with the
41371 // element's mid point
41372 var connection = find(newParent.children, function(element) {
41373 var canInsert = bpmnRules.canInsert(shapes, element);
41374
41375 return canInsert && getApproxIntersection(element.waypoints, newShapeMid);
41376 });
41377
41378 if (connection) {
41379 context.targetFlow = connection;
41380 context.position = newShapeMid;
41381 }
41382
41383 }, true);
41384
41385 this.postExecuted('elements.move', function(context) {
41386
41387 var shapes = context.shapes,
41388 targetFlow = context.targetFlow,
41389 position = context.position;
41390
41391 if (targetFlow) {
41392 insertShape(shapes[0], targetFlow, position);
41393 }
41394
41395 }, true);
41396
41397 this.preExecute('shape.create', function(context) {
41398
41399 var parent = context.parent,
41400 shape = context.shape;
41401
41402 if (bpmnRules.canInsert(shape, parent)) {
41403 context.targetFlow = parent;
41404 context.parent = parent.parent;
41405 }
41406 }, true);
41407
41408 this.postExecuted('shape.create', function(context) {
41409
41410 var shape = context.shape,
41411 targetFlow = context.targetFlow,
41412 positionOrBounds = context.position;
41413
41414 if (targetFlow) {
41415 insertShape(shape, targetFlow, positionOrBounds);
41416 }
41417 }, true);
41418 }
41419
41420 inherits$1(DropOnFlowBehavior, CommandInterceptor);
41421
41422 DropOnFlowBehavior.$inject = [
41423 'eventBus',
41424 'bpmnRules',
41425 'modeling'
41426 ];
41427
41428
41429 // helpers /////////////////////
41430
41431 function isPointInsideBBox(bbox, point) {
41432 var x = point.x,
41433 y = point.y;
41434
41435 return x >= bbox.x &&
41436 x <= bbox.x + bbox.width &&
41437 y >= bbox.y &&
41438 y <= bbox.y + bbox.height;
41439 }
41440
41441 function copy(obj) {
41442 return assign({}, obj);
41443 }
41444
41445 function EventBasedGatewayBehavior(eventBus, modeling) {
41446
41447 CommandInterceptor.call(this, eventBus);
41448
41449 /**
41450 * Remove existing sequence flows of event-based target before connecting
41451 * from event-based gateway.
41452 */
41453 this.preExecuted('connection.create', function(event) {
41454
41455 var context = event.context,
41456 source = context.source,
41457 target = context.target,
41458 existingIncomingConnections = target.incoming.slice();
41459
41460 if (context.hints && context.hints.createElementsBehavior === false) {
41461 return;
41462 }
41463
41464 if (
41465 is$1(source, 'bpmn:EventBasedGateway') &&
41466 target.incoming.length
41467 ) {
41468
41469 existingIncomingConnections.filter(isSequenceFlow)
41470 .forEach(function(sequenceFlow) {
41471 modeling.removeConnection(sequenceFlow);
41472 });
41473 }
41474 });
41475
41476 /**
41477 * After replacing shape with event-based gateway, remove incoming sequence
41478 * flows of event-based targets which do not belong to event-based gateway
41479 * source.
41480 */
41481 this.preExecuted('shape.replace', function(event) {
41482
41483 var newShape = event.context.newShape,
41484 newShapeTargets,
41485 newShapeTargetsIncomingSequenceFlows;
41486
41487 if (!is$1(newShape, 'bpmn:EventBasedGateway')) {
41488 return;
41489 }
41490
41491 newShapeTargets = newShape.outgoing.filter(isSequenceFlow)
41492 .map(function(sequenceFlow) {
41493 return sequenceFlow.target;
41494 });
41495
41496 newShapeTargetsIncomingSequenceFlows = newShapeTargets.reduce(function(sequenceFlows, target) {
41497 var incomingSequenceFlows = target.incoming.filter(isSequenceFlow);
41498
41499 return sequenceFlows.concat(incomingSequenceFlows);
41500 }, []);
41501
41502 newShapeTargetsIncomingSequenceFlows.forEach(function(sequenceFlow) {
41503 if (sequenceFlow.source !== newShape) {
41504 modeling.removeConnection(sequenceFlow);
41505 }
41506 });
41507 });
41508 }
41509
41510 EventBasedGatewayBehavior.$inject = [
41511 'eventBus',
41512 'modeling'
41513 ];
41514
41515 inherits$1(EventBasedGatewayBehavior, CommandInterceptor);
41516
41517
41518
41519 // helpers //////////////////////
41520
41521 function isSequenceFlow(connection) {
41522 return is$1(connection, 'bpmn:SequenceFlow');
41523 }
41524
41525 var HIGH_PRIORITY$9 = 2000;
41526
41527
41528 /**
41529 * BPMN specific Group behavior
41530 */
41531 function GroupBehavior(
41532 bpmnFactory,
41533 canvas,
41534 elementRegistry,
41535 eventBus,
41536 injector,
41537 moddleCopy
41538 ) {
41539 injector.invoke(CommandInterceptor, this);
41540
41541 /**
41542 * Gets process definitions
41543 *
41544 * @return {ModdleElement} definitions
41545 */
41546 function getDefinitions() {
41547 var rootElement = canvas.getRootElement(),
41548 businessObject = getBusinessObject(rootElement);
41549
41550 return businessObject.$parent;
41551 }
41552
41553 /**
41554 * Removes a referenced category value for a given group shape
41555 *
41556 * @param {djs.model.Shape} shape
41557 */
41558 function removeReferencedCategoryValue(shape) {
41559
41560 var businessObject = getBusinessObject(shape),
41561 categoryValue = businessObject.categoryValueRef;
41562
41563 if (!categoryValue) {
41564 return;
41565 }
41566
41567 var category = categoryValue.$parent;
41568
41569 if (!categoryValue) {
41570 return;
41571 }
41572
41573 remove(category.categoryValue, categoryValue);
41574
41575 // cleanup category if it is empty
41576 if (category && !category.categoryValue.length) {
41577 removeCategory(category);
41578 }
41579 }
41580
41581 /**
41582 * Removes a given category from the definitions
41583 *
41584 * @param {ModdleElement} category
41585 */
41586 function removeCategory(category) {
41587
41588 var definitions = getDefinitions();
41589
41590 remove(definitions.get('rootElements'), category);
41591 }
41592
41593 /**
41594 * Returns all group element in the current registry
41595 *
41596 * @return {Array<djs.model.shape>} a list of group shapes
41597 */
41598 function getGroupElements() {
41599 return elementRegistry.filter(function(e) {
41600 return is$1(e, 'bpmn:Group');
41601 });
41602 }
41603
41604 /**
41605 * Returns true if given categoryValue is referenced in one of the given elements
41606 *
41607 * @param {Array<djs.model.shape>} elements
41608 * @param {ModdleElement} categoryValue
41609 * @return {boolean}
41610 */
41611 function isReferenced(elements, categoryValue) {
41612 return elements.some(function(e) {
41613
41614 var businessObject = getBusinessObject(e);
41615
41616 return businessObject.categoryValueRef
41617 && businessObject.categoryValueRef === categoryValue;
41618 });
41619 }
41620
41621 /**
41622 * remove referenced category + value when group was deleted
41623 */
41624 this.executed('shape.delete', function(event) {
41625
41626 var context = event.context,
41627 shape = context.shape;
41628
41629 if (is$1(shape, 'bpmn:Group')) {
41630
41631 var businessObject = getBusinessObject(shape),
41632 categoryValueRef = businessObject.categoryValueRef,
41633 groupElements = getGroupElements();
41634
41635 if (!isReferenced(groupElements, categoryValueRef)) {
41636 removeReferencedCategoryValue(shape);
41637 }
41638 }
41639 });
41640
41641 /**
41642 * re-attach removed category
41643 */
41644 this.reverted('shape.delete', function(event) {
41645
41646 var context = event.context,
41647 shape = context.shape;
41648
41649 if (is$1(shape, 'bpmn:Group')) {
41650
41651 var businessObject = getBusinessObject(shape),
41652 categoryValueRef = businessObject.categoryValueRef,
41653 definitions = getDefinitions(),
41654 category = categoryValueRef ? categoryValueRef.$parent : null;
41655
41656 add(category.get('categoryValue'), categoryValueRef);
41657 add(definitions.get('rootElements'), category);
41658 }
41659 });
41660
41661 /**
41662 * create new category + value when group was created
41663 */
41664 this.execute('shape.create', function(event) {
41665 var context = event.context,
41666 shape = context.shape,
41667 businessObject = getBusinessObject(shape);
41668
41669 if (is$1(businessObject, 'bpmn:Group') && !businessObject.categoryValueRef) {
41670
41671 var definitions = getDefinitions(),
41672 categoryValue = createCategoryValue(definitions, bpmnFactory);
41673
41674 // link the reference to the Group
41675 businessObject.categoryValueRef = categoryValue;
41676 }
41677 });
41678
41679
41680 this.revert('shape.create', function(event) {
41681
41682 var context = event.context,
41683 shape = context.shape;
41684
41685 if (is$1(shape, 'bpmn:Group')) {
41686 removeReferencedCategoryValue(shape);
41687
41688 delete getBusinessObject(shape).categoryValueRef;
41689
41690 }
41691 });
41692
41693 // copy bpmn:CategoryValue when copying element
41694 eventBus.on('moddleCopy.canCopyProperty', HIGH_PRIORITY$9, function(context) {
41695 var property = context.property,
41696 categoryValue;
41697
41698 if (is$1(property, 'bpmn:CategoryValue')) {
41699 categoryValue = createCategoryValue(getDefinitions(), bpmnFactory);
41700
41701 // return copy of category
41702 return moddleCopy.copyElement(property, categoryValue);
41703 }
41704 });
41705
41706 }
41707
41708 GroupBehavior.$inject = [
41709 'bpmnFactory',
41710 'canvas',
41711 'elementRegistry',
41712 'eventBus',
41713 'injector',
41714 'moddleCopy'
41715 ];
41716
41717 inherits$1(GroupBehavior, CommandInterceptor);
41718
41719 /**
41720 * Returns the intersection between two line segments a and b.
41721 *
41722 * @param {Point} l1s
41723 * @param {Point} l1e
41724 * @param {Point} l2s
41725 * @param {Point} l2e
41726 *
41727 * @return {Point}
41728 */
41729 function lineIntersect(l1s, l1e, l2s, l2e) {
41730
41731 // if the lines intersect, the result contains the x and y of the
41732 // intersection (treating the lines as infinite) and booleans for
41733 // whether line segment 1 or line segment 2 contain the point
41734 var denominator, a, b, c, numerator;
41735
41736 denominator = ((l2e.y - l2s.y) * (l1e.x - l1s.x)) - ((l2e.x - l2s.x) * (l1e.y - l1s.y));
41737
41738 if (denominator == 0) {
41739 return null;
41740 }
41741
41742 a = l1s.y - l2s.y;
41743 b = l1s.x - l2s.x;
41744 numerator = ((l2e.x - l2s.x) * a) - ((l2e.y - l2s.y) * b);
41745
41746 c = numerator / denominator;
41747
41748 // if we cast these lines infinitely in
41749 // both directions, they intersect here
41750 return {
41751 x: Math.round(l1s.x + (c * (l1e.x - l1s.x))),
41752 y: Math.round(l1s.y + (c * (l1e.y - l1s.y)))
41753 };
41754 }
41755
41756 /**
41757 * Fix broken dockings after DI imports.
41758 *
41759 * @param {EventBus} eventBus
41760 */
41761 function ImportDockingFix(eventBus) {
41762
41763 function adjustDocking(startPoint, nextPoint, elementMid) {
41764
41765 var elementTop = {
41766 x: elementMid.x,
41767 y: elementMid.y - 50
41768 };
41769
41770 var elementLeft = {
41771 x: elementMid.x - 50,
41772 y: elementMid.y
41773 };
41774
41775 var verticalIntersect = lineIntersect(startPoint, nextPoint, elementMid, elementTop),
41776 horizontalIntersect = lineIntersect(startPoint, nextPoint, elementMid, elementLeft);
41777
41778 // original is horizontal or vertical center cross intersection
41779 var centerIntersect;
41780
41781 if (verticalIntersect && horizontalIntersect) {
41782 if (getDistance$1(verticalIntersect, elementMid) > getDistance$1(horizontalIntersect, elementMid)) {
41783 centerIntersect = horizontalIntersect;
41784 } else {
41785 centerIntersect = verticalIntersect;
41786 }
41787 } else {
41788 centerIntersect = verticalIntersect || horizontalIntersect;
41789 }
41790
41791 startPoint.original = centerIntersect;
41792 }
41793
41794 function fixDockings(connection) {
41795 var waypoints = connection.waypoints;
41796
41797 adjustDocking(
41798 waypoints[0],
41799 waypoints[1],
41800 getMid(connection.source)
41801 );
41802
41803 adjustDocking(
41804 waypoints[waypoints.length - 1],
41805 waypoints[waypoints.length - 2],
41806 getMid(connection.target)
41807 );
41808 }
41809
41810 eventBus.on('bpmnElement.added', function(e) {
41811
41812 var element = e.element;
41813
41814 if (element.waypoints) {
41815 fixDockings(element);
41816 }
41817 });
41818 }
41819
41820 ImportDockingFix.$inject = [
41821 'eventBus'
41822 ];
41823
41824
41825 // helpers //////////////////////
41826
41827 function getDistance$1(p1, p2) {
41828 return Math.sqrt(Math.pow(p1.x - p2.x, 2) + Math.pow(p1.y - p2.y, 2));
41829 }
41830
41831 /**
41832 * A component that makes sure that each created or updated
41833 * Pool and Lane is assigned an isHorizontal property set to true.
41834 *
41835 * @param {EventBus} eventBus
41836 */
41837 function IsHorizontalFix(eventBus) {
41838
41839 CommandInterceptor.call(this, eventBus);
41840
41841 var elementTypesToUpdate = [
41842 'bpmn:Participant',
41843 'bpmn:Lane'
41844 ];
41845
41846 this.executed([ 'shape.move', 'shape.create', 'shape.resize' ], function(event) {
41847 var bo = getBusinessObject(event.context.shape);
41848
41849 if (isAny(bo, elementTypesToUpdate) && !bo.di.get('isHorizontal')) {
41850
41851 // set attribute directly to avoid modeling#updateProperty side effects
41852 bo.di.set('isHorizontal', true);
41853 }
41854 });
41855
41856 }
41857
41858 IsHorizontalFix.$inject = [ 'eventBus' ];
41859
41860 inherits$1(IsHorizontalFix, CommandInterceptor);
41861
41862 /**
41863 * Returns the length of a vector
41864 *
41865 * @param {Vector}
41866 * @return {Float}
41867 */
41868 function vectorLength(v) {
41869 return Math.sqrt(Math.pow(v.x, 2) + Math.pow(v.y, 2));
41870 }
41871
41872
41873 /**
41874 * Calculates the angle between a line a the yAxis
41875 *
41876 * @param {Array}
41877 * @return {Float}
41878 */
41879 function getAngle(line) {
41880
41881 // return value is between 0, 180 and -180, -0
41882 // @janstuemmel: maybe replace return a/b with b/a
41883 return Math.atan((line[1].y - line[0].y) / (line[1].x - line[0].x));
41884 }
41885
41886
41887 /**
41888 * Rotates a vector by a given angle
41889 *
41890 * @param {Vector}
41891 * @param {Float} Angle in radians
41892 * @return {Vector}
41893 */
41894 function rotateVector(vector, angle) {
41895 return (!angle) ? vector : {
41896 x: Math.cos(angle) * vector.x - Math.sin(angle) * vector.y,
41897 y: Math.sin(angle) * vector.x + Math.cos(angle) * vector.y
41898 };
41899 }
41900
41901
41902 /**
41903 * Solves a 2D equation system
41904 * a + r*b = c, where a,b,c are 2D vectors
41905 *
41906 * @param {Vector}
41907 * @param {Vector}
41908 * @param {Vector}
41909 * @return {Float}
41910 */
41911 function solveLambaSystem(a, b, c) {
41912
41913 // the 2d system
41914 var system = [
41915 { n: a[0] - c[0], lambda: b[0] },
41916 { n: a[1] - c[1], lambda: b[1] }
41917 ];
41918
41919 // solve
41920 var n = system[0].n * b[0] + system[1].n * b[1],
41921 l = system[0].lambda * b[0] + system[1].lambda * b[1];
41922
41923 return -n/l;
41924 }
41925
41926
41927 /**
41928 * Position of perpendicular foot
41929 *
41930 * @param {Point}
41931 * @param [ {Point}, {Point} ] line defined through two points
41932 * @return {Point} the perpendicular foot position
41933 */
41934 function perpendicularFoot(point, line) {
41935
41936 var a = line[0], b = line[1];
41937
41938 // relative position of b from a
41939 var bd = { x: b.x - a.x, y: b.y - a.y };
41940
41941 // solve equation system to the parametrized vectors param real value
41942 var r = solveLambaSystem([ a.x, a.y ], [ bd.x, bd.y ], [ point.x, point.y ]);
41943
41944 return { x: a.x + r*bd.x, y: a.y + r*bd.y };
41945 }
41946
41947
41948 /**
41949 * Calculates the distance between a point and a line
41950 *
41951 * @param {Point}
41952 * @param [ {Point}, {Point} ] line defined through two points
41953 * @return {Float} distance
41954 */
41955 function getDistancePointLine(point, line) {
41956
41957 var pfPoint = perpendicularFoot(point, line);
41958
41959 // distance vector
41960 var connectionVector = {
41961 x: pfPoint.x - point.x,
41962 y: pfPoint.y - point.y
41963 };
41964
41965 return vectorLength(connectionVector);
41966 }
41967
41968
41969 /**
41970 * Calculates the distance between two points
41971 *
41972 * @param {Point}
41973 * @param {Point}
41974 * @return {Float} distance
41975 */
41976 function getDistancePointPoint(point1, point2) {
41977
41978 return vectorLength({
41979 x: point1.x - point2.x,
41980 y: point1.y - point2.y
41981 });
41982 }
41983
41984 var sqrt = Math.sqrt,
41985 min$1 = Math.min,
41986 max$3 = Math.max,
41987 abs$3 = Math.abs;
41988
41989 /**
41990 * Calculate the square (power to two) of a number.
41991 *
41992 * @param {number} n
41993 *
41994 * @return {number}
41995 */
41996 function sq(n) {
41997 return Math.pow(n, 2);
41998 }
41999
42000 /**
42001 * Get distance between two points.
42002 *
42003 * @param {Point} p1
42004 * @param {Point} p2
42005 *
42006 * @return {number}
42007 */
42008 function getDistance(p1, p2) {
42009 return sqrt(sq(p1.x - p2.x) + sq(p1.y - p2.y));
42010 }
42011
42012 /**
42013 * Return the attachment of the given point on the specified line.
42014 *
42015 * The attachment is either a bendpoint (attached to the given point)
42016 * or segment (attached to a location on a line segment) attachment:
42017 *
42018 * ```javascript
42019 * var pointAttachment = {
42020 * type: 'bendpoint',
42021 * bendpointIndex: 3,
42022 * position: { x: 10, y: 10 } // the attach point on the line
42023 * };
42024 *
42025 * var segmentAttachment = {
42026 * type: 'segment',
42027 * segmentIndex: 2,
42028 * relativeLocation: 0.31, // attach point location between 0 (at start) and 1 (at end)
42029 * position: { x: 10, y: 10 } // the attach point on the line
42030 * };
42031 * ```
42032 *
42033 * @param {Point} point
42034 * @param {Array<Point>} line
42035 *
42036 * @return {Object} attachment
42037 */
42038 function getAttachment(point, line) {
42039
42040 var idx = 0,
42041 segmentStart,
42042 segmentEnd,
42043 segmentStartDistance,
42044 segmentEndDistance,
42045 attachmentPosition,
42046 minDistance,
42047 intersections,
42048 attachment,
42049 attachmentDistance,
42050 closestAttachmentDistance,
42051 closestAttachment;
42052
42053 for (idx = 0; idx < line.length - 1; idx++) {
42054
42055 segmentStart = line[idx];
42056 segmentEnd = line[idx + 1];
42057
42058 if (pointsEqual(segmentStart, segmentEnd)) {
42059 intersections = [ segmentStart ];
42060 } else {
42061 segmentStartDistance = getDistance(point, segmentStart);
42062 segmentEndDistance = getDistance(point, segmentEnd);
42063
42064 minDistance = min$1(segmentStartDistance, segmentEndDistance);
42065
42066 intersections = getCircleSegmentIntersections(segmentStart, segmentEnd, point, minDistance);
42067 }
42068
42069 if (intersections.length < 1) {
42070 throw new Error('expected between [1, 2] circle -> line intersections');
42071 }
42072
42073 // one intersection -> bendpoint attachment
42074 if (intersections.length === 1) {
42075 attachment = {
42076 type: 'bendpoint',
42077 position: intersections[0],
42078 segmentIndex: idx,
42079 bendpointIndex: pointsEqual(segmentStart, intersections[0]) ? idx : idx + 1
42080 };
42081 }
42082
42083 // two intersections -> segment attachment
42084 if (intersections.length === 2) {
42085
42086 attachmentPosition = mid$1(intersections[0], intersections[1]);
42087
42088 attachment = {
42089 type: 'segment',
42090 position: attachmentPosition,
42091 segmentIndex: idx,
42092 relativeLocation: getDistance(segmentStart, attachmentPosition) / getDistance(segmentStart, segmentEnd)
42093 };
42094 }
42095
42096 attachmentDistance = getDistance(attachment.position, point);
42097
42098 if (!closestAttachment || closestAttachmentDistance > attachmentDistance) {
42099 closestAttachment = attachment;
42100 closestAttachmentDistance = attachmentDistance;
42101 }
42102 }
42103
42104 return closestAttachment;
42105 }
42106
42107 /**
42108 * Gets the intersection between a circle and a line segment.
42109 *
42110 * @param {Point} s1 segment start
42111 * @param {Point} s2 segment end
42112 * @param {Point} cc circle center
42113 * @param {number} cr circle radius
42114 *
42115 * @return {Array<Point>} intersections
42116 */
42117 function getCircleSegmentIntersections(s1, s2, cc, cr) {
42118
42119 var baX = s2.x - s1.x;
42120 var baY = s2.y - s1.y;
42121 var caX = cc.x - s1.x;
42122 var caY = cc.y - s1.y;
42123
42124 var a = baX * baX + baY * baY;
42125 var bBy2 = baX * caX + baY * caY;
42126 var c = caX * caX + caY * caY - cr * cr;
42127
42128 var pBy2 = bBy2 / a;
42129 var q = c / a;
42130
42131 var disc = pBy2 * pBy2 - q;
42132
42133 // check against negative value to work around
42134 // negative, very close to zero results (-4e-15)
42135 // being produced in some environments
42136 if (disc < 0 && disc > -0.000001) {
42137 disc = 0;
42138 }
42139
42140 if (disc < 0) {
42141 return [];
42142 }
42143
42144 // if disc == 0 ... dealt with later
42145 var tmpSqrt = sqrt(disc);
42146 var abScalingFactor1 = -pBy2 + tmpSqrt;
42147 var abScalingFactor2 = -pBy2 - tmpSqrt;
42148
42149 var i1 = {
42150 x: s1.x - baX * abScalingFactor1,
42151 y: s1.y - baY * abScalingFactor1
42152 };
42153
42154 if (disc === 0) { // abScalingFactor1 == abScalingFactor2
42155 return [ i1 ];
42156 }
42157
42158 var i2 = {
42159 x: s1.x - baX * abScalingFactor2,
42160 y: s1.y - baY * abScalingFactor2
42161 };
42162
42163 // return only points on line segment
42164 return [ i1, i2 ].filter(function(p) {
42165 return isPointInSegment(p, s1, s2);
42166 });
42167 }
42168
42169
42170 function isPointInSegment(p, segmentStart, segmentEnd) {
42171 return (
42172 fenced(p.x, segmentStart.x, segmentEnd.x) &&
42173 fenced(p.y, segmentStart.y, segmentEnd.y)
42174 );
42175 }
42176
42177 function fenced(n, rangeStart, rangeEnd) {
42178
42179 // use matching threshold to work around
42180 // precision errors in intersection computation
42181
42182 return (
42183 n >= min$1(rangeStart, rangeEnd) - EQUAL_THRESHOLD &&
42184 n <= max$3(rangeStart, rangeEnd) + EQUAL_THRESHOLD
42185 );
42186 }
42187
42188 /**
42189 * Calculate mid of two points.
42190 *
42191 * @param {Point} p1
42192 * @param {Point} p2
42193 *
42194 * @return {Point}
42195 */
42196 function mid$1(p1, p2) {
42197
42198 return {
42199 x: (p1.x + p2.x) / 2,
42200 y: (p1.y + p2.y) / 2
42201 };
42202 }
42203
42204 var EQUAL_THRESHOLD = 0.1;
42205
42206 function pointsEqual(p1, p2) {
42207
42208 return (
42209 abs$3(p1.x - p2.x) <= EQUAL_THRESHOLD &&
42210 abs$3(p1.y - p2.y) <= EQUAL_THRESHOLD
42211 );
42212 }
42213
42214 function findNewLabelLineStartIndex(oldWaypoints, newWaypoints, attachment, hints) {
42215
42216 var index = attachment.segmentIndex;
42217
42218 var offset = newWaypoints.length - oldWaypoints.length;
42219
42220 // segmentMove happened
42221 if (hints.segmentMove) {
42222
42223 var oldSegmentStartIndex = hints.segmentMove.segmentStartIndex,
42224 newSegmentStartIndex = hints.segmentMove.newSegmentStartIndex;
42225
42226 // if label was on moved segment return new segment index
42227 if (index === oldSegmentStartIndex) {
42228 return newSegmentStartIndex;
42229 }
42230
42231 // label is after new segment index
42232 if (index >= newSegmentStartIndex) {
42233 return (index+offset < newSegmentStartIndex) ? newSegmentStartIndex : index+offset;
42234 }
42235
42236 // if label is before new segment index
42237 return index;
42238 }
42239
42240 // bendpointMove happened
42241 if (hints.bendpointMove) {
42242
42243 var insert = hints.bendpointMove.insert,
42244 bendpointIndex = hints.bendpointMove.bendpointIndex,
42245 newIndex;
42246
42247 // waypoints length didnt change
42248 if (offset === 0) {
42249 return index;
42250 }
42251
42252 // label behind new/removed bendpoint
42253 if (index >= bendpointIndex) {
42254 newIndex = insert ? index + 1 : index - 1;
42255 }
42256
42257 // label before new/removed bendpoint
42258 if (index < bendpointIndex) {
42259
42260 newIndex = index;
42261
42262 // decide label should take right or left segment
42263 if (insert && attachment.type !== 'bendpoint' && bendpointIndex-1 === index) {
42264
42265 var rel = relativePositionMidWaypoint(newWaypoints, bendpointIndex);
42266
42267 if (rel < attachment.relativeLocation) {
42268 newIndex++;
42269 }
42270 }
42271 }
42272
42273 return newIndex;
42274 }
42275
42276 // start/end changed
42277 if (offset === 0) {
42278 return index;
42279 }
42280
42281 if (hints.connectionStart) {
42282 return (index === 0) ? 0 : null;
42283 }
42284
42285 if (hints.connectionEnd) {
42286 return (index === oldWaypoints.length - 2) ? newWaypoints.length - 2 : null;
42287 }
42288
42289 // if nothing fits, return null
42290 return null;
42291 }
42292
42293
42294 /**
42295 * Calculate the required adjustment (move delta) for the given label
42296 * after the connection waypoints got updated.
42297 *
42298 * @param {djs.model.Label} label
42299 * @param {Array<Point>} newWaypoints
42300 * @param {Array<Point>} oldWaypoints
42301 * @param {Object} hints
42302 *
42303 * @return {Point} delta
42304 */
42305 function getLabelAdjustment(label, newWaypoints, oldWaypoints, hints) {
42306
42307 var x = 0,
42308 y = 0;
42309
42310 var labelPosition = getLabelMid(label);
42311
42312 // get closest attachment
42313 var attachment = getAttachment(labelPosition, oldWaypoints),
42314 oldLabelLineIndex = attachment.segmentIndex,
42315 newLabelLineIndex = findNewLabelLineStartIndex(oldWaypoints, newWaypoints, attachment, hints);
42316
42317 if (newLabelLineIndex === null) {
42318 return { x: x, y: y };
42319 }
42320
42321 // should never happen
42322 // TODO(@janstuemmel): throw an error here when connectionSegmentMove is refactored
42323 if (newLabelLineIndex < 0 ||
42324 newLabelLineIndex > newWaypoints.length - 2) {
42325 return { x: x, y: y };
42326 }
42327
42328 var oldLabelLine = getLine(oldWaypoints, oldLabelLineIndex),
42329 newLabelLine = getLine(newWaypoints, newLabelLineIndex),
42330 oldFoot = attachment.position;
42331
42332 var relativeFootPosition = getRelativeFootPosition(oldLabelLine, oldFoot),
42333 angleDelta = getAngleDelta(oldLabelLine, newLabelLine);
42334
42335 // special rule if label on bendpoint
42336 if (attachment.type === 'bendpoint') {
42337
42338 var offset = newWaypoints.length - oldWaypoints.length,
42339 oldBendpointIndex = attachment.bendpointIndex,
42340 oldBendpoint = oldWaypoints[oldBendpointIndex];
42341
42342 // bendpoint position hasn't changed, return same position
42343 if (newWaypoints.indexOf(oldBendpoint) !== -1) {
42344 return { x: x, y: y };
42345 }
42346
42347 // new bendpoint and old bendpoint have same index, then just return the offset
42348 if (offset === 0) {
42349 var newBendpoint = newWaypoints[oldBendpointIndex];
42350
42351 return {
42352 x: newBendpoint.x - attachment.position.x,
42353 y: newBendpoint.y - attachment.position.y
42354 };
42355 }
42356
42357 // if bendpoints get removed
42358 if (offset < 0 && oldBendpointIndex !== 0 && oldBendpointIndex < oldWaypoints.length - 1) {
42359 relativeFootPosition = relativePositionMidWaypoint(oldWaypoints, oldBendpointIndex);
42360 }
42361 }
42362
42363 var newFoot = {
42364 x: (newLabelLine[1].x - newLabelLine[0].x) * relativeFootPosition + newLabelLine[0].x,
42365 y: (newLabelLine[1].y - newLabelLine[0].y) * relativeFootPosition + newLabelLine[0].y
42366 };
42367
42368 // the rotated vector to label
42369 var newLabelVector = rotateVector({
42370 x: labelPosition.x - oldFoot.x,
42371 y: labelPosition.y - oldFoot.y
42372 }, angleDelta);
42373
42374 // the new relative position
42375 x = newFoot.x + newLabelVector.x - labelPosition.x;
42376 y = newFoot.y + newLabelVector.y - labelPosition.y;
42377
42378 return roundPoint({
42379 x: x,
42380 y: y
42381 });
42382 }
42383
42384
42385 // HELPERS //////////////////////
42386
42387 function relativePositionMidWaypoint(waypoints, idx) {
42388
42389 var distanceSegment1 = getDistancePointPoint(waypoints[idx-1], waypoints[idx]),
42390 distanceSegment2 = getDistancePointPoint(waypoints[idx], waypoints[idx+1]);
42391
42392 var relativePosition = distanceSegment1 / (distanceSegment1 + distanceSegment2);
42393
42394 return relativePosition;
42395 }
42396
42397 function getLabelMid(label) {
42398 return {
42399 x: label.x + label.width / 2,
42400 y: label.y + label.height / 2
42401 };
42402 }
42403
42404 function getAngleDelta(l1, l2) {
42405 var a1 = getAngle(l1),
42406 a2 = getAngle(l2);
42407 return a2 - a1;
42408 }
42409
42410 function getLine(waypoints, idx) {
42411 return [ waypoints[idx], waypoints[idx+1] ];
42412 }
42413
42414 function getRelativeFootPosition(line, foot) {
42415
42416 var length = getDistancePointPoint(line[0], line[1]),
42417 lengthToFoot = getDistancePointPoint(line[0], foot);
42418
42419 return length === 0 ? 0 : lengthToFoot / length;
42420 }
42421
42422 /**
42423 * Calculates the absolute point relative to the new element's position
42424 *
42425 * @param {point} point [absolute]
42426 * @param {bounds} oldBounds
42427 * @param {bounds} newBounds
42428 *
42429 * @return {point} point [absolute]
42430 */
42431 function getNewAttachPoint(point, oldBounds, newBounds) {
42432 var oldCenter = center(oldBounds),
42433 newCenter = center(newBounds),
42434 oldDelta = delta(point, oldCenter);
42435
42436 var newDelta = {
42437 x: oldDelta.x * (newBounds.width / oldBounds.width),
42438 y: oldDelta.y * (newBounds.height / oldBounds.height)
42439 };
42440
42441 return roundPoint({
42442 x: newCenter.x + newDelta.x,
42443 y: newCenter.y + newDelta.y
42444 });
42445 }
42446
42447
42448 /**
42449 * Calculates the shape's delta relative to a new position
42450 * of a certain element's bounds
42451 *
42452 * @param {djs.model.Shape} point [absolute]
42453 * @param {bounds} oldBounds
42454 * @param {bounds} newBounds
42455 *
42456 * @return {delta} delta
42457 */
42458 function getNewAttachShapeDelta(shape, oldBounds, newBounds) {
42459 var shapeCenter = center(shape),
42460 oldCenter = center(oldBounds),
42461 newCenter = center(newBounds),
42462 shapeDelta = delta(shape, shapeCenter),
42463 oldCenterDelta = delta(shapeCenter, oldCenter),
42464 stickyPositionDelta = getStickyPositionDelta(shapeCenter, oldBounds, newBounds);
42465
42466 if (stickyPositionDelta) {
42467 return stickyPositionDelta;
42468 }
42469
42470 var newCenterDelta = {
42471 x: oldCenterDelta.x * (newBounds.width / oldBounds.width),
42472 y: oldCenterDelta.y * (newBounds.height / oldBounds.height)
42473 };
42474
42475 var newShapeCenter = {
42476 x: newCenter.x + newCenterDelta.x,
42477 y: newCenter.y + newCenterDelta.y
42478 };
42479
42480 return roundPoint({
42481 x: newShapeCenter.x + shapeDelta.x - shape.x,
42482 y: newShapeCenter.y + shapeDelta.y - shape.y
42483 });
42484 }
42485
42486 function getStickyPositionDelta(oldShapeCenter, oldBounds, newBounds) {
42487 var oldTRBL = asTRBL(oldBounds),
42488 newTRBL = asTRBL(newBounds);
42489
42490 if (isMoved(oldTRBL, newTRBL)) {
42491 return null;
42492 }
42493
42494 var oldOrientation = getOrientation(oldBounds, oldShapeCenter),
42495 stickyPositionDelta,
42496 newShapeCenter,
42497 newOrientation;
42498
42499 if (oldOrientation === 'top') {
42500 stickyPositionDelta = {
42501 x: 0,
42502 y: newTRBL.bottom - oldTRBL.bottom
42503 };
42504 } else if (oldOrientation === 'bottom') {
42505 stickyPositionDelta = {
42506 x: 0,
42507 y: newTRBL.top - oldTRBL.top
42508 };
42509 } else if (oldOrientation === 'right') {
42510 stickyPositionDelta = {
42511 x: newTRBL.left - oldTRBL.left,
42512 y: 0
42513 };
42514 } else if (oldOrientation === 'left') {
42515 stickyPositionDelta = {
42516 x: newTRBL.right - oldTRBL.right,
42517 y: 0
42518 };
42519 } else {
42520
42521 // fallback to proportional movement for corner-placed attachments
42522 return null;
42523 }
42524
42525 newShapeCenter = {
42526 x: oldShapeCenter.x + stickyPositionDelta.x,
42527 y: oldShapeCenter.y + stickyPositionDelta.y
42528 };
42529
42530 newOrientation = getOrientation(newBounds, newShapeCenter);
42531
42532 if (newOrientation !== oldOrientation) {
42533
42534 // fallback to proportional movement if orientation would otherwise change
42535 return null;
42536 }
42537
42538 return stickyPositionDelta;
42539 }
42540
42541 function isMoved(oldTRBL, newTRBL) {
42542 return isHorizontallyMoved(oldTRBL, newTRBL) || isVerticallyMoved(oldTRBL, newTRBL);
42543 }
42544
42545 function isHorizontallyMoved(oldTRBL, newTRBL) {
42546 return oldTRBL.right !== newTRBL.right && oldTRBL.left !== newTRBL.left;
42547 }
42548
42549 function isVerticallyMoved(oldTRBL, newTRBL) {
42550 return oldTRBL.top !== newTRBL.top && oldTRBL.bottom !== newTRBL.bottom;
42551 }
42552
42553 var DEFAULT_LABEL_DIMENSIONS = {
42554 width: 90,
42555 height: 20
42556 };
42557
42558 var NAME_PROPERTY = 'name';
42559 var TEXT_PROPERTY = 'text';
42560
42561 /**
42562 * A component that makes sure that external labels are added
42563 * together with respective elements and properly updated (DI wise)
42564 * during move.
42565 *
42566 * @param {EventBus} eventBus
42567 * @param {Modeling} modeling
42568 * @param {BpmnFactory} bpmnFactory
42569 * @param {TextRenderer} textRenderer
42570 */
42571 function LabelBehavior(
42572 eventBus, modeling, bpmnFactory,
42573 textRenderer) {
42574
42575 CommandInterceptor.call(this, eventBus);
42576
42577 // update label if name property was updated
42578 this.postExecute('element.updateProperties', function(e) {
42579 var context = e.context,
42580 element = context.element,
42581 properties = context.properties;
42582
42583 if (NAME_PROPERTY in properties) {
42584 modeling.updateLabel(element, properties[NAME_PROPERTY]);
42585 }
42586
42587 if (TEXT_PROPERTY in properties
42588 && is$1(element, 'bpmn:TextAnnotation')) {
42589
42590 var newBounds = textRenderer.getTextAnnotationBounds(
42591 {
42592 x: element.x,
42593 y: element.y,
42594 width: element.width,
42595 height: element.height
42596 },
42597 properties[TEXT_PROPERTY] || ''
42598 );
42599
42600 modeling.updateLabel(element, properties.text, newBounds);
42601 }
42602 });
42603
42604 // create label shape after shape/connection was created
42605 this.postExecute([ 'shape.create', 'connection.create' ], function(e) {
42606 var context = e.context,
42607 hints = context.hints || {};
42608
42609 if (hints.createElementsBehavior === false) {
42610 return;
42611 }
42612
42613 var element = context.shape || context.connection,
42614 businessObject = element.businessObject;
42615
42616 if (isLabel$6(element) || !isLabelExternal(element)) {
42617 return;
42618 }
42619
42620 // only create label if attribute available
42621 if (!getLabel(element)) {
42622 return;
42623 }
42624
42625 var labelCenter = getExternalLabelMid(element);
42626
42627 // we don't care about x and y
42628 var labelDimensions = textRenderer.getExternalLabelBounds(
42629 DEFAULT_LABEL_DIMENSIONS,
42630 getLabel(element)
42631 );
42632
42633 modeling.createLabel(element, labelCenter, {
42634 id: businessObject.id + '_label',
42635 businessObject: businessObject,
42636 width: labelDimensions.width,
42637 height: labelDimensions.height
42638 });
42639 });
42640
42641 // update label after label shape was deleted
42642 this.postExecute('shape.delete', function(event) {
42643 var context = event.context,
42644 labelTarget = context.labelTarget,
42645 hints = context.hints || {};
42646
42647 // check if label
42648 if (labelTarget && hints.unsetLabel !== false) {
42649 modeling.updateLabel(labelTarget, null, null, { removeShape: false });
42650 }
42651 });
42652
42653 // update di information on label creation
42654 this.postExecute([ 'label.create' ], function(event) {
42655
42656 var context = event.context,
42657 element = context.shape,
42658 businessObject,
42659 di;
42660
42661 // we want to trigger on real labels only
42662 if (!element.labelTarget) {
42663 return;
42664 }
42665
42666 // we want to trigger on BPMN elements only
42667 if (!is$1(element.labelTarget || element, 'bpmn:BaseElement')) {
42668 return;
42669 }
42670
42671 businessObject = element.businessObject,
42672 di = businessObject.di;
42673
42674
42675 if (!di.label) {
42676 di.label = bpmnFactory.create('bpmndi:BPMNLabel', {
42677 bounds: bpmnFactory.create('dc:Bounds')
42678 });
42679 }
42680
42681 assign(di.label.bounds, {
42682 x: element.x,
42683 y: element.y,
42684 width: element.width,
42685 height: element.height
42686 });
42687 });
42688
42689 function getVisibleLabelAdjustment(event) {
42690
42691 var context = event.context,
42692 connection = context.connection,
42693 label = connection.label,
42694 hints = assign({}, context.hints),
42695 newWaypoints = context.newWaypoints || connection.waypoints,
42696 oldWaypoints = context.oldWaypoints;
42697
42698
42699 if (typeof hints.startChanged === 'undefined') {
42700 hints.startChanged = !!hints.connectionStart;
42701 }
42702
42703 if (typeof hints.endChanged === 'undefined') {
42704 hints.endChanged = !!hints.connectionEnd;
42705 }
42706
42707 return getLabelAdjustment(label, newWaypoints, oldWaypoints, hints);
42708 }
42709
42710 this.postExecute([
42711 'connection.layout',
42712 'connection.updateWaypoints'
42713 ], function(event) {
42714 var context = event.context,
42715 hints = context.hints || {};
42716
42717 if (hints.labelBehavior === false) {
42718 return;
42719 }
42720
42721 var connection = context.connection,
42722 label = connection.label,
42723 labelAdjustment;
42724
42725 // handle missing label as well as the case
42726 // that the label parent does not exist (yet),
42727 // because it is being pasted / created via multi element create
42728 //
42729 // Cf. https://github.com/bpmn-io/bpmn-js/pull/1227
42730 if (!label || !label.parent) {
42731 return;
42732 }
42733
42734 labelAdjustment = getVisibleLabelAdjustment(event);
42735
42736 modeling.moveShape(label, labelAdjustment);
42737 });
42738
42739
42740 // keep label position on shape replace
42741 this.postExecute([ 'shape.replace' ], function(event) {
42742 var context = event.context,
42743 newShape = context.newShape,
42744 oldShape = context.oldShape;
42745
42746 var businessObject = getBusinessObject(newShape);
42747
42748 if (businessObject
42749 && isLabelExternal(businessObject)
42750 && oldShape.label
42751 && newShape.label) {
42752 newShape.label.x = oldShape.label.x;
42753 newShape.label.y = oldShape.label.y;
42754 }
42755 });
42756
42757
42758 // move external label after resizing
42759 this.postExecute('shape.resize', function(event) {
42760
42761 var context = event.context,
42762 shape = context.shape,
42763 newBounds = context.newBounds,
42764 oldBounds = context.oldBounds;
42765
42766 if (hasExternalLabel(shape)) {
42767
42768 var label = shape.label,
42769 labelMid = getMid(label),
42770 edges = asEdges(oldBounds);
42771
42772 // get nearest border point to label as reference point
42773 var referencePoint = getReferencePoint(labelMid, edges);
42774
42775 var delta = getReferencePointDelta(referencePoint, oldBounds, newBounds);
42776
42777 modeling.moveShape(label, delta);
42778
42779 }
42780
42781 });
42782
42783 }
42784
42785 inherits$1(LabelBehavior, CommandInterceptor);
42786
42787 LabelBehavior.$inject = [
42788 'eventBus',
42789 'modeling',
42790 'bpmnFactory',
42791 'textRenderer'
42792 ];
42793
42794 // helpers //////////////////////
42795
42796 /**
42797 * Calculates a reference point delta relative to a new position
42798 * of a certain element's bounds
42799 *
42800 * @param {Point} point
42801 * @param {Bounds} oldBounds
42802 * @param {Bounds} newBounds
42803 *
42804 * @return {Delta} delta
42805 */
42806 function getReferencePointDelta(referencePoint, oldBounds, newBounds) {
42807
42808 var newReferencePoint = getNewAttachPoint(referencePoint, oldBounds, newBounds);
42809
42810 return roundPoint(delta(newReferencePoint, referencePoint));
42811 }
42812
42813 /**
42814 * Generates the nearest point (reference point) for a given point
42815 * onto given set of lines
42816 *
42817 * @param {Array<Point, Point>} lines
42818 * @param {Point} point
42819 *
42820 * @param {Point}
42821 */
42822 function getReferencePoint(point, lines) {
42823
42824 if (!lines.length) {
42825 return;
42826 }
42827
42828 var nearestLine = getNearestLine(point, lines);
42829
42830 return perpendicularFoot(point, nearestLine);
42831 }
42832
42833 /**
42834 * Convert the given bounds to a lines array containing all edges
42835 *
42836 * @param {Bounds|Point} bounds
42837 *
42838 * @return Array<Point>
42839 */
42840 function asEdges(bounds) {
42841 return [
42842 [ // top
42843 {
42844 x: bounds.x,
42845 y: bounds.y
42846 },
42847 {
42848 x: bounds.x + (bounds.width || 0),
42849 y: bounds.y
42850 }
42851 ],
42852 [ // right
42853 {
42854 x: bounds.x + (bounds.width || 0),
42855 y: bounds.y
42856 },
42857 {
42858 x: bounds.x + (bounds.width || 0),
42859 y: bounds.y + (bounds.height || 0)
42860 }
42861 ],
42862 [ // bottom
42863 {
42864 x: bounds.x,
42865 y: bounds.y + (bounds.height || 0)
42866 },
42867 {
42868 x: bounds.x + (bounds.width || 0),
42869 y: bounds.y + (bounds.height || 0)
42870 }
42871 ],
42872 [ // left
42873 {
42874 x: bounds.x,
42875 y: bounds.y
42876 },
42877 {
42878 x: bounds.x,
42879 y: bounds.y + (bounds.height || 0)
42880 }
42881 ]
42882 ];
42883 }
42884
42885 /**
42886 * Returns the nearest line for a given point by distance
42887 * @param {Point} point
42888 * @param Array<Point> lines
42889 *
42890 * @return Array<Point>
42891 */
42892 function getNearestLine(point, lines) {
42893
42894 var distances = lines.map(function(l) {
42895 return {
42896 line: l,
42897 distance: getDistancePointLine(point, l)
42898 };
42899 });
42900
42901 var sorted = sortBy(distances, 'distance');
42902
42903 return sorted[0].line;
42904 }
42905
42906 function getResizedSourceAnchor(connection, shape, oldBounds) {
42907
42908 var waypoints = safeGetWaypoints(connection),
42909 waypointsInsideNewBounds = getWaypointsInsideBounds(waypoints, shape),
42910 oldAnchor = waypoints[0];
42911
42912 // new anchor is the last waypoint enclosed be resized source
42913 if (waypointsInsideNewBounds.length) {
42914 return waypointsInsideNewBounds[ waypointsInsideNewBounds.length - 1 ];
42915 }
42916
42917 return getNewAttachPoint(oldAnchor.original || oldAnchor, oldBounds, shape);
42918 }
42919
42920
42921 function getResizedTargetAnchor(connection, shape, oldBounds) {
42922
42923 var waypoints = safeGetWaypoints(connection),
42924 waypointsInsideNewBounds = getWaypointsInsideBounds(waypoints, shape),
42925 oldAnchor = waypoints[waypoints.length - 1];
42926
42927 // new anchor is the first waypoint enclosed be resized target
42928 if (waypointsInsideNewBounds.length) {
42929 return waypointsInsideNewBounds[ 0 ];
42930 }
42931
42932 return getNewAttachPoint(oldAnchor.original || oldAnchor, oldBounds, shape);
42933 }
42934
42935
42936 function getMovedSourceAnchor(connection, source, moveDelta) {
42937
42938 var waypoints = safeGetWaypoints(connection),
42939 oldBounds = subtract(source, moveDelta),
42940 oldAnchor = waypoints[ 0 ];
42941
42942 return getNewAttachPoint(oldAnchor.original || oldAnchor, oldBounds, source);
42943 }
42944
42945
42946 function getMovedTargetAnchor(connection, target, moveDelta) {
42947
42948 var waypoints = safeGetWaypoints(connection),
42949 oldBounds = subtract(target, moveDelta),
42950 oldAnchor = waypoints[ waypoints.length - 1 ];
42951
42952 return getNewAttachPoint(oldAnchor.original || oldAnchor, oldBounds, target);
42953 }
42954
42955
42956 // helpers //////////////////////
42957
42958 function subtract(bounds, delta) {
42959 return {
42960 x: bounds.x - delta.x,
42961 y: bounds.y - delta.y,
42962 width: bounds.width,
42963 height: bounds.height
42964 };
42965 }
42966
42967
42968 /**
42969 * Return waypoints of given connection; throw if non exists (should not happen!!).
42970 *
42971 * @param {Connection} connection
42972 *
42973 * @return {Array<Point>}
42974 */
42975 function safeGetWaypoints(connection) {
42976
42977 var waypoints = connection.waypoints;
42978
42979 if (!waypoints.length) {
42980 throw new Error('connection#' + connection.id + ': no waypoints');
42981 }
42982
42983 return waypoints;
42984 }
42985
42986 function getWaypointsInsideBounds(waypoints, bounds) {
42987 var originalWaypoints = map$1(waypoints, getOriginal);
42988
42989 return filter(originalWaypoints, function(waypoint) {
42990 return isInsideBounds(waypoint, bounds);
42991 });
42992 }
42993
42994 /**
42995 * Checks if point is inside bounds, incl. edges.
42996 *
42997 * @param {Point} point
42998 * @param {Bounds} bounds
42999 */
43000 function isInsideBounds(point, bounds) {
43001 return getOrientation(bounds, point, 1) === 'intersect';
43002 }
43003
43004 function getOriginal(point) {
43005 return point.original || point;
43006 }
43007
43008 /**
43009 * BPMN-specific message flow behavior.
43010 */
43011 function MessageFlowBehavior(eventBus, modeling) {
43012
43013 CommandInterceptor.call(this, eventBus);
43014
43015 this.postExecute('shape.replace', function(context) {
43016 var oldShape = context.oldShape,
43017 newShape = context.newShape;
43018
43019 if (!isParticipantCollapse(oldShape, newShape)) {
43020 return;
43021 }
43022
43023 var messageFlows = getMessageFlows(oldShape);
43024
43025 messageFlows.incoming.forEach(function(incoming) {
43026 var anchor = getResizedTargetAnchor(incoming, newShape, oldShape);
43027
43028 modeling.reconnectEnd(incoming, newShape, anchor);
43029 });
43030
43031 messageFlows.outgoing.forEach(function(outgoing) {
43032 var anchor = getResizedSourceAnchor(outgoing, newShape, oldShape);
43033
43034 modeling.reconnectStart(outgoing, newShape, anchor);
43035 });
43036 }, true);
43037
43038 }
43039
43040 MessageFlowBehavior.$inject = [ 'eventBus', 'modeling' ];
43041
43042 inherits$1(MessageFlowBehavior, CommandInterceptor);
43043
43044 // helpers //////////
43045
43046 function isParticipantCollapse(oldShape, newShape) {
43047 return is$1(oldShape, 'bpmn:Participant')
43048 && isExpanded(oldShape)
43049 && is$1(newShape, 'bpmn:Participant')
43050 && !isExpanded(newShape);
43051 }
43052
43053 function getMessageFlows(parent) {
43054 var elements = selfAndAllChildren([ parent ], false);
43055
43056 var incoming = [],
43057 outgoing = [];
43058
43059 elements.forEach(function(element) {
43060 if (element === parent) {
43061 return;
43062 }
43063
43064 element.incoming.forEach(function(connection) {
43065 if (is$1(connection, 'bpmn:MessageFlow')) {
43066 incoming.push(connection);
43067 }
43068 });
43069
43070 element.outgoing.forEach(function(connection) {
43071 if (is$1(connection, 'bpmn:MessageFlow')) {
43072 outgoing.push(connection);
43073 }
43074 });
43075 }, []);
43076
43077 return {
43078 incoming: incoming,
43079 outgoing: outgoing
43080 };
43081 }
43082
43083 var COLLAB_ERR_MSG = 'flow elements must be children of pools/participants';
43084
43085 function ModelingFeedback(eventBus, tooltips, translate) {
43086
43087 function showError(position, message, timeout) {
43088 tooltips.add({
43089 position: {
43090 x: position.x + 5,
43091 y: position.y + 5
43092 },
43093 type: 'error',
43094 timeout: timeout || 2000,
43095 html: '<div>' + message + '</div>'
43096 });
43097 }
43098
43099 eventBus.on([ 'shape.move.rejected', 'create.rejected' ], function(event) {
43100 var context = event.context,
43101 shape = context.shape,
43102 target = context.target;
43103
43104 if (is$1(target, 'bpmn:Collaboration') && is$1(shape, 'bpmn:FlowNode')) {
43105 showError(event, translate(COLLAB_ERR_MSG));
43106 }
43107 });
43108
43109 }
43110
43111 ModelingFeedback.$inject = [
43112 'eventBus',
43113 'tooltips',
43114 'translate'
43115 ];
43116
43117 function ReplaceConnectionBehavior(eventBus, modeling, bpmnRules, injector) {
43118
43119 CommandInterceptor.call(this, eventBus);
43120
43121 var dragging = injector.get('dragging', false);
43122
43123 function fixConnection(connection) {
43124
43125 var source = connection.source,
43126 target = connection.target,
43127 parent = connection.parent;
43128
43129 // do not do anything if connection
43130 // is already deleted (may happen due to other
43131 // behaviors plugged-in before)
43132 if (!parent) {
43133 return;
43134 }
43135
43136 var replacementType,
43137 remove;
43138
43139 /**
43140 * Check if incoming or outgoing connections
43141 * can stay or could be substituted with an
43142 * appropriate replacement.
43143 *
43144 * This holds true for SequenceFlow <> MessageFlow.
43145 */
43146
43147 if (is$1(connection, 'bpmn:SequenceFlow')) {
43148 if (!bpmnRules.canConnectSequenceFlow(source, target)) {
43149 remove = true;
43150 }
43151
43152 if (bpmnRules.canConnectMessageFlow(source, target)) {
43153 replacementType = 'bpmn:MessageFlow';
43154 }
43155 }
43156
43157 // transform message flows into sequence flows, if possible
43158
43159 if (is$1(connection, 'bpmn:MessageFlow')) {
43160
43161 if (!bpmnRules.canConnectMessageFlow(source, target)) {
43162 remove = true;
43163 }
43164
43165 if (bpmnRules.canConnectSequenceFlow(source, target)) {
43166 replacementType = 'bpmn:SequenceFlow';
43167 }
43168 }
43169
43170 if (is$1(connection, 'bpmn:Association') && !bpmnRules.canConnectAssociation(source, target)) {
43171 remove = true;
43172 }
43173
43174
43175 // remove invalid connection,
43176 // unless it has been removed already
43177 if (remove) {
43178 modeling.removeConnection(connection);
43179 }
43180
43181 // replace SequenceFlow <> MessageFlow
43182
43183 if (replacementType) {
43184 modeling.connect(source, target, {
43185 type: replacementType,
43186 waypoints: connection.waypoints.slice()
43187 });
43188 }
43189 }
43190
43191 function replaceReconnectedConnection(event) {
43192
43193 var context = event.context,
43194 connection = context.connection,
43195 source = context.newSource || connection.source,
43196 target = context.newTarget || connection.target,
43197 allowed,
43198 replacement;
43199
43200 allowed = bpmnRules.canConnect(source, target);
43201
43202 if (!allowed || allowed.type === connection.type) {
43203 return;
43204 }
43205
43206 replacement = modeling.connect(source, target, {
43207 type: allowed.type,
43208 waypoints: connection.waypoints.slice()
43209 });
43210
43211 // remove old connection
43212 modeling.removeConnection(connection);
43213
43214 // replace connection in context to reconnect end/start
43215 context.connection = replacement;
43216
43217 if (dragging) {
43218 cleanDraggingSelection(connection, replacement);
43219 }
43220 }
43221
43222 // monkey-patch selection saved in dragging in order to re-select it when operation is finished
43223 function cleanDraggingSelection(oldConnection, newConnection) {
43224 var context = dragging.context(),
43225 previousSelection = context && context.payload.previousSelection,
43226 index;
43227
43228 // do nothing if not dragging or no selection was present
43229 if (!previousSelection || !previousSelection.length) {
43230 return;
43231 }
43232
43233 index = previousSelection.indexOf(oldConnection);
43234
43235 if (index === -1) {
43236 return;
43237 }
43238
43239 previousSelection.splice(index, 1, newConnection);
43240 }
43241
43242 // lifecycle hooks
43243
43244 this.postExecuted('elements.move', function(context) {
43245
43246 var closure = context.closure,
43247 allConnections = closure.allConnections;
43248
43249 forEach(allConnections, fixConnection);
43250 }, true);
43251
43252 this.preExecute('connection.reconnect', replaceReconnectedConnection);
43253
43254 this.postExecuted('element.updateProperties', function(event) {
43255 var context = event.context,
43256 properties = context.properties,
43257 element = context.element,
43258 businessObject = element.businessObject,
43259 connection;
43260
43261 // remove condition on change to default
43262 if (properties.default) {
43263 connection = find(
43264 element.outgoing,
43265 matchPattern({ id: element.businessObject.default.id })
43266 );
43267
43268 if (connection) {
43269 modeling.updateProperties(connection, { conditionExpression: undefined });
43270 }
43271 }
43272
43273 // remove default from source on change to conditional
43274 if (properties.conditionExpression && businessObject.sourceRef.default === businessObject) {
43275 modeling.updateProperties(element.source, { default: undefined });
43276 }
43277 });
43278 }
43279
43280 inherits$1(ReplaceConnectionBehavior, CommandInterceptor);
43281
43282 ReplaceConnectionBehavior.$inject = [
43283 'eventBus',
43284 'modeling',
43285 'bpmnRules',
43286 'injector'
43287 ];
43288
43289 /**
43290 * BPMN specific remove behavior
43291 */
43292 function RemoveParticipantBehavior(eventBus, modeling) {
43293
43294 CommandInterceptor.call(this, eventBus);
43295
43296
43297 /**
43298 * morph collaboration diagram into process diagram
43299 * after the last participant has been removed
43300 */
43301
43302 this.preExecute('shape.delete', function(context) {
43303
43304 var shape = context.shape,
43305 parent = shape.parent;
43306
43307 // activate the behavior if the shape to be removed
43308 // is a participant
43309 if (is$1(shape, 'bpmn:Participant')) {
43310 context.collaborationRoot = parent;
43311 }
43312 }, true);
43313
43314 this.postExecute('shape.delete', function(context) {
43315
43316 var collaborationRoot = context.collaborationRoot;
43317
43318 if (collaborationRoot && !collaborationRoot.businessObject.participants.length) {
43319
43320 // replace empty collaboration with process diagram
43321 modeling.makeProcess();
43322 }
43323 }, true);
43324
43325 }
43326
43327 RemoveParticipantBehavior.$inject = [ 'eventBus', 'modeling' ];
43328
43329 inherits$1(RemoveParticipantBehavior, CommandInterceptor);
43330
43331 /**
43332 * BPMN-specific replace behavior.
43333 */
43334 function ReplaceElementBehaviour(
43335 bpmnReplace,
43336 bpmnRules,
43337 elementRegistry,
43338 injector,
43339 modeling,
43340 selection
43341 ) {
43342 injector.invoke(CommandInterceptor, this);
43343
43344 this._bpmnReplace = bpmnReplace;
43345 this._elementRegistry = elementRegistry;
43346 this._selection = selection;
43347
43348 // replace elements on create, e.g. during copy-paste
43349 this.postExecuted([ 'elements.create' ], 500, function(event) {
43350 var context = event.context,
43351 target = context.parent,
43352 elements = context.elements;
43353
43354 var canReplace = bpmnRules.canReplace(elements, target);
43355
43356 if (canReplace) {
43357 this.replaceElements(elements, canReplace.replacements);
43358 }
43359 }, this);
43360
43361 // replace elements on move
43362 this.postExecuted([ 'elements.move' ], 500, function(event) {
43363 var context = event.context,
43364 target = context.newParent,
43365 newHost = context.newHost,
43366 elements = [];
43367
43368 forEach(context.closure.topLevel, function(topLevelElements) {
43369 if (isEventSubProcess(topLevelElements)) {
43370 elements = elements.concat(topLevelElements.children);
43371 } else {
43372 elements = elements.concat(topLevelElements);
43373 }
43374 });
43375
43376 // set target to host if attaching
43377 if (elements.length === 1 && newHost) {
43378 target = newHost;
43379 }
43380
43381 var canReplace = bpmnRules.canReplace(elements, target);
43382
43383 if (canReplace) {
43384 this.replaceElements(elements, canReplace.replacements, newHost);
43385 }
43386 }, this);
43387
43388 // update attachments on host replace
43389 this.postExecute([ 'shape.replace' ], 1500, function(e) {
43390 var context = e.context,
43391 oldShape = context.oldShape,
43392 newShape = context.newShape,
43393 attachers = oldShape.attachers,
43394 canReplace;
43395
43396 if (attachers && attachers.length) {
43397 canReplace = bpmnRules.canReplace(attachers, newShape);
43398
43399 this.replaceElements(attachers, canReplace.replacements);
43400 }
43401
43402 }, this);
43403
43404 // keep ID on shape replace
43405 this.postExecuted([ 'shape.replace' ], 1500, function(e) {
43406 var context = e.context,
43407 oldShape = context.oldShape,
43408 newShape = context.newShape;
43409
43410 modeling.unclaimId(oldShape.businessObject.id, oldShape.businessObject);
43411 modeling.updateProperties(newShape, { id: oldShape.id });
43412 });
43413 }
43414
43415 inherits$1(ReplaceElementBehaviour, CommandInterceptor);
43416
43417 ReplaceElementBehaviour.prototype.replaceElements = function(elements, newElements) {
43418 var elementRegistry = this._elementRegistry,
43419 bpmnReplace = this._bpmnReplace,
43420 selection = this._selection;
43421
43422 forEach(newElements, function(replacement) {
43423 var newElement = {
43424 type: replacement.newElementType
43425 };
43426
43427 var oldElement = elementRegistry.get(replacement.oldElementId);
43428
43429 var idx = elements.indexOf(oldElement);
43430
43431 elements[idx] = bpmnReplace.replaceElement(oldElement, newElement, { select: false });
43432 });
43433
43434 if (newElements) {
43435 selection.select(elements);
43436 }
43437 };
43438
43439 ReplaceElementBehaviour.$inject = [
43440 'bpmnReplace',
43441 'bpmnRules',
43442 'elementRegistry',
43443 'injector',
43444 'modeling',
43445 'selection'
43446 ];
43447
43448 var HIGH_PRIORITY$8 = 1500;
43449
43450 var LANE_MIN_DIMENSIONS = { width: 300, height: 60 };
43451
43452 var PARTICIPANT_MIN_DIMENSIONS = { width: 300, height: 150 };
43453
43454 var SUB_PROCESS_MIN_DIMENSIONS = { width: 140, height: 120 };
43455
43456 var TEXT_ANNOTATION_MIN_DIMENSIONS = { width: 50, height: 30 };
43457
43458 /**
43459 * Set minimum bounds/resize constraints on resize.
43460 *
43461 * @param {EventBus} eventBus
43462 */
43463 function ResizeBehavior(eventBus) {
43464 eventBus.on('resize.start', HIGH_PRIORITY$8, function(event) {
43465 var context = event.context,
43466 shape = context.shape,
43467 direction = context.direction,
43468 balanced = context.balanced;
43469
43470 if (is$1(shape, 'bpmn:Lane') || is$1(shape, 'bpmn:Participant')) {
43471 context.resizeConstraints = getParticipantResizeConstraints(shape, direction, balanced);
43472 }
43473
43474 if (is$1(shape, 'bpmn:Participant')) {
43475 context.minDimensions = PARTICIPANT_MIN_DIMENSIONS;
43476 }
43477
43478 if (is$1(shape, 'bpmn:SubProcess') && isExpanded(shape)) {
43479 context.minDimensions = SUB_PROCESS_MIN_DIMENSIONS;
43480 }
43481
43482 if (is$1(shape, 'bpmn:TextAnnotation')) {
43483 context.minDimensions = TEXT_ANNOTATION_MIN_DIMENSIONS;
43484 }
43485 });
43486 }
43487
43488 ResizeBehavior.$inject = [ 'eventBus' ];
43489
43490
43491 var abs$2 = Math.abs,
43492 min = Math.min,
43493 max$2 = Math.max;
43494
43495
43496 function addToTrbl(trbl, attr, value, choice) {
43497 var current = trbl[attr];
43498
43499 // make sure to set the value if it does not exist
43500 // or apply the correct value by comparing against
43501 // choice(value, currentValue)
43502 trbl[attr] = current === undefined ? value : choice(value, current);
43503 }
43504
43505 function addMin(trbl, attr, value) {
43506 return addToTrbl(trbl, attr, value, min);
43507 }
43508
43509 function addMax(trbl, attr, value) {
43510 return addToTrbl(trbl, attr, value, max$2);
43511 }
43512
43513 var LANE_RIGHT_PADDING = 20,
43514 LANE_LEFT_PADDING = 50,
43515 LANE_TOP_PADDING = 20,
43516 LANE_BOTTOM_PADDING = 20;
43517
43518 function getParticipantResizeConstraints(laneShape, resizeDirection, balanced) {
43519 var lanesRoot = getLanesRoot(laneShape);
43520
43521 var isFirst = true,
43522 isLast = true;
43523
43524 // max top/bottom size for lanes
43525 var allLanes = collectLanes(lanesRoot, [ lanesRoot ]);
43526
43527 var laneTrbl = asTRBL(laneShape);
43528
43529 var maxTrbl = {},
43530 minTrbl = {};
43531
43532 if (/e/.test(resizeDirection)) {
43533 minTrbl.right = laneTrbl.left + LANE_MIN_DIMENSIONS.width;
43534 } else
43535 if (/w/.test(resizeDirection)) {
43536 minTrbl.left = laneTrbl.right - LANE_MIN_DIMENSIONS.width;
43537 }
43538
43539 allLanes.forEach(function(other) {
43540
43541 var otherTrbl = asTRBL(other);
43542
43543 if (/n/.test(resizeDirection)) {
43544
43545 if (otherTrbl.top < (laneTrbl.top - 10)) {
43546 isFirst = false;
43547 }
43548
43549 // max top size (based on next element)
43550 if (balanced && abs$2(laneTrbl.top - otherTrbl.bottom) < 10) {
43551 addMax(maxTrbl, 'top', otherTrbl.top + LANE_MIN_DIMENSIONS.height);
43552 }
43553
43554 // min top size (based on self or nested element)
43555 if (abs$2(laneTrbl.top - otherTrbl.top) < 5) {
43556 addMin(minTrbl, 'top', otherTrbl.bottom - LANE_MIN_DIMENSIONS.height);
43557 }
43558 }
43559
43560 if (/s/.test(resizeDirection)) {
43561
43562 if (otherTrbl.bottom > (laneTrbl.bottom + 10)) {
43563 isLast = false;
43564 }
43565
43566 // max bottom size (based on previous element)
43567 if (balanced && abs$2(laneTrbl.bottom - otherTrbl.top) < 10) {
43568 addMin(maxTrbl, 'bottom', otherTrbl.bottom - LANE_MIN_DIMENSIONS.height);
43569 }
43570
43571 // min bottom size (based on self or nested element)
43572 if (abs$2(laneTrbl.bottom - otherTrbl.bottom) < 5) {
43573 addMax(minTrbl, 'bottom', otherTrbl.top + LANE_MIN_DIMENSIONS.height);
43574 }
43575 }
43576 });
43577
43578 // max top/bottom/left/right size based on flow nodes
43579 var flowElements = lanesRoot.children.filter(function(s) {
43580 return !s.hidden && !s.waypoints && (is$1(s, 'bpmn:FlowElement') || is$1(s, 'bpmn:Artifact'));
43581 });
43582
43583 flowElements.forEach(function(flowElement) {
43584
43585 var flowElementTrbl = asTRBL(flowElement);
43586
43587 if (isFirst && /n/.test(resizeDirection)) {
43588 addMin(minTrbl, 'top', flowElementTrbl.top - LANE_TOP_PADDING);
43589 }
43590
43591 if (/e/.test(resizeDirection)) {
43592 addMax(minTrbl, 'right', flowElementTrbl.right + LANE_RIGHT_PADDING);
43593 }
43594
43595 if (isLast && /s/.test(resizeDirection)) {
43596 addMax(minTrbl, 'bottom', flowElementTrbl.bottom + LANE_BOTTOM_PADDING);
43597 }
43598
43599 if (/w/.test(resizeDirection)) {
43600 addMin(minTrbl, 'left', flowElementTrbl.left - LANE_LEFT_PADDING);
43601 }
43602 });
43603
43604 return {
43605 min: minTrbl,
43606 max: maxTrbl
43607 };
43608 }
43609
43610 var SLIGHTLY_HIGHER_PRIORITY = 1001;
43611
43612
43613 /**
43614 * Invoke {@link Modeling#resizeLane} instead of
43615 * {@link Modeling#resizeShape} when resizing a Lane
43616 * or Participant shape.
43617 */
43618 function ResizeLaneBehavior(eventBus, modeling) {
43619
43620 eventBus.on('resize.start', SLIGHTLY_HIGHER_PRIORITY + 500, function(event) {
43621 var context = event.context,
43622 shape = context.shape;
43623
43624 if (is$1(shape, 'bpmn:Lane') || is$1(shape, 'bpmn:Participant')) {
43625
43626 // should we resize the opposite lane(s) in
43627 // order to compensate for the resize operation?
43628 context.balanced = !hasPrimaryModifier(event);
43629 }
43630 });
43631
43632 /**
43633 * Intercept resize end and call resize lane function instead.
43634 */
43635 eventBus.on('resize.end', SLIGHTLY_HIGHER_PRIORITY, function(event) {
43636 var context = event.context,
43637 shape = context.shape,
43638 canExecute = context.canExecute,
43639 newBounds = context.newBounds;
43640
43641 if (is$1(shape, 'bpmn:Lane') || is$1(shape, 'bpmn:Participant')) {
43642
43643 if (canExecute) {
43644
43645 // ensure we have actual pixel values for new bounds
43646 // (important when zoom level was > 1 during move)
43647 newBounds = roundBounds(newBounds);
43648
43649 // perform the actual resize
43650 modeling.resizeLane(shape, newBounds, context.balanced);
43651 }
43652
43653 // stop propagation
43654 return false;
43655 }
43656 });
43657 }
43658
43659 ResizeLaneBehavior.$inject = [
43660 'eventBus',
43661 'modeling'
43662 ];
43663
43664 function RemoveElementBehavior(eventBus, bpmnRules, modeling) {
43665
43666 CommandInterceptor.call(this, eventBus);
43667
43668 /**
43669 * Combine sequence flows when deleting an element
43670 * if there is one incoming and one outgoing
43671 * sequence flow
43672 */
43673 this.preExecute('shape.delete', function(e) {
43674
43675 var shape = e.context.shape;
43676
43677 // only handle [a] -> [shape] -> [b] patterns
43678 if (shape.incoming.length !== 1 || shape.outgoing.length !== 1) {
43679 return;
43680 }
43681
43682 var inConnection = shape.incoming[0],
43683 outConnection = shape.outgoing[0];
43684
43685 // only handle sequence flows
43686 if (!is$1(inConnection, 'bpmn:SequenceFlow') || !is$1(outConnection, 'bpmn:SequenceFlow')) {
43687 return;
43688 }
43689
43690 if (bpmnRules.canConnect(inConnection.source, outConnection.target, inConnection)) {
43691
43692 // compute new, combined waypoints
43693 var newWaypoints = getNewWaypoints(inConnection.waypoints, outConnection.waypoints);
43694
43695 modeling.reconnectEnd(inConnection, outConnection.target, newWaypoints);
43696 }
43697 });
43698
43699 }
43700
43701 inherits$1(RemoveElementBehavior, CommandInterceptor);
43702
43703 RemoveElementBehavior.$inject = [
43704 'eventBus',
43705 'bpmnRules',
43706 'modeling'
43707 ];
43708
43709
43710 // helpers //////////////////////
43711
43712 function getDocking$1(point) {
43713 return point.original || point;
43714 }
43715
43716
43717 function getNewWaypoints(inWaypoints, outWaypoints) {
43718
43719 var intersection = lineIntersect(
43720 getDocking$1(inWaypoints[inWaypoints.length - 2]),
43721 getDocking$1(inWaypoints[inWaypoints.length - 1]),
43722 getDocking$1(outWaypoints[1]),
43723 getDocking$1(outWaypoints[0]));
43724
43725 if (intersection) {
43726 return [].concat(
43727 inWaypoints.slice(0, inWaypoints.length - 1),
43728 [ intersection ],
43729 outWaypoints.slice(1));
43730 } else {
43731 return [
43732 getDocking$1(inWaypoints[0]),
43733 getDocking$1(outWaypoints[outWaypoints.length - 1])
43734 ];
43735 }
43736 }
43737
43738 var max$1 = Math.max;
43739
43740
43741 function SpaceToolBehavior(eventBus) {
43742 eventBus.on('spaceTool.getMinDimensions', function(context) {
43743 var shapes = context.shapes,
43744 axis = context.axis,
43745 start = context.start,
43746 minDimensions = {};
43747
43748 forEach(shapes, function(shape) {
43749 var id = shape.id;
43750
43751 if (is$1(shape, 'bpmn:Participant')) {
43752
43753 if (isHorizontal$1(axis)) {
43754 minDimensions[ id ] = PARTICIPANT_MIN_DIMENSIONS;
43755 } else {
43756 minDimensions[ id ] = {
43757 width: PARTICIPANT_MIN_DIMENSIONS.width,
43758 height: getParticipantMinHeight(shape, start)
43759 };
43760 }
43761
43762 }
43763
43764 if (is$1(shape, 'bpmn:SubProcess') && isExpanded(shape)) {
43765 minDimensions[ id ] = SUB_PROCESS_MIN_DIMENSIONS;
43766 }
43767
43768 if (is$1(shape, 'bpmn:TextAnnotation')) {
43769 minDimensions[ id ] = TEXT_ANNOTATION_MIN_DIMENSIONS;
43770 }
43771 });
43772
43773 return minDimensions;
43774 });
43775 }
43776
43777 SpaceToolBehavior.$inject = [ 'eventBus' ];
43778
43779
43780 // helpers //////////
43781 function isHorizontal$1(axis) {
43782 return axis === 'x';
43783 }
43784
43785 /**
43786 * Get minimum height for participant taking lanes into account.
43787 *
43788 * @param {<djs.model.Shape>} participant
43789 * @param {number} start
43790 *
43791 * @returns {Object}
43792 */
43793 function getParticipantMinHeight(participant, start) {
43794 var lanesMinHeight;
43795
43796 if (!hasChildLanes(participant)) {
43797 return PARTICIPANT_MIN_DIMENSIONS.height;
43798 }
43799
43800 lanesMinHeight = getLanesMinHeight(participant, start);
43801
43802 return max$1(PARTICIPANT_MIN_DIMENSIONS.height, lanesMinHeight);
43803 }
43804
43805 function hasChildLanes(element) {
43806 return !!getChildLanes(element).length;
43807 }
43808
43809 function getLanesMinHeight(participant, resizeStart) {
43810 var lanes = getChildLanes(participant),
43811 resizedLane;
43812
43813 // find the nested lane which is currently resized
43814 resizedLane = findResizedLane(lanes, resizeStart);
43815
43816 // resized lane cannot shrink below the minimum height
43817 // but remaining lanes' dimensions are kept intact
43818 return participant.height - resizedLane.height + LANE_MIN_DIMENSIONS.height;
43819 }
43820
43821 /**
43822 * Find nested lane which is currently resized.
43823 *
43824 * @param {Array<djs.model.Shape>} lanes
43825 * @param {number} resizeStart
43826 */
43827 function findResizedLane(lanes, resizeStart) {
43828 var i, lane, childLanes;
43829
43830 for (i = 0; i < lanes.length; i++) {
43831 lane = lanes[i];
43832
43833 // resizing current lane or a lane nested
43834 if (resizeStart >= lane.y && resizeStart <= lane.y + lane.height) {
43835 childLanes = getChildLanes(lane);
43836
43837 // a nested lane is resized
43838 if (childLanes.length) {
43839 return findResizedLane(childLanes, resizeStart);
43840 }
43841
43842 // current lane is the resized one
43843 return lane;
43844 }
43845 }
43846 }
43847
43848 /**
43849 * Add start event replacing element with expanded sub process.
43850 *
43851 * @param {Injector} injector
43852 * @param {Modeling} modeling
43853 */
43854 function SubProcessStartEventBehavior(injector, modeling) {
43855 injector.invoke(CommandInterceptor, this);
43856
43857 this.postExecuted('shape.replace', function(event) {
43858 var oldShape = event.context.oldShape,
43859 newShape = event.context.newShape;
43860
43861 if (
43862 !is$1(newShape, 'bpmn:SubProcess') ||
43863 !is$1(oldShape, 'bpmn:Task') ||
43864 !isExpanded(newShape)
43865 ) {
43866 return;
43867 }
43868
43869 var position = getStartEventPosition(newShape);
43870
43871 modeling.createShape({ type: 'bpmn:StartEvent' }, position, newShape);
43872 });
43873 }
43874
43875 SubProcessStartEventBehavior.$inject = [
43876 'injector',
43877 'modeling'
43878 ];
43879
43880 inherits$1(SubProcessStartEventBehavior, CommandInterceptor);
43881
43882 // helpers //////////
43883
43884 function getStartEventPosition(shape) {
43885 return {
43886 x: shape.x + shape.width / 6,
43887 y: shape.y + shape.height / 2
43888 };
43889 }
43890
43891 var LOW_PRIORITY$8 = 500;
43892
43893
43894 function ToggleElementCollapseBehaviour(
43895 eventBus, elementFactory, modeling,
43896 resize) {
43897
43898 CommandInterceptor.call(this, eventBus);
43899
43900
43901 function hideEmptyLabels(children) {
43902 if (children.length) {
43903 children.forEach(function(child) {
43904 if (child.type === 'label' && !child.businessObject.name) {
43905 child.hidden = true;
43906 }
43907 });
43908 }
43909 }
43910
43911 function expandedBounds(shape, defaultSize) {
43912 var children = shape.children,
43913 newBounds = defaultSize,
43914 visibleElements,
43915 visibleBBox;
43916
43917 visibleElements = filterVisible(children).concat([ shape ]);
43918
43919 visibleBBox = computeChildrenBBox(visibleElements);
43920
43921 if (visibleBBox) {
43922
43923 // center to visibleBBox with max(defaultSize, childrenBounds)
43924 newBounds.width = Math.max(visibleBBox.width, newBounds.width);
43925 newBounds.height = Math.max(visibleBBox.height, newBounds.height);
43926
43927 newBounds.x = visibleBBox.x + (visibleBBox.width - newBounds.width) / 2;
43928 newBounds.y = visibleBBox.y + (visibleBBox.height - newBounds.height) / 2;
43929 } else {
43930
43931 // center to collapsed shape with defaultSize
43932 newBounds.x = shape.x + (shape.width - newBounds.width) / 2;
43933 newBounds.y = shape.y + (shape.height - newBounds.height) / 2;
43934 }
43935
43936 return newBounds;
43937 }
43938
43939 function collapsedBounds(shape, defaultSize) {
43940
43941 return {
43942 x: shape.x + (shape.width - defaultSize.width) / 2,
43943 y: shape.y + (shape.height - defaultSize.height) / 2,
43944 width: defaultSize.width,
43945 height: defaultSize.height
43946 };
43947 }
43948
43949 this.executed([ 'shape.toggleCollapse' ], LOW_PRIORITY$8, function(e) {
43950
43951 var context = e.context,
43952 shape = context.shape;
43953
43954 if (!is$1(shape, 'bpmn:SubProcess')) {
43955 return;
43956 }
43957
43958 if (!shape.collapsed) {
43959
43960 // all children got made visible through djs, hide empty labels
43961 hideEmptyLabels(shape.children);
43962
43963 // remove collapsed marker
43964 getBusinessObject(shape).di.isExpanded = true;
43965 } else {
43966
43967 // place collapsed marker
43968 getBusinessObject(shape).di.isExpanded = false;
43969 }
43970 });
43971
43972 this.reverted([ 'shape.toggleCollapse' ], LOW_PRIORITY$8, function(e) {
43973
43974 var context = e.context;
43975 var shape = context.shape;
43976
43977
43978 // revert removing/placing collapsed marker
43979 if (!shape.collapsed) {
43980 getBusinessObject(shape).di.isExpanded = true;
43981
43982 } else {
43983 getBusinessObject(shape).di.isExpanded = false;
43984 }
43985 });
43986
43987 this.postExecuted([ 'shape.toggleCollapse' ], LOW_PRIORITY$8, function(e) {
43988 var shape = e.context.shape,
43989 defaultSize = elementFactory._getDefaultSize(shape),
43990 newBounds;
43991
43992 if (shape.collapsed) {
43993
43994 // resize to default size of collapsed shapes
43995 newBounds = collapsedBounds(shape, defaultSize);
43996 } else {
43997
43998 // resize to bounds of max(visible children, defaultSize)
43999 newBounds = expandedBounds(shape, defaultSize);
44000 }
44001
44002 modeling.resizeShape(shape, newBounds, null, {
44003 autoResize: shape.collapsed ? false : 'nwse'
44004 });
44005 });
44006
44007 }
44008
44009
44010 inherits$1(ToggleElementCollapseBehaviour, CommandInterceptor);
44011
44012 ToggleElementCollapseBehaviour.$inject = [
44013 'eventBus',
44014 'elementFactory',
44015 'modeling'
44016 ];
44017
44018
44019 // helpers //////////////////////
44020
44021 function filterVisible(elements) {
44022 return elements.filter(function(e) {
44023 return !e.hidden;
44024 });
44025 }
44026
44027 /**
44028 * Unclaims model IDs on element deletion.
44029 *
44030 * @param {Canvas} canvas
44031 * @param {Injector} injector
44032 * @param {Moddle} moddle
44033 * @param {Modeling} modeling
44034 */
44035 function UnclaimIdBehavior(canvas, injector, moddle, modeling) {
44036 injector.invoke(CommandInterceptor, this);
44037
44038 this.preExecute('shape.delete', function(event) {
44039 var context = event.context,
44040 shape = context.shape,
44041 shapeBo = shape.businessObject;
44042
44043 if (isLabel$6(shape)) {
44044 return;
44045 }
44046
44047 if (is$1(shape, 'bpmn:Participant') && isExpanded(shape)) {
44048 moddle.ids.unclaim(shapeBo.processRef.id);
44049 }
44050
44051 modeling.unclaimId(shapeBo.id, shapeBo);
44052 });
44053
44054
44055 this.preExecute('connection.delete', function(event) {
44056 var context = event.context,
44057 connection = context.connection,
44058 connectionBo = connection.businessObject;
44059
44060 modeling.unclaimId(connectionBo.id, connectionBo);
44061 });
44062
44063 this.preExecute('canvas.updateRoot', function() {
44064 var rootElement = canvas.getRootElement(),
44065 rootElementBo = rootElement.businessObject;
44066
44067 moddle.ids.unclaim(rootElementBo.id);
44068 });
44069 }
44070
44071 inherits$1(UnclaimIdBehavior, CommandInterceptor);
44072
44073 UnclaimIdBehavior.$inject = [ 'canvas', 'injector', 'moddle', 'modeling' ];
44074
44075 var LOW_PRIORITY$7 = 500,
44076 HIGH_PRIORITY$7 = 5000;
44077
44078
44079 /**
44080 * BPMN specific delete lane behavior
44081 */
44082 function UpdateFlowNodeRefsBehavior(eventBus, modeling, translate) {
44083
44084 CommandInterceptor.call(this, eventBus);
44085
44086 /**
44087 * Ok, this is it:
44088 *
44089 * We have to update the Lane#flowNodeRefs _and_
44090 * FlowNode#lanes with every FlowNode move/resize and
44091 * Lane move/resize.
44092 *
44093 * We want to group that stuff to recompute containments
44094 * as efficient as possible.
44095 *
44096 * Yea!
44097 */
44098
44099 // the update context
44100 var context;
44101
44102
44103 function initContext() {
44104 context = context || new UpdateContext();
44105 context.enter();
44106
44107 return context;
44108 }
44109
44110 function getContext() {
44111 if (!context) {
44112 throw new Error(translate('out of bounds release'));
44113 }
44114
44115 return context;
44116 }
44117
44118 function releaseContext() {
44119
44120 if (!context) {
44121 throw new Error(translate('out of bounds release'));
44122 }
44123
44124 var triggerUpdate = context.leave();
44125
44126 if (triggerUpdate) {
44127 modeling.updateLaneRefs(context.flowNodes, context.lanes);
44128
44129 context = null;
44130 }
44131
44132 return triggerUpdate;
44133 }
44134
44135
44136 var laneRefUpdateEvents = [
44137 'spaceTool',
44138 'lane.add',
44139 'lane.resize',
44140 'lane.split',
44141 'elements.create',
44142 'elements.delete',
44143 'elements.move',
44144 'shape.create',
44145 'shape.delete',
44146 'shape.move',
44147 'shape.resize'
44148 ];
44149
44150
44151 // listen to a lot of stuff to group lane updates
44152
44153 this.preExecute(laneRefUpdateEvents, HIGH_PRIORITY$7, function(event) {
44154 initContext();
44155 });
44156
44157 this.postExecuted(laneRefUpdateEvents, LOW_PRIORITY$7, function(event) {
44158 releaseContext();
44159 });
44160
44161
44162 // Mark flow nodes + lanes that need an update
44163
44164 this.preExecute([
44165 'shape.create',
44166 'shape.move',
44167 'shape.delete',
44168 'shape.resize'
44169 ], function(event) {
44170
44171 var context = event.context,
44172 shape = context.shape;
44173
44174 var updateContext = getContext();
44175
44176 // no need to update labels
44177 if (shape.labelTarget) {
44178 return;
44179 }
44180
44181 if (is$1(shape, 'bpmn:Lane')) {
44182 updateContext.addLane(shape);
44183 }
44184
44185 if (is$1(shape, 'bpmn:FlowNode')) {
44186 updateContext.addFlowNode(shape);
44187 }
44188 });
44189 }
44190
44191 UpdateFlowNodeRefsBehavior.$inject = [
44192 'eventBus',
44193 'modeling' ,
44194 'translate'
44195 ];
44196
44197 inherits$1(UpdateFlowNodeRefsBehavior, CommandInterceptor);
44198
44199
44200 function UpdateContext() {
44201
44202 this.flowNodes = [];
44203 this.lanes = [];
44204
44205 this.counter = 0;
44206
44207 this.addLane = function(lane) {
44208 this.lanes.push(lane);
44209 };
44210
44211 this.addFlowNode = function(flowNode) {
44212 this.flowNodes.push(flowNode);
44213 };
44214
44215 this.enter = function() {
44216 this.counter++;
44217 };
44218
44219 this.leave = function() {
44220 this.counter--;
44221
44222 return !this.counter;
44223 };
44224 }
44225
44226 /**
44227 * A behavior that unsets the Default property of
44228 * sequence flow source on element delete, if the
44229 * removed element is the Gateway or Task's default flow.
44230 *
44231 * @param {EventBus} eventBus
44232 * @param {Modeling} modeling
44233 */
44234 function DeleteSequenceFlowBehavior(eventBus, modeling) {
44235
44236 CommandInterceptor.call(this, eventBus);
44237
44238
44239 this.preExecute('connection.delete', function(event) {
44240 var context = event.context,
44241 connection = context.connection,
44242 source = connection.source;
44243
44244 if (isDefaultFlow(connection, source)) {
44245 modeling.updateProperties(source, {
44246 'default': null
44247 });
44248 }
44249 });
44250 }
44251
44252 inherits$1(DeleteSequenceFlowBehavior, CommandInterceptor);
44253
44254 DeleteSequenceFlowBehavior.$inject = [
44255 'eventBus',
44256 'modeling'
44257 ];
44258
44259
44260 // helpers //////////////////////
44261
44262 function isDefaultFlow(connection, source) {
44263
44264 if (!is$1(connection, 'bpmn:SequenceFlow')) {
44265 return false;
44266 }
44267
44268 var sourceBo = getBusinessObject(source),
44269 sequenceFlow = getBusinessObject(connection);
44270
44271 return sourceBo.get('default') === sequenceFlow;
44272 }
44273
44274 var BehaviorModule = {
44275 __init__: [
44276 'adaptiveLabelPositioningBehavior',
44277 'appendBehavior',
44278 'associationBehavior',
44279 'attachEventBehavior',
44280 'boundaryEventBehavior',
44281 'rootElementReferenceBehavior',
44282 'createBehavior',
44283 'fixHoverBehavior',
44284 'createDataObjectBehavior',
44285 'createParticipantBehavior',
44286 'dataStoreBehavior',
44287 'dataInputAssociationBehavior',
44288 'deleteLaneBehavior',
44289 'detachEventBehavior',
44290 'dropOnFlowBehavior',
44291 'eventBasedGatewayBehavior',
44292 'groupBehavior',
44293 'importDockingFix',
44294 'isHorizontalFix',
44295 'labelBehavior',
44296 'messageFlowBehavior',
44297 'modelingFeedback',
44298 'removeElementBehavior',
44299 'removeParticipantBehavior',
44300 'replaceConnectionBehavior',
44301 'replaceElementBehaviour',
44302 'resizeBehavior',
44303 'resizeLaneBehavior',
44304 'toggleElementCollapseBehaviour',
44305 'spaceToolBehavior',
44306 'subProcessStartEventBehavior',
44307 'unclaimIdBehavior',
44308 'unsetDefaultFlowBehavior',
44309 'updateFlowNodeRefsBehavior'
44310 ],
44311 adaptiveLabelPositioningBehavior: [ 'type', AdaptiveLabelPositioningBehavior ],
44312 appendBehavior: [ 'type', AppendBehavior ],
44313 associationBehavior: [ 'type', AssociationBehavior ],
44314 attachEventBehavior: [ 'type', AttachEventBehavior ],
44315 boundaryEventBehavior: [ 'type', BoundaryEventBehavior ],
44316 rootElementReferenceBehavior: [ 'type', RootElementReferenceBehavior ],
44317 createBehavior: [ 'type', CreateBehavior ],
44318 fixHoverBehavior: [ 'type', FixHoverBehavior ],
44319 createDataObjectBehavior: [ 'type', CreateDataObjectBehavior ],
44320 createParticipantBehavior: [ 'type', CreateParticipantBehavior ],
44321 dataInputAssociationBehavior: [ 'type', DataInputAssociationBehavior ],
44322 dataStoreBehavior: [ 'type', DataStoreBehavior ],
44323 deleteLaneBehavior: [ 'type', DeleteLaneBehavior ],
44324 detachEventBehavior: [ 'type', DetachEventBehavior ],
44325 dropOnFlowBehavior: [ 'type', DropOnFlowBehavior ],
44326 eventBasedGatewayBehavior: [ 'type', EventBasedGatewayBehavior ],
44327 groupBehavior: [ 'type', GroupBehavior ],
44328 importDockingFix: [ 'type', ImportDockingFix ],
44329 isHorizontalFix: [ 'type', IsHorizontalFix ],
44330 labelBehavior: [ 'type', LabelBehavior ],
44331 messageFlowBehavior: [ 'type', MessageFlowBehavior ],
44332 modelingFeedback: [ 'type', ModelingFeedback ],
44333 replaceConnectionBehavior: [ 'type', ReplaceConnectionBehavior ],
44334 removeParticipantBehavior: [ 'type', RemoveParticipantBehavior ],
44335 replaceElementBehaviour: [ 'type', ReplaceElementBehaviour ],
44336 resizeBehavior: [ 'type', ResizeBehavior ],
44337 resizeLaneBehavior: [ 'type', ResizeLaneBehavior ],
44338 removeElementBehavior: [ 'type', RemoveElementBehavior ],
44339 toggleElementCollapseBehaviour : [ 'type', ToggleElementCollapseBehaviour ],
44340 spaceToolBehavior: [ 'type', SpaceToolBehavior ],
44341 subProcessStartEventBehavior: [ 'type', SubProcessStartEventBehavior ],
44342 unclaimIdBehavior: [ 'type', UnclaimIdBehavior ],
44343 updateFlowNodeRefsBehavior: [ 'type', UpdateFlowNodeRefsBehavior ],
44344 unsetDefaultFlowBehavior: [ 'type', DeleteSequenceFlowBehavior ]
44345 };
44346
44347 function getBoundaryAttachment(position, targetBounds) {
44348
44349 var orientation = getOrientation(position, targetBounds, -15);
44350
44351 if (orientation !== 'intersect') {
44352 return orientation;
44353 } else {
44354 return null;
44355 }
44356 }
44357
44358 /**
44359 * BPMN specific modeling rule
44360 */
44361 function BpmnRules(eventBus) {
44362 RuleProvider.call(this, eventBus);
44363 }
44364
44365 inherits$1(BpmnRules, RuleProvider);
44366
44367 BpmnRules.$inject = [ 'eventBus' ];
44368
44369 BpmnRules.prototype.init = function() {
44370
44371 this.addRule('connection.start', function(context) {
44372 var source = context.source;
44373
44374 return canStartConnection(source);
44375 });
44376
44377 this.addRule('connection.create', function(context) {
44378 var source = context.source,
44379 target = context.target,
44380 hints = context.hints || {},
44381 targetParent = hints.targetParent,
44382 targetAttach = hints.targetAttach;
44383
44384 // don't allow incoming connections on
44385 // newly created boundary events
44386 // to boundary events
44387 if (targetAttach) {
44388 return false;
44389 }
44390
44391 // temporarily set target parent for scoping
44392 // checks to work
44393 if (targetParent) {
44394 target.parent = targetParent;
44395 }
44396
44397 try {
44398 return canConnect(source, target);
44399 } finally {
44400
44401 // unset temporary target parent
44402 if (targetParent) {
44403 target.parent = null;
44404 }
44405 }
44406 });
44407
44408 this.addRule('connection.reconnect', function(context) {
44409
44410 var connection = context.connection,
44411 source = context.source,
44412 target = context.target;
44413
44414 return canConnect(source, target, connection);
44415 });
44416
44417 this.addRule('connection.updateWaypoints', function(context) {
44418 return {
44419 type: context.connection.type
44420 };
44421 });
44422
44423 this.addRule('shape.resize', function(context) {
44424
44425 var shape = context.shape,
44426 newBounds = context.newBounds;
44427
44428 return canResize(shape, newBounds);
44429 });
44430
44431 this.addRule('elements.create', function(context) {
44432 var elements = context.elements,
44433 position = context.position,
44434 target = context.target;
44435
44436 if (isConnection$8(target) && !canInsert(elements, target)) {
44437 return false;
44438 }
44439
44440 return every(elements, function(element) {
44441 if (isConnection$8(element)) {
44442 return canConnect(element.source, element.target, element);
44443 }
44444
44445 if (element.host) {
44446 return canAttach(element, element.host, null, position);
44447 }
44448
44449 return canCreate(element, target, null);
44450 });
44451 });
44452
44453 this.addRule('elements.move', function(context) {
44454
44455 var target = context.target,
44456 shapes = context.shapes,
44457 position = context.position;
44458
44459 return canAttach(shapes, target, null, position) ||
44460 canReplace(shapes, target, position) ||
44461 canMove(shapes, target) ||
44462 canInsert(shapes, target);
44463 });
44464
44465 this.addRule('shape.create', function(context) {
44466 return canCreate(
44467 context.shape,
44468 context.target,
44469 context.source,
44470 context.position
44471 );
44472 });
44473
44474 this.addRule('shape.attach', function(context) {
44475
44476 return canAttach(
44477 context.shape,
44478 context.target,
44479 null,
44480 context.position
44481 );
44482 });
44483
44484 this.addRule('element.copy', function(context) {
44485 var element = context.element,
44486 elements = context.elements;
44487
44488 return canCopy(elements, element);
44489 });
44490 };
44491
44492 BpmnRules.prototype.canConnectMessageFlow = canConnectMessageFlow;
44493
44494 BpmnRules.prototype.canConnectSequenceFlow = canConnectSequenceFlow;
44495
44496 BpmnRules.prototype.canConnectDataAssociation = canConnectDataAssociation;
44497
44498 BpmnRules.prototype.canConnectAssociation = canConnectAssociation;
44499
44500 BpmnRules.prototype.canMove = canMove;
44501
44502 BpmnRules.prototype.canAttach = canAttach;
44503
44504 BpmnRules.prototype.canReplace = canReplace;
44505
44506 BpmnRules.prototype.canDrop = canDrop;
44507
44508 BpmnRules.prototype.canInsert = canInsert;
44509
44510 BpmnRules.prototype.canCreate = canCreate;
44511
44512 BpmnRules.prototype.canConnect = canConnect;
44513
44514 BpmnRules.prototype.canResize = canResize;
44515
44516 BpmnRules.prototype.canCopy = canCopy;
44517
44518 /**
44519 * Utility functions for rule checking
44520 */
44521
44522 /**
44523 * Checks if given element can be used for starting connection.
44524 *
44525 * @param {Element} source
44526 * @return {boolean}
44527 */
44528 function canStartConnection(element) {
44529 if (nonExistingOrLabel(element)) {
44530 return null;
44531 }
44532
44533 return isAny(element, [
44534 'bpmn:FlowNode',
44535 'bpmn:InteractionNode',
44536 'bpmn:DataObjectReference',
44537 'bpmn:DataStoreReference',
44538 'bpmn:Group',
44539 'bpmn:TextAnnotation'
44540 ]);
44541 }
44542
44543 function nonExistingOrLabel(element) {
44544 return !element || isLabel$6(element);
44545 }
44546
44547 function isSame$1(a, b) {
44548 return a === b;
44549 }
44550
44551 function getOrganizationalParent(element) {
44552
44553 do {
44554 if (is$1(element, 'bpmn:Process')) {
44555 return getBusinessObject(element);
44556 }
44557
44558 if (is$1(element, 'bpmn:Participant')) {
44559 return (
44560 getBusinessObject(element).processRef ||
44561 getBusinessObject(element)
44562 );
44563 }
44564 } while ((element = element.parent));
44565
44566 }
44567
44568 function isTextAnnotation(element) {
44569 return is$1(element, 'bpmn:TextAnnotation');
44570 }
44571
44572 function isGroup(element) {
44573 return is$1(element, 'bpmn:Group') && !element.labelTarget;
44574 }
44575
44576 function isCompensationBoundary(element) {
44577 return is$1(element, 'bpmn:BoundaryEvent') &&
44578 hasEventDefinition(element, 'bpmn:CompensateEventDefinition');
44579 }
44580
44581 function isForCompensation(e) {
44582 return getBusinessObject(e).isForCompensation;
44583 }
44584
44585 function isSameOrganization(a, b) {
44586 var parentA = getOrganizationalParent(a),
44587 parentB = getOrganizationalParent(b);
44588
44589 return parentA === parentB;
44590 }
44591
44592 function isMessageFlowSource(element) {
44593 return (
44594 is$1(element, 'bpmn:InteractionNode') &&
44595 !is$1(element, 'bpmn:BoundaryEvent') && (
44596 !is$1(element, 'bpmn:Event') || (
44597 is$1(element, 'bpmn:ThrowEvent') &&
44598 hasEventDefinitionOrNone(element, 'bpmn:MessageEventDefinition')
44599 )
44600 )
44601 );
44602 }
44603
44604 function isMessageFlowTarget(element) {
44605 return (
44606 is$1(element, 'bpmn:InteractionNode') &&
44607 !isForCompensation(element) && (
44608 !is$1(element, 'bpmn:Event') || (
44609 is$1(element, 'bpmn:CatchEvent') &&
44610 hasEventDefinitionOrNone(element, 'bpmn:MessageEventDefinition')
44611 )
44612 ) && !(
44613 is$1(element, 'bpmn:BoundaryEvent') &&
44614 !hasEventDefinition(element, 'bpmn:MessageEventDefinition')
44615 )
44616 );
44617 }
44618
44619 function getScopeParent(element) {
44620
44621 var parent = element;
44622
44623 while ((parent = parent.parent)) {
44624
44625 if (is$1(parent, 'bpmn:FlowElementsContainer')) {
44626 return getBusinessObject(parent);
44627 }
44628
44629 if (is$1(parent, 'bpmn:Participant')) {
44630 return getBusinessObject(parent).processRef;
44631 }
44632 }
44633
44634 return null;
44635 }
44636
44637 function isSameScope(a, b) {
44638 var scopeParentA = getScopeParent(a),
44639 scopeParentB = getScopeParent(b);
44640
44641 return scopeParentA === scopeParentB;
44642 }
44643
44644 function hasEventDefinition(element, eventDefinition) {
44645 var bo = getBusinessObject(element);
44646
44647 return !!find(bo.eventDefinitions || [], function(definition) {
44648 return is$1(definition, eventDefinition);
44649 });
44650 }
44651
44652 function hasEventDefinitionOrNone(element, eventDefinition) {
44653 var bo = getBusinessObject(element);
44654
44655 return (bo.eventDefinitions || []).every(function(definition) {
44656 return is$1(definition, eventDefinition);
44657 });
44658 }
44659
44660 function isSequenceFlowSource(element) {
44661 return (
44662 is$1(element, 'bpmn:FlowNode') &&
44663 !is$1(element, 'bpmn:EndEvent') &&
44664 !isEventSubProcess(element) &&
44665 !(is$1(element, 'bpmn:IntermediateThrowEvent') &&
44666 hasEventDefinition(element, 'bpmn:LinkEventDefinition')
44667 ) &&
44668 !isCompensationBoundary(element) &&
44669 !isForCompensation(element)
44670 );
44671 }
44672
44673 function isSequenceFlowTarget(element) {
44674 return (
44675 is$1(element, 'bpmn:FlowNode') &&
44676 !is$1(element, 'bpmn:StartEvent') &&
44677 !is$1(element, 'bpmn:BoundaryEvent') &&
44678 !isEventSubProcess(element) &&
44679 !(is$1(element, 'bpmn:IntermediateCatchEvent') &&
44680 hasEventDefinition(element, 'bpmn:LinkEventDefinition')
44681 ) &&
44682 !isForCompensation(element)
44683 );
44684 }
44685
44686 function isEventBasedTarget(element) {
44687 return (
44688 is$1(element, 'bpmn:ReceiveTask') || (
44689 is$1(element, 'bpmn:IntermediateCatchEvent') && (
44690 hasEventDefinition(element, 'bpmn:MessageEventDefinition') ||
44691 hasEventDefinition(element, 'bpmn:TimerEventDefinition') ||
44692 hasEventDefinition(element, 'bpmn:ConditionalEventDefinition') ||
44693 hasEventDefinition(element, 'bpmn:SignalEventDefinition')
44694 )
44695 )
44696 );
44697 }
44698
44699 function isConnection$8(element) {
44700 return element.waypoints;
44701 }
44702
44703 function getParents(element) {
44704
44705 var parents = [];
44706
44707 while (element) {
44708 element = element.parent;
44709
44710 if (element) {
44711 parents.push(element);
44712 }
44713 }
44714
44715 return parents;
44716 }
44717
44718 function isParent(possibleParent, element) {
44719 var allParents = getParents(element);
44720 return allParents.indexOf(possibleParent) !== -1;
44721 }
44722
44723 function canConnect(source, target, connection) {
44724
44725 if (nonExistingOrLabel(source) || nonExistingOrLabel(target)) {
44726 return null;
44727 }
44728
44729 if (!is$1(connection, 'bpmn:DataAssociation')) {
44730
44731 if (canConnectMessageFlow(source, target)) {
44732 return { type: 'bpmn:MessageFlow' };
44733 }
44734
44735 if (canConnectSequenceFlow(source, target)) {
44736 return { type: 'bpmn:SequenceFlow' };
44737 }
44738 }
44739
44740 var connectDataAssociation = canConnectDataAssociation(source, target);
44741
44742 if (connectDataAssociation) {
44743 return connectDataAssociation;
44744 }
44745
44746 if (isCompensationBoundary(source) && isForCompensation(target)) {
44747 return {
44748 type: 'bpmn:Association',
44749 associationDirection: 'One'
44750 };
44751 }
44752
44753 if (canConnectAssociation(source, target)) {
44754
44755 return {
44756 type: 'bpmn:Association'
44757 };
44758 }
44759
44760 return false;
44761 }
44762
44763 /**
44764 * Can an element be dropped into the target element
44765 *
44766 * @return {boolean}
44767 */
44768 function canDrop(element, target, position) {
44769
44770 // can move labels and groups everywhere
44771 if (isLabel$6(element) || isGroup(element)) {
44772 return true;
44773 }
44774
44775
44776 // disallow to create elements on collapsed pools
44777 if (is$1(target, 'bpmn:Participant') && !isExpanded(target)) {
44778 return false;
44779 }
44780
44781 // allow to create new participants on
44782 // existing collaboration and process diagrams
44783 if (is$1(element, 'bpmn:Participant')) {
44784 return is$1(target, 'bpmn:Process') || is$1(target, 'bpmn:Collaboration');
44785 }
44786
44787 // allow moving DataInput / DataOutput within its original container only
44788 if (isAny(element, [ 'bpmn:DataInput', 'bpmn:DataOutput' ])) {
44789
44790 if (element.parent) {
44791 return target === element.parent;
44792 }
44793 }
44794
44795 // allow creating lanes on participants and other lanes only
44796 if (is$1(element, 'bpmn:Lane')) {
44797 return is$1(target, 'bpmn:Participant') || is$1(target, 'bpmn:Lane');
44798 }
44799
44800 // disallow dropping boundary events which cannot replace with intermediate event
44801 if (is$1(element, 'bpmn:BoundaryEvent') && !isDroppableBoundaryEvent(element)) {
44802 return false;
44803 }
44804
44805 // drop flow elements onto flow element containers
44806 // and participants
44807 if (is$1(element, 'bpmn:FlowElement') && !is$1(element, 'bpmn:DataStoreReference')) {
44808 if (is$1(target, 'bpmn:FlowElementsContainer')) {
44809 return isExpanded(target);
44810 }
44811
44812 return isAny(target, [ 'bpmn:Participant', 'bpmn:Lane' ]);
44813 }
44814
44815 // disallow dropping data store reference if there is no process to append to
44816 if (is$1(element, 'bpmn:DataStoreReference') && is$1(target, 'bpmn:Collaboration')) {
44817 return some(getBusinessObject(target).get('participants'), function(participant) {
44818 return !!participant.get('processRef');
44819 });
44820 }
44821
44822 // account for the fact that data associations are always
44823 // rendered and moved to top (Process or Collaboration level)
44824 //
44825 // artifacts may be placed wherever, too
44826 if (isAny(element, [ 'bpmn:Artifact', 'bpmn:DataAssociation', 'bpmn:DataStoreReference' ])) {
44827 return isAny(target, [
44828 'bpmn:Collaboration',
44829 'bpmn:Lane',
44830 'bpmn:Participant',
44831 'bpmn:Process',
44832 'bpmn:SubProcess' ]);
44833 }
44834
44835 if (is$1(element, 'bpmn:MessageFlow')) {
44836 return is$1(target, 'bpmn:Collaboration')
44837 || element.source.parent == target
44838 || element.target.parent == target;
44839 }
44840
44841 return false;
44842 }
44843
44844 function isDroppableBoundaryEvent(event) {
44845 return getBusinessObject(event).cancelActivity && (
44846 hasNoEventDefinition(event) || hasCommonBoundaryIntermediateEventDefinition(event)
44847 );
44848 }
44849
44850 function isBoundaryEvent(element) {
44851 return !isLabel$6(element) && is$1(element, 'bpmn:BoundaryEvent');
44852 }
44853
44854 function isLane(element) {
44855 return is$1(element, 'bpmn:Lane');
44856 }
44857
44858 /**
44859 * We treat IntermediateThrowEvents as boundary events during create,
44860 * this must be reflected in the rules.
44861 */
44862 function isBoundaryCandidate(element) {
44863 if (isBoundaryEvent(element)) {
44864 return true;
44865 }
44866
44867 if (is$1(element, 'bpmn:IntermediateThrowEvent') && hasNoEventDefinition(element)) {
44868 return true;
44869 }
44870
44871 return (
44872 is$1(element, 'bpmn:IntermediateCatchEvent') &&
44873 hasCommonBoundaryIntermediateEventDefinition(element)
44874 );
44875 }
44876
44877 function hasNoEventDefinition(element) {
44878 var bo = getBusinessObject(element);
44879
44880 return bo && !(bo.eventDefinitions && bo.eventDefinitions.length);
44881 }
44882
44883 function hasCommonBoundaryIntermediateEventDefinition(element) {
44884 return hasOneOfEventDefinitions(element, [
44885 'bpmn:MessageEventDefinition',
44886 'bpmn:TimerEventDefinition',
44887 'bpmn:SignalEventDefinition',
44888 'bpmn:ConditionalEventDefinition'
44889 ]);
44890 }
44891
44892 function hasOneOfEventDefinitions(element, eventDefinitions) {
44893 return eventDefinitions.some(function(definition) {
44894 return hasEventDefinition(element, definition);
44895 });
44896 }
44897
44898 function isReceiveTaskAfterEventBasedGateway(element) {
44899 return (
44900 is$1(element, 'bpmn:ReceiveTask') &&
44901 find(element.incoming, function(incoming) {
44902 return is$1(incoming.source, 'bpmn:EventBasedGateway');
44903 })
44904 );
44905 }
44906
44907
44908 function canAttach(elements, target, source, position) {
44909
44910 if (!Array.isArray(elements)) {
44911 elements = [ elements ];
44912 }
44913
44914 // only (re-)attach one element at a time
44915 if (elements.length !== 1) {
44916 return false;
44917 }
44918
44919 var element = elements[0];
44920
44921 // do not attach labels
44922 if (isLabel$6(element)) {
44923 return false;
44924 }
44925
44926 // only handle boundary events
44927 if (!isBoundaryCandidate(element)) {
44928 return false;
44929 }
44930
44931 // disallow drop on event sub processes
44932 if (isEventSubProcess(target)) {
44933 return false;
44934 }
44935
44936 // only allow drop on non compensation activities
44937 if (!is$1(target, 'bpmn:Activity') || isForCompensation(target)) {
44938 return false;
44939 }
44940
44941 // only attach to subprocess border
44942 if (position && !getBoundaryAttachment(position, target)) {
44943 return false;
44944 }
44945
44946 // do not attach on receive tasks after event based gateways
44947 if (isReceiveTaskAfterEventBasedGateway(target)) {
44948 return false;
44949 }
44950
44951 return 'attach';
44952 }
44953
44954
44955 /**
44956 * Defines how to replace elements for a given target.
44957 *
44958 * Returns an array containing all elements which will be replaced.
44959 *
44960 * @example
44961 *
44962 * [{ id: 'IntermediateEvent_2',
44963 * type: 'bpmn:StartEvent'
44964 * },
44965 * { id: 'IntermediateEvent_5',
44966 * type: 'bpmn:EndEvent'
44967 * }]
44968 *
44969 * @param {Array} elements
44970 * @param {Object} target
44971 *
44972 * @return {Object} an object containing all elements which have to be replaced
44973 */
44974 function canReplace(elements, target, position) {
44975
44976 if (!target) {
44977 return false;
44978 }
44979
44980 var canExecute = {
44981 replacements: []
44982 };
44983
44984 forEach(elements, function(element) {
44985
44986 if (!isEventSubProcess(target)) {
44987
44988 if (is$1(element, 'bpmn:StartEvent') &&
44989 element.type !== 'label' &&
44990 canDrop(element, target)) {
44991
44992 // replace a non-interrupting start event by a blank interrupting start event
44993 // when the target is not an event sub process
44994 if (!isInterrupting(element)) {
44995 canExecute.replacements.push({
44996 oldElementId: element.id,
44997 newElementType: 'bpmn:StartEvent'
44998 });
44999 }
45000
45001 // replace an error/escalation/compensate start event by a blank interrupting start event
45002 // when the target is not an event sub process
45003 if (hasErrorEventDefinition(element) ||
45004 hasEscalationEventDefinition(element) ||
45005 hasCompensateEventDefinition(element)) {
45006 canExecute.replacements.push({
45007 oldElementId: element.id,
45008 newElementType: 'bpmn:StartEvent'
45009 });
45010 }
45011
45012 // replace a typed start event by a blank interrupting start event
45013 // when the target is a sub process but not an event sub process
45014 if (hasOneOfEventDefinitions(element,
45015 [
45016 'bpmn:MessageEventDefinition',
45017 'bpmn:TimerEventDefinition',
45018 'bpmn:SignalEventDefinition',
45019 'bpmn:ConditionalEventDefinition'
45020 ]) &&
45021 is$1(target, 'bpmn:SubProcess')) {
45022 canExecute.replacements.push({
45023 oldElementId: element.id,
45024 newElementType: 'bpmn:StartEvent'
45025 });
45026 }
45027 }
45028 }
45029
45030 if (!is$1(target, 'bpmn:Transaction')) {
45031 if (hasEventDefinition(element, 'bpmn:CancelEventDefinition') &&
45032 element.type !== 'label') {
45033
45034 if (is$1(element, 'bpmn:EndEvent') && canDrop(element, target)) {
45035 canExecute.replacements.push({
45036 oldElementId: element.id,
45037 newElementType: 'bpmn:EndEvent'
45038 });
45039 }
45040
45041 if (is$1(element, 'bpmn:BoundaryEvent') && canAttach(element, target, null, position)) {
45042 canExecute.replacements.push({
45043 oldElementId: element.id,
45044 newElementType: 'bpmn:BoundaryEvent'
45045 });
45046 }
45047 }
45048 }
45049 });
45050
45051 return canExecute.replacements.length ? canExecute : false;
45052 }
45053
45054 function canMove(elements, target) {
45055
45056 // do not move selection containing lanes
45057 if (some(elements, isLane)) {
45058 return false;
45059 }
45060
45061 // allow default move check to start move operation
45062 if (!target) {
45063 return true;
45064 }
45065
45066 return elements.every(function(element) {
45067 return canDrop(element, target);
45068 });
45069 }
45070
45071 function canCreate(shape, target, source, position) {
45072
45073 if (!target) {
45074 return false;
45075 }
45076
45077 if (isLabel$6(shape) || isGroup(shape)) {
45078 return true;
45079 }
45080
45081 if (isSame$1(source, target)) {
45082 return false;
45083 }
45084
45085 // ensure we do not drop the element
45086 // into source
45087 if (source && isParent(source, target)) {
45088 return false;
45089 }
45090
45091 return canDrop(shape, target) || canInsert(shape, target);
45092 }
45093
45094 function canResize(shape, newBounds) {
45095 if (is$1(shape, 'bpmn:SubProcess')) {
45096 return (
45097 isExpanded(shape) && (
45098 !newBounds || (newBounds.width >= 100 && newBounds.height >= 80)
45099 )
45100 );
45101 }
45102
45103 if (is$1(shape, 'bpmn:Lane')) {
45104 return !newBounds || (newBounds.width >= 130 && newBounds.height >= 60);
45105 }
45106
45107 if (is$1(shape, 'bpmn:Participant')) {
45108 return !newBounds || (newBounds.width >= 250 && newBounds.height >= 50);
45109 }
45110
45111 if (isTextAnnotation(shape)) {
45112 return true;
45113 }
45114
45115 if (isGroup(shape)) {
45116 return true;
45117 }
45118
45119 return false;
45120 }
45121
45122 /**
45123 * Check, whether one side of the relationship
45124 * is a text annotation.
45125 */
45126 function isOneTextAnnotation(source, target) {
45127
45128 var sourceTextAnnotation = isTextAnnotation(source),
45129 targetTextAnnotation = isTextAnnotation(target);
45130
45131 return (
45132 (sourceTextAnnotation || targetTextAnnotation) &&
45133 (sourceTextAnnotation !== targetTextAnnotation)
45134 );
45135 }
45136
45137
45138 function canConnectAssociation(source, target) {
45139
45140 // do not connect connections
45141 if (isConnection$8(source) || isConnection$8(target)) {
45142 return false;
45143 }
45144
45145 // compensation boundary events are exception
45146 if (isCompensationBoundary(source) && isForCompensation(target)) {
45147 return true;
45148 }
45149
45150 // don't connect parent <-> child
45151 if (isParent(target, source) || isParent(source, target)) {
45152 return false;
45153 }
45154
45155 // allow connection of associations between <!TextAnnotation> and <TextAnnotation>
45156 if (isOneTextAnnotation(source, target)) {
45157 return true;
45158 }
45159
45160 // can connect associations where we can connect
45161 // data associations, too (!)
45162 return !!canConnectDataAssociation(source, target);
45163 }
45164
45165 function canConnectMessageFlow(source, target) {
45166
45167 // during connect user might move mouse out of canvas
45168 // https://github.com/bpmn-io/bpmn-js/issues/1033
45169 if (getRootElement(source) && !getRootElement(target)) {
45170 return false;
45171 }
45172
45173 return (
45174 isMessageFlowSource(source) &&
45175 isMessageFlowTarget(target) &&
45176 !isSameOrganization(source, target)
45177 );
45178 }
45179
45180 function canConnectSequenceFlow(source, target) {
45181
45182 if (
45183 isEventBasedTarget(target) &&
45184 target.incoming.length > 0 &&
45185 areOutgoingEventBasedGatewayConnections(target.incoming) &&
45186 !is$1(source, 'bpmn:EventBasedGateway')
45187 ) {
45188 return false;
45189 }
45190
45191 return isSequenceFlowSource(source) &&
45192 isSequenceFlowTarget(target) &&
45193 isSameScope(source, target) &&
45194 !(is$1(source, 'bpmn:EventBasedGateway') && !isEventBasedTarget(target));
45195 }
45196
45197
45198 function canConnectDataAssociation(source, target) {
45199
45200 if (isAny(source, [ 'bpmn:DataObjectReference', 'bpmn:DataStoreReference' ]) &&
45201 isAny(target, [ 'bpmn:Activity', 'bpmn:ThrowEvent' ])) {
45202 return { type: 'bpmn:DataInputAssociation' };
45203 }
45204
45205 if (isAny(target, [ 'bpmn:DataObjectReference', 'bpmn:DataStoreReference' ]) &&
45206 isAny(source, [ 'bpmn:Activity', 'bpmn:CatchEvent' ])) {
45207 return { type: 'bpmn:DataOutputAssociation' };
45208 }
45209
45210 return false;
45211 }
45212
45213 function canInsert(shape, flow, position) {
45214
45215 if (!flow) {
45216 return false;
45217 }
45218
45219 if (Array.isArray(shape)) {
45220 if (shape.length !== 1) {
45221 return false;
45222 }
45223
45224 shape = shape[0];
45225 }
45226
45227 if (flow.source === shape ||
45228 flow.target === shape) {
45229 return false;
45230 }
45231
45232 // return true if we can drop on the
45233 // underlying flow parent
45234 //
45235 // at this point we are not really able to talk
45236 // about connection rules (yet)
45237
45238 return (
45239 isAny(flow, [ 'bpmn:SequenceFlow', 'bpmn:MessageFlow' ]) &&
45240 !isLabel$6(flow) &&
45241 is$1(shape, 'bpmn:FlowNode') &&
45242 !is$1(shape, 'bpmn:BoundaryEvent') &&
45243 canDrop(shape, flow.parent));
45244 }
45245
45246 function includes$5(elements, element) {
45247 return (elements && element) && elements.indexOf(element) !== -1;
45248 }
45249
45250 function canCopy(elements, element) {
45251 if (isLabel$6(element)) {
45252 return true;
45253 }
45254
45255 if (is$1(element, 'bpmn:Lane') && !includes$5(elements, element.parent)) {
45256 return false;
45257 }
45258
45259 return true;
45260 }
45261
45262 function isOutgoingEventBasedGatewayConnection(connection) {
45263
45264 if (connection && connection.source) {
45265 return is$1(connection.source, 'bpmn:EventBasedGateway');
45266 }
45267 }
45268
45269 function areOutgoingEventBasedGatewayConnections(connections) {
45270 connections = connections || [];
45271
45272 return connections.some(isOutgoingEventBasedGatewayConnection);
45273 }
45274
45275 function getRootElement(element) {
45276 return getParent(element, 'bpmn:Process') || getParent(element, 'bpmn:Collaboration');
45277 }
45278
45279 var RulesModule = {
45280 __depends__: [
45281 RulesModule$1
45282 ],
45283 __init__: [ 'bpmnRules' ],
45284 bpmnRules: [ 'type', BpmnRules ]
45285 };
45286
45287 var HIGH_PRIORITY$6 = 2000;
45288
45289 function BpmnDiOrdering(eventBus, canvas) {
45290
45291 eventBus.on('saveXML.start', HIGH_PRIORITY$6, orderDi);
45292
45293 function orderDi() {
45294 var root = canvas.getRootElement(),
45295 rootDi = getBusinessObject(root).di,
45296 elements,
45297 diElements;
45298
45299 elements = selfAndAllChildren([ root ], false);
45300
45301 // only bpmndi:Shape and bpmndi:Edge can be direct children of bpmndi:Plane
45302 elements = filter(elements, function(element) {
45303 return element !== root && !element.labelTarget;
45304 });
45305
45306 diElements = map$1(elements, getDi);
45307
45308 rootDi.set('planeElement', diElements);
45309 }
45310 }
45311
45312 BpmnDiOrdering.$inject = [ 'eventBus', 'canvas' ];
45313
45314 var DiOrderingModule = {
45315 __init__: [
45316 'bpmnDiOrdering'
45317 ],
45318 bpmnDiOrdering: [ 'type', BpmnDiOrdering ]
45319 };
45320
45321 /**
45322 * An abstract provider that allows modelers to implement a custom
45323 * ordering of diagram elements on the canvas.
45324 *
45325 * It makes sure that the order is always preserved during element
45326 * creation and move operations.
45327 *
45328 * In order to use this behavior, inherit from it and override
45329 * the method {@link OrderingProvider#getOrdering}.
45330 *
45331 * @example
45332 *
45333 * ```javascript
45334 * function CustomOrderingProvider(eventBus) {
45335 * OrderingProvider.call(this, eventBus);
45336 *
45337 * this.getOrdering = function(element, newParent) {
45338 * // always insert elements at the front
45339 * // when moving
45340 * return {
45341 * index: 0,
45342 * parent: newParent
45343 * };
45344 * };
45345 * }
45346 * ```
45347 *
45348 * @param {EventBus} eventBus
45349 */
45350 function OrderingProvider(eventBus) {
45351
45352 CommandInterceptor.call(this, eventBus);
45353
45354
45355 var self = this;
45356
45357 this.preExecute([ 'shape.create', 'connection.create' ], function(event) {
45358
45359 var context = event.context,
45360 element = context.shape || context.connection,
45361 parent = context.parent;
45362
45363 var ordering = self.getOrdering(element, parent);
45364
45365 if (ordering) {
45366
45367 if (ordering.parent !== undefined) {
45368 context.parent = ordering.parent;
45369 }
45370
45371 context.parentIndex = ordering.index;
45372 }
45373 });
45374
45375 this.preExecute([ 'shape.move', 'connection.move' ], function(event) {
45376
45377 var context = event.context,
45378 element = context.shape || context.connection,
45379 parent = context.newParent || element.parent;
45380
45381 var ordering = self.getOrdering(element, parent);
45382
45383 if (ordering) {
45384
45385 if (ordering.parent !== undefined) {
45386 context.newParent = ordering.parent;
45387 }
45388
45389 context.newParentIndex = ordering.index;
45390 }
45391 });
45392 }
45393
45394 /**
45395 * Return a custom ordering of the element, both in terms
45396 * of parent element and index in the new parent.
45397 *
45398 * Implementors of this method must return an object with
45399 * `parent` _and_ `index` in it.
45400 *
45401 * @param {djs.model.Base} element
45402 * @param {djs.model.Shape} newParent
45403 *
45404 * @return {Object} ordering descriptor
45405 */
45406 OrderingProvider.prototype.getOrdering = function(element, newParent) {
45407 return null;
45408 };
45409
45410 inherits$1(OrderingProvider, CommandInterceptor);
45411
45412 /**
45413 * a simple ordering provider that makes sure:
45414 *
45415 * (0) labels and groups are rendered always on top
45416 * (1) elements are ordered by a {level} property
45417 */
45418 function BpmnOrderingProvider(eventBus, canvas, translate) {
45419
45420 OrderingProvider.call(this, eventBus);
45421
45422 var orders = [
45423 { type: 'bpmn:SubProcess', order: { level: 6 } },
45424 {
45425 type: 'bpmn:SequenceFlow',
45426 order: {
45427 level: 3,
45428 containers: [
45429 'bpmn:Participant',
45430 'bpmn:FlowElementsContainer'
45431 ]
45432 }
45433 },
45434
45435 // handle DataAssociation(s) like message flows and render them always on top
45436 {
45437 type: 'bpmn:DataAssociation',
45438 order: {
45439 level: 9,
45440 containers: [
45441 'bpmn:Collaboration',
45442 'bpmn:Process'
45443 ]
45444 }
45445 },
45446 {
45447 type: 'bpmn:MessageFlow', order: {
45448 level: 9,
45449 containers: [ 'bpmn:Collaboration' ]
45450 }
45451 },
45452 {
45453 type: 'bpmn:Association',
45454 order: {
45455 level: 6,
45456 containers: [
45457 'bpmn:Participant',
45458 'bpmn:FlowElementsContainer',
45459 'bpmn:Collaboration'
45460 ]
45461 }
45462 },
45463 { type: 'bpmn:BoundaryEvent', order: { level: 8 } },
45464 {
45465 type: 'bpmn:Group',
45466 order: {
45467 level: 10,
45468 containers: [
45469 'bpmn:Collaboration',
45470 'bpmn:Process'
45471 ]
45472 }
45473 },
45474 { type: 'bpmn:FlowElement', order: { level: 5 } },
45475 { type: 'bpmn:Participant', order: { level: -2 } },
45476 { type: 'bpmn:Lane', order: { level: -1 } }
45477 ];
45478
45479 function computeOrder(element) {
45480 if (element.labelTarget) {
45481 return { level: 10 };
45482 }
45483
45484 var entry = find(orders, function(o) {
45485 return isAny(element, [ o.type ]);
45486 });
45487
45488 return entry && entry.order || { level: 1 };
45489 }
45490
45491 function getOrder(element) {
45492
45493 var order = element.order;
45494
45495 if (!order) {
45496 element.order = order = computeOrder(element);
45497 }
45498
45499 if (!order) {
45500 throw new Error('no order for <' + element.id + '>');
45501 }
45502
45503 return order;
45504 }
45505
45506 function findActualParent(element, newParent, containers) {
45507
45508 var actualParent = newParent;
45509
45510 while (actualParent) {
45511
45512 if (isAny(actualParent, containers)) {
45513 break;
45514 }
45515
45516 actualParent = actualParent.parent;
45517 }
45518
45519 if (!actualParent) {
45520 throw new Error('no parent for <' + element.id + '> in <' + (newParent && newParent.id) + '>');
45521 }
45522
45523 return actualParent;
45524 }
45525
45526 this.getOrdering = function(element, newParent) {
45527
45528 // render labels always on top
45529 if (element.labelTarget) {
45530 return {
45531 parent: canvas.getRootElement(),
45532 index: -1
45533 };
45534 }
45535
45536 var elementOrder = getOrder(element);
45537
45538 if (elementOrder.containers) {
45539 newParent = findActualParent(element, newParent, elementOrder.containers);
45540 }
45541
45542 var currentIndex = newParent.children.indexOf(element);
45543
45544 var insertIndex = findIndex(newParent.children, function(child) {
45545
45546 // do not compare with labels, they are created
45547 // in the wrong order (right after elements) during import and
45548 // mess up the positioning.
45549 if (!element.labelTarget && child.labelTarget) {
45550 return false;
45551 }
45552
45553 return elementOrder.level < getOrder(child).level;
45554 });
45555
45556
45557 // if the element is already in the child list at
45558 // a smaller index, we need to adjust the insert index.
45559 // this takes into account that the element is being removed
45560 // before being re-inserted
45561 if (insertIndex !== -1) {
45562 if (currentIndex !== -1 && currentIndex < insertIndex) {
45563 insertIndex -= 1;
45564 }
45565 }
45566
45567 return {
45568 index: insertIndex,
45569 parent: newParent
45570 };
45571 };
45572 }
45573
45574 BpmnOrderingProvider.$inject = [ 'eventBus', 'canvas', 'translate' ];
45575
45576 inherits$1(BpmnOrderingProvider, OrderingProvider);
45577
45578 var OrderingModule = {
45579 __depends__: [
45580 translate
45581 ],
45582 __init__: [ 'bpmnOrderingProvider' ],
45583 bpmnOrderingProvider: [ 'type', BpmnOrderingProvider ]
45584 };
45585
45586 /**
45587 * A service that offers un- and redoable execution of commands.
45588 *
45589 * The command stack is responsible for executing modeling actions
45590 * in a un- and redoable manner. To do this it delegates the actual
45591 * command execution to {@link CommandHandler}s.
45592 *
45593 * Command handlers provide {@link CommandHandler#execute(ctx)} and
45594 * {@link CommandHandler#revert(ctx)} methods to un- and redo a command
45595 * identified by a command context.
45596 *
45597 *
45598 * ## Life-Cycle events
45599 *
45600 * In the process the command stack fires a number of life-cycle events
45601 * that other components to participate in the command execution.
45602 *
45603 * * preExecute
45604 * * preExecuted
45605 * * execute
45606 * * executed
45607 * * postExecute
45608 * * postExecuted
45609 * * revert
45610 * * reverted
45611 *
45612 * A special event is used for validating, whether a command can be
45613 * performed prior to its execution.
45614 *
45615 * * canExecute
45616 *
45617 * Each of the events is fired as `commandStack.{eventName}` and
45618 * `commandStack.{commandName}.{eventName}`, respectively. This gives
45619 * components fine grained control on where to hook into.
45620 *
45621 * The event object fired transports `command`, the name of the
45622 * command and `context`, the command context.
45623 *
45624 *
45625 * ## Creating Command Handlers
45626 *
45627 * Command handlers should provide the {@link CommandHandler#execute(ctx)}
45628 * and {@link CommandHandler#revert(ctx)} methods to implement
45629 * redoing and undoing of a command.
45630 *
45631 * A command handler _must_ ensure undo is performed properly in order
45632 * not to break the undo chain. It must also return the shapes that
45633 * got changed during the `execute` and `revert` operations.
45634 *
45635 * Command handlers may execute other modeling operations (and thus
45636 * commands) in their `preExecute` and `postExecute` phases. The command
45637 * stack will properly group all commands together into a logical unit
45638 * that may be re- and undone atomically.
45639 *
45640 * Command handlers must not execute other commands from within their
45641 * core implementation (`execute`, `revert`).
45642 *
45643 *
45644 * ## Change Tracking
45645 *
45646 * During the execution of the CommandStack it will keep track of all
45647 * elements that have been touched during the command's execution.
45648 *
45649 * At the end of the CommandStack execution it will notify interested
45650 * components via an 'elements.changed' event with all the dirty
45651 * elements.
45652 *
45653 * The event can be picked up by components that are interested in the fact
45654 * that elements have been changed. One use case for this is updating
45655 * their graphical representation after moving / resizing or deletion.
45656 *
45657 * @see CommandHandler
45658 *
45659 * @param {EventBus} eventBus
45660 * @param {Injector} injector
45661 */
45662 function CommandStack(eventBus, injector) {
45663
45664 /**
45665 * A map of all registered command handlers.
45666 *
45667 * @type {Object}
45668 */
45669 this._handlerMap = {};
45670
45671 /**
45672 * A stack containing all re/undoable actions on the diagram
45673 *
45674 * @type {Array<Object>}
45675 */
45676 this._stack = [];
45677
45678 /**
45679 * The current index on the stack
45680 *
45681 * @type {number}
45682 */
45683 this._stackIdx = -1;
45684
45685 /**
45686 * Current active commandStack execution
45687 *
45688 * @type {Object}
45689 * @property {Object[]} actions
45690 * @property {Object[]} dirty
45691 * @property { 'undo' | 'redo' | 'clear' | 'execute' | null } trigger the cause of the current excecution
45692 */
45693 this._currentExecution = {
45694 actions: [],
45695 dirty: [],
45696 trigger: null
45697 };
45698
45699
45700 this._injector = injector;
45701 this._eventBus = eventBus;
45702
45703 this._uid = 1;
45704
45705 eventBus.on([
45706 'diagram.destroy',
45707 'diagram.clear'
45708 ], function() {
45709 this.clear(false);
45710 }, this);
45711 }
45712
45713 CommandStack.$inject = [ 'eventBus', 'injector' ];
45714
45715
45716 /**
45717 * Execute a command
45718 *
45719 * @param {string} command the command to execute
45720 * @param {Object} context the environment to execute the command in
45721 */
45722 CommandStack.prototype.execute = function(command, context) {
45723 if (!command) {
45724 throw new Error('command required');
45725 }
45726
45727 this._currentExecution.trigger = 'execute';
45728
45729 var action = { command: command, context: context };
45730
45731 this._pushAction(action);
45732 this._internalExecute(action);
45733 this._popAction(action);
45734 };
45735
45736
45737 /**
45738 * Ask whether a given command can be executed.
45739 *
45740 * Implementors may hook into the mechanism on two ways:
45741 *
45742 * * in event listeners:
45743 *
45744 * Users may prevent the execution via an event listener.
45745 * It must prevent the default action for `commandStack.(<command>.)canExecute` events.
45746 *
45747 * * in command handlers:
45748 *
45749 * If the method {@link CommandHandler#canExecute} is implemented in a handler
45750 * it will be called to figure out whether the execution is allowed.
45751 *
45752 * @param {string} command the command to execute
45753 * @param {Object} context the environment to execute the command in
45754 *
45755 * @return {boolean} true if the command can be executed
45756 */
45757 CommandStack.prototype.canExecute = function(command, context) {
45758
45759 var action = { command: command, context: context };
45760
45761 var handler = this._getHandler(command);
45762
45763 var result = this._fire(command, 'canExecute', action);
45764
45765 // handler#canExecute will only be called if no listener
45766 // decided on a result already
45767 if (result === undefined) {
45768 if (!handler) {
45769 return false;
45770 }
45771
45772 if (handler.canExecute) {
45773 result = handler.canExecute(context);
45774 }
45775 }
45776
45777 return result;
45778 };
45779
45780
45781 /**
45782 * Clear the command stack, erasing all undo / redo history
45783 */
45784 CommandStack.prototype.clear = function(emit) {
45785 this._stack.length = 0;
45786 this._stackIdx = -1;
45787
45788 if (emit !== false) {
45789 this._fire('changed', { trigger: 'clear' });
45790 }
45791 };
45792
45793
45794 /**
45795 * Undo last command(s)
45796 */
45797 CommandStack.prototype.undo = function() {
45798 var action = this._getUndoAction(),
45799 next;
45800
45801 if (action) {
45802 this._currentExecution.trigger = 'undo';
45803
45804 this._pushAction(action);
45805
45806 while (action) {
45807 this._internalUndo(action);
45808 next = this._getUndoAction();
45809
45810 if (!next || next.id !== action.id) {
45811 break;
45812 }
45813
45814 action = next;
45815 }
45816
45817 this._popAction();
45818 }
45819 };
45820
45821
45822 /**
45823 * Redo last command(s)
45824 */
45825 CommandStack.prototype.redo = function() {
45826 var action = this._getRedoAction(),
45827 next;
45828
45829 if (action) {
45830 this._currentExecution.trigger = 'redo';
45831
45832 this._pushAction(action);
45833
45834 while (action) {
45835 this._internalExecute(action, true);
45836 next = this._getRedoAction();
45837
45838 if (!next || next.id !== action.id) {
45839 break;
45840 }
45841
45842 action = next;
45843 }
45844
45845 this._popAction();
45846 }
45847 };
45848
45849
45850 /**
45851 * Register a handler instance with the command stack
45852 *
45853 * @param {string} command
45854 * @param {CommandHandler} handler
45855 */
45856 CommandStack.prototype.register = function(command, handler) {
45857 this._setHandler(command, handler);
45858 };
45859
45860
45861 /**
45862 * Register a handler type with the command stack
45863 * by instantiating it and injecting its dependencies.
45864 *
45865 * @param {string} command
45866 * @param {Function} a constructor for a {@link CommandHandler}
45867 */
45868 CommandStack.prototype.registerHandler = function(command, handlerCls) {
45869
45870 if (!command || !handlerCls) {
45871 throw new Error('command and handlerCls must be defined');
45872 }
45873
45874 var handler = this._injector.instantiate(handlerCls);
45875 this.register(command, handler);
45876 };
45877
45878 CommandStack.prototype.canUndo = function() {
45879 return !!this._getUndoAction();
45880 };
45881
45882 CommandStack.prototype.canRedo = function() {
45883 return !!this._getRedoAction();
45884 };
45885
45886 // stack access //////////////////////
45887
45888 CommandStack.prototype._getRedoAction = function() {
45889 return this._stack[this._stackIdx + 1];
45890 };
45891
45892
45893 CommandStack.prototype._getUndoAction = function() {
45894 return this._stack[this._stackIdx];
45895 };
45896
45897
45898 // internal functionality //////////////////////
45899
45900 CommandStack.prototype._internalUndo = function(action) {
45901 var self = this;
45902
45903 var command = action.command,
45904 context = action.context;
45905
45906 var handler = this._getHandler(command);
45907
45908 // guard against illegal nested command stack invocations
45909 this._atomicDo(function() {
45910 self._fire(command, 'revert', action);
45911
45912 if (handler.revert) {
45913 self._markDirty(handler.revert(context));
45914 }
45915
45916 self._revertedAction(action);
45917
45918 self._fire(command, 'reverted', action);
45919 });
45920 };
45921
45922
45923 CommandStack.prototype._fire = function(command, qualifier, event) {
45924 if (arguments.length < 3) {
45925 event = qualifier;
45926 qualifier = null;
45927 }
45928
45929 var names = qualifier ? [ command + '.' + qualifier, qualifier ] : [ command ],
45930 i, name, result;
45931
45932 event = this._eventBus.createEvent(event);
45933
45934 for (i = 0; (name = names[i]); i++) {
45935 result = this._eventBus.fire('commandStack.' + name, event);
45936
45937 if (event.cancelBubble) {
45938 break;
45939 }
45940 }
45941
45942 return result;
45943 };
45944
45945 CommandStack.prototype._createId = function() {
45946 return this._uid++;
45947 };
45948
45949 CommandStack.prototype._atomicDo = function(fn) {
45950
45951 var execution = this._currentExecution;
45952
45953 execution.atomic = true;
45954
45955 try {
45956 fn();
45957 } finally {
45958 execution.atomic = false;
45959 }
45960 };
45961
45962 CommandStack.prototype._internalExecute = function(action, redo) {
45963 var self = this;
45964
45965 var command = action.command,
45966 context = action.context;
45967
45968 var handler = this._getHandler(command);
45969
45970 if (!handler) {
45971 throw new Error('no command handler registered for <' + command + '>');
45972 }
45973
45974 this._pushAction(action);
45975
45976 if (!redo) {
45977 this._fire(command, 'preExecute', action);
45978
45979 if (handler.preExecute) {
45980 handler.preExecute(context);
45981 }
45982
45983 this._fire(command, 'preExecuted', action);
45984 }
45985
45986 // guard against illegal nested command stack invocations
45987 this._atomicDo(function() {
45988
45989 self._fire(command, 'execute', action);
45990
45991 if (handler.execute) {
45992
45993 // actual execute + mark return results as dirty
45994 self._markDirty(handler.execute(context));
45995 }
45996
45997 // log to stack
45998 self._executedAction(action, redo);
45999
46000 self._fire(command, 'executed', action);
46001 });
46002
46003 if (!redo) {
46004 this._fire(command, 'postExecute', action);
46005
46006 if (handler.postExecute) {
46007 handler.postExecute(context);
46008 }
46009
46010 this._fire(command, 'postExecuted', action);
46011 }
46012
46013 this._popAction(action);
46014 };
46015
46016
46017 CommandStack.prototype._pushAction = function(action) {
46018
46019 var execution = this._currentExecution,
46020 actions = execution.actions;
46021
46022 var baseAction = actions[0];
46023
46024 if (execution.atomic) {
46025 throw new Error('illegal invocation in <execute> or <revert> phase (action: ' + action.command + ')');
46026 }
46027
46028 if (!action.id) {
46029 action.id = (baseAction && baseAction.id) || this._createId();
46030 }
46031
46032 actions.push(action);
46033 };
46034
46035
46036 CommandStack.prototype._popAction = function() {
46037 var execution = this._currentExecution,
46038 trigger = execution.trigger,
46039 actions = execution.actions,
46040 dirty = execution.dirty;
46041
46042 actions.pop();
46043
46044 if (!actions.length) {
46045 this._eventBus.fire('elements.changed', { elements: uniqueBy('id', dirty.reverse()) });
46046
46047 dirty.length = 0;
46048
46049 this._fire('changed', { trigger: trigger });
46050
46051 execution.trigger = null;
46052 }
46053 };
46054
46055
46056 CommandStack.prototype._markDirty = function(elements) {
46057 var execution = this._currentExecution;
46058
46059 if (!elements) {
46060 return;
46061 }
46062
46063 elements = isArray$2(elements) ? elements : [ elements ];
46064
46065 execution.dirty = execution.dirty.concat(elements);
46066 };
46067
46068
46069 CommandStack.prototype._executedAction = function(action, redo) {
46070 var stackIdx = ++this._stackIdx;
46071
46072 if (!redo) {
46073 this._stack.splice(stackIdx, this._stack.length, action);
46074 }
46075 };
46076
46077
46078 CommandStack.prototype._revertedAction = function(action) {
46079 this._stackIdx--;
46080 };
46081
46082
46083 CommandStack.prototype._getHandler = function(command) {
46084 return this._handlerMap[command];
46085 };
46086
46087 CommandStack.prototype._setHandler = function(command, handler) {
46088 if (!command || !handler) {
46089 throw new Error('command and handler required');
46090 }
46091
46092 if (this._handlerMap[command]) {
46093 throw new Error('overriding handler for command <' + command + '>');
46094 }
46095
46096 this._handlerMap[command] = handler;
46097 };
46098
46099 var CommandModule = {
46100 commandStack: [ 'type', CommandStack ]
46101 };
46102
46103 // document wide unique tooltip ids
46104 var ids = new IdGenerator('tt');
46105
46106
46107 function createRoot(parentNode) {
46108 var root = domify(
46109 '<div class="djs-tooltip-container" style="position: absolute; width: 0; height: 0;" />'
46110 );
46111
46112 parentNode.insertBefore(root, parentNode.firstChild);
46113
46114 return root;
46115 }
46116
46117
46118 function setPosition(el, x, y) {
46119 assign(el.style, { left: x + 'px', top: y + 'px' });
46120 }
46121
46122 function setVisible(el, visible) {
46123 el.style.display = visible === false ? 'none' : '';
46124 }
46125
46126
46127 var tooltipClass = 'djs-tooltip',
46128 tooltipSelector = '.' + tooltipClass;
46129
46130 /**
46131 * A service that allows users to render tool tips on the diagram.
46132 *
46133 * The tooltip service will take care of updating the tooltip positioning
46134 * during navigation + zooming.
46135 *
46136 * @example
46137 *
46138 * ```javascript
46139 *
46140 * // add a pink badge on the top left of the shape
46141 * tooltips.add({
46142 * position: {
46143 * x: 50,
46144 * y: 100
46145 * },
46146 * html: '<div style="width: 10px; background: fuchsia; color: white;">0</div>'
46147 * });
46148 *
46149 * // or with optional life span
46150 * tooltips.add({
46151 * position: {
46152 * top: -5,
46153 * left: -5
46154 * },
46155 * html: '<div style="width: 10px; background: fuchsia; color: white;">0</div>',
46156 * ttl: 2000
46157 * });
46158 *
46159 * // remove a tool tip
46160 * var id = tooltips.add(...);
46161 * tooltips.remove(id);
46162 * ```
46163 *
46164 * @param {EventBus} eventBus
46165 * @param {Canvas} canvas
46166 */
46167 function Tooltips(eventBus, canvas) {
46168
46169 this._eventBus = eventBus;
46170 this._canvas = canvas;
46171
46172 this._ids = ids;
46173
46174 this._tooltipDefaults = {
46175 show: {
46176 minZoom: 0.7,
46177 maxZoom: 5.0
46178 }
46179 };
46180
46181 /**
46182 * Mapping tooltipId -> tooltip
46183 */
46184 this._tooltips = {};
46185
46186 // root html element for all tooltips
46187 this._tooltipRoot = createRoot(canvas.getContainer());
46188
46189
46190 var self = this;
46191
46192 delegate.bind(this._tooltipRoot, tooltipSelector, 'mousedown', function(event) {
46193 event.stopPropagation();
46194 });
46195
46196 delegate.bind(this._tooltipRoot, tooltipSelector, 'mouseover', function(event) {
46197 self.trigger('mouseover', event);
46198 });
46199
46200 delegate.bind(this._tooltipRoot, tooltipSelector, 'mouseout', function(event) {
46201 self.trigger('mouseout', event);
46202 });
46203
46204 this._init();
46205 }
46206
46207
46208 Tooltips.$inject = [ 'eventBus', 'canvas' ];
46209
46210
46211 /**
46212 * Adds a HTML tooltip to the diagram
46213 *
46214 * @param {Object} tooltip the tooltip configuration
46215 *
46216 * @param {string|DOMElement} tooltip.html html element to use as an tooltip
46217 * @param {Object} [tooltip.show] show configuration
46218 * @param {number} [tooltip.show.minZoom] minimal zoom level to show the tooltip
46219 * @param {number} [tooltip.show.maxZoom] maximum zoom level to show the tooltip
46220 * @param {Object} tooltip.position where to attach the tooltip
46221 * @param {number} [tooltip.position.left] relative to element bbox left attachment
46222 * @param {number} [tooltip.position.top] relative to element bbox top attachment
46223 * @param {number} [tooltip.position.bottom] relative to element bbox bottom attachment
46224 * @param {number} [tooltip.position.right] relative to element bbox right attachment
46225 * @param {number} [tooltip.timeout=-1]
46226 *
46227 * @return {string} id that may be used to reference the tooltip for update or removal
46228 */
46229 Tooltips.prototype.add = function(tooltip) {
46230
46231 if (!tooltip.position) {
46232 throw new Error('must specifiy tooltip position');
46233 }
46234
46235 if (!tooltip.html) {
46236 throw new Error('must specifiy tooltip html');
46237 }
46238
46239 var id = this._ids.next();
46240
46241 tooltip = assign({}, this._tooltipDefaults, tooltip, {
46242 id: id
46243 });
46244
46245 this._addTooltip(tooltip);
46246
46247 if (tooltip.timeout) {
46248 this.setTimeout(tooltip);
46249 }
46250
46251 return id;
46252 };
46253
46254 Tooltips.prototype.trigger = function(action, event) {
46255
46256 var node = event.delegateTarget || event.target;
46257
46258 var tooltip = this.get(attr$1(node, 'data-tooltip-id'));
46259
46260 if (!tooltip) {
46261 return;
46262 }
46263
46264 if (action === 'mouseover' && tooltip.timeout) {
46265 this.clearTimeout(tooltip);
46266 }
46267
46268 if (action === 'mouseout' && tooltip.timeout) {
46269
46270 // cut timeout after mouse out
46271 tooltip.timeout = 1000;
46272
46273 this.setTimeout(tooltip);
46274 }
46275 };
46276
46277 /**
46278 * Get a tooltip with the given id
46279 *
46280 * @param {string} id
46281 */
46282 Tooltips.prototype.get = function(id) {
46283
46284 if (typeof id !== 'string') {
46285 id = id.id;
46286 }
46287
46288 return this._tooltips[id];
46289 };
46290
46291 Tooltips.prototype.clearTimeout = function(tooltip) {
46292
46293 tooltip = this.get(tooltip);
46294
46295 if (!tooltip) {
46296 return;
46297 }
46298
46299 var removeTimer = tooltip.removeTimer;
46300
46301 if (removeTimer) {
46302 clearTimeout(removeTimer);
46303 tooltip.removeTimer = null;
46304 }
46305 };
46306
46307 Tooltips.prototype.setTimeout = function(tooltip) {
46308
46309 tooltip = this.get(tooltip);
46310
46311 if (!tooltip) {
46312 return;
46313 }
46314
46315 this.clearTimeout(tooltip);
46316
46317 var self = this;
46318
46319 tooltip.removeTimer = setTimeout(function() {
46320 self.remove(tooltip);
46321 }, tooltip.timeout);
46322 };
46323
46324 /**
46325 * Remove an tooltip with the given id
46326 *
46327 * @param {string} id
46328 */
46329 Tooltips.prototype.remove = function(id) {
46330
46331 var tooltip = this.get(id);
46332
46333 if (tooltip) {
46334 remove$2(tooltip.html);
46335 remove$2(tooltip.htmlContainer);
46336
46337 delete tooltip.htmlContainer;
46338
46339 delete this._tooltips[tooltip.id];
46340 }
46341 };
46342
46343
46344 Tooltips.prototype.show = function() {
46345 setVisible(this._tooltipRoot);
46346 };
46347
46348
46349 Tooltips.prototype.hide = function() {
46350 setVisible(this._tooltipRoot, false);
46351 };
46352
46353
46354 Tooltips.prototype._updateRoot = function(viewbox) {
46355 var a = viewbox.scale || 1;
46356 var d = viewbox.scale || 1;
46357
46358 var matrix = 'matrix(' + a + ',0,0,' + d + ',' + (-1 * viewbox.x * a) + ',' + (-1 * viewbox.y * d) + ')';
46359
46360 this._tooltipRoot.style.transform = matrix;
46361 this._tooltipRoot.style['-ms-transform'] = matrix;
46362 };
46363
46364
46365 Tooltips.prototype._addTooltip = function(tooltip) {
46366
46367 var id = tooltip.id,
46368 html = tooltip.html,
46369 htmlContainer,
46370 tooltipRoot = this._tooltipRoot;
46371
46372 // unwrap jquery (for those who need it)
46373 if (html.get && html.constructor.prototype.jquery) {
46374 html = html.get(0);
46375 }
46376
46377 // create proper html elements from
46378 // tooltip HTML strings
46379 if (isString(html)) {
46380 html = domify(html);
46381 }
46382
46383 htmlContainer = domify('<div data-tooltip-id="' + id + '" class="' + tooltipClass + '" style="position: absolute">');
46384
46385 htmlContainer.appendChild(html);
46386
46387 if (tooltip.type) {
46388 classes$1(htmlContainer).add('djs-tooltip-' + tooltip.type);
46389 }
46390
46391 if (tooltip.className) {
46392 classes$1(htmlContainer).add(tooltip.className);
46393 }
46394
46395 tooltip.htmlContainer = htmlContainer;
46396
46397 tooltipRoot.appendChild(htmlContainer);
46398
46399 this._tooltips[id] = tooltip;
46400
46401 this._updateTooltip(tooltip);
46402 };
46403
46404
46405 Tooltips.prototype._updateTooltip = function(tooltip) {
46406
46407 var position = tooltip.position,
46408 htmlContainer = tooltip.htmlContainer;
46409
46410 // update overlay html based on tooltip x, y
46411
46412 setPosition(htmlContainer, position.x, position.y);
46413 };
46414
46415
46416 Tooltips.prototype._updateTooltipVisibilty = function(viewbox) {
46417
46418 forEach(this._tooltips, function(tooltip) {
46419 var show = tooltip.show,
46420 htmlContainer = tooltip.htmlContainer,
46421 visible = true;
46422
46423 if (show) {
46424 if (show.minZoom > viewbox.scale ||
46425 show.maxZoom < viewbox.scale) {
46426 visible = false;
46427 }
46428
46429 setVisible(htmlContainer, visible);
46430 }
46431 });
46432 };
46433
46434 Tooltips.prototype._init = function() {
46435
46436 var self = this;
46437
46438 // scroll/zoom integration
46439
46440 function updateViewbox(viewbox) {
46441 self._updateRoot(viewbox);
46442 self._updateTooltipVisibilty(viewbox);
46443
46444 self.show();
46445 }
46446
46447 this._eventBus.on('canvas.viewbox.changing', function(event) {
46448 self.hide();
46449 });
46450
46451 this._eventBus.on('canvas.viewbox.changed', function(event) {
46452 updateViewbox(event.viewbox);
46453 });
46454 };
46455
46456 var TooltipsModule = {
46457 __init__: [ 'tooltips' ],
46458 tooltips: [ 'type', Tooltips ]
46459 };
46460
46461 /**
46462 * Remove from the beginning of a collection until it is empty.
46463 *
46464 * This is a null-safe operation that ensures elements
46465 * are being removed from the given collection until the
46466 * collection is empty.
46467 *
46468 * The implementation deals with the fact that a remove operation
46469 * may touch, i.e. remove multiple elements in the collection
46470 * at a time.
46471 *
46472 * @param {Array<Object>} [collection]
46473 * @param {Function} removeFn
46474 *
46475 * @return {Array<Object>} the cleared collection
46476 */
46477 function saveClear(collection, removeFn) {
46478
46479 if (typeof removeFn !== 'function') {
46480 throw new Error('removeFn iterator must be a function');
46481 }
46482
46483 if (!collection) {
46484 return;
46485 }
46486
46487 var e;
46488
46489 while ((e = collection[0])) {
46490 removeFn(e);
46491 }
46492
46493 return collection;
46494 }
46495
46496 var LOW_PRIORITY$6 = 250,
46497 HIGH_PRIORITY$5 = 1400;
46498
46499
46500 /**
46501 * A handler that makes sure labels are properly moved with
46502 * their label targets.
46503 *
46504 * @param {didi.Injector} injector
46505 * @param {EventBus} eventBus
46506 * @param {Modeling} modeling
46507 */
46508 function LabelSupport(injector, eventBus, modeling) {
46509
46510 CommandInterceptor.call(this, eventBus);
46511
46512 var movePreview = injector.get('movePreview', false);
46513
46514 // remove labels from the collection that are being
46515 // moved with other elements anyway
46516 eventBus.on('shape.move.start', HIGH_PRIORITY$5, function(e) {
46517
46518 var context = e.context,
46519 shapes = context.shapes,
46520 validatedShapes = context.validatedShapes;
46521
46522 context.shapes = removeLabels(shapes);
46523 context.validatedShapes = removeLabels(validatedShapes);
46524 });
46525
46526 // add labels to visual's group
46527 movePreview && eventBus.on('shape.move.start', LOW_PRIORITY$6, function(e) {
46528
46529 var context = e.context,
46530 shapes = context.shapes;
46531
46532 var labels = [];
46533
46534 forEach(shapes, function(element) {
46535
46536 forEach(element.labels, function(label) {
46537
46538 if (!label.hidden && context.shapes.indexOf(label) === -1) {
46539 labels.push(label);
46540 }
46541
46542 if (element.labelTarget) {
46543 labels.push(element);
46544 }
46545 });
46546 });
46547
46548 forEach(labels, function(label) {
46549 movePreview.makeDraggable(context, label, true);
46550 });
46551
46552 });
46553
46554 // add all labels to move closure
46555 this.preExecuted('elements.move', HIGH_PRIORITY$5, function(e) {
46556 var context = e.context,
46557 closure = context.closure,
46558 enclosedElements = closure.enclosedElements;
46559
46560 var enclosedLabels = [];
46561
46562 // find labels that are not part of
46563 // move closure yet and add them
46564 forEach(enclosedElements, function(element) {
46565 forEach(element.labels, function(label) {
46566
46567 if (!enclosedElements[label.id]) {
46568 enclosedLabels.push(label);
46569 }
46570 });
46571 });
46572
46573 closure.addAll(enclosedLabels);
46574 });
46575
46576
46577 this.preExecute([
46578 'connection.delete',
46579 'shape.delete'
46580 ], function(e) {
46581
46582 var context = e.context,
46583 element = context.connection || context.shape;
46584
46585 saveClear(element.labels, function(label) {
46586 modeling.removeShape(label, { nested: true });
46587 });
46588 });
46589
46590
46591 this.execute('shape.delete', function(e) {
46592
46593 var context = e.context,
46594 shape = context.shape,
46595 labelTarget = shape.labelTarget;
46596
46597 // unset labelTarget
46598 if (labelTarget) {
46599 context.labelTargetIndex = indexOf(labelTarget.labels, shape);
46600 context.labelTarget = labelTarget;
46601
46602 shape.labelTarget = null;
46603 }
46604 });
46605
46606 this.revert('shape.delete', function(e) {
46607
46608 var context = e.context,
46609 shape = context.shape,
46610 labelTarget = context.labelTarget,
46611 labelTargetIndex = context.labelTargetIndex;
46612
46613 // restore labelTarget
46614 if (labelTarget) {
46615 add(labelTarget.labels, shape, labelTargetIndex);
46616
46617 shape.labelTarget = labelTarget;
46618 }
46619 });
46620
46621 }
46622
46623 inherits$1(LabelSupport, CommandInterceptor);
46624
46625 LabelSupport.$inject = [
46626 'injector',
46627 'eventBus',
46628 'modeling'
46629 ];
46630
46631
46632 /**
46633 * Return a filtered list of elements that do not
46634 * contain attached elements with hosts being part
46635 * of the selection.
46636 *
46637 * @param {Array<djs.model.Base>} elements
46638 *
46639 * @return {Array<djs.model.Base>} filtered
46640 */
46641 function removeLabels(elements) {
46642
46643 return filter(elements, function(element) {
46644
46645 // filter out labels that are move together
46646 // with their label targets
46647 return elements.indexOf(element.labelTarget) === -1;
46648 });
46649 }
46650
46651 var LabelSupportModule = {
46652 __init__: [ 'labelSupport'],
46653 labelSupport: [ 'type', LabelSupport ]
46654 };
46655
46656 var LOW_PRIORITY$5 = 251,
46657 HIGH_PRIORITY$4 = 1401;
46658
46659 var MARKER_ATTACH$1 = 'attach-ok';
46660
46661
46662 /**
46663 * Adds the notion of attached elements to the modeler.
46664 *
46665 * Optionally depends on `diagram-js/lib/features/move` to render
46666 * the attached elements during move preview.
46667 *
46668 * Optionally depends on `diagram-js/lib/features/label-support`
46669 * to render attached labels during move preview.
46670 *
46671 * @param {didi.Injector} injector
46672 * @param {EventBus} eventBus
46673 * @param {Canvas} canvas
46674 * @param {Rules} rules
46675 * @param {Modeling} modeling
46676 */
46677 function AttachSupport(injector, eventBus, canvas, rules, modeling) {
46678
46679 CommandInterceptor.call(this, eventBus);
46680
46681 var movePreview = injector.get('movePreview', false);
46682
46683
46684 // remove all the attached elements from the shapes to be validated
46685 // add all the attached shapes to the overall list of moved shapes
46686 eventBus.on('shape.move.start', HIGH_PRIORITY$4, function(e) {
46687
46688 var context = e.context,
46689 shapes = context.shapes,
46690 validatedShapes = context.validatedShapes;
46691
46692 context.shapes = addAttached(shapes);
46693
46694 context.validatedShapes = removeAttached(validatedShapes);
46695 });
46696
46697 // add attachers to the visual's group
46698 movePreview && eventBus.on('shape.move.start', LOW_PRIORITY$5, function(e) {
46699
46700 var context = e.context,
46701 shapes = context.shapes,
46702 attachers = getAttachers(shapes);
46703
46704 forEach(attachers, function(attacher) {
46705 movePreview.makeDraggable(context, attacher, true);
46706
46707 forEach(attacher.labels, function(label) {
46708 movePreview.makeDraggable(context, label, true);
46709 });
46710 });
46711 });
46712
46713 // add attach-ok marker to current host
46714 movePreview && eventBus.on('shape.move.start', function(event) {
46715 var context = event.context,
46716 shapes = context.shapes;
46717
46718 if (shapes.length !== 1) {
46719 return;
46720 }
46721
46722 var shape = shapes[0];
46723
46724 var host = shape.host;
46725
46726 if (host) {
46727 canvas.addMarker(host, MARKER_ATTACH$1);
46728
46729 eventBus.once([
46730 'shape.move.out',
46731 'shape.move.cleanup'
46732 ], function() {
46733 canvas.removeMarker(host, MARKER_ATTACH$1);
46734 });
46735 }
46736 });
46737
46738 // add all attachers to move closure
46739 this.preExecuted('elements.move', HIGH_PRIORITY$4, function(e) {
46740 var context = e.context,
46741 closure = context.closure,
46742 shapes = context.shapes,
46743 attachers = getAttachers(shapes);
46744
46745 forEach(attachers, function(attacher) {
46746 closure.add(attacher, closure.topLevel[attacher.host.id]);
46747 });
46748 });
46749
46750 // perform the attaching after shapes are done moving
46751 this.postExecuted('elements.move', function(e) {
46752
46753 var context = e.context,
46754 shapes = context.shapes,
46755 newHost = context.newHost,
46756 attachers;
46757
46758 // only single elements can be attached
46759 // multiply elements can be detached
46760 if (newHost && shapes.length !== 1) {
46761 return;
46762 }
46763
46764 if (newHost) {
46765 attachers = shapes;
46766 } else {
46767
46768 // find attachers moved without host
46769 attachers = filter(shapes, function(shape) {
46770 var host = shape.host;
46771
46772 return isAttacher(shape) && !includes$4(shapes, host);
46773 });
46774 }
46775
46776 forEach(attachers, function(attacher) {
46777 modeling.updateAttachment(attacher, newHost);
46778 });
46779 });
46780
46781 // ensure invalid attachment connections are removed
46782 this.postExecuted('elements.move', function(e) {
46783
46784 var shapes = e.context.shapes;
46785
46786 forEach(shapes, function(shape) {
46787
46788 forEach(shape.attachers, function(attacher) {
46789
46790 // remove invalid outgoing connections
46791 forEach(attacher.outgoing.slice(), function(connection) {
46792 var allowed = rules.allowed('connection.reconnect', {
46793 connection: connection,
46794 source: connection.source,
46795 target: connection.target
46796 });
46797
46798 if (!allowed) {
46799 modeling.removeConnection(connection);
46800 }
46801 });
46802
46803 // remove invalid incoming connections
46804 forEach(attacher.incoming.slice(), function(connection) {
46805 var allowed = rules.allowed('connection.reconnect', {
46806 connection: connection,
46807 source: connection.source,
46808 target: connection.target
46809 });
46810
46811 if (!allowed) {
46812 modeling.removeConnection(connection);
46813 }
46814 });
46815 });
46816 });
46817 });
46818
46819 this.postExecute('shape.create', function(e) {
46820 var context = e.context,
46821 shape = context.shape,
46822 host = context.host;
46823
46824 if (host) {
46825 modeling.updateAttachment(shape, host);
46826 }
46827 });
46828
46829 // update attachments if the host is replaced
46830 this.postExecute('shape.replace', function(e) {
46831
46832 var context = e.context,
46833 oldShape = context.oldShape,
46834 newShape = context.newShape;
46835
46836 // move the attachers to the new host
46837 saveClear(oldShape.attachers, function(attacher) {
46838 var allowed = rules.allowed('elements.move', {
46839 target: newShape,
46840 shapes: [attacher]
46841 });
46842
46843 if (allowed === 'attach') {
46844 modeling.updateAttachment(attacher, newShape);
46845 } else {
46846 modeling.removeShape(attacher);
46847 }
46848 });
46849
46850 // move attachers if new host has different size
46851 if (newShape.attachers.length) {
46852
46853 forEach(newShape.attachers, function(attacher) {
46854 var delta = getNewAttachShapeDelta(attacher, oldShape, newShape);
46855 modeling.moveShape(attacher, delta, attacher.parent);
46856 });
46857 }
46858
46859 });
46860
46861 // move shape on host resize
46862 this.postExecute('shape.resize', function(event) {
46863 var context = event.context,
46864 shape = context.shape,
46865 oldBounds = context.oldBounds,
46866 newBounds = context.newBounds,
46867 attachers = shape.attachers,
46868 hints = context.hints || {};
46869
46870 if (hints.attachSupport === false) {
46871 return;
46872 }
46873
46874 forEach(attachers, function(attacher) {
46875 var delta = getNewAttachShapeDelta(attacher, oldBounds, newBounds);
46876
46877 modeling.moveShape(attacher, delta, attacher.parent);
46878
46879 forEach(attacher.labels, function(label) {
46880 modeling.moveShape(label, delta, label.parent);
46881 });
46882 });
46883 });
46884
46885 // remove attachments
46886 this.preExecute('shape.delete', function(event) {
46887
46888 var shape = event.context.shape;
46889
46890 saveClear(shape.attachers, function(attacher) {
46891 modeling.removeShape(attacher);
46892 });
46893
46894 if (shape.host) {
46895 modeling.updateAttachment(shape, null);
46896 }
46897 });
46898 }
46899
46900 inherits$1(AttachSupport, CommandInterceptor);
46901
46902 AttachSupport.$inject = [
46903 'injector',
46904 'eventBus',
46905 'canvas',
46906 'rules',
46907 'modeling'
46908 ];
46909
46910
46911 /**
46912 * Return attachers of the given shapes
46913 *
46914 * @param {Array<djs.model.Base>} shapes
46915 * @return {Array<djs.model.Base>}
46916 */
46917 function getAttachers(shapes) {
46918 return flatten(map$1(shapes, function(s) {
46919 return s.attachers || [];
46920 }));
46921 }
46922
46923 /**
46924 * Return a combined list of elements and
46925 * attachers.
46926 *
46927 * @param {Array<djs.model.Base>} elements
46928 * @return {Array<djs.model.Base>} filtered
46929 */
46930 function addAttached(elements) {
46931 var attachers = getAttachers(elements);
46932
46933 return unionBy('id', elements, attachers);
46934 }
46935
46936 /**
46937 * Return a filtered list of elements that do not
46938 * contain attached elements with hosts being part
46939 * of the selection.
46940 *
46941 * @param {Array<djs.model.Base>} elements
46942 *
46943 * @return {Array<djs.model.Base>} filtered
46944 */
46945 function removeAttached(elements) {
46946
46947 var ids = groupBy(elements, 'id');
46948
46949 return filter(elements, function(element) {
46950 while (element) {
46951
46952 // host in selection
46953 if (element.host && ids[element.host.id]) {
46954 return false;
46955 }
46956
46957 element = element.parent;
46958 }
46959
46960 return true;
46961 });
46962 }
46963
46964 function isAttacher(shape) {
46965 return !!shape.host;
46966 }
46967
46968 function includes$4(array, item) {
46969 return array.indexOf(item) !== -1;
46970 }
46971
46972 var AttachSupportModule = {
46973 __depends__: [
46974 RulesModule$1
46975 ],
46976 __init__: [ 'attachSupport' ],
46977 attachSupport: [ 'type', AttachSupport ]
46978 };
46979
46980 var LOW_PRIORITY$4 = 250;
46981
46982 /**
46983 * The tool manager acts as middle-man between the available tool's and the Palette,
46984 * it takes care of making sure that the correct active state is set.
46985 *
46986 * @param {Object} eventBus
46987 * @param {Object} dragging
46988 */
46989 function ToolManager(eventBus, dragging) {
46990 this._eventBus = eventBus;
46991 this._dragging = dragging;
46992
46993 this._tools = [];
46994 this._active = null;
46995 }
46996
46997 ToolManager.$inject = [ 'eventBus', 'dragging' ];
46998
46999 ToolManager.prototype.registerTool = function(name, events) {
47000 var tools = this._tools;
47001
47002 if (!events) {
47003 throw new Error('A tool has to be registered with it\'s "events"');
47004 }
47005
47006 tools.push(name);
47007
47008 this.bindEvents(name, events);
47009 };
47010
47011 ToolManager.prototype.isActive = function(tool) {
47012 return tool && this._active === tool;
47013 };
47014
47015 ToolManager.prototype.length = function(tool) {
47016 return this._tools.length;
47017 };
47018
47019 ToolManager.prototype.setActive = function(tool) {
47020 var eventBus = this._eventBus;
47021
47022 if (this._active !== tool) {
47023 this._active = tool;
47024
47025 eventBus.fire('tool-manager.update', { tool: tool });
47026 }
47027 };
47028
47029 ToolManager.prototype.bindEvents = function(name, events) {
47030 var eventBus = this._eventBus,
47031 dragging = this._dragging;
47032
47033 var eventsToRegister = [];
47034
47035 eventBus.on(events.tool + '.init', function(event) {
47036 var context = event.context;
47037
47038 // Active tools that want to reactivate themselves must do this explicitly
47039 if (!context.reactivate && this.isActive(name)) {
47040 this.setActive(null);
47041
47042 dragging.cancel();
47043 return;
47044 }
47045
47046 this.setActive(name);
47047
47048 }, this);
47049
47050 // Todo[ricardo]: add test cases
47051 forEach(events, function(event) {
47052 eventsToRegister.push(event + '.ended');
47053 eventsToRegister.push(event + '.canceled');
47054 });
47055
47056 eventBus.on(eventsToRegister, LOW_PRIORITY$4, function(event) {
47057
47058 // We defer the de-activation of the tool to the .activate phase,
47059 // so we're able to check if we want to toggle off the current
47060 // active tool or switch to a new one
47061 if (!this._active) {
47062 return;
47063 }
47064
47065 if (isPaletteClick(event)) {
47066 return;
47067 }
47068
47069 this.setActive(null);
47070 }, this);
47071
47072 };
47073
47074
47075 // helpers ///////////////
47076
47077 /**
47078 * Check if a given event is a palette click event.
47079 *
47080 * @param {EventBus.Event} event
47081 *
47082 * @return {boolean}
47083 */
47084 function isPaletteClick(event) {
47085 var target = event.originalEvent && event.originalEvent.target;
47086
47087 return target && closest(target, '.group[data-group="tools"]');
47088 }
47089
47090 var ToolManagerModule = {
47091 __depends__: [
47092 DraggingModule
47093 ],
47094 __init__: [ 'toolManager' ],
47095 toolManager: [ 'type', ToolManager ]
47096 };
47097
47098 /**
47099 * Return direction given axis and delta.
47100 *
47101 * @param {string} axis
47102 * @param {number} delta
47103 *
47104 * @return {string}
47105 */
47106 function getDirection(axis, delta) {
47107
47108 if (axis === 'x') {
47109 if (delta > 0) {
47110 return 'e';
47111 }
47112
47113 if (delta < 0) {
47114 return 'w';
47115 }
47116 }
47117
47118 if (axis === 'y') {
47119 if (delta > 0) {
47120 return 's';
47121 }
47122
47123 if (delta < 0) {
47124 return 'n';
47125 }
47126 }
47127
47128 return null;
47129 }
47130
47131 /**
47132 * Returns connections whose waypoints are to be updated. Waypoints are to be updated if start
47133 * or end is to be moved or resized.
47134 *
47135 * @param {Array<djs.model.Shape} movingShapes
47136 * @param {Array<djs.model.Shape} resizingShapes
47137 *
47138 * @returns {Array<djs.model.Connection>}
47139 */
47140 function getWaypointsUpdatingConnections(movingShapes, resizingShapes) {
47141 var waypointsUpdatingConnections = [];
47142
47143 forEach(movingShapes.concat(resizingShapes), function(shape) {
47144 var incoming = shape.incoming,
47145 outgoing = shape.outgoing;
47146
47147 forEach(incoming.concat(outgoing), function(connection) {
47148 var source = connection.source,
47149 target = connection.target;
47150
47151 if (includes$3(movingShapes, source) ||
47152 includes$3(movingShapes, target) ||
47153 includes$3(resizingShapes, source) ||
47154 includes$3(resizingShapes, target)) {
47155
47156 if (!includes$3(waypointsUpdatingConnections, connection)) {
47157 waypointsUpdatingConnections.push(connection);
47158 }
47159 }
47160 });
47161 });
47162
47163 return waypointsUpdatingConnections;
47164 }
47165
47166 function includes$3(array, item) {
47167 return array.indexOf(item) !== -1;
47168 }
47169
47170 /**
47171 * Resize bounds.
47172 *
47173 * @param {Object} bounds
47174 * @param {number} bounds.x
47175 * @param {number} bounds.y
47176 * @param {number} bounds.width
47177 * @param {number} bounds.height
47178 * @param {string} direction
47179 * @param {Object} delta
47180 * @param {number} delta.x
47181 * @param {number} delta.y
47182 *
47183 * @return {Object}
47184 */
47185 function resizeBounds(bounds, direction, delta) {
47186 var x = bounds.x,
47187 y = bounds.y,
47188 width = bounds.width,
47189 height = bounds.height,
47190 dx = delta.x,
47191 dy = delta.y;
47192
47193 switch (direction) {
47194 case 'n':
47195 return {
47196 x: x,
47197 y: y + dy,
47198 width: width,
47199 height: height - dy
47200 };
47201 case 's':
47202 return {
47203 x: x,
47204 y: y,
47205 width: width,
47206 height: height + dy
47207 };
47208 case 'w':
47209 return {
47210 x: x + dx,
47211 y: y,
47212 width: width - dx,
47213 height: height
47214 };
47215 case 'e':
47216 return {
47217 x: x,
47218 y: y,
47219 width: width + dx,
47220 height: height
47221 };
47222 default:
47223 throw new Error('unknown direction: ' + direction);
47224 }
47225 }
47226
47227 var abs$1 = Math.abs,
47228 round$4 = Math.round;
47229
47230 var AXIS_TO_DIMENSION = {
47231 x: 'width',
47232 y: 'height'
47233 };
47234
47235 var CURSOR_CROSSHAIR = 'crosshair';
47236
47237 var DIRECTION_TO_TRBL = {
47238 n: 'top',
47239 w: 'left',
47240 s: 'bottom',
47241 e: 'right'
47242 };
47243
47244 var HIGH_PRIORITY$3 = 1500;
47245
47246 var DIRECTION_TO_OPPOSITE = {
47247 n: 's',
47248 w: 'e',
47249 s: 'n',
47250 e: 'w'
47251 };
47252
47253 var PADDING = 20;
47254
47255
47256 /**
47257 * Add or remove space by moving and resizing elements.
47258 *
47259 * @param {Canvas} canvas
47260 * @param {Dragging} dragging
47261 * @param {EventBus} eventBus
47262 * @param {Modeling} modeling
47263 * @param {Rules} rules
47264 * @param {ToolManager} toolManager
47265 * @param {Mouse} mouse
47266 */
47267 function SpaceTool(
47268 canvas, dragging, eventBus,
47269 modeling, rules, toolManager,
47270 mouse) {
47271
47272 this._canvas = canvas;
47273 this._dragging = dragging;
47274 this._eventBus = eventBus;
47275 this._modeling = modeling;
47276 this._rules = rules;
47277 this._toolManager = toolManager;
47278 this._mouse = mouse;
47279
47280 var self = this;
47281
47282 toolManager.registerTool('space', {
47283 tool: 'spaceTool.selection',
47284 dragging: 'spaceTool'
47285 });
47286
47287 eventBus.on('spaceTool.selection.end', function(event) {
47288 eventBus.once('spaceTool.selection.ended', function() {
47289 self.activateMakeSpace(event.originalEvent);
47290 });
47291 });
47292
47293 eventBus.on('spaceTool.move', HIGH_PRIORITY$3 , function(event) {
47294 var context = event.context,
47295 initialized = context.initialized;
47296
47297 if (!initialized) {
47298 initialized = context.initialized = self.init(event, context);
47299 }
47300
47301 if (initialized) {
47302 ensureConstraints(event);
47303 }
47304 });
47305
47306 eventBus.on('spaceTool.end', function(event) {
47307 var context = event.context,
47308 axis = context.axis,
47309 direction = context.direction,
47310 movingShapes = context.movingShapes,
47311 resizingShapes = context.resizingShapes,
47312 start = context.start;
47313
47314 if (!context.initialized) {
47315 return;
47316 }
47317
47318 ensureConstraints(event);
47319
47320 var delta = {
47321 x: 0,
47322 y: 0
47323 };
47324
47325 delta[ axis ] = round$4(event[ 'd' + axis ]);
47326
47327 self.makeSpace(movingShapes, resizingShapes, delta, direction, start);
47328
47329 eventBus.once('spaceTool.ended', function(event) {
47330
47331 // activate space tool selection after make space
47332 self.activateSelection(event.originalEvent, true, true);
47333 });
47334 });
47335 }
47336
47337 SpaceTool.$inject = [
47338 'canvas',
47339 'dragging',
47340 'eventBus',
47341 'modeling',
47342 'rules',
47343 'toolManager',
47344 'mouse'
47345 ];
47346
47347 /**
47348 * Activate space tool selection.
47349 *
47350 * @param {Object} event
47351 * @param {boolean} autoActivate
47352 */
47353 SpaceTool.prototype.activateSelection = function(event, autoActivate, reactivate) {
47354 this._dragging.init(event, 'spaceTool.selection', {
47355 autoActivate: autoActivate,
47356 cursor: CURSOR_CROSSHAIR,
47357 data: {
47358 context: {
47359 reactivate: reactivate
47360 }
47361 },
47362 trapClick: false
47363 });
47364 };
47365
47366 /**
47367 * Activate space tool make space.
47368 *
47369 * @param {MouseEvent} event
47370 */
47371 SpaceTool.prototype.activateMakeSpace = function(event) {
47372 this._dragging.init(event, 'spaceTool', {
47373 autoActivate: true,
47374 cursor: CURSOR_CROSSHAIR,
47375 data: {
47376 context: {}
47377 }
47378 });
47379 };
47380
47381 /**
47382 * Make space.
47383 *
47384 * @param {Array<djs.model.Shape>} movingShapes
47385 * @param {Array<djs.model.Shape>} resizingShapes
47386 * @param {Object} delta
47387 * @param {number} delta.x
47388 * @param {number} delta.y
47389 * @param {string} direction
47390 * @param {number} start
47391 */
47392 SpaceTool.prototype.makeSpace = function(movingShapes, resizingShapes, delta, direction, start) {
47393 return this._modeling.createSpace(movingShapes, resizingShapes, delta, direction, start);
47394 };
47395
47396 /**
47397 * Initialize make space and return true if that was successful.
47398 *
47399 * @param {Object} event
47400 * @param {Object} context
47401 *
47402 * @return {boolean}
47403 */
47404 SpaceTool.prototype.init = function(event, context) {
47405 var axis = abs$1(event.dx) > abs$1(event.dy) ? 'x' : 'y',
47406 delta = event[ 'd' + axis ],
47407 start = event[ axis ] - delta;
47408
47409 if (abs$1(delta) < 5) {
47410 return false;
47411 }
47412
47413 // invert delta to remove space when moving left
47414 if (delta < 0) {
47415 delta *= -1;
47416 }
47417
47418 // invert delta to add/remove space when removing/adding space if modifier key is pressed
47419 if (hasPrimaryModifier(event)) {
47420 delta *= -1;
47421 }
47422
47423 var direction = getDirection(axis, delta);
47424
47425 var root = this._canvas.getRootElement();
47426
47427 var children = selfAndAllChildren(root, true);
47428
47429 var elements = this.calculateAdjustments(children, axis, delta, start);
47430
47431 var minDimensions = this._eventBus.fire('spaceTool.getMinDimensions', {
47432 axis: axis,
47433 direction: direction,
47434 shapes: elements.resizingShapes,
47435 start: start
47436 });
47437
47438 var spaceToolConstraints = getSpaceToolConstraints(elements, axis, direction, start, minDimensions);
47439
47440 assign(
47441 context,
47442 elements,
47443 {
47444 axis: axis,
47445 direction: direction,
47446 spaceToolConstraints: spaceToolConstraints,
47447 start: start
47448 }
47449 );
47450
47451 set('resize-' + (axis === 'x' ? 'ew' : 'ns'));
47452
47453 return true;
47454 };
47455
47456 /**
47457 * Get elements to be moved and resized.
47458 *
47459 * @param {Array<djs.model.Shape>} elements
47460 * @param {string} axis
47461 * @param {number} delta
47462 * @param {number} start
47463 *
47464 * @return {Object}
47465 */
47466 SpaceTool.prototype.calculateAdjustments = function(elements, axis, delta, start) {
47467 var rules = this._rules;
47468
47469 var movingShapes = [],
47470 resizingShapes = [];
47471
47472 forEach(elements, function(element) {
47473 if (!element.parent || isConnection$7(element)) {
47474 return;
47475 }
47476
47477 var shapeStart = element[ axis ],
47478 shapeEnd = shapeStart + element[ AXIS_TO_DIMENSION[ axis ] ];
47479
47480 // shape to be moved
47481 if ((delta > 0 && shapeStart > start) || (delta < 0 && shapeEnd < start)) {
47482 return movingShapes.push(element);
47483 }
47484
47485 // shape to be resized
47486 if (shapeStart < start &&
47487 shapeEnd > start &&
47488 rules.allowed('shape.resize', { shape: element })
47489 ) {
47490
47491 return resizingShapes.push(element);
47492 }
47493 });
47494
47495 return {
47496 movingShapes: movingShapes,
47497 resizingShapes: resizingShapes
47498 };
47499 };
47500
47501 SpaceTool.prototype.toggle = function() {
47502
47503 if (this.isActive()) {
47504 return this._dragging.cancel();
47505 }
47506
47507 var mouseEvent = this._mouse.getLastMoveEvent();
47508
47509 this.activateSelection(mouseEvent, !!mouseEvent);
47510 };
47511
47512 SpaceTool.prototype.isActive = function() {
47513 var context = this._dragging.context();
47514
47515 return context && /^spaceTool/.test(context.prefix);
47516 };
47517
47518 // helpers //////////
47519
47520 function addPadding(trbl) {
47521 return {
47522 top: trbl.top - PADDING,
47523 right: trbl.right + PADDING,
47524 bottom: trbl.bottom + PADDING,
47525 left: trbl.left - PADDING
47526 };
47527 }
47528
47529 function ensureConstraints(event) {
47530 var context = event.context,
47531 spaceToolConstraints = context.spaceToolConstraints;
47532
47533 if (!spaceToolConstraints) {
47534 return;
47535 }
47536
47537 var x, y;
47538
47539 if (isNumber(spaceToolConstraints.left)) {
47540 x = Math.max(event.x, spaceToolConstraints.left);
47541
47542 event.dx = event.dx + x - event.x;
47543 event.x = x;
47544 }
47545
47546 if (isNumber(spaceToolConstraints.right)) {
47547 x = Math.min(event.x, spaceToolConstraints.right);
47548
47549 event.dx = event.dx + x - event.x;
47550 event.x = x;
47551 }
47552
47553 if (isNumber(spaceToolConstraints.top)) {
47554 y = Math.max(event.y, spaceToolConstraints.top);
47555
47556 event.dy = event.dy + y - event.y;
47557 event.y = y;
47558 }
47559
47560 if (isNumber(spaceToolConstraints.bottom)) {
47561 y = Math.min(event.y, spaceToolConstraints.bottom);
47562
47563 event.dy = event.dy + y - event.y;
47564 event.y = y;
47565 }
47566 }
47567
47568 function getSpaceToolConstraints(elements, axis, direction, start, minDimensions) {
47569 var movingShapes = elements.movingShapes,
47570 resizingShapes = elements.resizingShapes;
47571
47572 if (!resizingShapes.length) {
47573 return;
47574 }
47575
47576 var spaceToolConstraints = {},
47577 min,
47578 max;
47579
47580 forEach(resizingShapes, function(resizingShape) {
47581 var resizingShapeBBox = asTRBL(resizingShape);
47582
47583 // find children that are not moving or resizing
47584 var nonMovingResizingChildren = filter(resizingShape.children, function(child) {
47585 return !isConnection$7(child) &&
47586 !isLabel$2(child) &&
47587 !includes$2(movingShapes, child) &&
47588 !includes$2(resizingShapes, child);
47589 });
47590
47591 // find children that are moving
47592 var movingChildren = filter(resizingShape.children, function(child) {
47593 return !isConnection$7(child) && !isLabel$2(child) && includes$2(movingShapes, child);
47594 });
47595
47596 var minOrMax,
47597 nonMovingResizingChildrenBBox,
47598 movingChildrenBBox;
47599
47600 if (nonMovingResizingChildren.length) {
47601 nonMovingResizingChildrenBBox = addPadding(asTRBL(getBBox(nonMovingResizingChildren)));
47602
47603 minOrMax = start -
47604 resizingShapeBBox[ DIRECTION_TO_TRBL[ direction ] ] +
47605 nonMovingResizingChildrenBBox[ DIRECTION_TO_TRBL[ direction ] ];
47606
47607 if (direction === 'n') {
47608 spaceToolConstraints.bottom = max = isNumber(max) ? Math.min(max, minOrMax) : minOrMax;
47609 } else if (direction === 'w') {
47610 spaceToolConstraints.right = max = isNumber(max) ? Math.min(max, minOrMax) : minOrMax;
47611 } else if (direction === 's') {
47612 spaceToolConstraints.top = min = isNumber(min) ? Math.max(min, minOrMax) : minOrMax;
47613 } else if (direction === 'e') {
47614 spaceToolConstraints.left = min = isNumber(min) ? Math.max(min, minOrMax) : minOrMax;
47615 }
47616 }
47617
47618 if (movingChildren.length) {
47619 movingChildrenBBox = addPadding(asTRBL(getBBox(movingChildren)));
47620
47621 minOrMax = start -
47622 movingChildrenBBox[ DIRECTION_TO_TRBL[ DIRECTION_TO_OPPOSITE[ direction ] ] ] +
47623 resizingShapeBBox[ DIRECTION_TO_TRBL[ DIRECTION_TO_OPPOSITE[ direction ] ] ];
47624
47625 if (direction === 'n') {
47626 spaceToolConstraints.bottom = max = isNumber(max) ? Math.min(max, minOrMax) : minOrMax;
47627 } else if (direction === 'w') {
47628 spaceToolConstraints.right = max = isNumber(max) ? Math.min(max, minOrMax) : minOrMax;
47629 } else if (direction === 's') {
47630 spaceToolConstraints.top = min = isNumber(min) ? Math.max(min, minOrMax) : minOrMax;
47631 } else if (direction === 'e') {
47632 spaceToolConstraints.left = min = isNumber(min) ? Math.max(min, minOrMax) : minOrMax;
47633 }
47634 }
47635
47636 var resizingShapeMinDimensions = minDimensions && minDimensions[ resizingShape.id ];
47637
47638 if (resizingShapeMinDimensions) {
47639 if (direction === 'n') {
47640 minOrMax = start +
47641 resizingShape[ AXIS_TO_DIMENSION [ axis ] ] -
47642 resizingShapeMinDimensions[ AXIS_TO_DIMENSION[ axis ] ];
47643
47644 spaceToolConstraints.bottom = max = isNumber(max) ? Math.min(max, minOrMax) : minOrMax;
47645 } else if (direction === 'w') {
47646 minOrMax = start +
47647 resizingShape[ AXIS_TO_DIMENSION [ axis ] ] -
47648 resizingShapeMinDimensions[ AXIS_TO_DIMENSION[ axis ] ];
47649
47650 spaceToolConstraints.right = max = isNumber(max) ? Math.min(max, minOrMax) : minOrMax;
47651 } else if (direction === 's') {
47652 minOrMax = start -
47653 resizingShape[ AXIS_TO_DIMENSION [ axis ] ] +
47654 resizingShapeMinDimensions[ AXIS_TO_DIMENSION[ axis ] ];
47655
47656 spaceToolConstraints.top = min = isNumber(min) ? Math.max(min, minOrMax) : minOrMax;
47657 } else if (direction === 'e') {
47658 minOrMax = start -
47659 resizingShape[ AXIS_TO_DIMENSION [ axis ] ] +
47660 resizingShapeMinDimensions[ AXIS_TO_DIMENSION[ axis ] ];
47661
47662 spaceToolConstraints.left = min = isNumber(min) ? Math.max(min, minOrMax) : minOrMax;
47663 }
47664 }
47665 });
47666
47667 return spaceToolConstraints;
47668 }
47669
47670 function includes$2(array, item) {
47671 return array.indexOf(item) !== -1;
47672 }
47673
47674 function isConnection$7(element) {
47675 return !!element.waypoints;
47676 }
47677
47678 function isLabel$2(element) {
47679 return !!element.labelTarget;
47680 }
47681
47682 var MARKER_DRAGGING$1 = 'djs-dragging',
47683 MARKER_RESIZING = 'djs-resizing';
47684
47685 var LOW_PRIORITY$3 = 250;
47686
47687 var max = Math.max;
47688
47689
47690 /**
47691 * Provides previews for selecting/moving/resizing shapes when creating/removing space.
47692 *
47693 * @param {EventBus} eventBus
47694 * @param {ElementRegistry} elementRegistry
47695 * @param {Canvas} canvas
47696 * @param {Styles} styles
47697 */
47698 function SpaceToolPreview(
47699 eventBus, elementRegistry, canvas,
47700 styles, previewSupport) {
47701
47702 function addPreviewGfx(collection, dragGroup) {
47703 forEach(collection, function(element) {
47704 previewSupport.addDragger(element, dragGroup);
47705
47706 canvas.addMarker(element, MARKER_DRAGGING$1);
47707 });
47708 }
47709
47710 // add crosshair
47711 eventBus.on('spaceTool.selection.start', function(event) {
47712 var space = canvas.getLayer('space'),
47713 context = event.context;
47714
47715 var orientation = {
47716 x: 'M 0,-10000 L 0,10000',
47717 y: 'M -10000,0 L 10000,0'
47718 };
47719
47720 var crosshairGroup = create$1('g');
47721 attr(crosshairGroup, styles.cls('djs-crosshair-group', [ 'no-events' ]));
47722
47723 append(space, crosshairGroup);
47724
47725 // horizontal path
47726 var pathX = create$1('path');
47727 attr(pathX, 'd', orientation.x);
47728 classes(pathX).add('djs-crosshair');
47729
47730 append(crosshairGroup, pathX);
47731
47732 // vertical path
47733 var pathY = create$1('path');
47734 attr(pathY, 'd', orientation.y);
47735 classes(pathY).add('djs-crosshair');
47736
47737 append(crosshairGroup, pathY);
47738
47739 context.crosshairGroup = crosshairGroup;
47740 });
47741
47742 // update crosshair
47743 eventBus.on('spaceTool.selection.move', function(event) {
47744 var crosshairGroup = event.context.crosshairGroup;
47745
47746 translate$2(crosshairGroup, event.x, event.y);
47747 });
47748
47749 // remove crosshair
47750 eventBus.on('spaceTool.selection.cleanup', function(event) {
47751 var context = event.context,
47752 crosshairGroup = context.crosshairGroup;
47753
47754 if (crosshairGroup) {
47755 remove$1(crosshairGroup);
47756 }
47757 });
47758
47759 // add and update move/resize previews
47760 eventBus.on('spaceTool.move', LOW_PRIORITY$3, function(event) {
47761
47762 var context = event.context,
47763 line = context.line,
47764 axis = context.axis,
47765 movingShapes = context.movingShapes,
47766 resizingShapes = context.resizingShapes;
47767
47768 if (!context.initialized) {
47769 return;
47770 }
47771
47772 if (!context.dragGroup) {
47773 var spaceLayer = canvas.getLayer('space');
47774
47775 line = create$1('path');
47776 attr(line, 'd', 'M0,0 L0,0');
47777 classes(line).add('djs-crosshair');
47778
47779 append(spaceLayer, line);
47780
47781 context.line = line;
47782
47783 var dragGroup = create$1('g');
47784 attr(dragGroup, styles.cls('djs-drag-group', [ 'no-events' ]));
47785
47786 append(canvas.getActiveLayer(), dragGroup);
47787
47788 // shapes
47789 addPreviewGfx(movingShapes, dragGroup);
47790
47791 // connections
47792 var movingConnections = context.movingConnections = elementRegistry.filter(function(element) {
47793 var sourceIsMoving = false;
47794
47795 forEach(movingShapes, function(shape) {
47796 forEach(shape.outgoing, function(connection) {
47797 if (element === connection) {
47798 sourceIsMoving = true;
47799 }
47800 });
47801 });
47802
47803 var targetIsMoving = false;
47804
47805 forEach(movingShapes, function(shape) {
47806 forEach(shape.incoming, function(connection) {
47807 if (element === connection) {
47808 targetIsMoving = true;
47809 }
47810 });
47811 });
47812
47813 var sourceIsResizing = false;
47814
47815 forEach(resizingShapes, function(shape) {
47816 forEach(shape.outgoing, function(connection) {
47817 if (element === connection) {
47818 sourceIsResizing = true;
47819 }
47820 });
47821 });
47822
47823 var targetIsResizing = false;
47824
47825 forEach(resizingShapes, function(shape) {
47826 forEach(shape.incoming, function(connection) {
47827 if (element === connection) {
47828 targetIsResizing = true;
47829 }
47830 });
47831 });
47832
47833 return isConnection$6(element)
47834 && (sourceIsMoving || sourceIsResizing)
47835 && (targetIsMoving || targetIsResizing);
47836 });
47837
47838
47839 addPreviewGfx(movingConnections, dragGroup);
47840
47841 context.dragGroup = dragGroup;
47842 }
47843
47844 if (!context.frameGroup) {
47845 var frameGroup = create$1('g');
47846 attr(frameGroup, styles.cls('djs-frame-group', [ 'no-events' ]));
47847
47848 append(canvas.getActiveLayer(), frameGroup);
47849
47850 var frames = [];
47851
47852 forEach(resizingShapes, function(shape) {
47853 var frame = previewSupport.addFrame(shape, frameGroup);
47854
47855 var initialBounds = frame.getBBox();
47856
47857 frames.push({
47858 element: frame,
47859 initialBounds: initialBounds
47860 });
47861
47862 canvas.addMarker(shape, MARKER_RESIZING);
47863 });
47864
47865 context.frameGroup = frameGroup;
47866 context.frames = frames;
47867 }
47868
47869 var orientation = {
47870 x: 'M' + event.x + ', -10000 L' + event.x + ', 10000',
47871 y: 'M -10000, ' + event.y + ' L 10000, ' + event.y
47872 };
47873
47874 attr(line, { d: orientation[ axis ] });
47875
47876 var opposite = { x: 'y', y: 'x' };
47877 var delta = { x: event.dx, y: event.dy };
47878 delta[ opposite[ context.axis ] ] = 0;
47879
47880 // update move previews
47881 translate$2(context.dragGroup, delta.x, delta.y);
47882
47883 // update resize previews
47884 forEach(context.frames, function(frame) {
47885 var element = frame.element,
47886 initialBounds = frame.initialBounds,
47887 width,
47888 height;
47889
47890 if (context.direction === 'e') {
47891 attr(element, {
47892 width: max(initialBounds.width + delta.x, 5)
47893 });
47894 } else {
47895 width = max(initialBounds.width - delta.x, 5);
47896
47897 attr(element, {
47898 width: width,
47899 x: initialBounds.x + initialBounds.width - width
47900 });
47901 }
47902
47903 if (context.direction === 's') {
47904 attr(element, {
47905 height: max(initialBounds.height + delta.y, 5)
47906 });
47907 } else {
47908 height = max(initialBounds.height - delta.y, 5);
47909
47910 attr(element, {
47911 height: height,
47912 y: initialBounds.y + initialBounds.height - height
47913 });
47914 }
47915 });
47916
47917 });
47918
47919 // remove move/resize previews
47920 eventBus.on('spaceTool.cleanup', function(event) {
47921
47922 var context = event.context,
47923 movingShapes = context.movingShapes,
47924 movingConnections = context.movingConnections,
47925 resizingShapes = context.resizingShapes,
47926 line = context.line,
47927 dragGroup = context.dragGroup,
47928 frameGroup = context.frameGroup;
47929
47930 // moving shapes
47931 forEach(movingShapes, function(shape) {
47932 canvas.removeMarker(shape, MARKER_DRAGGING$1);
47933 });
47934
47935 // moving connections
47936 forEach(movingConnections, function(connection) {
47937 canvas.removeMarker(connection, MARKER_DRAGGING$1);
47938 });
47939
47940 if (dragGroup) {
47941 remove$1(line);
47942 remove$1(dragGroup);
47943 }
47944
47945 forEach(resizingShapes, function(shape) {
47946 canvas.removeMarker(shape, MARKER_RESIZING);
47947 });
47948
47949 if (frameGroup) {
47950 remove$1(frameGroup);
47951 }
47952 });
47953 }
47954
47955 SpaceToolPreview.$inject = [
47956 'eventBus',
47957 'elementRegistry',
47958 'canvas',
47959 'styles',
47960 'previewSupport'
47961 ];
47962
47963
47964 // helpers //////////////////////
47965
47966 /**
47967 * Checks if an element is a connection.
47968 */
47969 function isConnection$6(element) {
47970 return element.waypoints;
47971 }
47972
47973 var SpaceToolModule = {
47974 __init__: ['spaceToolPreview'],
47975 __depends__: [
47976 DraggingModule,
47977 RulesModule$1,
47978 ToolManagerModule,
47979 PreviewSupportModule,
47980 MouseModule
47981 ],
47982 spaceTool: ['type', SpaceTool ],
47983 spaceToolPreview: ['type', SpaceToolPreview ]
47984 };
47985
47986 function BpmnFactory(moddle) {
47987 this._model = moddle;
47988 }
47989
47990 BpmnFactory.$inject = [ 'moddle' ];
47991
47992
47993 BpmnFactory.prototype._needsId = function(element) {
47994 return isAny(element, [
47995 'bpmn:RootElement',
47996 'bpmn:FlowElement',
47997 'bpmn:MessageFlow',
47998 'bpmn:DataAssociation',
47999 'bpmn:Artifact',
48000 'bpmn:Participant',
48001 'bpmn:Lane',
48002 'bpmn:LaneSet',
48003 'bpmn:Process',
48004 'bpmn:Collaboration',
48005 'bpmndi:BPMNShape',
48006 'bpmndi:BPMNEdge',
48007 'bpmndi:BPMNDiagram',
48008 'bpmndi:BPMNPlane',
48009 'bpmn:Property',
48010 'bpmn:CategoryValue'
48011 ]);
48012 };
48013
48014 BpmnFactory.prototype._ensureId = function(element) {
48015
48016 // generate semantic ids for elements
48017 // bpmn:SequenceFlow -> SequenceFlow_ID
48018 var prefix;
48019
48020 if (is$1(element, 'bpmn:Activity')) {
48021 prefix = 'Activity';
48022 } else if (is$1(element, 'bpmn:Event')) {
48023 prefix = 'Event';
48024 } else if (is$1(element, 'bpmn:Gateway')) {
48025 prefix = 'Gateway';
48026 } else if (isAny(element, [ 'bpmn:SequenceFlow', 'bpmn:MessageFlow' ])) {
48027 prefix = 'Flow';
48028 } else {
48029 prefix = (element.$type || '').replace(/^[^:]*:/g, '');
48030 }
48031
48032 prefix += '_';
48033
48034 if (!element.id && this._needsId(element)) {
48035 element.id = this._model.ids.nextPrefixed(prefix, element);
48036 }
48037 };
48038
48039
48040 BpmnFactory.prototype.create = function(type, attrs) {
48041 var element = this._model.create(type, attrs || {});
48042
48043 this._ensureId(element);
48044
48045 return element;
48046 };
48047
48048
48049 BpmnFactory.prototype.createDiLabel = function() {
48050 return this.create('bpmndi:BPMNLabel', {
48051 bounds: this.createDiBounds()
48052 });
48053 };
48054
48055
48056 BpmnFactory.prototype.createDiShape = function(semantic, bounds, attrs) {
48057
48058 return this.create('bpmndi:BPMNShape', assign({
48059 bpmnElement: semantic,
48060 bounds: this.createDiBounds(bounds)
48061 }, attrs));
48062 };
48063
48064
48065 BpmnFactory.prototype.createDiBounds = function(bounds) {
48066 return this.create('dc:Bounds', bounds);
48067 };
48068
48069
48070 BpmnFactory.prototype.createDiWaypoints = function(waypoints) {
48071 var self = this;
48072
48073 return map$1(waypoints, function(pos) {
48074 return self.createDiWaypoint(pos);
48075 });
48076 };
48077
48078 BpmnFactory.prototype.createDiWaypoint = function(point) {
48079 return this.create('dc:Point', pick(point, [ 'x', 'y' ]));
48080 };
48081
48082
48083 BpmnFactory.prototype.createDiEdge = function(semantic, waypoints, attrs) {
48084 return this.create('bpmndi:BPMNEdge', assign({
48085 bpmnElement: semantic
48086 }, attrs));
48087 };
48088
48089 BpmnFactory.prototype.createDiPlane = function(semantic) {
48090 return this.create('bpmndi:BPMNPlane', {
48091 bpmnElement: semantic
48092 });
48093 };
48094
48095 /**
48096 * A handler responsible for updating the underlying BPMN 2.0 XML + DI
48097 * once changes on the diagram happen
48098 */
48099 function BpmnUpdater(
48100 eventBus, bpmnFactory, connectionDocking,
48101 translate) {
48102
48103 CommandInterceptor.call(this, eventBus);
48104
48105 this._bpmnFactory = bpmnFactory;
48106 this._translate = translate;
48107
48108 var self = this;
48109
48110
48111
48112 // connection cropping //////////////////////
48113
48114 // crop connection ends during create/update
48115 function cropConnection(e) {
48116 var context = e.context,
48117 hints = context.hints || {},
48118 connection;
48119
48120 if (!context.cropped && hints.createElementsBehavior !== false) {
48121 connection = context.connection;
48122 connection.waypoints = connectionDocking.getCroppedWaypoints(connection);
48123 context.cropped = true;
48124 }
48125 }
48126
48127 this.executed([
48128 'connection.layout',
48129 'connection.create'
48130 ], cropConnection);
48131
48132 this.reverted([ 'connection.layout' ], function(e) {
48133 delete e.context.cropped;
48134 });
48135
48136
48137
48138 // BPMN + DI update //////////////////////
48139
48140
48141 // update parent
48142 function updateParent(e) {
48143 var context = e.context;
48144
48145 self.updateParent(context.shape || context.connection, context.oldParent);
48146 }
48147
48148 function reverseUpdateParent(e) {
48149 var context = e.context;
48150
48151 var element = context.shape || context.connection,
48152
48153 // oldParent is the (old) new parent, because we are undoing
48154 oldParent = context.parent || context.newParent;
48155
48156 self.updateParent(element, oldParent);
48157 }
48158
48159 this.executed([
48160 'shape.move',
48161 'shape.create',
48162 'shape.delete',
48163 'connection.create',
48164 'connection.move',
48165 'connection.delete'
48166 ], ifBpmn(updateParent));
48167
48168 this.reverted([
48169 'shape.move',
48170 'shape.create',
48171 'shape.delete',
48172 'connection.create',
48173 'connection.move',
48174 'connection.delete'
48175 ], ifBpmn(reverseUpdateParent));
48176
48177 /*
48178 * ## Updating Parent
48179 *
48180 * When morphing a Process into a Collaboration or vice-versa,
48181 * make sure that both the *semantic* and *di* parent of each element
48182 * is updated.
48183 *
48184 */
48185 function updateRoot(event) {
48186 var context = event.context,
48187 oldRoot = context.oldRoot,
48188 children = oldRoot.children;
48189
48190 forEach(children, function(child) {
48191 if (is$1(child, 'bpmn:BaseElement')) {
48192 self.updateParent(child);
48193 }
48194 });
48195 }
48196
48197 this.executed([ 'canvas.updateRoot' ], updateRoot);
48198 this.reverted([ 'canvas.updateRoot' ], updateRoot);
48199
48200
48201 // update bounds
48202 function updateBounds(e) {
48203 var shape = e.context.shape;
48204
48205 if (!is$1(shape, 'bpmn:BaseElement')) {
48206 return;
48207 }
48208
48209 self.updateBounds(shape);
48210 }
48211
48212 this.executed([ 'shape.move', 'shape.create', 'shape.resize' ], ifBpmn(function(event) {
48213
48214 // exclude labels because they're handled separately during shape.changed
48215 if (event.context.shape.type === 'label') {
48216 return;
48217 }
48218
48219 updateBounds(event);
48220 }));
48221
48222 this.reverted([ 'shape.move', 'shape.create', 'shape.resize' ], ifBpmn(function(event) {
48223
48224 // exclude labels because they're handled separately during shape.changed
48225 if (event.context.shape.type === 'label') {
48226 return;
48227 }
48228
48229 updateBounds(event);
48230 }));
48231
48232 // Handle labels separately. This is necessary, because the label bounds have to be updated
48233 // every time its shape changes, not only on move, create and resize.
48234 eventBus.on('shape.changed', function(event) {
48235 if (event.element.type === 'label') {
48236 updateBounds({ context: { shape: event.element } });
48237 }
48238 });
48239
48240 // attach / detach connection
48241 function updateConnection(e) {
48242 self.updateConnection(e.context);
48243 }
48244
48245 this.executed([
48246 'connection.create',
48247 'connection.move',
48248 'connection.delete',
48249 'connection.reconnect'
48250 ], ifBpmn(updateConnection));
48251
48252 this.reverted([
48253 'connection.create',
48254 'connection.move',
48255 'connection.delete',
48256 'connection.reconnect'
48257 ], ifBpmn(updateConnection));
48258
48259
48260 // update waypoints
48261 function updateConnectionWaypoints(e) {
48262 self.updateConnectionWaypoints(e.context.connection);
48263 }
48264
48265 this.executed([
48266 'connection.layout',
48267 'connection.move',
48268 'connection.updateWaypoints',
48269 ], ifBpmn(updateConnectionWaypoints));
48270
48271 this.reverted([
48272 'connection.layout',
48273 'connection.move',
48274 'connection.updateWaypoints',
48275 ], ifBpmn(updateConnectionWaypoints));
48276
48277 // update conditional/default flows
48278 this.executed('connection.reconnect', ifBpmn(function(event) {
48279 var context = event.context,
48280 connection = context.connection,
48281 oldSource = context.oldSource,
48282 newSource = context.newSource,
48283 connectionBo = getBusinessObject(connection),
48284 oldSourceBo = getBusinessObject(oldSource),
48285 newSourceBo = getBusinessObject(newSource);
48286
48287 // remove condition from connection on reconnect to new source
48288 // if new source can NOT have condional sequence flow
48289 if (connectionBo.conditionExpression && !isAny(newSourceBo, [
48290 'bpmn:Activity',
48291 'bpmn:ExclusiveGateway',
48292 'bpmn:InclusiveGateway'
48293 ])) {
48294 context.oldConditionExpression = connectionBo.conditionExpression;
48295
48296 delete connectionBo.conditionExpression;
48297 }
48298
48299 // remove default from old source flow on reconnect to new source
48300 // if source changed
48301 if (oldSource !== newSource && oldSourceBo.default === connectionBo) {
48302 context.oldDefault = oldSourceBo.default;
48303
48304 delete oldSourceBo.default;
48305 }
48306 }));
48307
48308 this.reverted('connection.reconnect', ifBpmn(function(event) {
48309 var context = event.context,
48310 connection = context.connection,
48311 oldSource = context.oldSource,
48312 newSource = context.newSource,
48313 connectionBo = getBusinessObject(connection),
48314 oldSourceBo = getBusinessObject(oldSource),
48315 newSourceBo = getBusinessObject(newSource);
48316
48317 // add condition to connection on revert reconnect to new source
48318 if (context.oldConditionExpression) {
48319 connectionBo.conditionExpression = context.oldConditionExpression;
48320 }
48321
48322 // add default to old source on revert reconnect to new source
48323 if (context.oldDefault) {
48324 oldSourceBo.default = context.oldDefault;
48325
48326 delete newSourceBo.default;
48327 }
48328 }));
48329
48330 // update attachments
48331 function updateAttachment(e) {
48332 self.updateAttachment(e.context);
48333 }
48334
48335 this.executed([ 'element.updateAttachment' ], ifBpmn(updateAttachment));
48336 this.reverted([ 'element.updateAttachment' ], ifBpmn(updateAttachment));
48337 }
48338
48339 inherits$1(BpmnUpdater, CommandInterceptor);
48340
48341 BpmnUpdater.$inject = [
48342 'eventBus',
48343 'bpmnFactory',
48344 'connectionDocking',
48345 'translate'
48346 ];
48347
48348
48349 // implementation //////////////////////
48350
48351 BpmnUpdater.prototype.updateAttachment = function(context) {
48352
48353 var shape = context.shape,
48354 businessObject = shape.businessObject,
48355 host = shape.host;
48356
48357 businessObject.attachedToRef = host && host.businessObject;
48358 };
48359
48360 BpmnUpdater.prototype.updateParent = function(element, oldParent) {
48361
48362 // do not update BPMN 2.0 label parent
48363 if (element instanceof Label) {
48364 return;
48365 }
48366
48367 // data stores in collaborations are handled separately by DataStoreBehavior
48368 if (is$1(element, 'bpmn:DataStoreReference') &&
48369 element.parent &&
48370 is$1(element.parent, 'bpmn:Collaboration')) {
48371 return;
48372 }
48373
48374 var parentShape = element.parent;
48375
48376 var businessObject = element.businessObject,
48377 parentBusinessObject = parentShape && parentShape.businessObject,
48378 parentDi = parentBusinessObject && parentBusinessObject.di;
48379
48380 if (is$1(element, 'bpmn:FlowNode')) {
48381 this.updateFlowNodeRefs(businessObject, parentBusinessObject, oldParent && oldParent.businessObject);
48382 }
48383
48384 if (is$1(element, 'bpmn:DataOutputAssociation')) {
48385 if (element.source) {
48386 parentBusinessObject = element.source.businessObject;
48387 } else {
48388 parentBusinessObject = null;
48389 }
48390 }
48391
48392 if (is$1(element, 'bpmn:DataInputAssociation')) {
48393 if (element.target) {
48394 parentBusinessObject = element.target.businessObject;
48395 } else {
48396 parentBusinessObject = null;
48397 }
48398 }
48399
48400 this.updateSemanticParent(businessObject, parentBusinessObject);
48401
48402 if (is$1(element, 'bpmn:DataObjectReference') && businessObject.dataObjectRef) {
48403 this.updateSemanticParent(businessObject.dataObjectRef, parentBusinessObject);
48404 }
48405
48406 this.updateDiParent(businessObject.di, parentDi);
48407 };
48408
48409
48410 BpmnUpdater.prototype.updateBounds = function(shape) {
48411
48412 var di = shape.businessObject.di;
48413
48414 var target = (shape instanceof Label) ? this._getLabel(di) : di;
48415
48416 var bounds = target.bounds;
48417
48418 if (!bounds) {
48419 bounds = this._bpmnFactory.createDiBounds();
48420 target.set('bounds', bounds);
48421 }
48422
48423 assign(bounds, {
48424 x: shape.x,
48425 y: shape.y,
48426 width: shape.width,
48427 height: shape.height
48428 });
48429 };
48430
48431 BpmnUpdater.prototype.updateFlowNodeRefs = function(businessObject, newContainment, oldContainment) {
48432
48433 if (oldContainment === newContainment) {
48434 return;
48435 }
48436
48437 var oldRefs, newRefs;
48438
48439 if (is$1 (oldContainment, 'bpmn:Lane')) {
48440 oldRefs = oldContainment.get('flowNodeRef');
48441 remove(oldRefs, businessObject);
48442 }
48443
48444 if (is$1(newContainment, 'bpmn:Lane')) {
48445 newRefs = newContainment.get('flowNodeRef');
48446 add(newRefs, businessObject);
48447 }
48448 };
48449
48450
48451 // update existing sourceElement and targetElement di information
48452 BpmnUpdater.prototype.updateDiConnection = function(di, newSource, newTarget) {
48453
48454 if (di.sourceElement && di.sourceElement.bpmnElement !== newSource) {
48455 di.sourceElement = newSource && newSource.di;
48456 }
48457
48458 if (di.targetElement && di.targetElement.bpmnElement !== newTarget) {
48459 di.targetElement = newTarget && newTarget.di;
48460 }
48461
48462 };
48463
48464
48465 BpmnUpdater.prototype.updateDiParent = function(di, parentDi) {
48466
48467 if (parentDi && !is$1(parentDi, 'bpmndi:BPMNPlane')) {
48468 parentDi = parentDi.$parent;
48469 }
48470
48471 if (di.$parent === parentDi) {
48472 return;
48473 }
48474
48475 var planeElements = (parentDi || di.$parent).get('planeElement');
48476
48477 if (parentDi) {
48478 planeElements.push(di);
48479 di.$parent = parentDi;
48480 } else {
48481 remove(planeElements, di);
48482 di.$parent = null;
48483 }
48484 };
48485
48486 function getDefinitions(element) {
48487 while (element && !is$1(element, 'bpmn:Definitions')) {
48488 element = element.$parent;
48489 }
48490
48491 return element;
48492 }
48493
48494 BpmnUpdater.prototype.getLaneSet = function(container) {
48495
48496 var laneSet, laneSets;
48497
48498 // bpmn:Lane
48499 if (is$1(container, 'bpmn:Lane')) {
48500 laneSet = container.childLaneSet;
48501
48502 if (!laneSet) {
48503 laneSet = this._bpmnFactory.create('bpmn:LaneSet');
48504 container.childLaneSet = laneSet;
48505 laneSet.$parent = container;
48506 }
48507
48508 return laneSet;
48509 }
48510
48511 // bpmn:Participant
48512 if (is$1(container, 'bpmn:Participant')) {
48513 container = container.processRef;
48514 }
48515
48516 // bpmn:FlowElementsContainer
48517 laneSets = container.get('laneSets');
48518 laneSet = laneSets[0];
48519
48520 if (!laneSet) {
48521 laneSet = this._bpmnFactory.create('bpmn:LaneSet');
48522 laneSet.$parent = container;
48523 laneSets.push(laneSet);
48524 }
48525
48526 return laneSet;
48527 };
48528
48529 BpmnUpdater.prototype.updateSemanticParent = function(businessObject, newParent, visualParent) {
48530
48531 var containment,
48532 translate = this._translate;
48533
48534 if (businessObject.$parent === newParent) {
48535 return;
48536 }
48537
48538 if (is$1(businessObject, 'bpmn:DataInput') || is$1(businessObject, 'bpmn:DataOutput')) {
48539
48540 if (is$1(newParent, 'bpmn:Participant') && 'processRef' in newParent) {
48541 newParent = newParent.processRef;
48542 }
48543
48544 // already in correct ioSpecification
48545 if ('ioSpecification' in newParent && newParent.ioSpecification === businessObject.$parent) {
48546 return;
48547 }
48548 }
48549
48550 if (is$1(businessObject, 'bpmn:Lane')) {
48551
48552 if (newParent) {
48553 newParent = this.getLaneSet(newParent);
48554 }
48555
48556 containment = 'lanes';
48557 } else
48558
48559 if (is$1(businessObject, 'bpmn:FlowElement')) {
48560
48561 if (newParent) {
48562
48563 if (is$1(newParent, 'bpmn:Participant')) {
48564 newParent = newParent.processRef;
48565 } else
48566
48567 if (is$1(newParent, 'bpmn:Lane')) {
48568 do {
48569
48570 // unwrap Lane -> LaneSet -> (Lane | FlowElementsContainer)
48571 newParent = newParent.$parent.$parent;
48572 } while (is$1(newParent, 'bpmn:Lane'));
48573
48574 }
48575 }
48576
48577 containment = 'flowElements';
48578
48579 } else
48580
48581 if (is$1(businessObject, 'bpmn:Artifact')) {
48582
48583 while (newParent &&
48584 !is$1(newParent, 'bpmn:Process') &&
48585 !is$1(newParent, 'bpmn:SubProcess') &&
48586 !is$1(newParent, 'bpmn:Collaboration')) {
48587
48588 if (is$1(newParent, 'bpmn:Participant')) {
48589 newParent = newParent.processRef;
48590 break;
48591 } else {
48592 newParent = newParent.$parent;
48593 }
48594 }
48595
48596 containment = 'artifacts';
48597 } else
48598
48599 if (is$1(businessObject, 'bpmn:MessageFlow')) {
48600 containment = 'messageFlows';
48601
48602 } else
48603
48604 if (is$1(businessObject, 'bpmn:Participant')) {
48605 containment = 'participants';
48606
48607 // make sure the participants process is properly attached / detached
48608 // from the XML document
48609
48610 var process = businessObject.processRef,
48611 definitions;
48612
48613 if (process) {
48614 definitions = getDefinitions(businessObject.$parent || newParent);
48615
48616 if (businessObject.$parent) {
48617 remove(definitions.get('rootElements'), process);
48618 process.$parent = null;
48619 }
48620
48621 if (newParent) {
48622 add(definitions.get('rootElements'), process);
48623 process.$parent = definitions;
48624 }
48625 }
48626 } else
48627
48628 if (is$1(businessObject, 'bpmn:DataOutputAssociation')) {
48629 containment = 'dataOutputAssociations';
48630 } else
48631
48632 if (is$1(businessObject, 'bpmn:DataInputAssociation')) {
48633 containment = 'dataInputAssociations';
48634 }
48635
48636 if (!containment) {
48637 throw new Error(translate(
48638 'no parent for {element} in {parent}',
48639 {
48640 element: businessObject.id,
48641 parent: newParent.id
48642 }
48643 ));
48644 }
48645
48646 var children;
48647
48648 if (businessObject.$parent) {
48649
48650 // remove from old parent
48651 children = businessObject.$parent.get(containment);
48652 remove(children, businessObject);
48653 }
48654
48655 if (!newParent) {
48656 businessObject.$parent = null;
48657 } else {
48658
48659 // add to new parent
48660 children = newParent.get(containment);
48661 children.push(businessObject);
48662 businessObject.$parent = newParent;
48663 }
48664
48665 if (visualParent) {
48666 var diChildren = visualParent.get(containment);
48667
48668 remove(children, businessObject);
48669
48670 if (newParent) {
48671
48672 if (!diChildren) {
48673 diChildren = [];
48674 newParent.set(containment, diChildren);
48675 }
48676
48677 diChildren.push(businessObject);
48678 }
48679 }
48680 };
48681
48682
48683 BpmnUpdater.prototype.updateConnectionWaypoints = function(connection) {
48684 connection.businessObject.di.set('waypoint', this._bpmnFactory.createDiWaypoints(connection.waypoints));
48685 };
48686
48687
48688 BpmnUpdater.prototype.updateConnection = function(context) {
48689
48690 var connection = context.connection,
48691 businessObject = getBusinessObject(connection),
48692 newSource = getBusinessObject(connection.source),
48693 newTarget = getBusinessObject(connection.target),
48694 visualParent;
48695
48696 if (!is$1(businessObject, 'bpmn:DataAssociation')) {
48697
48698 var inverseSet = is$1(businessObject, 'bpmn:SequenceFlow');
48699
48700 if (businessObject.sourceRef !== newSource) {
48701 if (inverseSet) {
48702 remove(businessObject.sourceRef && businessObject.sourceRef.get('outgoing'), businessObject);
48703
48704 if (newSource && newSource.get('outgoing')) {
48705 newSource.get('outgoing').push(businessObject);
48706 }
48707 }
48708
48709 businessObject.sourceRef = newSource;
48710 }
48711
48712 if (businessObject.targetRef !== newTarget) {
48713 if (inverseSet) {
48714 remove(businessObject.targetRef && businessObject.targetRef.get('incoming'), businessObject);
48715
48716 if (newTarget && newTarget.get('incoming')) {
48717 newTarget.get('incoming').push(businessObject);
48718 }
48719 }
48720
48721 businessObject.targetRef = newTarget;
48722 }
48723 } else
48724
48725 if (is$1(businessObject, 'bpmn:DataInputAssociation')) {
48726
48727 // handle obnoxious isMsome sourceRef
48728 businessObject.get('sourceRef')[0] = newSource;
48729
48730 visualParent = context.parent || context.newParent || newTarget;
48731
48732 this.updateSemanticParent(businessObject, newTarget, visualParent);
48733 } else
48734
48735 if (is$1(businessObject, 'bpmn:DataOutputAssociation')) {
48736 visualParent = context.parent || context.newParent || newSource;
48737
48738 this.updateSemanticParent(businessObject, newSource, visualParent);
48739
48740 // targetRef = new target
48741 businessObject.targetRef = newTarget;
48742 }
48743
48744 this.updateConnectionWaypoints(connection);
48745
48746 this.updateDiConnection(businessObject.di, newSource, newTarget);
48747 };
48748
48749
48750 // helpers //////////////////////
48751
48752 BpmnUpdater.prototype._getLabel = function(di) {
48753 if (!di.label) {
48754 di.label = this._bpmnFactory.createDiLabel();
48755 }
48756
48757 return di.label;
48758 };
48759
48760
48761 /**
48762 * Make sure the event listener is only called
48763 * if the touched element is a BPMN element.
48764 *
48765 * @param {Function} fn
48766 * @return {Function} guarded function
48767 */
48768 function ifBpmn(fn) {
48769
48770 return function(event) {
48771
48772 var context = event.context,
48773 element = context.shape || context.connection;
48774
48775 if (is$1(element, 'bpmn:BaseElement')) {
48776 fn(event);
48777 }
48778 };
48779 }
48780
48781 /**
48782 * A bpmn-aware factory for diagram-js shapes
48783 */
48784 function ElementFactory(bpmnFactory, moddle, translate) {
48785 ElementFactory$1.call(this);
48786
48787 this._bpmnFactory = bpmnFactory;
48788 this._moddle = moddle;
48789 this._translate = translate;
48790 }
48791
48792 inherits$1(ElementFactory, ElementFactory$1);
48793
48794 ElementFactory.$inject = [
48795 'bpmnFactory',
48796 'moddle',
48797 'translate'
48798 ];
48799
48800 ElementFactory.prototype.baseCreate = ElementFactory$1.prototype.create;
48801
48802 ElementFactory.prototype.create = function(elementType, attrs) {
48803
48804 // no special magic for labels,
48805 // we assume their businessObjects have already been created
48806 // and wired via attrs
48807 if (elementType === 'label') {
48808 return this.baseCreate(elementType, assign({ type: 'label' }, DEFAULT_LABEL_SIZE, attrs));
48809 }
48810
48811 return this.createBpmnElement(elementType, attrs);
48812 };
48813
48814 ElementFactory.prototype.createBpmnElement = function(elementType, attrs) {
48815 var size,
48816 translate = this._translate;
48817
48818 attrs = attrs || {};
48819
48820 var businessObject = attrs.businessObject;
48821
48822 if (!businessObject) {
48823 if (!attrs.type) {
48824 throw new Error(translate('no shape type specified'));
48825 }
48826
48827 businessObject = this._bpmnFactory.create(attrs.type);
48828 }
48829
48830 if (!businessObject.di) {
48831 if (elementType === 'root') {
48832 businessObject.di = this._bpmnFactory.createDiPlane(businessObject, [], {
48833 id: businessObject.id + '_di'
48834 });
48835 } else
48836 if (elementType === 'connection') {
48837 businessObject.di = this._bpmnFactory.createDiEdge(businessObject, [], {
48838 id: businessObject.id + '_di'
48839 });
48840 } else {
48841 businessObject.di = this._bpmnFactory.createDiShape(businessObject, {}, {
48842 id: businessObject.id + '_di'
48843 });
48844 }
48845 }
48846
48847 if (is$1(businessObject, 'bpmn:Group')) {
48848 attrs = assign({
48849 isFrame: true
48850 }, attrs);
48851 }
48852
48853 if (attrs.di) {
48854 assign(businessObject.di, attrs.di);
48855
48856 delete attrs.di;
48857 }
48858
48859 applyAttributes(businessObject, attrs, [
48860 'processRef',
48861 'isInterrupting',
48862 'associationDirection',
48863 'isForCompensation'
48864 ]);
48865
48866 if (attrs.isExpanded) {
48867 applyAttribute(businessObject.di, attrs, 'isExpanded');
48868 }
48869
48870 if (is$1(businessObject, 'bpmn:ExclusiveGateway')) {
48871 businessObject.di.isMarkerVisible = true;
48872 }
48873
48874 var eventDefinitions,
48875 newEventDefinition;
48876
48877 if (attrs.eventDefinitionType) {
48878 eventDefinitions = businessObject.get('eventDefinitions') || [];
48879 newEventDefinition = this._bpmnFactory.create(attrs.eventDefinitionType, attrs.eventDefinitionAttrs);
48880
48881 if (attrs.eventDefinitionType === 'bpmn:ConditionalEventDefinition') {
48882 newEventDefinition.condition = this._bpmnFactory.create('bpmn:FormalExpression');
48883 }
48884
48885 eventDefinitions.push(newEventDefinition);
48886
48887 newEventDefinition.$parent = businessObject;
48888 businessObject.eventDefinitions = eventDefinitions;
48889
48890 delete attrs.eventDefinitionType;
48891 }
48892
48893 size = this._getDefaultSize(businessObject);
48894
48895 attrs = assign({
48896 businessObject: businessObject,
48897 id: businessObject.id
48898 }, size, attrs);
48899
48900 return this.baseCreate(elementType, attrs);
48901 };
48902
48903
48904 ElementFactory.prototype._getDefaultSize = function(semantic) {
48905
48906 if (is$1(semantic, 'bpmn:SubProcess')) {
48907
48908 if (isExpanded(semantic)) {
48909 return { width: 350, height: 200 };
48910 } else {
48911 return { width: 100, height: 80 };
48912 }
48913 }
48914
48915 if (is$1(semantic, 'bpmn:Task')) {
48916 return { width: 100, height: 80 };
48917 }
48918
48919 if (is$1(semantic, 'bpmn:Gateway')) {
48920 return { width: 50, height: 50 };
48921 }
48922
48923 if (is$1(semantic, 'bpmn:Event')) {
48924 return { width: 36, height: 36 };
48925 }
48926
48927 if (is$1(semantic, 'bpmn:Participant')) {
48928 if (isExpanded(semantic)) {
48929 return { width: 600, height: 250 };
48930 } else {
48931 return { width: 400, height: 60 };
48932 }
48933 }
48934
48935 if (is$1(semantic, 'bpmn:Lane')) {
48936 return { width: 400, height: 100 };
48937 }
48938
48939 if (is$1(semantic, 'bpmn:DataObjectReference')) {
48940 return { width: 36, height: 50 };
48941 }
48942
48943 if (is$1(semantic, 'bpmn:DataStoreReference')) {
48944 return { width: 50, height: 50 };
48945 }
48946
48947 if (is$1(semantic, 'bpmn:TextAnnotation')) {
48948 return { width: 100, height: 30 };
48949 }
48950
48951 if (is$1(semantic, 'bpmn:Group')) {
48952 return { width: 300, height: 300 };
48953 }
48954
48955 return { width: 100, height: 80 };
48956 };
48957
48958
48959 /**
48960 * Create participant.
48961 *
48962 * @param {boolean|Object} [attrs] attrs
48963 *
48964 * @returns {djs.model.Shape}
48965 */
48966 ElementFactory.prototype.createParticipantShape = function(attrs) {
48967
48968 if (!isObject(attrs)) {
48969 attrs = { isExpanded: attrs };
48970 }
48971
48972 attrs = assign({ type: 'bpmn:Participant' }, attrs || {});
48973
48974 // participants are expanded by default
48975 if (attrs.isExpanded !== false) {
48976 attrs.processRef = this._bpmnFactory.create('bpmn:Process');
48977 }
48978
48979 return this.createShape(attrs);
48980 };
48981
48982
48983 // helpers //////////////////////
48984
48985 /**
48986 * Apply attributes from a map to the given element,
48987 * remove attribute from the map on application.
48988 *
48989 * @param {Base} element
48990 * @param {Object} attrs (in/out map of attributes)
48991 * @param {Array<string>} attributeNames name of attributes to apply
48992 */
48993 function applyAttributes(element, attrs, attributeNames) {
48994
48995 forEach(attributeNames, function(property) {
48996 if (attrs[property] !== undefined) {
48997 applyAttribute(element, attrs, property);
48998 }
48999 });
49000 }
49001
49002 /**
49003 * Apply named property to element and drain it from the attrs
49004 * collection.
49005 *
49006 * @param {Base} element
49007 * @param {Object} attrs (in/out map of attributes)
49008 * @param {string} attributeName to apply
49009 */
49010 function applyAttribute(element, attrs, attributeName) {
49011 element[attributeName] = attrs[attributeName];
49012
49013 delete attrs[attributeName];
49014 }
49015
49016 /**
49017 * A handler that align elements in a certain way.
49018 *
49019 */
49020 function AlignElements(modeling, canvas) {
49021 this._modeling = modeling;
49022 this._canvas = canvas;
49023 }
49024
49025 AlignElements.$inject = [ 'modeling', 'canvas' ];
49026
49027
49028 AlignElements.prototype.preExecute = function(context) {
49029 var modeling = this._modeling;
49030
49031 var elements = context.elements,
49032 alignment = context.alignment;
49033
49034
49035 forEach(elements, function(element) {
49036 var delta = {
49037 x: 0,
49038 y: 0
49039 };
49040
49041 if (alignment.left) {
49042 delta.x = alignment.left - element.x;
49043
49044 } else if (alignment.right) {
49045 delta.x = (alignment.right - element.width) - element.x;
49046
49047 } else if (alignment.center) {
49048 delta.x = (alignment.center - Math.round(element.width / 2)) - element.x;
49049
49050 } else if (alignment.top) {
49051 delta.y = alignment.top - element.y;
49052
49053 } else if (alignment.bottom) {
49054 delta.y = (alignment.bottom - element.height) - element.y;
49055
49056 } else if (alignment.middle) {
49057 delta.y = (alignment.middle - Math.round(element.height / 2)) - element.y;
49058 }
49059
49060 modeling.moveElements([ element ], delta, element.parent);
49061 });
49062 };
49063
49064 AlignElements.prototype.postExecute = function(context) {
49065
49066 };
49067
49068 /**
49069 * A handler that implements reversible appending of shapes
49070 * to a source shape.
49071 *
49072 * @param {canvas} Canvas
49073 * @param {elementFactory} ElementFactory
49074 * @param {modeling} Modeling
49075 */
49076 function AppendShapeHandler(modeling) {
49077 this._modeling = modeling;
49078 }
49079
49080 AppendShapeHandler.$inject = [ 'modeling' ];
49081
49082
49083 // api //////////////////////
49084
49085
49086 /**
49087 * Creates a new shape
49088 *
49089 * @param {Object} context
49090 * @param {ElementDescriptor} context.shape the new shape
49091 * @param {ElementDescriptor} context.source the source object
49092 * @param {ElementDescriptor} context.parent the parent object
49093 * @param {Point} context.position position of the new element
49094 */
49095 AppendShapeHandler.prototype.preExecute = function(context) {
49096
49097 var source = context.source;
49098
49099 if (!source) {
49100 throw new Error('source required');
49101 }
49102
49103 var target = context.target || source.parent,
49104 shape = context.shape,
49105 hints = context.hints || {};
49106
49107 shape = context.shape =
49108 this._modeling.createShape(
49109 shape,
49110 context.position,
49111 target, { attach: hints.attach });
49112
49113 context.shape = shape;
49114 };
49115
49116 AppendShapeHandler.prototype.postExecute = function(context) {
49117 var hints = context.hints || {};
49118
49119 if (!existsConnection(context.source, context.shape)) {
49120
49121 // create connection
49122 if (hints.connectionTarget === context.source) {
49123 this._modeling.connect(context.shape, context.source, context.connection);
49124 } else {
49125 this._modeling.connect(context.source, context.shape, context.connection);
49126 }
49127 }
49128 };
49129
49130
49131 function existsConnection(source, target) {
49132 return some(source.outgoing, function(c) {
49133 return c.target === target;
49134 });
49135 }
49136
49137 function CreateConnectionHandler(canvas, layouter) {
49138 this._canvas = canvas;
49139 this._layouter = layouter;
49140 }
49141
49142 CreateConnectionHandler.$inject = [ 'canvas', 'layouter' ];
49143
49144
49145 // api //////////////////////
49146
49147
49148 /**
49149 * Appends a shape to a target shape
49150 *
49151 * @param {Object} context
49152 * @param {djs.element.Base} context.source the source object
49153 * @param {djs.element.Base} context.target the parent object
49154 * @param {Point} context.position position of the new element
49155 */
49156 CreateConnectionHandler.prototype.execute = function(context) {
49157
49158 var connection = context.connection,
49159 source = context.source,
49160 target = context.target,
49161 parent = context.parent,
49162 parentIndex = context.parentIndex,
49163 hints = context.hints;
49164
49165 if (!source || !target) {
49166 throw new Error('source and target required');
49167 }
49168
49169 if (!parent) {
49170 throw new Error('parent required');
49171 }
49172
49173 connection.source = source;
49174 connection.target = target;
49175
49176 if (!connection.waypoints) {
49177 connection.waypoints = this._layouter.layoutConnection(connection, hints);
49178 }
49179
49180 // add connection
49181 this._canvas.addConnection(connection, parent, parentIndex);
49182
49183 return connection;
49184 };
49185
49186 CreateConnectionHandler.prototype.revert = function(context) {
49187 var connection = context.connection;
49188
49189 this._canvas.removeConnection(connection);
49190
49191 connection.source = null;
49192 connection.target = null;
49193
49194 return connection;
49195 };
49196
49197 var round$3 = Math.round;
49198
49199 function CreateElementsHandler(modeling) {
49200 this._modeling = modeling;
49201 }
49202
49203 CreateElementsHandler.$inject = [
49204 'modeling'
49205 ];
49206
49207 CreateElementsHandler.prototype.preExecute = function(context) {
49208 var elements = context.elements,
49209 parent = context.parent,
49210 parentIndex = context.parentIndex,
49211 position = context.position,
49212 hints = context.hints;
49213
49214 var modeling = this._modeling;
49215
49216 // make sure each element has x and y
49217 forEach(elements, function(element) {
49218 if (!isNumber(element.x)) {
49219 element.x = 0;
49220 }
49221
49222 if (!isNumber(element.y)) {
49223 element.y = 0;
49224 }
49225 });
49226
49227 var bbox = getBBox(elements);
49228
49229 // center elements around position
49230 forEach(elements, function(element) {
49231 if (isConnection$5(element)) {
49232 element.waypoints = map$1(element.waypoints, function(waypoint) {
49233 return {
49234 x: round$3(waypoint.x - bbox.x - bbox.width / 2 + position.x),
49235 y: round$3(waypoint.y - bbox.y - bbox.height / 2 + position.y)
49236 };
49237 });
49238 }
49239
49240 assign(element, {
49241 x: round$3(element.x - bbox.x - bbox.width / 2 + position.x),
49242 y: round$3(element.y - bbox.y - bbox.height / 2 + position.y)
49243 });
49244 });
49245
49246 var parents = getParents$1(elements);
49247
49248 var cache = {};
49249
49250 forEach(elements, function(element) {
49251 if (isConnection$5(element)) {
49252 cache[ element.id ] = isNumber(parentIndex) ?
49253 modeling.createConnection(
49254 cache[ element.source.id ],
49255 cache[ element.target.id ],
49256 parentIndex,
49257 element,
49258 element.parent || parent,
49259 hints
49260 ) :
49261 modeling.createConnection(
49262 cache[ element.source.id ],
49263 cache[ element.target.id ],
49264 element,
49265 element.parent || parent,
49266 hints
49267 );
49268
49269 return;
49270 }
49271
49272 var createShapeHints = assign({}, hints);
49273
49274 if (parents.indexOf(element) === -1) {
49275 createShapeHints.autoResize = false;
49276 }
49277
49278 cache[ element.id ] = isNumber(parentIndex) ?
49279 modeling.createShape(
49280 element,
49281 pick(element, [ 'x', 'y', 'width', 'height' ]),
49282 element.parent || parent,
49283 parentIndex,
49284 createShapeHints
49285 ) :
49286 modeling.createShape(
49287 element,
49288 pick(element, [ 'x', 'y', 'width', 'height' ]),
49289 element.parent || parent,
49290 createShapeHints
49291 );
49292 });
49293
49294 context.elements = values(cache);
49295 };
49296
49297 // helpers //////////
49298
49299 function isConnection$5(element) {
49300 return !!element.waypoints;
49301 }
49302
49303 var round$2 = Math.round;
49304
49305
49306 /**
49307 * A handler that implements reversible addition of shapes.
49308 *
49309 * @param {canvas} Canvas
49310 */
49311 function CreateShapeHandler(canvas) {
49312 this._canvas = canvas;
49313 }
49314
49315 CreateShapeHandler.$inject = [ 'canvas' ];
49316
49317
49318 // api //////////////////////
49319
49320
49321 /**
49322 * Appends a shape to a target shape
49323 *
49324 * @param {Object} context
49325 * @param {djs.model.Base} context.parent the parent object
49326 * @param {Point} context.position position of the new element
49327 */
49328 CreateShapeHandler.prototype.execute = function(context) {
49329
49330 var shape = context.shape,
49331 positionOrBounds = context.position,
49332 parent = context.parent,
49333 parentIndex = context.parentIndex;
49334
49335 if (!parent) {
49336 throw new Error('parent required');
49337 }
49338
49339 if (!positionOrBounds) {
49340 throw new Error('position required');
49341 }
49342
49343 // (1) add at event center position _or_ at given bounds
49344 if (positionOrBounds.width !== undefined) {
49345 assign(shape, positionOrBounds);
49346 } else {
49347 assign(shape, {
49348 x: positionOrBounds.x - round$2(shape.width / 2),
49349 y: positionOrBounds.y - round$2(shape.height / 2)
49350 });
49351 }
49352
49353 // (2) add to canvas
49354 this._canvas.addShape(shape, parent, parentIndex);
49355
49356 return shape;
49357 };
49358
49359
49360 /**
49361 * Undo append by removing the shape
49362 */
49363 CreateShapeHandler.prototype.revert = function(context) {
49364
49365 var shape = context.shape;
49366
49367 // (3) remove form canvas
49368 this._canvas.removeShape(shape);
49369
49370 return shape;
49371 };
49372
49373 /**
49374 * A handler that attaches a label to a given target shape.
49375 *
49376 * @param {Canvas} canvas
49377 */
49378 function CreateLabelHandler(canvas) {
49379 CreateShapeHandler.call(this, canvas);
49380 }
49381
49382 inherits$1(CreateLabelHandler, CreateShapeHandler);
49383
49384 CreateLabelHandler.$inject = [ 'canvas' ];
49385
49386
49387 // api //////////////////////
49388
49389
49390 var originalExecute = CreateShapeHandler.prototype.execute;
49391
49392 /**
49393 * Appends a label to a target shape.
49394 *
49395 * @method CreateLabelHandler#execute
49396 *
49397 * @param {Object} context
49398 * @param {ElementDescriptor} context.target the element the label is attached to
49399 * @param {ElementDescriptor} context.parent the parent object
49400 * @param {Point} context.position position of the new element
49401 */
49402 CreateLabelHandler.prototype.execute = function(context) {
49403
49404 var label = context.shape;
49405
49406 ensureValidDimensions(label);
49407
49408 label.labelTarget = context.labelTarget;
49409
49410 return originalExecute.call(this, context);
49411 };
49412
49413 var originalRevert = CreateShapeHandler.prototype.revert;
49414
49415 /**
49416 * Undo append by removing the shape
49417 */
49418 CreateLabelHandler.prototype.revert = function(context) {
49419 context.shape.labelTarget = null;
49420
49421 return originalRevert.call(this, context);
49422 };
49423
49424
49425 // helpers //////////////////////
49426
49427 function ensureValidDimensions(label) {
49428
49429 // make sure a label has valid { width, height } dimensions
49430 [ 'width', 'height' ].forEach(function(prop) {
49431 if (typeof label[prop] === 'undefined') {
49432 label[prop] = 0;
49433 }
49434 });
49435 }
49436
49437 /**
49438 * A handler that implements reversible deletion of Connections.
49439 */
49440 function DeleteConnectionHandler(canvas, modeling) {
49441 this._canvas = canvas;
49442 this._modeling = modeling;
49443 }
49444
49445 DeleteConnectionHandler.$inject = [
49446 'canvas',
49447 'modeling'
49448 ];
49449
49450
49451 DeleteConnectionHandler.prototype.execute = function(context) {
49452
49453 var connection = context.connection,
49454 parent = connection.parent;
49455
49456 context.parent = parent;
49457
49458 // remember containment
49459 context.parentIndex = indexOf(parent.children, connection);
49460
49461 context.source = connection.source;
49462 context.target = connection.target;
49463
49464 this._canvas.removeConnection(connection);
49465
49466 connection.source = null;
49467 connection.target = null;
49468
49469 return connection;
49470 };
49471
49472 /**
49473 * Command revert implementation.
49474 */
49475 DeleteConnectionHandler.prototype.revert = function(context) {
49476
49477 var connection = context.connection,
49478 parent = context.parent,
49479 parentIndex = context.parentIndex;
49480
49481 connection.source = context.source;
49482 connection.target = context.target;
49483
49484 // restore containment
49485 add(parent.children, connection, parentIndex);
49486
49487 this._canvas.addConnection(connection, parent);
49488
49489 return connection;
49490 };
49491
49492 function DeleteElementsHandler(modeling, elementRegistry) {
49493 this._modeling = modeling;
49494 this._elementRegistry = elementRegistry;
49495 }
49496
49497 DeleteElementsHandler.$inject = [
49498 'modeling',
49499 'elementRegistry'
49500 ];
49501
49502
49503 DeleteElementsHandler.prototype.postExecute = function(context) {
49504
49505 var modeling = this._modeling,
49506 elementRegistry = this._elementRegistry,
49507 elements = context.elements;
49508
49509 forEach(elements, function(element) {
49510
49511 // element may have been removed with previous
49512 // remove operations already (e.g. in case of nesting)
49513 if (!elementRegistry.get(element.id)) {
49514 return;
49515 }
49516
49517 if (element.waypoints) {
49518 modeling.removeConnection(element);
49519 } else {
49520 modeling.removeShape(element);
49521 }
49522 });
49523 };
49524
49525 /**
49526 * A handler that implements reversible deletion of shapes.
49527 *
49528 */
49529 function DeleteShapeHandler(canvas, modeling) {
49530 this._canvas = canvas;
49531 this._modeling = modeling;
49532 }
49533
49534 DeleteShapeHandler.$inject = [ 'canvas', 'modeling' ];
49535
49536
49537 /**
49538 * - Remove connections
49539 * - Remove all direct children
49540 */
49541 DeleteShapeHandler.prototype.preExecute = function(context) {
49542
49543 var modeling = this._modeling;
49544
49545 var shape = context.shape;
49546
49547 // remove connections
49548 saveClear(shape.incoming, function(connection) {
49549
49550 // To make sure that the connection isn't removed twice
49551 // For example if a container is removed
49552 modeling.removeConnection(connection, { nested: true });
49553 });
49554
49555 saveClear(shape.outgoing, function(connection) {
49556 modeling.removeConnection(connection, { nested: true });
49557 });
49558
49559 // remove child shapes and connections
49560 saveClear(shape.children, function(child) {
49561 if (isConnection$4(child)) {
49562 modeling.removeConnection(child, { nested: true });
49563 } else {
49564 modeling.removeShape(child, { nested: true });
49565 }
49566 });
49567 };
49568
49569 /**
49570 * Remove shape and remember the parent
49571 */
49572 DeleteShapeHandler.prototype.execute = function(context) {
49573 var canvas = this._canvas;
49574
49575 var shape = context.shape,
49576 oldParent = shape.parent;
49577
49578 context.oldParent = oldParent;
49579
49580 // remove containment
49581 context.oldParentIndex = indexOf(oldParent.children, shape);
49582
49583 // remove shape
49584 canvas.removeShape(shape);
49585
49586 return shape;
49587 };
49588
49589
49590 /**
49591 * Command revert implementation
49592 */
49593 DeleteShapeHandler.prototype.revert = function(context) {
49594
49595 var canvas = this._canvas;
49596
49597 var shape = context.shape,
49598 oldParent = context.oldParent,
49599 oldParentIndex = context.oldParentIndex;
49600
49601 // restore containment
49602 add(oldParent.children, shape, oldParentIndex);
49603
49604 canvas.addShape(shape, oldParent);
49605
49606 return shape;
49607 };
49608
49609 function isConnection$4(element) {
49610 return element.waypoints;
49611 }
49612
49613 /**
49614 * A handler that distributes elements evenly.
49615 */
49616 function DistributeElements(modeling) {
49617 this._modeling = modeling;
49618 }
49619
49620 DistributeElements.$inject = [ 'modeling' ];
49621
49622 var OFF_AXIS = {
49623 x: 'y',
49624 y: 'x'
49625 };
49626
49627 DistributeElements.prototype.preExecute = function(context) {
49628 var modeling = this._modeling;
49629
49630 var groups = context.groups,
49631 axis = context.axis,
49632 dimension = context.dimension;
49633
49634 function updateRange(group, element) {
49635 group.range.min = Math.min(element[axis], group.range.min);
49636 group.range.max = Math.max(element[axis] + element[dimension], group.range.max);
49637 }
49638
49639 function center(element) {
49640 return element[axis] + element[dimension] / 2;
49641 }
49642
49643 function lastIdx(arr) {
49644 return arr.length - 1;
49645 }
49646
49647 function rangeDiff(range) {
49648 return range.max - range.min;
49649 }
49650
49651 function centerElement(refCenter, element) {
49652 var delta = { y: 0 };
49653
49654 delta[axis] = refCenter - center(element);
49655
49656 if (delta[axis]) {
49657
49658 delta[OFF_AXIS[axis]] = 0;
49659
49660 modeling.moveElements([ element ], delta, element.parent);
49661 }
49662 }
49663
49664 var firstGroup = groups[0],
49665 lastGroupIdx = lastIdx(groups),
49666 lastGroup = groups[ lastGroupIdx ];
49667
49668 var margin,
49669 spaceInBetween,
49670 groupsSize = 0; // the size of each range
49671
49672 forEach(groups, function(group, idx) {
49673 var sortedElements,
49674 refElem,
49675 refCenter;
49676
49677 if (group.elements.length < 2) {
49678 if (idx && idx !== groups.length - 1) {
49679 updateRange(group, group.elements[0]);
49680
49681 groupsSize += rangeDiff(group.range);
49682 }
49683 return;
49684 }
49685
49686 sortedElements = sortBy(group.elements, axis);
49687
49688 refElem = sortedElements[0];
49689
49690 if (idx === lastGroupIdx) {
49691 refElem = sortedElements[lastIdx(sortedElements)];
49692 }
49693
49694 refCenter = center(refElem);
49695
49696 // wanna update the ranges after the shapes have been centered
49697 group.range = null;
49698
49699 forEach(sortedElements, function(element) {
49700
49701 centerElement(refCenter, element);
49702
49703 if (group.range === null) {
49704 group.range = {
49705 min: element[axis],
49706 max: element[axis] + element[dimension]
49707 };
49708
49709 return;
49710 }
49711
49712 // update group's range after centering the range elements
49713 updateRange(group, element);
49714 });
49715
49716 if (idx && idx !== groups.length - 1) {
49717 groupsSize += rangeDiff(group.range);
49718 }
49719 });
49720
49721 spaceInBetween = Math.abs(lastGroup.range.min - firstGroup.range.max);
49722
49723 margin = Math.round((spaceInBetween - groupsSize) / (groups.length - 1));
49724
49725 if (margin < groups.length - 1) {
49726 return;
49727 }
49728
49729 forEach(groups, function(group, groupIdx) {
49730 var delta = {},
49731 prevGroup;
49732
49733 if (group === firstGroup || group === lastGroup) {
49734 return;
49735 }
49736
49737 prevGroup = groups[groupIdx - 1];
49738
49739 group.range.max = 0;
49740
49741 forEach(group.elements, function(element, idx) {
49742 delta[OFF_AXIS[axis]] = 0;
49743 delta[axis] = (prevGroup.range.max - element[axis]) + margin;
49744
49745 if (group.range.min !== element[axis]) {
49746 delta[axis] += element[axis] - group.range.min;
49747 }
49748
49749 if (delta[axis]) {
49750 modeling.moveElements([ element ], delta, element.parent);
49751 }
49752
49753 group.range.max = Math.max(element[axis] + element[dimension], idx ? group.range.max : 0);
49754 });
49755 });
49756 };
49757
49758 DistributeElements.prototype.postExecute = function(context) {
49759
49760 };
49761
49762 /**
49763 * A handler that implements reversible moving of shapes.
49764 */
49765 function LayoutConnectionHandler(layouter, canvas) {
49766 this._layouter = layouter;
49767 this._canvas = canvas;
49768 }
49769
49770 LayoutConnectionHandler.$inject = [ 'layouter', 'canvas' ];
49771
49772 LayoutConnectionHandler.prototype.execute = function(context) {
49773
49774 var connection = context.connection;
49775
49776 var oldWaypoints = connection.waypoints;
49777
49778 assign(context, {
49779 oldWaypoints: oldWaypoints
49780 });
49781
49782 connection.waypoints = this._layouter.layoutConnection(connection, context.hints);
49783
49784 return connection;
49785 };
49786
49787 LayoutConnectionHandler.prototype.revert = function(context) {
49788
49789 var connection = context.connection;
49790
49791 connection.waypoints = context.oldWaypoints;
49792
49793 return connection;
49794 };
49795
49796 /**
49797 * A handler that implements reversible moving of connections.
49798 *
49799 * The handler differs from the layout connection handler in a sense
49800 * that it preserves the connection layout.
49801 */
49802 function MoveConnectionHandler() { }
49803
49804
49805 MoveConnectionHandler.prototype.execute = function(context) {
49806
49807 var connection = context.connection,
49808 delta = context.delta;
49809
49810 var newParent = context.newParent || connection.parent,
49811 newParentIndex = context.newParentIndex,
49812 oldParent = connection.parent;
49813
49814 // save old parent in context
49815 context.oldParent = oldParent;
49816 context.oldParentIndex = remove(oldParent.children, connection);
49817
49818 // add to new parent at position
49819 add(newParent.children, connection, newParentIndex);
49820
49821 // update parent
49822 connection.parent = newParent;
49823
49824 // update waypoint positions
49825 forEach(connection.waypoints, function(p) {
49826 p.x += delta.x;
49827 p.y += delta.y;
49828
49829 if (p.original) {
49830 p.original.x += delta.x;
49831 p.original.y += delta.y;
49832 }
49833 });
49834
49835 return connection;
49836 };
49837
49838 MoveConnectionHandler.prototype.revert = function(context) {
49839
49840 var connection = context.connection,
49841 newParent = connection.parent,
49842 oldParent = context.oldParent,
49843 oldParentIndex = context.oldParentIndex,
49844 delta = context.delta;
49845
49846 // remove from newParent
49847 remove(newParent.children, connection);
49848
49849 // restore previous location in old parent
49850 add(oldParent.children, connection, oldParentIndex);
49851
49852 // restore parent
49853 connection.parent = oldParent;
49854
49855 // revert to old waypoint positions
49856 forEach(connection.waypoints, function(p) {
49857 p.x -= delta.x;
49858 p.y -= delta.y;
49859
49860 if (p.original) {
49861 p.original.x -= delta.x;
49862 p.original.y -= delta.y;
49863 }
49864 });
49865
49866 return connection;
49867 };
49868
49869 function MoveClosure() {
49870
49871 this.allShapes = {};
49872 this.allConnections = {};
49873
49874 this.enclosedElements = {};
49875 this.enclosedConnections = {};
49876
49877 this.topLevel = {};
49878 }
49879
49880
49881 MoveClosure.prototype.add = function(element, isTopLevel) {
49882 return this.addAll([ element ], isTopLevel);
49883 };
49884
49885
49886 MoveClosure.prototype.addAll = function(elements, isTopLevel) {
49887
49888 var newClosure = getClosure(elements, !!isTopLevel, this);
49889
49890 assign(this, newClosure);
49891
49892 return this;
49893 };
49894
49895 /**
49896 * A helper that is able to carry out serialized move
49897 * operations on multiple elements.
49898 *
49899 * @param {Modeling} modeling
49900 */
49901 function MoveHelper(modeling) {
49902 this._modeling = modeling;
49903 }
49904
49905 /**
49906 * Move the specified elements and all children by the given delta.
49907 *
49908 * This moves all enclosed connections, too and layouts all affected
49909 * external connections.
49910 *
49911 * @param {Array<djs.model.Base>} elements
49912 * @param {Point} delta
49913 * @param {djs.model.Base} newParent applied to the first level of shapes
49914 *
49915 * @return {Array<djs.model.Base>} list of touched elements
49916 */
49917 MoveHelper.prototype.moveRecursive = function(elements, delta, newParent) {
49918 if (!elements) {
49919 return [];
49920 } else {
49921 return this.moveClosure(this.getClosure(elements), delta, newParent);
49922 }
49923 };
49924
49925 /**
49926 * Move the given closure of elmements.
49927 *
49928 * @param {Object} closure
49929 * @param {Point} delta
49930 * @param {djs.model.Base} [newParent]
49931 * @param {djs.model.Base} [newHost]
49932 */
49933 MoveHelper.prototype.moveClosure = function(closure, delta, newParent, newHost, primaryShape) {
49934 var modeling = this._modeling;
49935
49936 var allShapes = closure.allShapes,
49937 allConnections = closure.allConnections,
49938 enclosedConnections = closure.enclosedConnections,
49939 topLevel = closure.topLevel,
49940 keepParent = false;
49941
49942 if (primaryShape && primaryShape.parent === newParent) {
49943 keepParent = true;
49944 }
49945
49946 // move all shapes
49947 forEach(allShapes, function(shape) {
49948
49949 // move the element according to the given delta
49950 modeling.moveShape(shape, delta, topLevel[shape.id] && !keepParent && newParent, {
49951 recurse: false,
49952 layout: false
49953 });
49954 });
49955
49956 // move all child connections / layout external connections
49957 forEach(allConnections, function(c) {
49958
49959 var sourceMoved = !!allShapes[c.source.id],
49960 targetMoved = !!allShapes[c.target.id];
49961
49962 if (enclosedConnections[c.id] && sourceMoved && targetMoved) {
49963 modeling.moveConnection(c, delta, topLevel[c.id] && !keepParent && newParent);
49964 } else {
49965 modeling.layoutConnection(c, {
49966 connectionStart: sourceMoved && getMovedSourceAnchor(c, c.source, delta),
49967 connectionEnd: targetMoved && getMovedTargetAnchor(c, c.target, delta)
49968 });
49969 }
49970 });
49971 };
49972
49973 /**
49974 * Returns the closure for the selected elements
49975 *
49976 * @param {Array<djs.model.Base>} elements
49977 * @return {MoveClosure} closure
49978 */
49979 MoveHelper.prototype.getClosure = function(elements) {
49980 return new MoveClosure().addAll(elements, true);
49981 };
49982
49983 /**
49984 * A handler that implements reversible moving of shapes.
49985 */
49986 function MoveElementsHandler(modeling) {
49987 this._helper = new MoveHelper(modeling);
49988 }
49989
49990 MoveElementsHandler.$inject = [ 'modeling' ];
49991
49992 MoveElementsHandler.prototype.preExecute = function(context) {
49993 context.closure = this._helper.getClosure(context.shapes);
49994 };
49995
49996 MoveElementsHandler.prototype.postExecute = function(context) {
49997
49998 var hints = context.hints,
49999 primaryShape;
50000
50001 if (hints && hints.primaryShape) {
50002 primaryShape = hints.primaryShape;
50003 hints.oldParent = primaryShape.parent;
50004 }
50005
50006 this._helper.moveClosure(
50007 context.closure,
50008 context.delta,
50009 context.newParent,
50010 context.newHost,
50011 primaryShape
50012 );
50013 };
50014
50015 /**
50016 * A handler that implements reversible moving of shapes.
50017 */
50018 function MoveShapeHandler(modeling) {
50019 this._modeling = modeling;
50020
50021 this._helper = new MoveHelper(modeling);
50022 }
50023
50024 MoveShapeHandler.$inject = [ 'modeling' ];
50025
50026
50027 MoveShapeHandler.prototype.execute = function(context) {
50028
50029 var shape = context.shape,
50030 delta = context.delta,
50031 newParent = context.newParent || shape.parent,
50032 newParentIndex = context.newParentIndex,
50033 oldParent = shape.parent;
50034
50035 context.oldBounds = pick(shape, [ 'x', 'y', 'width', 'height']);
50036
50037 // save old parent in context
50038 context.oldParent = oldParent;
50039 context.oldParentIndex = remove(oldParent.children, shape);
50040
50041 // add to new parent at position
50042 add(newParent.children, shape, newParentIndex);
50043
50044 // update shape parent + position
50045 assign(shape, {
50046 parent: newParent,
50047 x: shape.x + delta.x,
50048 y: shape.y + delta.y
50049 });
50050
50051 return shape;
50052 };
50053
50054 MoveShapeHandler.prototype.postExecute = function(context) {
50055
50056 var shape = context.shape,
50057 delta = context.delta,
50058 hints = context.hints;
50059
50060 var modeling = this._modeling;
50061
50062 if (hints.layout !== false) {
50063
50064 forEach(shape.incoming, function(c) {
50065 modeling.layoutConnection(c, {
50066 connectionEnd: getMovedTargetAnchor(c, shape, delta)
50067 });
50068 });
50069
50070 forEach(shape.outgoing, function(c) {
50071 modeling.layoutConnection(c, {
50072 connectionStart: getMovedSourceAnchor(c, shape, delta)
50073 });
50074 });
50075 }
50076
50077 if (hints.recurse !== false) {
50078 this.moveChildren(context);
50079 }
50080 };
50081
50082 MoveShapeHandler.prototype.revert = function(context) {
50083
50084 var shape = context.shape,
50085 oldParent = context.oldParent,
50086 oldParentIndex = context.oldParentIndex,
50087 delta = context.delta;
50088
50089 // restore previous location in old parent
50090 add(oldParent.children, shape, oldParentIndex);
50091
50092 // revert to old position and parent
50093 assign(shape, {
50094 parent: oldParent,
50095 x: shape.x - delta.x,
50096 y: shape.y - delta.y
50097 });
50098
50099 return shape;
50100 };
50101
50102 MoveShapeHandler.prototype.moveChildren = function(context) {
50103
50104 var delta = context.delta,
50105 shape = context.shape;
50106
50107 this._helper.moveRecursive(shape.children, delta, null);
50108 };
50109
50110 MoveShapeHandler.prototype.getNewParent = function(context) {
50111 return context.newParent || context.shape.parent;
50112 };
50113
50114 /**
50115 * Reconnect connection handler
50116 */
50117 function ReconnectConnectionHandler(modeling) {
50118 this._modeling = modeling;
50119 }
50120
50121 ReconnectConnectionHandler.$inject = [ 'modeling' ];
50122
50123 ReconnectConnectionHandler.prototype.execute = function(context) {
50124 var newSource = context.newSource,
50125 newTarget = context.newTarget,
50126 connection = context.connection,
50127 dockingOrPoints = context.dockingOrPoints;
50128
50129 if (!newSource && !newTarget) {
50130 throw new Error('newSource or newTarget required');
50131 }
50132
50133 if (isArray$2(dockingOrPoints)) {
50134 context.oldWaypoints = connection.waypoints;
50135 connection.waypoints = dockingOrPoints;
50136 }
50137
50138 if (newSource) {
50139 context.oldSource = connection.source;
50140 connection.source = newSource;
50141 }
50142
50143 if (newTarget) {
50144 context.oldTarget = connection.target;
50145 connection.target = newTarget;
50146 }
50147
50148 return connection;
50149 };
50150
50151 ReconnectConnectionHandler.prototype.postExecute = function(context) {
50152 var connection = context.connection,
50153 newSource = context.newSource,
50154 newTarget = context.newTarget,
50155 dockingOrPoints = context.dockingOrPoints,
50156 hints = context.hints || {};
50157
50158 var layoutConnectionHints = {};
50159
50160 if (hints.connectionStart) {
50161 layoutConnectionHints.connectionStart = hints.connectionStart;
50162 }
50163
50164 if (hints.connectionEnd) {
50165 layoutConnectionHints.connectionEnd = hints.connectionEnd;
50166 }
50167
50168 if (hints.layoutConnection === false) {
50169 return;
50170 }
50171
50172 if (newSource && (!newTarget || hints.docking === 'source')) {
50173 layoutConnectionHints.connectionStart = layoutConnectionHints.connectionStart
50174 || getDocking(isArray$2(dockingOrPoints) ? dockingOrPoints[ 0 ] : dockingOrPoints);
50175 }
50176
50177 if (newTarget && (!newSource || hints.docking === 'target')) {
50178 layoutConnectionHints.connectionEnd = layoutConnectionHints.connectionEnd
50179 || getDocking(isArray$2(dockingOrPoints) ? dockingOrPoints[ dockingOrPoints.length - 1 ] : dockingOrPoints);
50180 }
50181
50182 if (hints.newWaypoints) {
50183 layoutConnectionHints.waypoints = hints.newWaypoints;
50184 }
50185
50186 this._modeling.layoutConnection(connection, layoutConnectionHints);
50187 };
50188
50189 ReconnectConnectionHandler.prototype.revert = function(context) {
50190 var oldSource = context.oldSource,
50191 oldTarget = context.oldTarget,
50192 oldWaypoints = context.oldWaypoints,
50193 connection = context.connection;
50194
50195 if (oldSource) {
50196 connection.source = oldSource;
50197 }
50198
50199 if (oldTarget) {
50200 connection.target = oldTarget;
50201 }
50202
50203 if (oldWaypoints) {
50204 connection.waypoints = oldWaypoints;
50205 }
50206
50207 return connection;
50208 };
50209
50210
50211
50212 // helpers //////////
50213
50214 function getDocking(point) {
50215 return point.original || point;
50216 }
50217
50218 /**
50219 * Replace shape by adding new shape and removing old shape. Incoming and outgoing connections will
50220 * be kept if possible.
50221 *
50222 * @class
50223 * @constructor
50224 *
50225 * @param {Modeling} modeling
50226 * @param {Rules} rules
50227 */
50228 function ReplaceShapeHandler(modeling, rules) {
50229 this._modeling = modeling;
50230 this._rules = rules;
50231 }
50232
50233 ReplaceShapeHandler.$inject = [ 'modeling', 'rules' ];
50234
50235
50236 /**
50237 * Add new shape.
50238 *
50239 * @param {Object} context
50240 * @param {djs.model.Shape} context.oldShape
50241 * @param {Object} context.newData
50242 * @param {string} context.newData.type
50243 * @param {number} context.newData.x
50244 * @param {number} context.newData.y
50245 * @param {Object} [hints]
50246 */
50247 ReplaceShapeHandler.prototype.preExecute = function(context) {
50248 var self = this,
50249 modeling = this._modeling,
50250 rules = this._rules;
50251
50252 var oldShape = context.oldShape,
50253 newData = context.newData,
50254 hints = context.hints || {},
50255 newShape;
50256
50257 function canReconnect(source, target, connection) {
50258 return rules.allowed('connection.reconnect', {
50259 connection: connection,
50260 source: source,
50261 target: target
50262 });
50263 }
50264
50265 // (1) add new shape at given position
50266 var position = {
50267 x: newData.x,
50268 y: newData.y
50269 };
50270
50271 var oldBounds = {
50272 x: oldShape.x,
50273 y: oldShape.y,
50274 width: oldShape.width,
50275 height: oldShape.height
50276 };
50277
50278 newShape = context.newShape =
50279 context.newShape ||
50280 self.createShape(newData, position, oldShape.parent, hints);
50281
50282 // (2) update host
50283 if (oldShape.host) {
50284 modeling.updateAttachment(newShape, oldShape.host);
50285 }
50286
50287 // (3) adopt all children from old shape
50288 var children;
50289
50290 if (hints.moveChildren !== false) {
50291 children = oldShape.children.slice();
50292
50293 modeling.moveElements(children, { x: 0, y: 0 }, newShape, hints);
50294 }
50295
50296 // (4) reconnect connections to new shape if possible
50297 var incoming = oldShape.incoming.slice(),
50298 outgoing = oldShape.outgoing.slice();
50299
50300 forEach(incoming, function(connection) {
50301 var source = connection.source,
50302 allowed = canReconnect(source, newShape, connection);
50303
50304 if (allowed) {
50305 self.reconnectEnd(
50306 connection, newShape,
50307 getResizedTargetAnchor(connection, newShape, oldBounds),
50308 hints
50309 );
50310 }
50311 });
50312
50313 forEach(outgoing, function(connection) {
50314 var target = connection.target,
50315 allowed = canReconnect(newShape, target, connection);
50316
50317 if (allowed) {
50318 self.reconnectStart(
50319 connection, newShape,
50320 getResizedSourceAnchor(connection, newShape, oldBounds),
50321 hints
50322 );
50323 }
50324 });
50325 };
50326
50327
50328 /**
50329 * Remove old shape.
50330 */
50331 ReplaceShapeHandler.prototype.postExecute = function(context) {
50332 var oldShape = context.oldShape;
50333
50334 this._modeling.removeShape(oldShape);
50335 };
50336
50337
50338 ReplaceShapeHandler.prototype.execute = function(context) {};
50339
50340
50341 ReplaceShapeHandler.prototype.revert = function(context) {};
50342
50343
50344 ReplaceShapeHandler.prototype.createShape = function(shape, position, target, hints) {
50345 return this._modeling.createShape(shape, position, target, hints);
50346 };
50347
50348
50349 ReplaceShapeHandler.prototype.reconnectStart = function(connection, newSource, dockingPoint, hints) {
50350 this._modeling.reconnectStart(connection, newSource, dockingPoint, hints);
50351 };
50352
50353
50354 ReplaceShapeHandler.prototype.reconnectEnd = function(connection, newTarget, dockingPoint, hints) {
50355 this._modeling.reconnectEnd(connection, newTarget, dockingPoint, hints);
50356 };
50357
50358 /**
50359 * A handler that implements reversible resizing of shapes.
50360 *
50361 * @param {Modeling} modeling
50362 */
50363 function ResizeShapeHandler(modeling) {
50364 this._modeling = modeling;
50365 }
50366
50367 ResizeShapeHandler.$inject = [ 'modeling' ];
50368
50369 /**
50370 * {
50371 * shape: {....}
50372 * newBounds: {
50373 * width: 20,
50374 * height: 40,
50375 * x: 5,
50376 * y: 10
50377 * }
50378 *
50379 * }
50380 */
50381 ResizeShapeHandler.prototype.execute = function(context) {
50382 var shape = context.shape,
50383 newBounds = context.newBounds,
50384 minBounds = context.minBounds;
50385
50386 if (newBounds.x === undefined || newBounds.y === undefined ||
50387 newBounds.width === undefined || newBounds.height === undefined) {
50388 throw new Error('newBounds must have {x, y, width, height} properties');
50389 }
50390
50391 if (minBounds && (newBounds.width < minBounds.width
50392 || newBounds.height < minBounds.height)) {
50393 throw new Error('width and height cannot be less than minimum height and width');
50394 } else if (!minBounds
50395 && newBounds.width < 10 || newBounds.height < 10) {
50396 throw new Error('width and height cannot be less than 10px');
50397 }
50398
50399 // save old bbox in context
50400 context.oldBounds = {
50401 width: shape.width,
50402 height: shape.height,
50403 x: shape.x,
50404 y: shape.y
50405 };
50406
50407 // update shape
50408 assign(shape, {
50409 width: newBounds.width,
50410 height: newBounds.height,
50411 x: newBounds.x,
50412 y: newBounds.y
50413 });
50414
50415 return shape;
50416 };
50417
50418 ResizeShapeHandler.prototype.postExecute = function(context) {
50419 var modeling = this._modeling;
50420
50421 var shape = context.shape,
50422 oldBounds = context.oldBounds,
50423 hints = context.hints || {};
50424
50425 if (hints.layout === false) {
50426 return;
50427 }
50428
50429 forEach(shape.incoming, function(c) {
50430 modeling.layoutConnection(c, {
50431 connectionEnd: getResizedTargetAnchor(c, shape, oldBounds)
50432 });
50433 });
50434
50435 forEach(shape.outgoing, function(c) {
50436 modeling.layoutConnection(c, {
50437 connectionStart: getResizedSourceAnchor(c, shape, oldBounds)
50438 });
50439 });
50440
50441 };
50442
50443 ResizeShapeHandler.prototype.revert = function(context) {
50444
50445 var shape = context.shape,
50446 oldBounds = context.oldBounds;
50447
50448 // restore previous bbox
50449 assign(shape, {
50450 width: oldBounds.width,
50451 height: oldBounds.height,
50452 x: oldBounds.x,
50453 y: oldBounds.y
50454 });
50455
50456 return shape;
50457 };
50458
50459 /**
50460 * Add or remove space by moving and resizing shapes and updating connection waypoints.
50461 */
50462 function SpaceToolHandler(modeling) {
50463 this._modeling = modeling;
50464 }
50465
50466 SpaceToolHandler.$inject = [ 'modeling' ];
50467
50468 SpaceToolHandler.prototype.preExecute = function(context) {
50469 var delta = context.delta,
50470 direction = context.direction,
50471 movingShapes = context.movingShapes,
50472 resizingShapes = context.resizingShapes,
50473 start = context.start,
50474 oldBounds = {};
50475
50476 // (1) move shapes
50477 this.moveShapes(movingShapes, delta);
50478
50479 // (2a) save old bounds of resized shapes
50480 forEach(resizingShapes, function(shape) {
50481 oldBounds[shape.id] = getBounds(shape);
50482 });
50483
50484 // (2b) resize shapes
50485 this.resizeShapes(resizingShapes, delta, direction);
50486
50487 // (3) update connection waypoints
50488 this.updateConnectionWaypoints(
50489 getWaypointsUpdatingConnections(movingShapes, resizingShapes),
50490 delta,
50491 direction,
50492 start,
50493 movingShapes,
50494 resizingShapes,
50495 oldBounds
50496 );
50497 };
50498
50499 SpaceToolHandler.prototype.execute = function() {};
50500 SpaceToolHandler.prototype.revert = function() {};
50501
50502 SpaceToolHandler.prototype.moveShapes = function(shapes, delta) {
50503 var self = this;
50504
50505 forEach(shapes, function(element) {
50506 self._modeling.moveShape(element, delta, null, {
50507 autoResize: false,
50508 layout: false,
50509 recurse: false
50510 });
50511 });
50512 };
50513
50514 SpaceToolHandler.prototype.resizeShapes = function(shapes, delta, direction) {
50515 var self = this;
50516
50517 forEach(shapes, function(shape) {
50518 var newBounds = resizeBounds(shape, direction, delta);
50519
50520 self._modeling.resizeShape(shape, newBounds, null, {
50521 attachSupport: false,
50522 autoResize: false,
50523 layout: false
50524 });
50525 });
50526 };
50527
50528 /**
50529 * Update connections waypoints according to the rules:
50530 * 1. Both source and target are moved/resized => move waypoints by the delta
50531 * 2. Only one of source and target is moved/resized => re-layout connection with moved start/end
50532 */
50533 SpaceToolHandler.prototype.updateConnectionWaypoints = function(
50534 connections,
50535 delta,
50536 direction,
50537 start,
50538 movingShapes,
50539 resizingShapes,
50540 oldBounds
50541 ) {
50542 var self = this,
50543 affectedShapes = movingShapes.concat(resizingShapes);
50544
50545 forEach(connections, function(connection) {
50546 var source = connection.source,
50547 target = connection.target,
50548 waypoints = copyWaypoints(connection),
50549 axis = getAxisFromDirection(direction),
50550 layoutHints = {
50551 labelBehavior: false
50552 };
50553
50554 if (includes$1(affectedShapes, source) && includes$1(affectedShapes, target)) {
50555
50556 // move waypoints
50557 waypoints = map$1(waypoints, function(waypoint) {
50558 if (shouldMoveWaypoint(waypoint, start, direction)) {
50559
50560 // move waypoint
50561 waypoint[ axis ] = waypoint[ axis ] + delta[ axis ];
50562 }
50563
50564 if (waypoint.original && shouldMoveWaypoint(waypoint.original, start, direction)) {
50565
50566 // move waypoint original
50567 waypoint.original[ axis ] = waypoint.original[ axis ] + delta[ axis ];
50568 }
50569
50570 return waypoint;
50571 });
50572
50573 self._modeling.updateWaypoints(connection, waypoints, {
50574 labelBehavior: false
50575 });
50576 } else if (includes$1(affectedShapes, source) || includes$1(affectedShapes, target)) {
50577
50578 // re-layout connection with moved start/end
50579 if (includes$1(movingShapes, source)) {
50580 layoutHints.connectionStart = getMovedSourceAnchor(connection, source, delta);
50581 } else if (includes$1(movingShapes, target)) {
50582 layoutHints.connectionEnd = getMovedTargetAnchor(connection, target, delta);
50583 } else if (includes$1(resizingShapes, source)) {
50584 layoutHints.connectionStart = getResizedSourceAnchor(
50585 connection, source, oldBounds[source.id]
50586 );
50587 } else if (includes$1(resizingShapes, target)) {
50588 layoutHints.connectionEnd = getResizedTargetAnchor(
50589 connection, target, oldBounds[target.id]
50590 );
50591 }
50592
50593 self._modeling.layoutConnection(connection, layoutHints);
50594 }
50595 });
50596 };
50597
50598
50599 // helpers //////////
50600
50601 function copyWaypoint(waypoint) {
50602 return assign({}, waypoint);
50603 }
50604
50605 function copyWaypoints(connection) {
50606 return map$1(connection.waypoints, function(waypoint) {
50607
50608 waypoint = copyWaypoint(waypoint);
50609
50610 if (waypoint.original) {
50611 waypoint.original = copyWaypoint(waypoint.original);
50612 }
50613
50614 return waypoint;
50615 });
50616 }
50617
50618 function getAxisFromDirection(direction) {
50619 switch (direction) {
50620 case 'n':
50621 return 'y';
50622 case 'w':
50623 return 'x';
50624 case 's':
50625 return 'y';
50626 case 'e':
50627 return 'x';
50628 }
50629 }
50630
50631 function shouldMoveWaypoint(waypoint, start, direction) {
50632 var relevantAxis = getAxisFromDirection(direction);
50633
50634 if (/e|s/.test(direction)) {
50635 return waypoint[ relevantAxis ] > start;
50636 } else if (/n|w/.test(direction)) {
50637 return waypoint[ relevantAxis ] < start;
50638 }
50639 }
50640
50641 function includes$1(array, item) {
50642 return array.indexOf(item) !== -1;
50643 }
50644
50645 function getBounds(shape) {
50646 return {
50647 x: shape.x,
50648 y: shape.y,
50649 height: shape.height,
50650 width: shape.width
50651 };
50652 }
50653
50654 /**
50655 * A handler that toggles the collapsed state of an element
50656 * and the visibility of all its children.
50657 *
50658 * @param {Modeling} modeling
50659 */
50660 function ToggleShapeCollapseHandler(modeling) {
50661 this._modeling = modeling;
50662 }
50663
50664 ToggleShapeCollapseHandler.$inject = [ 'modeling' ];
50665
50666
50667 ToggleShapeCollapseHandler.prototype.execute = function(context) {
50668
50669 var shape = context.shape,
50670 children = shape.children;
50671
50672 // recursively remember previous visibility of children
50673 context.oldChildrenVisibility = getElementsVisibilityRecursive(children);
50674
50675 // toggle state
50676 shape.collapsed = !shape.collapsed;
50677
50678 // recursively hide/show children
50679 var result = setHiddenRecursive(children, shape.collapsed);
50680
50681 return [shape].concat(result);
50682 };
50683
50684
50685 ToggleShapeCollapseHandler.prototype.revert = function(context) {
50686
50687 var shape = context.shape,
50688 oldChildrenVisibility = context.oldChildrenVisibility;
50689
50690 var children = shape.children;
50691
50692 // recursively set old visability of children
50693 var result = restoreVisibilityRecursive(children, oldChildrenVisibility);
50694
50695 // retoggle state
50696 shape.collapsed = !shape.collapsed;
50697
50698 return [shape].concat(result);
50699 };
50700
50701
50702 // helpers //////////////////////
50703
50704 /**
50705 * Return a map { elementId -> hiddenState}.
50706 *
50707 * @param {Array<djs.model.Shape>} elements
50708 *
50709 * @return {Object}
50710 */
50711 function getElementsVisibilityRecursive(elements) {
50712
50713 var result = {};
50714
50715 forEach(elements, function(element) {
50716 result[element.id] = element.hidden;
50717
50718 if (element.children) {
50719 result = assign({}, result, getElementsVisibilityRecursive(element.children));
50720 }
50721 });
50722
50723 return result;
50724 }
50725
50726
50727 function setHiddenRecursive(elements, newHidden) {
50728 var result = [];
50729 forEach(elements, function(element) {
50730 element.hidden = newHidden;
50731
50732 result = result.concat(element);
50733
50734 if (element.children) {
50735 result = result.concat(setHiddenRecursive(element.children, element.collapsed || newHidden));
50736 }
50737 });
50738
50739 return result;
50740 }
50741
50742 function restoreVisibilityRecursive(elements, lastState) {
50743 var result = [];
50744 forEach(elements, function(element) {
50745 element.hidden = lastState[element.id];
50746
50747 result = result.concat(element);
50748
50749 if (element.children) {
50750 result = result.concat(restoreVisibilityRecursive(element.children, lastState));
50751 }
50752 });
50753
50754 return result;
50755 }
50756
50757 /**
50758 * A handler that implements reversible attaching/detaching of shapes.
50759 */
50760 function UpdateAttachmentHandler(modeling) {
50761 this._modeling = modeling;
50762 }
50763
50764 UpdateAttachmentHandler.$inject = [ 'modeling' ];
50765
50766
50767 UpdateAttachmentHandler.prototype.execute = function(context) {
50768 var shape = context.shape,
50769 newHost = context.newHost,
50770 oldHost = shape.host;
50771
50772 // (0) detach from old host
50773 context.oldHost = oldHost;
50774 context.attacherIdx = removeAttacher(oldHost, shape);
50775
50776 // (1) attach to new host
50777 addAttacher(newHost, shape);
50778
50779 // (2) update host
50780 shape.host = newHost;
50781
50782 return shape;
50783 };
50784
50785 UpdateAttachmentHandler.prototype.revert = function(context) {
50786 var shape = context.shape,
50787 newHost = context.newHost,
50788 oldHost = context.oldHost,
50789 attacherIdx = context.attacherIdx;
50790
50791 // (2) update host
50792 shape.host = oldHost;
50793
50794 // (1) attach to new host
50795 removeAttacher(newHost, shape);
50796
50797 // (0) detach from old host
50798 addAttacher(oldHost, shape, attacherIdx);
50799
50800 return shape;
50801 };
50802
50803
50804 function removeAttacher(host, attacher) {
50805
50806 // remove attacher from host
50807 return remove(host && host.attachers, attacher);
50808 }
50809
50810 function addAttacher(host, attacher, idx) {
50811
50812 if (!host) {
50813 return;
50814 }
50815
50816 var attachers = host.attachers;
50817
50818 if (!attachers) {
50819 host.attachers = attachers = [];
50820 }
50821
50822 add(attachers, attacher, idx);
50823 }
50824
50825 function UpdateWaypointsHandler() { }
50826
50827 UpdateWaypointsHandler.prototype.execute = function(context) {
50828
50829 var connection = context.connection,
50830 newWaypoints = context.newWaypoints;
50831
50832 context.oldWaypoints = connection.waypoints;
50833
50834 connection.waypoints = newWaypoints;
50835
50836 return connection;
50837 };
50838
50839 UpdateWaypointsHandler.prototype.revert = function(context) {
50840
50841 var connection = context.connection,
50842 oldWaypoints = context.oldWaypoints;
50843
50844 connection.waypoints = oldWaypoints;
50845
50846 return connection;
50847 };
50848
50849 /**
50850 * The basic modeling entry point.
50851 *
50852 * @param {EventBus} eventBus
50853 * @param {ElementFactory} elementFactory
50854 * @param {CommandStack} commandStack
50855 */
50856 function Modeling$1(eventBus, elementFactory, commandStack) {
50857 this._eventBus = eventBus;
50858 this._elementFactory = elementFactory;
50859 this._commandStack = commandStack;
50860
50861 var self = this;
50862
50863 eventBus.on('diagram.init', function() {
50864
50865 // register modeling handlers
50866 self.registerHandlers(commandStack);
50867 });
50868 }
50869
50870 Modeling$1.$inject = [ 'eventBus', 'elementFactory', 'commandStack' ];
50871
50872
50873 Modeling$1.prototype.getHandlers = function() {
50874 return {
50875 'shape.append': AppendShapeHandler,
50876 'shape.create': CreateShapeHandler,
50877 'shape.delete': DeleteShapeHandler,
50878 'shape.move': MoveShapeHandler,
50879 'shape.resize': ResizeShapeHandler,
50880 'shape.replace': ReplaceShapeHandler,
50881 'shape.toggleCollapse': ToggleShapeCollapseHandler,
50882
50883 'spaceTool': SpaceToolHandler,
50884
50885 'label.create': CreateLabelHandler,
50886
50887 'connection.create': CreateConnectionHandler,
50888 'connection.delete': DeleteConnectionHandler,
50889 'connection.move': MoveConnectionHandler,
50890 'connection.layout': LayoutConnectionHandler,
50891
50892 'connection.updateWaypoints': UpdateWaypointsHandler,
50893
50894 'connection.reconnect': ReconnectConnectionHandler,
50895
50896 'elements.create': CreateElementsHandler,
50897 'elements.move': MoveElementsHandler,
50898 'elements.delete': DeleteElementsHandler,
50899
50900 'elements.distribute': DistributeElements,
50901 'elements.align': AlignElements,
50902
50903 'element.updateAttachment': UpdateAttachmentHandler
50904 };
50905 };
50906
50907 /**
50908 * Register handlers with the command stack
50909 *
50910 * @param {CommandStack} commandStack
50911 */
50912 Modeling$1.prototype.registerHandlers = function(commandStack) {
50913 forEach(this.getHandlers(), function(handler, id) {
50914 commandStack.registerHandler(id, handler);
50915 });
50916 };
50917
50918
50919 // modeling helpers //////////////////////
50920
50921 Modeling$1.prototype.moveShape = function(shape, delta, newParent, newParentIndex, hints) {
50922
50923 if (typeof newParentIndex === 'object') {
50924 hints = newParentIndex;
50925 newParentIndex = null;
50926 }
50927
50928 var context = {
50929 shape: shape,
50930 delta: delta,
50931 newParent: newParent,
50932 newParentIndex: newParentIndex,
50933 hints: hints || {}
50934 };
50935
50936 this._commandStack.execute('shape.move', context);
50937 };
50938
50939
50940 /**
50941 * Update the attachment of the given shape.
50942 *
50943 * @param {djs.mode.Base} shape
50944 * @param {djs.model.Base} [newHost]
50945 */
50946 Modeling$1.prototype.updateAttachment = function(shape, newHost) {
50947 var context = {
50948 shape: shape,
50949 newHost: newHost
50950 };
50951
50952 this._commandStack.execute('element.updateAttachment', context);
50953 };
50954
50955
50956 /**
50957 * Move a number of shapes to a new target, either setting it as
50958 * the new parent or attaching it.
50959 *
50960 * @param {Array<djs.mode.Base>} shapes
50961 * @param {Point} delta
50962 * @param {djs.model.Base} [target]
50963 * @param {Object} [hints]
50964 * @param {boolean} [hints.attach=false]
50965 */
50966 Modeling$1.prototype.moveElements = function(shapes, delta, target, hints) {
50967
50968 hints = hints || {};
50969
50970 var attach = hints.attach;
50971
50972 var newParent = target,
50973 newHost;
50974
50975 if (attach === true) {
50976 newHost = target;
50977 newParent = target.parent;
50978 } else
50979
50980 if (attach === false) {
50981 newHost = null;
50982 }
50983
50984 var context = {
50985 shapes: shapes,
50986 delta: delta,
50987 newParent: newParent,
50988 newHost: newHost,
50989 hints: hints
50990 };
50991
50992 this._commandStack.execute('elements.move', context);
50993 };
50994
50995
50996 Modeling$1.prototype.moveConnection = function(connection, delta, newParent, newParentIndex, hints) {
50997
50998 if (typeof newParentIndex === 'object') {
50999 hints = newParentIndex;
51000 newParentIndex = undefined;
51001 }
51002
51003 var context = {
51004 connection: connection,
51005 delta: delta,
51006 newParent: newParent,
51007 newParentIndex: newParentIndex,
51008 hints: hints || {}
51009 };
51010
51011 this._commandStack.execute('connection.move', context);
51012 };
51013
51014
51015 Modeling$1.prototype.layoutConnection = function(connection, hints) {
51016 var context = {
51017 connection: connection,
51018 hints: hints || {}
51019 };
51020
51021 this._commandStack.execute('connection.layout', context);
51022 };
51023
51024
51025 /**
51026 * Create connection.
51027 *
51028 * @param {djs.model.Base} source
51029 * @param {djs.model.Base} target
51030 * @param {number} [parentIndex]
51031 * @param {Object|djs.model.Connection} connection
51032 * @param {djs.model.Base} parent
51033 * @param {Object} hints
51034 *
51035 * @return {djs.model.Connection} the created connection.
51036 */
51037 Modeling$1.prototype.createConnection = function(source, target, parentIndex, connection, parent, hints) {
51038
51039 if (typeof parentIndex === 'object') {
51040 hints = parent;
51041 parent = connection;
51042 connection = parentIndex;
51043 parentIndex = undefined;
51044 }
51045
51046 connection = this._create('connection', connection);
51047
51048 var context = {
51049 source: source,
51050 target: target,
51051 parent: parent,
51052 parentIndex: parentIndex,
51053 connection: connection,
51054 hints: hints
51055 };
51056
51057 this._commandStack.execute('connection.create', context);
51058
51059 return context.connection;
51060 };
51061
51062
51063 /**
51064 * Create a shape at the specified position.
51065 *
51066 * @param {djs.model.Shape|Object} shape
51067 * @param {Point} position
51068 * @param {djs.model.Shape|djs.model.Root} target
51069 * @param {number} [parentIndex] position in parents children list
51070 * @param {Object} [hints]
51071 * @param {boolean} [hints.attach] whether to attach to target or become a child
51072 *
51073 * @return {djs.model.Shape} the created shape
51074 */
51075 Modeling$1.prototype.createShape = function(shape, position, target, parentIndex, hints) {
51076
51077 if (typeof parentIndex !== 'number') {
51078 hints = parentIndex;
51079 parentIndex = undefined;
51080 }
51081
51082 hints = hints || {};
51083
51084 var attach = hints.attach,
51085 parent,
51086 host;
51087
51088 shape = this._create('shape', shape);
51089
51090 if (attach) {
51091 parent = target.parent;
51092 host = target;
51093 } else {
51094 parent = target;
51095 }
51096
51097 var context = {
51098 position: position,
51099 shape: shape,
51100 parent: parent,
51101 parentIndex: parentIndex,
51102 host: host,
51103 hints: hints
51104 };
51105
51106 this._commandStack.execute('shape.create', context);
51107
51108 return context.shape;
51109 };
51110
51111
51112 Modeling$1.prototype.createElements = function(elements, position, parent, parentIndex, hints) {
51113 if (!isArray$2(elements)) {
51114 elements = [ elements ];
51115 }
51116
51117 if (typeof parentIndex !== 'number') {
51118 hints = parentIndex;
51119 parentIndex = undefined;
51120 }
51121
51122 hints = hints || {};
51123
51124 var context = {
51125 position: position,
51126 elements: elements,
51127 parent: parent,
51128 parentIndex: parentIndex,
51129 hints: hints
51130 };
51131
51132 this._commandStack.execute('elements.create', context);
51133
51134 return context.elements;
51135 };
51136
51137
51138 Modeling$1.prototype.createLabel = function(labelTarget, position, label, parent) {
51139
51140 label = this._create('label', label);
51141
51142 var context = {
51143 labelTarget: labelTarget,
51144 position: position,
51145 parent: parent || labelTarget.parent,
51146 shape: label
51147 };
51148
51149 this._commandStack.execute('label.create', context);
51150
51151 return context.shape;
51152 };
51153
51154
51155 /**
51156 * Append shape to given source, drawing a connection
51157 * between source and the newly created shape.
51158 *
51159 * @param {djs.model.Shape} source
51160 * @param {djs.model.Shape|Object} shape
51161 * @param {Point} position
51162 * @param {djs.model.Shape} target
51163 * @param {Object} [hints]
51164 * @param {boolean} [hints.attach]
51165 * @param {djs.model.Connection|Object} [hints.connection]
51166 * @param {djs.model.Base} [hints.connectionParent]
51167 *
51168 * @return {djs.model.Shape} the newly created shape
51169 */
51170 Modeling$1.prototype.appendShape = function(source, shape, position, target, hints) {
51171
51172 hints = hints || {};
51173
51174 shape = this._create('shape', shape);
51175
51176 var context = {
51177 source: source,
51178 position: position,
51179 target: target,
51180 shape: shape,
51181 connection: hints.connection,
51182 connectionParent: hints.connectionParent,
51183 hints: hints
51184 };
51185
51186 this._commandStack.execute('shape.append', context);
51187
51188 return context.shape;
51189 };
51190
51191
51192 Modeling$1.prototype.removeElements = function(elements) {
51193 var context = {
51194 elements: elements
51195 };
51196
51197 this._commandStack.execute('elements.delete', context);
51198 };
51199
51200
51201 Modeling$1.prototype.distributeElements = function(groups, axis, dimension) {
51202 var context = {
51203 groups: groups,
51204 axis: axis,
51205 dimension: dimension
51206 };
51207
51208 this._commandStack.execute('elements.distribute', context);
51209 };
51210
51211
51212 Modeling$1.prototype.removeShape = function(shape, hints) {
51213 var context = {
51214 shape: shape,
51215 hints: hints || {}
51216 };
51217
51218 this._commandStack.execute('shape.delete', context);
51219 };
51220
51221
51222 Modeling$1.prototype.removeConnection = function(connection, hints) {
51223 var context = {
51224 connection: connection,
51225 hints: hints || {}
51226 };
51227
51228 this._commandStack.execute('connection.delete', context);
51229 };
51230
51231 Modeling$1.prototype.replaceShape = function(oldShape, newShape, hints) {
51232 var context = {
51233 oldShape: oldShape,
51234 newData: newShape,
51235 hints: hints || {}
51236 };
51237
51238 this._commandStack.execute('shape.replace', context);
51239
51240 return context.newShape;
51241 };
51242
51243 Modeling$1.prototype.alignElements = function(elements, alignment) {
51244 var context = {
51245 elements: elements,
51246 alignment: alignment
51247 };
51248
51249 this._commandStack.execute('elements.align', context);
51250 };
51251
51252 Modeling$1.prototype.resizeShape = function(shape, newBounds, minBounds, hints) {
51253 var context = {
51254 shape: shape,
51255 newBounds: newBounds,
51256 minBounds: minBounds,
51257 hints: hints
51258 };
51259
51260 this._commandStack.execute('shape.resize', context);
51261 };
51262
51263 Modeling$1.prototype.createSpace = function(movingShapes, resizingShapes, delta, direction, start) {
51264 var context = {
51265 delta: delta,
51266 direction: direction,
51267 movingShapes: movingShapes,
51268 resizingShapes: resizingShapes,
51269 start: start
51270 };
51271
51272 this._commandStack.execute('spaceTool', context);
51273 };
51274
51275 Modeling$1.prototype.updateWaypoints = function(connection, newWaypoints, hints) {
51276 var context = {
51277 connection: connection,
51278 newWaypoints: newWaypoints,
51279 hints: hints || {}
51280 };
51281
51282 this._commandStack.execute('connection.updateWaypoints', context);
51283 };
51284
51285 Modeling$1.prototype.reconnect = function(connection, source, target, dockingOrPoints, hints) {
51286 var context = {
51287 connection: connection,
51288 newSource: source,
51289 newTarget: target,
51290 dockingOrPoints: dockingOrPoints,
51291 hints: hints || {}
51292 };
51293
51294 this._commandStack.execute('connection.reconnect', context);
51295 };
51296
51297 Modeling$1.prototype.reconnectStart = function(connection, newSource, dockingOrPoints, hints) {
51298 if (!hints) {
51299 hints = {};
51300 }
51301
51302 this.reconnect(connection, newSource, connection.target, dockingOrPoints, assign(hints, {
51303 docking: 'source'
51304 }));
51305 };
51306
51307 Modeling$1.prototype.reconnectEnd = function(connection, newTarget, dockingOrPoints, hints) {
51308 if (!hints) {
51309 hints = {};
51310 }
51311
51312 this.reconnect(connection, connection.source, newTarget, dockingOrPoints, assign(hints, {
51313 docking: 'target'
51314 }));
51315 };
51316
51317 Modeling$1.prototype.connect = function(source, target, attrs, hints) {
51318 return this.createConnection(source, target, attrs || {}, source.parent, hints);
51319 };
51320
51321 Modeling$1.prototype._create = function(type, attrs) {
51322 if (attrs instanceof Base$1) {
51323 return attrs;
51324 } else {
51325 return this._elementFactory.create(type, attrs);
51326 }
51327 };
51328
51329 Modeling$1.prototype.toggleCollapse = function(shape, hints) {
51330 var context = {
51331 shape: shape,
51332 hints: hints || {}
51333 };
51334
51335 this._commandStack.execute('shape.toggleCollapse', context);
51336 };
51337
51338 function UpdateModdlePropertiesHandler(elementRegistry) {
51339 this._elementRegistry = elementRegistry;
51340 }
51341
51342 UpdateModdlePropertiesHandler.$inject = ['elementRegistry'];
51343
51344 UpdateModdlePropertiesHandler.prototype.execute = function(context) {
51345
51346 var element = context.element,
51347 moddleElement = context.moddleElement,
51348 properties = context.properties;
51349
51350 if (!moddleElement) {
51351 throw new Error('<moddleElement> required');
51352 }
51353
51354 var changed = context.changed || this.getVisualReferences(moddleElement).concat(element);
51355 var oldProperties = context.oldProperties || getModdleProperties(moddleElement, keys(properties));
51356
51357 setModdleProperties(moddleElement, properties);
51358
51359 context.oldProperties = oldProperties;
51360 context.changed = changed;
51361
51362 return changed;
51363 };
51364
51365 UpdateModdlePropertiesHandler.prototype.revert = function(context) {
51366 var oldProperties = context.oldProperties,
51367 moddleElement = context.moddleElement,
51368 changed = context.changed;
51369
51370 setModdleProperties(moddleElement, oldProperties);
51371
51372 return changed;
51373 };
51374
51375 /**
51376 * Return visual references of given moddle element within the diagram.
51377 *
51378 * @param {ModdleElement} moddleElement
51379 *
51380 * @return {Array<djs.model.Element>}
51381 */
51382 UpdateModdlePropertiesHandler.prototype.getVisualReferences = function(moddleElement) {
51383
51384 var elementRegistry = this._elementRegistry;
51385
51386 if (is$1(moddleElement, 'bpmn:DataObject')) {
51387 return getAllDataObjectReferences(moddleElement, elementRegistry);
51388 }
51389
51390 return [];
51391 };
51392
51393
51394 // helpers /////////////////
51395
51396 function getModdleProperties(moddleElement, propertyNames) {
51397 return reduce(propertyNames, function(result, key) {
51398 result[key] = moddleElement.get(key);
51399 return result;
51400 }, {});
51401 }
51402
51403 function setModdleProperties(moddleElement, properties) {
51404 forEach(properties, function(value, key) {
51405 moddleElement.set(key, value);
51406 });
51407 }
51408
51409 function getAllDataObjectReferences(dataObject, elementRegistry) {
51410 return elementRegistry.filter(function(element) {
51411 return (
51412 is$1(element, 'bpmn:DataObjectReference') &&
51413 getBusinessObject(element).dataObjectRef === dataObject
51414 );
51415 });
51416 }
51417
51418 var DEFAULT_FLOW = 'default',
51419 ID = 'id',
51420 DI = 'di';
51421
51422 var NULL_DIMENSIONS$1 = {
51423 width: 0,
51424 height: 0
51425 };
51426
51427 /**
51428 * A handler that implements a BPMN 2.0 property update.
51429 *
51430 * This should be used to set simple properties on elements with
51431 * an underlying BPMN business object.
51432 *
51433 * Use respective diagram-js provided handlers if you would
51434 * like to perform automated modeling.
51435 */
51436 function UpdatePropertiesHandler(
51437 elementRegistry, moddle, translate,
51438 modeling, textRenderer) {
51439
51440 this._elementRegistry = elementRegistry;
51441 this._moddle = moddle;
51442 this._translate = translate;
51443 this._modeling = modeling;
51444 this._textRenderer = textRenderer;
51445 }
51446
51447 UpdatePropertiesHandler.$inject = [
51448 'elementRegistry',
51449 'moddle',
51450 'translate',
51451 'modeling',
51452 'textRenderer'
51453 ];
51454
51455
51456 // api //////////////////////
51457
51458 /**
51459 * Updates a BPMN element with a list of new properties
51460 *
51461 * @param {Object} context
51462 * @param {djs.model.Base} context.element the element to update
51463 * @param {Object} context.properties a list of properties to set on the element's
51464 * businessObject (the BPMN model element)
51465 *
51466 * @return {Array<djs.model.Base>} the updated element
51467 */
51468 UpdatePropertiesHandler.prototype.execute = function(context) {
51469
51470 var element = context.element,
51471 changed = [ element ],
51472 translate = this._translate;
51473
51474 if (!element) {
51475 throw new Error(translate('element required'));
51476 }
51477
51478 var elementRegistry = this._elementRegistry,
51479 ids = this._moddle.ids;
51480
51481 var businessObject = element.businessObject,
51482 properties = unwrapBusinessObjects(context.properties),
51483 oldProperties = context.oldProperties || getProperties(businessObject, properties);
51484
51485 if (isIdChange(properties, businessObject)) {
51486 ids.unclaim(businessObject[ID]);
51487
51488 elementRegistry.updateId(element, properties[ID]);
51489
51490 ids.claim(properties[ID], businessObject);
51491 }
51492
51493 // correctly indicate visual changes on default flow updates
51494 if (DEFAULT_FLOW in properties) {
51495
51496 if (properties[DEFAULT_FLOW]) {
51497 changed.push(elementRegistry.get(properties[DEFAULT_FLOW].id));
51498 }
51499
51500 if (businessObject[DEFAULT_FLOW]) {
51501 changed.push(elementRegistry.get(businessObject[DEFAULT_FLOW].id));
51502 }
51503 }
51504
51505 // update properties
51506 setProperties(businessObject, properties);
51507
51508 // store old values
51509 context.oldProperties = oldProperties;
51510 context.changed = changed;
51511
51512 // indicate changed on objects affected by the update
51513 return changed;
51514 };
51515
51516
51517 UpdatePropertiesHandler.prototype.postExecute = function(context) {
51518 var element = context.element,
51519 label = element.label;
51520
51521 var text = label && getBusinessObject(label).name;
51522
51523 if (!text) {
51524 return;
51525 }
51526
51527 // get layouted text bounds and resize external
51528 // external label accordingly
51529 var newLabelBounds = this._textRenderer.getExternalLabelBounds(label, text);
51530
51531 this._modeling.resizeShape(label, newLabelBounds, NULL_DIMENSIONS$1);
51532 };
51533
51534 /**
51535 * Reverts the update on a BPMN elements properties.
51536 *
51537 * @param {Object} context
51538 *
51539 * @return {djs.model.Base} the updated element
51540 */
51541 UpdatePropertiesHandler.prototype.revert = function(context) {
51542
51543 var element = context.element,
51544 properties = context.properties,
51545 oldProperties = context.oldProperties,
51546 businessObject = element.businessObject,
51547 elementRegistry = this._elementRegistry,
51548 ids = this._moddle.ids;
51549
51550 // update properties
51551 setProperties(businessObject, oldProperties);
51552
51553 if (isIdChange(properties, businessObject)) {
51554 ids.unclaim(properties[ID]);
51555
51556 elementRegistry.updateId(element, oldProperties[ID]);
51557
51558 ids.claim(oldProperties[ID], businessObject);
51559 }
51560
51561 return context.changed;
51562 };
51563
51564
51565 function isIdChange(properties, businessObject) {
51566 return ID in properties && properties[ID] !== businessObject[ID];
51567 }
51568
51569
51570 function getProperties(businessObject, properties) {
51571 var propertyNames = keys(properties);
51572
51573 return reduce(propertyNames, function(result, key) {
51574
51575 // handle DI separately
51576 if (key !== DI) {
51577 result[key] = businessObject.get(key);
51578 } else {
51579 result[key] = getDiProperties(businessObject.di, keys(properties.di));
51580 }
51581
51582 return result;
51583 }, {});
51584 }
51585
51586
51587 function getDiProperties(di, propertyNames) {
51588 return reduce(propertyNames, function(result, key) {
51589 result[key] = di.get(key);
51590
51591 return result;
51592 }, {});
51593 }
51594
51595
51596 function setProperties(businessObject, properties) {
51597 forEach(properties, function(value, key) {
51598
51599 if (key !== DI) {
51600 businessObject.set(key, value);
51601 } else {
51602
51603 // only update, if businessObject.di exists
51604 if (businessObject.di) {
51605 setDiProperties(businessObject.di, value);
51606 }
51607 }
51608 });
51609 }
51610
51611
51612 function setDiProperties(di, properties) {
51613 forEach(properties, function(value, key) {
51614 di.set(key, value);
51615 });
51616 }
51617
51618
51619 var referencePropertyNames = [ 'default' ];
51620
51621 /**
51622 * Make sure we unwrap the actual business object
51623 * behind diagram element that may have been
51624 * passed as arguments.
51625 *
51626 * @param {Object} properties
51627 *
51628 * @return {Object} unwrappedProps
51629 */
51630 function unwrapBusinessObjects(properties) {
51631
51632 var unwrappedProps = assign({}, properties);
51633
51634 referencePropertyNames.forEach(function(name) {
51635 if (name in properties) {
51636 unwrappedProps[name] = getBusinessObject(unwrappedProps[name]);
51637 }
51638 });
51639
51640 return unwrappedProps;
51641 }
51642
51643 function UpdateCanvasRootHandler(canvas, modeling) {
51644 this._canvas = canvas;
51645 this._modeling = modeling;
51646 }
51647
51648 UpdateCanvasRootHandler.$inject = [
51649 'canvas',
51650 'modeling'
51651 ];
51652
51653
51654 UpdateCanvasRootHandler.prototype.execute = function(context) {
51655
51656 var canvas = this._canvas;
51657
51658 var newRoot = context.newRoot,
51659 newRootBusinessObject = newRoot.businessObject,
51660 oldRoot = canvas.getRootElement(),
51661 oldRootBusinessObject = oldRoot.businessObject,
51662 bpmnDefinitions = oldRootBusinessObject.$parent,
51663 diPlane = oldRootBusinessObject.di;
51664
51665 // (1) replace process old <> new root
51666 canvas.setRootElement(newRoot, true);
51667
51668 // (2) update root elements
51669 add(bpmnDefinitions.rootElements, newRootBusinessObject);
51670 newRootBusinessObject.$parent = bpmnDefinitions;
51671
51672 remove(bpmnDefinitions.rootElements, oldRootBusinessObject);
51673 oldRootBusinessObject.$parent = null;
51674
51675 // (3) wire di
51676 oldRootBusinessObject.di = null;
51677
51678 diPlane.bpmnElement = newRootBusinessObject;
51679 newRootBusinessObject.di = diPlane;
51680
51681 context.oldRoot = oldRoot;
51682
51683 // TODO(nikku): return changed elements?
51684 // return [ newRoot, oldRoot ];
51685 };
51686
51687
51688 UpdateCanvasRootHandler.prototype.revert = function(context) {
51689
51690 var canvas = this._canvas;
51691
51692 var newRoot = context.newRoot,
51693 newRootBusinessObject = newRoot.businessObject,
51694 oldRoot = context.oldRoot,
51695 oldRootBusinessObject = oldRoot.businessObject,
51696 bpmnDefinitions = newRootBusinessObject.$parent,
51697 diPlane = newRootBusinessObject.di;
51698
51699 // (1) replace process old <> new root
51700 canvas.setRootElement(oldRoot, true);
51701
51702 // (2) update root elements
51703 remove(bpmnDefinitions.rootElements, newRootBusinessObject);
51704 newRootBusinessObject.$parent = null;
51705
51706 add(bpmnDefinitions.rootElements, oldRootBusinessObject);
51707 oldRootBusinessObject.$parent = bpmnDefinitions;
51708
51709 // (3) wire di
51710 newRootBusinessObject.di = null;
51711
51712 diPlane.bpmnElement = oldRootBusinessObject;
51713 oldRootBusinessObject.di = diPlane;
51714
51715 // TODO(nikku): return changed elements?
51716 // return [ newRoot, oldRoot ];
51717 };
51718
51719 /**
51720 * A handler that allows us to add a new lane
51721 * above or below an existing one.
51722 *
51723 * @param {Modeling} modeling
51724 * @param {SpaceTool} spaceTool
51725 */
51726 function AddLaneHandler(modeling, spaceTool) {
51727 this._modeling = modeling;
51728 this._spaceTool = spaceTool;
51729 }
51730
51731 AddLaneHandler.$inject = [
51732 'modeling',
51733 'spaceTool'
51734 ];
51735
51736
51737 AddLaneHandler.prototype.preExecute = function(context) {
51738
51739 var spaceTool = this._spaceTool,
51740 modeling = this._modeling;
51741
51742 var shape = context.shape,
51743 location = context.location;
51744
51745 var lanesRoot = getLanesRoot(shape);
51746
51747 var isRoot = lanesRoot === shape,
51748 laneParent = isRoot ? shape : shape.parent;
51749
51750 var existingChildLanes = getChildLanes(laneParent);
51751
51752 // (0) add a lane if we currently got none and are adding to root
51753 if (!existingChildLanes.length) {
51754 modeling.createShape({ type: 'bpmn:Lane' }, {
51755 x: shape.x + LANE_INDENTATION,
51756 y: shape.y,
51757 width: shape.width - LANE_INDENTATION,
51758 height: shape.height
51759 }, laneParent);
51760 }
51761
51762 // (1) collect affected elements to create necessary space
51763 var allAffected = [];
51764
51765 eachElement(lanesRoot, function(element) {
51766 allAffected.push(element);
51767
51768 // handle element labels in the diagram root
51769 if (element.label) {
51770 allAffected.push(element.label);
51771 }
51772
51773 if (element === shape) {
51774 return [];
51775 }
51776
51777 return filter(element.children, function(c) {
51778 return c !== shape;
51779 });
51780 });
51781
51782 var offset = location === 'top' ? -120 : 120,
51783 lanePosition = location === 'top' ? shape.y : shape.y + shape.height,
51784 spacePos = lanePosition + (location === 'top' ? 10 : -10),
51785 direction = location === 'top' ? 'n' : 's';
51786
51787 var adjustments = spaceTool.calculateAdjustments(allAffected, 'y', offset, spacePos);
51788
51789 spaceTool.makeSpace(
51790 adjustments.movingShapes,
51791 adjustments.resizingShapes,
51792 { x: 0, y: offset },
51793 direction,
51794 spacePos
51795 );
51796
51797 // (2) create new lane at open space
51798 context.newLane = modeling.createShape({ type: 'bpmn:Lane' }, {
51799 x: shape.x + (isRoot ? LANE_INDENTATION : 0),
51800 y: lanePosition - (location === 'top' ? 120 : 0),
51801 width: shape.width - (isRoot ? LANE_INDENTATION : 0),
51802 height: 120
51803 }, laneParent);
51804 };
51805
51806 /**
51807 * A handler that splits a lane into a number of sub-lanes,
51808 * creating new sub lanes, if necessary.
51809 *
51810 * @param {Modeling} modeling
51811 */
51812 function SplitLaneHandler(modeling, translate) {
51813 this._modeling = modeling;
51814 this._translate = translate;
51815 }
51816
51817 SplitLaneHandler.$inject = [
51818 'modeling',
51819 'translate'
51820 ];
51821
51822
51823 SplitLaneHandler.prototype.preExecute = function(context) {
51824
51825 var modeling = this._modeling,
51826 translate = this._translate;
51827
51828 var shape = context.shape,
51829 newLanesCount = context.count;
51830
51831 var childLanes = getChildLanes(shape),
51832 existingLanesCount = childLanes.length;
51833
51834 if (existingLanesCount > newLanesCount) {
51835 throw new Error(translate('more than {count} child lanes', { count: newLanesCount }));
51836 }
51837
51838 var newLanesHeight = Math.round(shape.height / newLanesCount);
51839
51840 // Iterate from top to bottom in child lane order,
51841 // resizing existing lanes and creating new ones
51842 // so that they split the parent proportionally.
51843 //
51844 // Due to rounding related errors, the bottom lane
51845 // needs to take up all the remaining space.
51846 var laneY,
51847 laneHeight,
51848 laneBounds,
51849 newLaneAttrs,
51850 idx;
51851
51852 for (idx = 0; idx < newLanesCount; idx++) {
51853
51854 laneY = shape.y + idx * newLanesHeight;
51855
51856 // if bottom lane
51857 if (idx === newLanesCount - 1) {
51858 laneHeight = shape.height - (newLanesHeight * idx);
51859 } else {
51860 laneHeight = newLanesHeight;
51861 }
51862
51863 laneBounds = {
51864 x: shape.x + LANE_INDENTATION,
51865 y: laneY,
51866 width: shape.width - LANE_INDENTATION,
51867 height: laneHeight
51868 };
51869
51870 if (idx < existingLanesCount) {
51871
51872 // resize existing lane
51873 modeling.resizeShape(childLanes[idx], laneBounds);
51874 } else {
51875
51876 // create a new lane at position
51877 newLaneAttrs = {
51878 type: 'bpmn:Lane'
51879 };
51880
51881 modeling.createShape(newLaneAttrs, laneBounds, shape);
51882 }
51883 }
51884 };
51885
51886 /**
51887 * A handler that resizes a lane.
51888 *
51889 * @param {Modeling} modeling
51890 */
51891 function ResizeLaneHandler(modeling, spaceTool) {
51892 this._modeling = modeling;
51893 this._spaceTool = spaceTool;
51894 }
51895
51896 ResizeLaneHandler.$inject = [
51897 'modeling',
51898 'spaceTool'
51899 ];
51900
51901
51902 ResizeLaneHandler.prototype.preExecute = function(context) {
51903
51904 var shape = context.shape,
51905 newBounds = context.newBounds,
51906 balanced = context.balanced;
51907
51908 if (balanced !== false) {
51909 this.resizeBalanced(shape, newBounds);
51910 } else {
51911 this.resizeSpace(shape, newBounds);
51912 }
51913 };
51914
51915
51916 /**
51917 * Resize balanced, adjusting next / previous lane sizes.
51918 *
51919 * @param {djs.model.Shape} shape
51920 * @param {Bounds} newBounds
51921 */
51922 ResizeLaneHandler.prototype.resizeBalanced = function(shape, newBounds) {
51923
51924 var modeling = this._modeling;
51925
51926 var resizeNeeded = computeLanesResize(shape, newBounds);
51927
51928 // resize the lane
51929 modeling.resizeShape(shape, newBounds);
51930
51931 // resize other lanes as needed
51932 resizeNeeded.forEach(function(r) {
51933 modeling.resizeShape(r.shape, r.newBounds);
51934 });
51935 };
51936
51937
51938 /**
51939 * Resize, making actual space and moving below / above elements.
51940 *
51941 * @param {djs.model.Shape} shape
51942 * @param {Bounds} newBounds
51943 */
51944 ResizeLaneHandler.prototype.resizeSpace = function(shape, newBounds) {
51945 var spaceTool = this._spaceTool;
51946
51947 var shapeTrbl = asTRBL(shape),
51948 newTrbl = asTRBL(newBounds);
51949
51950 var trblDiff = substractTRBL(newTrbl, shapeTrbl);
51951
51952 var lanesRoot = getLanesRoot(shape);
51953
51954 var allAffected = [],
51955 allLanes = [];
51956
51957 eachElement(lanesRoot, function(element) {
51958 allAffected.push(element);
51959
51960 if (is$1(element, 'bpmn:Lane') || is$1(element, 'bpmn:Participant')) {
51961 allLanes.push(element);
51962 }
51963
51964 return element.children;
51965 });
51966
51967 var change,
51968 spacePos,
51969 direction,
51970 offset,
51971 adjustments;
51972
51973 if (trblDiff.bottom || trblDiff.top) {
51974
51975 change = trblDiff.bottom || trblDiff.top;
51976 spacePos = shape.y + (trblDiff.bottom ? shape.height : 0) + (trblDiff.bottom ? -10 : 10);
51977 direction = trblDiff.bottom ? 's' : 'n';
51978
51979 offset = trblDiff.top > 0 || trblDiff.bottom < 0 ? -change : change;
51980
51981 adjustments = spaceTool.calculateAdjustments(allAffected, 'y', offset, spacePos);
51982
51983 spaceTool.makeSpace(adjustments.movingShapes, adjustments.resizingShapes, { x: 0, y: change }, direction);
51984 }
51985
51986
51987 if (trblDiff.left || trblDiff.right) {
51988
51989 change = trblDiff.right || trblDiff.left;
51990 spacePos = shape.x + (trblDiff.right ? shape.width : 0) + (trblDiff.right ? -10 : 100);
51991 direction = trblDiff.right ? 'e' : 'w';
51992
51993 offset = trblDiff.left > 0 || trblDiff.right < 0 ? -change : change;
51994
51995 adjustments = spaceTool.calculateAdjustments(allLanes, 'x', offset, spacePos);
51996
51997 spaceTool.makeSpace(adjustments.movingShapes, adjustments.resizingShapes, { x: change, y: 0 }, direction);
51998 }
51999 };
52000
52001 var FLOW_NODE_REFS_ATTR = 'flowNodeRef',
52002 LANES_ATTR = 'lanes';
52003
52004
52005 /**
52006 * A handler that updates lane refs on changed elements
52007 */
52008 function UpdateFlowNodeRefsHandler(elementRegistry) {
52009 this._elementRegistry = elementRegistry;
52010 }
52011
52012 UpdateFlowNodeRefsHandler.$inject = [
52013 'elementRegistry'
52014 ];
52015
52016
52017 UpdateFlowNodeRefsHandler.prototype.computeUpdates = function(flowNodeShapes, laneShapes) {
52018
52019 var handledNodes = [];
52020
52021 var updates = [];
52022
52023 var participantCache = {};
52024
52025 var allFlowNodeShapes = [];
52026
52027 function isInLaneShape(element, laneShape) {
52028
52029 var laneTrbl = asTRBL(laneShape);
52030
52031 var elementMid = {
52032 x: element.x + element.width / 2,
52033 y: element.y + element.height / 2
52034 };
52035
52036 return elementMid.x > laneTrbl.left &&
52037 elementMid.x < laneTrbl.right &&
52038 elementMid.y > laneTrbl.top &&
52039 elementMid.y < laneTrbl.bottom;
52040 }
52041
52042 function addFlowNodeShape(flowNodeShape) {
52043 if (handledNodes.indexOf(flowNodeShape) === -1) {
52044 allFlowNodeShapes.push(flowNodeShape);
52045 handledNodes.push(flowNodeShape);
52046 }
52047 }
52048
52049 function getAllLaneShapes(flowNodeShape) {
52050
52051 var root = getLanesRoot(flowNodeShape);
52052
52053 if (!participantCache[root.id]) {
52054 participantCache[root.id] = collectLanes(root);
52055 }
52056
52057 return participantCache[root.id];
52058 }
52059
52060 function getNewLanes(flowNodeShape) {
52061 if (!flowNodeShape.parent) {
52062 return [];
52063 }
52064
52065 var allLaneShapes = getAllLaneShapes(flowNodeShape);
52066
52067 return allLaneShapes.filter(function(l) {
52068 return isInLaneShape(flowNodeShape, l);
52069 }).map(function(shape) {
52070 return shape.businessObject;
52071 });
52072 }
52073
52074 laneShapes.forEach(function(laneShape) {
52075 var root = getLanesRoot(laneShape);
52076
52077 if (!root || handledNodes.indexOf(root) !== -1) {
52078 return;
52079 }
52080
52081 var children = root.children.filter(function(c) {
52082 return is$1(c, 'bpmn:FlowNode');
52083 });
52084
52085 children.forEach(addFlowNodeShape);
52086
52087 handledNodes.push(root);
52088 });
52089
52090 flowNodeShapes.forEach(addFlowNodeShape);
52091
52092
52093 allFlowNodeShapes.forEach(function(flowNodeShape) {
52094
52095 var flowNode = flowNodeShape.businessObject;
52096
52097 var lanes = flowNode.get(LANES_ATTR),
52098 remove = lanes.slice(),
52099 add = getNewLanes(flowNodeShape);
52100
52101 updates.push({ flowNode: flowNode, remove: remove, add: add });
52102 });
52103
52104 laneShapes.forEach(function(laneShape) {
52105
52106 var lane = laneShape.businessObject;
52107
52108 // lane got removed XX-)
52109 if (!laneShape.parent) {
52110 lane.get(FLOW_NODE_REFS_ATTR).forEach(function(flowNode) {
52111 updates.push({ flowNode: flowNode, remove: [ lane ], add: [] });
52112 });
52113 }
52114 });
52115
52116 return updates;
52117 };
52118
52119 UpdateFlowNodeRefsHandler.prototype.execute = function(context) {
52120
52121 var updates = context.updates;
52122
52123 if (!updates) {
52124 updates = context.updates = this.computeUpdates(context.flowNodeShapes, context.laneShapes);
52125 }
52126
52127
52128 updates.forEach(function(update) {
52129
52130 var flowNode = update.flowNode,
52131 lanes = flowNode.get(LANES_ATTR);
52132
52133 // unwire old
52134 update.remove.forEach(function(oldLane) {
52135 remove(lanes, oldLane);
52136 remove(oldLane.get(FLOW_NODE_REFS_ATTR), flowNode);
52137 });
52138
52139 // wire new
52140 update.add.forEach(function(newLane) {
52141 add(lanes, newLane);
52142 add(newLane.get(FLOW_NODE_REFS_ATTR), flowNode);
52143 });
52144 });
52145
52146 // TODO(nikku): return changed elements
52147 // return [ ... ];
52148 };
52149
52150
52151 UpdateFlowNodeRefsHandler.prototype.revert = function(context) {
52152
52153 var updates = context.updates;
52154
52155 updates.forEach(function(update) {
52156
52157 var flowNode = update.flowNode,
52158 lanes = flowNode.get(LANES_ATTR);
52159
52160 // unwire new
52161 update.add.forEach(function(newLane) {
52162 remove(lanes, newLane);
52163 remove(newLane.get(FLOW_NODE_REFS_ATTR), flowNode);
52164 });
52165
52166 // wire old
52167 update.remove.forEach(function(oldLane) {
52168 add(lanes, oldLane);
52169 add(oldLane.get(FLOW_NODE_REFS_ATTR), flowNode);
52170 });
52171 });
52172
52173 // TODO(nikku): return changed elements
52174 // return [ ... ];
52175 };
52176
52177 function IdClaimHandler(moddle) {
52178 this._moddle = moddle;
52179 }
52180
52181 IdClaimHandler.$inject = [ 'moddle' ];
52182
52183
52184 IdClaimHandler.prototype.execute = function(context) {
52185 var ids = this._moddle.ids,
52186 id = context.id,
52187 element = context.element,
52188 claiming = context.claiming;
52189
52190 if (claiming) {
52191 ids.claim(id, element);
52192 } else {
52193 ids.unclaim(id);
52194 }
52195 };
52196
52197 /**
52198 * Command revert implementation.
52199 */
52200 IdClaimHandler.prototype.revert = function(context) {
52201 var ids = this._moddle.ids,
52202 id = context.id,
52203 element = context.element,
52204 claiming = context.claiming;
52205
52206 if (claiming) {
52207 ids.unclaim(id);
52208 } else {
52209 ids.claim(id, element);
52210 }
52211 };
52212
52213 var DEFAULT_COLORS = {
52214 fill: undefined,
52215 stroke: undefined
52216 };
52217
52218
52219 function SetColorHandler(commandStack) {
52220 this._commandStack = commandStack;
52221
52222 this._normalizeColor = function(color) {
52223
52224 // Remove color for falsy values.
52225 if (!color) {
52226 return undefined;
52227 }
52228
52229 if (isString(color)) {
52230 var hexColor = colorToHex(color);
52231
52232 if (hexColor) {
52233 return hexColor;
52234 }
52235 }
52236
52237 throw new Error('invalid color value: ' + color);
52238 };
52239 }
52240
52241 SetColorHandler.$inject = [
52242 'commandStack'
52243 ];
52244
52245
52246 SetColorHandler.prototype.postExecute = function(context) {
52247 var elements = context.elements,
52248 colors = context.colors || DEFAULT_COLORS;
52249
52250 var self = this;
52251
52252 var di = {};
52253
52254 if ('fill' in colors) {
52255 assign(di, {
52256 'background-color': this._normalizeColor(colors.fill) });
52257 }
52258
52259 if ('stroke' in colors) {
52260 assign(di, {
52261 'border-color': this._normalizeColor(colors.stroke) });
52262 }
52263
52264 forEach(elements, function(element) {
52265 var assignedDi = isConnection$3(element) ? pick(di, [ 'border-color' ]) : di;
52266
52267 // TODO @barmac: remove once we drop bpmn.io properties
52268 ensureLegacySupport(assignedDi);
52269
52270 self._commandStack.execute('element.updateProperties', {
52271 element: element,
52272 properties: {
52273 di: assignedDi
52274 }
52275 });
52276 });
52277
52278 };
52279
52280 /**
52281 * Convert color from rgb(a)/hsl to hex. Returns `null` for unknown color names and for colors
52282 * with alpha less than 1.0. This depends on `<canvas>` serialization of the `context.fillStyle`.
52283 * Cf. https://html.spec.whatwg.org/multipage/canvas.html#dom-context-2d-fillstyle
52284 *
52285 * @example
52286 * ```js
52287 * var color = 'fuchsia';
52288 * console.log(colorToHex(color));
52289 * // "#ff00ff"
52290 * color = 'rgba(1,2,3,0.4)';
52291 * console.log(colorToHex(color));
52292 * // null
52293 * ```
52294 *
52295 * @param {string} color
52296 * @returns {string|null}
52297 */
52298 function colorToHex(color) {
52299 var context = document.createElement('canvas').getContext('2d');
52300
52301 // (0) Start with transparent to account for browser default values.
52302 context.fillStyle = 'transparent';
52303
52304 // (1) Assign color so that it's serialized.
52305 context.fillStyle = color;
52306
52307 // (2) Return null for non-hex serialization result.
52308 return /^#[0-9a-fA-F]{6}$/.test(context.fillStyle) ? context.fillStyle : null;
52309 }
52310
52311 function isConnection$3(element) {
52312 return !!element.waypoints;
52313 }
52314
52315 /**
52316 * Add legacy properties if required.
52317 * @param {{ 'border-color': string?, 'background-color': string? }} di
52318 */
52319 function ensureLegacySupport(di) {
52320 if ('border-color' in di) {
52321 di.stroke = di['border-color'];
52322 }
52323
52324 if ('background-color' in di) {
52325 di.fill = di['background-color'];
52326 }
52327 }
52328
52329 var NULL_DIMENSIONS = {
52330 width: 0,
52331 height: 0
52332 };
52333
52334
52335 /**
52336 * A handler that updates the text of a BPMN element.
52337 */
52338 function UpdateLabelHandler(modeling, textRenderer) {
52339
52340 /**
52341 * Set the label and return the changed elements.
52342 *
52343 * Element parameter can be label itself or connection (i.e. sequence flow).
52344 *
52345 * @param {djs.model.Base} element
52346 * @param {string} text
52347 */
52348 function setText(element, text) {
52349
52350 // external label if present
52351 var label = element.label || element;
52352
52353 var labelTarget = element.labelTarget || element;
52354
52355 setLabel(label, text);
52356
52357 return [ label, labelTarget ];
52358 }
52359
52360 function preExecute(ctx) {
52361 var element = ctx.element,
52362 businessObject = element.businessObject,
52363 newLabel = ctx.newLabel;
52364
52365 if (!isLabel$6(element)
52366 && isLabelExternal(element)
52367 && !hasExternalLabel(element)
52368 && !isEmptyText(newLabel)) {
52369
52370 // create label
52371 var paddingTop = 7;
52372
52373 var labelCenter = getExternalLabelMid(element);
52374
52375 labelCenter = {
52376 x: labelCenter.x,
52377 y: labelCenter.y + paddingTop
52378 };
52379
52380 modeling.createLabel(element, labelCenter, {
52381 id: businessObject.id + '_label',
52382 businessObject: businessObject
52383 });
52384 }
52385 }
52386
52387 function execute(ctx) {
52388 ctx.oldLabel = getLabel(ctx.element);
52389 return setText(ctx.element, ctx.newLabel);
52390 }
52391
52392 function revert(ctx) {
52393 return setText(ctx.element, ctx.oldLabel);
52394 }
52395
52396 function postExecute(ctx) {
52397 var element = ctx.element,
52398 label = element.label || element,
52399 newLabel = ctx.newLabel,
52400 newBounds = ctx.newBounds,
52401 hints = ctx.hints || {};
52402
52403 // ignore internal labels for elements except text annotations
52404 if (!isLabel$6(label) && !is$1(label, 'bpmn:TextAnnotation')) {
52405 return;
52406 }
52407
52408 if (isLabel$6(label) && isEmptyText(newLabel)) {
52409
52410 if (hints.removeShape !== false) {
52411 modeling.removeShape(label, { unsetLabel: false });
52412 }
52413
52414 return;
52415 }
52416
52417 var text = getLabel(label);
52418
52419 // resize element based on label _or_ pre-defined bounds
52420 if (typeof newBounds === 'undefined') {
52421 newBounds = textRenderer.getExternalLabelBounds(label, text);
52422 }
52423
52424 // setting newBounds to false or _null_ will
52425 // disable the postExecute resize operation
52426 if (newBounds) {
52427 modeling.resizeShape(label, newBounds, NULL_DIMENSIONS);
52428 }
52429 }
52430
52431 // API
52432
52433 this.preExecute = preExecute;
52434 this.execute = execute;
52435 this.revert = revert;
52436 this.postExecute = postExecute;
52437 }
52438
52439 UpdateLabelHandler.$inject = [
52440 'modeling',
52441 'textRenderer'
52442 ];
52443
52444
52445 // helpers ///////////////////////
52446
52447 function isEmptyText(label) {
52448 return !label || !label.trim();
52449 }
52450
52451 /**
52452 * BPMN 2.0 modeling features activator
52453 *
52454 * @param {EventBus} eventBus
52455 * @param {ElementFactory} elementFactory
52456 * @param {CommandStack} commandStack
52457 * @param {BpmnRules} bpmnRules
52458 */
52459 function Modeling(
52460 eventBus, elementFactory, commandStack,
52461 bpmnRules) {
52462
52463 Modeling$1.call(this, eventBus, elementFactory, commandStack);
52464
52465 this._bpmnRules = bpmnRules;
52466 }
52467
52468 inherits$1(Modeling, Modeling$1);
52469
52470 Modeling.$inject = [
52471 'eventBus',
52472 'elementFactory',
52473 'commandStack',
52474 'bpmnRules'
52475 ];
52476
52477
52478 Modeling.prototype.getHandlers = function() {
52479 var handlers = Modeling$1.prototype.getHandlers.call(this);
52480
52481 handlers['element.updateModdleProperties'] = UpdateModdlePropertiesHandler;
52482 handlers['element.updateProperties'] = UpdatePropertiesHandler;
52483 handlers['canvas.updateRoot'] = UpdateCanvasRootHandler;
52484 handlers['lane.add'] = AddLaneHandler;
52485 handlers['lane.resize'] = ResizeLaneHandler;
52486 handlers['lane.split'] = SplitLaneHandler;
52487 handlers['lane.updateRefs'] = UpdateFlowNodeRefsHandler;
52488 handlers['id.updateClaim'] = IdClaimHandler;
52489 handlers['element.setColor'] = SetColorHandler;
52490 handlers['element.updateLabel'] = UpdateLabelHandler;
52491
52492 return handlers;
52493 };
52494
52495
52496 Modeling.prototype.updateLabel = function(element, newLabel, newBounds, hints) {
52497 this._commandStack.execute('element.updateLabel', {
52498 element: element,
52499 newLabel: newLabel,
52500 newBounds: newBounds,
52501 hints: hints || {}
52502 });
52503 };
52504
52505
52506 Modeling.prototype.connect = function(source, target, attrs, hints) {
52507
52508 var bpmnRules = this._bpmnRules;
52509
52510 if (!attrs) {
52511 attrs = bpmnRules.canConnect(source, target);
52512 }
52513
52514 if (!attrs) {
52515 return;
52516 }
52517
52518 return this.createConnection(source, target, attrs, source.parent, hints);
52519 };
52520
52521
52522 Modeling.prototype.updateModdleProperties = function(element, moddleElement, properties) {
52523 this._commandStack.execute('element.updateModdleProperties', {
52524 element: element,
52525 moddleElement: moddleElement,
52526 properties: properties
52527 });
52528 };
52529
52530 Modeling.prototype.updateProperties = function(element, properties) {
52531 this._commandStack.execute('element.updateProperties', {
52532 element: element,
52533 properties: properties
52534 });
52535 };
52536
52537 Modeling.prototype.resizeLane = function(laneShape, newBounds, balanced) {
52538 this._commandStack.execute('lane.resize', {
52539 shape: laneShape,
52540 newBounds: newBounds,
52541 balanced: balanced
52542 });
52543 };
52544
52545 Modeling.prototype.addLane = function(targetLaneShape, location) {
52546 var context = {
52547 shape: targetLaneShape,
52548 location: location
52549 };
52550
52551 this._commandStack.execute('lane.add', context);
52552
52553 return context.newLane;
52554 };
52555
52556 Modeling.prototype.splitLane = function(targetLane, count) {
52557 this._commandStack.execute('lane.split', {
52558 shape: targetLane,
52559 count: count
52560 });
52561 };
52562
52563 /**
52564 * Transform the current diagram into a collaboration.
52565 *
52566 * @return {djs.model.Root} the new root element
52567 */
52568 Modeling.prototype.makeCollaboration = function() {
52569
52570 var collaborationElement = this._create('root', {
52571 type: 'bpmn:Collaboration'
52572 });
52573
52574 var context = {
52575 newRoot: collaborationElement
52576 };
52577
52578 this._commandStack.execute('canvas.updateRoot', context);
52579
52580 return collaborationElement;
52581 };
52582
52583 Modeling.prototype.updateLaneRefs = function(flowNodeShapes, laneShapes) {
52584
52585 this._commandStack.execute('lane.updateRefs', {
52586 flowNodeShapes: flowNodeShapes,
52587 laneShapes: laneShapes
52588 });
52589 };
52590
52591 /**
52592 * Transform the current diagram into a process.
52593 *
52594 * @return {djs.model.Root} the new root element
52595 */
52596 Modeling.prototype.makeProcess = function() {
52597
52598 var processElement = this._create('root', {
52599 type: 'bpmn:Process'
52600 });
52601
52602 var context = {
52603 newRoot: processElement
52604 };
52605
52606 this._commandStack.execute('canvas.updateRoot', context);
52607 };
52608
52609
52610 Modeling.prototype.claimId = function(id, moddleElement) {
52611 this._commandStack.execute('id.updateClaim', {
52612 id: id,
52613 element: moddleElement,
52614 claiming: true
52615 });
52616 };
52617
52618
52619 Modeling.prototype.unclaimId = function(id, moddleElement) {
52620 this._commandStack.execute('id.updateClaim', {
52621 id: id,
52622 element: moddleElement
52623 });
52624 };
52625
52626 Modeling.prototype.setColor = function(elements, colors) {
52627 if (!elements.length) {
52628 elements = [ elements ];
52629 }
52630
52631 this._commandStack.execute('element.setColor', {
52632 elements: elements,
52633 colors: colors
52634 });
52635 };
52636
52637 /**
52638 * A base connection layouter implementation
52639 * that layouts the connection by directly connecting
52640 * mid(source) + mid(target).
52641 */
52642 function BaseLayouter() {}
52643
52644
52645 /**
52646 * Return the new layouted waypoints for the given connection.
52647 *
52648 * The connection passed is still unchanged; you may figure out about
52649 * the new connection start / end via the layout hints provided.
52650 *
52651 * @param {djs.model.Connection} connection
52652 * @param {Object} [hints]
52653 * @param {Point} [hints.connectionStart]
52654 * @param {Point} [hints.connectionEnd]
52655 * @param {Point} [hints.source]
52656 * @param {Point} [hints.target]
52657 *
52658 * @return {Array<Point>} the layouted connection waypoints
52659 */
52660 BaseLayouter.prototype.layoutConnection = function(connection, hints) {
52661
52662 hints = hints || {};
52663
52664 return [
52665 hints.connectionStart || getMid(hints.source || connection.source),
52666 hints.connectionEnd || getMid(hints.target || connection.target)
52667 ];
52668 };
52669
52670 var MIN_SEGMENT_LENGTH = 20,
52671 POINT_ORIENTATION_PADDING = 5;
52672
52673 var round$1 = Math.round;
52674
52675 var INTERSECTION_THRESHOLD = 20,
52676 ORIENTATION_THRESHOLD = {
52677 'h:h': 20,
52678 'v:v': 20,
52679 'h:v': -10,
52680 'v:h': -10
52681 };
52682
52683 function needsTurn(orientation, startDirection) {
52684 return !{
52685 t: /top/,
52686 r: /right/,
52687 b: /bottom/,
52688 l: /left/,
52689 h: /./,
52690 v: /./
52691 }[startDirection].test(orientation);
52692 }
52693
52694 function canLayoutStraight(direction, targetOrientation) {
52695 return {
52696 t: /top/,
52697 r: /right/,
52698 b: /bottom/,
52699 l: /left/,
52700 h: /left|right/,
52701 v: /top|bottom/
52702 }[direction].test(targetOrientation);
52703 }
52704
52705 function getSegmentBendpoints(a, b, directions) {
52706 var orientation = getOrientation(b, a, POINT_ORIENTATION_PADDING);
52707
52708 var startDirection = directions.split(':')[0];
52709
52710 var xmid = round$1((b.x - a.x) / 2 + a.x),
52711 ymid = round$1((b.y - a.y) / 2 + a.y);
52712
52713 var segmentEnd, segmentDirections;
52714
52715 var layoutStraight = canLayoutStraight(startDirection, orientation),
52716 layoutHorizontal = /h|r|l/.test(startDirection),
52717 layoutTurn = false;
52718
52719 var turnNextDirections = false;
52720
52721 if (layoutStraight) {
52722 segmentEnd = layoutHorizontal ? { x: xmid, y: a.y } : { x: a.x, y: ymid };
52723
52724 segmentDirections = layoutHorizontal ? 'h:h' : 'v:v';
52725 } else {
52726 layoutTurn = needsTurn(orientation, startDirection);
52727
52728 segmentDirections = layoutHorizontal ? 'h:v' : 'v:h';
52729
52730 if (layoutTurn) {
52731
52732 if (layoutHorizontal) {
52733 turnNextDirections = ymid === a.y;
52734
52735 segmentEnd = {
52736 x: a.x + MIN_SEGMENT_LENGTH * (/l/.test(startDirection) ? -1 : 1),
52737 y: turnNextDirections ? ymid + MIN_SEGMENT_LENGTH : ymid
52738 };
52739 } else {
52740 turnNextDirections = xmid === a.x;
52741
52742 segmentEnd = {
52743 x: turnNextDirections ? xmid + MIN_SEGMENT_LENGTH : xmid,
52744 y: a.y + MIN_SEGMENT_LENGTH * (/t/.test(startDirection) ? -1 : 1)
52745 };
52746 }
52747
52748 } else {
52749 segmentEnd = {
52750 x: xmid,
52751 y: ymid
52752 };
52753 }
52754 }
52755
52756 return {
52757 waypoints: getBendpoints(a, segmentEnd, segmentDirections).concat(segmentEnd),
52758 directions: segmentDirections,
52759 turnNextDirections: turnNextDirections
52760 };
52761 }
52762
52763 function getStartSegment(a, b, directions) {
52764 return getSegmentBendpoints(a, b, directions);
52765 }
52766
52767 function getEndSegment(a, b, directions) {
52768 var invertedSegment = getSegmentBendpoints(b, a, invertDirections(directions));
52769
52770 return {
52771 waypoints: invertedSegment.waypoints.slice().reverse(),
52772 directions: invertDirections(invertedSegment.directions),
52773 turnNextDirections: invertedSegment.turnNextDirections
52774 };
52775 }
52776
52777 function getMidSegment(startSegment, endSegment) {
52778
52779 var startDirection = startSegment.directions.split(':')[1],
52780 endDirection = endSegment.directions.split(':')[0];
52781
52782 if (startSegment.turnNextDirections) {
52783 startDirection = startDirection == 'h' ? 'v' : 'h';
52784 }
52785
52786 if (endSegment.turnNextDirections) {
52787 endDirection = endDirection == 'h' ? 'v' : 'h';
52788 }
52789
52790 var directions = startDirection + ':' + endDirection;
52791
52792 var bendpoints = getBendpoints(
52793 startSegment.waypoints[startSegment.waypoints.length - 1],
52794 endSegment.waypoints[0],
52795 directions
52796 );
52797
52798 return {
52799 waypoints: bendpoints,
52800 directions: directions
52801 };
52802 }
52803
52804 function invertDirections(directions) {
52805 return directions.split(':').reverse().join(':');
52806 }
52807
52808 /**
52809 * Handle simple layouts with maximum two bendpoints.
52810 */
52811 function getSimpleBendpoints(a, b, directions) {
52812
52813 var xmid = round$1((b.x - a.x) / 2 + a.x),
52814 ymid = round$1((b.y - a.y) / 2 + a.y);
52815
52816 // one point, right or left from a
52817 if (directions === 'h:v') {
52818 return [ { x: b.x, y: a.y } ];
52819 }
52820
52821 // one point, above or below a
52822 if (directions === 'v:h') {
52823 return [ { x: a.x, y: b.y } ];
52824 }
52825
52826 // vertical segment between a and b
52827 if (directions === 'h:h') {
52828 return [
52829 { x: xmid, y: a.y },
52830 { x: xmid, y: b.y }
52831 ];
52832 }
52833
52834 // horizontal segment between a and b
52835 if (directions === 'v:v') {
52836 return [
52837 { x: a.x, y: ymid },
52838 { x: b.x, y: ymid }
52839 ];
52840 }
52841
52842 throw new Error('invalid directions: can only handle varians of [hv]:[hv]');
52843 }
52844
52845
52846 /**
52847 * Returns the mid points for a manhattan connection between two points.
52848 *
52849 * @example h:h (horizontal:horizontal)
52850 *
52851 * [a]----[x]
52852 * |
52853 * [x]----[b]
52854 *
52855 * @example h:v (horizontal:vertical)
52856 *
52857 * [a]----[x]
52858 * |
52859 * [b]
52860 *
52861 * @example h:r (horizontal:right)
52862 *
52863 * [a]----[x]
52864 * |
52865 * [b]-[x]
52866 *
52867 * @param {Point} a
52868 * @param {Point} b
52869 * @param {string} directions
52870 *
52871 * @return {Array<Point>}
52872 */
52873 function getBendpoints(a, b, directions) {
52874 directions = directions || 'h:h';
52875
52876 if (!isValidDirections(directions)) {
52877 throw new Error(
52878 'unknown directions: <' + directions + '>: ' +
52879 'must be specified as <start>:<end> ' +
52880 'with start/end in { h,v,t,r,b,l }'
52881 );
52882 }
52883
52884 // compute explicit directions, involving trbl dockings
52885 // using a three segmented layouting algorithm
52886 if (isExplicitDirections(directions)) {
52887 var startSegment = getStartSegment(a, b, directions),
52888 endSegment = getEndSegment(a, b, directions),
52889 midSegment = getMidSegment(startSegment, endSegment);
52890
52891 return [].concat(
52892 startSegment.waypoints,
52893 midSegment.waypoints,
52894 endSegment.waypoints
52895 );
52896 }
52897
52898 // handle simple [hv]:[hv] cases that can be easily computed
52899 return getSimpleBendpoints(a, b, directions);
52900 }
52901
52902 /**
52903 * Create a connection between the two points according
52904 * to the manhattan layout (only horizontal and vertical) edges.
52905 *
52906 * @param {Point} a
52907 * @param {Point} b
52908 *
52909 * @param {string} [directions='h:h'] specifies manhattan directions for each point as {adirection}:{bdirection}.
52910 A directionfor a point is either `h` (horizontal) or `v` (vertical)
52911 *
52912 * @return {Array<Point>}
52913 */
52914 function connectPoints(a, b, directions) {
52915
52916 var points = getBendpoints(a, b, directions);
52917
52918 points.unshift(a);
52919 points.push(b);
52920
52921 return withoutRedundantPoints(points);
52922 }
52923
52924
52925 /**
52926 * Connect two rectangles using a manhattan layouted connection.
52927 *
52928 * @param {Bounds} source source rectangle
52929 * @param {Bounds} target target rectangle
52930 * @param {Point} [start] source docking
52931 * @param {Point} [end] target docking
52932 *
52933 * @param {Object} [hints]
52934 * @param {string} [hints.preserveDocking=source] preserve docking on selected side
52935 * @param {Array<string>} [hints.preferredLayouts]
52936 * @param {Point|boolean} [hints.connectionStart] whether the start changed
52937 * @param {Point|boolean} [hints.connectionEnd] whether the end changed
52938 *
52939 * @return {Array<Point>} connection points
52940 */
52941 function connectRectangles(source, target, start, end, hints) {
52942
52943 var preferredLayouts = hints && hints.preferredLayouts || [];
52944
52945 var preferredLayout = without(preferredLayouts, 'straight')[0] || 'h:h';
52946
52947 var threshold = ORIENTATION_THRESHOLD[preferredLayout] || 0;
52948
52949 var orientation = getOrientation(source, target, threshold);
52950
52951 var directions = getDirections(orientation, preferredLayout);
52952
52953 start = start || getMid(source);
52954 end = end || getMid(target);
52955
52956 var directionSplit = directions.split(':');
52957
52958 // compute actual docking points for start / end
52959 // this ensures we properly layout only parts of the
52960 // connection that lies in between the two rectangles
52961 var startDocking = getDockingPoint(start, source, directionSplit[0], invertOrientation(orientation)),
52962 endDocking = getDockingPoint(end, target, directionSplit[1], orientation);
52963
52964 return connectPoints(startDocking, endDocking, directions);
52965 }
52966
52967
52968 /**
52969 * Repair the connection between two rectangles, of which one has been updated.
52970 *
52971 * @param {Bounds} source
52972 * @param {Bounds} target
52973 * @param {Point} [start]
52974 * @param {Point} [end]
52975 * @param {Array<Point>} [waypoints]
52976 * @param {Object} [hints]
52977 * @param {Array<string>} [hints.preferredLayouts] list of preferred layouts
52978 * @param {boolean} [hints.connectionStart]
52979 * @param {boolean} [hints.connectionEnd]
52980 *
52981 * @return {Array<Point>} repaired waypoints
52982 */
52983 function repairConnection(source, target, start, end, waypoints, hints) {
52984
52985 if (isArray$2(start)) {
52986 waypoints = start;
52987 hints = end;
52988
52989 start = getMid(source);
52990 end = getMid(target);
52991 }
52992
52993 hints = assign({ preferredLayouts: [] }, hints);
52994 waypoints = waypoints || [];
52995
52996 var preferredLayouts = hints.preferredLayouts,
52997 preferStraight = preferredLayouts.indexOf('straight') !== -1,
52998 repairedWaypoints;
52999
53000 // just layout non-existing or simple connections
53001 // attempt to render straight lines, if required
53002
53003 // attempt to layout a straight line
53004 repairedWaypoints = preferStraight && tryLayoutStraight(source, target, start, end, hints);
53005
53006 if (repairedWaypoints) {
53007 return repairedWaypoints;
53008 }
53009
53010 // try to layout from end
53011 repairedWaypoints = hints.connectionEnd && tryRepairConnectionEnd(target, source, end, waypoints);
53012
53013 if (repairedWaypoints) {
53014 return repairedWaypoints;
53015 }
53016
53017 // try to layout from start
53018 repairedWaypoints = hints.connectionStart && tryRepairConnectionStart(source, target, start, waypoints);
53019
53020 if (repairedWaypoints) {
53021 return repairedWaypoints;
53022 }
53023
53024 // or whether nothing seems to have changed
53025 if (!hints.connectionStart && !hints.connectionEnd && waypoints && waypoints.length) {
53026 return waypoints;
53027 }
53028
53029 // simply reconnect if nothing else worked
53030 return connectRectangles(source, target, start, end, hints);
53031 }
53032
53033
53034 function inRange(a, start, end) {
53035 return a >= start && a <= end;
53036 }
53037
53038 function isInRange(axis, a, b) {
53039 var size = {
53040 x: 'width',
53041 y: 'height'
53042 };
53043
53044 return inRange(a[axis], b[axis], b[axis] + b[size[axis]]);
53045 }
53046
53047 /**
53048 * Layout a straight connection
53049 *
53050 * @param {Bounds} source
53051 * @param {Bounds} target
53052 * @param {Point} start
53053 * @param {Point} end
53054 * @param {Object} [hints]
53055 *
53056 * @return {Array<Point>|null} waypoints if straight layout worked
53057 */
53058 function tryLayoutStraight(source, target, start, end, hints) {
53059 var axis = {},
53060 primaryAxis,
53061 orientation;
53062
53063 orientation = getOrientation(source, target);
53064
53065 // only layout a straight connection if shapes are
53066 // horizontally or vertically aligned
53067 if (!/^(top|bottom|left|right)$/.test(orientation)) {
53068 return null;
53069 }
53070
53071 if (/top|bottom/.test(orientation)) {
53072 primaryAxis = 'x';
53073 }
53074
53075 if (/left|right/.test(orientation)) {
53076 primaryAxis = 'y';
53077 }
53078
53079 if (hints.preserveDocking === 'target') {
53080
53081 if (!isInRange(primaryAxis, end, source)) {
53082 return null;
53083 }
53084
53085 axis[primaryAxis] = end[primaryAxis];
53086
53087 return [
53088 {
53089 x: axis.x !== undefined ? axis.x : start.x,
53090 y: axis.y !== undefined ? axis.y : start.y,
53091 original: {
53092 x: axis.x !== undefined ? axis.x : start.x,
53093 y: axis.y !== undefined ? axis.y : start.y
53094 }
53095 },
53096 {
53097 x: end.x,
53098 y: end.y
53099 }
53100 ];
53101
53102 } else {
53103
53104 if (!isInRange(primaryAxis, start, target)) {
53105 return null;
53106 }
53107
53108 axis[primaryAxis] = start[primaryAxis];
53109
53110 return [
53111 {
53112 x: start.x,
53113 y: start.y
53114 },
53115 {
53116 x: axis.x !== undefined ? axis.x : end.x,
53117 y: axis.y !== undefined ? axis.y : end.y,
53118 original: {
53119 x: axis.x !== undefined ? axis.x : end.x,
53120 y: axis.y !== undefined ? axis.y : end.y
53121 }
53122 }
53123 ];
53124 }
53125
53126 }
53127
53128 /**
53129 * Repair a connection from start.
53130 *
53131 * @param {Bounds} moved
53132 * @param {Bounds} other
53133 * @param {Point} newDocking
53134 * @param {Array<Point>} points originalPoints from moved to other
53135 *
53136 * @return {Array<Point>|null} the repaired points between the two rectangles
53137 */
53138 function tryRepairConnectionStart(moved, other, newDocking, points) {
53139 return _tryRepairConnectionSide(moved, other, newDocking, points);
53140 }
53141
53142 /**
53143 * Repair a connection from end.
53144 *
53145 * @param {Bounds} moved
53146 * @param {Bounds} other
53147 * @param {Point} newDocking
53148 * @param {Array<Point>} points originalPoints from moved to other
53149 *
53150 * @return {Array<Point>|null} the repaired points between the two rectangles
53151 */
53152 function tryRepairConnectionEnd(moved, other, newDocking, points) {
53153 var waypoints = points.slice().reverse();
53154
53155 waypoints = _tryRepairConnectionSide(moved, other, newDocking, waypoints);
53156
53157 return waypoints ? waypoints.reverse() : null;
53158 }
53159
53160 /**
53161 * Repair a connection from one side that moved.
53162 *
53163 * @param {Bounds} moved
53164 * @param {Bounds} other
53165 * @param {Point} newDocking
53166 * @param {Array<Point>} points originalPoints from moved to other
53167 *
53168 * @return {Array<Point>} the repaired points between the two rectangles
53169 */
53170 function _tryRepairConnectionSide(moved, other, newDocking, points) {
53171
53172 function needsRelayout(points) {
53173 if (points.length < 3) {
53174 return true;
53175 }
53176
53177 if (points.length > 4) {
53178 return false;
53179 }
53180
53181 // relayout if two points overlap
53182 // this is most likely due to
53183 return !!find(points, function(p, idx) {
53184 var q = points[idx - 1];
53185
53186 return q && pointDistance(p, q) < 3;
53187 });
53188 }
53189
53190 function repairBendpoint(candidate, oldPeer, newPeer) {
53191
53192 var alignment = pointsAligned(oldPeer, candidate);
53193
53194 switch (alignment) {
53195 case 'v':
53196
53197 // repair horizontal alignment
53198 return { x: newPeer.x, y: candidate.y };
53199 case 'h':
53200
53201 // repair vertical alignment
53202 return { x: candidate.x, y: newPeer.y };
53203 }
53204
53205 return { x: candidate.x, y: candidate. y };
53206 }
53207
53208 function removeOverlapping(points, a, b) {
53209 var i;
53210
53211 for (i = points.length - 2; i !== 0; i--) {
53212
53213 // intersects (?) break, remove all bendpoints up to this one and relayout
53214 if (pointInRect(points[i], a, INTERSECTION_THRESHOLD) ||
53215 pointInRect(points[i], b, INTERSECTION_THRESHOLD)) {
53216
53217 // return sliced old connection
53218 return points.slice(i);
53219 }
53220 }
53221
53222 return points;
53223 }
53224
53225 // (0) only repair what has layoutable bendpoints
53226
53227 // (1) if only one bendpoint and on shape moved onto other shapes axis
53228 // (horizontally / vertically), relayout
53229
53230 if (needsRelayout(points)) {
53231 return null;
53232 }
53233
53234 var oldDocking = points[0],
53235 newPoints = points.slice(),
53236 slicedPoints;
53237
53238 // (2) repair only last line segment and only if it was layouted before
53239
53240 newPoints[0] = newDocking;
53241 newPoints[1] = repairBendpoint(newPoints[1], oldDocking, newDocking);
53242
53243
53244 // (3) if shape intersects with any bendpoint after repair,
53245 // remove all segments up to this bendpoint and repair from there
53246 slicedPoints = removeOverlapping(newPoints, moved, other);
53247
53248 if (slicedPoints !== newPoints) {
53249 newPoints = _tryRepairConnectionSide(moved, other, newDocking, slicedPoints);
53250 }
53251
53252 // (4) do NOT repair if repaired bendpoints are aligned
53253 if (newPoints && pointsAligned(newPoints)) {
53254 return null;
53255 }
53256
53257 return newPoints;
53258 }
53259
53260
53261 /**
53262 * Returns the manhattan directions connecting two rectangles
53263 * with the given orientation.
53264 *
53265 * Will always return the default layout, if it is specific
53266 * regarding sides already (trbl).
53267 *
53268 * @example
53269 *
53270 * getDirections('top'); // -> 'v:v'
53271 * getDirections('intersect'); // -> 't:t'
53272 *
53273 * getDirections('top-right', 'v:h'); // -> 'v:h'
53274 * getDirections('top-right', 'h:h'); // -> 'h:h'
53275 *
53276 *
53277 * @param {string} orientation
53278 * @param {string} defaultLayout
53279 *
53280 * @return {string}
53281 */
53282 function getDirections(orientation, defaultLayout) {
53283
53284 // don't override specific trbl directions
53285 if (isExplicitDirections(defaultLayout)) {
53286 return defaultLayout;
53287 }
53288
53289 switch (orientation) {
53290 case 'intersect':
53291 return 't:t';
53292
53293 case 'top':
53294 case 'bottom':
53295 return 'v:v';
53296
53297 case 'left':
53298 case 'right':
53299 return 'h:h';
53300
53301 // 'top-left'
53302 // 'top-right'
53303 // 'bottom-left'
53304 // 'bottom-right'
53305 default:
53306 return defaultLayout;
53307 }
53308 }
53309
53310 function isValidDirections(directions) {
53311 return directions && /^h|v|t|r|b|l:h|v|t|r|b|l$/.test(directions);
53312 }
53313
53314 function isExplicitDirections(directions) {
53315 return directions && /t|r|b|l/.test(directions);
53316 }
53317
53318 function invertOrientation(orientation) {
53319 return {
53320 'top': 'bottom',
53321 'bottom': 'top',
53322 'left': 'right',
53323 'right': 'left',
53324 'top-left': 'bottom-right',
53325 'bottom-right': 'top-left',
53326 'top-right': 'bottom-left',
53327 'bottom-left': 'top-right',
53328 }[orientation];
53329 }
53330
53331 function getDockingPoint(point, rectangle, dockingDirection, targetOrientation) {
53332
53333 // ensure we end up with a specific docking direction
53334 // based on the targetOrientation, if <h|v> is being passed
53335
53336 if (dockingDirection === 'h') {
53337 dockingDirection = /left/.test(targetOrientation) ? 'l' : 'r';
53338 }
53339
53340 if (dockingDirection === 'v') {
53341 dockingDirection = /top/.test(targetOrientation) ? 't' : 'b';
53342 }
53343
53344 if (dockingDirection === 't') {
53345 return { original: point, x: point.x, y: rectangle.y };
53346 }
53347
53348 if (dockingDirection === 'r') {
53349 return { original: point, x: rectangle.x + rectangle.width, y: point.y };
53350 }
53351
53352 if (dockingDirection === 'b') {
53353 return { original: point, x: point.x, y: rectangle.y + rectangle.height };
53354 }
53355
53356 if (dockingDirection === 'l') {
53357 return { original: point, x: rectangle.x, y: point.y };
53358 }
53359
53360 throw new Error('unexpected dockingDirection: <' + dockingDirection + '>');
53361 }
53362
53363
53364 /**
53365 * Return list of waypoints with redundant ones filtered out.
53366 *
53367 * @example
53368 *
53369 * Original points:
53370 *
53371 * [x] ----- [x] ------ [x]
53372 * |
53373 * [x] ----- [x] - [x]
53374 *
53375 * Filtered:
53376 *
53377 * [x] ---------------- [x]
53378 * |
53379 * [x] ----------- [x]
53380 *
53381 * @param {Array<Point>} waypoints
53382 *
53383 * @return {Array<Point>}
53384 */
53385 function withoutRedundantPoints(waypoints) {
53386 return waypoints.reduce(function(points, p, idx) {
53387
53388 var previous = points[points.length - 1],
53389 next = waypoints[idx + 1];
53390
53391 if (!pointsOnLine(previous, next, p, 0)) {
53392 points.push(p);
53393 }
53394
53395 return points;
53396 }, []);
53397 }
53398
53399 var ATTACH_ORIENTATION_PADDING = -10,
53400 BOUNDARY_TO_HOST_THRESHOLD$1 = 40;
53401
53402 var oppositeOrientationMapping = {
53403 'top': 'bottom',
53404 'top-right': 'bottom-left',
53405 'top-left': 'bottom-right',
53406 'right': 'left',
53407 'bottom': 'top',
53408 'bottom-right': 'top-left',
53409 'bottom-left': 'top-right',
53410 'left': 'right'
53411 };
53412
53413 var orientationDirectionMapping = {
53414 top: 't',
53415 right: 'r',
53416 bottom: 'b',
53417 left: 'l'
53418 };
53419
53420
53421 function BpmnLayouter() {}
53422
53423 inherits$1(BpmnLayouter, BaseLayouter);
53424
53425
53426 BpmnLayouter.prototype.layoutConnection = function(connection, hints) {
53427 if (!hints) {
53428 hints = {};
53429 }
53430
53431 var source = hints.source || connection.source,
53432 target = hints.target || connection.target,
53433 waypoints = hints.waypoints || connection.waypoints,
53434 connectionStart = hints.connectionStart,
53435 connectionEnd = hints.connectionEnd;
53436
53437 var manhattanOptions,
53438 updatedWaypoints;
53439
53440 if (!connectionStart) {
53441 connectionStart = getConnectionDocking(waypoints && waypoints[ 0 ], source);
53442 }
53443
53444 if (!connectionEnd) {
53445 connectionEnd = getConnectionDocking(waypoints && waypoints[ waypoints.length - 1 ], target);
53446 }
53447
53448 // TODO(nikku): support vertical modeling
53449 // and invert preferredLayouts accordingly
53450
53451 if (is$1(connection, 'bpmn:Association') ||
53452 is$1(connection, 'bpmn:DataAssociation')) {
53453
53454 if (waypoints && !isCompensationAssociation(source, target)) {
53455 return [].concat([ connectionStart ], waypoints.slice(1, -1), [ connectionEnd ]);
53456 }
53457 }
53458
53459 if (is$1(connection, 'bpmn:MessageFlow')) {
53460 manhattanOptions = getMessageFlowManhattanOptions(source, target);
53461 } else if (is$1(connection, 'bpmn:SequenceFlow') || isCompensationAssociation(source, target)) {
53462
53463 // layout all connection between flow elements h:h, except for
53464 // (1) outgoing of boundary events -> layout based on attach orientation and target orientation
53465 // (2) incoming/outgoing of gateways -> v:h for outgoing, h:v for incoming
53466 // (3) loops
53467 if (source === target) {
53468 manhattanOptions = {
53469 preferredLayouts: getLoopPreferredLayout(source, connection)
53470 };
53471 } else if (is$1(source, 'bpmn:BoundaryEvent')) {
53472 manhattanOptions = {
53473 preferredLayouts: getBoundaryEventPreferredLayouts(source, target, connectionEnd)
53474 };
53475 } else if (isExpandedSubProcess(source) || isExpandedSubProcess(target)) {
53476 manhattanOptions = getSubProcessManhattanOptions(source);
53477 } else if (is$1(source, 'bpmn:Gateway')) {
53478 manhattanOptions = {
53479 preferredLayouts: [ 'v:h' ]
53480 };
53481 } else if (is$1(target, 'bpmn:Gateway')) {
53482 manhattanOptions = {
53483 preferredLayouts: [ 'h:v' ]
53484 };
53485 } else {
53486 manhattanOptions = {
53487 preferredLayouts: [ 'h:h' ]
53488 };
53489 }
53490 }
53491
53492 if (manhattanOptions) {
53493 manhattanOptions = assign(manhattanOptions, hints);
53494
53495 updatedWaypoints = withoutRedundantPoints(repairConnection(
53496 source,
53497 target,
53498 connectionStart,
53499 connectionEnd,
53500 waypoints,
53501 manhattanOptions
53502 ));
53503 }
53504
53505 return updatedWaypoints || [ connectionStart, connectionEnd ];
53506 };
53507
53508
53509 // helpers //////////
53510
53511 function getAttachOrientation(attachedElement) {
53512 var hostElement = attachedElement.host;
53513
53514 return getOrientation(getMid(attachedElement), hostElement, ATTACH_ORIENTATION_PADDING);
53515 }
53516
53517 function getMessageFlowManhattanOptions(source, target) {
53518 return {
53519 preferredLayouts: [ 'straight', 'v:v' ],
53520 preserveDocking: getMessageFlowPreserveDocking(source, target)
53521 };
53522 }
53523
53524 function getMessageFlowPreserveDocking(source, target) {
53525
53526 // (1) docking element connected to participant has precedence
53527 if (is$1(target, 'bpmn:Participant')) {
53528 return 'source';
53529 }
53530
53531 if (is$1(source, 'bpmn:Participant')) {
53532 return 'target';
53533 }
53534
53535 // (2) docking element connected to expanded sub-process has precedence
53536 if (isExpandedSubProcess(target)) {
53537 return 'source';
53538 }
53539
53540 if (isExpandedSubProcess(source)) {
53541 return 'target';
53542 }
53543
53544 // (3) docking event has precedence
53545 if (is$1(target, 'bpmn:Event')) {
53546 return 'target';
53547 }
53548
53549 if (is$1(source, 'bpmn:Event')) {
53550 return 'source';
53551 }
53552
53553 return null;
53554 }
53555
53556 function getSubProcessManhattanOptions(source) {
53557 return {
53558 preferredLayouts: [ 'straight', 'h:h' ],
53559 preserveDocking: getSubProcessPreserveDocking(source)
53560 };
53561 }
53562
53563 function getSubProcessPreserveDocking(source) {
53564 return isExpandedSubProcess(source) ? 'target' : 'source';
53565 }
53566
53567 function getConnectionDocking(point, shape) {
53568 return point ? (point.original || point) : getMid(shape);
53569 }
53570
53571 function isCompensationAssociation(source, target) {
53572 return is$1(target, 'bpmn:Activity') &&
53573 is$1(source, 'bpmn:BoundaryEvent') &&
53574 target.businessObject.isForCompensation;
53575 }
53576
53577 function isExpandedSubProcess(element) {
53578 return is$1(element, 'bpmn:SubProcess') && isExpanded(element);
53579 }
53580
53581 function isSame(a, b) {
53582 return a === b;
53583 }
53584
53585 function isAnyOrientation(orientation, orientations) {
53586 return orientations.indexOf(orientation) !== -1;
53587 }
53588
53589 function getHorizontalOrientation(orientation) {
53590 var matches = /right|left/.exec(orientation);
53591
53592 return matches && matches[0];
53593 }
53594
53595 function getVerticalOrientation(orientation) {
53596 var matches = /top|bottom/.exec(orientation);
53597
53598 return matches && matches[0];
53599 }
53600
53601 function isOppositeOrientation(a, b) {
53602 return oppositeOrientationMapping[a] === b;
53603 }
53604
53605 function isOppositeHorizontalOrientation(a, b) {
53606 var horizontalOrientation = getHorizontalOrientation(a);
53607
53608 var oppositeHorizontalOrientation = oppositeOrientationMapping[horizontalOrientation];
53609
53610 return b.indexOf(oppositeHorizontalOrientation) !== -1;
53611 }
53612
53613 function isOppositeVerticalOrientation(a, b) {
53614 var verticalOrientation = getVerticalOrientation(a);
53615
53616 var oppositeVerticalOrientation = oppositeOrientationMapping[verticalOrientation];
53617
53618 return b.indexOf(oppositeVerticalOrientation) !== -1;
53619 }
53620
53621 function isHorizontalOrientation(orientation) {
53622 return orientation === 'right' || orientation === 'left';
53623 }
53624
53625 function getLoopPreferredLayout(source, connection) {
53626 var waypoints = connection.waypoints;
53627
53628 var orientation = waypoints && waypoints.length && getOrientation(waypoints[0], source);
53629
53630 if (orientation === 'top') {
53631 return [ 't:r' ];
53632 } else if (orientation === 'right') {
53633 return [ 'r:b' ];
53634 } else if (orientation === 'left') {
53635 return [ 'l:t' ];
53636 }
53637
53638 return [ 'b:l' ];
53639 }
53640
53641 function getBoundaryEventPreferredLayouts(source, target, end) {
53642 var sourceMid = getMid(source),
53643 targetMid = getMid(target),
53644 attachOrientation = getAttachOrientation(source),
53645 sourceLayout,
53646 targetLayout;
53647
53648 var isLoop = isSame(source.host, target);
53649
53650 var attachedToSide = isAnyOrientation(attachOrientation, [ 'top', 'right', 'bottom', 'left' ]);
53651
53652 var targetOrientation = getOrientation(targetMid, sourceMid, {
53653 x: source.width / 2 + target.width / 2,
53654 y: source.height / 2 + target.height / 2
53655 });
53656
53657 if (isLoop) {
53658 return getBoundaryEventLoopLayout(attachOrientation, attachedToSide, source, target, end);
53659 }
53660
53661 // source layout
53662 sourceLayout = getBoundaryEventSourceLayout(attachOrientation, targetOrientation, attachedToSide);
53663
53664 // target layout
53665 targetLayout = getBoundaryEventTargetLayout(attachOrientation, targetOrientation, attachedToSide);
53666
53667 return [ sourceLayout + ':' + targetLayout ];
53668 }
53669
53670 function getBoundaryEventLoopLayout(attachOrientation, attachedToSide, source, target, end) {
53671 var orientation = attachedToSide ? attachOrientation : getVerticalOrientation(attachOrientation),
53672 sourceLayout = orientationDirectionMapping[ orientation ],
53673 targetLayout;
53674
53675 if (attachedToSide) {
53676 if (isHorizontalOrientation(attachOrientation)) {
53677 targetLayout = shouldConnectToSameSide('y', source, target, end) ? 'h' : 'b';
53678 } else {
53679 targetLayout = shouldConnectToSameSide('x', source, target, end) ? 'v' : 'l';
53680 }
53681 } else {
53682 targetLayout = 'v';
53683 }
53684
53685 return [ sourceLayout + ':' + targetLayout ];
53686 }
53687
53688 function shouldConnectToSameSide(axis, source, target, end) {
53689 var threshold = BOUNDARY_TO_HOST_THRESHOLD$1;
53690
53691 return !(
53692 areCloseOnAxis(axis, end, target, threshold) ||
53693 areCloseOnAxis(axis, end, {
53694 x: target.x + target.width,
53695 y: target.y + target.height
53696 }, threshold) ||
53697 areCloseOnAxis(axis, end, getMid(source), threshold)
53698 );
53699 }
53700
53701 function areCloseOnAxis(axis, a, b, threshold) {
53702 return Math.abs(a[ axis ] - b[ axis ]) < threshold;
53703 }
53704
53705 function getBoundaryEventSourceLayout(attachOrientation, targetOrientation, attachedToSide) {
53706
53707 // attached to either top, right, bottom or left side
53708 if (attachedToSide) {
53709 return orientationDirectionMapping[ attachOrientation ];
53710 }
53711
53712 // attached to either top-right, top-left, bottom-right or bottom-left corner
53713
53714 // same vertical or opposite horizontal orientation
53715 if (isSame(
53716 getVerticalOrientation(attachOrientation), getVerticalOrientation(targetOrientation)
53717 ) || isOppositeOrientation(
53718 getHorizontalOrientation(attachOrientation), getHorizontalOrientation(targetOrientation)
53719 )) {
53720 return orientationDirectionMapping[ getVerticalOrientation(attachOrientation) ];
53721 }
53722
53723 // fallback
53724 return orientationDirectionMapping[ getHorizontalOrientation(attachOrientation) ];
53725 }
53726
53727 function getBoundaryEventTargetLayout(attachOrientation, targetOrientation, attachedToSide) {
53728
53729 // attached to either top, right, bottom or left side
53730 if (attachedToSide) {
53731 if (isHorizontalOrientation(attachOrientation)) {
53732
53733 // orientation is right or left
53734
53735 // opposite horizontal orientation or same orientation
53736 if (
53737 isOppositeHorizontalOrientation(attachOrientation, targetOrientation) ||
53738 isSame(attachOrientation, targetOrientation)
53739 ) {
53740 return 'h';
53741 }
53742
53743 // fallback
53744 return 'v';
53745 } else {
53746
53747 // orientation is top or bottom
53748
53749 // opposite vertical orientation or same orientation
53750 if (
53751 isOppositeVerticalOrientation(attachOrientation, targetOrientation) ||
53752 isSame(attachOrientation, targetOrientation)
53753 ) {
53754 return 'v';
53755 }
53756
53757 // fallback
53758 return 'h';
53759 }
53760 }
53761
53762 // attached to either top-right, top-left, bottom-right or bottom-left corner
53763
53764 // orientation is right, left
53765 // or same vertical orientation but also right or left
53766 if (isHorizontalOrientation(targetOrientation) ||
53767 (isSame(getVerticalOrientation(attachOrientation), getVerticalOrientation(targetOrientation)) &&
53768 getHorizontalOrientation(targetOrientation))) {
53769 return 'h';
53770 } else {
53771 return 'v';
53772 }
53773 }
53774
53775 function dockingToPoint(docking) {
53776
53777 // use the dockings actual point and
53778 // retain the original docking
53779 return assign({ original: docking.point.original || docking.point }, docking.actual);
53780 }
53781
53782
53783 /**
53784 * A {@link ConnectionDocking} that crops connection waypoints based on
53785 * the path(s) of the connection source and target.
53786 *
53787 * @param {djs.core.ElementRegistry} elementRegistry
53788 */
53789 function CroppingConnectionDocking(elementRegistry, graphicsFactory) {
53790 this._elementRegistry = elementRegistry;
53791 this._graphicsFactory = graphicsFactory;
53792 }
53793
53794 CroppingConnectionDocking.$inject = [ 'elementRegistry', 'graphicsFactory' ];
53795
53796
53797 /**
53798 * @inheritDoc ConnectionDocking#getCroppedWaypoints
53799 */
53800 CroppingConnectionDocking.prototype.getCroppedWaypoints = function(connection, source, target) {
53801
53802 source = source || connection.source;
53803 target = target || connection.target;
53804
53805 var sourceDocking = this.getDockingPoint(connection, source, true),
53806 targetDocking = this.getDockingPoint(connection, target);
53807
53808 var croppedWaypoints = connection.waypoints.slice(sourceDocking.idx + 1, targetDocking.idx);
53809
53810 croppedWaypoints.unshift(dockingToPoint(sourceDocking));
53811 croppedWaypoints.push(dockingToPoint(targetDocking));
53812
53813 return croppedWaypoints;
53814 };
53815
53816 /**
53817 * Return the connection docking point on the specified shape
53818 *
53819 * @inheritDoc ConnectionDocking#getDockingPoint
53820 */
53821 CroppingConnectionDocking.prototype.getDockingPoint = function(connection, shape, dockStart) {
53822
53823 var waypoints = connection.waypoints,
53824 dockingIdx,
53825 dockingPoint,
53826 croppedPoint;
53827
53828 dockingIdx = dockStart ? 0 : waypoints.length - 1;
53829 dockingPoint = waypoints[dockingIdx];
53830
53831 croppedPoint = this._getIntersection(shape, connection, dockStart);
53832
53833 return {
53834 point: dockingPoint,
53835 actual: croppedPoint || dockingPoint,
53836 idx: dockingIdx
53837 };
53838 };
53839
53840
53841 // helpers //////////////////////
53842
53843 CroppingConnectionDocking.prototype._getIntersection = function(shape, connection, takeFirst) {
53844
53845 var shapePath = this._getShapePath(shape),
53846 connectionPath = this._getConnectionPath(connection);
53847
53848 return getElementLineIntersection(shapePath, connectionPath, takeFirst);
53849 };
53850
53851 CroppingConnectionDocking.prototype._getConnectionPath = function(connection) {
53852 return this._graphicsFactory.getConnectionPath(connection);
53853 };
53854
53855 CroppingConnectionDocking.prototype._getShapePath = function(shape) {
53856 return this._graphicsFactory.getShapePath(shape);
53857 };
53858
53859 CroppingConnectionDocking.prototype._getGfx = function(element) {
53860 return this._elementRegistry.getGraphics(element);
53861 };
53862
53863 var ModelingModule = {
53864 __init__: [
53865 'modeling',
53866 'bpmnUpdater'
53867 ],
53868 __depends__: [
53869 BehaviorModule,
53870 RulesModule,
53871 DiOrderingModule,
53872 OrderingModule,
53873 ReplaceModule,
53874 CommandModule,
53875 TooltipsModule,
53876 LabelSupportModule,
53877 AttachSupportModule,
53878 SelectionModule,
53879 ChangeSupportModule,
53880 SpaceToolModule
53881 ],
53882 bpmnFactory: [ 'type', BpmnFactory ],
53883 bpmnUpdater: [ 'type', BpmnUpdater ],
53884 elementFactory: [ 'type', ElementFactory ],
53885 modeling: [ 'type', Modeling ],
53886 layouter: [ 'type', BpmnLayouter ],
53887 connectionDocking: [ 'type', CroppingConnectionDocking ]
53888 };
53889
53890 var LOW_PRIORITY$2 = 500,
53891 MEDIUM_PRIORITY = 1250,
53892 HIGH_PRIORITY$2 = 1500;
53893
53894 var round = Math.round;
53895
53896 function mid(element) {
53897 return {
53898 x: element.x + round(element.width / 2),
53899 y: element.y + round(element.height / 2)
53900 };
53901 }
53902
53903 /**
53904 * A plugin that makes shapes draggable / droppable.
53905 *
53906 * @param {EventBus} eventBus
53907 * @param {Dragging} dragging
53908 * @param {Modeling} modeling
53909 * @param {Selection} selection
53910 * @param {Rules} rules
53911 */
53912 function MoveEvents(
53913 eventBus, dragging, modeling,
53914 selection, rules) {
53915
53916 // rules
53917
53918 function canMove(shapes, delta, position, target) {
53919
53920 return rules.allowed('elements.move', {
53921 shapes: shapes,
53922 delta: delta,
53923 position: position,
53924 target: target
53925 });
53926 }
53927
53928
53929 // move events
53930
53931 // assign a high priority to this handler to setup the environment
53932 // others may hook up later, e.g. at default priority and modify
53933 // the move environment.
53934 //
53935 // This sets up the context with
53936 //
53937 // * shape: the primary shape being moved
53938 // * shapes: a list of shapes to be moved
53939 // * validatedShapes: a list of shapes that are being checked
53940 // against the rules before and during move
53941 //
53942 eventBus.on('shape.move.start', HIGH_PRIORITY$2, function(event) {
53943
53944 var context = event.context,
53945 shape = event.shape,
53946 shapes = selection.get().slice();
53947
53948 // move only single shape if the dragged element
53949 // is not part of the current selection
53950 if (shapes.indexOf(shape) === -1) {
53951 shapes = [ shape ];
53952 }
53953
53954 // ensure we remove nested elements in the collection
53955 // and add attachers for a proper dragger
53956 shapes = removeNested(shapes);
53957
53958 // attach shapes to drag context
53959 assign(context, {
53960 shapes: shapes,
53961 validatedShapes: shapes,
53962 shape: shape
53963 });
53964 });
53965
53966
53967 // assign a high priority to this handler to setup the environment
53968 // others may hook up later, e.g. at default priority and modify
53969 // the move environment
53970 //
53971 eventBus.on('shape.move.start', MEDIUM_PRIORITY, function(event) {
53972
53973 var context = event.context,
53974 validatedShapes = context.validatedShapes,
53975 canExecute;
53976
53977 canExecute = context.canExecute = canMove(validatedShapes);
53978
53979 // check if we can move the elements
53980 if (!canExecute) {
53981 return false;
53982 }
53983 });
53984
53985 // assign a low priority to this handler
53986 // to let others modify the move event before we update
53987 // the context
53988 //
53989 eventBus.on('shape.move.move', LOW_PRIORITY$2, function(event) {
53990
53991 var context = event.context,
53992 validatedShapes = context.validatedShapes,
53993 hover = event.hover,
53994 delta = { x: event.dx, y: event.dy },
53995 position = { x: event.x, y: event.y },
53996 canExecute;
53997
53998 // check if we can move the elements
53999 canExecute = canMove(validatedShapes, delta, position, hover);
54000
54001 context.delta = delta;
54002 context.canExecute = canExecute;
54003
54004 // simply ignore move over
54005 if (canExecute === null) {
54006 context.target = null;
54007
54008 return;
54009 }
54010
54011 context.target = hover;
54012 });
54013
54014 eventBus.on('shape.move.end', function(event) {
54015
54016 var context = event.context;
54017
54018 var delta = context.delta,
54019 canExecute = context.canExecute,
54020 isAttach = canExecute === 'attach',
54021 shapes = context.shapes;
54022
54023 if (canExecute === false) {
54024 return false;
54025 }
54026
54027 // ensure we have actual pixel values deltas
54028 // (important when zoom level was > 1 during move)
54029 delta.x = round(delta.x);
54030 delta.y = round(delta.y);
54031
54032 if (delta.x === 0 && delta.y === 0) {
54033
54034 // didn't move
54035 return;
54036 }
54037
54038 modeling.moveElements(shapes, delta, context.target, {
54039 primaryShape: context.shape,
54040 attach: isAttach
54041 });
54042 });
54043
54044
54045 // move activation
54046
54047 eventBus.on('element.mousedown', function(event) {
54048
54049 if (!isPrimaryButton(event)) {
54050 return;
54051 }
54052
54053 var originalEvent = getOriginal$1(event);
54054
54055 if (!originalEvent) {
54056 throw new Error('must supply DOM mousedown event');
54057 }
54058
54059 return start(originalEvent, event.element);
54060 });
54061
54062 /**
54063 * Start move.
54064 *
54065 * @param {MouseEvent} event
54066 * @param {djs.model.Shape} shape
54067 * @param {boolean} [activate]
54068 * @param {Object} [context]
54069 */
54070 function start(event, element, activate, context) {
54071 if (isObject(activate)) {
54072 context = activate;
54073 activate = false;
54074 }
54075
54076 // do not move connections or the root element
54077 if (element.waypoints || !element.parent) {
54078 return;
54079 }
54080
54081 var referencePoint = mid(element);
54082
54083 dragging.init(event, referencePoint, 'shape.move', {
54084 cursor: 'grabbing',
54085 autoActivate: activate,
54086 data: {
54087 shape: element,
54088 context: context || {}
54089 }
54090 });
54091
54092 // we've handled the event
54093 return true;
54094 }
54095
54096 // API
54097
54098 this.start = start;
54099 }
54100
54101 MoveEvents.$inject = [
54102 'eventBus',
54103 'dragging',
54104 'modeling',
54105 'selection',
54106 'rules'
54107 ];
54108
54109
54110 /**
54111 * Return a filtered list of elements that do not contain
54112 * those nested into others.
54113 *
54114 * @param {Array<djs.model.Base>} elements
54115 *
54116 * @return {Array<djs.model.Base>} filtered
54117 */
54118 function removeNested(elements) {
54119
54120 var ids = groupBy(elements, 'id');
54121
54122 return filter(elements, function(element) {
54123 while ((element = element.parent)) {
54124
54125 // parent in selection
54126 if (ids[element.id]) {
54127 return false;
54128 }
54129 }
54130
54131 return true;
54132 });
54133 }
54134
54135 var LOW_PRIORITY$1 = 499;
54136
54137 var MARKER_DRAGGING = 'djs-dragging',
54138 MARKER_OK$1 = 'drop-ok',
54139 MARKER_NOT_OK$1 = 'drop-not-ok',
54140 MARKER_NEW_PARENT = 'new-parent',
54141 MARKER_ATTACH = 'attach-ok';
54142
54143
54144 /**
54145 * Provides previews for moving shapes when moving.
54146 *
54147 * @param {EventBus} eventBus
54148 * @param {ElementRegistry} elementRegistry
54149 * @param {Canvas} canvas
54150 * @param {Styles} styles
54151 */
54152 function MovePreview(
54153 eventBus, canvas, styles, previewSupport) {
54154
54155 function getVisualDragShapes(shapes) {
54156 var elements = getAllDraggedElements(shapes);
54157
54158 var filteredElements = removeEdges(elements);
54159
54160 return filteredElements;
54161 }
54162
54163 function getAllDraggedElements(shapes) {
54164 var allShapes = selfAndAllChildren(shapes, true);
54165
54166 var allConnections = map$1(allShapes, function(shape) {
54167 return (shape.incoming || []).concat(shape.outgoing || []);
54168 });
54169
54170 return flatten(allShapes.concat(allConnections));
54171 }
54172
54173 /**
54174 * Sets drop marker on an element.
54175 */
54176 function setMarker(element, marker) {
54177
54178 [ MARKER_ATTACH, MARKER_OK$1, MARKER_NOT_OK$1, MARKER_NEW_PARENT ].forEach(function(m) {
54179
54180 if (m === marker) {
54181 canvas.addMarker(element, m);
54182 } else {
54183 canvas.removeMarker(element, m);
54184 }
54185 });
54186 }
54187
54188 /**
54189 * Make an element draggable.
54190 *
54191 * @param {Object} context
54192 * @param {djs.model.Base} element
54193 * @param {boolean} addMarker
54194 */
54195 function makeDraggable(context, element, addMarker) {
54196
54197 previewSupport.addDragger(element, context.dragGroup);
54198
54199 if (addMarker) {
54200 canvas.addMarker(element, MARKER_DRAGGING);
54201 }
54202
54203 if (context.allDraggedElements) {
54204 context.allDraggedElements.push(element);
54205 } else {
54206 context.allDraggedElements = [ element ];
54207 }
54208 }
54209
54210 // assign a low priority to this handler
54211 // to let others modify the move context before
54212 // we draw things
54213 eventBus.on('shape.move.start', LOW_PRIORITY$1, function(event) {
54214 var context = event.context,
54215 dragShapes = context.shapes,
54216 allDraggedElements = context.allDraggedElements;
54217
54218 var visuallyDraggedShapes = getVisualDragShapes(dragShapes);
54219
54220 if (!context.dragGroup) {
54221 var dragGroup = create$1('g');
54222
54223 attr(dragGroup, styles.cls('djs-drag-group', [ 'no-events' ]));
54224
54225 var activeLayer = canvas.getActiveLayer();
54226
54227 append(activeLayer, dragGroup);
54228
54229 context.dragGroup = dragGroup;
54230 }
54231
54232 // add previews
54233 visuallyDraggedShapes.forEach(function(shape) {
54234 previewSupport.addDragger(shape, context.dragGroup);
54235 });
54236
54237 // cache all dragged elements / gfx
54238 // so that we can quickly undo their state changes later
54239 if (!allDraggedElements) {
54240 allDraggedElements = getAllDraggedElements(dragShapes);
54241 } else {
54242 allDraggedElements = flatten([
54243 allDraggedElements,
54244 getAllDraggedElements(dragShapes)
54245 ]);
54246 }
54247
54248 // add dragging marker
54249 forEach(allDraggedElements, function(e) {
54250 canvas.addMarker(e, MARKER_DRAGGING);
54251 });
54252
54253 context.allDraggedElements = allDraggedElements;
54254
54255 // determine, if any of the dragged elements have different parents
54256 context.differentParents = haveDifferentParents(dragShapes);
54257 });
54258
54259 // update previews
54260 eventBus.on('shape.move.move', LOW_PRIORITY$1, function(event) {
54261
54262 var context = event.context,
54263 dragGroup = context.dragGroup,
54264 target = context.target,
54265 parent = context.shape.parent,
54266 canExecute = context.canExecute;
54267
54268 if (target) {
54269 if (canExecute === 'attach') {
54270 setMarker(target, MARKER_ATTACH);
54271 } else if (context.canExecute && target && target.id !== parent.id) {
54272 setMarker(target, MARKER_NEW_PARENT);
54273 } else {
54274 setMarker(target, context.canExecute ? MARKER_OK$1 : MARKER_NOT_OK$1);
54275 }
54276 }
54277
54278 translate$2(dragGroup, event.dx, event.dy);
54279 });
54280
54281 eventBus.on([ 'shape.move.out', 'shape.move.cleanup' ], function(event) {
54282 var context = event.context,
54283 target = context.target;
54284
54285 if (target) {
54286 setMarker(target, null);
54287 }
54288 });
54289
54290 // remove previews
54291 eventBus.on('shape.move.cleanup', function(event) {
54292
54293 var context = event.context,
54294 allDraggedElements = context.allDraggedElements,
54295 dragGroup = context.dragGroup;
54296
54297
54298 // remove dragging marker
54299 forEach(allDraggedElements, function(e) {
54300 canvas.removeMarker(e, MARKER_DRAGGING);
54301 });
54302
54303 if (dragGroup) {
54304 remove$1(dragGroup);
54305 }
54306 });
54307
54308
54309 // API //////////////////////
54310
54311 /**
54312 * Make an element draggable.
54313 *
54314 * @param {Object} context
54315 * @param {djs.model.Base} element
54316 * @param {boolean} addMarker
54317 */
54318 this.makeDraggable = makeDraggable;
54319 }
54320
54321 MovePreview.$inject = [
54322 'eventBus',
54323 'canvas',
54324 'styles',
54325 'previewSupport'
54326 ];
54327
54328
54329 // helpers //////////////////////
54330
54331 /**
54332 * returns elements minus all connections
54333 * where source or target is not elements
54334 */
54335 function removeEdges(elements) {
54336
54337 var filteredElements = filter(elements, function(element) {
54338
54339 if (!isConnection$2(element)) {
54340 return true;
54341 } else {
54342
54343 return (
54344 find(elements, matchPattern({ id: element.source.id })) &&
54345 find(elements, matchPattern({ id: element.target.id }))
54346 );
54347 }
54348 });
54349
54350 return filteredElements;
54351 }
54352
54353 function haveDifferentParents(elements) {
54354 return size(groupBy(elements, function(e) { return e.parent && e.parent.id; })) !== 1;
54355 }
54356
54357 /**
54358 * Checks if an element is a connection.
54359 */
54360 function isConnection$2(element) {
54361 return element.waypoints;
54362 }
54363
54364 var MoveModule = {
54365 __depends__: [
54366 InteractionEventsModule$1,
54367 SelectionModule,
54368 OutlineModule,
54369 RulesModule$1,
54370 DraggingModule,
54371 PreviewSupportModule
54372 ],
54373 __init__: [
54374 'move',
54375 'movePreview'
54376 ],
54377 move: [ 'type', MoveEvents ],
54378 movePreview: [ 'type', MovePreview ]
54379 };
54380
54381 var TOGGLE_SELECTOR = '.djs-palette-toggle',
54382 ENTRY_SELECTOR = '.entry',
54383 ELEMENT_SELECTOR = TOGGLE_SELECTOR + ', ' + ENTRY_SELECTOR;
54384
54385 var PALETTE_OPEN_CLS = 'open',
54386 PALETTE_TWO_COLUMN_CLS = 'two-column';
54387
54388 var DEFAULT_PRIORITY = 1000;
54389
54390
54391 /**
54392 * A palette containing modeling elements.
54393 */
54394 function Palette(eventBus, canvas) {
54395
54396 this._eventBus = eventBus;
54397 this._canvas = canvas;
54398
54399 var self = this;
54400
54401 eventBus.on('tool-manager.update', function(event) {
54402 var tool = event.tool;
54403
54404 self.updateToolHighlight(tool);
54405 });
54406
54407 eventBus.on('i18n.changed', function() {
54408 self._update();
54409 });
54410
54411 eventBus.on('diagram.init', function() {
54412
54413 self._diagramInitialized = true;
54414
54415 self._rebuild();
54416 });
54417 }
54418
54419 Palette.$inject = [ 'eventBus', 'canvas' ];
54420
54421
54422 /**
54423 * Register a provider with the palette
54424 *
54425 * @param {number} [priority=1000]
54426 * @param {PaletteProvider} provider
54427 *
54428 * @example
54429 * const paletteProvider = {
54430 * getPaletteEntries: function() {
54431 * return function(entries) {
54432 * return {
54433 * ...entries,
54434 * 'entry-1': {
54435 * label: 'My Entry',
54436 * action: function() { alert("I have been clicked!"); }
54437 * }
54438 * };
54439 * }
54440 * }
54441 * };
54442 *
54443 * palette.registerProvider(800, paletteProvider);
54444 */
54445 Palette.prototype.registerProvider = function(priority, provider) {
54446 if (!provider) {
54447 provider = priority;
54448 priority = DEFAULT_PRIORITY;
54449 }
54450
54451 this._eventBus.on('palette.getProviders', priority, function(event) {
54452 event.providers.push(provider);
54453 });
54454
54455 this._rebuild();
54456 };
54457
54458
54459 /**
54460 * Returns the palette entries
54461 *
54462 * @return {Object<string, PaletteEntryDescriptor>} map of entries
54463 */
54464 Palette.prototype.getEntries = function() {
54465 var providers = this._getProviders();
54466
54467 return providers.reduce(addPaletteEntries, {});
54468 };
54469
54470 Palette.prototype._rebuild = function() {
54471
54472 if (!this._diagramInitialized) {
54473 return;
54474 }
54475
54476 var providers = this._getProviders();
54477
54478 if (!providers.length) {
54479 return;
54480 }
54481
54482 if (!this._container) {
54483 this._init();
54484 }
54485
54486 this._update();
54487 };
54488
54489 /**
54490 * Initialize
54491 */
54492 Palette.prototype._init = function() {
54493
54494 var self = this;
54495
54496 var eventBus = this._eventBus;
54497
54498 var parentContainer = this._getParentContainer();
54499
54500 var container = this._container = domify(Palette.HTML_MARKUP);
54501
54502 parentContainer.appendChild(container);
54503
54504 delegate.bind(container, ELEMENT_SELECTOR, 'click', function(event) {
54505
54506 var target = event.delegateTarget;
54507
54508 if (matchesSelector(target, TOGGLE_SELECTOR)) {
54509 return self.toggle();
54510 }
54511
54512 self.trigger('click', event);
54513 });
54514
54515 // prevent drag propagation
54516 componentEvent.bind(container, 'mousedown', function(event) {
54517 event.stopPropagation();
54518 });
54519
54520 // prevent drag propagation
54521 delegate.bind(container, ENTRY_SELECTOR, 'dragstart', function(event) {
54522 self.trigger('dragstart', event);
54523 });
54524
54525 eventBus.on('canvas.resized', this._layoutChanged, this);
54526
54527 eventBus.fire('palette.create', {
54528 container: container
54529 });
54530 };
54531
54532 Palette.prototype._getProviders = function(id) {
54533
54534 var event = this._eventBus.createEvent({
54535 type: 'palette.getProviders',
54536 providers: []
54537 });
54538
54539 this._eventBus.fire(event);
54540
54541 return event.providers;
54542 };
54543
54544 /**
54545 * Update palette state.
54546 *
54547 * @param {Object} [state] { open, twoColumn }
54548 */
54549 Palette.prototype._toggleState = function(state) {
54550
54551 state = state || {};
54552
54553 var parent = this._getParentContainer(),
54554 container = this._container;
54555
54556 var eventBus = this._eventBus;
54557
54558 var twoColumn;
54559
54560 var cls = classes$1(container);
54561
54562 if ('twoColumn' in state) {
54563 twoColumn = state.twoColumn;
54564 } else {
54565 twoColumn = this._needsCollapse(parent.clientHeight, this._entries || {});
54566 }
54567
54568 // always update two column
54569 cls.toggle(PALETTE_TWO_COLUMN_CLS, twoColumn);
54570
54571 if ('open' in state) {
54572 cls.toggle(PALETTE_OPEN_CLS, state.open);
54573 }
54574
54575 eventBus.fire('palette.changed', {
54576 twoColumn: twoColumn,
54577 open: this.isOpen()
54578 });
54579 };
54580
54581 Palette.prototype._update = function() {
54582
54583 var entriesContainer = query('.djs-palette-entries', this._container),
54584 entries = this._entries = this.getEntries();
54585
54586 clear$1(entriesContainer);
54587
54588 forEach(entries, function(entry, id) {
54589
54590 var grouping = entry.group || 'default';
54591
54592 var container = query('[data-group=' + grouping + ']', entriesContainer);
54593 if (!container) {
54594 container = domify('<div class="group" data-group="' + grouping + '"></div>');
54595 entriesContainer.appendChild(container);
54596 }
54597
54598 var html = entry.html || (
54599 entry.separator ?
54600 '<hr class="separator" />' :
54601 '<div class="entry" draggable="true"></div>');
54602
54603
54604 var control = domify(html);
54605 container.appendChild(control);
54606
54607 if (!entry.separator) {
54608 attr$1(control, 'data-action', id);
54609
54610 if (entry.title) {
54611 attr$1(control, 'title', entry.title);
54612 }
54613
54614 if (entry.className) {
54615 addClasses(control, entry.className);
54616 }
54617
54618 if (entry.imageUrl) {
54619 control.appendChild(domify('<img src="' + entry.imageUrl + '">'));
54620 }
54621 }
54622 });
54623
54624 // open after update
54625 this.open();
54626 };
54627
54628
54629 /**
54630 * Trigger an action available on the palette
54631 *
54632 * @param {string} action
54633 * @param {Event} event
54634 */
54635 Palette.prototype.trigger = function(action, event, autoActivate) {
54636 var entries = this._entries,
54637 entry,
54638 handler,
54639 originalEvent,
54640 button = event.delegateTarget || event.target;
54641
54642 if (!button) {
54643 return event.preventDefault();
54644 }
54645
54646 entry = entries[attr$1(button, 'data-action')];
54647
54648 // when user clicks on the palette and not on an action
54649 if (!entry) {
54650 return;
54651 }
54652
54653 handler = entry.action;
54654
54655 originalEvent = event.originalEvent || event;
54656
54657 // simple action (via callback function)
54658 if (isFunction(handler)) {
54659 if (action === 'click') {
54660 handler(originalEvent, autoActivate);
54661 }
54662 } else {
54663 if (handler[action]) {
54664 handler[action](originalEvent, autoActivate);
54665 }
54666 }
54667
54668 // silence other actions
54669 event.preventDefault();
54670 };
54671
54672 Palette.prototype._layoutChanged = function() {
54673 this._toggleState({});
54674 };
54675
54676 /**
54677 * Do we need to collapse to two columns?
54678 *
54679 * @param {number} availableHeight
54680 * @param {Object} entries
54681 *
54682 * @return {boolean}
54683 */
54684 Palette.prototype._needsCollapse = function(availableHeight, entries) {
54685
54686 // top margin + bottom toggle + bottom margin
54687 // implementors must override this method if they
54688 // change the palette styles
54689 var margin = 20 + 10 + 20;
54690
54691 var entriesHeight = Object.keys(entries).length * 46;
54692
54693 return availableHeight < entriesHeight + margin;
54694 };
54695
54696 /**
54697 * Close the palette
54698 */
54699 Palette.prototype.close = function() {
54700
54701 this._toggleState({
54702 open: false,
54703 twoColumn: false
54704 });
54705 };
54706
54707
54708 /**
54709 * Open the palette
54710 */
54711 Palette.prototype.open = function() {
54712 this._toggleState({ open: true });
54713 };
54714
54715
54716 Palette.prototype.toggle = function(open) {
54717 if (this.isOpen()) {
54718 this.close();
54719 } else {
54720 this.open();
54721 }
54722 };
54723
54724 Palette.prototype.isActiveTool = function(tool) {
54725 return tool && this._activeTool === tool;
54726 };
54727
54728 Palette.prototype.updateToolHighlight = function(name) {
54729 var entriesContainer,
54730 toolsContainer;
54731
54732 if (!this._toolsContainer) {
54733 entriesContainer = query('.djs-palette-entries', this._container);
54734
54735 this._toolsContainer = query('[data-group=tools]', entriesContainer);
54736 }
54737
54738 toolsContainer = this._toolsContainer;
54739
54740 forEach(toolsContainer.children, function(tool) {
54741 var actionName = tool.getAttribute('data-action');
54742
54743 if (!actionName) {
54744 return;
54745 }
54746
54747 var toolClasses = classes$1(tool);
54748
54749 actionName = actionName.replace('-tool', '');
54750
54751 if (toolClasses.contains('entry') && actionName === name) {
54752 toolClasses.add('highlighted-entry');
54753 } else {
54754 toolClasses.remove('highlighted-entry');
54755 }
54756 });
54757 };
54758
54759
54760 /**
54761 * Return true if the palette is opened.
54762 *
54763 * @example
54764 *
54765 * palette.open();
54766 *
54767 * if (palette.isOpen()) {
54768 * // yes, we are open
54769 * }
54770 *
54771 * @return {boolean} true if palette is opened
54772 */
54773 Palette.prototype.isOpen = function() {
54774 return classes$1(this._container).has(PALETTE_OPEN_CLS);
54775 };
54776
54777 /**
54778 * Get container the palette lives in.
54779 *
54780 * @return {Element}
54781 */
54782 Palette.prototype._getParentContainer = function() {
54783 return this._canvas.getContainer();
54784 };
54785
54786
54787 /* markup definition */
54788
54789 Palette.HTML_MARKUP =
54790 '<div class="djs-palette">' +
54791 '<div class="djs-palette-entries"></div>' +
54792 '<div class="djs-palette-toggle"></div>' +
54793 '</div>';
54794
54795
54796 // helpers //////////////////////
54797
54798 function addClasses(element, classNames) {
54799
54800 var classes = classes$1(element);
54801
54802 var actualClassNames = isArray$2(classNames) ? classNames : classNames.split(/\s+/g);
54803 actualClassNames.forEach(function(cls) {
54804 classes.add(cls);
54805 });
54806 }
54807
54808 function addPaletteEntries(entries, provider) {
54809
54810 var entriesOrUpdater = provider.getPaletteEntries();
54811
54812 if (isFunction(entriesOrUpdater)) {
54813 return entriesOrUpdater(entries);
54814 }
54815
54816 forEach(entriesOrUpdater, function(entry, id) {
54817 entries[id] = entry;
54818 });
54819
54820 return entries;
54821 }
54822
54823 var PaletteModule$1 = {
54824 __init__: [ 'palette' ],
54825 palette: [ 'type', Palette ]
54826 };
54827
54828 var LASSO_TOOL_CURSOR = 'crosshair';
54829
54830
54831 function LassoTool(
54832 eventBus, canvas, dragging,
54833 elementRegistry, selection, toolManager,
54834 mouse) {
54835
54836 this._selection = selection;
54837 this._dragging = dragging;
54838 this._mouse = mouse;
54839
54840 var self = this;
54841
54842 // lasso visuals implementation
54843
54844 /**
54845 * A helper that realizes the selection box visual
54846 */
54847 var visuals = {
54848
54849 create: function(context) {
54850 var container = canvas.getActiveLayer(),
54851 frame;
54852
54853 frame = context.frame = create$1('rect');
54854 attr(frame, {
54855 class: 'djs-lasso-overlay',
54856 width: 1,
54857 height: 1,
54858 x: 0,
54859 y: 0
54860 });
54861
54862 append(container, frame);
54863 },
54864
54865 update: function(context) {
54866 var frame = context.frame,
54867 bbox = context.bbox;
54868
54869 attr(frame, {
54870 x: bbox.x,
54871 y: bbox.y,
54872 width: bbox.width,
54873 height: bbox.height
54874 });
54875 },
54876
54877 remove: function(context) {
54878
54879 if (context.frame) {
54880 remove$1(context.frame);
54881 }
54882 }
54883 };
54884
54885 toolManager.registerTool('lasso', {
54886 tool: 'lasso.selection',
54887 dragging: 'lasso'
54888 });
54889
54890 eventBus.on('lasso.selection.end', function(event) {
54891 var target = event.originalEvent.target;
54892
54893 // only reactive on diagram click
54894 // on some occasions, event.hover is not set and we have to check if the target is an svg
54895 if (!event.hover && !(target instanceof SVGElement)) {
54896 return;
54897 }
54898
54899 eventBus.once('lasso.selection.ended', function() {
54900 self.activateLasso(event.originalEvent, true);
54901 });
54902 });
54903
54904 // lasso interaction implementation
54905
54906 eventBus.on('lasso.end', function(event) {
54907
54908 var bbox = toBBox(event);
54909
54910 var elements = elementRegistry.filter(function(element) {
54911 return element;
54912 });
54913
54914 self.select(elements, bbox);
54915 });
54916
54917 eventBus.on('lasso.start', function(event) {
54918
54919 var context = event.context;
54920
54921 context.bbox = toBBox(event);
54922 visuals.create(context);
54923 });
54924
54925 eventBus.on('lasso.move', function(event) {
54926
54927 var context = event.context;
54928
54929 context.bbox = toBBox(event);
54930 visuals.update(context);
54931 });
54932
54933 eventBus.on('lasso.cleanup', function(event) {
54934
54935 var context = event.context;
54936
54937 visuals.remove(context);
54938 });
54939
54940
54941 // event integration
54942
54943 eventBus.on('element.mousedown', 1500, function(event) {
54944
54945 if (!hasSecondaryModifier(event)) {
54946 return;
54947 }
54948
54949 self.activateLasso(event.originalEvent);
54950
54951 // we've handled the event
54952 return true;
54953 });
54954 }
54955
54956 LassoTool.$inject = [
54957 'eventBus',
54958 'canvas',
54959 'dragging',
54960 'elementRegistry',
54961 'selection',
54962 'toolManager',
54963 'mouse'
54964 ];
54965
54966
54967 LassoTool.prototype.activateLasso = function(event, autoActivate) {
54968
54969 this._dragging.init(event, 'lasso', {
54970 autoActivate: autoActivate,
54971 cursor: LASSO_TOOL_CURSOR,
54972 data: {
54973 context: {}
54974 }
54975 });
54976 };
54977
54978 LassoTool.prototype.activateSelection = function(event, autoActivate) {
54979
54980 this._dragging.init(event, 'lasso.selection', {
54981 trapClick: false,
54982 autoActivate: autoActivate,
54983 cursor: LASSO_TOOL_CURSOR,
54984 data: {
54985 context: {}
54986 }
54987 });
54988 };
54989
54990 LassoTool.prototype.select = function(elements, bbox) {
54991 var selectedElements = getEnclosedElements(elements, bbox);
54992
54993 this._selection.select(values(selectedElements));
54994 };
54995
54996 LassoTool.prototype.toggle = function() {
54997 if (this.isActive()) {
54998 return this._dragging.cancel();
54999 }
55000
55001 var mouseEvent = this._mouse.getLastMoveEvent();
55002
55003 this.activateSelection(mouseEvent, !!mouseEvent);
55004 };
55005
55006 LassoTool.prototype.isActive = function() {
55007 var context = this._dragging.context();
55008
55009 return context && /^lasso/.test(context.prefix);
55010 };
55011
55012
55013
55014 function toBBox(event) {
55015
55016 var start = {
55017
55018 x: event.x - event.dx,
55019 y: event.y - event.dy
55020 };
55021
55022 var end = {
55023 x: event.x,
55024 y: event.y
55025 };
55026
55027 var bbox;
55028
55029 if ((start.x <= end.x && start.y < end.y) ||
55030 (start.x < end.x && start.y <= end.y)) {
55031
55032 bbox = {
55033 x: start.x,
55034 y: start.y,
55035 width: end.x - start.x,
55036 height: end.y - start.y
55037 };
55038 } else if ((start.x >= end.x && start.y < end.y) ||
55039 (start.x > end.x && start.y <= end.y)) {
55040
55041 bbox = {
55042 x: end.x,
55043 y: start.y,
55044 width: start.x - end.x,
55045 height: end.y - start.y
55046 };
55047 } else if ((start.x <= end.x && start.y > end.y) ||
55048 (start.x < end.x && start.y >= end.y)) {
55049
55050 bbox = {
55051 x: start.x,
55052 y: end.y,
55053 width: end.x - start.x,
55054 height: start.y - end.y
55055 };
55056 } else if ((start.x >= end.x && start.y > end.y) ||
55057 (start.x > end.x && start.y >= end.y)) {
55058
55059 bbox = {
55060 x: end.x,
55061 y: end.y,
55062 width: start.x - end.x,
55063 height: start.y - end.y
55064 };
55065 } else {
55066
55067 bbox = {
55068 x: end.x,
55069 y: end.y,
55070 width: 0,
55071 height: 0
55072 };
55073 }
55074 return bbox;
55075 }
55076
55077 var LassoToolModule = {
55078 __depends__: [
55079 ToolManagerModule,
55080 MouseModule
55081 ],
55082 __init__: [ 'lassoTool' ],
55083 lassoTool: [ 'type', LassoTool ]
55084 };
55085
55086 var HIGH_PRIORITY$1 = 1500;
55087 var HAND_CURSOR = 'grab';
55088
55089
55090 function HandTool(
55091 eventBus, canvas, dragging,
55092 injector, toolManager, mouse) {
55093
55094 this._dragging = dragging;
55095 this._mouse = mouse;
55096
55097 var self = this,
55098 keyboard = injector.get('keyboard', false);
55099
55100 toolManager.registerTool('hand', {
55101 tool: 'hand',
55102 dragging: 'hand.move'
55103 });
55104
55105 eventBus.on('element.mousedown', HIGH_PRIORITY$1, function(event) {
55106
55107 if (!hasPrimaryModifier(event)) {
55108 return;
55109 }
55110
55111 self.activateMove(event.originalEvent, true);
55112
55113 return false;
55114 });
55115
55116 keyboard && keyboard.addListener(HIGH_PRIORITY$1, function(e) {
55117 if (!isSpace(e.keyEvent) || self.isActive()) {
55118 return;
55119 }
55120
55121 var mouseEvent = self._mouse.getLastMoveEvent();
55122
55123 self.activateMove(mouseEvent, !!mouseEvent);
55124 }, 'keyboard.keydown');
55125
55126 keyboard && keyboard.addListener(HIGH_PRIORITY$1, function(e) {
55127 if (!isSpace(e.keyEvent) || !self.isActive()) {
55128 return;
55129 }
55130
55131 self.toggle();
55132 }, 'keyboard.keyup');
55133
55134 eventBus.on('hand.end', function(event) {
55135 var target = event.originalEvent.target;
55136
55137 // only reactive on diagram click
55138 // on some occasions, event.hover is not set and we have to check if the target is an svg
55139 if (!event.hover && !(target instanceof SVGElement)) {
55140 return false;
55141 }
55142
55143 eventBus.once('hand.ended', function() {
55144 self.activateMove(event.originalEvent, { reactivate: true });
55145 });
55146
55147 });
55148
55149 eventBus.on('hand.move.move', function(event) {
55150 var scale = canvas.viewbox().scale;
55151
55152 canvas.scroll({
55153 dx: event.dx * scale,
55154 dy: event.dy * scale
55155 });
55156 });
55157
55158 eventBus.on('hand.move.end', function(event) {
55159 var context = event.context,
55160 reactivate = context.reactivate;
55161
55162 // Don't reactivate if the user is using the keyboard keybinding
55163 if (!hasPrimaryModifier(event) && reactivate) {
55164
55165 eventBus.once('hand.move.ended', function(event) {
55166 self.activateHand(event.originalEvent, true, true);
55167 });
55168
55169 }
55170
55171 return false;
55172 });
55173
55174 }
55175
55176 HandTool.$inject = [
55177 'eventBus',
55178 'canvas',
55179 'dragging',
55180 'injector',
55181 'toolManager',
55182 'mouse'
55183 ];
55184
55185
55186 HandTool.prototype.activateMove = function(event, autoActivate, context) {
55187 if (typeof autoActivate === 'object') {
55188 context = autoActivate;
55189 autoActivate = false;
55190 }
55191
55192 this._dragging.init(event, 'hand.move', {
55193 autoActivate: autoActivate,
55194 cursor: HAND_CURSOR,
55195 data: {
55196 context: context || {}
55197 }
55198 });
55199 };
55200
55201 HandTool.prototype.activateHand = function(event, autoActivate, reactivate) {
55202 this._dragging.init(event, 'hand', {
55203 trapClick: false,
55204 autoActivate: autoActivate,
55205 cursor: HAND_CURSOR,
55206 data: {
55207 context: {
55208 reactivate: reactivate
55209 }
55210 }
55211 });
55212 };
55213
55214 HandTool.prototype.toggle = function() {
55215 if (this.isActive()) {
55216 return this._dragging.cancel();
55217 }
55218
55219 var mouseEvent = this._mouse.getLastMoveEvent();
55220
55221 this.activateHand(mouseEvent, !!mouseEvent);
55222 };
55223
55224 HandTool.prototype.isActive = function() {
55225 var context = this._dragging.context();
55226
55227 if (context) {
55228 return /^(hand|hand\.move)$/.test(context.prefix);
55229 }
55230
55231 return false;
55232 };
55233
55234 // helpers //////////
55235
55236 function isSpace(keyEvent) {
55237 return isKey(' ', keyEvent);
55238 }
55239
55240 var HandToolModule = {
55241 __depends__: [
55242 ToolManagerModule,
55243 MouseModule
55244 ],
55245 __init__: [ 'handTool' ],
55246 handTool: [ 'type', HandTool ]
55247 };
55248
55249 var MARKER_OK = 'connect-ok',
55250 MARKER_NOT_OK = 'connect-not-ok';
55251
55252 /**
55253 * @class
55254 * @constructor
55255 *
55256 * @param {EventBus} eventBus
55257 * @param {Dragging} dragging
55258 * @param {Connect} connect
55259 * @param {Canvas} canvas
55260 * @param {ToolManager} toolManager
55261 * @param {Rules} rules
55262 * @param {Mouse} mouse
55263 */
55264 function GlobalConnect(
55265 eventBus, dragging, connect,
55266 canvas, toolManager, rules,
55267 mouse) {
55268
55269 var self = this;
55270
55271 this._dragging = dragging;
55272 this._rules = rules;
55273 this._mouse = mouse;
55274
55275 toolManager.registerTool('global-connect', {
55276 tool: 'global-connect',
55277 dragging: 'global-connect.drag'
55278 });
55279
55280 eventBus.on('global-connect.hover', function(event) {
55281 var context = event.context,
55282 startTarget = event.hover;
55283
55284 var canStartConnect = context.canStartConnect = self.canStartConnect(startTarget);
55285
55286 // simply ignore hover
55287 if (canStartConnect === null) {
55288 return;
55289 }
55290
55291 context.startTarget = startTarget;
55292
55293 canvas.addMarker(startTarget, canStartConnect ? MARKER_OK : MARKER_NOT_OK);
55294 });
55295
55296
55297 eventBus.on([ 'global-connect.out', 'global-connect.cleanup' ], function(event) {
55298 var startTarget = event.context.startTarget,
55299 canStartConnect = event.context.canStartConnect;
55300
55301 if (startTarget) {
55302 canvas.removeMarker(startTarget, canStartConnect ? MARKER_OK : MARKER_NOT_OK);
55303 }
55304 });
55305
55306
55307 eventBus.on([ 'global-connect.ended' ], function(event) {
55308 var context = event.context,
55309 startTarget = context.startTarget,
55310 startPosition = {
55311 x: event.x,
55312 y: event.y
55313 };
55314
55315 var canStartConnect = self.canStartConnect(startTarget);
55316
55317 if (!canStartConnect) {
55318 return;
55319 }
55320
55321 eventBus.once('element.out', function() {
55322 eventBus.once([ 'connect.ended', 'connect.canceled' ], function() {
55323 eventBus.fire('global-connect.drag.ended');
55324 });
55325
55326 connect.start(null, startTarget, startPosition);
55327 });
55328
55329 return false;
55330 });
55331 }
55332
55333 GlobalConnect.$inject = [
55334 'eventBus',
55335 'dragging',
55336 'connect',
55337 'canvas',
55338 'toolManager',
55339 'rules',
55340 'mouse'
55341 ];
55342
55343 /**
55344 * Initiates tool activity.
55345 */
55346 GlobalConnect.prototype.start = function(event, autoActivate) {
55347 this._dragging.init(event, 'global-connect', {
55348 autoActivate: autoActivate,
55349 trapClick: false,
55350 data: {
55351 context: {}
55352 }
55353 });
55354 };
55355
55356 GlobalConnect.prototype.toggle = function() {
55357
55358 if (this.isActive()) {
55359 return this._dragging.cancel();
55360 }
55361
55362 var mouseEvent = this._mouse.getLastMoveEvent();
55363
55364 return this.start(mouseEvent, !!mouseEvent);
55365 };
55366
55367 GlobalConnect.prototype.isActive = function() {
55368 var context = this._dragging.context();
55369
55370 return context && /^global-connect/.test(context.prefix);
55371 };
55372
55373 /**
55374 * Check if source shape can initiate connection.
55375 *
55376 * @param {Shape} startTarget
55377 * @return {boolean}
55378 */
55379 GlobalConnect.prototype.canStartConnect = function(startTarget) {
55380 return this._rules.allowed('connection.start', { source: startTarget });
55381 };
55382
55383 var GlobalConnectModule = {
55384 __depends__: [
55385 ConnectModule,
55386 RulesModule$1,
55387 DraggingModule,
55388 ToolManagerModule,
55389 MouseModule
55390 ],
55391 globalConnect: [ 'type', GlobalConnect ]
55392 };
55393
55394 /**
55395 * A palette provider for BPMN 2.0 elements.
55396 */
55397 function PaletteProvider(
55398 palette, create, elementFactory,
55399 spaceTool, lassoTool, handTool,
55400 globalConnect, translate) {
55401
55402 this._palette = palette;
55403 this._create = create;
55404 this._elementFactory = elementFactory;
55405 this._spaceTool = spaceTool;
55406 this._lassoTool = lassoTool;
55407 this._handTool = handTool;
55408 this._globalConnect = globalConnect;
55409 this._translate = translate;
55410
55411 palette.registerProvider(this);
55412 }
55413
55414 PaletteProvider.$inject = [
55415 'palette',
55416 'create',
55417 'elementFactory',
55418 'spaceTool',
55419 'lassoTool',
55420 'handTool',
55421 'globalConnect',
55422 'translate'
55423 ];
55424
55425
55426 PaletteProvider.prototype.getPaletteEntries = function(element) {
55427
55428 var actions = {},
55429 create = this._create,
55430 elementFactory = this._elementFactory,
55431 spaceTool = this._spaceTool,
55432 lassoTool = this._lassoTool,
55433 handTool = this._handTool,
55434 globalConnect = this._globalConnect,
55435 translate = this._translate;
55436
55437 function createAction(type, group, className, title, options) {
55438
55439 function createListener(event) {
55440 var shape = elementFactory.createShape(assign({ type: type }, options));
55441
55442 if (options) {
55443 shape.businessObject.di.isExpanded = options.isExpanded;
55444 }
55445
55446 create.start(event, shape);
55447 }
55448
55449 var shortType = type.replace(/^bpmn:/, '');
55450
55451 return {
55452 group: group,
55453 className: className,
55454 title: title || translate('Create {type}', { type: shortType }),
55455 action: {
55456 dragstart: createListener,
55457 click: createListener
55458 }
55459 };
55460 }
55461
55462 function createSubprocess(event) {
55463 var subProcess = elementFactory.createShape({
55464 type: 'bpmn:SubProcess',
55465 x: 0,
55466 y: 0,
55467 isExpanded: true
55468 });
55469
55470 var startEvent = elementFactory.createShape({
55471 type: 'bpmn:StartEvent',
55472 x: 40,
55473 y: 82,
55474 parent: subProcess
55475 });
55476
55477 create.start(event, [ subProcess, startEvent ], {
55478 hints: {
55479 autoSelect: [ startEvent ]
55480 }
55481 });
55482 }
55483
55484 function createParticipant(event) {
55485 create.start(event, elementFactory.createParticipantShape());
55486 }
55487
55488 assign(actions, {
55489 'hand-tool': {
55490 group: 'tools',
55491 className: 'bpmn-icon-hand-tool',
55492 title: translate('Activate the hand tool'),
55493 action: {
55494 click: function(event) {
55495 handTool.activateHand(event);
55496 }
55497 }
55498 },
55499 'lasso-tool': {
55500 group: 'tools',
55501 className: 'bpmn-icon-lasso-tool',
55502 title: translate('Activate the lasso tool'),
55503 action: {
55504 click: function(event) {
55505 lassoTool.activateSelection(event);
55506 }
55507 }
55508 },
55509 'space-tool': {
55510 group: 'tools',
55511 className: 'bpmn-icon-space-tool',
55512 title: translate('Activate the create/remove space tool'),
55513 action: {
55514 click: function(event) {
55515 spaceTool.activateSelection(event);
55516 }
55517 }
55518 },
55519 'global-connect-tool': {
55520 group: 'tools',
55521 className: 'bpmn-icon-connection-multi',
55522 title: translate('Activate the global connect tool'),
55523 action: {
55524 click: function(event) {
55525 globalConnect.start(event);
55526 }
55527 }
55528 },
55529 'tool-separator': {
55530 group: 'tools',
55531 separator: true
55532 },
55533 'create.start-event': createAction(
55534 'bpmn:StartEvent', 'event', 'bpmn-icon-start-event-none',
55535 translate('Create StartEvent')
55536 ),
55537 'create.intermediate-event': createAction(
55538 'bpmn:IntermediateThrowEvent', 'event', 'bpmn-icon-intermediate-event-none',
55539 translate('Create Intermediate/Boundary Event')
55540 ),
55541 'create.end-event': createAction(
55542 'bpmn:EndEvent', 'event', 'bpmn-icon-end-event-none',
55543 translate('Create EndEvent')
55544 ),
55545 'create.exclusive-gateway': createAction(
55546 'bpmn:ExclusiveGateway', 'gateway', 'bpmn-icon-gateway-none',
55547 translate('Create Gateway')
55548 ),
55549 'create.task': createAction(
55550 'bpmn:Task', 'activity', 'bpmn-icon-task',
55551 translate('Create Task')
55552 ),
55553 'create.data-object': createAction(
55554 'bpmn:DataObjectReference', 'data-object', 'bpmn-icon-data-object',
55555 translate('Create DataObjectReference')
55556 ),
55557 'create.data-store': createAction(
55558 'bpmn:DataStoreReference', 'data-store', 'bpmn-icon-data-store',
55559 translate('Create DataStoreReference')
55560 ),
55561 'create.subprocess-expanded': {
55562 group: 'activity',
55563 className: 'bpmn-icon-subprocess-expanded',
55564 title: translate('Create expanded SubProcess'),
55565 action: {
55566 dragstart: createSubprocess,
55567 click: createSubprocess
55568 }
55569 },
55570 'create.participant-expanded': {
55571 group: 'collaboration',
55572 className: 'bpmn-icon-participant',
55573 title: translate('Create Pool/Participant'),
55574 action: {
55575 dragstart: createParticipant,
55576 click: createParticipant
55577 }
55578 },
55579 'create.group': createAction(
55580 'bpmn:Group', 'artifact', 'bpmn-icon-group',
55581 translate('Create Group')
55582 ),
55583 });
55584
55585 return actions;
55586 };
55587
55588 var PaletteModule = {
55589 __depends__: [
55590 PaletteModule$1,
55591 CreateModule,
55592 SpaceToolModule,
55593 LassoToolModule,
55594 HandToolModule,
55595 GlobalConnectModule,
55596 translate
55597 ],
55598 __init__: [ 'paletteProvider' ],
55599 paletteProvider: [ 'type', PaletteProvider ]
55600 };
55601
55602 var LOW_PRIORITY = 250;
55603
55604
55605 function BpmnReplacePreview(
55606 eventBus, elementRegistry, elementFactory,
55607 canvas, previewSupport) {
55608
55609 CommandInterceptor.call(this, eventBus);
55610
55611 /**
55612 * Replace the visuals of all elements in the context which can be replaced
55613 *
55614 * @param {Object} context
55615 */
55616 function replaceVisual(context) {
55617
55618 var replacements = context.canExecute.replacements;
55619
55620 forEach(replacements, function(replacement) {
55621
55622 var id = replacement.oldElementId;
55623
55624 var newElement = {
55625 type: replacement.newElementType
55626 };
55627
55628 // if the visual of the element is already replaced
55629 if (context.visualReplacements[id]) {
55630 return;
55631 }
55632
55633 var element = elementRegistry.get(id);
55634
55635 assign(newElement, { x: element.x, y: element.y });
55636
55637 // create a temporary shape
55638 var tempShape = elementFactory.createShape(newElement);
55639
55640 canvas.addShape(tempShape, element.parent);
55641
55642 // select the original SVG element related to the element and hide it
55643 var gfx = query('[data-element-id="' + cssEscape(element.id) + '"]', context.dragGroup);
55644
55645 if (gfx) {
55646 attr(gfx, { display: 'none' });
55647 }
55648
55649 // clone the gfx of the temporary shape and add it to the drag group
55650 var dragger = previewSupport.addDragger(tempShape, context.dragGroup);
55651
55652 context.visualReplacements[id] = dragger;
55653
55654 canvas.removeShape(tempShape);
55655 });
55656 }
55657
55658 /**
55659 * Restore the original visuals of the previously replaced elements
55660 *
55661 * @param {Object} context
55662 */
55663 function restoreVisual(context) {
55664
55665 var visualReplacements = context.visualReplacements;
55666
55667 forEach(visualReplacements, function(dragger, id) {
55668
55669 var originalGfx = query('[data-element-id="' + cssEscape(id) + '"]', context.dragGroup);
55670
55671 if (originalGfx) {
55672 attr(originalGfx, { display: 'inline' });
55673 }
55674
55675 dragger.remove();
55676
55677 if (visualReplacements[id]) {
55678 delete visualReplacements[id];
55679 }
55680 });
55681 }
55682
55683 eventBus.on('shape.move.move', LOW_PRIORITY, function(event) {
55684
55685 var context = event.context,
55686 canExecute = context.canExecute;
55687
55688 if (!context.visualReplacements) {
55689 context.visualReplacements = {};
55690 }
55691
55692 if (canExecute && canExecute.replacements) {
55693 replaceVisual(context);
55694 } else {
55695 restoreVisual(context);
55696 }
55697 });
55698 }
55699
55700 BpmnReplacePreview.$inject = [
55701 'eventBus',
55702 'elementRegistry',
55703 'elementFactory',
55704 'canvas',
55705 'previewSupport'
55706 ];
55707
55708 inherits$1(BpmnReplacePreview, CommandInterceptor);
55709
55710 var ReplacePreviewModule = {
55711 __depends__: [
55712 PreviewSupportModule
55713 ],
55714 __init__: [ 'bpmnReplacePreview' ],
55715 bpmnReplacePreview: [ 'type', BpmnReplacePreview ]
55716 };
55717
55718 var HIGHER_PRIORITY$2 = 1250;
55719
55720 var BOUNDARY_TO_HOST_THRESHOLD = 40;
55721
55722 var TARGET_BOUNDS_PADDING = 20,
55723 TASK_BOUNDS_PADDING = 10;
55724
55725 var TARGET_CENTER_PADDING = 20;
55726
55727 var AXES = [ 'x', 'y' ];
55728
55729 var abs = Math.abs;
55730
55731 /**
55732 * Snap during connect.
55733 *
55734 * @param {EventBus} eventBus
55735 */
55736 function BpmnConnectSnapping(eventBus) {
55737 eventBus.on([
55738 'connect.hover',
55739 'connect.move',
55740 'connect.end',
55741 ], HIGHER_PRIORITY$2, function(event) {
55742 var context = event.context,
55743 canExecute = context.canExecute,
55744 start = context.start,
55745 hover = context.hover,
55746 source = context.source,
55747 target = context.target;
55748
55749 // do NOT snap on CMD
55750 if (event.originalEvent && isCmd(event.originalEvent)) {
55751 return;
55752 }
55753
55754 if (!context.initialConnectionStart) {
55755 context.initialConnectionStart = context.connectionStart;
55756 }
55757
55758 // snap hover
55759 if (canExecute && hover) {
55760 snapToShape(event, hover, getTargetBoundsPadding(hover));
55761 }
55762
55763 if (hover && isAnyType(canExecute, [
55764 'bpmn:Association',
55765 'bpmn:DataInputAssociation',
55766 'bpmn:DataOutputAssociation',
55767 'bpmn:SequenceFlow'
55768 ])) {
55769 context.connectionStart = mid$2(start);
55770
55771 // snap hover
55772 if (isAny(hover, [ 'bpmn:Event', 'bpmn:Gateway' ])) {
55773 snapToPosition(event, mid$2(hover));
55774 }
55775
55776 // snap hover
55777 if (isAny(hover, [ 'bpmn:Task', 'bpmn:SubProcess' ])) {
55778 snapToTargetMid(event, hover);
55779 }
55780
55781 // snap source and target
55782 if (is$1(source, 'bpmn:BoundaryEvent') && target === source.host) {
55783 snapBoundaryEventLoop(event);
55784 }
55785
55786 } else if (isType(canExecute, 'bpmn:MessageFlow')) {
55787
55788 if (is$1(start, 'bpmn:Event')) {
55789
55790 // snap start
55791 context.connectionStart = mid$2(start);
55792 }
55793
55794 if (is$1(hover, 'bpmn:Event')) {
55795
55796 // snap hover
55797 snapToPosition(event, mid$2(hover));
55798 }
55799
55800 } else {
55801
55802 // un-snap source
55803 context.connectionStart = context.initialConnectionStart;
55804 }
55805 });
55806 }
55807
55808 BpmnConnectSnapping.$inject = [ 'eventBus' ];
55809
55810
55811 // helpers //////////
55812
55813 // snap to target if event in target
55814 function snapToShape(event, target, padding) {
55815 AXES.forEach(function(axis) {
55816 var dimensionForAxis = getDimensionForAxis(axis, target);
55817
55818 if (event[ axis ] < target[ axis ] + padding) {
55819 setSnapped(event, axis, target[ axis ] + padding);
55820 } else if (event[ axis ] > target[ axis ] + dimensionForAxis - padding) {
55821 setSnapped(event, axis, target[ axis ] + dimensionForAxis - padding);
55822 }
55823 });
55824 }
55825
55826 // snap to target mid if event in target mid
55827 function snapToTargetMid(event, target) {
55828 var targetMid = mid$2(target);
55829
55830 AXES.forEach(function(axis) {
55831 if (isMid(event, target, axis)) {
55832 setSnapped(event, axis, targetMid[ axis ]);
55833 }
55834 });
55835 }
55836
55837 // snap to prevent loop overlapping boundary event
55838 function snapBoundaryEventLoop(event) {
55839 var context = event.context,
55840 source = context.source,
55841 target = context.target;
55842
55843 if (isReverse(context)) {
55844 return;
55845 }
55846
55847 var sourceMid = mid$2(source),
55848 orientation = getOrientation(sourceMid, target, -10),
55849 axes = [];
55850
55851 if (/top|bottom/.test(orientation)) {
55852 axes.push('x');
55853 }
55854
55855 if (/left|right/.test(orientation)) {
55856 axes.push('y');
55857 }
55858
55859 axes.forEach(function(axis) {
55860 var coordinate = event[ axis ], newCoordinate;
55861
55862 if (abs(coordinate - sourceMid[ axis ]) < BOUNDARY_TO_HOST_THRESHOLD) {
55863 if (coordinate > sourceMid[ axis ]) {
55864 newCoordinate = sourceMid[ axis ] + BOUNDARY_TO_HOST_THRESHOLD;
55865 }
55866 else {
55867 newCoordinate = sourceMid[ axis ] - BOUNDARY_TO_HOST_THRESHOLD;
55868 }
55869
55870 setSnapped(event, axis, newCoordinate);
55871 }
55872 });
55873 }
55874
55875 function snapToPosition(event, position) {
55876 setSnapped(event, 'x', position.x);
55877 setSnapped(event, 'y', position.y);
55878 }
55879
55880 function isType(attrs, type) {
55881 return attrs && attrs.type === type;
55882 }
55883
55884 function isAnyType(attrs, types) {
55885 return some(types, function(type) {
55886 return isType(attrs, type);
55887 });
55888 }
55889
55890 function getDimensionForAxis(axis, element) {
55891 return axis === 'x' ? element.width : element.height;
55892 }
55893
55894 function getTargetBoundsPadding(target) {
55895 if (is$1(target, 'bpmn:Task')) {
55896 return TASK_BOUNDS_PADDING;
55897 } else {
55898 return TARGET_BOUNDS_PADDING;
55899 }
55900 }
55901
55902 function isMid(event, target, axis) {
55903 return event[ axis ] > target[ axis ] + TARGET_CENTER_PADDING
55904 && event[ axis ] < target[ axis ] + getDimensionForAxis(axis, target) - TARGET_CENTER_PADDING;
55905 }
55906
55907 function isReverse(context) {
55908 var hover = context.hover,
55909 source = context.source;
55910
55911 return hover && source && hover === source;
55912 }
55913
55914 /**
55915 * A snap context, containing the (possibly incomplete)
55916 * mappings of drop targets (to identify the snapping)
55917 * to computed snap points.
55918 */
55919 function SnapContext() {
55920
55921 /**
55922 * Map<String, SnapPoints> mapping drop targets to
55923 * a list of possible snappings.
55924 *
55925 * @type {Object}
55926 */
55927 this._targets = {};
55928
55929 /**
55930 * Map<String, Point> initial positioning of element
55931 * regarding various snap directions.
55932 *
55933 * @type {Object}
55934 */
55935 this._snapOrigins = {};
55936
55937 /**
55938 * List of snap locations
55939 *
55940 * @type {Array<string>}
55941 */
55942 this._snapLocations = [];
55943
55944 /**
55945 * Map<String, Array<Point>> of default snapping locations
55946 *
55947 * @type {Object}
55948 */
55949 this._defaultSnaps = {};
55950 }
55951
55952
55953 SnapContext.prototype.getSnapOrigin = function(snapLocation) {
55954 return this._snapOrigins[snapLocation];
55955 };
55956
55957
55958 SnapContext.prototype.setSnapOrigin = function(snapLocation, initialValue) {
55959 this._snapOrigins[snapLocation] = initialValue;
55960
55961 if (this._snapLocations.indexOf(snapLocation) === -1) {
55962 this._snapLocations.push(snapLocation);
55963 }
55964 };
55965
55966
55967 SnapContext.prototype.addDefaultSnap = function(type, point) {
55968
55969 var snapValues = this._defaultSnaps[type];
55970
55971 if (!snapValues) {
55972 snapValues = this._defaultSnaps[type] = [];
55973 }
55974
55975 snapValues.push(point);
55976 };
55977
55978 /**
55979 * Return a number of initialized snaps, i.e. snap locations such as
55980 * top-left, mid, bottom-right and so forth.
55981 *
55982 * @return {Array<string>} snapLocations
55983 */
55984 SnapContext.prototype.getSnapLocations = function() {
55985 return this._snapLocations;
55986 };
55987
55988 /**
55989 * Set the snap locations for this context.
55990 *
55991 * The order of locations determines precedence.
55992 *
55993 * @param {Array<string>} snapLocations
55994 */
55995 SnapContext.prototype.setSnapLocations = function(snapLocations) {
55996 this._snapLocations = snapLocations;
55997 };
55998
55999 /**
56000 * Get snap points for a given target
56001 *
56002 * @param {Element|string} target
56003 */
56004 SnapContext.prototype.pointsForTarget = function(target) {
56005
56006 var targetId = target.id || target;
56007
56008 var snapPoints = this._targets[targetId];
56009
56010 if (!snapPoints) {
56011 snapPoints = this._targets[targetId] = new SnapPoints();
56012 snapPoints.initDefaults(this._defaultSnaps);
56013 }
56014
56015 return snapPoints;
56016 };
56017
56018
56019 /**
56020 * Creates the snap points and initializes them with the
56021 * given default values.
56022 *
56023 * @param {Object<string, Array<Point>>} [defaultPoints]
56024 */
56025 function SnapPoints(defaultSnaps) {
56026
56027 /**
56028 * Map<String, Map<(x|y), Array<number>>> mapping snap locations,
56029 * i.e. top-left, bottom-right, center to actual snap values.
56030 *
56031 * @type {Object}
56032 */
56033 this._snapValues = {};
56034 }
56035
56036 SnapPoints.prototype.add = function(snapLocation, point) {
56037
56038 var snapValues = this._snapValues[snapLocation];
56039
56040 if (!snapValues) {
56041 snapValues = this._snapValues[snapLocation] = { x: [], y: [] };
56042 }
56043
56044 if (snapValues.x.indexOf(point.x) === -1) {
56045 snapValues.x.push(point.x);
56046 }
56047
56048 if (snapValues.y.indexOf(point.y) === -1) {
56049 snapValues.y.push(point.y);
56050 }
56051 };
56052
56053
56054 SnapPoints.prototype.snap = function(point, snapLocation, axis, tolerance) {
56055 var snappingValues = this._snapValues[snapLocation];
56056
56057 return snappingValues && snapTo(point[axis], snappingValues[axis], tolerance);
56058 };
56059
56060 /**
56061 * Initialize a number of default snapping points.
56062 *
56063 * @param {Object} defaultSnaps
56064 */
56065 SnapPoints.prototype.initDefaults = function(defaultSnaps) {
56066
56067 var self = this;
56068
56069 forEach(defaultSnaps || {}, function(snapPoints, snapLocation) {
56070 forEach(snapPoints, function(point) {
56071 self.add(snapLocation, point);
56072 });
56073 });
56074 };
56075
56076 var HIGHER_PRIORITY$1 = 1250;
56077
56078
56079 /**
56080 * Snap during create and move.
56081 *
56082 * @param {EventBus} elementRegistry
56083 * @param {EventBus} eventBus
56084 * @param {Snapping} snapping
56085 */
56086 function CreateMoveSnapping(elementRegistry, eventBus, snapping) {
56087 var self = this;
56088
56089 this._elementRegistry = elementRegistry;
56090
56091 eventBus.on([
56092 'create.start',
56093 'shape.move.start'
56094 ], function(event) {
56095 self.initSnap(event);
56096 });
56097
56098 eventBus.on([
56099 'create.move',
56100 'create.end',
56101 'shape.move.move',
56102 'shape.move.end'
56103 ], HIGHER_PRIORITY$1, function(event) {
56104 var context = event.context,
56105 shape = context.shape,
56106 snapContext = context.snapContext,
56107 target = context.target;
56108
56109 if (event.originalEvent && isCmd(event.originalEvent)) {
56110 return;
56111 }
56112
56113 if (isSnapped(event) || !target) {
56114 return;
56115 }
56116
56117 var snapPoints = snapContext.pointsForTarget(target);
56118
56119 if (!snapPoints.initialized) {
56120 snapPoints = self.addSnapTargetPoints(snapPoints, shape, target);
56121
56122 snapPoints.initialized = true;
56123 }
56124
56125 snapping.snap(event, snapPoints);
56126 });
56127
56128 eventBus.on([
56129 'create.cleanup',
56130 'shape.move.cleanup'
56131 ], function() {
56132 snapping.hide();
56133 });
56134 }
56135
56136 CreateMoveSnapping.$inject = [
56137 'elementRegistry',
56138 'eventBus',
56139 'snapping'
56140 ];
56141
56142 CreateMoveSnapping.prototype.initSnap = function(event) {
56143 var elementRegistry = this._elementRegistry;
56144
56145 var context = event.context,
56146 shape = context.shape,
56147 snapContext = context.snapContext;
56148
56149 if (!snapContext) {
56150 snapContext = context.snapContext = new SnapContext();
56151 }
56152
56153 var shapeMid;
56154
56155 if (elementRegistry.get(shape.id)) {
56156
56157 // move
56158 shapeMid = mid$2(shape, event);
56159 } else {
56160
56161 // create
56162 shapeMid = {
56163 x: event.x + mid$2(shape).x,
56164 y: event.y + mid$2(shape).y
56165 };
56166 }
56167
56168 var shapeTopLeft = {
56169 x: shapeMid.x - shape.width / 2,
56170 y: shapeMid.y - shape.height / 2
56171 },
56172 shapeBottomRight = {
56173 x: shapeMid.x + shape.width / 2,
56174 y: shapeMid.y + shape.height / 2
56175 };
56176
56177 snapContext.setSnapOrigin('mid', {
56178 x: shapeMid.x - event.x,
56179 y: shapeMid.y - event.y
56180 });
56181
56182 // snap labels to mid only
56183 if (isLabel$1(shape)) {
56184 return snapContext;
56185 }
56186
56187 snapContext.setSnapOrigin('top-left', {
56188 x: shapeTopLeft.x - event.x,
56189 y: shapeTopLeft.y - event.y
56190 });
56191
56192 snapContext.setSnapOrigin('bottom-right', {
56193 x: shapeBottomRight.x - event.x,
56194 y: shapeBottomRight.y - event.y
56195 });
56196
56197 return snapContext;
56198 };
56199
56200 CreateMoveSnapping.prototype.addSnapTargetPoints = function(snapPoints, shape, target) {
56201 var snapTargets = this.getSnapTargets(shape, target);
56202
56203 forEach(snapTargets, function(snapTarget) {
56204
56205 // handle labels
56206 if (isLabel$1(snapTarget)) {
56207
56208 if (isLabel$1(shape)) {
56209 snapPoints.add('mid', mid$2(snapTarget));
56210 }
56211
56212 return;
56213 }
56214
56215 // handle connections
56216 if (isConnection$1(snapTarget)) {
56217
56218 // ignore single segment connections
56219 if (snapTarget.waypoints.length < 3) {
56220 return;
56221 }
56222
56223 // ignore first and last waypoint
56224 var waypoints = snapTarget.waypoints.slice(1, -1);
56225
56226 forEach(waypoints, function(waypoint) {
56227 snapPoints.add('mid', waypoint);
56228 });
56229
56230 return;
56231 }
56232
56233 // handle shapes
56234 snapPoints.add('mid', mid$2(snapTarget));
56235 });
56236
56237 if (!isNumber(shape.x) || !isNumber(shape.y)) {
56238 return snapPoints;
56239 }
56240
56241 // snap to original position when moving
56242 if (this._elementRegistry.get(shape.id)) {
56243 snapPoints.add('mid', mid$2(shape));
56244 }
56245
56246 return snapPoints;
56247 };
56248
56249 CreateMoveSnapping.prototype.getSnapTargets = function(shape, target) {
56250 return getChildren(target).filter(function(child) {
56251 return !isHidden$1(child);
56252 });
56253 };
56254
56255 // helpers //////////
56256
56257 function isConnection$1(element) {
56258 return !!element.waypoints;
56259 }
56260
56261 function isHidden$1(element) {
56262 return !!element.hidden;
56263 }
56264
56265 function isLabel$1(element) {
56266 return !!element.labelTarget;
56267 }
56268
56269 var HIGH_PRIORITY = 1500;
56270
56271
56272 /**
56273 * Snap during create and move.
56274 *
56275 * @param {EventBus} eventBus
56276 * @param {Injector} injector
56277 */
56278 function BpmnCreateMoveSnapping(eventBus, injector) {
56279 injector.invoke(CreateMoveSnapping, this);
56280
56281 // creating first participant
56282 eventBus.on([ 'create.move', 'create.end' ], HIGH_PRIORITY, setSnappedIfConstrained);
56283
56284 // snap boundary events
56285 eventBus.on([
56286 'create.move',
56287 'create.end',
56288 'shape.move.move',
56289 'shape.move.end'
56290 ], HIGH_PRIORITY, function(event) {
56291 var context = event.context,
56292 canExecute = context.canExecute,
56293 target = context.target;
56294
56295 var canAttach = canExecute && (canExecute === 'attach' || canExecute.attach);
56296
56297 if (canAttach && !isSnapped(event)) {
56298 snapBoundaryEvent(event, target);
56299 }
56300 });
56301 }
56302
56303 inherits$1(BpmnCreateMoveSnapping, CreateMoveSnapping);
56304
56305 BpmnCreateMoveSnapping.$inject = [
56306 'eventBus',
56307 'injector'
56308 ];
56309
56310 BpmnCreateMoveSnapping.prototype.initSnap = function(event) {
56311 var snapContext = CreateMoveSnapping.prototype.initSnap.call(this, event);
56312
56313 var shape = event.shape;
56314
56315 var isMove = !!this._elementRegistry.get(shape.id);
56316
56317 // snap to docking points
56318 forEach(shape.outgoing, function(connection) {
56319 var docking = connection.waypoints[0];
56320
56321 docking = docking.original || docking;
56322
56323 snapContext.setSnapOrigin(connection.id + '-docking', getDockingSnapOrigin(docking, isMove, event));
56324 });
56325
56326 forEach(shape.incoming, function(connection) {
56327 var docking = connection.waypoints[connection.waypoints.length - 1];
56328
56329 docking = docking.original || docking;
56330
56331 snapContext.setSnapOrigin(connection.id + '-docking', getDockingSnapOrigin(docking, isMove, event));
56332 });
56333
56334 if (is$1(shape, 'bpmn:Participant')) {
56335
56336 // snap to borders with higher priority
56337 snapContext.setSnapLocations([ 'top-left', 'bottom-right', 'mid' ]);
56338 }
56339
56340 return snapContext;
56341 };
56342
56343 BpmnCreateMoveSnapping.prototype.addSnapTargetPoints = function(snapPoints, shape, target) {
56344 CreateMoveSnapping.prototype.addSnapTargetPoints.call(this, snapPoints, shape, target);
56345
56346 var snapTargets = this.getSnapTargets(shape, target);
56347
56348 forEach(snapTargets, function(snapTarget) {
56349
56350 // handle TRBL alignment
56351 //
56352 // * with container elements
56353 // * with text annotations
56354 if (isContainer(snapTarget) || areAll([ shape, snapTarget ], 'bpmn:TextAnnotation')) {
56355 snapPoints.add('top-left', topLeft(snapTarget));
56356 snapPoints.add('bottom-right', bottomRight(snapTarget));
56357 }
56358 });
56359
56360 var elementRegistry = this._elementRegistry;
56361
56362 // snap to docking points if not create mode
56363 forEach(shape.incoming, function(connection) {
56364 if (elementRegistry.get(shape.id)) {
56365
56366 if (!includes(snapTargets, connection.source)) {
56367 snapPoints.add('mid', getMid(connection.source));
56368 }
56369
56370 var docking = connection.waypoints[0];
56371 snapPoints.add(connection.id + '-docking', docking.original || docking);
56372 }
56373 });
56374
56375 forEach(shape.outgoing, function(connection) {
56376 if (elementRegistry.get(shape.id)) {
56377
56378 if (!includes(snapTargets, connection.target)) {
56379 snapPoints.add('mid', getMid(connection.target));
56380 }
56381
56382 var docking = connection.waypoints[ connection.waypoints.length - 1 ];
56383
56384 snapPoints.add(connection.id + '-docking', docking.original || docking);
56385 }
56386 });
56387
56388 // add sequence flow parents as snap targets
56389 if (is$1(target, 'bpmn:SequenceFlow')) {
56390 snapPoints = this.addSnapTargetPoints(snapPoints, shape, target.parent);
56391 }
56392
56393 return snapPoints;
56394 };
56395
56396 BpmnCreateMoveSnapping.prototype.getSnapTargets = function(shape, target) {
56397 return CreateMoveSnapping.prototype.getSnapTargets.call(this, shape, target)
56398 .filter(function(snapTarget) {
56399
56400 // do not snap to lanes
56401 return !is$1(snapTarget, 'bpmn:Lane');
56402 });
56403 };
56404
56405 // helpers //////////
56406
56407 function snapBoundaryEvent(event, target) {
56408 var targetTRBL = asTRBL(target);
56409
56410 var direction = getBoundaryAttachment(event, target);
56411
56412 var context = event.context,
56413 shape = context.shape;
56414
56415 var offset;
56416
56417 if (shape.parent) {
56418 offset = { x: 0, y: 0 };
56419 } else {
56420 offset = getMid(shape);
56421 }
56422
56423 if (/top/.test(direction)) {
56424 setSnapped(event, 'y', targetTRBL.top - offset.y);
56425 } else if (/bottom/.test(direction)) {
56426 setSnapped(event, 'y', targetTRBL.bottom - offset.y);
56427 }
56428
56429 if (/left/.test(direction)) {
56430 setSnapped(event, 'x', targetTRBL.left - offset.x);
56431 } else if (/right/.test(direction)) {
56432 setSnapped(event, 'x', targetTRBL.right - offset.x);
56433 }
56434 }
56435
56436 function areAll(elements, type) {
56437 return elements.every(function(el) {
56438 return is$1(el, type);
56439 });
56440 }
56441
56442 function isContainer(element) {
56443 if (is$1(element, 'bpmn:SubProcess') && isExpanded(element)) {
56444 return true;
56445 }
56446
56447 return is$1(element, 'bpmn:Participant');
56448 }
56449
56450
56451 function setSnappedIfConstrained(event) {
56452 var context = event.context,
56453 createConstraints = context.createConstraints;
56454
56455 if (!createConstraints) {
56456 return;
56457 }
56458
56459 var top = createConstraints.top,
56460 right = createConstraints.right,
56461 bottom = createConstraints.bottom,
56462 left = createConstraints.left;
56463
56464 if ((left && left >= event.x) || (right && right <= event.x)) {
56465 setSnapped(event, 'x', event.x);
56466 }
56467
56468 if ((top && top >= event.y) || (bottom && bottom <= event.y)) {
56469 setSnapped(event, 'y', event.y);
56470 }
56471 }
56472
56473 function includes(array, value) {
56474 return array.indexOf(value) !== -1;
56475 }
56476
56477 function getDockingSnapOrigin(docking, isMove, event) {
56478 return isMove ? (
56479 {
56480 x: docking.x - event.x,
56481 y: docking.y - event.y
56482 }
56483 ) : {
56484 x: docking.x,
56485 y: docking.y
56486 };
56487 }
56488
56489 var HIGHER_PRIORITY = 1250;
56490
56491
56492 /**
56493 * Snap during resize.
56494 *
56495 * @param {EventBus} eventBus
56496 * @param {Snapping} snapping
56497 */
56498 function ResizeSnapping(eventBus, snapping) {
56499 var self = this;
56500
56501 eventBus.on([ 'resize.start' ], function(event) {
56502 self.initSnap(event);
56503 });
56504
56505 eventBus.on([
56506 'resize.move',
56507 'resize.end',
56508 ], HIGHER_PRIORITY, function(event) {
56509 var context = event.context,
56510 shape = context.shape,
56511 parent = shape.parent,
56512 direction = context.direction,
56513 snapContext = context.snapContext;
56514
56515 if (event.originalEvent && isCmd(event.originalEvent)) {
56516 return;
56517 }
56518
56519 if (isSnapped(event)) {
56520 return;
56521 }
56522
56523 var snapPoints = snapContext.pointsForTarget(parent);
56524
56525 if (!snapPoints.initialized) {
56526 snapPoints = self.addSnapTargetPoints(snapPoints, shape, parent, direction);
56527
56528 snapPoints.initialized = true;
56529 }
56530
56531 if (isHorizontal(direction)) {
56532 setSnapped(event, 'x', event.x);
56533 }
56534
56535 if (isVertical(direction)) {
56536 setSnapped(event, 'y', event.y);
56537 }
56538
56539 snapping.snap(event, snapPoints);
56540 });
56541
56542 eventBus.on([ 'resize.cleanup' ], function() {
56543 snapping.hide();
56544 });
56545 }
56546
56547 ResizeSnapping.prototype.initSnap = function(event) {
56548 var context = event.context,
56549 shape = context.shape,
56550 direction = context.direction,
56551 snapContext = context.snapContext;
56552
56553 if (!snapContext) {
56554 snapContext = context.snapContext = new SnapContext();
56555 }
56556
56557 var snapOrigin = getSnapOrigin(shape, direction);
56558
56559 snapContext.setSnapOrigin('corner', {
56560 x: snapOrigin.x - event.x,
56561 y: snapOrigin.y - event.y
56562 });
56563
56564 return snapContext;
56565 };
56566
56567 ResizeSnapping.prototype.addSnapTargetPoints = function(snapPoints, shape, target, direction) {
56568 var snapTargets = this.getSnapTargets(shape, target);
56569
56570 forEach(snapTargets, function(snapTarget) {
56571 snapPoints.add('corner', bottomRight(snapTarget));
56572 snapPoints.add('corner', topLeft(snapTarget));
56573 });
56574
56575 snapPoints.add('corner', getSnapOrigin(shape, direction));
56576
56577 return snapPoints;
56578 };
56579
56580 ResizeSnapping.$inject = [
56581 'eventBus',
56582 'snapping'
56583 ];
56584
56585 ResizeSnapping.prototype.getSnapTargets = function(shape, target) {
56586 return getChildren(target).filter(function(child) {
56587 return !isAttached(child, shape)
56588 && !isConnection(child)
56589 && !isHidden(child)
56590 && !isLabel(child);
56591 });
56592 };
56593
56594 // helpers //////////
56595
56596 function getSnapOrigin(shape, direction) {
56597 var mid = getMid(shape),
56598 trbl = asTRBL(shape);
56599
56600 var snapOrigin = {
56601 x: mid.x,
56602 y: mid.y
56603 };
56604
56605 if (direction.indexOf('n') !== -1) {
56606 snapOrigin.y = trbl.top;
56607 } else if (direction.indexOf('s') !== -1) {
56608 snapOrigin.y = trbl.bottom;
56609 }
56610
56611 if (direction.indexOf('e') !== -1) {
56612 snapOrigin.x = trbl.right;
56613 } else if (direction.indexOf('w') !== -1) {
56614 snapOrigin.x = trbl.left;
56615 }
56616
56617 return snapOrigin;
56618 }
56619
56620 function isAttached(element, host) {
56621 return element.host === host;
56622 }
56623
56624 function isConnection(element) {
56625 return !!element.waypoints;
56626 }
56627
56628 function isHidden(element) {
56629 return !!element.hidden;
56630 }
56631
56632 function isLabel(element) {
56633 return !!element.labelTarget;
56634 }
56635
56636 function isHorizontal(direction) {
56637 return direction === 'n' || direction === 's';
56638 }
56639
56640 function isVertical(direction) {
56641 return direction === 'e' || direction === 'w';
56642 }
56643
56644 var SNAP_TOLERANCE = 7;
56645
56646 var SNAP_LINE_HIDE_DELAY = 1000;
56647
56648
56649 /**
56650 * Generic snapping feature.
56651 *
56652 * @param {EventBus} eventBus
56653 * @param {Canvas} canvas
56654 */
56655 function Snapping(canvas) {
56656 this._canvas = canvas;
56657
56658 // delay hide by 1000 seconds since last snap
56659 this._asyncHide = debounce(bind$2(this.hide, this), SNAP_LINE_HIDE_DELAY);
56660 }
56661
56662 Snapping.$inject = [ 'canvas' ];
56663
56664 /**
56665 * Snap an event to given snap points.
56666 *
56667 * @param {Event} event
56668 * @param {SnapPoints} snapPoints
56669 */
56670 Snapping.prototype.snap = function(event, snapPoints) {
56671 var context = event.context,
56672 snapContext = context.snapContext,
56673 snapLocations = snapContext.getSnapLocations();
56674
56675 var snapping = {
56676 x: isSnapped(event, 'x'),
56677 y: isSnapped(event, 'y')
56678 };
56679
56680 forEach(snapLocations, function(location) {
56681 var snapOrigin = snapContext.getSnapOrigin(location);
56682
56683 var snapCurrent = {
56684 x: event.x + snapOrigin.x,
56685 y: event.y + snapOrigin.y
56686 };
56687
56688 // snap both axis if not snapped already
56689 forEach([ 'x', 'y' ], function(axis) {
56690 var locationSnapping;
56691
56692 if (!snapping[axis]) {
56693 locationSnapping = snapPoints.snap(snapCurrent, location, axis, SNAP_TOLERANCE);
56694
56695 if (locationSnapping !== undefined) {
56696 snapping[axis] = {
56697 value: locationSnapping,
56698 originValue: locationSnapping - snapOrigin[axis]
56699 };
56700 }
56701 }
56702 });
56703
56704 // no need to continue snapping
56705 if (snapping.x && snapping.y) {
56706 return false;
56707 }
56708 });
56709
56710 // show snap lines
56711 this.showSnapLine('vertical', snapping.x && snapping.x.value);
56712 this.showSnapLine('horizontal', snapping.y && snapping.y.value);
56713
56714 // snap event
56715 forEach([ 'x', 'y' ], function(axis) {
56716 var axisSnapping = snapping[axis];
56717
56718 if (isObject(axisSnapping)) {
56719 setSnapped(event, axis, axisSnapping.originValue);
56720 }
56721 });
56722 };
56723
56724 Snapping.prototype._createLine = function(orientation) {
56725 var root = this._canvas.getLayer('snap');
56726
56727 var line = create$1('path');
56728
56729 attr(line, { d: 'M0,0 L0,0' });
56730
56731 classes(line).add('djs-snap-line');
56732
56733 append(root, line);
56734
56735 return {
56736 update: function(position) {
56737
56738 if (!isNumber(position)) {
56739 attr(line, { display: 'none' });
56740 } else {
56741 if (orientation === 'horizontal') {
56742 attr(line, {
56743 d: 'M-100000,' + position + ' L+100000,' + position,
56744 display: ''
56745 });
56746 } else {
56747 attr(line, {
56748 d: 'M ' + position + ',-100000 L ' + position + ', +100000',
56749 display: ''
56750 });
56751 }
56752 }
56753 }
56754 };
56755 };
56756
56757 Snapping.prototype._createSnapLines = function() {
56758 this._snapLines = {
56759 horizontal: this._createLine('horizontal'),
56760 vertical: this._createLine('vertical')
56761 };
56762 };
56763
56764 Snapping.prototype.showSnapLine = function(orientation, position) {
56765
56766 var line = this.getSnapLine(orientation);
56767
56768 if (line) {
56769 line.update(position);
56770 }
56771
56772 this._asyncHide();
56773 };
56774
56775 Snapping.prototype.getSnapLine = function(orientation) {
56776 if (!this._snapLines) {
56777 this._createSnapLines();
56778 }
56779
56780 return this._snapLines[orientation];
56781 };
56782
56783 Snapping.prototype.hide = function() {
56784 forEach(this._snapLines, function(snapLine) {
56785 snapLine.update();
56786 });
56787 };
56788
56789 var SnappingModule$1 = {
56790 __init__: [
56791 'createMoveSnapping',
56792 'resizeSnapping',
56793 'snapping'
56794 ],
56795 createMoveSnapping: [ 'type', CreateMoveSnapping ],
56796 resizeSnapping: [ 'type', ResizeSnapping ],
56797 snapping: [ 'type', Snapping ]
56798 };
56799
56800 var SnappingModule = {
56801 __depends__: [ SnappingModule$1 ],
56802 __init__: [
56803 'connectSnapping',
56804 'createMoveSnapping'
56805 ],
56806 connectSnapping: [ 'type', BpmnConnectSnapping ],
56807 createMoveSnapping: [ 'type', BpmnCreateMoveSnapping ]
56808 };
56809
56810 /**
56811 * Provides searching infrastructure
56812 */
56813 function SearchPad(canvas, eventBus, overlays, selection) {
56814 this._open = false;
56815 this._results = [];
56816 this._eventMaps = [];
56817
56818 this._canvas = canvas;
56819 this._eventBus = eventBus;
56820 this._overlays = overlays;
56821 this._selection = selection;
56822
56823 // setup elements
56824 this._container = domify(SearchPad.BOX_HTML);
56825 this._searchInput = query(SearchPad.INPUT_SELECTOR, this._container);
56826 this._resultsContainer = query(SearchPad.RESULTS_CONTAINER_SELECTOR, this._container);
56827
56828 // attach search pad
56829 this._canvas.getContainer().appendChild(this._container);
56830
56831 // cleanup on destroy
56832 eventBus.on([ 'canvas.destroy', 'diagram.destroy' ], this.close, this);
56833 }
56834
56835
56836 SearchPad.$inject = [
56837 'canvas',
56838 'eventBus',
56839 'overlays',
56840 'selection'
56841 ];
56842
56843
56844 /**
56845 * Binds and keeps track of all event listereners
56846 */
56847 SearchPad.prototype._bindEvents = function() {
56848 var self = this;
56849
56850 function listen(el, selector, type, fn) {
56851 self._eventMaps.push({
56852 el: el,
56853 type: type,
56854 listener: delegate.bind(el, selector, type, fn)
56855 });
56856 }
56857
56858 // close search on clicking anywhere outside
56859 listen(document, 'html', 'click', function(e) {
56860 self.close();
56861 });
56862
56863 // stop event from propagating and closing search
56864 // focus on input
56865 listen(this._container, SearchPad.INPUT_SELECTOR, 'click', function(e) {
56866 e.stopPropagation();
56867 e.delegateTarget.focus();
56868 });
56869
56870 // preselect result on hover
56871 listen(this._container, SearchPad.RESULT_SELECTOR, 'mouseover', function(e) {
56872 e.stopPropagation();
56873 self._scrollToNode(e.delegateTarget);
56874 self._preselect(e.delegateTarget);
56875 });
56876
56877 // selects desired result on mouse click
56878 listen(this._container, SearchPad.RESULT_SELECTOR, 'click', function(e) {
56879 e.stopPropagation();
56880 self._select(e.delegateTarget);
56881 });
56882
56883 // prevent cursor in input from going left and right when using up/down to
56884 // navigate results
56885 listen(this._container, SearchPad.INPUT_SELECTOR, 'keydown', function(e) {
56886
56887 // up
56888 if (e.keyCode === 38) {
56889 e.preventDefault();
56890 }
56891
56892 // down
56893 if (e.keyCode === 40) {
56894 e.preventDefault();
56895 }
56896 });
56897
56898 // handle keyboard input
56899 listen(this._container, SearchPad.INPUT_SELECTOR, 'keyup', function(e) {
56900
56901 // escape
56902 if (e.keyCode === 27) {
56903 return self.close();
56904 }
56905
56906 // enter
56907 if (e.keyCode === 13) {
56908 var selected = self._getCurrentResult();
56909
56910 return selected ? self._select(selected) : self.close();
56911 }
56912
56913 // up
56914 if (e.keyCode === 38) {
56915 return self._scrollToDirection(true);
56916 }
56917
56918 // down
56919 if (e.keyCode === 40) {
56920 return self._scrollToDirection();
56921 }
56922
56923 // left && right
56924 // do not search while navigating text input
56925 if (e.keyCode === 37 || e.keyCode === 39) {
56926 return;
56927 }
56928
56929 // anything else
56930 self._search(e.delegateTarget.value);
56931 });
56932 };
56933
56934
56935 /**
56936 * Unbinds all previously established listeners
56937 */
56938 SearchPad.prototype._unbindEvents = function() {
56939 this._eventMaps.forEach(function(m) {
56940 delegate.unbind(m.el, m.type, m.listener);
56941 });
56942 };
56943
56944
56945 /**
56946 * Performs a search for the given pattern.
56947 *
56948 * @param {string} pattern
56949 */
56950 SearchPad.prototype._search = function(pattern) {
56951 var self = this;
56952
56953 this._clearResults();
56954
56955 // do not search on empty query
56956 if (!pattern || pattern === '') {
56957 return;
56958 }
56959
56960 var searchResults = this._searchProvider.find(pattern);
56961
56962 if (!searchResults.length) {
56963 return;
56964 }
56965
56966 // append new results
56967 searchResults.forEach(function(result) {
56968 var id = result.element.id;
56969 var node = self._createResultNode(result, id);
56970 self._results[id] = {
56971 element: result.element,
56972 node: node
56973 };
56974 });
56975
56976 // preselect first result
56977 var node = query(SearchPad.RESULT_SELECTOR, this._resultsContainer);
56978 this._scrollToNode(node);
56979 this._preselect(node);
56980 };
56981
56982
56983 /**
56984 * Navigate to the previous/next result. Defaults to next result.
56985 * @param {boolean} previous
56986 */
56987 SearchPad.prototype._scrollToDirection = function(previous) {
56988 var selected = this._getCurrentResult();
56989 if (!selected) {
56990 return;
56991 }
56992
56993 var node = previous ? selected.previousElementSibling : selected.nextElementSibling;
56994 if (node) {
56995 this._scrollToNode(node);
56996 this._preselect(node);
56997 }
56998 };
56999
57000
57001 /**
57002 * Scroll to the node if it is not visible.
57003 *
57004 * @param {Element} node
57005 */
57006 SearchPad.prototype._scrollToNode = function(node) {
57007 if (!node || node === this._getCurrentResult()) {
57008 return;
57009 }
57010
57011 var nodeOffset = node.offsetTop;
57012 var containerScroll = this._resultsContainer.scrollTop;
57013
57014 var bottomScroll = nodeOffset - this._resultsContainer.clientHeight + node.clientHeight;
57015
57016 if (nodeOffset < containerScroll) {
57017 this._resultsContainer.scrollTop = nodeOffset;
57018 } else if (containerScroll < bottomScroll) {
57019 this._resultsContainer.scrollTop = bottomScroll;
57020 }
57021 };
57022
57023
57024 /**
57025 * Clears all results data.
57026 */
57027 SearchPad.prototype._clearResults = function() {
57028 clear$1(this._resultsContainer);
57029
57030 this._results = [];
57031
57032 this._resetOverlay();
57033
57034 this._eventBus.fire('searchPad.cleared');
57035 };
57036
57037
57038 /**
57039 * Get currently selected result.
57040 *
57041 * @return {Element}
57042 */
57043 SearchPad.prototype._getCurrentResult = function() {
57044 return query(SearchPad.RESULT_SELECTED_SELECTOR, this._resultsContainer);
57045 };
57046
57047
57048 /**
57049 * Create result DOM element within results container
57050 * that corresponds to a search result.
57051 *
57052 * 'result' : one of the elements returned by SearchProvider
57053 * 'id' : id attribute value to assign to the new DOM node
57054 * return : created DOM element
57055 *
57056 * @param {SearchResult} result
57057 * @param {string} id
57058 * @return {Element}
57059 */
57060 SearchPad.prototype._createResultNode = function(result, id) {
57061 var node = domify(SearchPad.RESULT_HTML);
57062
57063 // create only if available
57064 if (result.primaryTokens.length > 0) {
57065 createInnerTextNode(node, result.primaryTokens, SearchPad.RESULT_PRIMARY_HTML);
57066 }
57067
57068 // secondary tokens (represent element ID) are allways available
57069 createInnerTextNode(node, result.secondaryTokens, SearchPad.RESULT_SECONDARY_HTML);
57070
57071 attr$1(node, SearchPad.RESULT_ID_ATTRIBUTE, id);
57072
57073 this._resultsContainer.appendChild(node);
57074
57075 return node;
57076 };
57077
57078
57079 /**
57080 * Register search element provider.
57081 *
57082 * SearchProvider.find - provides search function over own elements
57083 * (pattern) => [{ text: <String>, element: <Element>}, ...]
57084 *
57085 * @param {SearchProvider} provider
57086 */
57087 SearchPad.prototype.registerProvider = function(provider) {
57088 this._searchProvider = provider;
57089 };
57090
57091
57092 /**
57093 * Open search pad.
57094 */
57095 SearchPad.prototype.open = function() {
57096 if (!this._searchProvider) {
57097 throw new Error('no search provider registered');
57098 }
57099
57100 if (this.isOpen()) {
57101 return;
57102 }
57103
57104 this._bindEvents();
57105
57106 this._open = true;
57107
57108 classes$1(this._container).add('open');
57109
57110 this._searchInput.focus();
57111
57112 this._eventBus.fire('searchPad.opened');
57113 };
57114
57115
57116 /**
57117 * Close search pad.
57118 */
57119 SearchPad.prototype.close = function() {
57120 if (!this.isOpen()) {
57121 return;
57122 }
57123
57124 this._unbindEvents();
57125
57126 this._open = false;
57127
57128 classes$1(this._container).remove('open');
57129
57130 this._clearResults();
57131
57132 this._searchInput.value = '';
57133 this._searchInput.blur();
57134
57135 this._resetOverlay();
57136
57137 this._eventBus.fire('searchPad.closed');
57138 };
57139
57140
57141 /**
57142 * Toggles search pad on/off.
57143 */
57144 SearchPad.prototype.toggle = function() {
57145 this.isOpen() ? this.close() : this.open();
57146 };
57147
57148
57149 /**
57150 * Report state of search pad.
57151 */
57152 SearchPad.prototype.isOpen = function() {
57153 return this._open;
57154 };
57155
57156
57157 /**
57158 * Preselect result entry.
57159 *
57160 * @param {Element} element
57161 */
57162 SearchPad.prototype._preselect = function(node) {
57163 var selectedNode = this._getCurrentResult();
57164
57165 // already selected
57166 if (node === selectedNode) {
57167 return;
57168 }
57169
57170 // removing preselection from current node
57171 if (selectedNode) {
57172 classes$1(selectedNode).remove(SearchPad.RESULT_SELECTED_CLASS);
57173 }
57174
57175 var id = attr$1(node, SearchPad.RESULT_ID_ATTRIBUTE);
57176 var element = this._results[id].element;
57177
57178 classes$1(node).add(SearchPad.RESULT_SELECTED_CLASS);
57179
57180 this._resetOverlay(element);
57181
57182 this._canvas.scrollToElement(element, { top: 400 });
57183
57184 this._selection.select(element);
57185
57186 this._eventBus.fire('searchPad.preselected', element);
57187 };
57188
57189
57190 /**
57191 * Select result node.
57192 *
57193 * @param {Element} element
57194 */
57195 SearchPad.prototype._select = function(node) {
57196 var id = attr$1(node, SearchPad.RESULT_ID_ATTRIBUTE);
57197 var element = this._results[id].element;
57198
57199 this.close();
57200
57201 this._resetOverlay();
57202
57203 this._canvas.scrollToElement(element, { top: 400 });
57204
57205 this._selection.select(element);
57206
57207 this._eventBus.fire('searchPad.selected', element);
57208 };
57209
57210
57211 /**
57212 * Reset overlay removes and, optionally, set
57213 * overlay to a new element.
57214 *
57215 * @param {Element} element
57216 */
57217 SearchPad.prototype._resetOverlay = function(element) {
57218 if (this._overlayId) {
57219 this._overlays.remove(this._overlayId);
57220 }
57221
57222 if (element) {
57223 var box = getBBox(element);
57224 var overlay = constructOverlay(box);
57225 this._overlayId = this._overlays.add(element, overlay);
57226 }
57227 };
57228
57229
57230 /**
57231 * Construct overlay object for the given bounding box.
57232 *
57233 * @param {BoundingBox} box
57234 * @return {Object}
57235 */
57236 function constructOverlay(box) {
57237
57238 var offset = 6;
57239 var w = box.width + offset * 2;
57240 var h = box.height + offset * 2;
57241
57242 var styles = [
57243 'width: '+ w +'px',
57244 'height: '+ h + 'px'
57245 ].join('; ');
57246
57247 return {
57248 position: {
57249 bottom: h - offset,
57250 right: w - offset
57251 },
57252 show: true,
57253 html: '<div style="' + styles + '" class="' + SearchPad.OVERLAY_CLASS + '"></div>'
57254 };
57255 }
57256
57257
57258 /**
57259 * Creates and appends child node from result tokens and HTML template.
57260 *
57261 * @param {Element} node
57262 * @param {Array<Object>} tokens
57263 * @param {string} template
57264 */
57265 function createInnerTextNode(parentNode, tokens, template) {
57266 var text = createHtmlText(tokens);
57267 var childNode = domify(template);
57268 childNode.innerHTML = text;
57269 parentNode.appendChild(childNode);
57270 }
57271
57272 /**
57273 * Create internal HTML markup from result tokens.
57274 * Caters for highlighting pattern matched tokens.
57275 *
57276 * @param {Array<Object>} tokens
57277 * @return {string}
57278 */
57279 function createHtmlText(tokens) {
57280 var htmlText = '';
57281
57282 tokens.forEach(function(t) {
57283 if (t.matched) {
57284 htmlText += '<strong class="' + SearchPad.RESULT_HIGHLIGHT_CLASS + '">' + escapeHTML(t.matched) + '</strong>';
57285 } else {
57286 htmlText += escapeHTML(t.normal);
57287 }
57288 });
57289
57290 return htmlText !== '' ? htmlText : null;
57291 }
57292
57293
57294 /**
57295 * CONSTANTS
57296 */
57297 SearchPad.CONTAINER_SELECTOR = '.djs-search-container';
57298 SearchPad.INPUT_SELECTOR = '.djs-search-input input';
57299 SearchPad.RESULTS_CONTAINER_SELECTOR = '.djs-search-results';
57300 SearchPad.RESULT_SELECTOR = '.djs-search-result';
57301 SearchPad.RESULT_SELECTED_CLASS = 'djs-search-result-selected';
57302 SearchPad.RESULT_SELECTED_SELECTOR = '.' + SearchPad.RESULT_SELECTED_CLASS;
57303 SearchPad.RESULT_ID_ATTRIBUTE = 'data-result-id';
57304 SearchPad.RESULT_HIGHLIGHT_CLASS = 'djs-search-highlight';
57305 SearchPad.OVERLAY_CLASS = 'djs-search-overlay';
57306
57307 SearchPad.BOX_HTML =
57308 '<div class="djs-search-container djs-draggable djs-scrollable">' +
57309 '<div class="djs-search-input">' +
57310 '<input type="text"/>' +
57311 '</div>' +
57312 '<div class="djs-search-results"></div>' +
57313 '</div>';
57314
57315 SearchPad.RESULT_HTML =
57316 '<div class="djs-search-result"></div>';
57317
57318 SearchPad.RESULT_PRIMARY_HTML =
57319 '<div class="djs-search-result-primary"></div>';
57320
57321 SearchPad.RESULT_SECONDARY_HTML =
57322 '<p class="djs-search-result-secondary"></p>';
57323
57324 var SearchPadModule = {
57325 __depends__: [
57326 OverlaysModule,
57327 SelectionModule
57328 ],
57329 searchPad: [ 'type', SearchPad ]
57330 };
57331
57332 /**
57333 * Provides ability to search through BPMN elements
57334 */
57335 function BpmnSearchProvider(elementRegistry, searchPad, canvas) {
57336
57337 this._elementRegistry = elementRegistry;
57338 this._canvas = canvas;
57339
57340 searchPad.registerProvider(this);
57341 }
57342
57343 BpmnSearchProvider.$inject = [
57344 'elementRegistry',
57345 'searchPad',
57346 'canvas'
57347 ];
57348
57349
57350 /**
57351 * Finds all elements that match given pattern
57352 *
57353 * <Result> :
57354 * {
57355 * primaryTokens: <Array<Token>>,
57356 * secondaryTokens: <Array<Token>>,
57357 * element: <Element>
57358 * }
57359 *
57360 * <Token> :
57361 * {
57362 * normal|matched: <string>
57363 * }
57364 *
57365 * @param {string} pattern
57366 * @return {Array<Result>}
57367 */
57368 BpmnSearchProvider.prototype.find = function(pattern) {
57369 var rootElement = this._canvas.getRootElement();
57370
57371 var elements = this._elementRegistry.filter(function(element) {
57372 if (element.labelTarget) {
57373 return false;
57374 }
57375 return true;
57376 });
57377
57378 // do not include root element
57379 elements = filter(elements, function(element) {
57380 return element !== rootElement;
57381 });
57382
57383 elements = map$1(elements, function(element) {
57384 return {
57385 primaryTokens: matchAndSplit(getLabel(element), pattern),
57386 secondaryTokens: matchAndSplit(element.id, pattern),
57387 element: element
57388 };
57389 });
57390
57391 // exclude non-matched elements
57392 elements = filter(elements, function(element) {
57393 return hasMatched(element.primaryTokens) || hasMatched(element.secondaryTokens);
57394 });
57395
57396 elements = sortBy(elements, function(element) {
57397 return getLabel(element.element) + element.element.id;
57398 });
57399
57400 return elements;
57401 };
57402
57403
57404 function hasMatched(tokens) {
57405 var matched = filter(tokens, function(t) {
57406 return !!t.matched;
57407 });
57408
57409 return matched.length > 0;
57410 }
57411
57412
57413 function matchAndSplit(text, pattern) {
57414 var tokens = [],
57415 originalText = text;
57416
57417 if (!text) {
57418 return tokens;
57419 }
57420
57421 text = text.toLowerCase();
57422 pattern = pattern.toLowerCase();
57423
57424 var i = text.indexOf(pattern);
57425
57426 if (i > -1) {
57427 if (i !== 0) {
57428 tokens.push({
57429 normal: originalText.substr(0, i)
57430 });
57431 }
57432
57433 tokens.push({
57434 matched: originalText.substr(i, pattern.length)
57435 });
57436
57437 if (pattern.length + i < text.length) {
57438 tokens.push({
57439 normal: originalText.substr(pattern.length + i, text.length)
57440 });
57441 }
57442 } else {
57443 tokens.push({
57444 normal: originalText
57445 });
57446 }
57447
57448 return tokens;
57449 }
57450
57451 var SearchModule = {
57452 __depends__: [
57453 SearchPadModule
57454 ],
57455 __init__: [ 'bpmnSearch'],
57456 bpmnSearch: [ 'type', BpmnSearchProvider ]
57457 };
57458
57459 var initialDiagram =
57460 '<?xml version="1.0" encoding="UTF-8"?>' +
57461 '<bpmn:definitions xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" ' +
57462 'xmlns:bpmn="http://www.omg.org/spec/BPMN/20100524/MODEL" ' +
57463 'xmlns:bpmndi="http://www.omg.org/spec/BPMN/20100524/DI" ' +
57464 'xmlns:dc="http://www.omg.org/spec/DD/20100524/DC" ' +
57465 'targetNamespace="http://bpmn.io/schema/bpmn" ' +
57466 'id="Definitions_1">' +
57467 '<bpmn:process id="Process_1" isExecutable="false">' +
57468 '<bpmn:startEvent id="StartEvent_1"/>' +
57469 '</bpmn:process>' +
57470 '<bpmndi:BPMNDiagram id="BPMNDiagram_1">' +
57471 '<bpmndi:BPMNPlane id="BPMNPlane_1" bpmnElement="Process_1">' +
57472 '<bpmndi:BPMNShape id="_BPMNShape_StartEvent_2" bpmnElement="StartEvent_1">' +
57473 '<dc:Bounds height="36.0" width="36.0" x="173.0" y="102.0"/>' +
57474 '</bpmndi:BPMNShape>' +
57475 '</bpmndi:BPMNPlane>' +
57476 '</bpmndi:BPMNDiagram>' +
57477 '</bpmn:definitions>';
57478
57479
57480 /**
57481 * A modeler for BPMN 2.0 diagrams.
57482 *
57483 *
57484 * ## Extending the Modeler
57485 *
57486 * In order to extend the viewer pass extension modules to bootstrap via the
57487 * `additionalModules` option. An extension module is an object that exposes
57488 * named services.
57489 *
57490 * The following example depicts the integration of a simple
57491 * logging component that integrates with interaction events:
57492 *
57493 *
57494 * ```javascript
57495 *
57496 * // logging component
57497 * function InteractionLogger(eventBus) {
57498 * eventBus.on('element.hover', function(event) {
57499 * console.log()
57500 * })
57501 * }
57502 *
57503 * InteractionLogger.$inject = [ 'eventBus' ]; // minification save
57504 *
57505 * // extension module
57506 * var extensionModule = {
57507 * __init__: [ 'interactionLogger' ],
57508 * interactionLogger: [ 'type', InteractionLogger ]
57509 * };
57510 *
57511 * // extend the viewer
57512 * var bpmnModeler = new Modeler({ additionalModules: [ extensionModule ] });
57513 * bpmnModeler.importXML(...);
57514 * ```
57515 *
57516 *
57517 * ## Customizing / Replacing Components
57518 *
57519 * You can replace individual diagram components by redefining them in override modules.
57520 * This works for all components, including those defined in the core.
57521 *
57522 * Pass in override modules via the `options.additionalModules` flag like this:
57523 *
57524 * ```javascript
57525 * function CustomContextPadProvider(contextPad) {
57526 *
57527 * contextPad.registerProvider(this);
57528 *
57529 * this.getContextPadEntries = function(element) {
57530 * // no entries, effectively disable the context pad
57531 * return {};
57532 * };
57533 * }
57534 *
57535 * CustomContextPadProvider.$inject = [ 'contextPad' ];
57536 *
57537 * var overrideModule = {
57538 * contextPadProvider: [ 'type', CustomContextPadProvider ]
57539 * };
57540 *
57541 * var bpmnModeler = new Modeler({ additionalModules: [ overrideModule ]});
57542 * ```
57543 *
57544 * @param {Object} [options] configuration options to pass to the viewer
57545 * @param {DOMElement} [options.container] the container to render the viewer in, defaults to body.
57546 * @param {string|number} [options.width] the width of the viewer
57547 * @param {string|number} [options.height] the height of the viewer
57548 * @param {Object} [options.moddleExtensions] extension packages to provide
57549 * @param {Array<didi.Module>} [options.modules] a list of modules to override the default modules
57550 * @param {Array<didi.Module>} [options.additionalModules] a list of modules to use with the default modules
57551 */
57552 function Modeler(options) {
57553 BaseModeler.call(this, options);
57554 }
57555
57556 inherits$1(Modeler, BaseModeler);
57557
57558
57559 Modeler.Viewer = Viewer;
57560 Modeler.NavigatedViewer = NavigatedViewer;
57561
57562 /**
57563 * The createDiagram result.
57564 *
57565 * @typedef {Object} CreateDiagramResult
57566 *
57567 * @property {Array<string>} warnings
57568 */
57569
57570 /**
57571 * The createDiagram error.
57572 *
57573 * @typedef {Error} CreateDiagramError
57574 *
57575 * @property {Array<string>} warnings
57576 */
57577
57578 /**
57579 * Create a new diagram to start modeling.
57580 *
57581 * Returns {Promise<CreateDiagramResult, CreateDiagramError>}
57582 */
57583 Modeler.prototype.createDiagram = wrapForCompatibility(function createDiagram() {
57584 return this.importXML(initialDiagram);
57585 });
57586
57587
57588 Modeler.prototype._interactionModules = [
57589
57590 // non-modeling components
57591 KeyboardMoveModule,
57592 MoveCanvasModule,
57593 TouchModule,
57594 ZoomScrollModule
57595 ];
57596
57597 Modeler.prototype._modelingModules = [
57598
57599 // modeling components
57600 AlignElementsModule,
57601 AutoPlaceModule,
57602 AutoScrollModule,
57603 AutoResizeModule,
57604 BendpointsModule,
57605 ConnectModule,
57606 ConnectionPreviewModule,
57607 ContextPadModule,
57608 CopyPasteModule,
57609 CreateModule,
57610 DistributeElementsModule,
57611 EditorActionsModule,
57612 GridSnappingModule,
57613 InteractionEventsModule,
57614 KeyboardModule,
57615 KeyboardMoveSelectionModule,
57616 LabelEditingModule,
57617 ModelingModule,
57618 MoveModule,
57619 PaletteModule,
57620 ReplacePreviewModule,
57621 ResizeModule,
57622 SnappingModule,
57623 SearchModule
57624 ];
57625
57626
57627 // modules the modeler is composed of
57628 //
57629 // - viewer modules
57630 // - interaction modules
57631 // - modeling modules
57632
57633 Modeler.prototype._modules = [].concat(
57634 Viewer.prototype._modules,
57635 Modeler.prototype._interactionModules,
57636 Modeler.prototype._modelingModules
57637 );
57638
57639 return Modeler;
57640
57641})));