UNPKG

1.45 MBJavaScriptView Raw
1/*!
2 * bpmn-js - bpmn-modeler v9.1.0
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: 2022-05-18
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$2 = Object.prototype.toString;
228 var nativeHasOwnProperty$2 = Object.prototype.hasOwnProperty;
229 function isUndefined$3(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$4(obj) {
239 return nativeToString$2.call(obj) === '[object Array]';
240 }
241 function isObject(obj) {
242 return nativeToString$2.call(obj) === '[object Object]';
243 }
244 function isNumber(obj) {
245 return nativeToString$2.call(obj) === '[object Number]';
246 }
247 function isFunction(obj) {
248 var tag = nativeToString$2.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$2.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$4(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$2(target, key) {
277 return nativeHasOwnProperty$2.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$2(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$4(collection) ? -1 : undefined;
312 forEach$2(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$2(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$2(collection, iterator) {
349 var val, result;
350
351 if (isUndefined$3(collection)) {
352 return;
353 }
354
355 var convertKey = isArray$4(collection) ? toNum$2 : identity$2;
356
357 for (var key in collection) {
358 if (has$2(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$3(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$2(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(collection, fn) {
443 var result = [];
444 forEach$2(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(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$2(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$2(collections, function (c) {
517 return groupBy(c, extractor, grouped);
518 });
519 var result = map(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$2(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(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 * let 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$2(arg) {
595 return arg;
596 }
597
598 function toNum$2(arg) {
599 return Number(arg);
600 }
601
602 /**
603 * Debounce fn, calling it only once if the given time
604 * elapsed between calls.
605 *
606 * Lodash-style the function exposes methods to `#clear`
607 * and `#flush` to control internal behavior.
608 *
609 * @param {Function} fn
610 * @param {Number} timeout
611 *
612 * @return {Function} debounced function
613 */
614 function debounce(fn, timeout) {
615 var timer;
616 var lastArgs;
617 var lastThis;
618 var lastNow;
619
620 function fire(force) {
621 var now = Date.now();
622 var scheduledDiff = force ? 0 : lastNow + timeout - now;
623
624 if (scheduledDiff > 0) {
625 return schedule(scheduledDiff);
626 }
627
628 fn.apply(lastThis, lastArgs);
629 clear();
630 }
631
632 function schedule(timeout) {
633 timer = setTimeout(fire, timeout);
634 }
635
636 function clear() {
637 if (timer) {
638 clearTimeout(timer);
639 }
640
641 timer = lastNow = lastArgs = lastThis = undefined;
642 }
643
644 function flush() {
645 if (timer) {
646 fire(true);
647 }
648
649 clear();
650 }
651
652 function callback() {
653 lastNow = Date.now();
654
655 for (var _len = arguments.length, args = new Array(_len), _key = 0; _key < _len; _key++) {
656 args[_key] = arguments[_key];
657 }
658
659 lastArgs = args;
660 lastThis = this; // ensure an execution is scheduled
661
662 if (!timer) {
663 schedule(timeout);
664 }
665 }
666
667 callback.flush = flush;
668 callback.cancel = clear;
669 return callback;
670 }
671 /**
672 * Bind function against target <this>.
673 *
674 * @param {Function} fn
675 * @param {Object} target
676 *
677 * @return {Function} bound function
678 */
679
680 function bind(fn, target) {
681 return fn.bind(target);
682 }
683
684 function _extends() {
685 _extends = Object.assign || function (target) {
686 for (var i = 1; i < arguments.length; i++) {
687 var source = arguments[i];
688
689 for (var key in source) {
690 if (Object.prototype.hasOwnProperty.call(source, key)) {
691 target[key] = source[key];
692 }
693 }
694 }
695
696 return target;
697 };
698
699 return _extends.apply(this, arguments);
700 }
701
702 /**
703 * Convenience wrapper for `Object.assign`.
704 *
705 * @param {Object} target
706 * @param {...Object} others
707 *
708 * @return {Object} the target
709 */
710
711 function assign(target) {
712 for (var _len = arguments.length, others = new Array(_len > 1 ? _len - 1 : 0), _key = 1; _key < _len; _key++) {
713 others[_key - 1] = arguments[_key];
714 }
715
716 return _extends.apply(void 0, [target].concat(others));
717 }
718 /**
719 * Pick given properties from the target object.
720 *
721 * @param {Object} target
722 * @param {Array} properties
723 *
724 * @return {Object} target
725 */
726
727 function pick(target, properties) {
728 var result = {};
729 var obj = Object(target);
730 forEach$2(properties, function (prop) {
731 if (prop in obj) {
732 result[prop] = target[prop];
733 }
734 });
735 return result;
736 }
737 /**
738 * Pick all target properties, excluding the given ones.
739 *
740 * @param {Object} target
741 * @param {Array} properties
742 *
743 * @return {Object} target
744 */
745
746 function omit(target, properties) {
747 var result = {};
748 var obj = Object(target);
749 forEach$2(obj, function (prop, key) {
750 if (properties.indexOf(key) === -1) {
751 result[key] = prop;
752 }
753 });
754 return result;
755 }
756
757 /**
758 * Flatten array, one level deep.
759 *
760 * @param {Array<?>} arr
761 *
762 * @return {Array<?>}
763 */
764
765 var nativeToString$1 = Object.prototype.toString;
766 var nativeHasOwnProperty$1 = Object.prototype.hasOwnProperty;
767 function isUndefined$2(obj) {
768 return obj === undefined;
769 }
770 function isArray$3(obj) {
771 return nativeToString$1.call(obj) === '[object Array]';
772 }
773 /**
774 * Return true, if target owns a property with the given key.
775 *
776 * @param {Object} target
777 * @param {String} key
778 *
779 * @return {Boolean}
780 */
781
782 function has$1(target, key) {
783 return nativeHasOwnProperty$1.call(target, key);
784 }
785 /**
786 * Iterate over collection; returning something
787 * (non-undefined) will stop iteration.
788 *
789 * @param {Array|Object} collection
790 * @param {Function} iterator
791 *
792 * @return {Object} return result that stopped the iteration
793 */
794
795 function forEach$1(collection, iterator) {
796 var val, result;
797
798 if (isUndefined$2(collection)) {
799 return;
800 }
801
802 var convertKey = isArray$3(collection) ? toNum$1 : identity$1;
803
804 for (var key in collection) {
805 if (has$1(collection, key)) {
806 val = collection[key];
807 result = iterator(val, convertKey(key));
808
809 if (result === false) {
810 return val;
811 }
812 }
813 }
814 }
815
816 function identity$1(arg) {
817 return arg;
818 }
819
820 function toNum$1(arg) {
821 return Number(arg);
822 }
823
824 /**
825 * Assigns style attributes in a style-src compliant way.
826 *
827 * @param {Element} element
828 * @param {...Object} styleSources
829 *
830 * @return {Element} the element
831 */
832 function assign$1$1(element) {
833 var target = element.style;
834
835 for (var _len = arguments.length, styleSources = Array(_len > 1 ? _len - 1 : 0), _key = 1; _key < _len; _key++) {
836 styleSources[_key - 1] = arguments[_key];
837 }
838
839 forEach$1(styleSources, function (style) {
840 if (!style) {
841 return;
842 }
843
844 forEach$1(style, function (value, key) {
845 target[key] = value;
846 });
847 });
848
849 return element;
850 }
851
852 var indexOf$2 = [].indexOf;
853
854 var indexof$1 = function(arr, obj){
855 if (indexOf$2) return arr.indexOf(obj);
856 for (var i = 0; i < arr.length; ++i) {
857 if (arr[i] === obj) return i;
858 }
859 return -1;
860 };
861
862 /**
863 * Taken from https://github.com/component/classes
864 *
865 * Without the component bits.
866 */
867
868 /**
869 * Whitespace regexp.
870 */
871
872 var re$2 = /\s+/;
873
874 /**
875 * toString reference.
876 */
877
878 var toString$2 = Object.prototype.toString;
879
880 /**
881 * Wrap `el` in a `ClassList`.
882 *
883 * @param {Element} el
884 * @return {ClassList}
885 * @api public
886 */
887
888 function classes$2(el) {
889 return new ClassList$2(el);
890 }
891
892 /**
893 * Initialize a new ClassList for `el`.
894 *
895 * @param {Element} el
896 * @api private
897 */
898
899 function ClassList$2(el) {
900 if (!el || !el.nodeType) {
901 throw new Error('A DOM element reference is required');
902 }
903 this.el = el;
904 this.list = el.classList;
905 }
906
907 /**
908 * Add class `name` if not already present.
909 *
910 * @param {String} name
911 * @return {ClassList}
912 * @api public
913 */
914
915 ClassList$2.prototype.add = function (name) {
916 // classList
917 if (this.list) {
918 this.list.add(name);
919 return this;
920 }
921
922 // fallback
923 var arr = this.array();
924 var i = indexof$1(arr, name);
925 if (!~i) arr.push(name);
926 this.el.className = arr.join(' ');
927 return this;
928 };
929
930 /**
931 * Remove class `name` when present, or
932 * pass a regular expression to remove
933 * any which match.
934 *
935 * @param {String|RegExp} name
936 * @return {ClassList}
937 * @api public
938 */
939
940 ClassList$2.prototype.remove = function (name) {
941 if ('[object RegExp]' == toString$2.call(name)) {
942 return this.removeMatching(name);
943 }
944
945 // classList
946 if (this.list) {
947 this.list.remove(name);
948 return this;
949 }
950
951 // fallback
952 var arr = this.array();
953 var i = indexof$1(arr, name);
954 if (~i) arr.splice(i, 1);
955 this.el.className = arr.join(' ');
956 return this;
957 };
958
959 /**
960 * Remove all classes matching `re`.
961 *
962 * @param {RegExp} re
963 * @return {ClassList}
964 * @api private
965 */
966
967 ClassList$2.prototype.removeMatching = function (re) {
968 var arr = this.array();
969 for (var i = 0; i < arr.length; i++) {
970 if (re.test(arr[i])) {
971 this.remove(arr[i]);
972 }
973 }
974 return this;
975 };
976
977 /**
978 * Toggle class `name`, can force state via `force`.
979 *
980 * For browsers that support classList, but do not support `force` yet,
981 * the mistake will be detected and corrected.
982 *
983 * @param {String} name
984 * @param {Boolean} force
985 * @return {ClassList}
986 * @api public
987 */
988
989 ClassList$2.prototype.toggle = function (name, force) {
990 // classList
991 if (this.list) {
992 if ('undefined' !== typeof force) {
993 if (force !== this.list.toggle(name, force)) {
994 this.list.toggle(name); // toggle again to correct
995 }
996 } else {
997 this.list.toggle(name);
998 }
999 return this;
1000 }
1001
1002 // fallback
1003 if ('undefined' !== typeof force) {
1004 if (!force) {
1005 this.remove(name);
1006 } else {
1007 this.add(name);
1008 }
1009 } else {
1010 if (this.has(name)) {
1011 this.remove(name);
1012 } else {
1013 this.add(name);
1014 }
1015 }
1016
1017 return this;
1018 };
1019
1020 /**
1021 * Return an array of classes.
1022 *
1023 * @return {Array}
1024 * @api public
1025 */
1026
1027 ClassList$2.prototype.array = function () {
1028 var className = this.el.getAttribute('class') || '';
1029 var str = className.replace(/^\s+|\s+$/g, '');
1030 var arr = str.split(re$2);
1031 if ('' === arr[0]) arr.shift();
1032 return arr;
1033 };
1034
1035 /**
1036 * Check if class `name` is present.
1037 *
1038 * @param {String} name
1039 * @return {ClassList}
1040 * @api public
1041 */
1042
1043 ClassList$2.prototype.has = ClassList$2.prototype.contains = function (name) {
1044 return this.list ? this.list.contains(name) : !!~indexof$1(this.array(), name);
1045 };
1046
1047 var proto$1 = typeof Element !== 'undefined' ? Element.prototype : {};
1048 var vendor$1 = proto$1.matches
1049 || proto$1.matchesSelector
1050 || proto$1.webkitMatchesSelector
1051 || proto$1.mozMatchesSelector
1052 || proto$1.msMatchesSelector
1053 || proto$1.oMatchesSelector;
1054
1055 var matchesSelector$1 = match$1;
1056
1057 /**
1058 * Match `el` to `selector`.
1059 *
1060 * @param {Element} el
1061 * @param {String} selector
1062 * @return {Boolean}
1063 * @api public
1064 */
1065
1066 function match$1(el, selector) {
1067 if (!el || el.nodeType !== 1) return false;
1068 if (vendor$1) return vendor$1.call(el, selector);
1069 var nodes = el.parentNode.querySelectorAll(selector);
1070 for (var i = 0; i < nodes.length; i++) {
1071 if (nodes[i] == el) return true;
1072 }
1073 return false;
1074 }
1075
1076 /**
1077 * Closest
1078 *
1079 * @param {Element} el
1080 * @param {String} selector
1081 * @param {Boolean} checkYourSelf (optional)
1082 */
1083 function closest$1 (element, selector, checkYourSelf) {
1084 var currentElem = checkYourSelf ? element : element.parentNode;
1085
1086 while (currentElem && currentElem.nodeType !== document.DOCUMENT_NODE && currentElem.nodeType !== document.DOCUMENT_FRAGMENT_NODE) {
1087
1088 if (matchesSelector$1(currentElem, selector)) {
1089 return currentElem;
1090 }
1091
1092 currentElem = currentElem.parentNode;
1093 }
1094
1095 return matchesSelector$1(currentElem, selector) ? currentElem : null;
1096 }
1097
1098 var bind$1$1 = window.addEventListener ? 'addEventListener' : 'attachEvent',
1099 unbind$2 = window.removeEventListener ? 'removeEventListener' : 'detachEvent',
1100 prefix$7 = bind$1$1 !== 'addEventListener' ? 'on' : '';
1101
1102 /**
1103 * Bind `el` event `type` to `fn`.
1104 *
1105 * @param {Element} el
1106 * @param {String} type
1107 * @param {Function} fn
1108 * @param {Boolean} capture
1109 * @return {Function}
1110 * @api public
1111 */
1112
1113 var bind_1$1 = function(el, type, fn, capture){
1114 el[bind$1$1](prefix$7 + type, fn, capture || false);
1115 return fn;
1116 };
1117
1118 /**
1119 * Unbind `el` event `type`'s callback `fn`.
1120 *
1121 * @param {Element} el
1122 * @param {String} type
1123 * @param {Function} fn
1124 * @param {Boolean} capture
1125 * @return {Function}
1126 * @api public
1127 */
1128
1129 var unbind_1$1 = function(el, type, fn, capture){
1130 el[unbind$2](prefix$7 + type, fn, capture || false);
1131 return fn;
1132 };
1133
1134 var componentEvent$1 = {
1135 bind: bind_1$1,
1136 unbind: unbind_1$1
1137 };
1138
1139 /**
1140 * Module dependencies.
1141 */
1142
1143 /**
1144 * Delegate event `type` to `selector`
1145 * and invoke `fn(e)`. A callback function
1146 * is returned which may be passed to `.unbind()`.
1147 *
1148 * @param {Element} el
1149 * @param {String} selector
1150 * @param {String} type
1151 * @param {Function} fn
1152 * @param {Boolean} capture
1153 * @return {Function}
1154 * @api public
1155 */
1156
1157 // Some events don't bubble, so we want to bind to the capture phase instead
1158 // when delegating.
1159 var forceCaptureEvents$1 = ['focus', 'blur'];
1160
1161 function bind$2$1(el, selector, type, fn, capture) {
1162 if (forceCaptureEvents$1.indexOf(type) !== -1) {
1163 capture = true;
1164 }
1165
1166 return componentEvent$1.bind(el, type, function (e) {
1167 var target = e.target || e.srcElement;
1168 e.delegateTarget = closest$1(target, selector, true);
1169 if (e.delegateTarget) {
1170 fn.call(el, e);
1171 }
1172 }, capture);
1173 }
1174
1175 /**
1176 * Unbind event `type`'s callback `fn`.
1177 *
1178 * @param {Element} el
1179 * @param {String} type
1180 * @param {Function} fn
1181 * @param {Boolean} capture
1182 * @api public
1183 */
1184 function unbind$1$1(el, type, fn, capture) {
1185 if (forceCaptureEvents$1.indexOf(type) !== -1) {
1186 capture = true;
1187 }
1188
1189 return componentEvent$1.unbind(el, type, fn, capture);
1190 }
1191
1192 var delegate$1 = {
1193 bind: bind$2$1,
1194 unbind: unbind$1$1
1195 };
1196
1197 /**
1198 * Expose `parse`.
1199 */
1200
1201 var domify$1 = parse$2;
1202
1203 /**
1204 * Tests for browser support.
1205 */
1206
1207 var innerHTMLBug$1 = false;
1208 var bugTestDiv$1;
1209 if (typeof document !== 'undefined') {
1210 bugTestDiv$1 = document.createElement('div');
1211 // Setup
1212 bugTestDiv$1.innerHTML = ' <link/><table></table><a href="/a">a</a><input type="checkbox"/>';
1213 // Make sure that link elements get serialized correctly by innerHTML
1214 // This requires a wrapper element in IE
1215 innerHTMLBug$1 = !bugTestDiv$1.getElementsByTagName('link').length;
1216 bugTestDiv$1 = undefined;
1217 }
1218
1219 /**
1220 * Wrap map from jquery.
1221 */
1222
1223 var map$1$1 = {
1224 legend: [1, '<fieldset>', '</fieldset>'],
1225 tr: [2, '<table><tbody>', '</tbody></table>'],
1226 col: [2, '<table><tbody></tbody><colgroup>', '</colgroup></table>'],
1227 // for script/link/style tags to work in IE6-8, you have to wrap
1228 // in a div with a non-whitespace character in front, ha!
1229 _default: innerHTMLBug$1 ? [1, 'X<div>', '</div>'] : [0, '', '']
1230 };
1231
1232 map$1$1.td =
1233 map$1$1.th = [3, '<table><tbody><tr>', '</tr></tbody></table>'];
1234
1235 map$1$1.option =
1236 map$1$1.optgroup = [1, '<select multiple="multiple">', '</select>'];
1237
1238 map$1$1.thead =
1239 map$1$1.tbody =
1240 map$1$1.colgroup =
1241 map$1$1.caption =
1242 map$1$1.tfoot = [1, '<table>', '</table>'];
1243
1244 map$1$1.polyline =
1245 map$1$1.ellipse =
1246 map$1$1.polygon =
1247 map$1$1.circle =
1248 map$1$1.text =
1249 map$1$1.line =
1250 map$1$1.path =
1251 map$1$1.rect =
1252 map$1$1.g = [1, '<svg xmlns="http://www.w3.org/2000/svg" version="1.1">','</svg>'];
1253
1254 /**
1255 * Parse `html` and return a DOM Node instance, which could be a TextNode,
1256 * HTML DOM Node of some kind (<div> for example), or a DocumentFragment
1257 * instance, depending on the contents of the `html` string.
1258 *
1259 * @param {String} html - HTML string to "domify"
1260 * @param {Document} doc - The `document` instance to create the Node for
1261 * @return {DOMNode} the TextNode, DOM Node, or DocumentFragment instance
1262 * @api private
1263 */
1264
1265 function parse$2(html, doc) {
1266 if ('string' != typeof html) throw new TypeError('String expected');
1267
1268 // default to the global `document` object
1269 if (!doc) doc = document;
1270
1271 // tag name
1272 var m = /<([\w:]+)/.exec(html);
1273 if (!m) return doc.createTextNode(html);
1274
1275 html = html.replace(/^\s+|\s+$/g, ''); // Remove leading/trailing whitespace
1276
1277 var tag = m[1];
1278
1279 // body support
1280 if (tag == 'body') {
1281 var el = doc.createElement('html');
1282 el.innerHTML = html;
1283 return el.removeChild(el.lastChild);
1284 }
1285
1286 // wrap map
1287 var wrap = map$1$1[tag] || map$1$1._default;
1288 var depth = wrap[0];
1289 var prefix = wrap[1];
1290 var suffix = wrap[2];
1291 var el = doc.createElement('div');
1292 el.innerHTML = prefix + html + suffix;
1293 while (depth--) el = el.lastChild;
1294
1295 // one element
1296 if (el.firstChild == el.lastChild) {
1297 return el.removeChild(el.firstChild);
1298 }
1299
1300 // several elements
1301 var fragment = doc.createDocumentFragment();
1302 while (el.firstChild) {
1303 fragment.appendChild(el.removeChild(el.firstChild));
1304 }
1305
1306 return fragment;
1307 }
1308
1309 function query$1(selector, el) {
1310 el = el || document;
1311
1312 return el.querySelector(selector);
1313 }
1314
1315 function remove$3(el) {
1316 el.parentNode && el.parentNode.removeChild(el);
1317 }
1318
1319 function ensureImported(element, target) {
1320
1321 if (element.ownerDocument !== target.ownerDocument) {
1322 try {
1323 // may fail on webkit
1324 return target.ownerDocument.importNode(element, true);
1325 } catch (e) {
1326 // ignore
1327 }
1328 }
1329
1330 return element;
1331 }
1332
1333 /**
1334 * appendTo utility
1335 */
1336
1337 /**
1338 * Append a node to a target element and return the appended node.
1339 *
1340 * @param {SVGElement} element
1341 * @param {SVGElement} target
1342 *
1343 * @return {SVGElement} the appended node
1344 */
1345 function appendTo(element, target) {
1346 return target.appendChild(ensureImported(element, target));
1347 }
1348
1349 /**
1350 * append utility
1351 */
1352
1353 /**
1354 * Append a node to an element
1355 *
1356 * @param {SVGElement} element
1357 * @param {SVGElement} node
1358 *
1359 * @return {SVGElement} the element
1360 */
1361 function append(target, node) {
1362 appendTo(node, target);
1363 return target;
1364 }
1365
1366 /**
1367 * attribute accessor utility
1368 */
1369
1370 var LENGTH_ATTR = 2;
1371
1372 var CSS_PROPERTIES = {
1373 'alignment-baseline': 1,
1374 'baseline-shift': 1,
1375 'clip': 1,
1376 'clip-path': 1,
1377 'clip-rule': 1,
1378 'color': 1,
1379 'color-interpolation': 1,
1380 'color-interpolation-filters': 1,
1381 'color-profile': 1,
1382 'color-rendering': 1,
1383 'cursor': 1,
1384 'direction': 1,
1385 'display': 1,
1386 'dominant-baseline': 1,
1387 'enable-background': 1,
1388 'fill': 1,
1389 'fill-opacity': 1,
1390 'fill-rule': 1,
1391 'filter': 1,
1392 'flood-color': 1,
1393 'flood-opacity': 1,
1394 'font': 1,
1395 'font-family': 1,
1396 'font-size': LENGTH_ATTR,
1397 'font-size-adjust': 1,
1398 'font-stretch': 1,
1399 'font-style': 1,
1400 'font-variant': 1,
1401 'font-weight': 1,
1402 'glyph-orientation-horizontal': 1,
1403 'glyph-orientation-vertical': 1,
1404 'image-rendering': 1,
1405 'kerning': 1,
1406 'letter-spacing': 1,
1407 'lighting-color': 1,
1408 'marker': 1,
1409 'marker-end': 1,
1410 'marker-mid': 1,
1411 'marker-start': 1,
1412 'mask': 1,
1413 'opacity': 1,
1414 'overflow': 1,
1415 'pointer-events': 1,
1416 'shape-rendering': 1,
1417 'stop-color': 1,
1418 'stop-opacity': 1,
1419 'stroke': 1,
1420 'stroke-dasharray': 1,
1421 'stroke-dashoffset': 1,
1422 'stroke-linecap': 1,
1423 'stroke-linejoin': 1,
1424 'stroke-miterlimit': 1,
1425 'stroke-opacity': 1,
1426 'stroke-width': LENGTH_ATTR,
1427 'text-anchor': 1,
1428 'text-decoration': 1,
1429 'text-rendering': 1,
1430 'unicode-bidi': 1,
1431 'visibility': 1,
1432 'word-spacing': 1,
1433 'writing-mode': 1
1434 };
1435
1436
1437 function getAttribute(node, name) {
1438 if (CSS_PROPERTIES[name]) {
1439 return node.style[name];
1440 } else {
1441 return node.getAttributeNS(null, name);
1442 }
1443 }
1444
1445 function setAttribute(node, name, value) {
1446 var hyphenated = name.replace(/([a-z])([A-Z])/g, '$1-$2').toLowerCase();
1447
1448 var type = CSS_PROPERTIES[hyphenated];
1449
1450 if (type) {
1451 // append pixel unit, unless present
1452 if (type === LENGTH_ATTR && typeof value === 'number') {
1453 value = String(value) + 'px';
1454 }
1455
1456 node.style[hyphenated] = value;
1457 } else {
1458 node.setAttributeNS(null, name, value);
1459 }
1460 }
1461
1462 function setAttributes(node, attrs) {
1463
1464 var names = Object.keys(attrs), i, name;
1465
1466 for (i = 0, name; (name = names[i]); i++) {
1467 setAttribute(node, name, attrs[name]);
1468 }
1469 }
1470
1471 /**
1472 * Gets or sets raw attributes on a node.
1473 *
1474 * @param {SVGElement} node
1475 * @param {Object} [attrs]
1476 * @param {String} [name]
1477 * @param {String} [value]
1478 *
1479 * @return {String}
1480 */
1481 function attr$1(node, name, value) {
1482 if (typeof name === 'string') {
1483 if (value !== undefined) {
1484 setAttribute(node, name, value);
1485 } else {
1486 return getAttribute(node, name);
1487 }
1488 } else {
1489 setAttributes(node, name);
1490 }
1491
1492 return node;
1493 }
1494
1495 /**
1496 * Clear utility
1497 */
1498 function index(arr, obj) {
1499 if (arr.indexOf) {
1500 return arr.indexOf(obj);
1501 }
1502
1503
1504 for (var i = 0; i < arr.length; ++i) {
1505 if (arr[i] === obj) {
1506 return i;
1507 }
1508 }
1509
1510 return -1;
1511 }
1512
1513 var re$1 = /\s+/;
1514
1515 var toString$1 = Object.prototype.toString;
1516
1517 function defined(o) {
1518 return typeof o !== 'undefined';
1519 }
1520
1521 /**
1522 * Wrap `el` in a `ClassList`.
1523 *
1524 * @param {Element} el
1525 * @return {ClassList}
1526 * @api public
1527 */
1528
1529 function classes$1(el) {
1530 return new ClassList$1(el);
1531 }
1532
1533 function ClassList$1(el) {
1534 if (!el || !el.nodeType) {
1535 throw new Error('A DOM element reference is required');
1536 }
1537 this.el = el;
1538 this.list = el.classList;
1539 }
1540
1541 /**
1542 * Add class `name` if not already present.
1543 *
1544 * @param {String} name
1545 * @return {ClassList}
1546 * @api public
1547 */
1548
1549 ClassList$1.prototype.add = function(name) {
1550
1551 // classList
1552 if (this.list) {
1553 this.list.add(name);
1554 return this;
1555 }
1556
1557 // fallback
1558 var arr = this.array();
1559 var i = index(arr, name);
1560 if (!~i) {
1561 arr.push(name);
1562 }
1563
1564 if (defined(this.el.className.baseVal)) {
1565 this.el.className.baseVal = arr.join(' ');
1566 } else {
1567 this.el.className = arr.join(' ');
1568 }
1569
1570 return this;
1571 };
1572
1573 /**
1574 * Remove class `name` when present, or
1575 * pass a regular expression to remove
1576 * any which match.
1577 *
1578 * @param {String|RegExp} name
1579 * @return {ClassList}
1580 * @api public
1581 */
1582
1583 ClassList$1.prototype.remove = function(name) {
1584 if ('[object RegExp]' === toString$1.call(name)) {
1585 return this.removeMatching(name);
1586 }
1587
1588 // classList
1589 if (this.list) {
1590 this.list.remove(name);
1591 return this;
1592 }
1593
1594 // fallback
1595 var arr = this.array();
1596 var i = index(arr, name);
1597 if (~i) {
1598 arr.splice(i, 1);
1599 }
1600 this.el.className.baseVal = arr.join(' ');
1601 return this;
1602 };
1603
1604 /**
1605 * Remove all classes matching `re`.
1606 *
1607 * @param {RegExp} re
1608 * @return {ClassList}
1609 * @api private
1610 */
1611
1612 ClassList$1.prototype.removeMatching = function(re) {
1613 var arr = this.array();
1614 for (var i = 0; i < arr.length; i++) {
1615 if (re.test(arr[i])) {
1616 this.remove(arr[i]);
1617 }
1618 }
1619 return this;
1620 };
1621
1622 /**
1623 * Toggle class `name`, can force state via `force`.
1624 *
1625 * For browsers that support classList, but do not support `force` yet,
1626 * the mistake will be detected and corrected.
1627 *
1628 * @param {String} name
1629 * @param {Boolean} force
1630 * @return {ClassList}
1631 * @api public
1632 */
1633
1634 ClassList$1.prototype.toggle = function(name, force) {
1635 // classList
1636 if (this.list) {
1637 if (defined(force)) {
1638 if (force !== this.list.toggle(name, force)) {
1639 this.list.toggle(name); // toggle again to correct
1640 }
1641 } else {
1642 this.list.toggle(name);
1643 }
1644 return this;
1645 }
1646
1647 // fallback
1648 if (defined(force)) {
1649 if (!force) {
1650 this.remove(name);
1651 } else {
1652 this.add(name);
1653 }
1654 } else {
1655 if (this.has(name)) {
1656 this.remove(name);
1657 } else {
1658 this.add(name);
1659 }
1660 }
1661
1662 return this;
1663 };
1664
1665 /**
1666 * Return an array of classes.
1667 *
1668 * @return {Array}
1669 * @api public
1670 */
1671
1672 ClassList$1.prototype.array = function() {
1673 var className = this.el.getAttribute('class') || '';
1674 var str = className.replace(/^\s+|\s+$/g, '');
1675 var arr = str.split(re$1);
1676 if ('' === arr[0]) {
1677 arr.shift();
1678 }
1679 return arr;
1680 };
1681
1682 /**
1683 * Check if class `name` is present.
1684 *
1685 * @param {String} name
1686 * @return {ClassList}
1687 * @api public
1688 */
1689
1690 ClassList$1.prototype.has =
1691 ClassList$1.prototype.contains = function(name) {
1692 return (
1693 this.list ?
1694 this.list.contains(name) :
1695 !! ~index(this.array(), name)
1696 );
1697 };
1698
1699 function remove$2(element) {
1700 var parent = element.parentNode;
1701
1702 if (parent) {
1703 parent.removeChild(element);
1704 }
1705
1706 return element;
1707 }
1708
1709 /**
1710 * Clear utility
1711 */
1712
1713 /**
1714 * Removes all children from the given element
1715 *
1716 * @param {DOMElement} element
1717 * @return {DOMElement} the element (for chaining)
1718 */
1719 function clear$1(element) {
1720 var child;
1721
1722 while ((child = element.firstChild)) {
1723 remove$2(child);
1724 }
1725
1726 return element;
1727 }
1728
1729 function clone$1(element) {
1730 return element.cloneNode(true);
1731 }
1732
1733 var ns = {
1734 svg: 'http://www.w3.org/2000/svg'
1735 };
1736
1737 /**
1738 * DOM parsing utility
1739 */
1740
1741 var SVG_START = '<svg xmlns="' + ns.svg + '"';
1742
1743 function parse$1(svg) {
1744
1745 var unwrap = false;
1746
1747 // ensure we import a valid svg document
1748 if (svg.substring(0, 4) === '<svg') {
1749 if (svg.indexOf(ns.svg) === -1) {
1750 svg = SVG_START + svg.substring(4);
1751 }
1752 } else {
1753 // namespace svg
1754 svg = SVG_START + '>' + svg + '</svg>';
1755 unwrap = true;
1756 }
1757
1758 var parsed = parseDocument(svg);
1759
1760 if (!unwrap) {
1761 return parsed;
1762 }
1763
1764 var fragment = document.createDocumentFragment();
1765
1766 var parent = parsed.firstChild;
1767
1768 while (parent.firstChild) {
1769 fragment.appendChild(parent.firstChild);
1770 }
1771
1772 return fragment;
1773 }
1774
1775 function parseDocument(svg) {
1776
1777 var parser;
1778
1779 // parse
1780 parser = new DOMParser();
1781 parser.async = false;
1782
1783 return parser.parseFromString(svg, 'text/xml');
1784 }
1785
1786 /**
1787 * Create utility for SVG elements
1788 */
1789
1790
1791 /**
1792 * Create a specific type from name or SVG markup.
1793 *
1794 * @param {String} name the name or markup of the element
1795 * @param {Object} [attrs] attributes to set on the element
1796 *
1797 * @returns {SVGElement}
1798 */
1799 function create$1(name, attrs) {
1800 var element;
1801
1802 if (name.charAt(0) === '<') {
1803 element = parse$1(name).firstChild;
1804 element = document.importNode(element, true);
1805 } else {
1806 element = document.createElementNS(ns.svg, name);
1807 }
1808
1809 if (attrs) {
1810 attr$1(element, attrs);
1811 }
1812
1813 return element;
1814 }
1815
1816 /**
1817 * Geometry helpers
1818 */
1819
1820 // fake node used to instantiate svg geometry elements
1821 var node = create$1('svg');
1822
1823 function extend$1(object, props) {
1824 var i, k, keys = Object.keys(props);
1825
1826 for (i = 0; (k = keys[i]); i++) {
1827 object[k] = props[k];
1828 }
1829
1830 return object;
1831 }
1832
1833 /**
1834 * Create matrix via args.
1835 *
1836 * @example
1837 *
1838 * createMatrix({ a: 1, b: 1 });
1839 * createMatrix();
1840 * createMatrix(1, 2, 0, 0, 30, 20);
1841 *
1842 * @return {SVGMatrix}
1843 */
1844 function createMatrix(a, b, c, d, e, f) {
1845 var matrix = node.createSVGMatrix();
1846
1847 switch (arguments.length) {
1848 case 0:
1849 return matrix;
1850 case 1:
1851 return extend$1(matrix, a);
1852 case 6:
1853 return extend$1(matrix, {
1854 a: a,
1855 b: b,
1856 c: c,
1857 d: d,
1858 e: e,
1859 f: f
1860 });
1861 }
1862 }
1863
1864 function createTransform(matrix) {
1865 if (matrix) {
1866 return node.createSVGTransformFromMatrix(matrix);
1867 } else {
1868 return node.createSVGTransform();
1869 }
1870 }
1871
1872 /**
1873 * Serialization util
1874 */
1875
1876 var TEXT_ENTITIES = /([&<>]{1})/g;
1877 var ATTR_ENTITIES = /([\n\r"]{1})/g;
1878
1879 var ENTITY_REPLACEMENT = {
1880 '&': '&amp;',
1881 '<': '&lt;',
1882 '>': '&gt;',
1883 '"': '\''
1884 };
1885
1886 function escape$1(str, pattern) {
1887
1888 function replaceFn(match, entity) {
1889 return ENTITY_REPLACEMENT[entity] || entity;
1890 }
1891
1892 return str.replace(pattern, replaceFn);
1893 }
1894
1895 function serialize(node, output) {
1896
1897 var i, len, attrMap, attrNode, childNodes;
1898
1899 switch (node.nodeType) {
1900 // TEXT
1901 case 3:
1902 // replace special XML characters
1903 output.push(escape$1(node.textContent, TEXT_ENTITIES));
1904 break;
1905
1906 // ELEMENT
1907 case 1:
1908 output.push('<', node.tagName);
1909
1910 if (node.hasAttributes()) {
1911 attrMap = node.attributes;
1912 for (i = 0, len = attrMap.length; i < len; ++i) {
1913 attrNode = attrMap.item(i);
1914 output.push(' ', attrNode.name, '="', escape$1(attrNode.value, ATTR_ENTITIES), '"');
1915 }
1916 }
1917
1918 if (node.hasChildNodes()) {
1919 output.push('>');
1920 childNodes = node.childNodes;
1921 for (i = 0, len = childNodes.length; i < len; ++i) {
1922 serialize(childNodes.item(i), output);
1923 }
1924 output.push('</', node.tagName, '>');
1925 } else {
1926 output.push('/>');
1927 }
1928 break;
1929
1930 // COMMENT
1931 case 8:
1932 output.push('<!--', escape$1(node.nodeValue, TEXT_ENTITIES), '-->');
1933 break;
1934
1935 // CDATA
1936 case 4:
1937 output.push('<![CDATA[', node.nodeValue, ']]>');
1938 break;
1939
1940 default:
1941 throw new Error('unable to handle node ' + node.nodeType);
1942 }
1943
1944 return output;
1945 }
1946
1947 /**
1948 * innerHTML like functionality for SVG elements.
1949 * based on innerSVG (https://code.google.com/p/innersvg)
1950 */
1951
1952
1953 function set$1(element, svg) {
1954
1955 var parsed = parse$1(svg);
1956
1957 // clear element contents
1958 clear$1(element);
1959
1960 if (!svg) {
1961 return;
1962 }
1963
1964 if (!isFragment(parsed)) {
1965 // extract <svg> from parsed document
1966 parsed = parsed.documentElement;
1967 }
1968
1969 var nodes = slice$1(parsed.childNodes);
1970
1971 // import + append each node
1972 for (var i = 0; i < nodes.length; i++) {
1973 appendTo(nodes[i], element);
1974 }
1975
1976 }
1977
1978 function get$1(element) {
1979 var child = element.firstChild,
1980 output = [];
1981
1982 while (child) {
1983 serialize(child, output);
1984 child = child.nextSibling;
1985 }
1986
1987 return output.join('');
1988 }
1989
1990 function isFragment(node) {
1991 return node.nodeName === '#document-fragment';
1992 }
1993
1994 function innerSVG(element, svg) {
1995
1996 if (svg !== undefined) {
1997
1998 try {
1999 set$1(element, svg);
2000 } catch (e) {
2001 throw new Error('error parsing SVG: ' + e.message);
2002 }
2003
2004 return element;
2005 } else {
2006 return get$1(element);
2007 }
2008 }
2009
2010
2011 function slice$1(arr) {
2012 return Array.prototype.slice.call(arr);
2013 }
2014
2015 /**
2016 * transform accessor utility
2017 */
2018
2019 function wrapMatrix(transformList, transform) {
2020 if (transform instanceof SVGMatrix) {
2021 return transformList.createSVGTransformFromMatrix(transform);
2022 }
2023
2024 return transform;
2025 }
2026
2027
2028 function setTransforms(transformList, transforms) {
2029 var i, t;
2030
2031 transformList.clear();
2032
2033 for (i = 0; (t = transforms[i]); i++) {
2034 transformList.appendItem(wrapMatrix(transformList, t));
2035 }
2036 }
2037
2038 /**
2039 * Get or set the transforms on the given node.
2040 *
2041 * @param {SVGElement} node
2042 * @param {SVGTransform|SVGMatrix|Array<SVGTransform|SVGMatrix>} [transforms]
2043 *
2044 * @return {SVGTransform} the consolidated transform
2045 */
2046 function transform$1(node, transforms) {
2047 var transformList = node.transform.baseVal;
2048
2049 if (transforms) {
2050
2051 if (!Array.isArray(transforms)) {
2052 transforms = [ transforms ];
2053 }
2054
2055 setTransforms(transformList, transforms);
2056 }
2057
2058 return transformList.consolidate();
2059 }
2060
2061 var CLASS_PATTERN = /^class /;
2062
2063 function isClass(fn) {
2064 return CLASS_PATTERN.test(fn.toString());
2065 }
2066
2067 function isArray$2(obj) {
2068 return Object.prototype.toString.call(obj) === '[object Array]';
2069 }
2070
2071 function hasOwnProp(obj, prop) {
2072 return Object.prototype.hasOwnProperty.call(obj, prop);
2073 }
2074
2075 function annotate() {
2076 var args = Array.prototype.slice.call(arguments);
2077
2078 if (args.length === 1 && isArray$2(args[0])) {
2079 args = args[0];
2080 }
2081
2082 var fn = args.pop();
2083
2084 fn.$inject = args;
2085
2086 return fn;
2087 }
2088
2089
2090 // Current limitations:
2091 // - can't put into "function arg" comments
2092 // function /* (no parenthesis like this) */ (){}
2093 // function abc( /* xx (no parenthesis like this) */ a, b) {}
2094 //
2095 // Just put the comment before function or inside:
2096 // /* (((this is fine))) */ function(a, b) {}
2097 // function abc(a) { /* (((this is fine))) */}
2098 //
2099 // - can't reliably auto-annotate constructor; we'll match the
2100 // first constructor(...) pattern found which may be the one
2101 // of a nested class, too.
2102
2103 var CONSTRUCTOR_ARGS = /constructor\s*[^(]*\(\s*([^)]*)\)/m;
2104 var FN_ARGS = /^(?:async )?(?:function\s*)?[^(]*\(\s*([^)]*)\)/m;
2105 var FN_ARG = /\/\*([^*]*)\*\//m;
2106
2107 function parseAnnotations(fn) {
2108
2109 if (typeof fn !== 'function') {
2110 throw new Error('Cannot annotate "' + fn + '". Expected a function!');
2111 }
2112
2113 var match = fn.toString().match(isClass(fn) ? CONSTRUCTOR_ARGS : FN_ARGS);
2114
2115 // may parse class without constructor
2116 if (!match) {
2117 return [];
2118 }
2119
2120 return match[1] && match[1].split(',').map(function(arg) {
2121 match = arg.match(FN_ARG);
2122 return match ? match[1].trim() : arg.trim();
2123 }) || [];
2124 }
2125
2126 function Module() {
2127 var providers = [];
2128
2129 this.factory = function(name, factory) {
2130 providers.push([name, 'factory', factory]);
2131 return this;
2132 };
2133
2134 this.value = function(name, value) {
2135 providers.push([name, 'value', value]);
2136 return this;
2137 };
2138
2139 this.type = function(name, type) {
2140 providers.push([name, 'type', type]);
2141 return this;
2142 };
2143
2144 this.forEach = function(iterator) {
2145 providers.forEach(iterator);
2146 };
2147
2148 }
2149
2150 function Injector(modules, parent) {
2151 parent = parent || {
2152 get: function(name, strict) {
2153 currentlyResolving.push(name);
2154
2155 if (strict === false) {
2156 return null;
2157 } else {
2158 throw error('No provider for "' + name + '"!');
2159 }
2160 }
2161 };
2162
2163 var currentlyResolving = [];
2164 var providers = this._providers = Object.create(parent._providers || null);
2165 var instances = this._instances = Object.create(null);
2166
2167 var self = instances.injector = this;
2168
2169 var error = function(msg) {
2170 var stack = currentlyResolving.join(' -> ');
2171 currentlyResolving.length = 0;
2172 return new Error(stack ? msg + ' (Resolving: ' + stack + ')' : msg);
2173 };
2174
2175 /**
2176 * Return a named service.
2177 *
2178 * @param {String} name
2179 * @param {Boolean} [strict=true] if false, resolve missing services to null
2180 *
2181 * @return {Object}
2182 */
2183 var get = function(name, strict) {
2184 if (!providers[name] && name.indexOf('.') !== -1) {
2185 var parts = name.split('.');
2186 var pivot = get(parts.shift());
2187
2188 while (parts.length) {
2189 pivot = pivot[parts.shift()];
2190 }
2191
2192 return pivot;
2193 }
2194
2195 if (hasOwnProp(instances, name)) {
2196 return instances[name];
2197 }
2198
2199 if (hasOwnProp(providers, name)) {
2200 if (currentlyResolving.indexOf(name) !== -1) {
2201 currentlyResolving.push(name);
2202 throw error('Cannot resolve circular dependency!');
2203 }
2204
2205 currentlyResolving.push(name);
2206 instances[name] = providers[name][0](providers[name][1]);
2207 currentlyResolving.pop();
2208
2209 return instances[name];
2210 }
2211
2212 return parent.get(name, strict);
2213 };
2214
2215 var fnDef = function(fn, locals) {
2216
2217 if (typeof locals === 'undefined') {
2218 locals = {};
2219 }
2220
2221 if (typeof fn !== 'function') {
2222 if (isArray$2(fn)) {
2223 fn = annotate(fn.slice());
2224 } else {
2225 throw new Error('Cannot invoke "' + fn + '". Expected a function!');
2226 }
2227 }
2228
2229 var inject = fn.$inject || parseAnnotations(fn);
2230 var dependencies = inject.map(function(dep) {
2231 if (hasOwnProp(locals, dep)) {
2232 return locals[dep];
2233 } else {
2234 return get(dep);
2235 }
2236 });
2237
2238 return {
2239 fn: fn,
2240 dependencies: dependencies
2241 };
2242 };
2243
2244 var instantiate = function(Type) {
2245 var def = fnDef(Type);
2246
2247 var fn = def.fn,
2248 dependencies = def.dependencies;
2249
2250 // instantiate var args constructor
2251 var Constructor = Function.prototype.bind.apply(fn, [ null ].concat(dependencies));
2252
2253 return new Constructor();
2254 };
2255
2256 var invoke = function(func, context, locals) {
2257 var def = fnDef(func, locals);
2258
2259 var fn = def.fn,
2260 dependencies = def.dependencies;
2261
2262 return fn.apply(context, dependencies);
2263 };
2264
2265
2266 var createPrivateInjectorFactory = function(privateChildInjector) {
2267 return annotate(function(key) {
2268 return privateChildInjector.get(key);
2269 });
2270 };
2271
2272 var createChild = function(modules, forceNewInstances) {
2273 if (forceNewInstances && forceNewInstances.length) {
2274 var fromParentModule = Object.create(null);
2275 var matchedScopes = Object.create(null);
2276
2277 var privateInjectorsCache = [];
2278 var privateChildInjectors = [];
2279 var privateChildFactories = [];
2280
2281 var provider;
2282 var cacheIdx;
2283 var privateChildInjector;
2284 var privateChildInjectorFactory;
2285 for (var name in providers) {
2286 provider = providers[name];
2287
2288 if (forceNewInstances.indexOf(name) !== -1) {
2289 if (provider[2] === 'private') {
2290 cacheIdx = privateInjectorsCache.indexOf(provider[3]);
2291 if (cacheIdx === -1) {
2292 privateChildInjector = provider[3].createChild([], forceNewInstances);
2293 privateChildInjectorFactory = createPrivateInjectorFactory(privateChildInjector);
2294 privateInjectorsCache.push(provider[3]);
2295 privateChildInjectors.push(privateChildInjector);
2296 privateChildFactories.push(privateChildInjectorFactory);
2297 fromParentModule[name] = [privateChildInjectorFactory, name, 'private', privateChildInjector];
2298 } else {
2299 fromParentModule[name] = [privateChildFactories[cacheIdx], name, 'private', privateChildInjectors[cacheIdx]];
2300 }
2301 } else {
2302 fromParentModule[name] = [provider[2], provider[1]];
2303 }
2304 matchedScopes[name] = true;
2305 }
2306
2307 if ((provider[2] === 'factory' || provider[2] === 'type') && provider[1].$scope) {
2308 /* jshint -W083 */
2309 forceNewInstances.forEach(function(scope) {
2310 if (provider[1].$scope.indexOf(scope) !== -1) {
2311 fromParentModule[name] = [provider[2], provider[1]];
2312 matchedScopes[scope] = true;
2313 }
2314 });
2315 }
2316 }
2317
2318 forceNewInstances.forEach(function(scope) {
2319 if (!matchedScopes[scope]) {
2320 throw new Error('No provider for "' + scope + '". Cannot use provider from the parent!');
2321 }
2322 });
2323
2324 modules.unshift(fromParentModule);
2325 }
2326
2327 return new Injector(modules, self);
2328 };
2329
2330 var factoryMap = {
2331 factory: invoke,
2332 type: instantiate,
2333 value: function(value) {
2334 return value;
2335 }
2336 };
2337
2338 modules.forEach(function(module) {
2339
2340 function arrayUnwrap(type, value) {
2341 if (type !== 'value' && isArray$2(value)) {
2342 value = annotate(value.slice());
2343 }
2344
2345 return value;
2346 }
2347
2348 // TODO(vojta): handle wrong inputs (modules)
2349 if (module instanceof Module) {
2350 module.forEach(function(provider) {
2351 var name = provider[0];
2352 var type = provider[1];
2353 var value = provider[2];
2354
2355 providers[name] = [factoryMap[type], arrayUnwrap(type, value), type];
2356 });
2357 } else if (typeof module === 'object') {
2358 if (module.__exports__) {
2359 var clonedModule = Object.keys(module).reduce(function(m, key) {
2360 if (key.substring(0, 2) !== '__') {
2361 m[key] = module[key];
2362 }
2363 return m;
2364 }, Object.create(null));
2365
2366 var privateInjector = new Injector((module.__modules__ || []).concat([clonedModule]), self);
2367 var getFromPrivateInjector = annotate(function(key) {
2368 return privateInjector.get(key);
2369 });
2370 module.__exports__.forEach(function(key) {
2371 providers[key] = [getFromPrivateInjector, key, 'private', privateInjector];
2372 });
2373 } else {
2374 Object.keys(module).forEach(function(name) {
2375 if (module[name][2] === 'private') {
2376 providers[name] = module[name];
2377 return;
2378 }
2379
2380 var type = module[name][0];
2381 var value = module[name][1];
2382
2383 providers[name] = [factoryMap[type], arrayUnwrap(type, value), type];
2384 });
2385 }
2386 }
2387 });
2388
2389 // public API
2390 this.get = get;
2391 this.invoke = invoke;
2392 this.instantiate = instantiate;
2393 this.createChild = createChild;
2394 }
2395
2396 var DEFAULT_RENDER_PRIORITY$1 = 1000;
2397
2398 /**
2399 * The base implementation of shape and connection renderers.
2400 *
2401 * @param {EventBus} eventBus
2402 * @param {number} [renderPriority=1000]
2403 */
2404 function BaseRenderer(eventBus, renderPriority) {
2405 var self = this;
2406
2407 renderPriority = renderPriority || DEFAULT_RENDER_PRIORITY$1;
2408
2409 eventBus.on([ 'render.shape', 'render.connection' ], renderPriority, function(evt, context) {
2410 var type = evt.type,
2411 element = context.element,
2412 visuals = context.gfx,
2413 attrs = context.attrs;
2414
2415 if (self.canRender(element)) {
2416 if (type === 'render.shape') {
2417 return self.drawShape(visuals, element, attrs);
2418 } else {
2419 return self.drawConnection(visuals, element, attrs);
2420 }
2421 }
2422 });
2423
2424 eventBus.on([ 'render.getShapePath', 'render.getConnectionPath' ], renderPriority, function(evt, element) {
2425 if (self.canRender(element)) {
2426 if (evt.type === 'render.getShapePath') {
2427 return self.getShapePath(element);
2428 } else {
2429 return self.getConnectionPath(element);
2430 }
2431 }
2432 });
2433 }
2434
2435 /**
2436 * Should check whether *this* renderer can render
2437 * the element/connection.
2438 *
2439 * @param {element} element
2440 *
2441 * @returns {boolean}
2442 */
2443 BaseRenderer.prototype.canRender = function() {};
2444
2445 /**
2446 * Provides the shape's snap svg element to be drawn on the `canvas`.
2447 *
2448 * @param {djs.Graphics} visuals
2449 * @param {Shape} shape
2450 *
2451 * @returns {Snap.svg} [returns a Snap.svg paper element ]
2452 */
2453 BaseRenderer.prototype.drawShape = function() {};
2454
2455 /**
2456 * Provides the shape's snap svg element to be drawn on the `canvas`.
2457 *
2458 * @param {djs.Graphics} visuals
2459 * @param {Connection} connection
2460 *
2461 * @returns {Snap.svg} [returns a Snap.svg paper element ]
2462 */
2463 BaseRenderer.prototype.drawConnection = function() {};
2464
2465 /**
2466 * Gets the SVG path of a shape that represents it's visual bounds.
2467 *
2468 * @param {Shape} shape
2469 *
2470 * @return {string} svg path
2471 */
2472 BaseRenderer.prototype.getShapePath = function() {};
2473
2474 /**
2475 * Gets the SVG path of a connection that represents it's visual bounds.
2476 *
2477 * @param {Connection} connection
2478 *
2479 * @return {string} svg path
2480 */
2481 BaseRenderer.prototype.getConnectionPath = function() {};
2482
2483 function componentsToPath(elements) {
2484 return elements.join(',').replace(/,?([A-z]),?/g, '$1');
2485 }
2486
2487 function toSVGPoints(points) {
2488 var result = '';
2489
2490 for (var i = 0, p; (p = points[i]); i++) {
2491 result += p.x + ',' + p.y + ' ';
2492 }
2493
2494 return result;
2495 }
2496
2497 function createLine(points, attrs) {
2498
2499 var line = create$1('polyline');
2500 attr$1(line, { points: toSVGPoints(points) });
2501
2502 if (attrs) {
2503 attr$1(line, attrs);
2504 }
2505
2506 return line;
2507 }
2508
2509 function updateLine(gfx, points) {
2510 attr$1(gfx, { points: toSVGPoints(points) });
2511
2512 return gfx;
2513 }
2514
2515 /**
2516 * Get parent elements.
2517 *
2518 * @param {Array<djs.model.base>} elements
2519 *
2520 * @returns {Array<djs.model.Base>}
2521 */
2522 function getParents$1(elements) {
2523
2524 // find elements that are not children of any other elements
2525 return filter(elements, function(element) {
2526 return !find(elements, function(e) {
2527 return e !== element && getParent$1(element, e);
2528 });
2529 });
2530 }
2531
2532
2533 function getParent$1(element, parent) {
2534 if (!parent) {
2535 return;
2536 }
2537
2538 if (element === parent) {
2539 return parent;
2540 }
2541
2542 if (!element.parent) {
2543 return;
2544 }
2545
2546 return getParent$1(element.parent, parent);
2547 }
2548
2549
2550 /**
2551 * Adds an element to a collection and returns true if the
2552 * element was added.
2553 *
2554 * @param {Array<Object>} elements
2555 * @param {Object} e
2556 * @param {boolean} unique
2557 */
2558 function add$1(elements, e, unique) {
2559 var canAdd = !unique || elements.indexOf(e) === -1;
2560
2561 if (canAdd) {
2562 elements.push(e);
2563 }
2564
2565 return canAdd;
2566 }
2567
2568
2569 /**
2570 * Iterate over each element in a collection, calling the iterator function `fn`
2571 * with (element, index, recursionDepth).
2572 *
2573 * Recurse into all elements that are returned by `fn`.
2574 *
2575 * @param {Object|Array<Object>} elements
2576 * @param {Function} fn iterator function called with (element, index, recursionDepth)
2577 * @param {number} [depth] maximum recursion depth
2578 */
2579 function eachElement(elements, fn, depth) {
2580
2581 depth = depth || 0;
2582
2583 if (!isArray$4(elements)) {
2584 elements = [ elements ];
2585 }
2586
2587 forEach$2(elements, function(s, i) {
2588 var filter = fn(s, i, depth);
2589
2590 if (isArray$4(filter) && filter.length) {
2591 eachElement(filter, fn, depth + 1);
2592 }
2593 });
2594 }
2595
2596
2597 /**
2598 * Collects self + child elements up to a given depth from a list of elements.
2599 *
2600 * @param {djs.model.Base|Array<djs.model.Base>} elements the elements to select the children from
2601 * @param {boolean} unique whether to return a unique result set (no duplicates)
2602 * @param {number} maxDepth the depth to search through or -1 for infinite
2603 *
2604 * @return {Array<djs.model.Base>} found elements
2605 */
2606 function selfAndChildren(elements, unique, maxDepth) {
2607 var result = [],
2608 processedChildren = [];
2609
2610 eachElement(elements, function(element, i, depth) {
2611 add$1(result, element, unique);
2612
2613 var children = element.children;
2614
2615 // max traversal depth not reached yet
2616 if (maxDepth === -1 || depth < maxDepth) {
2617
2618 // children exist && children not yet processed
2619 if (children && add$1(processedChildren, children, unique)) {
2620 return children;
2621 }
2622 }
2623 });
2624
2625 return result;
2626 }
2627
2628
2629 /**
2630 * Return self + ALL children for a number of elements
2631 *
2632 * @param {Array<djs.model.Base>} elements to query
2633 * @param {boolean} allowDuplicates to allow duplicates in the result set
2634 *
2635 * @return {Array<djs.model.Base>} the collected elements
2636 */
2637 function selfAndAllChildren(elements, allowDuplicates) {
2638 return selfAndChildren(elements, !allowDuplicates, -1);
2639 }
2640
2641
2642 /**
2643 * Gets the the closure for all selected elements,
2644 * their enclosed children and connections.
2645 *
2646 * @param {Array<djs.model.Base>} elements
2647 * @param {boolean} [isTopLevel=true]
2648 * @param {Object} [existingClosure]
2649 *
2650 * @return {Object} newClosure
2651 */
2652 function getClosure(elements, isTopLevel, closure) {
2653
2654 if (isUndefined$3(isTopLevel)) {
2655 isTopLevel = true;
2656 }
2657
2658 if (isObject(isTopLevel)) {
2659 closure = isTopLevel;
2660 isTopLevel = true;
2661 }
2662
2663
2664 closure = closure || {};
2665
2666 var allShapes = copyObject(closure.allShapes),
2667 allConnections = copyObject(closure.allConnections),
2668 enclosedElements = copyObject(closure.enclosedElements),
2669 enclosedConnections = copyObject(closure.enclosedConnections);
2670
2671 var topLevel = copyObject(
2672 closure.topLevel,
2673 isTopLevel && groupBy(elements, function(e) { return e.id; })
2674 );
2675
2676
2677 function handleConnection(c) {
2678 if (topLevel[c.source.id] && topLevel[c.target.id]) {
2679 topLevel[c.id] = [ c ];
2680 }
2681
2682 // not enclosed as a child, but maybe logically
2683 // (connecting two moved elements?)
2684 if (allShapes[c.source.id] && allShapes[c.target.id]) {
2685 enclosedConnections[c.id] = enclosedElements[c.id] = c;
2686 }
2687
2688 allConnections[c.id] = c;
2689 }
2690
2691 function handleElement(element) {
2692
2693 enclosedElements[element.id] = element;
2694
2695 if (element.waypoints) {
2696
2697 // remember connection
2698 enclosedConnections[element.id] = allConnections[element.id] = element;
2699 } else {
2700
2701 // remember shape
2702 allShapes[element.id] = element;
2703
2704 // remember all connections
2705 forEach$2(element.incoming, handleConnection);
2706
2707 forEach$2(element.outgoing, handleConnection);
2708
2709 // recurse into children
2710 return element.children;
2711 }
2712 }
2713
2714 eachElement(elements, handleElement);
2715
2716 return {
2717 allShapes: allShapes,
2718 allConnections: allConnections,
2719 topLevel: topLevel,
2720 enclosedConnections: enclosedConnections,
2721 enclosedElements: enclosedElements
2722 };
2723 }
2724
2725 /**
2726 * Returns the surrounding bbox for all elements in
2727 * the array or the element primitive.
2728 *
2729 * @param {Array<djs.model.Shape>|djs.model.Shape} elements
2730 * @param {boolean} stopRecursion
2731 */
2732 function getBBox(elements, stopRecursion) {
2733
2734 stopRecursion = !!stopRecursion;
2735 if (!isArray$4(elements)) {
2736 elements = [ elements ];
2737 }
2738
2739 var minX,
2740 minY,
2741 maxX,
2742 maxY;
2743
2744 forEach$2(elements, function(element) {
2745
2746 // If element is a connection the bbox must be computed first
2747 var bbox = element;
2748 if (element.waypoints && !stopRecursion) {
2749 bbox = getBBox(element.waypoints, true);
2750 }
2751
2752 var x = bbox.x,
2753 y = bbox.y,
2754 height = bbox.height || 0,
2755 width = bbox.width || 0;
2756
2757 if (x < minX || minX === undefined) {
2758 minX = x;
2759 }
2760 if (y < minY || minY === undefined) {
2761 minY = y;
2762 }
2763
2764 if ((x + width) > maxX || maxX === undefined) {
2765 maxX = x + width;
2766 }
2767 if ((y + height) > maxY || maxY === undefined) {
2768 maxY = y + height;
2769 }
2770 });
2771
2772 return {
2773 x: minX,
2774 y: minY,
2775 height: maxY - minY,
2776 width: maxX - minX
2777 };
2778 }
2779
2780
2781 /**
2782 * Returns all elements that are enclosed from the bounding box.
2783 *
2784 * * If bbox.(width|height) is not specified the method returns
2785 * all elements with element.x/y > bbox.x/y
2786 * * If only bbox.x or bbox.y is specified, method return all elements with
2787 * e.x > bbox.x or e.y > bbox.y
2788 *
2789 * @param {Array<djs.model.Shape>} elements List of Elements to search through
2790 * @param {djs.model.Shape} bbox the enclosing bbox.
2791 *
2792 * @return {Array<djs.model.Shape>} enclosed elements
2793 */
2794 function getEnclosedElements(elements, bbox) {
2795
2796 var filteredElements = {};
2797
2798 forEach$2(elements, function(element) {
2799
2800 var e = element;
2801
2802 if (e.waypoints) {
2803 e = getBBox(e);
2804 }
2805
2806 if (!isNumber(bbox.y) && (e.x > bbox.x)) {
2807 filteredElements[element.id] = element;
2808 }
2809 if (!isNumber(bbox.x) && (e.y > bbox.y)) {
2810 filteredElements[element.id] = element;
2811 }
2812 if (e.x > bbox.x && e.y > bbox.y) {
2813 if (isNumber(bbox.width) && isNumber(bbox.height) &&
2814 e.width + e.x < bbox.width + bbox.x &&
2815 e.height + e.y < bbox.height + bbox.y) {
2816
2817 filteredElements[element.id] = element;
2818 } else if (!isNumber(bbox.width) || !isNumber(bbox.height)) {
2819 filteredElements[element.id] = element;
2820 }
2821 }
2822 });
2823
2824 return filteredElements;
2825 }
2826
2827
2828 function getType(element) {
2829
2830 if ('waypoints' in element) {
2831 return 'connection';
2832 }
2833
2834 if ('x' in element) {
2835 return 'shape';
2836 }
2837
2838 return 'root';
2839 }
2840
2841 function isFrameElement$1(element) {
2842
2843 return !!(element && element.isFrame);
2844 }
2845
2846 // helpers ///////////////////////////////
2847
2848 function copyObject(src1, src2) {
2849 return assign({}, src1 || {}, src2 || {});
2850 }
2851
2852 // apply default renderer with lowest possible priority
2853 // so that it only kicks in if noone else could render
2854 var DEFAULT_RENDER_PRIORITY = 1;
2855
2856 /**
2857 * The default renderer used for shapes and connections.
2858 *
2859 * @param {EventBus} eventBus
2860 * @param {Styles} styles
2861 */
2862 function DefaultRenderer(eventBus, styles) {
2863
2864 //
2865 BaseRenderer.call(this, eventBus, DEFAULT_RENDER_PRIORITY);
2866
2867 this.CONNECTION_STYLE = styles.style([ 'no-fill' ], { strokeWidth: 5, stroke: 'fuchsia' });
2868 this.SHAPE_STYLE = styles.style({ fill: 'white', stroke: 'fuchsia', strokeWidth: 2 });
2869 this.FRAME_STYLE = styles.style([ 'no-fill' ], { stroke: 'fuchsia', strokeDasharray: 4, strokeWidth: 2 });
2870 }
2871
2872 inherits$1(DefaultRenderer, BaseRenderer);
2873
2874
2875 DefaultRenderer.prototype.canRender = function() {
2876 return true;
2877 };
2878
2879 DefaultRenderer.prototype.drawShape = function drawShape(visuals, element, attrs) {
2880 var rect = create$1('rect');
2881
2882 attr$1(rect, {
2883 x: 0,
2884 y: 0,
2885 width: element.width || 0,
2886 height: element.height || 0
2887 });
2888
2889 if (isFrameElement$1(element)) {
2890 attr$1(rect, assign({}, this.FRAME_STYLE, attrs || {}));
2891 } else {
2892 attr$1(rect, assign({}, this.SHAPE_STYLE, attrs || {}));
2893 }
2894
2895 append(visuals, rect);
2896
2897 return rect;
2898 };
2899
2900 DefaultRenderer.prototype.drawConnection = function drawConnection(visuals, connection, attrs) {
2901
2902 var line = createLine(connection.waypoints, assign({}, this.CONNECTION_STYLE, attrs || {}));
2903 append(visuals, line);
2904
2905 return line;
2906 };
2907
2908 DefaultRenderer.prototype.getShapePath = function getShapePath(shape) {
2909
2910 var x = shape.x,
2911 y = shape.y,
2912 width = shape.width,
2913 height = shape.height;
2914
2915 var shapePath = [
2916 [ 'M', x, y ],
2917 [ 'l', width, 0 ],
2918 [ 'l', 0, height ],
2919 [ 'l', -width, 0 ],
2920 [ 'z' ]
2921 ];
2922
2923 return componentsToPath(shapePath);
2924 };
2925
2926 DefaultRenderer.prototype.getConnectionPath = function getConnectionPath(connection) {
2927 var waypoints = connection.waypoints;
2928
2929 var idx, point, connectionPath = [];
2930
2931 for (idx = 0; (point = waypoints[idx]); idx++) {
2932
2933 // take invisible docking into account
2934 // when creating the path
2935 point = point.original || point;
2936
2937 connectionPath.push([ idx === 0 ? 'M' : 'L', point.x, point.y ]);
2938 }
2939
2940 return componentsToPath(connectionPath);
2941 };
2942
2943
2944 DefaultRenderer.$inject = [ 'eventBus', 'styles' ];
2945
2946 /**
2947 * A component that manages shape styles
2948 */
2949 function Styles() {
2950
2951 var defaultTraits = {
2952
2953 'no-fill': {
2954 fill: 'none'
2955 },
2956 'no-border': {
2957 strokeOpacity: 0.0
2958 },
2959 'no-events': {
2960 pointerEvents: 'none'
2961 }
2962 };
2963
2964 var self = this;
2965
2966 /**
2967 * Builds a style definition from a className, a list of traits and an object of additional attributes.
2968 *
2969 * @param {string} className
2970 * @param {Array<string>} traits
2971 * @param {Object} additionalAttrs
2972 *
2973 * @return {Object} the style defintion
2974 */
2975 this.cls = function(className, traits, additionalAttrs) {
2976 var attrs = this.style(traits, additionalAttrs);
2977
2978 return assign(attrs, { 'class': className });
2979 };
2980
2981 /**
2982 * Builds a style definition from a list of traits and an object of additional attributes.
2983 *
2984 * @param {Array<string>} traits
2985 * @param {Object} additionalAttrs
2986 *
2987 * @return {Object} the style defintion
2988 */
2989 this.style = function(traits, additionalAttrs) {
2990
2991 if (!isArray$4(traits) && !additionalAttrs) {
2992 additionalAttrs = traits;
2993 traits = [];
2994 }
2995
2996 var attrs = reduce(traits, function(attrs, t) {
2997 return assign(attrs, defaultTraits[t] || {});
2998 }, {});
2999
3000 return additionalAttrs ? assign(attrs, additionalAttrs) : attrs;
3001 };
3002
3003 this.computeStyle = function(custom, traits, defaultStyles) {
3004 if (!isArray$4(traits)) {
3005 defaultStyles = traits;
3006 traits = [];
3007 }
3008
3009 return self.style(traits || [], assign({}, defaultStyles, custom || {}));
3010 };
3011 }
3012
3013 var DrawModule$1 = {
3014 __init__: [ 'defaultRenderer' ],
3015 defaultRenderer: [ 'type', DefaultRenderer ],
3016 styles: [ 'type', Styles ]
3017 };
3018
3019 /**
3020 * Flatten array, one level deep.
3021 *
3022 * @param {Array<?>} arr
3023 *
3024 * @return {Array<?>}
3025 */
3026
3027 var nativeToString = Object.prototype.toString;
3028 var nativeHasOwnProperty = Object.prototype.hasOwnProperty;
3029 function isUndefined$1(obj) {
3030 return obj === undefined;
3031 }
3032 function isArray$1(obj) {
3033 return nativeToString.call(obj) === '[object Array]';
3034 }
3035 /**
3036 * Return true, if target owns a property with the given key.
3037 *
3038 * @param {Object} target
3039 * @param {String} key
3040 *
3041 * @return {Boolean}
3042 */
3043
3044 function has(target, key) {
3045 return nativeHasOwnProperty.call(target, key);
3046 }
3047 /**
3048 * Iterate over collection; returning something
3049 * (non-undefined) will stop iteration.
3050 *
3051 * @param {Array|Object} collection
3052 * @param {Function} iterator
3053 *
3054 * @return {Object} return result that stopped the iteration
3055 */
3056
3057 function forEach(collection, iterator) {
3058 var val, result;
3059
3060 if (isUndefined$1(collection)) {
3061 return;
3062 }
3063
3064 var convertKey = isArray$1(collection) ? toNum : identity;
3065
3066 for (var key in collection) {
3067 if (has(collection, key)) {
3068 val = collection[key];
3069 result = iterator(val, convertKey(key));
3070
3071 if (result === false) {
3072 return val;
3073 }
3074 }
3075 }
3076 }
3077
3078 function identity(arg) {
3079 return arg;
3080 }
3081
3082 function toNum(arg) {
3083 return Number(arg);
3084 }
3085
3086 /**
3087 * Assigns style attributes in a style-src compliant way.
3088 *
3089 * @param {Element} element
3090 * @param {...Object} styleSources
3091 *
3092 * @return {Element} the element
3093 */
3094 function assign$1(element) {
3095 var target = element.style;
3096
3097 for (var _len = arguments.length, styleSources = Array(_len > 1 ? _len - 1 : 0), _key = 1; _key < _len; _key++) {
3098 styleSources[_key - 1] = arguments[_key];
3099 }
3100
3101 forEach(styleSources, function (style) {
3102 if (!style) {
3103 return;
3104 }
3105
3106 forEach(style, function (value, key) {
3107 target[key] = value;
3108 });
3109 });
3110
3111 return element;
3112 }
3113
3114 /**
3115 * Set attribute `name` to `val`, or get attr `name`.
3116 *
3117 * @param {Element} el
3118 * @param {String} name
3119 * @param {String} [val]
3120 * @api public
3121 */
3122 function attr(el, name, val) {
3123 // get
3124 if (arguments.length == 2) {
3125 return el.getAttribute(name);
3126 }
3127
3128 // remove
3129 if (val === null) {
3130 return el.removeAttribute(name);
3131 }
3132
3133 // set
3134 el.setAttribute(name, val);
3135
3136 return el;
3137 }
3138
3139 var indexOf$1 = [].indexOf;
3140
3141 var indexof = function(arr, obj){
3142 if (indexOf$1) return arr.indexOf(obj);
3143 for (var i = 0; i < arr.length; ++i) {
3144 if (arr[i] === obj) return i;
3145 }
3146 return -1;
3147 };
3148
3149 /**
3150 * Taken from https://github.com/component/classes
3151 *
3152 * Without the component bits.
3153 */
3154
3155 /**
3156 * Whitespace regexp.
3157 */
3158
3159 var re = /\s+/;
3160
3161 /**
3162 * toString reference.
3163 */
3164
3165 var toString = Object.prototype.toString;
3166
3167 /**
3168 * Wrap `el` in a `ClassList`.
3169 *
3170 * @param {Element} el
3171 * @return {ClassList}
3172 * @api public
3173 */
3174
3175 function classes(el) {
3176 return new ClassList(el);
3177 }
3178
3179 /**
3180 * Initialize a new ClassList for `el`.
3181 *
3182 * @param {Element} el
3183 * @api private
3184 */
3185
3186 function ClassList(el) {
3187 if (!el || !el.nodeType) {
3188 throw new Error('A DOM element reference is required');
3189 }
3190 this.el = el;
3191 this.list = el.classList;
3192 }
3193
3194 /**
3195 * Add class `name` if not already present.
3196 *
3197 * @param {String} name
3198 * @return {ClassList}
3199 * @api public
3200 */
3201
3202 ClassList.prototype.add = function (name) {
3203 // classList
3204 if (this.list) {
3205 this.list.add(name);
3206 return this;
3207 }
3208
3209 // fallback
3210 var arr = this.array();
3211 var i = indexof(arr, name);
3212 if (!~i) arr.push(name);
3213 this.el.className = arr.join(' ');
3214 return this;
3215 };
3216
3217 /**
3218 * Remove class `name` when present, or
3219 * pass a regular expression to remove
3220 * any which match.
3221 *
3222 * @param {String|RegExp} name
3223 * @return {ClassList}
3224 * @api public
3225 */
3226
3227 ClassList.prototype.remove = function (name) {
3228 if ('[object RegExp]' == toString.call(name)) {
3229 return this.removeMatching(name);
3230 }
3231
3232 // classList
3233 if (this.list) {
3234 this.list.remove(name);
3235 return this;
3236 }
3237
3238 // fallback
3239 var arr = this.array();
3240 var i = indexof(arr, name);
3241 if (~i) arr.splice(i, 1);
3242 this.el.className = arr.join(' ');
3243 return this;
3244 };
3245
3246 /**
3247 * Remove all classes matching `re`.
3248 *
3249 * @param {RegExp} re
3250 * @return {ClassList}
3251 * @api private
3252 */
3253
3254 ClassList.prototype.removeMatching = function (re) {
3255 var arr = this.array();
3256 for (var i = 0; i < arr.length; i++) {
3257 if (re.test(arr[i])) {
3258 this.remove(arr[i]);
3259 }
3260 }
3261 return this;
3262 };
3263
3264 /**
3265 * Toggle class `name`, can force state via `force`.
3266 *
3267 * For browsers that support classList, but do not support `force` yet,
3268 * the mistake will be detected and corrected.
3269 *
3270 * @param {String} name
3271 * @param {Boolean} force
3272 * @return {ClassList}
3273 * @api public
3274 */
3275
3276 ClassList.prototype.toggle = function (name, force) {
3277 // classList
3278 if (this.list) {
3279 if ('undefined' !== typeof force) {
3280 if (force !== this.list.toggle(name, force)) {
3281 this.list.toggle(name); // toggle again to correct
3282 }
3283 } else {
3284 this.list.toggle(name);
3285 }
3286 return this;
3287 }
3288
3289 // fallback
3290 if ('undefined' !== typeof force) {
3291 if (!force) {
3292 this.remove(name);
3293 } else {
3294 this.add(name);
3295 }
3296 } else {
3297 if (this.has(name)) {
3298 this.remove(name);
3299 } else {
3300 this.add(name);
3301 }
3302 }
3303
3304 return this;
3305 };
3306
3307 /**
3308 * Return an array of classes.
3309 *
3310 * @return {Array}
3311 * @api public
3312 */
3313
3314 ClassList.prototype.array = function () {
3315 var className = this.el.getAttribute('class') || '';
3316 var str = className.replace(/^\s+|\s+$/g, '');
3317 var arr = str.split(re);
3318 if ('' === arr[0]) arr.shift();
3319 return arr;
3320 };
3321
3322 /**
3323 * Check if class `name` is present.
3324 *
3325 * @param {String} name
3326 * @return {ClassList}
3327 * @api public
3328 */
3329
3330 ClassList.prototype.has = ClassList.prototype.contains = function (name) {
3331 return this.list ? this.list.contains(name) : !!~indexof(this.array(), name);
3332 };
3333
3334 /**
3335 * Remove all children from the given element.
3336 */
3337 function clear(el) {
3338
3339 var c;
3340
3341 while (el.childNodes.length) {
3342 c = el.childNodes[0];
3343 el.removeChild(c);
3344 }
3345
3346 return el;
3347 }
3348
3349 var proto = typeof Element !== 'undefined' ? Element.prototype : {};
3350 var vendor = proto.matches
3351 || proto.matchesSelector
3352 || proto.webkitMatchesSelector
3353 || proto.mozMatchesSelector
3354 || proto.msMatchesSelector
3355 || proto.oMatchesSelector;
3356
3357 var matchesSelector = match;
3358
3359 /**
3360 * Match `el` to `selector`.
3361 *
3362 * @param {Element} el
3363 * @param {String} selector
3364 * @return {Boolean}
3365 * @api public
3366 */
3367
3368 function match(el, selector) {
3369 if (!el || el.nodeType !== 1) return false;
3370 if (vendor) return vendor.call(el, selector);
3371 var nodes = el.parentNode.querySelectorAll(selector);
3372 for (var i = 0; i < nodes.length; i++) {
3373 if (nodes[i] == el) return true;
3374 }
3375 return false;
3376 }
3377
3378 /**
3379 * Closest
3380 *
3381 * @param {Element} el
3382 * @param {String} selector
3383 * @param {Boolean} checkYourSelf (optional)
3384 */
3385 function closest (element, selector, checkYourSelf) {
3386 var currentElem = checkYourSelf ? element : element.parentNode;
3387
3388 while (currentElem && currentElem.nodeType !== document.DOCUMENT_NODE && currentElem.nodeType !== document.DOCUMENT_FRAGMENT_NODE) {
3389
3390 if (matchesSelector(currentElem, selector)) {
3391 return currentElem;
3392 }
3393
3394 currentElem = currentElem.parentNode;
3395 }
3396
3397 return matchesSelector(currentElem, selector) ? currentElem : null;
3398 }
3399
3400 var bind$1 = window.addEventListener ? 'addEventListener' : 'attachEvent',
3401 unbind = window.removeEventListener ? 'removeEventListener' : 'detachEvent',
3402 prefix$6 = bind$1 !== 'addEventListener' ? 'on' : '';
3403
3404 /**
3405 * Bind `el` event `type` to `fn`.
3406 *
3407 * @param {Element} el
3408 * @param {String} type
3409 * @param {Function} fn
3410 * @param {Boolean} capture
3411 * @return {Function}
3412 * @api public
3413 */
3414
3415 var bind_1 = function(el, type, fn, capture){
3416 el[bind$1](prefix$6 + type, fn, capture || false);
3417 return fn;
3418 };
3419
3420 /**
3421 * Unbind `el` event `type`'s callback `fn`.
3422 *
3423 * @param {Element} el
3424 * @param {String} type
3425 * @param {Function} fn
3426 * @param {Boolean} capture
3427 * @return {Function}
3428 * @api public
3429 */
3430
3431 var unbind_1 = function(el, type, fn, capture){
3432 el[unbind](prefix$6 + type, fn, capture || false);
3433 return fn;
3434 };
3435
3436 var componentEvent = {
3437 bind: bind_1,
3438 unbind: unbind_1
3439 };
3440
3441 /**
3442 * Module dependencies.
3443 */
3444
3445 /**
3446 * Delegate event `type` to `selector`
3447 * and invoke `fn(e)`. A callback function
3448 * is returned which may be passed to `.unbind()`.
3449 *
3450 * @param {Element} el
3451 * @param {String} selector
3452 * @param {String} type
3453 * @param {Function} fn
3454 * @param {Boolean} capture
3455 * @return {Function}
3456 * @api public
3457 */
3458
3459 // Some events don't bubble, so we want to bind to the capture phase instead
3460 // when delegating.
3461 var forceCaptureEvents = ['focus', 'blur'];
3462
3463 function bind$2(el, selector, type, fn, capture) {
3464 if (forceCaptureEvents.indexOf(type) !== -1) {
3465 capture = true;
3466 }
3467
3468 return componentEvent.bind(el, type, function (e) {
3469 var target = e.target || e.srcElement;
3470 e.delegateTarget = closest(target, selector, true);
3471 if (e.delegateTarget) {
3472 fn.call(el, e);
3473 }
3474 }, capture);
3475 }
3476
3477 /**
3478 * Unbind event `type`'s callback `fn`.
3479 *
3480 * @param {Element} el
3481 * @param {String} type
3482 * @param {Function} fn
3483 * @param {Boolean} capture
3484 * @api public
3485 */
3486 function unbind$1(el, type, fn, capture) {
3487 if (forceCaptureEvents.indexOf(type) !== -1) {
3488 capture = true;
3489 }
3490
3491 return componentEvent.unbind(el, type, fn, capture);
3492 }
3493
3494 var delegate = {
3495 bind: bind$2,
3496 unbind: unbind$1
3497 };
3498
3499 /**
3500 * Expose `parse`.
3501 */
3502
3503 var domify = parse;
3504
3505 /**
3506 * Tests for browser support.
3507 */
3508
3509 var innerHTMLBug = false;
3510 var bugTestDiv;
3511 if (typeof document !== 'undefined') {
3512 bugTestDiv = document.createElement('div');
3513 // Setup
3514 bugTestDiv.innerHTML = ' <link/><table></table><a href="/a">a</a><input type="checkbox"/>';
3515 // Make sure that link elements get serialized correctly by innerHTML
3516 // This requires a wrapper element in IE
3517 innerHTMLBug = !bugTestDiv.getElementsByTagName('link').length;
3518 bugTestDiv = undefined;
3519 }
3520
3521 /**
3522 * Wrap map from jquery.
3523 */
3524
3525 var map$1 = {
3526 legend: [1, '<fieldset>', '</fieldset>'],
3527 tr: [2, '<table><tbody>', '</tbody></table>'],
3528 col: [2, '<table><tbody></tbody><colgroup>', '</colgroup></table>'],
3529 // for script/link/style tags to work in IE6-8, you have to wrap
3530 // in a div with a non-whitespace character in front, ha!
3531 _default: innerHTMLBug ? [1, 'X<div>', '</div>'] : [0, '', '']
3532 };
3533
3534 map$1.td =
3535 map$1.th = [3, '<table><tbody><tr>', '</tr></tbody></table>'];
3536
3537 map$1.option =
3538 map$1.optgroup = [1, '<select multiple="multiple">', '</select>'];
3539
3540 map$1.thead =
3541 map$1.tbody =
3542 map$1.colgroup =
3543 map$1.caption =
3544 map$1.tfoot = [1, '<table>', '</table>'];
3545
3546 map$1.polyline =
3547 map$1.ellipse =
3548 map$1.polygon =
3549 map$1.circle =
3550 map$1.text =
3551 map$1.line =
3552 map$1.path =
3553 map$1.rect =
3554 map$1.g = [1, '<svg xmlns="http://www.w3.org/2000/svg" version="1.1">','</svg>'];
3555
3556 /**
3557 * Parse `html` and return a DOM Node instance, which could be a TextNode,
3558 * HTML DOM Node of some kind (<div> for example), or a DocumentFragment
3559 * instance, depending on the contents of the `html` string.
3560 *
3561 * @param {String} html - HTML string to "domify"
3562 * @param {Document} doc - The `document` instance to create the Node for
3563 * @return {DOMNode} the TextNode, DOM Node, or DocumentFragment instance
3564 * @api private
3565 */
3566
3567 function parse(html, doc) {
3568 if ('string' != typeof html) throw new TypeError('String expected');
3569
3570 // default to the global `document` object
3571 if (!doc) doc = document;
3572
3573 // tag name
3574 var m = /<([\w:]+)/.exec(html);
3575 if (!m) return doc.createTextNode(html);
3576
3577 html = html.replace(/^\s+|\s+$/g, ''); // Remove leading/trailing whitespace
3578
3579 var tag = m[1];
3580
3581 // body support
3582 if (tag == 'body') {
3583 var el = doc.createElement('html');
3584 el.innerHTML = html;
3585 return el.removeChild(el.lastChild);
3586 }
3587
3588 // wrap map
3589 var wrap = map$1[tag] || map$1._default;
3590 var depth = wrap[0];
3591 var prefix = wrap[1];
3592 var suffix = wrap[2];
3593 var el = doc.createElement('div');
3594 el.innerHTML = prefix + html + suffix;
3595 while (depth--) el = el.lastChild;
3596
3597 // one element
3598 if (el.firstChild == el.lastChild) {
3599 return el.removeChild(el.firstChild);
3600 }
3601
3602 // several elements
3603 var fragment = doc.createDocumentFragment();
3604 while (el.firstChild) {
3605 fragment.appendChild(el.removeChild(el.firstChild));
3606 }
3607
3608 return fragment;
3609 }
3610
3611 function query(selector, el) {
3612 el = el || document;
3613
3614 return el.querySelector(selector);
3615 }
3616
3617 function all(selector, el) {
3618 el = el || document;
3619
3620 return el.querySelectorAll(selector);
3621 }
3622
3623 function remove$1(el) {
3624 el.parentNode && el.parentNode.removeChild(el);
3625 }
3626
3627 /**
3628 * Failsafe remove an element from a collection
3629 *
3630 * @param {Array<Object>} [collection]
3631 * @param {Object} [element]
3632 *
3633 * @return {number} the previous index of the element
3634 */
3635 function remove(collection, element) {
3636
3637 if (!collection || !element) {
3638 return -1;
3639 }
3640
3641 var idx = collection.indexOf(element);
3642
3643 if (idx !== -1) {
3644 collection.splice(idx, 1);
3645 }
3646
3647 return idx;
3648 }
3649
3650 /**
3651 * Fail save add an element to the given connection, ensuring
3652 * it does not yet exist.
3653 *
3654 * @param {Array<Object>} collection
3655 * @param {Object} element
3656 * @param {number} idx
3657 */
3658 function add(collection, element, idx) {
3659
3660 if (!collection || !element) {
3661 return;
3662 }
3663
3664 if (typeof idx !== 'number') {
3665 idx = -1;
3666 }
3667
3668 var currentIdx = collection.indexOf(element);
3669
3670 if (currentIdx !== -1) {
3671
3672 if (currentIdx === idx) {
3673
3674 // nothing to do, position has not changed
3675 return;
3676 } else {
3677
3678 if (idx !== -1) {
3679
3680 // remove from current position
3681 collection.splice(currentIdx, 1);
3682 } else {
3683
3684 // already exists in collection
3685 return;
3686 }
3687 }
3688 }
3689
3690 if (idx !== -1) {
3691
3692 // insert at specified position
3693 collection.splice(idx, 0, element);
3694 } else {
3695
3696 // push to end
3697 collection.push(element);
3698 }
3699 }
3700
3701
3702 /**
3703 * Fail save get the index of an element in a collection.
3704 *
3705 * @param {Array<Object>} collection
3706 * @param {Object} element
3707 *
3708 * @return {number} the index or -1 if collection or element do
3709 * not exist or the element is not contained.
3710 */
3711 function indexOf(collection, element) {
3712
3713 if (!collection || !element) {
3714 return -1;
3715 }
3716
3717 return collection.indexOf(element);
3718 }
3719
3720 /**
3721 * Computes the distance between two points
3722 *
3723 * @param {Point} p
3724 * @param {Point} q
3725 *
3726 * @return {number} distance
3727 */
3728 function pointDistance(a, b) {
3729 if (!a || !b) {
3730 return -1;
3731 }
3732
3733 return Math.sqrt(
3734 Math.pow(a.x - b.x, 2) +
3735 Math.pow(a.y - b.y, 2)
3736 );
3737 }
3738
3739
3740 /**
3741 * Returns true if the point r is on the line between p and q
3742 *
3743 * @param {Point} p
3744 * @param {Point} q
3745 * @param {Point} r
3746 * @param {number} [accuracy=5] accuracy for points on line check (lower is better)
3747 *
3748 * @return {boolean}
3749 */
3750 function pointsOnLine(p, q, r, accuracy) {
3751
3752 if (typeof accuracy === 'undefined') {
3753 accuracy = 5;
3754 }
3755
3756 if (!p || !q || !r) {
3757 return false;
3758 }
3759
3760 var val = (q.x - p.x) * (r.y - p.y) - (q.y - p.y) * (r.x - p.x),
3761 dist = pointDistance(p, q);
3762
3763 // @see http://stackoverflow.com/a/907491/412190
3764 return Math.abs(val / dist) <= accuracy;
3765 }
3766
3767
3768 var ALIGNED_THRESHOLD = 2;
3769
3770 /**
3771 * Check whether two points are horizontally or vertically aligned.
3772 *
3773 * @param {Array<Point>|Point}
3774 * @param {Point}
3775 *
3776 * @return {string|boolean}
3777 */
3778 function pointsAligned(a, b) {
3779 var points;
3780
3781 if (isArray$4(a)) {
3782 points = a;
3783 } else {
3784 points = [ a, b ];
3785 }
3786
3787 if (pointsAlignedHorizontally(points)) {
3788 return 'h';
3789 }
3790
3791 if (pointsAlignedVertically(points)) {
3792 return 'v';
3793 }
3794
3795 return false;
3796 }
3797
3798 function pointsAlignedHorizontally(a, b) {
3799 var points;
3800
3801 if (isArray$4(a)) {
3802 points = a;
3803 } else {
3804 points = [ a, b ];
3805 }
3806
3807 var firstPoint = points.slice().shift();
3808
3809 return every(points, function(point) {
3810 return Math.abs(firstPoint.y - point.y) <= ALIGNED_THRESHOLD;
3811 });
3812 }
3813
3814 function pointsAlignedVertically(a, b) {
3815 var points;
3816
3817 if (isArray$4(a)) {
3818 points = a;
3819 } else {
3820 points = [ a, b ];
3821 }
3822
3823 var firstPoint = points.slice().shift();
3824
3825 return every(points, function(point) {
3826 return Math.abs(firstPoint.x - point.x) <= ALIGNED_THRESHOLD;
3827 });
3828 }
3829
3830
3831
3832 /**
3833 * Returns true if the point p is inside the rectangle rect
3834 *
3835 * @param {Point} p
3836 * @param {Rect} rect
3837 * @param {number} tolerance
3838 *
3839 * @return {boolean}
3840 */
3841 function pointInRect(p, rect, tolerance) {
3842 tolerance = tolerance || 0;
3843
3844 return p.x > rect.x - tolerance &&
3845 p.y > rect.y - tolerance &&
3846 p.x < rect.x + rect.width + tolerance &&
3847 p.y < rect.y + rect.height + tolerance;
3848 }
3849
3850 /**
3851 * Returns a point in the middle of points p and q
3852 *
3853 * @param {Point} p
3854 * @param {Point} q
3855 *
3856 * @return {Point} middle point
3857 */
3858 function getMidPoint(p, q) {
3859 return {
3860 x: Math.round(p.x + ((q.x - p.x) / 2.0)),
3861 y: Math.round(p.y + ((q.y - p.y) / 2.0))
3862 };
3863 }
3864
3865 /**
3866 * This file contains source code adapted from Snap.svg (licensed Apache-2.0).
3867 *
3868 * @see https://github.com/adobe-webplatform/Snap.svg/blob/master/src/path.js
3869 */
3870
3871 /* eslint no-fallthrough: "off" */
3872
3873 var p2s = /,?([a-z]),?/gi,
3874 toFloat = parseFloat,
3875 math = Math,
3876 PI = math.PI,
3877 mmin = math.min,
3878 mmax = math.max,
3879 pow = math.pow,
3880 abs$7 = math.abs,
3881 pathCommand = /([a-z])[\s,]*((-?\d*\.?\d*(?:e[-+]?\d+)?[\s]*,?[\s]*)+)/ig,
3882 pathValues = /(-?\d*\.?\d*(?:e[-+]?\d+)?)[\s]*,?[\s]*/ig;
3883
3884 var isArray = Array.isArray || function(o) { return o instanceof Array; };
3885
3886 function hasProperty(obj, property) {
3887 return Object.prototype.hasOwnProperty.call(obj, property);
3888 }
3889
3890 function clone(obj) {
3891
3892 if (typeof obj == 'function' || Object(obj) !== obj) {
3893 return obj;
3894 }
3895
3896 var res = new obj.constructor;
3897
3898 for (var key in obj) {
3899 if (hasProperty(obj, key)) {
3900 res[key] = clone(obj[key]);
3901 }
3902 }
3903
3904 return res;
3905 }
3906
3907 function repush(array, item) {
3908 for (var i = 0, ii = array.length; i < ii; i++) if (array[i] === item) {
3909 return array.push(array.splice(i, 1)[0]);
3910 }
3911 }
3912
3913 function cacher(f) {
3914
3915 function newf() {
3916
3917 var arg = Array.prototype.slice.call(arguments, 0),
3918 args = arg.join('\u2400'),
3919 cache = newf.cache = newf.cache || {},
3920 count = newf.count = newf.count || [];
3921
3922 if (hasProperty(cache, args)) {
3923 repush(count, args);
3924 return cache[args];
3925 }
3926
3927 count.length >= 1e3 && delete cache[count.shift()];
3928 count.push(args);
3929 cache[args] = f.apply(0, arg);
3930
3931 return cache[args];
3932 }
3933 return newf;
3934 }
3935
3936 function parsePathString(pathString) {
3937
3938 if (!pathString) {
3939 return null;
3940 }
3941
3942 var pth = paths(pathString);
3943
3944 if (pth.arr) {
3945 return clone(pth.arr);
3946 }
3947
3948 var paramCounts = { a: 7, c: 6, h: 1, l: 2, m: 2, q: 4, s: 4, t: 2, v: 1, z: 0 },
3949 data = [];
3950
3951 if (isArray(pathString) && isArray(pathString[0])) { // rough assumption
3952 data = clone(pathString);
3953 }
3954
3955 if (!data.length) {
3956
3957 String(pathString).replace(pathCommand, function(a, b, c) {
3958 var params = [],
3959 name = b.toLowerCase();
3960
3961 c.replace(pathValues, function(a, b) {
3962 b && params.push(+b);
3963 });
3964
3965 if (name == 'm' && params.length > 2) {
3966 data.push([b].concat(params.splice(0, 2)));
3967 name = 'l';
3968 b = b == 'm' ? 'l' : 'L';
3969 }
3970
3971 while (params.length >= paramCounts[name]) {
3972 data.push([b].concat(params.splice(0, paramCounts[name])));
3973 if (!paramCounts[name]) {
3974 break;
3975 }
3976 }
3977 });
3978 }
3979
3980 data.toString = paths.toString;
3981 pth.arr = clone(data);
3982
3983 return data;
3984 }
3985
3986 function paths(ps) {
3987 var p = paths.ps = paths.ps || {};
3988
3989 if (p[ps]) {
3990 p[ps].sleep = 100;
3991 } else {
3992 p[ps] = {
3993 sleep: 100
3994 };
3995 }
3996
3997 setTimeout(function() {
3998 for (var key in p) {
3999 if (hasProperty(p, key) && key != ps) {
4000 p[key].sleep--;
4001 !p[key].sleep && delete p[key];
4002 }
4003 }
4004 });
4005
4006 return p[ps];
4007 }
4008
4009 function rectBBox(x, y, width, height) {
4010
4011 if (arguments.length === 1) {
4012 y = x.y;
4013 width = x.width;
4014 height = x.height;
4015 x = x.x;
4016 }
4017
4018 return {
4019 x: x,
4020 y: y,
4021 width: width,
4022 height: height,
4023 x2: x + width,
4024 y2: y + height
4025 };
4026 }
4027
4028 function pathToString() {
4029 return this.join(',').replace(p2s, '$1');
4030 }
4031
4032 function pathClone(pathArray) {
4033 var res = clone(pathArray);
4034 res.toString = pathToString;
4035 return res;
4036 }
4037
4038 function findDotsAtSegment(p1x, p1y, c1x, c1y, c2x, c2y, p2x, p2y, t) {
4039 var t1 = 1 - t,
4040 t13 = pow(t1, 3),
4041 t12 = pow(t1, 2),
4042 t2 = t * t,
4043 t3 = t2 * t,
4044 x = t13 * p1x + t12 * 3 * t * c1x + t1 * 3 * t * t * c2x + t3 * p2x,
4045 y = t13 * p1y + t12 * 3 * t * c1y + t1 * 3 * t * t * c2y + t3 * p2y;
4046
4047 return {
4048 x: fixError(x),
4049 y: fixError(y)
4050 };
4051 }
4052
4053 function bezierBBox(points) {
4054
4055 var bbox = curveBBox.apply(null, points);
4056
4057 return rectBBox(
4058 bbox.x0,
4059 bbox.y0,
4060 bbox.x1 - bbox.x0,
4061 bbox.y1 - bbox.y0
4062 );
4063 }
4064
4065 function isPointInsideBBox$2(bbox, x, y) {
4066 return x >= bbox.x &&
4067 x <= bbox.x + bbox.width &&
4068 y >= bbox.y &&
4069 y <= bbox.y + bbox.height;
4070 }
4071
4072 function isBBoxIntersect(bbox1, bbox2) {
4073 bbox1 = rectBBox(bbox1);
4074 bbox2 = rectBBox(bbox2);
4075 return isPointInsideBBox$2(bbox2, bbox1.x, bbox1.y)
4076 || isPointInsideBBox$2(bbox2, bbox1.x2, bbox1.y)
4077 || isPointInsideBBox$2(bbox2, bbox1.x, bbox1.y2)
4078 || isPointInsideBBox$2(bbox2, bbox1.x2, bbox1.y2)
4079 || isPointInsideBBox$2(bbox1, bbox2.x, bbox2.y)
4080 || isPointInsideBBox$2(bbox1, bbox2.x2, bbox2.y)
4081 || isPointInsideBBox$2(bbox1, bbox2.x, bbox2.y2)
4082 || isPointInsideBBox$2(bbox1, bbox2.x2, bbox2.y2)
4083 || (bbox1.x < bbox2.x2 && bbox1.x > bbox2.x
4084 || bbox2.x < bbox1.x2 && bbox2.x > bbox1.x)
4085 && (bbox1.y < bbox2.y2 && bbox1.y > bbox2.y
4086 || bbox2.y < bbox1.y2 && bbox2.y > bbox1.y);
4087 }
4088
4089 function base3(t, p1, p2, p3, p4) {
4090 var t1 = -3 * p1 + 9 * p2 - 9 * p3 + 3 * p4,
4091 t2 = t * t1 + 6 * p1 - 12 * p2 + 6 * p3;
4092 return t * t2 - 3 * p1 + 3 * p2;
4093 }
4094
4095 function bezlen(x1, y1, x2, y2, x3, y3, x4, y4, z) {
4096
4097 if (z == null) {
4098 z = 1;
4099 }
4100
4101 z = z > 1 ? 1 : z < 0 ? 0 : z;
4102
4103 var z2 = z / 2,
4104 n = 12,
4105 Tvalues = [-.1252,.1252,-.3678,.3678,-.5873,.5873,-.7699,.7699,-.9041,.9041,-.9816,.9816],
4106 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],
4107 sum = 0;
4108
4109 for (var i = 0; i < n; i++) {
4110 var ct = z2 * Tvalues[i] + z2,
4111 xbase = base3(ct, x1, x2, x3, x4),
4112 ybase = base3(ct, y1, y2, y3, y4),
4113 comb = xbase * xbase + ybase * ybase;
4114
4115 sum += Cvalues[i] * math.sqrt(comb);
4116 }
4117
4118 return z2 * sum;
4119 }
4120
4121
4122 function intersectLines(x1, y1, x2, y2, x3, y3, x4, y4) {
4123
4124 if (
4125 mmax(x1, x2) < mmin(x3, x4) ||
4126 mmin(x1, x2) > mmax(x3, x4) ||
4127 mmax(y1, y2) < mmin(y3, y4) ||
4128 mmin(y1, y2) > mmax(y3, y4)
4129 ) {
4130 return;
4131 }
4132
4133 var nx = (x1 * y2 - y1 * x2) * (x3 - x4) - (x1 - x2) * (x3 * y4 - y3 * x4),
4134 ny = (x1 * y2 - y1 * x2) * (y3 - y4) - (y1 - y2) * (x3 * y4 - y3 * x4),
4135 denominator = (x1 - x2) * (y3 - y4) - (y1 - y2) * (x3 - x4);
4136
4137 if (!denominator) {
4138 return;
4139 }
4140
4141 var px = fixError(nx / denominator),
4142 py = fixError(ny / denominator),
4143 px2 = +px.toFixed(2),
4144 py2 = +py.toFixed(2);
4145
4146 if (
4147 px2 < +mmin(x1, x2).toFixed(2) ||
4148 px2 > +mmax(x1, x2).toFixed(2) ||
4149 px2 < +mmin(x3, x4).toFixed(2) ||
4150 px2 > +mmax(x3, x4).toFixed(2) ||
4151 py2 < +mmin(y1, y2).toFixed(2) ||
4152 py2 > +mmax(y1, y2).toFixed(2) ||
4153 py2 < +mmin(y3, y4).toFixed(2) ||
4154 py2 > +mmax(y3, y4).toFixed(2)
4155 ) {
4156 return;
4157 }
4158
4159 return { x: px, y: py };
4160 }
4161
4162 function fixError(number) {
4163 return Math.round(number * 100000000000) / 100000000000;
4164 }
4165
4166 function findBezierIntersections(bez1, bez2, justCount) {
4167 var bbox1 = bezierBBox(bez1),
4168 bbox2 = bezierBBox(bez2);
4169
4170 if (!isBBoxIntersect(bbox1, bbox2)) {
4171 return justCount ? 0 : [];
4172 }
4173
4174 // As an optimization, lines will have only 1 segment
4175
4176 var l1 = bezlen.apply(0, bez1),
4177 l2 = bezlen.apply(0, bez2),
4178 n1 = isLine(bez1) ? 1 : ~~(l1 / 5) || 1,
4179 n2 = isLine(bez2) ? 1 : ~~(l2 / 5) || 1,
4180 dots1 = [],
4181 dots2 = [],
4182 xy = {},
4183 res = justCount ? 0 : [];
4184
4185 for (var i = 0; i < n1 + 1; i++) {
4186 var p = findDotsAtSegment.apply(0, bez1.concat(i / n1));
4187 dots1.push({ x: p.x, y: p.y, t: i / n1 });
4188 }
4189
4190 for (i = 0; i < n2 + 1; i++) {
4191 p = findDotsAtSegment.apply(0, bez2.concat(i / n2));
4192 dots2.push({ x: p.x, y: p.y, t: i / n2 });
4193 }
4194
4195 for (i = 0; i < n1; i++) {
4196
4197 for (var j = 0; j < n2; j++) {
4198 var di = dots1[i],
4199 di1 = dots1[i + 1],
4200 dj = dots2[j],
4201 dj1 = dots2[j + 1],
4202 ci = abs$7(di1.x - di.x) < .01 ? 'y' : 'x',
4203 cj = abs$7(dj1.x - dj.x) < .01 ? 'y' : 'x',
4204 is = intersectLines(di.x, di.y, di1.x, di1.y, dj.x, dj.y, dj1.x, dj1.y),
4205 key;
4206
4207 if (is) {
4208 key = is.x.toFixed(9) + '#' + is.y.toFixed(9);
4209
4210 if (xy[key]) {
4211 continue;
4212 }
4213
4214 xy[key] = true;
4215
4216 var t1 = di.t + abs$7((is[ci] - di[ci]) / (di1[ci] - di[ci])) * (di1.t - di.t),
4217 t2 = dj.t + abs$7((is[cj] - dj[cj]) / (dj1[cj] - dj[cj])) * (dj1.t - dj.t);
4218
4219 if (t1 >= 0 && t1 <= 1 && t2 >= 0 && t2 <= 1) {
4220
4221 if (justCount) {
4222 res++;
4223 } else {
4224 res.push({
4225 x: is.x,
4226 y: is.y,
4227 t1: t1,
4228 t2: t2
4229 });
4230 }
4231 }
4232 }
4233 }
4234 }
4235
4236 return res;
4237 }
4238
4239
4240 /**
4241 * Find or counts the intersections between two SVG paths.
4242 *
4243 * Returns a number in counting mode and a list of intersections otherwise.
4244 *
4245 * A single intersection entry contains the intersection coordinates (x, y)
4246 * as well as additional information regarding the intersecting segments
4247 * on each path (segment1, segment2) and the relative location of the
4248 * intersection on these segments (t1, t2).
4249 *
4250 * The path may be an SVG path string or a list of path components
4251 * such as `[ [ 'M', 0, 10 ], [ 'L', 20, 0 ] ]`.
4252 *
4253 * @example
4254 *
4255 * var intersections = findPathIntersections(
4256 * 'M0,0L100,100',
4257 * [ [ 'M', 0, 100 ], [ 'L', 100, 0 ] ]
4258 * );
4259 *
4260 * // intersections = [
4261 * // { x: 50, y: 50, segment1: 1, segment2: 1, t1: 0.5, t2: 0.5 }
4262 * // ]
4263 *
4264 * @param {String|Array<PathDef>} path1
4265 * @param {String|Array<PathDef>} path2
4266 * @param {Boolean} [justCount=false]
4267 *
4268 * @return {Array<Intersection>|Number}
4269 */
4270 function findPathIntersections(path1, path2, justCount) {
4271 path1 = pathToCurve(path1);
4272 path2 = pathToCurve(path2);
4273
4274 var x1, y1, x2, y2, x1m, y1m, x2m, y2m, bez1, bez2,
4275 res = justCount ? 0 : [];
4276
4277 for (var i = 0, ii = path1.length; i < ii; i++) {
4278 var pi = path1[i];
4279
4280 if (pi[0] == 'M') {
4281 x1 = x1m = pi[1];
4282 y1 = y1m = pi[2];
4283 } else {
4284
4285 if (pi[0] == 'C') {
4286 bez1 = [x1, y1].concat(pi.slice(1));
4287 x1 = bez1[6];
4288 y1 = bez1[7];
4289 } else {
4290 bez1 = [x1, y1, x1, y1, x1m, y1m, x1m, y1m];
4291 x1 = x1m;
4292 y1 = y1m;
4293 }
4294
4295 for (var j = 0, jj = path2.length; j < jj; j++) {
4296 var pj = path2[j];
4297
4298 if (pj[0] == 'M') {
4299 x2 = x2m = pj[1];
4300 y2 = y2m = pj[2];
4301 } else {
4302
4303 if (pj[0] == 'C') {
4304 bez2 = [x2, y2].concat(pj.slice(1));
4305 x2 = bez2[6];
4306 y2 = bez2[7];
4307 } else {
4308 bez2 = [x2, y2, x2, y2, x2m, y2m, x2m, y2m];
4309 x2 = x2m;
4310 y2 = y2m;
4311 }
4312
4313 var intr = findBezierIntersections(bez1, bez2, justCount);
4314
4315 if (justCount) {
4316 res += intr;
4317 } else {
4318
4319 for (var k = 0, kk = intr.length; k < kk; k++) {
4320 intr[k].segment1 = i;
4321 intr[k].segment2 = j;
4322 intr[k].bez1 = bez1;
4323 intr[k].bez2 = bez2;
4324 }
4325
4326 res = res.concat(intr);
4327 }
4328 }
4329 }
4330 }
4331 }
4332
4333 return res;
4334 }
4335
4336
4337 function pathToAbsolute(pathArray) {
4338 var pth = paths(pathArray);
4339
4340 if (pth.abs) {
4341 return pathClone(pth.abs);
4342 }
4343
4344 if (!isArray(pathArray) || !isArray(pathArray && pathArray[0])) { // rough assumption
4345 pathArray = parsePathString(pathArray);
4346 }
4347
4348 if (!pathArray || !pathArray.length) {
4349 return [['M', 0, 0]];
4350 }
4351
4352 var res = [],
4353 x = 0,
4354 y = 0,
4355 mx = 0,
4356 my = 0,
4357 start = 0,
4358 pa0;
4359
4360 if (pathArray[0][0] == 'M') {
4361 x = +pathArray[0][1];
4362 y = +pathArray[0][2];
4363 mx = x;
4364 my = y;
4365 start++;
4366 res[0] = ['M', x, y];
4367 }
4368
4369 for (var r, pa, i = start, ii = pathArray.length; i < ii; i++) {
4370 res.push(r = []);
4371 pa = pathArray[i];
4372 pa0 = pa[0];
4373
4374 if (pa0 != pa0.toUpperCase()) {
4375 r[0] = pa0.toUpperCase();
4376
4377 switch (r[0]) {
4378 case 'A':
4379 r[1] = pa[1];
4380 r[2] = pa[2];
4381 r[3] = pa[3];
4382 r[4] = pa[4];
4383 r[5] = pa[5];
4384 r[6] = +pa[6] + x;
4385 r[7] = +pa[7] + y;
4386 break;
4387 case 'V':
4388 r[1] = +pa[1] + y;
4389 break;
4390 case 'H':
4391 r[1] = +pa[1] + x;
4392 break;
4393 case 'M':
4394 mx = +pa[1] + x;
4395 my = +pa[2] + y;
4396 default:
4397 for (var j = 1, jj = pa.length; j < jj; j++) {
4398 r[j] = +pa[j] + ((j % 2) ? x : y);
4399 }
4400 }
4401 } else {
4402 for (var k = 0, kk = pa.length; k < kk; k++) {
4403 r[k] = pa[k];
4404 }
4405 }
4406 pa0 = pa0.toUpperCase();
4407
4408 switch (r[0]) {
4409 case 'Z':
4410 x = +mx;
4411 y = +my;
4412 break;
4413 case 'H':
4414 x = r[1];
4415 break;
4416 case 'V':
4417 y = r[1];
4418 break;
4419 case 'M':
4420 mx = r[r.length - 2];
4421 my = r[r.length - 1];
4422 default:
4423 x = r[r.length - 2];
4424 y = r[r.length - 1];
4425 }
4426 }
4427
4428 res.toString = pathToString;
4429 pth.abs = pathClone(res);
4430
4431 return res;
4432 }
4433
4434 function isLine(bez) {
4435 return (
4436 bez[0] === bez[2] &&
4437 bez[1] === bez[3] &&
4438 bez[4] === bez[6] &&
4439 bez[5] === bez[7]
4440 );
4441 }
4442
4443 function lineToCurve(x1, y1, x2, y2) {
4444 return [
4445 x1, y1, x2,
4446 y2, x2, y2
4447 ];
4448 }
4449
4450 function qubicToCurve(x1, y1, ax, ay, x2, y2) {
4451 var _13 = 1 / 3,
4452 _23 = 2 / 3;
4453
4454 return [
4455 _13 * x1 + _23 * ax,
4456 _13 * y1 + _23 * ay,
4457 _13 * x2 + _23 * ax,
4458 _13 * y2 + _23 * ay,
4459 x2,
4460 y2
4461 ];
4462 }
4463
4464 function arcToCurve(x1, y1, rx, ry, angle, large_arc_flag, sweep_flag, x2, y2, recursive) {
4465
4466 // for more information of where this math came from visit:
4467 // http://www.w3.org/TR/SVG11/implnote.html#ArcImplementationNotes
4468 var _120 = PI * 120 / 180,
4469 rad = PI / 180 * (+angle || 0),
4470 res = [],
4471 xy,
4472 rotate = cacher(function(x, y, rad) {
4473 var X = x * math.cos(rad) - y * math.sin(rad),
4474 Y = x * math.sin(rad) + y * math.cos(rad);
4475
4476 return { x: X, y: Y };
4477 });
4478
4479 if (!recursive) {
4480 xy = rotate(x1, y1, -rad);
4481 x1 = xy.x;
4482 y1 = xy.y;
4483 xy = rotate(x2, y2, -rad);
4484 x2 = xy.x;
4485 y2 = xy.y;
4486
4487 var x = (x1 - x2) / 2,
4488 y = (y1 - y2) / 2;
4489
4490 var h = (x * x) / (rx * rx) + (y * y) / (ry * ry);
4491
4492 if (h > 1) {
4493 h = math.sqrt(h);
4494 rx = h * rx;
4495 ry = h * ry;
4496 }
4497
4498 var rx2 = rx * rx,
4499 ry2 = ry * ry,
4500 k = (large_arc_flag == sweep_flag ? -1 : 1) *
4501 math.sqrt(abs$7((rx2 * ry2 - rx2 * y * y - ry2 * x * x) / (rx2 * y * y + ry2 * x * x))),
4502 cx = k * rx * y / ry + (x1 + x2) / 2,
4503 cy = k * -ry * x / rx + (y1 + y2) / 2,
4504 f1 = math.asin(((y1 - cy) / ry).toFixed(9)),
4505 f2 = math.asin(((y2 - cy) / ry).toFixed(9));
4506
4507 f1 = x1 < cx ? PI - f1 : f1;
4508 f2 = x2 < cx ? PI - f2 : f2;
4509 f1 < 0 && (f1 = PI * 2 + f1);
4510 f2 < 0 && (f2 = PI * 2 + f2);
4511
4512 if (sweep_flag && f1 > f2) {
4513 f1 = f1 - PI * 2;
4514 }
4515 if (!sweep_flag && f2 > f1) {
4516 f2 = f2 - PI * 2;
4517 }
4518 } else {
4519 f1 = recursive[0];
4520 f2 = recursive[1];
4521 cx = recursive[2];
4522 cy = recursive[3];
4523 }
4524
4525 var df = f2 - f1;
4526
4527 if (abs$7(df) > _120) {
4528 var f2old = f2,
4529 x2old = x2,
4530 y2old = y2;
4531
4532 f2 = f1 + _120 * (sweep_flag && f2 > f1 ? 1 : -1);
4533 x2 = cx + rx * math.cos(f2);
4534 y2 = cy + ry * math.sin(f2);
4535 res = arcToCurve(x2, y2, rx, ry, angle, 0, sweep_flag, x2old, y2old, [f2, f2old, cx, cy]);
4536 }
4537
4538 df = f2 - f1;
4539
4540 var c1 = math.cos(f1),
4541 s1 = math.sin(f1),
4542 c2 = math.cos(f2),
4543 s2 = math.sin(f2),
4544 t = math.tan(df / 4),
4545 hx = 4 / 3 * rx * t,
4546 hy = 4 / 3 * ry * t,
4547 m1 = [x1, y1],
4548 m2 = [x1 + hx * s1, y1 - hy * c1],
4549 m3 = [x2 + hx * s2, y2 - hy * c2],
4550 m4 = [x2, y2];
4551
4552 m2[0] = 2 * m1[0] - m2[0];
4553 m2[1] = 2 * m1[1] - m2[1];
4554
4555 if (recursive) {
4556 return [m2, m3, m4].concat(res);
4557 } else {
4558 res = [m2, m3, m4].concat(res).join().split(',');
4559 var newres = [];
4560
4561 for (var i = 0, ii = res.length; i < ii; i++) {
4562 newres[i] = i % 2 ? rotate(res[i - 1], res[i], rad).y : rotate(res[i], res[i + 1], rad).x;
4563 }
4564
4565 return newres;
4566 }
4567 }
4568
4569 // Returns bounding box of cubic bezier curve.
4570 // Source: http://blog.hackers-cafe.net/2009/06/how-to-calculate-bezier-curves-bounding.html
4571 // Original version: NISHIO Hirokazu
4572 // Modifications: https://github.com/timo22345
4573 function curveBBox(x0, y0, x1, y1, x2, y2, x3, y3) {
4574 var tvalues = [],
4575 bounds = [[], []],
4576 a, b, c, t, t1, t2, b2ac, sqrtb2ac;
4577
4578 for (var i = 0; i < 2; ++i) {
4579
4580 if (i == 0) {
4581 b = 6 * x0 - 12 * x1 + 6 * x2;
4582 a = -3 * x0 + 9 * x1 - 9 * x2 + 3 * x3;
4583 c = 3 * x1 - 3 * x0;
4584 } else {
4585 b = 6 * y0 - 12 * y1 + 6 * y2;
4586 a = -3 * y0 + 9 * y1 - 9 * y2 + 3 * y3;
4587 c = 3 * y1 - 3 * y0;
4588 }
4589
4590 if (abs$7(a) < 1e-12) {
4591
4592 if (abs$7(b) < 1e-12) {
4593 continue;
4594 }
4595
4596 t = -c / b;
4597
4598 if (0 < t && t < 1) {
4599 tvalues.push(t);
4600 }
4601
4602 continue;
4603 }
4604
4605 b2ac = b * b - 4 * c * a;
4606 sqrtb2ac = math.sqrt(b2ac);
4607
4608 if (b2ac < 0) {
4609 continue;
4610 }
4611
4612 t1 = (-b + sqrtb2ac) / (2 * a);
4613
4614 if (0 < t1 && t1 < 1) {
4615 tvalues.push(t1);
4616 }
4617
4618 t2 = (-b - sqrtb2ac) / (2 * a);
4619
4620 if (0 < t2 && t2 < 1) {
4621 tvalues.push(t2);
4622 }
4623 }
4624
4625 var j = tvalues.length,
4626 jlen = j,
4627 mt;
4628
4629 while (j--) {
4630 t = tvalues[j];
4631 mt = 1 - t;
4632 bounds[0][j] = (mt * mt * mt * x0) + (3 * mt * mt * t * x1) + (3 * mt * t * t * x2) + (t * t * t * x3);
4633 bounds[1][j] = (mt * mt * mt * y0) + (3 * mt * mt * t * y1) + (3 * mt * t * t * y2) + (t * t * t * y3);
4634 }
4635
4636 bounds[0][jlen] = x0;
4637 bounds[1][jlen] = y0;
4638 bounds[0][jlen + 1] = x3;
4639 bounds[1][jlen + 1] = y3;
4640 bounds[0].length = bounds[1].length = jlen + 2;
4641
4642 return {
4643 x0: mmin.apply(0, bounds[0]),
4644 y0: mmin.apply(0, bounds[1]),
4645 x1: mmax.apply(0, bounds[0]),
4646 y1: mmax.apply(0, bounds[1])
4647 };
4648 }
4649
4650 function pathToCurve(path) {
4651
4652 var pth = paths(path);
4653
4654 // return cached curve, if existing
4655 if (pth.curve) {
4656 return pathClone(pth.curve);
4657 }
4658
4659 var curvedPath = pathToAbsolute(path),
4660 attrs = { x: 0, y: 0, bx: 0, by: 0, X: 0, Y: 0, qx: null, qy: null },
4661 processPath = function(path, d, pathCommand) {
4662 var nx, ny;
4663
4664 if (!path) {
4665 return ['C', d.x, d.y, d.x, d.y, d.x, d.y];
4666 }
4667
4668 !(path[0] in { T: 1, Q: 1 }) && (d.qx = d.qy = null);
4669
4670 switch (path[0]) {
4671 case 'M':
4672 d.X = path[1];
4673 d.Y = path[2];
4674 break;
4675 case 'A':
4676 path = ['C'].concat(arcToCurve.apply(0, [d.x, d.y].concat(path.slice(1))));
4677 break;
4678 case 'S':
4679 if (pathCommand == 'C' || pathCommand == 'S') {
4680
4681 // In 'S' case we have to take into account, if the previous command is C/S.
4682 nx = d.x * 2 - d.bx;
4683
4684 // And reflect the previous
4685 ny = d.y * 2 - d.by;
4686
4687 // command's control point relative to the current point.
4688 }
4689 else {
4690
4691 // or some else or nothing
4692 nx = d.x;
4693 ny = d.y;
4694 }
4695 path = ['C', nx, ny].concat(path.slice(1));
4696 break;
4697 case 'T':
4698 if (pathCommand == 'Q' || pathCommand == 'T') {
4699
4700 // In 'T' case we have to take into account, if the previous command is Q/T.
4701 d.qx = d.x * 2 - d.qx;
4702
4703 // And make a reflection similar
4704 d.qy = d.y * 2 - d.qy;
4705
4706 // to case 'S'.
4707 }
4708 else {
4709
4710 // or something else or nothing
4711 d.qx = d.x;
4712 d.qy = d.y;
4713 }
4714 path = ['C'].concat(qubicToCurve(d.x, d.y, d.qx, d.qy, path[1], path[2]));
4715 break;
4716 case 'Q':
4717 d.qx = path[1];
4718 d.qy = path[2];
4719 path = ['C'].concat(qubicToCurve(d.x, d.y, path[1], path[2], path[3], path[4]));
4720 break;
4721 case 'L':
4722 path = ['C'].concat(lineToCurve(d.x, d.y, path[1], path[2]));
4723 break;
4724 case 'H':
4725 path = ['C'].concat(lineToCurve(d.x, d.y, path[1], d.y));
4726 break;
4727 case 'V':
4728 path = ['C'].concat(lineToCurve(d.x, d.y, d.x, path[1]));
4729 break;
4730 case 'Z':
4731 path = ['C'].concat(lineToCurve(d.x, d.y, d.X, d.Y));
4732 break;
4733 }
4734
4735 return path;
4736 },
4737
4738 fixArc = function(pp, i) {
4739
4740 if (pp[i].length > 7) {
4741 pp[i].shift();
4742 var pi = pp[i];
4743
4744 while (pi.length) {
4745 pathCommands[i] = 'A'; // if created multiple C:s, their original seg is saved
4746 pp.splice(i++, 0, ['C'].concat(pi.splice(0, 6)));
4747 }
4748
4749 pp.splice(i, 1);
4750 ii = curvedPath.length;
4751 }
4752 },
4753
4754 pathCommands = [], // path commands of original path p
4755 pfirst = '', // temporary holder for original path command
4756 pathCommand = ''; // holder for previous path command of original path
4757
4758 for (var i = 0, ii = curvedPath.length; i < ii; i++) {
4759 curvedPath[i] && (pfirst = curvedPath[i][0]); // save current path command
4760
4761 if (pfirst != 'C') // C is not saved yet, because it may be result of conversion
4762 {
4763 pathCommands[i] = pfirst; // Save current path command
4764 i && (pathCommand = pathCommands[i - 1]); // Get previous path command pathCommand
4765 }
4766 curvedPath[i] = processPath(curvedPath[i], attrs, pathCommand); // Previous path command is inputted to processPath
4767
4768 if (pathCommands[i] != 'A' && pfirst == 'C') pathCommands[i] = 'C'; // A is the only command
4769 // which may produce multiple C:s
4770 // so we have to make sure that C is also C in original path
4771
4772 fixArc(curvedPath, i); // fixArc adds also the right amount of A:s to pathCommands
4773
4774 var seg = curvedPath[i],
4775 seglen = seg.length;
4776
4777 attrs.x = seg[seglen - 2];
4778 attrs.y = seg[seglen - 1];
4779 attrs.bx = toFloat(seg[seglen - 4]) || attrs.x;
4780 attrs.by = toFloat(seg[seglen - 3]) || attrs.y;
4781 }
4782
4783 // cache curve
4784 pth.curve = pathClone(curvedPath);
4785
4786 return curvedPath;
4787 }
4788
4789 var intersect = findPathIntersections;
4790
4791 function roundBounds(bounds) {
4792 return {
4793 x: Math.round(bounds.x),
4794 y: Math.round(bounds.y),
4795 width: Math.round(bounds.width),
4796 height: Math.round(bounds.height)
4797 };
4798 }
4799
4800
4801 function roundPoint(point) {
4802
4803 return {
4804 x: Math.round(point.x),
4805 y: Math.round(point.y)
4806 };
4807 }
4808
4809
4810 /**
4811 * Convert the given bounds to a { top, left, bottom, right } descriptor.
4812 *
4813 * @param {Bounds|Point} bounds
4814 *
4815 * @return {Object}
4816 */
4817 function asTRBL(bounds) {
4818 return {
4819 top: bounds.y,
4820 right: bounds.x + (bounds.width || 0),
4821 bottom: bounds.y + (bounds.height || 0),
4822 left: bounds.x
4823 };
4824 }
4825
4826
4827 /**
4828 * Convert a { top, left, bottom, right } to an objects bounds.
4829 *
4830 * @param {Object} trbl
4831 *
4832 * @return {Bounds}
4833 */
4834 function asBounds(trbl) {
4835 return {
4836 x: trbl.left,
4837 y: trbl.top,
4838 width: trbl.right - trbl.left,
4839 height: trbl.bottom - trbl.top
4840 };
4841 }
4842
4843
4844 /**
4845 * Get the mid of the given bounds or point.
4846 *
4847 * @param {Bounds|Point} bounds
4848 *
4849 * @return {Point}
4850 */
4851 function getMid(bounds) {
4852 return roundPoint({
4853 x: bounds.x + (bounds.width || 0) / 2,
4854 y: bounds.y + (bounds.height || 0) / 2
4855 });
4856 }
4857
4858
4859 // orientation utils //////////////////////
4860
4861 /**
4862 * Get orientation of the given rectangle with respect to
4863 * the reference rectangle.
4864 *
4865 * A padding (positive or negative) may be passed to influence
4866 * horizontal / vertical orientation and intersection.
4867 *
4868 * @param {Bounds} rect
4869 * @param {Bounds} reference
4870 * @param {Point|number} padding
4871 *
4872 * @return {string} the orientation; one of top, top-left, left, ..., bottom, right or intersect.
4873 */
4874 function getOrientation(rect, reference, padding) {
4875
4876 padding = padding || 0;
4877
4878 // make sure we can use an object, too
4879 // for individual { x, y } padding
4880 if (!isObject(padding)) {
4881 padding = { x: padding, y: padding };
4882 }
4883
4884
4885 var rectOrientation = asTRBL(rect),
4886 referenceOrientation = asTRBL(reference);
4887
4888 var top = rectOrientation.bottom + padding.y <= referenceOrientation.top,
4889 right = rectOrientation.left - padding.x >= referenceOrientation.right,
4890 bottom = rectOrientation.top - padding.y >= referenceOrientation.bottom,
4891 left = rectOrientation.right + padding.x <= referenceOrientation.left;
4892
4893 var vertical = top ? 'top' : (bottom ? 'bottom' : null),
4894 horizontal = left ? 'left' : (right ? 'right' : null);
4895
4896 if (horizontal && vertical) {
4897 return vertical + '-' + horizontal;
4898 } else {
4899 return horizontal || vertical || 'intersect';
4900 }
4901 }
4902
4903
4904 // intersection utils //////////////////////
4905
4906 /**
4907 * Get intersection between an element and a line path.
4908 *
4909 * @param {PathDef} elementPath
4910 * @param {PathDef} linePath
4911 * @param {boolean} cropStart crop from start or end
4912 *
4913 * @return {Point}
4914 */
4915 function getElementLineIntersection(elementPath, linePath, cropStart) {
4916
4917 var intersections = getIntersections(elementPath, linePath);
4918
4919 // recognize intersections
4920 // only one -> choose
4921 // two close together -> choose first
4922 // two or more distinct -> pull out appropriate one
4923 // none -> ok (fallback to point itself)
4924 if (intersections.length === 1) {
4925 return roundPoint(intersections[0]);
4926 } else if (intersections.length === 2 && pointDistance(intersections[0], intersections[1]) < 1) {
4927 return roundPoint(intersections[0]);
4928 } else if (intersections.length > 1) {
4929
4930 // sort by intersections based on connection segment +
4931 // distance from start
4932 intersections = sortBy(intersections, function(i) {
4933 var distance = Math.floor(i.t2 * 100) || 1;
4934
4935 distance = 100 - distance;
4936
4937 distance = (distance < 10 ? '0' : '') + distance;
4938
4939 // create a sort string that makes sure we sort
4940 // line segment ASC + line segment position DESC (for cropStart)
4941 // line segment ASC + line segment position ASC (for cropEnd)
4942 return i.segment2 + '#' + distance;
4943 });
4944
4945 return roundPoint(intersections[cropStart ? 0 : intersections.length - 1]);
4946 }
4947
4948 return null;
4949 }
4950
4951
4952 function getIntersections(a, b) {
4953 return intersect(a, b);
4954 }
4955
4956
4957 function filterRedundantWaypoints(waypoints) {
4958
4959 // alter copy of waypoints, not original
4960 waypoints = waypoints.slice();
4961
4962 var idx = 0,
4963 point,
4964 previousPoint,
4965 nextPoint;
4966
4967 while (waypoints[idx]) {
4968 point = waypoints[idx];
4969 previousPoint = waypoints[idx - 1];
4970 nextPoint = waypoints[idx + 1];
4971
4972 if (pointDistance(point, nextPoint) === 0 ||
4973 pointsOnLine(previousPoint, nextPoint, point)) {
4974
4975 // remove point, if overlapping with {nextPoint}
4976 // or on line with {previousPoint} -> {point} -> {nextPoint}
4977 waypoints.splice(idx, 1);
4978 } else {
4979 idx++;
4980 }
4981 }
4982
4983 return waypoints;
4984 }
4985
4986 function round$b(number, resolution) {
4987 return Math.round(number * resolution) / resolution;
4988 }
4989
4990 function ensurePx(number) {
4991 return isNumber(number) ? number + 'px' : number;
4992 }
4993
4994 function findRoot(element) {
4995 while (element.parent) {
4996 element = element.parent;
4997 }
4998
4999 return element;
5000 }
5001
5002 /**
5003 * Creates a HTML container element for a SVG element with
5004 * the given configuration
5005 *
5006 * @param {Object} options
5007 * @return {HTMLElement} the container element
5008 */
5009 function createContainer(options) {
5010
5011 options = assign({}, { width: '100%', height: '100%' }, options);
5012
5013 var container = options.container || document.body;
5014
5015 // create a <div> around the svg element with the respective size
5016 // this way we can always get the correct container size
5017 // (this is impossible for <svg> elements at the moment)
5018 var parent = document.createElement('div');
5019 parent.setAttribute('class', 'djs-container');
5020
5021 assign$1(parent, {
5022 position: 'relative',
5023 overflow: 'hidden',
5024 width: ensurePx(options.width),
5025 height: ensurePx(options.height)
5026 });
5027
5028 container.appendChild(parent);
5029
5030 return parent;
5031 }
5032
5033 function createGroup(parent, cls, childIndex) {
5034 var group = create$1('g');
5035 classes$1(group).add(cls);
5036
5037 var index = childIndex !== undefined ? childIndex : parent.childNodes.length - 1;
5038
5039 // must ensure second argument is node or _null_
5040 // cf. https://developer.mozilla.org/en-US/docs/Web/API/Node/insertBefore
5041 parent.insertBefore(group, parent.childNodes[index] || null);
5042
5043 return group;
5044 }
5045
5046 var BASE_LAYER = 'base';
5047
5048 // render plane contents behind utility layers
5049 var PLANE_LAYER_INDEX = 0;
5050 var UTILITY_LAYER_INDEX = 1;
5051
5052
5053 var REQUIRED_MODEL_ATTRS = {
5054 shape: [ 'x', 'y', 'width', 'height' ],
5055 connection: [ 'waypoints' ]
5056 };
5057
5058 /**
5059 * The main drawing canvas.
5060 *
5061 * @class
5062 * @constructor
5063 *
5064 * @emits Canvas#canvas.init
5065 *
5066 * @param {Object} config
5067 * @param {EventBus} eventBus
5068 * @param {GraphicsFactory} graphicsFactory
5069 * @param {ElementRegistry} elementRegistry
5070 */
5071 function Canvas(config, eventBus, graphicsFactory, elementRegistry) {
5072
5073 this._eventBus = eventBus;
5074 this._elementRegistry = elementRegistry;
5075 this._graphicsFactory = graphicsFactory;
5076
5077 this._rootsIdx = 0;
5078
5079 this._layers = {};
5080 this._planes = [];
5081 this._rootElement = null;
5082
5083 this._init(config || {});
5084 }
5085
5086 Canvas.$inject = [
5087 'config.canvas',
5088 'eventBus',
5089 'graphicsFactory',
5090 'elementRegistry'
5091 ];
5092
5093 /**
5094 * Creates a <svg> element that is wrapped into a <div>.
5095 * This way we are always able to correctly figure out the size of the svg element
5096 * by querying the parent node.
5097
5098 * (It is not possible to get the size of a svg element cross browser @ 2014-04-01)
5099
5100 * <div class="djs-container" style="width: {desired-width}, height: {desired-height}">
5101 * <svg width="100%" height="100%">
5102 * ...
5103 * </svg>
5104 * </div>
5105 */
5106 Canvas.prototype._init = function(config) {
5107
5108 var eventBus = this._eventBus;
5109
5110 // html container
5111 var container = this._container = createContainer(config);
5112
5113 var svg = this._svg = create$1('svg');
5114 attr$1(svg, { width: '100%', height: '100%' });
5115
5116 append(container, svg);
5117
5118 var viewport = this._viewport = createGroup(svg, 'viewport');
5119
5120 // debounce canvas.viewbox.changed events
5121 // for smoother diagram interaction
5122 if (config.deferUpdate !== false) {
5123 this._viewboxChanged = debounce(bind(this._viewboxChanged, this), 300);
5124 }
5125
5126 eventBus.on('diagram.init', function() {
5127
5128 /**
5129 * An event indicating that the canvas is ready to be drawn on.
5130 *
5131 * @memberOf Canvas
5132 *
5133 * @event canvas.init
5134 *
5135 * @type {Object}
5136 * @property {SVGElement} svg the created svg element
5137 * @property {SVGElement} viewport the direct parent of diagram elements and shapes
5138 */
5139 eventBus.fire('canvas.init', {
5140 svg: svg,
5141 viewport: viewport
5142 });
5143
5144 }, this);
5145
5146 // reset viewbox on shape changes to
5147 // recompute the viewbox
5148 eventBus.on([
5149 'shape.added',
5150 'connection.added',
5151 'shape.removed',
5152 'connection.removed',
5153 'elements.changed',
5154 'root.set'
5155 ], function() {
5156 delete this._cachedViewbox;
5157 }, this);
5158
5159 eventBus.on('diagram.destroy', 500, this._destroy, this);
5160 eventBus.on('diagram.clear', 500, this._clear, this);
5161 };
5162
5163 Canvas.prototype._destroy = function(emit) {
5164 this._eventBus.fire('canvas.destroy', {
5165 svg: this._svg,
5166 viewport: this._viewport
5167 });
5168
5169 var parent = this._container.parentNode;
5170
5171 if (parent) {
5172 parent.removeChild(this._container);
5173 }
5174
5175 delete this._svg;
5176 delete this._container;
5177 delete this._layers;
5178 delete this._planes;
5179 delete this._rootElement;
5180 delete this._viewport;
5181 };
5182
5183 Canvas.prototype._clear = function() {
5184
5185 var self = this;
5186
5187 var allElements = this._elementRegistry.getAll();
5188
5189 // remove all elements
5190 allElements.forEach(function(element) {
5191 var type = getType(element);
5192
5193 if (type === 'root') {
5194 self.removeRootElement(element);
5195 } else {
5196 self._removeElement(element, type);
5197 }
5198 });
5199
5200 // remove all planes
5201 this._planes = [];
5202 this._rootElement = null;
5203
5204 // force recomputation of view box
5205 delete this._cachedViewbox;
5206 };
5207
5208 /**
5209 * Returns the default layer on which
5210 * all elements are drawn.
5211 *
5212 * @returns {SVGElement}
5213 */
5214 Canvas.prototype.getDefaultLayer = function() {
5215 return this.getLayer(BASE_LAYER, PLANE_LAYER_INDEX);
5216 };
5217
5218 /**
5219 * Returns a layer that is used to draw elements
5220 * or annotations on it.
5221 *
5222 * Non-existing layers retrieved through this method
5223 * will be created. During creation, the optional index
5224 * may be used to create layers below or above existing layers.
5225 * A layer with a certain index is always created above all
5226 * existing layers with the same index.
5227 *
5228 * @param {string} name
5229 * @param {number} index
5230 *
5231 * @returns {SVGElement}
5232 */
5233 Canvas.prototype.getLayer = function(name, index) {
5234
5235 if (!name) {
5236 throw new Error('must specify a name');
5237 }
5238
5239 var layer = this._layers[name];
5240
5241 if (!layer) {
5242 layer = this._layers[name] = this._createLayer(name, index);
5243 }
5244
5245 // throw an error if layer creation / retrival is
5246 // requested on different index
5247 if (typeof index !== 'undefined' && layer.index !== index) {
5248 throw new Error('layer <' + name + '> already created at index <' + index + '>');
5249 }
5250
5251 return layer.group;
5252 };
5253
5254 /**
5255 * For a given index, return the number of layers that have a higher index and
5256 * are visible.
5257 *
5258 * This is used to determine the node a layer should be inserted at.
5259 *
5260 * @param {Number} index
5261 * @returns {Number}
5262 */
5263 Canvas.prototype._getChildIndex = function(index) {
5264 return reduce(this._layers, function(childIndex, layer) {
5265 if (layer.visible && index >= layer.index) {
5266 childIndex++;
5267 }
5268
5269 return childIndex;
5270 }, 0);
5271 };
5272
5273 /**
5274 * Creates a given layer and returns it.
5275 *
5276 * @param {string} name
5277 * @param {number} [index=0]
5278 *
5279 * @return {Object} layer descriptor with { index, group: SVGGroup }
5280 */
5281 Canvas.prototype._createLayer = function(name, index) {
5282
5283 if (typeof index === 'undefined') {
5284 index = UTILITY_LAYER_INDEX;
5285 }
5286
5287 var childIndex = this._getChildIndex(index);
5288
5289 return {
5290 group: createGroup(this._viewport, 'layer-' + name, childIndex),
5291 index: index,
5292 visible: true
5293 };
5294 };
5295
5296
5297 /**
5298 * Shows a given layer.
5299 *
5300 * @param {String} layer
5301 * @returns {SVGElement}
5302 */
5303 Canvas.prototype.showLayer = function(name) {
5304
5305 if (!name) {
5306 throw new Error('must specify a name');
5307 }
5308
5309 var layer = this._layers[name];
5310
5311 if (!layer) {
5312 throw new Error('layer <' + name + '> does not exist');
5313 }
5314
5315 var viewport = this._viewport;
5316 var group = layer.group;
5317 var index = layer.index;
5318
5319 if (layer.visible) {
5320 return group;
5321 }
5322
5323 var childIndex = this._getChildIndex(index);
5324
5325 viewport.insertBefore(group, viewport.childNodes[childIndex] || null);
5326
5327 layer.visible = true;
5328
5329 return group;
5330 };
5331
5332 /**
5333 * Hides a given layer.
5334 *
5335 * @param {String} layer
5336 * @returns {SVGElement}
5337 */
5338 Canvas.prototype.hideLayer = function(name) {
5339
5340 if (!name) {
5341 throw new Error('must specify a name');
5342 }
5343
5344 var layer = this._layers[name];
5345
5346 if (!layer) {
5347 throw new Error('layer <' + name + '> does not exist');
5348 }
5349
5350 var group = layer.group;
5351
5352 if (!layer.visible) {
5353 return group;
5354 }
5355
5356 remove$2(group);
5357
5358 layer.visible = false;
5359
5360 return group;
5361 };
5362
5363
5364 Canvas.prototype._removeLayer = function(name) {
5365
5366 var layer = this._layers[name];
5367
5368 if (layer) {
5369 delete this._layers[name];
5370
5371 remove$2(layer.group);
5372 }
5373 };
5374
5375 /**
5376 * Returns the currently active layer. Can be null.
5377 *
5378 * @returns {SVGElement|null}
5379 */
5380 Canvas.prototype.getActiveLayer = function() {
5381 var plane = this._findPlaneForRoot(this.getRootElement());
5382
5383 if (!plane) {
5384 return null;
5385 }
5386
5387 return plane.layer;
5388 };
5389
5390
5391 /**
5392 * Returns the plane which contains the given element.
5393 *
5394 * @param {string|djs.model.Base} element
5395 *
5396 * @return {djs.model.Base} root for element
5397 */
5398 Canvas.prototype.findRoot = function(element) {
5399 if (typeof element === 'string') {
5400 element = this._elementRegistry.get(element);
5401 }
5402
5403 if (!element) {
5404 return;
5405 }
5406
5407 var plane = this._findPlaneForRoot(
5408 findRoot(element)
5409 ) || {};
5410
5411 return plane.rootElement;
5412 };
5413
5414 /**
5415 * Return a list of all root elements on the diagram.
5416 *
5417 * @return {djs.model.Root[]}
5418 */
5419 Canvas.prototype.getRootElements = function() {
5420 return this._planes.map(function(plane) {
5421 return plane.rootElement;
5422 });
5423 };
5424
5425 Canvas.prototype._findPlaneForRoot = function(rootElement) {
5426 return find(this._planes, function(plane) {
5427 return plane.rootElement === rootElement;
5428 });
5429 };
5430
5431
5432 /**
5433 * Returns the html element that encloses the
5434 * drawing canvas.
5435 *
5436 * @return {DOMNode}
5437 */
5438 Canvas.prototype.getContainer = function() {
5439 return this._container;
5440 };
5441
5442
5443 // markers //////////////////////
5444
5445 Canvas.prototype._updateMarker = function(element, marker, add) {
5446 var container;
5447
5448 if (!element.id) {
5449 element = this._elementRegistry.get(element);
5450 }
5451
5452 // we need to access all
5453 container = this._elementRegistry._elements[element.id];
5454
5455 if (!container) {
5456 return;
5457 }
5458
5459 forEach$2([ container.gfx, container.secondaryGfx ], function(gfx) {
5460 if (gfx) {
5461
5462 // invoke either addClass or removeClass based on mode
5463 if (add) {
5464 classes$1(gfx).add(marker);
5465 } else {
5466 classes$1(gfx).remove(marker);
5467 }
5468 }
5469 });
5470
5471 /**
5472 * An event indicating that a marker has been updated for an element
5473 *
5474 * @event element.marker.update
5475 * @type {Object}
5476 * @property {djs.model.Element} element the shape
5477 * @property {Object} gfx the graphical representation of the shape
5478 * @property {string} marker
5479 * @property {boolean} add true if the marker was added, false if it got removed
5480 */
5481 this._eventBus.fire('element.marker.update', { element: element, gfx: container.gfx, marker: marker, add: !!add });
5482 };
5483
5484
5485 /**
5486 * Adds a marker to an element (basically a css class).
5487 *
5488 * Fires the element.marker.update event, making it possible to
5489 * integrate extension into the marker life-cycle, too.
5490 *
5491 * @example
5492 * canvas.addMarker('foo', 'some-marker');
5493 *
5494 * var fooGfx = canvas.getGraphics('foo');
5495 *
5496 * fooGfx; // <g class="... some-marker"> ... </g>
5497 *
5498 * @param {string|djs.model.Base} element
5499 * @param {string} marker
5500 */
5501 Canvas.prototype.addMarker = function(element, marker) {
5502 this._updateMarker(element, marker, true);
5503 };
5504
5505
5506 /**
5507 * Remove a marker from an element.
5508 *
5509 * Fires the element.marker.update event, making it possible to
5510 * integrate extension into the marker life-cycle, too.
5511 *
5512 * @param {string|djs.model.Base} element
5513 * @param {string} marker
5514 */
5515 Canvas.prototype.removeMarker = function(element, marker) {
5516 this._updateMarker(element, marker, false);
5517 };
5518
5519 /**
5520 * Check the existence of a marker on element.
5521 *
5522 * @param {string|djs.model.Base} element
5523 * @param {string} marker
5524 */
5525 Canvas.prototype.hasMarker = function(element, marker) {
5526 if (!element.id) {
5527 element = this._elementRegistry.get(element);
5528 }
5529
5530 var gfx = this.getGraphics(element);
5531
5532 return classes$1(gfx).has(marker);
5533 };
5534
5535 /**
5536 * Toggles a marker on an element.
5537 *
5538 * Fires the element.marker.update event, making it possible to
5539 * integrate extension into the marker life-cycle, too.
5540 *
5541 * @param {string|djs.model.Base} element
5542 * @param {string} marker
5543 */
5544 Canvas.prototype.toggleMarker = function(element, marker) {
5545 if (this.hasMarker(element, marker)) {
5546 this.removeMarker(element, marker);
5547 } else {
5548 this.addMarker(element, marker);
5549 }
5550 };
5551
5552 /**
5553 * Returns the current root element.
5554 *
5555 * Supports two different modes for handling root elements:
5556 *
5557 * 1. if no root element has been added before, an implicit root will be added
5558 * and returned. This is used in applications that don't require explicit
5559 * root elements.
5560 *
5561 * 2. when root elements have been added before calling `getRootElement`,
5562 * root elements can be null. This is used for applications that want to manage
5563 * root elements themselves.
5564 *
5565 * @returns {Object|djs.model.Root|null} rootElement.
5566 */
5567 Canvas.prototype.getRootElement = function() {
5568 var rootElement = this._rootElement;
5569
5570 // can return null if root elements are present but none was set yet
5571 if (rootElement || this._planes.length) {
5572 return rootElement;
5573 }
5574
5575 return this.setRootElement(this.addRootElement(null));
5576 };
5577
5578 /**
5579 * Adds a given root element and returns it.
5580 *
5581 * @param {Object|djs.model.Root} rootElement
5582 *
5583 * @return {Object|djs.model.Root} rootElement
5584 */
5585
5586 Canvas.prototype.addRootElement = function(rootElement) {
5587 var idx = this._rootsIdx++;
5588
5589 if (!rootElement) {
5590 rootElement = {
5591 id: '__implicitroot_' + idx,
5592 children: [],
5593 isImplicit: true
5594 };
5595 }
5596
5597 var layerName = rootElement.layer = 'root-' + idx;
5598
5599 this._ensureValid('root', rootElement);
5600
5601 var layer = this.getLayer(layerName, PLANE_LAYER_INDEX);
5602
5603 this.hideLayer(layerName);
5604
5605 this._addRoot(rootElement, layer);
5606
5607 this._planes.push({
5608 rootElement: rootElement,
5609 layer: layer
5610 });
5611
5612 return rootElement;
5613 };
5614
5615 /**
5616 * Removes a given rootElement and returns it.
5617 *
5618 * @param {djs.model.Root|String} rootElement
5619 *
5620 * @return {Object|djs.model.Root} rootElement
5621 */
5622 Canvas.prototype.removeRootElement = function(rootElement) {
5623
5624 if (typeof rootElement === 'string') {
5625 rootElement = this._elementRegistry.get(rootElement);
5626 }
5627
5628 var plane = this._findPlaneForRoot(rootElement);
5629
5630 if (!plane) {
5631 return;
5632 }
5633
5634 // hook up life-cycle events
5635 this._removeRoot(rootElement);
5636
5637 // clean up layer
5638 this._removeLayer(rootElement.layer);
5639
5640 // clean up plane
5641 this._planes = this._planes.filter(function(plane) {
5642 return plane.rootElement !== rootElement;
5643 });
5644
5645 // clean up active root
5646 if (this._rootElement === rootElement) {
5647 this._rootElement = null;
5648 }
5649
5650 return rootElement;
5651 };
5652
5653
5654 // root element handling //////////////////////
5655
5656 /**
5657 * Sets a given element as the new root element for the canvas
5658 * and returns the new root element.
5659 *
5660 * @param {Object|djs.model.Root} rootElement
5661 *
5662 * @return {Object|djs.model.Root} new root element
5663 */
5664 Canvas.prototype.setRootElement = function(rootElement, override) {
5665
5666 if (isDefined(override)) {
5667 throw new Error('override not supported');
5668 }
5669
5670 if (rootElement === this._rootElement) {
5671 return;
5672 }
5673
5674 var plane;
5675
5676 if (!rootElement) {
5677 throw new Error('rootElement required');
5678 }
5679
5680 plane = this._findPlaneForRoot(rootElement);
5681
5682 // give set add semantics for backwards compatibility
5683 if (!plane) {
5684 rootElement = this.addRootElement(rootElement);
5685 }
5686
5687 this._setRoot(rootElement);
5688
5689 return rootElement;
5690 };
5691
5692
5693 Canvas.prototype._removeRoot = function(element) {
5694 var elementRegistry = this._elementRegistry,
5695 eventBus = this._eventBus;
5696
5697 // simulate element remove event sequence
5698 eventBus.fire('root.remove', { element: element });
5699 eventBus.fire('root.removed', { element: element });
5700
5701 elementRegistry.remove(element);
5702 };
5703
5704
5705 Canvas.prototype._addRoot = function(element, gfx) {
5706 var elementRegistry = this._elementRegistry,
5707 eventBus = this._eventBus;
5708
5709 // resemble element add event sequence
5710 eventBus.fire('root.add', { element: element });
5711
5712 elementRegistry.add(element, gfx);
5713
5714 eventBus.fire('root.added', { element: element, gfx: gfx });
5715 };
5716
5717
5718 Canvas.prototype._setRoot = function(rootElement, layer) {
5719
5720 var currentRoot = this._rootElement;
5721
5722 if (currentRoot) {
5723
5724 // un-associate previous root element <svg>
5725 this._elementRegistry.updateGraphics(currentRoot, null, true);
5726
5727 // hide previous layer
5728 this.hideLayer(currentRoot.layer);
5729 }
5730
5731 if (rootElement) {
5732
5733 if (!layer) {
5734 layer = this._findPlaneForRoot(rootElement).layer;
5735 }
5736
5737 // associate element with <svg>
5738 this._elementRegistry.updateGraphics(rootElement, this._svg, true);
5739
5740 // show root layer
5741 this.showLayer(rootElement.layer);
5742 }
5743
5744 this._rootElement = rootElement;
5745
5746 this._eventBus.fire('root.set', { element: rootElement });
5747 };
5748
5749 // add functionality //////////////////////
5750
5751 Canvas.prototype._ensureValid = function(type, element) {
5752 if (!element.id) {
5753 throw new Error('element must have an id');
5754 }
5755
5756 if (this._elementRegistry.get(element.id)) {
5757 throw new Error('element <' + element.id + '> already exists');
5758 }
5759
5760 var requiredAttrs = REQUIRED_MODEL_ATTRS[type];
5761
5762 var valid = every(requiredAttrs, function(attr) {
5763 return typeof element[attr] !== 'undefined';
5764 });
5765
5766 if (!valid) {
5767 throw new Error(
5768 'must supply { ' + requiredAttrs.join(', ') + ' } with ' + type);
5769 }
5770 };
5771
5772 Canvas.prototype._setParent = function(element, parent, parentIndex) {
5773 add(parent.children, element, parentIndex);
5774 element.parent = parent;
5775 };
5776
5777 /**
5778 * Adds an element to the canvas.
5779 *
5780 * This wires the parent <-> child relationship between the element and
5781 * a explicitly specified parent or an implicit root element.
5782 *
5783 * During add it emits the events
5784 *
5785 * * <{type}.add> (element, parent)
5786 * * <{type}.added> (element, gfx)
5787 *
5788 * Extensions may hook into these events to perform their magic.
5789 *
5790 * @param {string} type
5791 * @param {Object|djs.model.Base} element
5792 * @param {Object|djs.model.Base} [parent]
5793 * @param {number} [parentIndex]
5794 *
5795 * @return {Object|djs.model.Base} the added element
5796 */
5797 Canvas.prototype._addElement = function(type, element, parent, parentIndex) {
5798
5799 parent = parent || this.getRootElement();
5800
5801 var eventBus = this._eventBus,
5802 graphicsFactory = this._graphicsFactory;
5803
5804 this._ensureValid(type, element);
5805
5806 eventBus.fire(type + '.add', { element: element, parent: parent });
5807
5808 this._setParent(element, parent, parentIndex);
5809
5810 // create graphics
5811 var gfx = graphicsFactory.create(type, element, parentIndex);
5812
5813 this._elementRegistry.add(element, gfx);
5814
5815 // update its visual
5816 graphicsFactory.update(type, element, gfx);
5817
5818 eventBus.fire(type + '.added', { element: element, gfx: gfx });
5819
5820 return element;
5821 };
5822
5823 /**
5824 * Adds a shape to the canvas
5825 *
5826 * @param {Object|djs.model.Shape} shape to add to the diagram
5827 * @param {djs.model.Base} [parent]
5828 * @param {number} [parentIndex]
5829 *
5830 * @return {djs.model.Shape} the added shape
5831 */
5832 Canvas.prototype.addShape = function(shape, parent, parentIndex) {
5833 return this._addElement('shape', shape, parent, parentIndex);
5834 };
5835
5836 /**
5837 * Adds a connection to the canvas
5838 *
5839 * @param {Object|djs.model.Connection} connection to add to the diagram
5840 * @param {djs.model.Base} [parent]
5841 * @param {number} [parentIndex]
5842 *
5843 * @return {djs.model.Connection} the added connection
5844 */
5845 Canvas.prototype.addConnection = function(connection, parent, parentIndex) {
5846 return this._addElement('connection', connection, parent, parentIndex);
5847 };
5848
5849
5850 /**
5851 * Internal remove element
5852 */
5853 Canvas.prototype._removeElement = function(element, type) {
5854
5855 var elementRegistry = this._elementRegistry,
5856 graphicsFactory = this._graphicsFactory,
5857 eventBus = this._eventBus;
5858
5859 element = elementRegistry.get(element.id || element);
5860
5861 if (!element) {
5862
5863 // element was removed already
5864 return;
5865 }
5866
5867 eventBus.fire(type + '.remove', { element: element });
5868
5869 graphicsFactory.remove(element);
5870
5871 // unset parent <-> child relationship
5872 remove(element.parent && element.parent.children, element);
5873 element.parent = null;
5874
5875 eventBus.fire(type + '.removed', { element: element });
5876
5877 elementRegistry.remove(element);
5878
5879 return element;
5880 };
5881
5882
5883 /**
5884 * Removes a shape from the canvas
5885 *
5886 * @param {string|djs.model.Shape} shape or shape id to be removed
5887 *
5888 * @return {djs.model.Shape} the removed shape
5889 */
5890 Canvas.prototype.removeShape = function(shape) {
5891
5892 /**
5893 * An event indicating that a shape is about to be removed from the canvas.
5894 *
5895 * @memberOf Canvas
5896 *
5897 * @event shape.remove
5898 * @type {Object}
5899 * @property {djs.model.Shape} element the shape descriptor
5900 * @property {Object} gfx the graphical representation of the shape
5901 */
5902
5903 /**
5904 * An event indicating that a shape has been removed from the canvas.
5905 *
5906 * @memberOf Canvas
5907 *
5908 * @event shape.removed
5909 * @type {Object}
5910 * @property {djs.model.Shape} element the shape descriptor
5911 * @property {Object} gfx the graphical representation of the shape
5912 */
5913 return this._removeElement(shape, 'shape');
5914 };
5915
5916
5917 /**
5918 * Removes a connection from the canvas
5919 *
5920 * @param {string|djs.model.Connection} connection or connection id to be removed
5921 *
5922 * @return {djs.model.Connection} the removed connection
5923 */
5924 Canvas.prototype.removeConnection = function(connection) {
5925
5926 /**
5927 * An event indicating that a connection is about to be removed from the canvas.
5928 *
5929 * @memberOf Canvas
5930 *
5931 * @event connection.remove
5932 * @type {Object}
5933 * @property {djs.model.Connection} element the connection descriptor
5934 * @property {Object} gfx the graphical representation of the connection
5935 */
5936
5937 /**
5938 * An event indicating that a connection has been removed from the canvas.
5939 *
5940 * @memberOf Canvas
5941 *
5942 * @event connection.removed
5943 * @type {Object}
5944 * @property {djs.model.Connection} element the connection descriptor
5945 * @property {Object} gfx the graphical representation of the connection
5946 */
5947 return this._removeElement(connection, 'connection');
5948 };
5949
5950
5951 /**
5952 * Return the graphical object underlaying a certain diagram element
5953 *
5954 * @param {string|djs.model.Base} element descriptor of the element
5955 * @param {boolean} [secondary=false] whether to return the secondary connected element
5956 *
5957 * @return {SVGElement}
5958 */
5959 Canvas.prototype.getGraphics = function(element, secondary) {
5960 return this._elementRegistry.getGraphics(element, secondary);
5961 };
5962
5963
5964 /**
5965 * Perform a viewbox update via a given change function.
5966 *
5967 * @param {Function} changeFn
5968 */
5969 Canvas.prototype._changeViewbox = function(changeFn) {
5970
5971 // notify others of the upcoming viewbox change
5972 this._eventBus.fire('canvas.viewbox.changing');
5973
5974 // perform actual change
5975 changeFn.apply(this);
5976
5977 // reset the cached viewbox so that
5978 // a new get operation on viewbox or zoom
5979 // triggers a viewbox re-computation
5980 this._cachedViewbox = null;
5981
5982 // notify others of the change; this step
5983 // may or may not be debounced
5984 this._viewboxChanged();
5985 };
5986
5987 Canvas.prototype._viewboxChanged = function() {
5988 this._eventBus.fire('canvas.viewbox.changed', { viewbox: this.viewbox() });
5989 };
5990
5991
5992 /**
5993 * Gets or sets the view box of the canvas, i.e. the
5994 * area that is currently displayed.
5995 *
5996 * The getter may return a cached viewbox (if it is currently
5997 * changing). To force a recomputation, pass `false` as the first argument.
5998 *
5999 * @example
6000 *
6001 * canvas.viewbox({ x: 100, y: 100, width: 500, height: 500 })
6002 *
6003 * // sets the visible area of the diagram to (100|100) -> (600|100)
6004 * // and and scales it according to the diagram width
6005 *
6006 * var viewbox = canvas.viewbox(); // pass `false` to force recomputing the box.
6007 *
6008 * console.log(viewbox);
6009 * // {
6010 * // inner: Dimensions,
6011 * // outer: Dimensions,
6012 * // scale,
6013 * // x, y,
6014 * // width, height
6015 * // }
6016 *
6017 * // if the current diagram is zoomed and scrolled, you may reset it to the
6018 * // default zoom via this method, too:
6019 *
6020 * var zoomedAndScrolledViewbox = canvas.viewbox();
6021 *
6022 * canvas.viewbox({
6023 * x: 0,
6024 * y: 0,
6025 * width: zoomedAndScrolledViewbox.outer.width,
6026 * height: zoomedAndScrolledViewbox.outer.height
6027 * });
6028 *
6029 * @param {Object} [box] the new view box to set
6030 * @param {number} box.x the top left X coordinate of the canvas visible in view box
6031 * @param {number} box.y the top left Y coordinate of the canvas visible in view box
6032 * @param {number} box.width the visible width
6033 * @param {number} box.height
6034 *
6035 * @return {Object} the current view box
6036 */
6037 Canvas.prototype.viewbox = function(box) {
6038
6039 if (box === undefined && this._cachedViewbox) {
6040 return this._cachedViewbox;
6041 }
6042
6043 var viewport = this._viewport,
6044 innerBox,
6045 outerBox = this.getSize(),
6046 matrix,
6047 activeLayer,
6048 transform,
6049 scale,
6050 x, y;
6051
6052 if (!box) {
6053
6054 // compute the inner box based on the
6055 // diagrams active layer. This allows us to exclude
6056 // external components, such as overlays
6057
6058 activeLayer = this._rootElement ? this.getActiveLayer() : null;
6059 innerBox = activeLayer && activeLayer.getBBox() || {};
6060
6061 transform = transform$1(viewport);
6062 matrix = transform ? transform.matrix : createMatrix();
6063 scale = round$b(matrix.a, 1000);
6064
6065 x = round$b(-matrix.e || 0, 1000);
6066 y = round$b(-matrix.f || 0, 1000);
6067
6068 box = this._cachedViewbox = {
6069 x: x ? x / scale : 0,
6070 y: y ? y / scale : 0,
6071 width: outerBox.width / scale,
6072 height: outerBox.height / scale,
6073 scale: scale,
6074 inner: {
6075 width: innerBox.width || 0,
6076 height: innerBox.height || 0,
6077 x: innerBox.x || 0,
6078 y: innerBox.y || 0
6079 },
6080 outer: outerBox
6081 };
6082
6083 return box;
6084 } else {
6085
6086 this._changeViewbox(function() {
6087 scale = Math.min(outerBox.width / box.width, outerBox.height / box.height);
6088
6089 var matrix = this._svg.createSVGMatrix()
6090 .scale(scale)
6091 .translate(-box.x, -box.y);
6092
6093 transform$1(viewport, matrix);
6094 });
6095 }
6096
6097 return box;
6098 };
6099
6100
6101 /**
6102 * Gets or sets the scroll of the canvas.
6103 *
6104 * @param {Object} [delta] the new scroll to apply.
6105 *
6106 * @param {number} [delta.dx]
6107 * @param {number} [delta.dy]
6108 */
6109 Canvas.prototype.scroll = function(delta) {
6110
6111 var node = this._viewport;
6112 var matrix = node.getCTM();
6113
6114 if (delta) {
6115 this._changeViewbox(function() {
6116 delta = assign({ dx: 0, dy: 0 }, delta || {});
6117
6118 matrix = this._svg.createSVGMatrix().translate(delta.dx, delta.dy).multiply(matrix);
6119
6120 setCTM(node, matrix);
6121 });
6122 }
6123
6124 return { x: matrix.e, y: matrix.f };
6125 };
6126
6127 /**
6128 * Scrolls the viewbox to contain the given element.
6129 * Optionally specify a padding to be applied to the edges.
6130 *
6131 * @param {Object|String} [element] the element to scroll to.
6132 * @param {Object|Number} [padding=100] the padding to be applied. Can also specify top, bottom, left and right.
6133 *
6134 */
6135 Canvas.prototype.scrollToElement = function(element, padding) {
6136 var defaultPadding = 100;
6137
6138 if (typeof element === 'string') {
6139 element = this._elementRegistry.get(element);
6140 }
6141
6142 // set to correct rootElement
6143 var rootElement = this.findRoot(element);
6144
6145 if (rootElement !== this.getRootElement()) {
6146 this.setRootElement(rootElement);
6147 }
6148
6149 if (!padding) {
6150 padding = {};
6151 }
6152 if (typeof padding === 'number') {
6153 defaultPadding = padding;
6154 }
6155
6156 padding = {
6157 top: padding.top || defaultPadding,
6158 right: padding.right || defaultPadding,
6159 bottom: padding.bottom || defaultPadding,
6160 left: padding.left || defaultPadding
6161 };
6162
6163 var elementBounds = getBBox(element),
6164 elementTrbl = asTRBL(elementBounds),
6165 viewboxBounds = this.viewbox(),
6166 zoom = this.zoom(),
6167 dx, dy;
6168
6169 // shrink viewboxBounds with padding
6170 viewboxBounds.y += padding.top / zoom;
6171 viewboxBounds.x += padding.left / zoom;
6172 viewboxBounds.width -= (padding.right + padding.left) / zoom;
6173 viewboxBounds.height -= (padding.bottom + padding.top) / zoom;
6174
6175 var viewboxTrbl = asTRBL(viewboxBounds);
6176
6177 var canFit = elementBounds.width < viewboxBounds.width && elementBounds.height < viewboxBounds.height;
6178
6179 if (!canFit) {
6180
6181 // top-left when element can't fit
6182 dx = elementBounds.x - viewboxBounds.x;
6183 dy = elementBounds.y - viewboxBounds.y;
6184
6185 } else {
6186
6187 var dRight = Math.max(0, elementTrbl.right - viewboxTrbl.right),
6188 dLeft = Math.min(0, elementTrbl.left - viewboxTrbl.left),
6189 dBottom = Math.max(0, elementTrbl.bottom - viewboxTrbl.bottom),
6190 dTop = Math.min(0, elementTrbl.top - viewboxTrbl.top);
6191
6192 dx = dRight || dLeft;
6193 dy = dBottom || dTop;
6194
6195 }
6196
6197 this.scroll({ dx: -dx * zoom, dy: -dy * zoom });
6198 };
6199
6200 /**
6201 * Gets or sets the current zoom of the canvas, optionally zooming
6202 * to the specified position.
6203 *
6204 * The getter may return a cached zoom level. Call it with `false` as
6205 * the first argument to force recomputation of the current level.
6206 *
6207 * @param {string|number} [newScale] the new zoom level, either a number, i.e. 0.9,
6208 * or `fit-viewport` to adjust the size to fit the current viewport
6209 * @param {string|Point} [center] the reference point { x: .., y: ..} to zoom to, 'auto' to zoom into mid or null
6210 *
6211 * @return {number} the current scale
6212 */
6213 Canvas.prototype.zoom = function(newScale, center) {
6214
6215 if (!newScale) {
6216 return this.viewbox(newScale).scale;
6217 }
6218
6219 if (newScale === 'fit-viewport') {
6220 return this._fitViewport(center);
6221 }
6222
6223 var outer,
6224 matrix;
6225
6226 this._changeViewbox(function() {
6227
6228 if (typeof center !== 'object') {
6229 outer = this.viewbox().outer;
6230
6231 center = {
6232 x: outer.width / 2,
6233 y: outer.height / 2
6234 };
6235 }
6236
6237 matrix = this._setZoom(newScale, center);
6238 });
6239
6240 return round$b(matrix.a, 1000);
6241 };
6242
6243 function setCTM(node, m) {
6244 var mstr = 'matrix(' + m.a + ',' + m.b + ',' + m.c + ',' + m.d + ',' + m.e + ',' + m.f + ')';
6245 node.setAttribute('transform', mstr);
6246 }
6247
6248 Canvas.prototype._fitViewport = function(center) {
6249
6250 var vbox = this.viewbox(),
6251 outer = vbox.outer,
6252 inner = vbox.inner,
6253 newScale,
6254 newViewbox;
6255
6256 // display the complete diagram without zooming in.
6257 // instead of relying on internal zoom, we perform a
6258 // hard reset on the canvas viewbox to realize this
6259 //
6260 // if diagram does not need to be zoomed in, we focus it around
6261 // the diagram origin instead
6262
6263 if (inner.x >= 0 &&
6264 inner.y >= 0 &&
6265 inner.x + inner.width <= outer.width &&
6266 inner.y + inner.height <= outer.height &&
6267 !center) {
6268
6269 newViewbox = {
6270 x: 0,
6271 y: 0,
6272 width: Math.max(inner.width + inner.x, outer.width),
6273 height: Math.max(inner.height + inner.y, outer.height)
6274 };
6275 } else {
6276
6277 newScale = Math.min(1, outer.width / inner.width, outer.height / inner.height);
6278 newViewbox = {
6279 x: inner.x + (center ? inner.width / 2 - outer.width / newScale / 2 : 0),
6280 y: inner.y + (center ? inner.height / 2 - outer.height / newScale / 2 : 0),
6281 width: outer.width / newScale,
6282 height: outer.height / newScale
6283 };
6284 }
6285
6286 this.viewbox(newViewbox);
6287
6288 return this.viewbox(false).scale;
6289 };
6290
6291
6292 Canvas.prototype._setZoom = function(scale, center) {
6293
6294 var svg = this._svg,
6295 viewport = this._viewport;
6296
6297 var matrix = svg.createSVGMatrix();
6298 var point = svg.createSVGPoint();
6299
6300 var centerPoint,
6301 originalPoint,
6302 currentMatrix,
6303 scaleMatrix,
6304 newMatrix;
6305
6306 currentMatrix = viewport.getCTM();
6307
6308 var currentScale = currentMatrix.a;
6309
6310 if (center) {
6311 centerPoint = assign(point, center);
6312
6313 // revert applied viewport transformations
6314 originalPoint = centerPoint.matrixTransform(currentMatrix.inverse());
6315
6316 // create scale matrix
6317 scaleMatrix = matrix
6318 .translate(originalPoint.x, originalPoint.y)
6319 .scale(1 / currentScale * scale)
6320 .translate(-originalPoint.x, -originalPoint.y);
6321
6322 newMatrix = currentMatrix.multiply(scaleMatrix);
6323 } else {
6324 newMatrix = matrix.scale(scale);
6325 }
6326
6327 setCTM(this._viewport, newMatrix);
6328
6329 return newMatrix;
6330 };
6331
6332
6333 /**
6334 * Returns the size of the canvas
6335 *
6336 * @return {Dimensions}
6337 */
6338 Canvas.prototype.getSize = function() {
6339 return {
6340 width: this._container.clientWidth,
6341 height: this._container.clientHeight
6342 };
6343 };
6344
6345
6346 /**
6347 * Return the absolute bounding box for the given element
6348 *
6349 * The absolute bounding box may be used to display overlays in the
6350 * callers (browser) coordinate system rather than the zoomed in/out
6351 * canvas coordinates.
6352 *
6353 * @param {ElementDescriptor} element
6354 * @return {Bounds} the absolute bounding box
6355 */
6356 Canvas.prototype.getAbsoluteBBox = function(element) {
6357 var vbox = this.viewbox();
6358 var bbox;
6359
6360 // connection
6361 // use svg bbox
6362 if (element.waypoints) {
6363 var gfx = this.getGraphics(element);
6364
6365 bbox = gfx.getBBox();
6366 }
6367
6368 // shapes
6369 // use data
6370 else {
6371 bbox = element;
6372 }
6373
6374 var x = bbox.x * vbox.scale - vbox.x * vbox.scale;
6375 var y = bbox.y * vbox.scale - vbox.y * vbox.scale;
6376
6377 var width = bbox.width * vbox.scale;
6378 var height = bbox.height * vbox.scale;
6379
6380 return {
6381 x: x,
6382 y: y,
6383 width: width,
6384 height: height
6385 };
6386 };
6387
6388 /**
6389 * Fires an event in order other modules can react to the
6390 * canvas resizing
6391 */
6392 Canvas.prototype.resized = function() {
6393
6394 // force recomputation of view box
6395 delete this._cachedViewbox;
6396
6397 this._eventBus.fire('canvas.resized');
6398 };
6399
6400 var ELEMENT_ID = 'data-element-id';
6401
6402
6403 /**
6404 * @class
6405 *
6406 * A registry that keeps track of all shapes in the diagram.
6407 */
6408 function ElementRegistry(eventBus) {
6409 this._elements = {};
6410
6411 this._eventBus = eventBus;
6412 }
6413
6414 ElementRegistry.$inject = [ 'eventBus' ];
6415
6416 /**
6417 * Register a pair of (element, gfx, (secondaryGfx)).
6418 *
6419 * @param {djs.model.Base} element
6420 * @param {SVGElement} gfx
6421 * @param {SVGElement} [secondaryGfx] optional other element to register, too
6422 */
6423 ElementRegistry.prototype.add = function(element, gfx, secondaryGfx) {
6424
6425 var id = element.id;
6426
6427 this._validateId(id);
6428
6429 // associate dom node with element
6430 attr$1(gfx, ELEMENT_ID, id);
6431
6432 if (secondaryGfx) {
6433 attr$1(secondaryGfx, ELEMENT_ID, id);
6434 }
6435
6436 this._elements[id] = { element: element, gfx: gfx, secondaryGfx: secondaryGfx };
6437 };
6438
6439 /**
6440 * Removes an element from the registry.
6441 *
6442 * @param {djs.model.Base} element
6443 */
6444 ElementRegistry.prototype.remove = function(element) {
6445 var elements = this._elements,
6446 id = element.id || element,
6447 container = id && elements[id];
6448
6449 if (container) {
6450
6451 // unset element id on gfx
6452 attr$1(container.gfx, ELEMENT_ID, '');
6453
6454 if (container.secondaryGfx) {
6455 attr$1(container.secondaryGfx, ELEMENT_ID, '');
6456 }
6457
6458 delete elements[id];
6459 }
6460 };
6461
6462 /**
6463 * Update the id of an element
6464 *
6465 * @param {djs.model.Base} element
6466 * @param {string} newId
6467 */
6468 ElementRegistry.prototype.updateId = function(element, newId) {
6469
6470 this._validateId(newId);
6471
6472 if (typeof element === 'string') {
6473 element = this.get(element);
6474 }
6475
6476 this._eventBus.fire('element.updateId', {
6477 element: element,
6478 newId: newId
6479 });
6480
6481 var gfx = this.getGraphics(element),
6482 secondaryGfx = this.getGraphics(element, true);
6483
6484 this.remove(element);
6485
6486 element.id = newId;
6487
6488 this.add(element, gfx, secondaryGfx);
6489 };
6490
6491 /**
6492 * Update the graphics of an element
6493 *
6494 * @param {djs.model.Base} element
6495 * @param {SVGElement} gfx
6496 * @param {boolean} [secondary=false] whether to update the secondary connected element
6497 */
6498 ElementRegistry.prototype.updateGraphics = function(filter, gfx, secondary) {
6499 var id = filter.id || filter;
6500
6501 var container = this._elements[id];
6502
6503 if (secondary) {
6504 container.secondaryGfx = gfx;
6505 } else {
6506 container.gfx = gfx;
6507 }
6508
6509 if (gfx) {
6510 attr$1(gfx, ELEMENT_ID, id);
6511 }
6512
6513 return gfx;
6514 };
6515
6516 /**
6517 * Return the model element for a given id or graphics.
6518 *
6519 * @example
6520 *
6521 * elementRegistry.get('SomeElementId_1');
6522 * elementRegistry.get(gfx);
6523 *
6524 *
6525 * @param {string|SVGElement} filter for selecting the element
6526 *
6527 * @return {djs.model.Base}
6528 */
6529 ElementRegistry.prototype.get = function(filter) {
6530 var id;
6531
6532 if (typeof filter === 'string') {
6533 id = filter;
6534 } else {
6535 id = filter && attr$1(filter, ELEMENT_ID);
6536 }
6537
6538 var container = this._elements[id];
6539 return container && container.element;
6540 };
6541
6542 /**
6543 * Return all elements that match a given filter function.
6544 *
6545 * @param {Function} fn
6546 *
6547 * @return {Array<djs.model.Base>}
6548 */
6549 ElementRegistry.prototype.filter = function(fn) {
6550
6551 var filtered = [];
6552
6553 this.forEach(function(element, gfx) {
6554 if (fn(element, gfx)) {
6555 filtered.push(element);
6556 }
6557 });
6558
6559 return filtered;
6560 };
6561
6562 /**
6563 * Return the first element that satisfies the provided testing function.
6564 *
6565 * @param {Function} fn
6566 *
6567 * @return {djs.model.Base}
6568 */
6569 ElementRegistry.prototype.find = function(fn) {
6570 var map = this._elements,
6571 keys = Object.keys(map);
6572
6573 for (var i = 0; i < keys.length; i++) {
6574 var id = keys[i],
6575 container = map[id],
6576 element = container.element,
6577 gfx = container.gfx;
6578
6579 if (fn(element, gfx)) {
6580 return element;
6581 }
6582 }
6583 };
6584
6585 /**
6586 * Return all rendered model elements.
6587 *
6588 * @return {Array<djs.model.Base>}
6589 */
6590 ElementRegistry.prototype.getAll = function() {
6591 return this.filter(function(e) { return e; });
6592 };
6593
6594 /**
6595 * Iterate over all diagram elements.
6596 *
6597 * @param {Function} fn
6598 */
6599 ElementRegistry.prototype.forEach = function(fn) {
6600
6601 var map = this._elements;
6602
6603 Object.keys(map).forEach(function(id) {
6604 var container = map[id],
6605 element = container.element,
6606 gfx = container.gfx;
6607
6608 return fn(element, gfx);
6609 });
6610 };
6611
6612 /**
6613 * Return the graphical representation of an element or its id.
6614 *
6615 * @example
6616 * elementRegistry.getGraphics('SomeElementId_1');
6617 * elementRegistry.getGraphics(rootElement); // <g ...>
6618 *
6619 * elementRegistry.getGraphics(rootElement, true); // <svg ...>
6620 *
6621 *
6622 * @param {string|djs.model.Base} filter
6623 * @param {boolean} [secondary=false] whether to return the secondary connected element
6624 *
6625 * @return {SVGElement}
6626 */
6627 ElementRegistry.prototype.getGraphics = function(filter, secondary) {
6628 var id = filter.id || filter;
6629
6630 var container = this._elements[id];
6631 return container && (secondary ? container.secondaryGfx : container.gfx);
6632 };
6633
6634 /**
6635 * Validate the suitability of the given id and signals a problem
6636 * with an exception.
6637 *
6638 * @param {string} id
6639 *
6640 * @throws {Error} if id is empty or already assigned
6641 */
6642 ElementRegistry.prototype._validateId = function(id) {
6643 if (!id) {
6644 throw new Error('element must have an id');
6645 }
6646
6647 if (this._elements[id]) {
6648 throw new Error('element with id ' + id + ' already added');
6649 }
6650 };
6651
6652 var objectRefs = {exports: {}};
6653
6654 var collection = {};
6655
6656 /**
6657 * An empty collection stub. Use {@link RefsCollection.extend} to extend a
6658 * collection with ref semantics.
6659 *
6660 * @class RefsCollection
6661 */
6662
6663 /**
6664 * Extends a collection with {@link Refs} aware methods
6665 *
6666 * @memberof RefsCollection
6667 * @static
6668 *
6669 * @param {Array<Object>} collection
6670 * @param {Refs} refs instance
6671 * @param {Object} property represented by the collection
6672 * @param {Object} target object the collection is attached to
6673 *
6674 * @return {RefsCollection<Object>} the extended array
6675 */
6676 function extend(collection, refs, property, target) {
6677
6678 var inverseProperty = property.inverse;
6679
6680 /**
6681 * Removes the given element from the array and returns it.
6682 *
6683 * @method RefsCollection#remove
6684 *
6685 * @param {Object} element the element to remove
6686 */
6687 Object.defineProperty(collection, 'remove', {
6688 value: function(element) {
6689 var idx = this.indexOf(element);
6690 if (idx !== -1) {
6691 this.splice(idx, 1);
6692
6693 // unset inverse
6694 refs.unset(element, inverseProperty, target);
6695 }
6696
6697 return element;
6698 }
6699 });
6700
6701 /**
6702 * Returns true if the collection contains the given element
6703 *
6704 * @method RefsCollection#contains
6705 *
6706 * @param {Object} element the element to check for
6707 */
6708 Object.defineProperty(collection, 'contains', {
6709 value: function(element) {
6710 return this.indexOf(element) !== -1;
6711 }
6712 });
6713
6714 /**
6715 * Adds an element to the array, unless it exists already (set semantics).
6716 *
6717 * @method RefsCollection#add
6718 *
6719 * @param {Object} element the element to add
6720 * @param {Number} optional index to add element to
6721 * (possibly moving other elements around)
6722 */
6723 Object.defineProperty(collection, 'add', {
6724 value: function(element, idx) {
6725
6726 var currentIdx = this.indexOf(element);
6727
6728 if (typeof idx === 'undefined') {
6729
6730 if (currentIdx !== -1) {
6731 // element already in collection (!)
6732 return;
6733 }
6734
6735 // add to end of array, as no idx is specified
6736 idx = this.length;
6737 }
6738
6739 // handle already in collection
6740 if (currentIdx !== -1) {
6741
6742 // remove element from currentIdx
6743 this.splice(currentIdx, 1);
6744 }
6745
6746 // add element at idx
6747 this.splice(idx, 0, element);
6748
6749 if (currentIdx === -1) {
6750 // set inverse, unless element was
6751 // in collection already
6752 refs.set(element, inverseProperty, target);
6753 }
6754 }
6755 });
6756
6757 // a simple marker, identifying this element
6758 // as being a refs collection
6759 Object.defineProperty(collection, '__refs_collection', {
6760 value: true
6761 });
6762
6763 return collection;
6764 }
6765
6766
6767 function isExtended(collection) {
6768 return collection.__refs_collection === true;
6769 }
6770
6771 collection.extend = extend;
6772
6773 collection.isExtended = isExtended;
6774
6775 var Collection = collection;
6776
6777 function hasOwnProperty$1(e, property) {
6778 return Object.prototype.hasOwnProperty.call(e, property.name || property);
6779 }
6780
6781 function defineCollectionProperty(ref, property, target) {
6782
6783 var collection = Collection.extend(target[property.name] || [], ref, property, target);
6784
6785 Object.defineProperty(target, property.name, {
6786 enumerable: property.enumerable,
6787 value: collection
6788 });
6789
6790 if (collection.length) {
6791
6792 collection.forEach(function(o) {
6793 ref.set(o, property.inverse, target);
6794 });
6795 }
6796 }
6797
6798
6799 function defineProperty$1(ref, property, target) {
6800
6801 var inverseProperty = property.inverse;
6802
6803 var _value = target[property.name];
6804
6805 Object.defineProperty(target, property.name, {
6806 configurable: property.configurable,
6807 enumerable: property.enumerable,
6808
6809 get: function() {
6810 return _value;
6811 },
6812
6813 set: function(value) {
6814
6815 // return if we already performed all changes
6816 if (value === _value) {
6817 return;
6818 }
6819
6820 var old = _value;
6821
6822 // temporary set null
6823 _value = null;
6824
6825 if (old) {
6826 ref.unset(old, inverseProperty, target);
6827 }
6828
6829 // set new value
6830 _value = value;
6831
6832 // set inverse value
6833 ref.set(_value, inverseProperty, target);
6834 }
6835 });
6836
6837 }
6838
6839 /**
6840 * Creates a new references object defining two inversly related
6841 * attribute descriptors a and b.
6842 *
6843 * <p>
6844 * When bound to an object using {@link Refs#bind} the references
6845 * get activated and ensure that add and remove operations are applied
6846 * reversely, too.
6847 * </p>
6848 *
6849 * <p>
6850 * For attributes represented as collections {@link Refs} provides the
6851 * {@link RefsCollection#add}, {@link RefsCollection#remove} and {@link RefsCollection#contains} extensions
6852 * that must be used to properly hook into the inverse change mechanism.
6853 * </p>
6854 *
6855 * @class Refs
6856 *
6857 * @classdesc A bi-directional reference between two attributes.
6858 *
6859 * @param {Refs.AttributeDescriptor} a property descriptor
6860 * @param {Refs.AttributeDescriptor} b property descriptor
6861 *
6862 * @example
6863 *
6864 * var refs = Refs({ name: 'wheels', collection: true, enumerable: true }, { name: 'car' });
6865 *
6866 * var car = { name: 'toyota' };
6867 * var wheels = [{ pos: 'front-left' }, { pos: 'front-right' }];
6868 *
6869 * refs.bind(car, 'wheels');
6870 *
6871 * car.wheels // []
6872 * car.wheels.add(wheels[0]);
6873 * car.wheels.add(wheels[1]);
6874 *
6875 * car.wheels // [{ pos: 'front-left' }, { pos: 'front-right' }]
6876 *
6877 * wheels[0].car // { name: 'toyota' };
6878 * car.wheels.remove(wheels[0]);
6879 *
6880 * wheels[0].car // undefined
6881 */
6882 function Refs$1(a, b) {
6883
6884 if (!(this instanceof Refs$1)) {
6885 return new Refs$1(a, b);
6886 }
6887
6888 // link
6889 a.inverse = b;
6890 b.inverse = a;
6891
6892 this.props = {};
6893 this.props[a.name] = a;
6894 this.props[b.name] = b;
6895 }
6896
6897 /**
6898 * Binds one side of a bi-directional reference to a
6899 * target object.
6900 *
6901 * @memberOf Refs
6902 *
6903 * @param {Object} target
6904 * @param {String} property
6905 */
6906 Refs$1.prototype.bind = function(target, property) {
6907 if (typeof property === 'string') {
6908 if (!this.props[property]) {
6909 throw new Error('no property <' + property + '> in ref');
6910 }
6911 property = this.props[property];
6912 }
6913
6914 if (property.collection) {
6915 defineCollectionProperty(this, property, target);
6916 } else {
6917 defineProperty$1(this, property, target);
6918 }
6919 };
6920
6921 Refs$1.prototype.ensureRefsCollection = function(target, property) {
6922
6923 var collection = target[property.name];
6924
6925 if (!Collection.isExtended(collection)) {
6926 defineCollectionProperty(this, property, target);
6927 }
6928
6929 return collection;
6930 };
6931
6932 Refs$1.prototype.ensureBound = function(target, property) {
6933 if (!hasOwnProperty$1(target, property)) {
6934 this.bind(target, property);
6935 }
6936 };
6937
6938 Refs$1.prototype.unset = function(target, property, value) {
6939
6940 if (target) {
6941 this.ensureBound(target, property);
6942
6943 if (property.collection) {
6944 this.ensureRefsCollection(target, property).remove(value);
6945 } else {
6946 target[property.name] = undefined;
6947 }
6948 }
6949 };
6950
6951 Refs$1.prototype.set = function(target, property, value) {
6952
6953 if (target) {
6954 this.ensureBound(target, property);
6955
6956 if (property.collection) {
6957 this.ensureRefsCollection(target, property).add(value);
6958 } else {
6959 target[property.name] = value;
6960 }
6961 }
6962 };
6963
6964 var refs = Refs$1;
6965
6966 objectRefs.exports = refs;
6967
6968 objectRefs.exports.Collection = collection;
6969
6970 var Refs = objectRefs.exports;
6971
6972 var parentRefs = new Refs({ name: 'children', enumerable: true, collection: true }, { name: 'parent' }),
6973 labelRefs = new Refs({ name: 'labels', enumerable: true, collection: true }, { name: 'labelTarget' }),
6974 attacherRefs = new Refs({ name: 'attachers', collection: true }, { name: 'host' }),
6975 outgoingRefs = new Refs({ name: 'outgoing', collection: true }, { name: 'source' }),
6976 incomingRefs = new Refs({ name: 'incoming', collection: true }, { name: 'target' });
6977
6978 /**
6979 * @namespace djs.model
6980 */
6981
6982 /**
6983 * @memberOf djs.model
6984 */
6985
6986 /**
6987 * The basic graphical representation
6988 *
6989 * @class
6990 *
6991 * @abstract
6992 */
6993 function Base$1() {
6994
6995 /**
6996 * The object that backs up the shape
6997 *
6998 * @name Base#businessObject
6999 * @type Object
7000 */
7001 Object.defineProperty(this, 'businessObject', {
7002 writable: true
7003 });
7004
7005
7006 /**
7007 * Single label support, will mapped to multi label array
7008 *
7009 * @name Base#label
7010 * @type Object
7011 */
7012 Object.defineProperty(this, 'label', {
7013 get: function() {
7014 return this.labels[0];
7015 },
7016 set: function(newLabel) {
7017
7018 var label = this.label,
7019 labels = this.labels;
7020
7021 if (!newLabel && label) {
7022 labels.remove(label);
7023 } else {
7024 labels.add(newLabel, 0);
7025 }
7026 }
7027 });
7028
7029 /**
7030 * The parent shape
7031 *
7032 * @name Base#parent
7033 * @type Shape
7034 */
7035 parentRefs.bind(this, 'parent');
7036
7037 /**
7038 * The list of labels
7039 *
7040 * @name Base#labels
7041 * @type Label
7042 */
7043 labelRefs.bind(this, 'labels');
7044
7045 /**
7046 * The list of outgoing connections
7047 *
7048 * @name Base#outgoing
7049 * @type Array<Connection>
7050 */
7051 outgoingRefs.bind(this, 'outgoing');
7052
7053 /**
7054 * The list of incoming connections
7055 *
7056 * @name Base#incoming
7057 * @type Array<Connection>
7058 */
7059 incomingRefs.bind(this, 'incoming');
7060 }
7061
7062
7063 /**
7064 * A graphical object
7065 *
7066 * @class
7067 * @constructor
7068 *
7069 * @extends Base
7070 */
7071 function Shape() {
7072 Base$1.call(this);
7073
7074 /**
7075 * Indicates frame shapes
7076 *
7077 * @name Shape#isFrame
7078 * @type boolean
7079 */
7080
7081 /**
7082 * The list of children
7083 *
7084 * @name Shape#children
7085 * @type Array<Base>
7086 */
7087 parentRefs.bind(this, 'children');
7088
7089 /**
7090 * @name Shape#host
7091 * @type Shape
7092 */
7093 attacherRefs.bind(this, 'host');
7094
7095 /**
7096 * @name Shape#attachers
7097 * @type Shape
7098 */
7099 attacherRefs.bind(this, 'attachers');
7100 }
7101
7102 inherits$1(Shape, Base$1);
7103
7104
7105 /**
7106 * A root graphical object
7107 *
7108 * @class
7109 * @constructor
7110 *
7111 * @extends Shape
7112 */
7113 function Root() {
7114 Shape.call(this);
7115 }
7116
7117 inherits$1(Root, Shape);
7118
7119
7120 /**
7121 * A label for an element
7122 *
7123 * @class
7124 * @constructor
7125 *
7126 * @extends Shape
7127 */
7128 function Label() {
7129 Shape.call(this);
7130
7131 /**
7132 * The labeled element
7133 *
7134 * @name Label#labelTarget
7135 * @type Base
7136 */
7137 labelRefs.bind(this, 'labelTarget');
7138 }
7139
7140 inherits$1(Label, Shape);
7141
7142
7143 /**
7144 * A connection between two elements
7145 *
7146 * @class
7147 * @constructor
7148 *
7149 * @extends Base
7150 */
7151 function Connection() {
7152 Base$1.call(this);
7153
7154 /**
7155 * The element this connection originates from
7156 *
7157 * @name Connection#source
7158 * @type Base
7159 */
7160 outgoingRefs.bind(this, 'source');
7161
7162 /**
7163 * The element this connection points to
7164 *
7165 * @name Connection#target
7166 * @type Base
7167 */
7168 incomingRefs.bind(this, 'target');
7169 }
7170
7171 inherits$1(Connection, Base$1);
7172
7173
7174 var types$6 = {
7175 connection: Connection,
7176 shape: Shape,
7177 label: Label,
7178 root: Root
7179 };
7180
7181 /**
7182 * Creates a new model element of the specified type
7183 *
7184 * @method create
7185 *
7186 * @example
7187 *
7188 * var shape1 = Model.create('shape', { x: 10, y: 10, width: 100, height: 100 });
7189 * var shape2 = Model.create('shape', { x: 210, y: 210, width: 100, height: 100 });
7190 *
7191 * var connection = Model.create('connection', { waypoints: [ { x: 110, y: 55 }, {x: 210, y: 55 } ] });
7192 *
7193 * @param {string} type lower-cased model name
7194 * @param {Object} attrs attributes to initialize the new model instance with
7195 *
7196 * @return {Base} the new model instance
7197 */
7198 function create(type, attrs) {
7199 var Type = types$6[type];
7200 if (!Type) {
7201 throw new Error('unknown type: <' + type + '>');
7202 }
7203 return assign(new Type(), attrs);
7204 }
7205
7206 /**
7207 * A factory for diagram-js shapes
7208 */
7209 function ElementFactory$1() {
7210 this._uid = 12;
7211 }
7212
7213
7214 ElementFactory$1.prototype.createRoot = function(attrs) {
7215 return this.create('root', attrs);
7216 };
7217
7218 ElementFactory$1.prototype.createLabel = function(attrs) {
7219 return this.create('label', attrs);
7220 };
7221
7222 ElementFactory$1.prototype.createShape = function(attrs) {
7223 return this.create('shape', attrs);
7224 };
7225
7226 ElementFactory$1.prototype.createConnection = function(attrs) {
7227 return this.create('connection', attrs);
7228 };
7229
7230 /**
7231 * Create a model element with the given type and
7232 * a number of pre-set attributes.
7233 *
7234 * @param {string} type
7235 * @param {Object} attrs
7236 * @return {djs.model.Base} the newly created model instance
7237 */
7238 ElementFactory$1.prototype.create = function(type, attrs) {
7239
7240 attrs = assign({}, attrs || {});
7241
7242 if (!attrs.id) {
7243 attrs.id = type + '_' + (this._uid++);
7244 }
7245
7246 return create(type, attrs);
7247 };
7248
7249 var FN_REF = '__fn';
7250
7251 var DEFAULT_PRIORITY$5 = 1000;
7252
7253 var slice = Array.prototype.slice;
7254
7255 /**
7256 * A general purpose event bus.
7257 *
7258 * This component is used to communicate across a diagram instance.
7259 * Other parts of a diagram can use it to listen to and broadcast events.
7260 *
7261 *
7262 * ## Registering for Events
7263 *
7264 * The event bus provides the {@link EventBus#on} and {@link EventBus#once}
7265 * methods to register for events. {@link EventBus#off} can be used to
7266 * remove event registrations. Listeners receive an instance of {@link Event}
7267 * as the first argument. It allows them to hook into the event execution.
7268 *
7269 * ```javascript
7270 *
7271 * // listen for event
7272 * eventBus.on('foo', function(event) {
7273 *
7274 * // access event type
7275 * event.type; // 'foo'
7276 *
7277 * // stop propagation to other listeners
7278 * event.stopPropagation();
7279 *
7280 * // prevent event default
7281 * event.preventDefault();
7282 * });
7283 *
7284 * // listen for event with custom payload
7285 * eventBus.on('bar', function(event, payload) {
7286 * console.log(payload);
7287 * });
7288 *
7289 * // listen for event returning value
7290 * eventBus.on('foobar', function(event) {
7291 *
7292 * // stop event propagation + prevent default
7293 * return false;
7294 *
7295 * // stop event propagation + return custom result
7296 * return {
7297 * complex: 'listening result'
7298 * };
7299 * });
7300 *
7301 *
7302 * // listen with custom priority (default=1000, higher is better)
7303 * eventBus.on('priorityfoo', 1500, function(event) {
7304 * console.log('invoked first!');
7305 * });
7306 *
7307 *
7308 * // listen for event and pass the context (`this`)
7309 * eventBus.on('foobar', function(event) {
7310 * this.foo();
7311 * }, this);
7312 * ```
7313 *
7314 *
7315 * ## Emitting Events
7316 *
7317 * Events can be emitted via the event bus using {@link EventBus#fire}.
7318 *
7319 * ```javascript
7320 *
7321 * // false indicates that the default action
7322 * // was prevented by listeners
7323 * if (eventBus.fire('foo') === false) {
7324 * console.log('default has been prevented!');
7325 * };
7326 *
7327 *
7328 * // custom args + return value listener
7329 * eventBus.on('sum', function(event, a, b) {
7330 * return a + b;
7331 * });
7332 *
7333 * // you can pass custom arguments + retrieve result values.
7334 * var sum = eventBus.fire('sum', 1, 2);
7335 * console.log(sum); // 3
7336 * ```
7337 */
7338 function EventBus() {
7339 this._listeners = {};
7340
7341 // cleanup on destroy on lowest priority to allow
7342 // message passing until the bitter end
7343 this.on('diagram.destroy', 1, this._destroy, this);
7344 }
7345
7346
7347 /**
7348 * Register an event listener for events with the given name.
7349 *
7350 * The callback will be invoked with `event, ...additionalArguments`
7351 * that have been passed to {@link EventBus#fire}.
7352 *
7353 * Returning false from a listener will prevent the events default action
7354 * (if any is specified). To stop an event from being processed further in
7355 * other listeners execute {@link Event#stopPropagation}.
7356 *
7357 * Returning anything but `undefined` from a listener will stop the listener propagation.
7358 *
7359 * @param {string|Array<string>} events
7360 * @param {number} [priority=1000] the priority in which this listener is called, larger is higher
7361 * @param {Function} callback
7362 * @param {Object} [that] Pass context (`this`) to the callback
7363 */
7364 EventBus.prototype.on = function(events, priority, callback, that) {
7365
7366 events = isArray$4(events) ? events : [ events ];
7367
7368 if (isFunction(priority)) {
7369 that = callback;
7370 callback = priority;
7371 priority = DEFAULT_PRIORITY$5;
7372 }
7373
7374 if (!isNumber(priority)) {
7375 throw new Error('priority must be a number');
7376 }
7377
7378 var actualCallback = callback;
7379
7380 if (that) {
7381 actualCallback = bind(callback, that);
7382
7383 // make sure we remember and are able to remove
7384 // bound callbacks via {@link #off} using the original
7385 // callback
7386 actualCallback[FN_REF] = callback[FN_REF] || callback;
7387 }
7388
7389 var self = this;
7390
7391 events.forEach(function(e) {
7392 self._addListener(e, {
7393 priority: priority,
7394 callback: actualCallback,
7395 next: null
7396 });
7397 });
7398 };
7399
7400
7401 /**
7402 * Register an event listener that is executed only once.
7403 *
7404 * @param {string} event the event name to register for
7405 * @param {number} [priority=1000] the priority in which this listener is called, larger is higher
7406 * @param {Function} callback the callback to execute
7407 * @param {Object} [that] Pass context (`this`) to the callback
7408 */
7409 EventBus.prototype.once = function(event, priority, callback, that) {
7410 var self = this;
7411
7412 if (isFunction(priority)) {
7413 that = callback;
7414 callback = priority;
7415 priority = DEFAULT_PRIORITY$5;
7416 }
7417
7418 if (!isNumber(priority)) {
7419 throw new Error('priority must be a number');
7420 }
7421
7422 function wrappedCallback() {
7423 wrappedCallback.__isTomb = true;
7424
7425 var result = callback.apply(that, arguments);
7426
7427 self.off(event, wrappedCallback);
7428
7429 return result;
7430 }
7431
7432 // make sure we remember and are able to remove
7433 // bound callbacks via {@link #off} using the original
7434 // callback
7435 wrappedCallback[FN_REF] = callback;
7436
7437 this.on(event, priority, wrappedCallback);
7438 };
7439
7440
7441 /**
7442 * Removes event listeners by event and callback.
7443 *
7444 * If no callback is given, all listeners for a given event name are being removed.
7445 *
7446 * @param {string|Array<string>} events
7447 * @param {Function} [callback]
7448 */
7449 EventBus.prototype.off = function(events, callback) {
7450
7451 events = isArray$4(events) ? events : [ events ];
7452
7453 var self = this;
7454
7455 events.forEach(function(event) {
7456 self._removeListener(event, callback);
7457 });
7458
7459 };
7460
7461
7462 /**
7463 * Create an EventBus event.
7464 *
7465 * @param {Object} data
7466 *
7467 * @return {Object} event, recognized by the eventBus
7468 */
7469 EventBus.prototype.createEvent = function(data) {
7470 var event = new InternalEvent();
7471
7472 event.init(data);
7473
7474 return event;
7475 };
7476
7477
7478 /**
7479 * Fires a named event.
7480 *
7481 * @example
7482 *
7483 * // fire event by name
7484 * events.fire('foo');
7485 *
7486 * // fire event object with nested type
7487 * var event = { type: 'foo' };
7488 * events.fire(event);
7489 *
7490 * // fire event with explicit type
7491 * var event = { x: 10, y: 20 };
7492 * events.fire('element.moved', event);
7493 *
7494 * // pass additional arguments to the event
7495 * events.on('foo', function(event, bar) {
7496 * alert(bar);
7497 * });
7498 *
7499 * events.fire({ type: 'foo' }, 'I am bar!');
7500 *
7501 * @param {string} [name] the optional event name
7502 * @param {Object} [event] the event object
7503 * @param {...Object} additional arguments to be passed to the callback functions
7504 *
7505 * @return {boolean} the events return value, if specified or false if the
7506 * default action was prevented by listeners
7507 */
7508 EventBus.prototype.fire = function(type, data) {
7509 var event,
7510 firstListener,
7511 returnValue,
7512 args;
7513
7514 args = slice.call(arguments);
7515
7516 if (typeof type === 'object') {
7517 data = type;
7518 type = data.type;
7519 }
7520
7521 if (!type) {
7522 throw new Error('no event type specified');
7523 }
7524
7525 firstListener = this._listeners[type];
7526
7527 if (!firstListener) {
7528 return;
7529 }
7530
7531 // we make sure we fire instances of our home made
7532 // events here. We wrap them only once, though
7533 if (data instanceof InternalEvent) {
7534
7535 // we are fine, we alread have an event
7536 event = data;
7537 } else {
7538 event = this.createEvent(data);
7539 }
7540
7541 // ensure we pass the event as the first parameter
7542 args[0] = event;
7543
7544 // original event type (in case we delegate)
7545 var originalType = event.type;
7546
7547 // update event type before delegation
7548 if (type !== originalType) {
7549 event.type = type;
7550 }
7551
7552 try {
7553 returnValue = this._invokeListeners(event, args, firstListener);
7554 } finally {
7555
7556 // reset event type after delegation
7557 if (type !== originalType) {
7558 event.type = originalType;
7559 }
7560 }
7561
7562 // set the return value to false if the event default
7563 // got prevented and no other return value exists
7564 if (returnValue === undefined && event.defaultPrevented) {
7565 returnValue = false;
7566 }
7567
7568 return returnValue;
7569 };
7570
7571
7572 EventBus.prototype.handleError = function(error) {
7573 return this.fire('error', { error: error }) === false;
7574 };
7575
7576
7577 EventBus.prototype._destroy = function() {
7578 this._listeners = {};
7579 };
7580
7581 EventBus.prototype._invokeListeners = function(event, args, listener) {
7582
7583 var returnValue;
7584
7585 while (listener) {
7586
7587 // handle stopped propagation
7588 if (event.cancelBubble) {
7589 break;
7590 }
7591
7592 returnValue = this._invokeListener(event, args, listener);
7593
7594 listener = listener.next;
7595 }
7596
7597 return returnValue;
7598 };
7599
7600 EventBus.prototype._invokeListener = function(event, args, listener) {
7601
7602 var returnValue;
7603
7604 if (listener.callback.__isTomb) {
7605 return returnValue;
7606 }
7607
7608 try {
7609
7610 // returning false prevents the default action
7611 returnValue = invokeFunction(listener.callback, args);
7612
7613 // stop propagation on return value
7614 if (returnValue !== undefined) {
7615 event.returnValue = returnValue;
7616 event.stopPropagation();
7617 }
7618
7619 // prevent default on return false
7620 if (returnValue === false) {
7621 event.preventDefault();
7622 }
7623 } catch (error) {
7624 if (!this.handleError(error)) {
7625 console.error('unhandled error in event listener', error);
7626
7627 throw error;
7628 }
7629 }
7630
7631 return returnValue;
7632 };
7633
7634 /*
7635 * Add new listener with a certain priority to the list
7636 * of listeners (for the given event).
7637 *
7638 * The semantics of listener registration / listener execution are
7639 * first register, first serve: New listeners will always be inserted
7640 * after existing listeners with the same priority.
7641 *
7642 * Example: Inserting two listeners with priority 1000 and 1300
7643 *
7644 * * before: [ 1500, 1500, 1000, 1000 ]
7645 * * after: [ 1500, 1500, (new=1300), 1000, 1000, (new=1000) ]
7646 *
7647 * @param {string} event
7648 * @param {Object} listener { priority, callback }
7649 */
7650 EventBus.prototype._addListener = function(event, newListener) {
7651
7652 var listener = this._getListeners(event),
7653 previousListener;
7654
7655 // no prior listeners
7656 if (!listener) {
7657 this._setListeners(event, newListener);
7658
7659 return;
7660 }
7661
7662 // ensure we order listeners by priority from
7663 // 0 (high) to n > 0 (low)
7664 while (listener) {
7665
7666 if (listener.priority < newListener.priority) {
7667
7668 newListener.next = listener;
7669
7670 if (previousListener) {
7671 previousListener.next = newListener;
7672 } else {
7673 this._setListeners(event, newListener);
7674 }
7675
7676 return;
7677 }
7678
7679 previousListener = listener;
7680 listener = listener.next;
7681 }
7682
7683 // add new listener to back
7684 previousListener.next = newListener;
7685 };
7686
7687
7688 EventBus.prototype._getListeners = function(name) {
7689 return this._listeners[name];
7690 };
7691
7692 EventBus.prototype._setListeners = function(name, listener) {
7693 this._listeners[name] = listener;
7694 };
7695
7696 EventBus.prototype._removeListener = function(event, callback) {
7697
7698 var listener = this._getListeners(event),
7699 nextListener,
7700 previousListener,
7701 listenerCallback;
7702
7703 if (!callback) {
7704
7705 // clear listeners
7706 this._setListeners(event, null);
7707
7708 return;
7709 }
7710
7711 while (listener) {
7712
7713 nextListener = listener.next;
7714
7715 listenerCallback = listener.callback;
7716
7717 if (listenerCallback === callback || listenerCallback[FN_REF] === callback) {
7718 if (previousListener) {
7719 previousListener.next = nextListener;
7720 } else {
7721
7722 // new first listener
7723 this._setListeners(event, nextListener);
7724 }
7725 }
7726
7727 previousListener = listener;
7728 listener = nextListener;
7729 }
7730 };
7731
7732 /**
7733 * A event that is emitted via the event bus.
7734 */
7735 function InternalEvent() { }
7736
7737 InternalEvent.prototype.stopPropagation = function() {
7738 this.cancelBubble = true;
7739 };
7740
7741 InternalEvent.prototype.preventDefault = function() {
7742 this.defaultPrevented = true;
7743 };
7744
7745 InternalEvent.prototype.init = function(data) {
7746 assign(this, data || {});
7747 };
7748
7749
7750 /**
7751 * Invoke function. Be fast...
7752 *
7753 * @param {Function} fn
7754 * @param {Array<Object>} args
7755 *
7756 * @return {Any}
7757 */
7758 function invokeFunction(fn, args) {
7759 return fn.apply(null, args);
7760 }
7761
7762 /**
7763 * SVGs for elements are generated by the {@link GraphicsFactory}.
7764 *
7765 * This utility gives quick access to the important semantic
7766 * parts of an element.
7767 */
7768
7769 /**
7770 * Returns the visual part of a diagram element
7771 *
7772 * @param {Snap<SVGElement>} gfx
7773 *
7774 * @return {Snap<SVGElement>}
7775 */
7776 function getVisual(gfx) {
7777 return gfx.childNodes[0];
7778 }
7779
7780 /**
7781 * Returns the children for a given diagram element.
7782 *
7783 * @param {Snap<SVGElement>} gfx
7784 * @return {Snap<SVGElement>}
7785 */
7786 function getChildren$1(gfx) {
7787 return gfx.parentNode.childNodes[1];
7788 }
7789
7790 /**
7791 * @param {<SVGElement>} element
7792 * @param {number} x
7793 * @param {number} y
7794 * @param {number} angle
7795 * @param {number} amount
7796 */
7797 function transform(gfx, x, y, angle, amount) {
7798 var translate = createTransform();
7799 translate.setTranslate(x, y);
7800
7801 var rotate = createTransform();
7802 rotate.setRotate(angle || 0, 0, 0);
7803
7804 var scale = createTransform();
7805 scale.setScale(amount || 1, amount || 1);
7806
7807 transform$1(gfx, [ translate, rotate, scale ]);
7808 }
7809
7810
7811 /**
7812 * @param {SVGElement} element
7813 * @param {number} x
7814 * @param {number} y
7815 */
7816 function translate$2(gfx, x, y) {
7817 var translate = createTransform();
7818 translate.setTranslate(x, y);
7819
7820 transform$1(gfx, translate);
7821 }
7822
7823
7824 /**
7825 * @param {SVGElement} element
7826 * @param {number} angle
7827 */
7828 function rotate(gfx, angle) {
7829 var rotate = createTransform();
7830 rotate.setRotate(angle, 0, 0);
7831
7832 transform$1(gfx, rotate);
7833 }
7834
7835 /**
7836 * A factory that creates graphical elements
7837 *
7838 * @param {EventBus} eventBus
7839 * @param {ElementRegistry} elementRegistry
7840 */
7841 function GraphicsFactory(eventBus, elementRegistry) {
7842 this._eventBus = eventBus;
7843 this._elementRegistry = elementRegistry;
7844 }
7845
7846 GraphicsFactory.$inject = [ 'eventBus' , 'elementRegistry' ];
7847
7848
7849 GraphicsFactory.prototype._getChildrenContainer = function(element) {
7850
7851 var gfx = this._elementRegistry.getGraphics(element);
7852
7853 var childrenGfx;
7854
7855 // root element
7856 if (!element.parent) {
7857 childrenGfx = gfx;
7858 } else {
7859 childrenGfx = getChildren$1(gfx);
7860 if (!childrenGfx) {
7861 childrenGfx = create$1('g');
7862 classes$1(childrenGfx).add('djs-children');
7863
7864 append(gfx.parentNode, childrenGfx);
7865 }
7866 }
7867
7868 return childrenGfx;
7869 };
7870
7871 /**
7872 * Clears the graphical representation of the element and returns the
7873 * cleared visual (the <g class="djs-visual" /> element).
7874 */
7875 GraphicsFactory.prototype._clear = function(gfx) {
7876 var visual = getVisual(gfx);
7877
7878 clear(visual);
7879
7880 return visual;
7881 };
7882
7883 /**
7884 * Creates a gfx container for shapes and connections
7885 *
7886 * The layout is as follows:
7887 *
7888 * <g class="djs-group">
7889 *
7890 * <!-- the gfx -->
7891 * <g class="djs-element djs-(shape|connection|frame)">
7892 * <g class="djs-visual">
7893 * <!-- the renderer draws in here -->
7894 * </g>
7895 *
7896 * <!-- extensions (overlays, click box, ...) goes here
7897 * </g>
7898 *
7899 * <!-- the gfx child nodes -->
7900 * <g class="djs-children"></g>
7901 * </g>
7902 *
7903 * @param {string} type the type of the element, i.e. shape | connection
7904 * @param {SVGElement} [childrenGfx]
7905 * @param {number} [parentIndex] position to create container in parent
7906 * @param {boolean} [isFrame] is frame element
7907 *
7908 * @return {SVGElement}
7909 */
7910 GraphicsFactory.prototype._createContainer = function(
7911 type, childrenGfx, parentIndex, isFrame
7912 ) {
7913 var outerGfx = create$1('g');
7914 classes$1(outerGfx).add('djs-group');
7915
7916 // insert node at position
7917 if (typeof parentIndex !== 'undefined') {
7918 prependTo(outerGfx, childrenGfx, childrenGfx.childNodes[parentIndex]);
7919 } else {
7920 append(childrenGfx, outerGfx);
7921 }
7922
7923 var gfx = create$1('g');
7924 classes$1(gfx).add('djs-element');
7925 classes$1(gfx).add('djs-' + type);
7926
7927 if (isFrame) {
7928 classes$1(gfx).add('djs-frame');
7929 }
7930
7931 append(outerGfx, gfx);
7932
7933 // create visual
7934 var visual = create$1('g');
7935 classes$1(visual).add('djs-visual');
7936
7937 append(gfx, visual);
7938
7939 return gfx;
7940 };
7941
7942 GraphicsFactory.prototype.create = function(type, element, parentIndex) {
7943 var childrenGfx = this._getChildrenContainer(element.parent);
7944 return this._createContainer(type, childrenGfx, parentIndex, isFrameElement$1(element));
7945 };
7946
7947 GraphicsFactory.prototype.updateContainments = function(elements) {
7948
7949 var self = this,
7950 elementRegistry = this._elementRegistry,
7951 parents;
7952
7953 parents = reduce(elements, function(map, e) {
7954
7955 if (e.parent) {
7956 map[e.parent.id] = e.parent;
7957 }
7958
7959 return map;
7960 }, {});
7961
7962 // update all parents of changed and reorganized their children
7963 // in the correct order (as indicated in our model)
7964 forEach$2(parents, function(parent) {
7965
7966 var children = parent.children;
7967
7968 if (!children) {
7969 return;
7970 }
7971
7972 var childrenGfx = self._getChildrenContainer(parent);
7973
7974 forEach$2(children.slice().reverse(), function(child) {
7975 var childGfx = elementRegistry.getGraphics(child);
7976
7977 prependTo(childGfx.parentNode, childrenGfx);
7978 });
7979 });
7980 };
7981
7982 GraphicsFactory.prototype.drawShape = function(visual, element) {
7983 var eventBus = this._eventBus;
7984
7985 return eventBus.fire('render.shape', { gfx: visual, element: element });
7986 };
7987
7988 GraphicsFactory.prototype.getShapePath = function(element) {
7989 var eventBus = this._eventBus;
7990
7991 return eventBus.fire('render.getShapePath', element);
7992 };
7993
7994 GraphicsFactory.prototype.drawConnection = function(visual, element) {
7995 var eventBus = this._eventBus;
7996
7997 return eventBus.fire('render.connection', { gfx: visual, element: element });
7998 };
7999
8000 GraphicsFactory.prototype.getConnectionPath = function(waypoints) {
8001 var eventBus = this._eventBus;
8002
8003 return eventBus.fire('render.getConnectionPath', waypoints);
8004 };
8005
8006 GraphicsFactory.prototype.update = function(type, element, gfx) {
8007
8008 // do NOT update root element
8009 if (!element.parent) {
8010 return;
8011 }
8012
8013 var visual = this._clear(gfx);
8014
8015 // redraw
8016 if (type === 'shape') {
8017 this.drawShape(visual, element);
8018
8019 // update positioning
8020 translate$2(gfx, element.x, element.y);
8021 } else
8022 if (type === 'connection') {
8023 this.drawConnection(visual, element);
8024 } else {
8025 throw new Error('unknown type: ' + type);
8026 }
8027
8028 if (element.hidden) {
8029 attr$1(gfx, 'display', 'none');
8030 } else {
8031 attr$1(gfx, 'display', 'block');
8032 }
8033 };
8034
8035 GraphicsFactory.prototype.remove = function(element) {
8036 var gfx = this._elementRegistry.getGraphics(element);
8037
8038 // remove
8039 remove$2(gfx.parentNode);
8040 };
8041
8042
8043 // helpers //////////
8044
8045 function prependTo(newNode, parentNode, siblingNode) {
8046 var node = siblingNode || parentNode.firstChild;
8047
8048 // do not prepend node to itself to prevent IE from crashing
8049 // https://github.com/bpmn-io/bpmn-js/issues/746
8050 if (newNode === node) {
8051 return;
8052 }
8053
8054 parentNode.insertBefore(newNode, node);
8055 }
8056
8057 var CoreModule$1 = {
8058 __depends__: [ DrawModule$1 ],
8059 __init__: [ 'canvas' ],
8060 canvas: [ 'type', Canvas ],
8061 elementRegistry: [ 'type', ElementRegistry ],
8062 elementFactory: [ 'type', ElementFactory$1 ],
8063 eventBus: [ 'type', EventBus ],
8064 graphicsFactory: [ 'type', GraphicsFactory ]
8065 };
8066
8067 /**
8068 * Bootstrap an injector from a list of modules, instantiating a number of default components
8069 *
8070 * @ignore
8071 * @param {Array<didi.Module>} bootstrapModules
8072 *
8073 * @return {didi.Injector} a injector to use to access the components
8074 */
8075 function bootstrap(bootstrapModules) {
8076
8077 var modules = [],
8078 components = [];
8079
8080 function hasModule(m) {
8081 return modules.indexOf(m) >= 0;
8082 }
8083
8084 function addModule(m) {
8085 modules.push(m);
8086 }
8087
8088 function visit(m) {
8089 if (hasModule(m)) {
8090 return;
8091 }
8092
8093 (m.__depends__ || []).forEach(visit);
8094
8095 if (hasModule(m)) {
8096 return;
8097 }
8098
8099 addModule(m);
8100
8101 (m.__init__ || []).forEach(function(c) {
8102 components.push(c);
8103 });
8104 }
8105
8106 bootstrapModules.forEach(visit);
8107
8108 var injector = new Injector(modules);
8109
8110 components.forEach(function(c) {
8111
8112 try {
8113
8114 // eagerly resolve component (fn or string)
8115 injector[typeof c === 'string' ? 'get' : 'invoke'](c);
8116 } catch (e) {
8117 console.error('Failed to instantiate component');
8118 console.error(e.stack);
8119
8120 throw e;
8121 }
8122 });
8123
8124 return injector;
8125 }
8126
8127 /**
8128 * Creates an injector from passed options.
8129 *
8130 * @ignore
8131 * @param {Object} options
8132 * @return {didi.Injector}
8133 */
8134 function createInjector(options) {
8135
8136 options = options || {};
8137
8138 var configModule = {
8139 'config': [ 'value', options ]
8140 };
8141
8142 var modules = [ configModule, CoreModule$1 ].concat(options.modules || []);
8143
8144 return bootstrap(modules);
8145 }
8146
8147
8148 /**
8149 * The main diagram-js entry point that bootstraps the diagram with the given
8150 * configuration.
8151 *
8152 * To register extensions with the diagram, pass them as Array<didi.Module> to the constructor.
8153 *
8154 * @class djs.Diagram
8155 * @memberOf djs
8156 * @constructor
8157 *
8158 * @example
8159 *
8160 * <caption>Creating a plug-in that logs whenever a shape is added to the canvas.</caption>
8161 *
8162 * // plug-in implemenentation
8163 * function MyLoggingPlugin(eventBus) {
8164 * eventBus.on('shape.added', function(event) {
8165 * console.log('shape ', event.shape, ' was added to the diagram');
8166 * });
8167 * }
8168 *
8169 * // export as module
8170 * export default {
8171 * __init__: [ 'myLoggingPlugin' ],
8172 * myLoggingPlugin: [ 'type', MyLoggingPlugin ]
8173 * };
8174 *
8175 *
8176 * // instantiate the diagram with the new plug-in
8177 *
8178 * import MyLoggingModule from 'path-to-my-logging-plugin';
8179 *
8180 * var diagram = new Diagram({
8181 * modules: [
8182 * MyLoggingModule
8183 * ]
8184 * });
8185 *
8186 * diagram.invoke([ 'canvas', function(canvas) {
8187 * // add shape to drawing canvas
8188 * canvas.addShape({ x: 10, y: 10 });
8189 * });
8190 *
8191 * // 'shape ... was added to the diagram' logged to console
8192 *
8193 * @param {Object} options
8194 * @param {Array<didi.Module>} [options.modules] external modules to instantiate with the diagram
8195 * @param {didi.Injector} [injector] an (optional) injector to bootstrap the diagram with
8196 */
8197 function Diagram(options, injector) {
8198
8199 // create injector unless explicitly specified
8200 this.injector = injector = injector || createInjector(options);
8201
8202 // API
8203
8204 /**
8205 * Resolves a diagram service
8206 *
8207 * @method Diagram#get
8208 *
8209 * @param {string} name the name of the diagram service to be retrieved
8210 * @param {boolean} [strict=true] if false, resolve missing services to null
8211 */
8212 this.get = injector.get;
8213
8214 /**
8215 * Executes a function into which diagram services are injected
8216 *
8217 * @method Diagram#invoke
8218 *
8219 * @param {Function|Object[]} fn the function to resolve
8220 * @param {Object} locals a number of locals to use to resolve certain dependencies
8221 */
8222 this.invoke = injector.invoke;
8223
8224 // init
8225
8226 // indicate via event
8227
8228
8229 /**
8230 * An event indicating that all plug-ins are loaded.
8231 *
8232 * Use this event to fire other events to interested plug-ins
8233 *
8234 * @memberOf Diagram
8235 *
8236 * @event diagram.init
8237 *
8238 * @example
8239 *
8240 * eventBus.on('diagram.init', function() {
8241 * eventBus.fire('my-custom-event', { foo: 'BAR' });
8242 * });
8243 *
8244 * @type {Object}
8245 */
8246 this.get('eventBus').fire('diagram.init');
8247 }
8248
8249
8250 /**
8251 * Destroys the diagram
8252 *
8253 * @method Diagram#destroy
8254 */
8255 Diagram.prototype.destroy = function() {
8256 this.get('eventBus').fire('diagram.destroy');
8257 };
8258
8259 /**
8260 * Clear the diagram, removing all contents.
8261 */
8262 Diagram.prototype.clear = function() {
8263 this.get('eventBus').fire('diagram.clear');
8264 };
8265
8266 /**
8267 * Moddle base element.
8268 */
8269 function Base() { }
8270
8271 Base.prototype.get = function(name) {
8272 return this.$model.properties.get(this, name);
8273 };
8274
8275 Base.prototype.set = function(name, value) {
8276 this.$model.properties.set(this, name, value);
8277 };
8278
8279 /**
8280 * A model element factory.
8281 *
8282 * @param {Moddle} model
8283 * @param {Properties} properties
8284 */
8285 function Factory(model, properties) {
8286 this.model = model;
8287 this.properties = properties;
8288 }
8289
8290
8291 Factory.prototype.createType = function(descriptor) {
8292
8293 var model = this.model;
8294
8295 var props = this.properties,
8296 prototype = Object.create(Base.prototype);
8297
8298 // initialize default values
8299 forEach$2(descriptor.properties, function(p) {
8300 if (!p.isMany && p.default !== undefined) {
8301 prototype[p.name] = p.default;
8302 }
8303 });
8304
8305 props.defineModel(prototype, model);
8306 props.defineDescriptor(prototype, descriptor);
8307
8308 var name = descriptor.ns.name;
8309
8310 /**
8311 * The new type constructor
8312 */
8313 function ModdleElement(attrs) {
8314 props.define(this, '$type', { value: name, enumerable: true });
8315 props.define(this, '$attrs', { value: {} });
8316 props.define(this, '$parent', { writable: true });
8317
8318 forEach$2(attrs, bind(function(val, key) {
8319 this.set(key, val);
8320 }, this));
8321 }
8322
8323 ModdleElement.prototype = prototype;
8324
8325 ModdleElement.hasType = prototype.$instanceOf = this.model.hasType;
8326
8327 // static links
8328 props.defineModel(ModdleElement, model);
8329 props.defineDescriptor(ModdleElement, descriptor);
8330
8331 return ModdleElement;
8332 };
8333
8334 /**
8335 * Built-in moddle types
8336 */
8337 var BUILTINS = {
8338 String: true,
8339 Boolean: true,
8340 Integer: true,
8341 Real: true,
8342 Element: true
8343 };
8344
8345 /**
8346 * Converters for built in types from string representations
8347 */
8348 var TYPE_CONVERTERS = {
8349 String: function(s) { return s; },
8350 Boolean: function(s) { return s === 'true'; },
8351 Integer: function(s) { return parseInt(s, 10); },
8352 Real: function(s) { return parseFloat(s); }
8353 };
8354
8355 /**
8356 * Convert a type to its real representation
8357 */
8358 function coerceType(type, value) {
8359
8360 var converter = TYPE_CONVERTERS[type];
8361
8362 if (converter) {
8363 return converter(value);
8364 } else {
8365 return value;
8366 }
8367 }
8368
8369 /**
8370 * Return whether the given type is built-in
8371 */
8372 function isBuiltIn(type) {
8373 return !!BUILTINS[type];
8374 }
8375
8376 /**
8377 * Return whether the given type is simple
8378 */
8379 function isSimple(type) {
8380 return !!TYPE_CONVERTERS[type];
8381 }
8382
8383 /**
8384 * Parses a namespaced attribute name of the form (ns:)localName to an object,
8385 * given a default prefix to assume in case no explicit namespace is given.
8386 *
8387 * @param {String} name
8388 * @param {String} [defaultPrefix] the default prefix to take, if none is present.
8389 *
8390 * @return {Object} the parsed name
8391 */
8392 function parseName(name, defaultPrefix) {
8393 var parts = name.split(/:/),
8394 localName, prefix;
8395
8396 // no prefix (i.e. only local name)
8397 if (parts.length === 1) {
8398 localName = name;
8399 prefix = defaultPrefix;
8400 } else
8401 // prefix + local name
8402 if (parts.length === 2) {
8403 localName = parts[1];
8404 prefix = parts[0];
8405 } else {
8406 throw new Error('expected <prefix:localName> or <localName>, got ' + name);
8407 }
8408
8409 name = (prefix ? prefix + ':' : '') + localName;
8410
8411 return {
8412 name: name,
8413 prefix: prefix,
8414 localName: localName
8415 };
8416 }
8417
8418 /**
8419 * A utility to build element descriptors.
8420 */
8421 function DescriptorBuilder(nameNs) {
8422 this.ns = nameNs;
8423 this.name = nameNs.name;
8424 this.allTypes = [];
8425 this.allTypesByName = {};
8426 this.properties = [];
8427 this.propertiesByName = {};
8428 }
8429
8430
8431 DescriptorBuilder.prototype.build = function() {
8432 return pick(this, [
8433 'ns',
8434 'name',
8435 'allTypes',
8436 'allTypesByName',
8437 'properties',
8438 'propertiesByName',
8439 'bodyProperty',
8440 'idProperty'
8441 ]);
8442 };
8443
8444 /**
8445 * Add property at given index.
8446 *
8447 * @param {Object} p
8448 * @param {Number} [idx]
8449 * @param {Boolean} [validate=true]
8450 */
8451 DescriptorBuilder.prototype.addProperty = function(p, idx, validate) {
8452
8453 if (typeof idx === 'boolean') {
8454 validate = idx;
8455 idx = undefined;
8456 }
8457
8458 this.addNamedProperty(p, validate !== false);
8459
8460 var properties = this.properties;
8461
8462 if (idx !== undefined) {
8463 properties.splice(idx, 0, p);
8464 } else {
8465 properties.push(p);
8466 }
8467 };
8468
8469
8470 DescriptorBuilder.prototype.replaceProperty = function(oldProperty, newProperty, replace) {
8471 var oldNameNs = oldProperty.ns;
8472
8473 var props = this.properties,
8474 propertiesByName = this.propertiesByName,
8475 rename = oldProperty.name !== newProperty.name;
8476
8477 if (oldProperty.isId) {
8478 if (!newProperty.isId) {
8479 throw new Error(
8480 'property <' + newProperty.ns.name + '> must be id property ' +
8481 'to refine <' + oldProperty.ns.name + '>');
8482 }
8483
8484 this.setIdProperty(newProperty, false);
8485 }
8486
8487 if (oldProperty.isBody) {
8488
8489 if (!newProperty.isBody) {
8490 throw new Error(
8491 'property <' + newProperty.ns.name + '> must be body property ' +
8492 'to refine <' + oldProperty.ns.name + '>');
8493 }
8494
8495 // TODO: Check compatibility
8496 this.setBodyProperty(newProperty, false);
8497 }
8498
8499 // validate existence and get location of old property
8500 var idx = props.indexOf(oldProperty);
8501 if (idx === -1) {
8502 throw new Error('property <' + oldNameNs.name + '> not found in property list');
8503 }
8504
8505 // remove old property
8506 props.splice(idx, 1);
8507
8508 // replacing the named property is intentional
8509 //
8510 // * validate only if this is a "rename" operation
8511 // * add at specific index unless we "replace"
8512 //
8513 this.addProperty(newProperty, replace ? undefined : idx, rename);
8514
8515 // make new property available under old name
8516 propertiesByName[oldNameNs.name] = propertiesByName[oldNameNs.localName] = newProperty;
8517 };
8518
8519
8520 DescriptorBuilder.prototype.redefineProperty = function(p, targetPropertyName, replace) {
8521
8522 var nsPrefix = p.ns.prefix;
8523 var parts = targetPropertyName.split('#');
8524
8525 var name = parseName(parts[0], nsPrefix);
8526 var attrName = parseName(parts[1], name.prefix).name;
8527
8528 var redefinedProperty = this.propertiesByName[attrName];
8529 if (!redefinedProperty) {
8530 throw new Error('refined property <' + attrName + '> not found');
8531 } else {
8532 this.replaceProperty(redefinedProperty, p, replace);
8533 }
8534
8535 delete p.redefines;
8536 };
8537
8538 DescriptorBuilder.prototype.addNamedProperty = function(p, validate) {
8539 var ns = p.ns,
8540 propsByName = this.propertiesByName;
8541
8542 if (validate) {
8543 this.assertNotDefined(p, ns.name);
8544 this.assertNotDefined(p, ns.localName);
8545 }
8546
8547 propsByName[ns.name] = propsByName[ns.localName] = p;
8548 };
8549
8550 DescriptorBuilder.prototype.removeNamedProperty = function(p) {
8551 var ns = p.ns,
8552 propsByName = this.propertiesByName;
8553
8554 delete propsByName[ns.name];
8555 delete propsByName[ns.localName];
8556 };
8557
8558 DescriptorBuilder.prototype.setBodyProperty = function(p, validate) {
8559
8560 if (validate && this.bodyProperty) {
8561 throw new Error(
8562 'body property defined multiple times ' +
8563 '(<' + this.bodyProperty.ns.name + '>, <' + p.ns.name + '>)');
8564 }
8565
8566 this.bodyProperty = p;
8567 };
8568
8569 DescriptorBuilder.prototype.setIdProperty = function(p, validate) {
8570
8571 if (validate && this.idProperty) {
8572 throw new Error(
8573 'id property defined multiple times ' +
8574 '(<' + this.idProperty.ns.name + '>, <' + p.ns.name + '>)');
8575 }
8576
8577 this.idProperty = p;
8578 };
8579
8580 DescriptorBuilder.prototype.assertNotDefined = function(p, name) {
8581 var propertyName = p.name,
8582 definedProperty = this.propertiesByName[propertyName];
8583
8584 if (definedProperty) {
8585 throw new Error(
8586 'property <' + propertyName + '> already defined; ' +
8587 'override of <' + definedProperty.definedBy.ns.name + '#' + definedProperty.ns.name + '> by ' +
8588 '<' + p.definedBy.ns.name + '#' + p.ns.name + '> not allowed without redefines');
8589 }
8590 };
8591
8592 DescriptorBuilder.prototype.hasProperty = function(name) {
8593 return this.propertiesByName[name];
8594 };
8595
8596 DescriptorBuilder.prototype.addTrait = function(t, inherited) {
8597
8598 var typesByName = this.allTypesByName,
8599 types = this.allTypes;
8600
8601 var typeName = t.name;
8602
8603 if (typeName in typesByName) {
8604 return;
8605 }
8606
8607 forEach$2(t.properties, bind(function(p) {
8608
8609 // clone property to allow extensions
8610 p = assign({}, p, {
8611 name: p.ns.localName,
8612 inherited: inherited
8613 });
8614
8615 Object.defineProperty(p, 'definedBy', {
8616 value: t
8617 });
8618
8619 var replaces = p.replaces,
8620 redefines = p.redefines;
8621
8622 // add replace/redefine support
8623 if (replaces || redefines) {
8624 this.redefineProperty(p, replaces || redefines, replaces);
8625 } else {
8626 if (p.isBody) {
8627 this.setBodyProperty(p);
8628 }
8629 if (p.isId) {
8630 this.setIdProperty(p);
8631 }
8632 this.addProperty(p);
8633 }
8634 }, this));
8635
8636 types.push(t);
8637 typesByName[typeName] = t;
8638 };
8639
8640 /**
8641 * A registry of Moddle packages.
8642 *
8643 * @param {Array<Package>} packages
8644 * @param {Properties} properties
8645 */
8646 function Registry(packages, properties) {
8647 this.packageMap = {};
8648 this.typeMap = {};
8649
8650 this.packages = [];
8651
8652 this.properties = properties;
8653
8654 forEach$2(packages, bind(this.registerPackage, this));
8655 }
8656
8657
8658 Registry.prototype.getPackage = function(uriOrPrefix) {
8659 return this.packageMap[uriOrPrefix];
8660 };
8661
8662 Registry.prototype.getPackages = function() {
8663 return this.packages;
8664 };
8665
8666
8667 Registry.prototype.registerPackage = function(pkg) {
8668
8669 // copy package
8670 pkg = assign({}, pkg);
8671
8672 var pkgMap = this.packageMap;
8673
8674 ensureAvailable(pkgMap, pkg, 'prefix');
8675 ensureAvailable(pkgMap, pkg, 'uri');
8676
8677 // register types
8678 forEach$2(pkg.types, bind(function(descriptor) {
8679 this.registerType(descriptor, pkg);
8680 }, this));
8681
8682 pkgMap[pkg.uri] = pkgMap[pkg.prefix] = pkg;
8683 this.packages.push(pkg);
8684 };
8685
8686
8687 /**
8688 * Register a type from a specific package with us
8689 */
8690 Registry.prototype.registerType = function(type, pkg) {
8691
8692 type = assign({}, type, {
8693 superClass: (type.superClass || []).slice(),
8694 extends: (type.extends || []).slice(),
8695 properties: (type.properties || []).slice(),
8696 meta: assign((type.meta || {}))
8697 });
8698
8699 var ns = parseName(type.name, pkg.prefix),
8700 name = ns.name,
8701 propertiesByName = {};
8702
8703 // parse properties
8704 forEach$2(type.properties, bind(function(p) {
8705
8706 // namespace property names
8707 var propertyNs = parseName(p.name, ns.prefix),
8708 propertyName = propertyNs.name;
8709
8710 // namespace property types
8711 if (!isBuiltIn(p.type)) {
8712 p.type = parseName(p.type, propertyNs.prefix).name;
8713 }
8714
8715 assign(p, {
8716 ns: propertyNs,
8717 name: propertyName
8718 });
8719
8720 propertiesByName[propertyName] = p;
8721 }, this));
8722
8723 // update ns + name
8724 assign(type, {
8725 ns: ns,
8726 name: name,
8727 propertiesByName: propertiesByName
8728 });
8729
8730 forEach$2(type.extends, bind(function(extendsName) {
8731 var extended = this.typeMap[extendsName];
8732
8733 extended.traits = extended.traits || [];
8734 extended.traits.push(name);
8735 }, this));
8736
8737 // link to package
8738 this.definePackage(type, pkg);
8739
8740 // register
8741 this.typeMap[name] = type;
8742 };
8743
8744
8745 /**
8746 * Traverse the type hierarchy from bottom to top,
8747 * calling iterator with (type, inherited) for all elements in
8748 * the inheritance chain.
8749 *
8750 * @param {Object} nsName
8751 * @param {Function} iterator
8752 * @param {Boolean} [trait=false]
8753 */
8754 Registry.prototype.mapTypes = function(nsName, iterator, trait) {
8755
8756 var type = isBuiltIn(nsName.name) ? { name: nsName.name } : this.typeMap[nsName.name];
8757
8758 var self = this;
8759
8760 /**
8761 * Traverse the selected trait.
8762 *
8763 * @param {String} cls
8764 */
8765 function traverseTrait(cls) {
8766 return traverseSuper(cls, true);
8767 }
8768
8769 /**
8770 * Traverse the selected super type or trait
8771 *
8772 * @param {String} cls
8773 * @param {Boolean} [trait=false]
8774 */
8775 function traverseSuper(cls, trait) {
8776 var parentNs = parseName(cls, isBuiltIn(cls) ? '' : nsName.prefix);
8777 self.mapTypes(parentNs, iterator, trait);
8778 }
8779
8780 if (!type) {
8781 throw new Error('unknown type <' + nsName.name + '>');
8782 }
8783
8784 forEach$2(type.superClass, trait ? traverseTrait : traverseSuper);
8785
8786 // call iterator with (type, inherited=!trait)
8787 iterator(type, !trait);
8788
8789 forEach$2(type.traits, traverseTrait);
8790 };
8791
8792
8793 /**
8794 * Returns the effective descriptor for a type.
8795 *
8796 * @param {String} type the namespaced name (ns:localName) of the type
8797 *
8798 * @return {Descriptor} the resulting effective descriptor
8799 */
8800 Registry.prototype.getEffectiveDescriptor = function(name) {
8801
8802 var nsName = parseName(name);
8803
8804 var builder = new DescriptorBuilder(nsName);
8805
8806 this.mapTypes(nsName, function(type, inherited) {
8807 builder.addTrait(type, inherited);
8808 });
8809
8810 var descriptor = builder.build();
8811
8812 // define package link
8813 this.definePackage(descriptor, descriptor.allTypes[descriptor.allTypes.length - 1].$pkg);
8814
8815 return descriptor;
8816 };
8817
8818
8819 Registry.prototype.definePackage = function(target, pkg) {
8820 this.properties.define(target, '$pkg', { value: pkg });
8821 };
8822
8823
8824
8825 ///////// helpers ////////////////////////////
8826
8827 function ensureAvailable(packageMap, pkg, identifierKey) {
8828
8829 var value = pkg[identifierKey];
8830
8831 if (value in packageMap) {
8832 throw new Error('package with ' + identifierKey + ' <' + value + '> already defined');
8833 }
8834 }
8835
8836 /**
8837 * A utility that gets and sets properties of model elements.
8838 *
8839 * @param {Model} model
8840 */
8841 function Properties(model) {
8842 this.model = model;
8843 }
8844
8845
8846 /**
8847 * Sets a named property on the target element.
8848 * If the value is undefined, the property gets deleted.
8849 *
8850 * @param {Object} target
8851 * @param {String} name
8852 * @param {Object} value
8853 */
8854 Properties.prototype.set = function(target, name, value) {
8855
8856 var property = this.model.getPropertyDescriptor(target, name);
8857
8858 var propertyName = property && property.name;
8859
8860 if (isUndefined(value)) {
8861 // unset the property, if the specified value is undefined;
8862 // delete from $attrs (for extensions) or the target itself
8863 if (property) {
8864 delete target[propertyName];
8865 } else {
8866 delete target.$attrs[name];
8867 }
8868 } else {
8869 // set the property, defining well defined properties on the fly
8870 // or simply updating them in target.$attrs (for extensions)
8871 if (property) {
8872 if (propertyName in target) {
8873 target[propertyName] = value;
8874 } else {
8875 defineProperty(target, property, value);
8876 }
8877 } else {
8878 target.$attrs[name] = value;
8879 }
8880 }
8881 };
8882
8883 /**
8884 * Returns the named property of the given element
8885 *
8886 * @param {Object} target
8887 * @param {String} name
8888 *
8889 * @return {Object}
8890 */
8891 Properties.prototype.get = function(target, name) {
8892
8893 var property = this.model.getPropertyDescriptor(target, name);
8894
8895 if (!property) {
8896 return target.$attrs[name];
8897 }
8898
8899 var propertyName = property.name;
8900
8901 // check if access to collection property and lazily initialize it
8902 if (!target[propertyName] && property.isMany) {
8903 defineProperty(target, property, []);
8904 }
8905
8906 return target[propertyName];
8907 };
8908
8909
8910 /**
8911 * Define a property on the target element
8912 *
8913 * @param {Object} target
8914 * @param {String} name
8915 * @param {Object} options
8916 */
8917 Properties.prototype.define = function(target, name, options) {
8918 Object.defineProperty(target, name, options);
8919 };
8920
8921
8922 /**
8923 * Define the descriptor for an element
8924 */
8925 Properties.prototype.defineDescriptor = function(target, descriptor) {
8926 this.define(target, '$descriptor', { value: descriptor });
8927 };
8928
8929 /**
8930 * Define the model for an element
8931 */
8932 Properties.prototype.defineModel = function(target, model) {
8933 this.define(target, '$model', { value: model });
8934 };
8935
8936
8937 function isUndefined(val) {
8938 return typeof val === 'undefined';
8939 }
8940
8941 function defineProperty(target, property, value) {
8942 Object.defineProperty(target, property.name, {
8943 enumerable: !property.isReference,
8944 writable: true,
8945 value: value,
8946 configurable: true
8947 });
8948 }
8949
8950 //// Moddle implementation /////////////////////////////////////////////////
8951
8952 /**
8953 * @class Moddle
8954 *
8955 * A model that can be used to create elements of a specific type.
8956 *
8957 * @example
8958 *
8959 * var Moddle = require('moddle');
8960 *
8961 * var pkg = {
8962 * name: 'mypackage',
8963 * prefix: 'my',
8964 * types: [
8965 * { name: 'Root' }
8966 * ]
8967 * };
8968 *
8969 * var moddle = new Moddle([pkg]);
8970 *
8971 * @param {Array<Package>} packages the packages to contain
8972 */
8973 function Moddle(packages) {
8974
8975 this.properties = new Properties(this);
8976
8977 this.factory = new Factory(this, this.properties);
8978 this.registry = new Registry(packages, this.properties);
8979
8980 this.typeCache = {};
8981 }
8982
8983
8984 /**
8985 * Create an instance of the specified type.
8986 *
8987 * @method Moddle#create
8988 *
8989 * @example
8990 *
8991 * var foo = moddle.create('my:Foo');
8992 * var bar = moddle.create('my:Bar', { id: 'BAR_1' });
8993 *
8994 * @param {String|Object} descriptor the type descriptor or name know to the model
8995 * @param {Object} attrs a number of attributes to initialize the model instance with
8996 * @return {Object} model instance
8997 */
8998 Moddle.prototype.create = function(descriptor, attrs) {
8999 var Type = this.getType(descriptor);
9000
9001 if (!Type) {
9002 throw new Error('unknown type <' + descriptor + '>');
9003 }
9004
9005 return new Type(attrs);
9006 };
9007
9008
9009 /**
9010 * Returns the type representing a given descriptor
9011 *
9012 * @method Moddle#getType
9013 *
9014 * @example
9015 *
9016 * var Foo = moddle.getType('my:Foo');
9017 * var foo = new Foo({ 'id' : 'FOO_1' });
9018 *
9019 * @param {String|Object} descriptor the type descriptor or name know to the model
9020 * @return {Object} the type representing the descriptor
9021 */
9022 Moddle.prototype.getType = function(descriptor) {
9023
9024 var cache = this.typeCache;
9025
9026 var name = isString(descriptor) ? descriptor : descriptor.ns.name;
9027
9028 var type = cache[name];
9029
9030 if (!type) {
9031 descriptor = this.registry.getEffectiveDescriptor(name);
9032 type = cache[name] = this.factory.createType(descriptor);
9033 }
9034
9035 return type;
9036 };
9037
9038
9039 /**
9040 * Creates an any-element type to be used within model instances.
9041 *
9042 * This can be used to create custom elements that lie outside the meta-model.
9043 * The created element contains all the meta-data required to serialize it
9044 * as part of meta-model elements.
9045 *
9046 * @method Moddle#createAny
9047 *
9048 * @example
9049 *
9050 * var foo = moddle.createAny('vendor:Foo', 'http://vendor', {
9051 * value: 'bar'
9052 * });
9053 *
9054 * var container = moddle.create('my:Container', 'http://my', {
9055 * any: [ foo ]
9056 * });
9057 *
9058 * // go ahead and serialize the stuff
9059 *
9060 *
9061 * @param {String} name the name of the element
9062 * @param {String} nsUri the namespace uri of the element
9063 * @param {Object} [properties] a map of properties to initialize the instance with
9064 * @return {Object} the any type instance
9065 */
9066 Moddle.prototype.createAny = function(name, nsUri, properties) {
9067
9068 var nameNs = parseName(name);
9069
9070 var element = {
9071 $type: name,
9072 $instanceOf: function(type) {
9073 return type === this.$type;
9074 }
9075 };
9076
9077 var descriptor = {
9078 name: name,
9079 isGeneric: true,
9080 ns: {
9081 prefix: nameNs.prefix,
9082 localName: nameNs.localName,
9083 uri: nsUri
9084 }
9085 };
9086
9087 this.properties.defineDescriptor(element, descriptor);
9088 this.properties.defineModel(element, this);
9089 this.properties.define(element, '$parent', { enumerable: false, writable: true });
9090 this.properties.define(element, '$instanceOf', { enumerable: false, writable: true });
9091
9092 forEach$2(properties, function(a, key) {
9093 if (isObject(a) && a.value !== undefined) {
9094 element[a.name] = a.value;
9095 } else {
9096 element[key] = a;
9097 }
9098 });
9099
9100 return element;
9101 };
9102
9103 /**
9104 * Returns a registered package by uri or prefix
9105 *
9106 * @return {Object} the package
9107 */
9108 Moddle.prototype.getPackage = function(uriOrPrefix) {
9109 return this.registry.getPackage(uriOrPrefix);
9110 };
9111
9112 /**
9113 * Returns a snapshot of all known packages
9114 *
9115 * @return {Object} the package
9116 */
9117 Moddle.prototype.getPackages = function() {
9118 return this.registry.getPackages();
9119 };
9120
9121 /**
9122 * Returns the descriptor for an element
9123 */
9124 Moddle.prototype.getElementDescriptor = function(element) {
9125 return element.$descriptor;
9126 };
9127
9128 /**
9129 * Returns true if the given descriptor or instance
9130 * represents the given type.
9131 *
9132 * May be applied to this, if element is omitted.
9133 */
9134 Moddle.prototype.hasType = function(element, type) {
9135 if (type === undefined) {
9136 type = element;
9137 element = this;
9138 }
9139
9140 var descriptor = element.$model.getElementDescriptor(element);
9141
9142 return (type in descriptor.allTypesByName);
9143 };
9144
9145 /**
9146 * Returns the descriptor of an elements named property
9147 */
9148 Moddle.prototype.getPropertyDescriptor = function(element, property) {
9149 return this.getElementDescriptor(element).propertiesByName[property];
9150 };
9151
9152 /**
9153 * Returns a mapped type's descriptor
9154 */
9155 Moddle.prototype.getTypeDescriptor = function(type) {
9156 return this.registry.typeMap[type];
9157 };
9158
9159 var fromCharCode = String.fromCharCode;
9160
9161 var hasOwnProperty = Object.prototype.hasOwnProperty;
9162
9163 var ENTITY_PATTERN = /&#(\d+);|&#x([0-9a-f]+);|&(\w+);/ig;
9164
9165 var ENTITY_MAPPING = {
9166 'amp': '&',
9167 'apos': '\'',
9168 'gt': '>',
9169 'lt': '<',
9170 'quot': '"'
9171 };
9172
9173 // map UPPERCASE variants of supported special chars
9174 Object.keys(ENTITY_MAPPING).forEach(function(k) {
9175 ENTITY_MAPPING[k.toUpperCase()] = ENTITY_MAPPING[k];
9176 });
9177
9178
9179 function replaceEntities(_, d, x, z) {
9180
9181 // reserved names, i.e. &nbsp;
9182 if (z) {
9183 if (hasOwnProperty.call(ENTITY_MAPPING, z)) {
9184 return ENTITY_MAPPING[z];
9185 } else {
9186
9187 // fall back to original value
9188 return '&' + z + ';';
9189 }
9190 }
9191
9192 // decimal encoded char
9193 if (d) {
9194 return fromCharCode(d);
9195 }
9196
9197 // hex encoded char
9198 return fromCharCode(parseInt(x, 16));
9199 }
9200
9201
9202 /**
9203 * A basic entity decoder that can decode a minimal
9204 * sub-set of reserved names (&amp;) as well as
9205 * hex (&#xaaf;) and decimal (&#1231;) encoded characters.
9206 *
9207 * @param {string} str
9208 *
9209 * @return {string} decoded string
9210 */
9211 function decodeEntities(s) {
9212 if (s.length > 3 && s.indexOf('&') !== -1) {
9213 return s.replace(ENTITY_PATTERN, replaceEntities);
9214 }
9215
9216 return s;
9217 }
9218
9219 var XSI_URI = 'http://www.w3.org/2001/XMLSchema-instance';
9220 var XSI_PREFIX = 'xsi';
9221 var XSI_TYPE$1 = 'xsi:type';
9222
9223 var NON_WHITESPACE_OUTSIDE_ROOT_NODE = 'non-whitespace outside of root node';
9224
9225 function error$2(msg) {
9226 return new Error(msg);
9227 }
9228
9229 function missingNamespaceForPrefix(prefix) {
9230 return 'missing namespace for prefix <' + prefix + '>';
9231 }
9232
9233 function getter(getFn) {
9234 return {
9235 'get': getFn,
9236 'enumerable': true
9237 };
9238 }
9239
9240 function cloneNsMatrix(nsMatrix) {
9241 var clone = {}, key;
9242 for (key in nsMatrix) {
9243 clone[key] = nsMatrix[key];
9244 }
9245 return clone;
9246 }
9247
9248 function uriPrefix(prefix) {
9249 return prefix + '$uri';
9250 }
9251
9252 function buildNsMatrix(nsUriToPrefix) {
9253 var nsMatrix = {},
9254 uri,
9255 prefix;
9256
9257 for (uri in nsUriToPrefix) {
9258 prefix = nsUriToPrefix[uri];
9259 nsMatrix[prefix] = prefix;
9260 nsMatrix[uriPrefix(prefix)] = uri;
9261 }
9262
9263 return nsMatrix;
9264 }
9265
9266 function noopGetContext() {
9267 return { 'line': 0, 'column': 0 };
9268 }
9269
9270 function throwFunc(err) {
9271 throw err;
9272 }
9273
9274 /**
9275 * Creates a new parser with the given options.
9276 *
9277 * @constructor
9278 *
9279 * @param {!Object<string, ?>=} options
9280 */
9281 function Parser(options) {
9282
9283 if (!this) {
9284 return new Parser(options);
9285 }
9286
9287 var proxy = options && options['proxy'];
9288
9289 var onText,
9290 onOpenTag,
9291 onCloseTag,
9292 onCDATA,
9293 onError = throwFunc,
9294 onWarning,
9295 onComment,
9296 onQuestion,
9297 onAttention;
9298
9299 var getContext = noopGetContext;
9300
9301 /**
9302 * Do we need to parse the current elements attributes for namespaces?
9303 *
9304 * @type {boolean}
9305 */
9306 var maybeNS = false;
9307
9308 /**
9309 * Do we process namespaces at all?
9310 *
9311 * @type {boolean}
9312 */
9313 var isNamespace = false;
9314
9315 /**
9316 * The caught error returned on parse end
9317 *
9318 * @type {Error}
9319 */
9320 var returnError = null;
9321
9322 /**
9323 * Should we stop parsing?
9324 *
9325 * @type {boolean}
9326 */
9327 var parseStop = false;
9328
9329 /**
9330 * A map of { uri: prefix } used by the parser.
9331 *
9332 * This map will ensure we can normalize prefixes during processing;
9333 * for each uri, only one prefix will be exposed to the handlers.
9334 *
9335 * @type {!Object<string, string>}}
9336 */
9337 var nsUriToPrefix;
9338
9339 /**
9340 * Handle parse error.
9341 *
9342 * @param {string|Error} err
9343 */
9344 function handleError(err) {
9345 if (!(err instanceof Error)) {
9346 err = error$2(err);
9347 }
9348
9349 returnError = err;
9350
9351 onError(err, getContext);
9352 }
9353
9354 /**
9355 * Handle parse error.
9356 *
9357 * @param {string|Error} err
9358 */
9359 function handleWarning(err) {
9360
9361 if (!onWarning) {
9362 return;
9363 }
9364
9365 if (!(err instanceof Error)) {
9366 err = error$2(err);
9367 }
9368
9369 onWarning(err, getContext);
9370 }
9371
9372 /**
9373 * Register parse listener.
9374 *
9375 * @param {string} name
9376 * @param {Function} cb
9377 *
9378 * @return {Parser}
9379 */
9380 this['on'] = function(name, cb) {
9381
9382 if (typeof cb !== 'function') {
9383 throw error$2('required args <name, cb>');
9384 }
9385
9386 switch (name) {
9387 case 'openTag': onOpenTag = cb; break;
9388 case 'text': onText = cb; break;
9389 case 'closeTag': onCloseTag = cb; break;
9390 case 'error': onError = cb; break;
9391 case 'warn': onWarning = cb; break;
9392 case 'cdata': onCDATA = cb; break;
9393 case 'attention': onAttention = cb; break; // <!XXXXX zzzz="eeee">
9394 case 'question': onQuestion = cb; break; // <? .... ?>
9395 case 'comment': onComment = cb; break;
9396 default:
9397 throw error$2('unsupported event: ' + name);
9398 }
9399
9400 return this;
9401 };
9402
9403 /**
9404 * Set the namespace to prefix mapping.
9405 *
9406 * @example
9407 *
9408 * parser.ns({
9409 * 'http://foo': 'foo',
9410 * 'http://bar': 'bar'
9411 * });
9412 *
9413 * @param {!Object<string, string>} nsMap
9414 *
9415 * @return {Parser}
9416 */
9417 this['ns'] = function(nsMap) {
9418
9419 if (typeof nsMap === 'undefined') {
9420 nsMap = {};
9421 }
9422
9423 if (typeof nsMap !== 'object') {
9424 throw error$2('required args <nsMap={}>');
9425 }
9426
9427 var _nsUriToPrefix = {}, k;
9428
9429 for (k in nsMap) {
9430 _nsUriToPrefix[k] = nsMap[k];
9431 }
9432
9433 // FORCE default mapping for schema instance
9434 _nsUriToPrefix[XSI_URI] = XSI_PREFIX;
9435
9436 isNamespace = true;
9437 nsUriToPrefix = _nsUriToPrefix;
9438
9439 return this;
9440 };
9441
9442 /**
9443 * Parse xml string.
9444 *
9445 * @param {string} xml
9446 *
9447 * @return {Error} returnError, if not thrown
9448 */
9449 this['parse'] = function(xml) {
9450 if (typeof xml !== 'string') {
9451 throw error$2('required args <xml=string>');
9452 }
9453
9454 returnError = null;
9455
9456 parse(xml);
9457
9458 getContext = noopGetContext;
9459 parseStop = false;
9460
9461 return returnError;
9462 };
9463
9464 /**
9465 * Stop parsing.
9466 */
9467 this['stop'] = function() {
9468 parseStop = true;
9469 };
9470
9471 /**
9472 * Parse string, invoking configured listeners on element.
9473 *
9474 * @param {string} xml
9475 */
9476 function parse(xml) {
9477 var nsMatrixStack = isNamespace ? [] : null,
9478 nsMatrix = isNamespace ? buildNsMatrix(nsUriToPrefix) : null,
9479 _nsMatrix,
9480 nodeStack = [],
9481 anonymousNsCount = 0,
9482 tagStart = false,
9483 tagEnd = false,
9484 i = 0, j = 0,
9485 x, y, q, w, v,
9486 xmlns,
9487 elementName,
9488 _elementName,
9489 elementProxy
9490 ;
9491
9492 var attrsString = '',
9493 attrsStart = 0,
9494 cachedAttrs // false = parsed with errors, null = needs parsing
9495 ;
9496
9497 /**
9498 * Parse attributes on demand and returns the parsed attributes.
9499 *
9500 * Return semantics: (1) `false` on attribute parse error,
9501 * (2) object hash on extracted attrs.
9502 *
9503 * @return {boolean|Object}
9504 */
9505 function getAttrs() {
9506 if (cachedAttrs !== null) {
9507 return cachedAttrs;
9508 }
9509
9510 var nsUri,
9511 nsUriPrefix,
9512 nsName,
9513 defaultAlias = isNamespace && nsMatrix['xmlns'],
9514 attrList = isNamespace && maybeNS ? [] : null,
9515 i = attrsStart,
9516 s = attrsString,
9517 l = s.length,
9518 hasNewMatrix,
9519 newalias,
9520 value,
9521 alias,
9522 name,
9523 attrs = {},
9524 seenAttrs = {},
9525 skipAttr,
9526 w,
9527 j;
9528
9529 parseAttr:
9530 for (; i < l; i++) {
9531 skipAttr = false;
9532 w = s.charCodeAt(i);
9533
9534 if (w === 32 || (w < 14 && w > 8)) { // WHITESPACE={ \f\n\r\t\v}
9535 continue;
9536 }
9537
9538 // wait for non whitespace character
9539 if (w < 65 || w > 122 || (w > 90 && w < 97)) {
9540 if (w !== 95 && w !== 58) { // char 95"_" 58":"
9541 handleWarning('illegal first char attribute name');
9542 skipAttr = true;
9543 }
9544 }
9545
9546 // parse attribute name
9547 for (j = i + 1; j < l; j++) {
9548 w = s.charCodeAt(j);
9549
9550 if (
9551 w > 96 && w < 123 ||
9552 w > 64 && w < 91 ||
9553 w > 47 && w < 59 ||
9554 w === 46 || // '.'
9555 w === 45 || // '-'
9556 w === 95 // '_'
9557 ) {
9558 continue;
9559 }
9560
9561 // unexpected whitespace
9562 if (w === 32 || (w < 14 && w > 8)) { // WHITESPACE
9563 handleWarning('missing attribute value');
9564 i = j;
9565
9566 continue parseAttr;
9567 }
9568
9569 // expected "="
9570 if (w === 61) { // "=" == 61
9571 break;
9572 }
9573
9574 handleWarning('illegal attribute name char');
9575 skipAttr = true;
9576 }
9577
9578 name = s.substring(i, j);
9579
9580 if (name === 'xmlns:xmlns') {
9581 handleWarning('illegal declaration of xmlns');
9582 skipAttr = true;
9583 }
9584
9585 w = s.charCodeAt(j + 1);
9586
9587 if (w === 34) { // '"'
9588 j = s.indexOf('"', i = j + 2);
9589
9590 if (j === -1) {
9591 j = s.indexOf('\'', i);
9592
9593 if (j !== -1) {
9594 handleWarning('attribute value quote missmatch');
9595 skipAttr = true;
9596 }
9597 }
9598
9599 } else if (w === 39) { // "'"
9600 j = s.indexOf('\'', i = j + 2);
9601
9602 if (j === -1) {
9603 j = s.indexOf('"', i);
9604
9605 if (j !== -1) {
9606 handleWarning('attribute value quote missmatch');
9607 skipAttr = true;
9608 }
9609 }
9610
9611 } else {
9612 handleWarning('missing attribute value quotes');
9613 skipAttr = true;
9614
9615 // skip to next space
9616 for (j = j + 1; j < l; j++) {
9617 w = s.charCodeAt(j + 1);
9618
9619 if (w === 32 || (w < 14 && w > 8)) { // WHITESPACE
9620 break;
9621 }
9622 }
9623
9624 }
9625
9626 if (j === -1) {
9627 handleWarning('missing closing quotes');
9628
9629 j = l;
9630 skipAttr = true;
9631 }
9632
9633 if (!skipAttr) {
9634 value = s.substring(i, j);
9635 }
9636
9637 i = j;
9638
9639 // ensure SPACE follows attribute
9640 // skip illegal content otherwise
9641 // example a="b"c
9642 for (; j + 1 < l; j++) {
9643 w = s.charCodeAt(j + 1);
9644
9645 if (w === 32 || (w < 14 && w > 8)) { // WHITESPACE
9646 break;
9647 }
9648
9649 // FIRST ILLEGAL CHAR
9650 if (i === j) {
9651 handleWarning('illegal character after attribute end');
9652 skipAttr = true;
9653 }
9654 }
9655
9656 // advance cursor to next attribute
9657 i = j + 1;
9658
9659 if (skipAttr) {
9660 continue parseAttr;
9661 }
9662
9663 // check attribute re-declaration
9664 if (name in seenAttrs) {
9665 handleWarning('attribute <' + name + '> already defined');
9666 continue;
9667 }
9668
9669 seenAttrs[name] = true;
9670
9671 if (!isNamespace) {
9672 attrs[name] = value;
9673 continue;
9674 }
9675
9676 // try to extract namespace information
9677 if (maybeNS) {
9678 newalias = (
9679 name === 'xmlns'
9680 ? 'xmlns'
9681 : (name.charCodeAt(0) === 120 && name.substr(0, 6) === 'xmlns:')
9682 ? name.substr(6)
9683 : null
9684 );
9685
9686 // handle xmlns(:alias) assignment
9687 if (newalias !== null) {
9688 nsUri = decodeEntities(value);
9689 nsUriPrefix = uriPrefix(newalias);
9690
9691 alias = nsUriToPrefix[nsUri];
9692
9693 if (!alias) {
9694
9695 // no prefix defined or prefix collision
9696 if (
9697 (newalias === 'xmlns') ||
9698 (nsUriPrefix in nsMatrix && nsMatrix[nsUriPrefix] !== nsUri)
9699 ) {
9700
9701 // alocate free ns prefix
9702 do {
9703 alias = 'ns' + (anonymousNsCount++);
9704 } while (typeof nsMatrix[alias] !== 'undefined');
9705 } else {
9706 alias = newalias;
9707 }
9708
9709 nsUriToPrefix[nsUri] = alias;
9710 }
9711
9712 if (nsMatrix[newalias] !== alias) {
9713 if (!hasNewMatrix) {
9714 nsMatrix = cloneNsMatrix(nsMatrix);
9715 hasNewMatrix = true;
9716 }
9717
9718 nsMatrix[newalias] = alias;
9719 if (newalias === 'xmlns') {
9720 nsMatrix[uriPrefix(alias)] = nsUri;
9721 defaultAlias = alias;
9722 }
9723
9724 nsMatrix[nsUriPrefix] = nsUri;
9725 }
9726
9727 // expose xmlns(:asd)="..." in attributes
9728 attrs[name] = value;
9729 continue;
9730 }
9731
9732 // collect attributes until all namespace
9733 // declarations are processed
9734 attrList.push(name, value);
9735 continue;
9736
9737 } /** end if (maybeNs) */
9738
9739 // handle attributes on element without
9740 // namespace declarations
9741 w = name.indexOf(':');
9742 if (w === -1) {
9743 attrs[name] = value;
9744 continue;
9745 }
9746
9747 // normalize ns attribute name
9748 if (!(nsName = nsMatrix[name.substring(0, w)])) {
9749 handleWarning(missingNamespaceForPrefix(name.substring(0, w)));
9750 continue;
9751 }
9752
9753 name = defaultAlias === nsName
9754 ? name.substr(w + 1)
9755 : nsName + name.substr(w);
9756
9757 // end: normalize ns attribute name
9758
9759 // normalize xsi:type ns attribute value
9760 if (name === XSI_TYPE$1) {
9761 w = value.indexOf(':');
9762
9763 if (w !== -1) {
9764 nsName = value.substring(0, w);
9765
9766 // handle default prefixes, i.e. xs:String gracefully
9767 nsName = nsMatrix[nsName] || nsName;
9768 value = nsName + value.substring(w);
9769 } else {
9770 value = defaultAlias + ':' + value;
9771 }
9772 }
9773
9774 // end: normalize xsi:type ns attribute value
9775
9776 attrs[name] = value;
9777 }
9778
9779
9780 // handle deferred, possibly namespaced attributes
9781 if (maybeNS) {
9782
9783 // normalize captured attributes
9784 for (i = 0, l = attrList.length; i < l; i++) {
9785
9786 name = attrList[i++];
9787 value = attrList[i];
9788
9789 w = name.indexOf(':');
9790
9791 if (w !== -1) {
9792
9793 // normalize ns attribute name
9794 if (!(nsName = nsMatrix[name.substring(0, w)])) {
9795 handleWarning(missingNamespaceForPrefix(name.substring(0, w)));
9796 continue;
9797 }
9798
9799 name = defaultAlias === nsName
9800 ? name.substr(w + 1)
9801 : nsName + name.substr(w);
9802
9803 // end: normalize ns attribute name
9804
9805 // normalize xsi:type ns attribute value
9806 if (name === XSI_TYPE$1) {
9807 w = value.indexOf(':');
9808
9809 if (w !== -1) {
9810 nsName = value.substring(0, w);
9811
9812 // handle default prefixes, i.e. xs:String gracefully
9813 nsName = nsMatrix[nsName] || nsName;
9814 value = nsName + value.substring(w);
9815 } else {
9816 value = defaultAlias + ':' + value;
9817 }
9818 }
9819
9820 // end: normalize xsi:type ns attribute value
9821 }
9822
9823 attrs[name] = value;
9824 }
9825
9826 // end: normalize captured attributes
9827 }
9828
9829 return cachedAttrs = attrs;
9830 }
9831
9832 /**
9833 * Extract the parse context { line, column, part }
9834 * from the current parser position.
9835 *
9836 * @return {Object} parse context
9837 */
9838 function getParseContext() {
9839 var splitsRe = /(\r\n|\r|\n)/g;
9840
9841 var line = 0;
9842 var column = 0;
9843 var startOfLine = 0;
9844 var endOfLine = j;
9845 var match;
9846 var data;
9847
9848 while (i >= startOfLine) {
9849
9850 match = splitsRe.exec(xml);
9851
9852 if (!match) {
9853 break;
9854 }
9855
9856 // end of line = (break idx + break chars)
9857 endOfLine = match[0].length + match.index;
9858
9859 if (endOfLine > i) {
9860 break;
9861 }
9862
9863 // advance to next line
9864 line += 1;
9865
9866 startOfLine = endOfLine;
9867 }
9868
9869 // EOF errors
9870 if (i == -1) {
9871 column = endOfLine;
9872 data = xml.substring(j);
9873 } else
9874
9875 // start errors
9876 if (j === 0) {
9877 data = xml.substring(j, i);
9878 }
9879
9880 // other errors
9881 else {
9882 column = i - startOfLine;
9883 data = (j == -1 ? xml.substring(i) : xml.substring(i, j + 1));
9884 }
9885
9886 return {
9887 'data': data,
9888 'line': line,
9889 'column': column
9890 };
9891 }
9892
9893 getContext = getParseContext;
9894
9895
9896 if (proxy) {
9897 elementProxy = Object.create({}, {
9898 'name': getter(function() {
9899 return elementName;
9900 }),
9901 'originalName': getter(function() {
9902 return _elementName;
9903 }),
9904 'attrs': getter(getAttrs),
9905 'ns': getter(function() {
9906 return nsMatrix;
9907 })
9908 });
9909 }
9910
9911 // actual parse logic
9912 while (j !== -1) {
9913
9914 if (xml.charCodeAt(j) === 60) { // "<"
9915 i = j;
9916 } else {
9917 i = xml.indexOf('<', j);
9918 }
9919
9920 // parse end
9921 if (i === -1) {
9922 if (nodeStack.length) {
9923 return handleError('unexpected end of file');
9924 }
9925
9926 if (j === 0) {
9927 return handleError('missing start tag');
9928 }
9929
9930 if (j < xml.length) {
9931 if (xml.substring(j).trim()) {
9932 handleWarning(NON_WHITESPACE_OUTSIDE_ROOT_NODE);
9933 }
9934 }
9935
9936 return;
9937 }
9938
9939 // parse text
9940 if (j !== i) {
9941
9942 if (nodeStack.length) {
9943 if (onText) {
9944 onText(xml.substring(j, i), decodeEntities, getContext);
9945
9946 if (parseStop) {
9947 return;
9948 }
9949 }
9950 } else {
9951 if (xml.substring(j, i).trim()) {
9952 handleWarning(NON_WHITESPACE_OUTSIDE_ROOT_NODE);
9953
9954 if (parseStop) {
9955 return;
9956 }
9957 }
9958 }
9959 }
9960
9961 w = xml.charCodeAt(i+1);
9962
9963 // parse comments + CDATA
9964 if (w === 33) { // "!"
9965 q = xml.charCodeAt(i+2);
9966
9967 // CDATA section
9968 if (q === 91 && xml.substr(i + 3, 6) === 'CDATA[') { // 91 == "["
9969 j = xml.indexOf(']]>', i);
9970 if (j === -1) {
9971 return handleError('unclosed cdata');
9972 }
9973
9974 if (onCDATA) {
9975 onCDATA(xml.substring(i + 9, j), getContext);
9976 if (parseStop) {
9977 return;
9978 }
9979 }
9980
9981 j += 3;
9982 continue;
9983 }
9984
9985 // comment
9986 if (q === 45 && xml.charCodeAt(i + 3) === 45) { // 45 == "-"
9987 j = xml.indexOf('-->', i);
9988 if (j === -1) {
9989 return handleError('unclosed comment');
9990 }
9991
9992
9993 if (onComment) {
9994 onComment(xml.substring(i + 4, j), decodeEntities, getContext);
9995 if (parseStop) {
9996 return;
9997 }
9998 }
9999
10000 j += 3;
10001 continue;
10002 }
10003 }
10004
10005 // parse question <? ... ?>
10006 if (w === 63) { // "?"
10007 j = xml.indexOf('?>', i);
10008 if (j === -1) {
10009 return handleError('unclosed question');
10010 }
10011
10012 if (onQuestion) {
10013 onQuestion(xml.substring(i, j + 2), getContext);
10014 if (parseStop) {
10015 return;
10016 }
10017 }
10018
10019 j += 2;
10020 continue;
10021 }
10022
10023 // find matching closing tag for attention or standard tags
10024 // for that we must skip through attribute values
10025 // (enclosed in single or double quotes)
10026 for (x = i + 1; ; x++) {
10027 v = xml.charCodeAt(x);
10028 if (isNaN(v)) {
10029 j = -1;
10030 return handleError('unclosed tag');
10031 }
10032
10033 // [10] AttValue ::= '"' ([^<&"] | Reference)* '"' | "'" ([^<&'] | Reference)* "'"
10034 // skips the quoted string
10035 // (double quotes) does not appear in a literal enclosed by (double quotes)
10036 // (single quote) does not appear in a literal enclosed by (single quote)
10037 if (v === 34) { // '"'
10038 q = xml.indexOf('"', x + 1);
10039 x = q !== -1 ? q : x;
10040 } else if (v === 39) { // "'"
10041 q = xml.indexOf("'", x + 1);
10042 x = q !== -1 ? q : x;
10043 } else if (v === 62) { // '>'
10044 j = x;
10045 break;
10046 }
10047 }
10048
10049
10050 // parse attention <! ...>
10051 // previously comment and CDATA have already been parsed
10052 if (w === 33) { // "!"
10053
10054 if (onAttention) {
10055 onAttention(xml.substring(i, j + 1), decodeEntities, getContext);
10056 if (parseStop) {
10057 return;
10058 }
10059 }
10060
10061 j += 1;
10062 continue;
10063 }
10064
10065 // don't process attributes;
10066 // there are none
10067 cachedAttrs = {};
10068
10069 // if (xml.charCodeAt(i+1) === 47) { // </...
10070 if (w === 47) { // </...
10071 tagStart = false;
10072 tagEnd = true;
10073
10074 if (!nodeStack.length) {
10075 return handleError('missing open tag');
10076 }
10077
10078 // verify open <-> close tag match
10079 x = elementName = nodeStack.pop();
10080 q = i + 2 + x.length;
10081
10082 if (xml.substring(i + 2, q) !== x) {
10083 return handleError('closing tag mismatch');
10084 }
10085
10086 // verify chars in close tag
10087 for (; q < j; q++) {
10088 w = xml.charCodeAt(q);
10089
10090 if (w === 32 || (w > 8 && w < 14)) { // \f\n\r\t\v space
10091 continue;
10092 }
10093
10094 return handleError('close tag');
10095 }
10096
10097 } else {
10098 if (xml.charCodeAt(j - 1) === 47) { // .../>
10099 x = elementName = xml.substring(i + 1, j - 1);
10100
10101 tagStart = true;
10102 tagEnd = true;
10103
10104 } else {
10105 x = elementName = xml.substring(i + 1, j);
10106
10107 tagStart = true;
10108 tagEnd = false;
10109 }
10110
10111 if (!(w > 96 && w < 123 || w > 64 && w < 91 || w === 95 || w === 58)) { // char 95"_" 58":"
10112 return handleError('illegal first char nodeName');
10113 }
10114
10115 for (q = 1, y = x.length; q < y; q++) {
10116 w = x.charCodeAt(q);
10117
10118 if (w > 96 && w < 123 || w > 64 && w < 91 || w > 47 && w < 59 || w === 45 || w === 95 || w == 46) {
10119 continue;
10120 }
10121
10122 if (w === 32 || (w < 14 && w > 8)) { // \f\n\r\t\v space
10123 elementName = x.substring(0, q);
10124
10125 // maybe there are attributes
10126 cachedAttrs = null;
10127 break;
10128 }
10129
10130 return handleError('invalid nodeName');
10131 }
10132
10133 if (!tagEnd) {
10134 nodeStack.push(elementName);
10135 }
10136 }
10137
10138 if (isNamespace) {
10139
10140 _nsMatrix = nsMatrix;
10141
10142 if (tagStart) {
10143
10144 // remember old namespace
10145 // unless we're self-closing
10146 if (!tagEnd) {
10147 nsMatrixStack.push(_nsMatrix);
10148 }
10149
10150 if (cachedAttrs === null) {
10151
10152 // quick check, whether there may be namespace
10153 // declarations on the node; if that is the case
10154 // we need to eagerly parse the node attributes
10155 if ((maybeNS = x.indexOf('xmlns', q) !== -1)) {
10156 attrsStart = q;
10157 attrsString = x;
10158
10159 getAttrs();
10160
10161 maybeNS = false;
10162 }
10163 }
10164 }
10165
10166 _elementName = elementName;
10167
10168 w = elementName.indexOf(':');
10169 if (w !== -1) {
10170 xmlns = nsMatrix[elementName.substring(0, w)];
10171
10172 // prefix given; namespace must exist
10173 if (!xmlns) {
10174 return handleError('missing namespace on <' + _elementName + '>');
10175 }
10176
10177 elementName = elementName.substr(w + 1);
10178 } else {
10179 xmlns = nsMatrix['xmlns'];
10180
10181 // if no default namespace is defined,
10182 // we'll import the element as anonymous.
10183 //
10184 // it is up to users to correct that to the document defined
10185 // targetNamespace, or whatever their undersanding of the
10186 // XML spec mandates.
10187 }
10188
10189 // adjust namespace prefixs as configured
10190 if (xmlns) {
10191 elementName = xmlns + ':' + elementName;
10192 }
10193
10194 }
10195
10196 if (tagStart) {
10197 attrsStart = q;
10198 attrsString = x;
10199
10200 if (onOpenTag) {
10201 if (proxy) {
10202 onOpenTag(elementProxy, decodeEntities, tagEnd, getContext);
10203 } else {
10204 onOpenTag(elementName, getAttrs, decodeEntities, tagEnd, getContext);
10205 }
10206
10207 if (parseStop) {
10208 return;
10209 }
10210 }
10211
10212 }
10213
10214 if (tagEnd) {
10215
10216 if (onCloseTag) {
10217 onCloseTag(proxy ? elementProxy : elementName, decodeEntities, tagStart, getContext);
10218
10219 if (parseStop) {
10220 return;
10221 }
10222 }
10223
10224 // restore old namespace
10225 if (isNamespace) {
10226 if (!tagStart) {
10227 nsMatrix = nsMatrixStack.pop();
10228 } else {
10229 nsMatrix = _nsMatrix;
10230 }
10231 }
10232 }
10233
10234 j += 1;
10235 }
10236 } /** end parse */
10237
10238 }
10239
10240 function hasLowerCaseAlias(pkg) {
10241 return pkg.xml && pkg.xml.tagAlias === 'lowerCase';
10242 }
10243
10244 var DEFAULT_NS_MAP = {
10245 'xsi': 'http://www.w3.org/2001/XMLSchema-instance',
10246 'xml': 'http://www.w3.org/XML/1998/namespace'
10247 };
10248
10249 var XSI_TYPE = 'xsi:type';
10250
10251 function serializeFormat(element) {
10252 return element.xml && element.xml.serialize;
10253 }
10254
10255 function serializeAsType(element) {
10256 return serializeFormat(element) === XSI_TYPE;
10257 }
10258
10259 function serializeAsProperty(element) {
10260 return serializeFormat(element) === 'property';
10261 }
10262
10263 function capitalize(str) {
10264 return str.charAt(0).toUpperCase() + str.slice(1);
10265 }
10266
10267 function aliasToName(aliasNs, pkg) {
10268
10269 if (!hasLowerCaseAlias(pkg)) {
10270 return aliasNs.name;
10271 }
10272
10273 return aliasNs.prefix + ':' + capitalize(aliasNs.localName);
10274 }
10275
10276 function prefixedToName(nameNs, pkg) {
10277
10278 var name = nameNs.name,
10279 localName = nameNs.localName;
10280
10281 var typePrefix = pkg.xml && pkg.xml.typePrefix;
10282
10283 if (typePrefix && localName.indexOf(typePrefix) === 0) {
10284 return nameNs.prefix + ':' + localName.slice(typePrefix.length);
10285 } else {
10286 return name;
10287 }
10288 }
10289
10290 function normalizeXsiTypeName(name, model) {
10291
10292 var nameNs = parseName(name);
10293 var pkg = model.getPackage(nameNs.prefix);
10294
10295 return prefixedToName(nameNs, pkg);
10296 }
10297
10298 function error$1(message) {
10299 return new Error(message);
10300 }
10301
10302 /**
10303 * Get the moddle descriptor for a given instance or type.
10304 *
10305 * @param {ModdleElement|Function} element
10306 *
10307 * @return {Object} the moddle descriptor
10308 */
10309 function getModdleDescriptor(element) {
10310 return element.$descriptor;
10311 }
10312
10313
10314 /**
10315 * A parse context.
10316 *
10317 * @class
10318 *
10319 * @param {Object} options
10320 * @param {ElementHandler} options.rootHandler the root handler for parsing a document
10321 * @param {boolean} [options.lax=false] whether or not to ignore invalid elements
10322 */
10323 function Context(options) {
10324
10325 /**
10326 * @property {ElementHandler} rootHandler
10327 */
10328
10329 /**
10330 * @property {Boolean} lax
10331 */
10332
10333 assign(this, options);
10334
10335 this.elementsById = {};
10336 this.references = [];
10337 this.warnings = [];
10338
10339 /**
10340 * Add an unresolved reference.
10341 *
10342 * @param {Object} reference
10343 */
10344 this.addReference = function(reference) {
10345 this.references.push(reference);
10346 };
10347
10348 /**
10349 * Add a processed element.
10350 *
10351 * @param {ModdleElement} element
10352 */
10353 this.addElement = function(element) {
10354
10355 if (!element) {
10356 throw error$1('expected element');
10357 }
10358
10359 var elementsById = this.elementsById;
10360
10361 var descriptor = getModdleDescriptor(element);
10362
10363 var idProperty = descriptor.idProperty,
10364 id;
10365
10366 if (idProperty) {
10367 id = element.get(idProperty.name);
10368
10369 if (id) {
10370
10371 // for QName validation as per http://www.w3.org/TR/REC-xml/#NT-NameChar
10372 if (!/^([a-z][\w-.]*:)?[a-z_][\w-.]*$/i.test(id)) {
10373 throw new Error('illegal ID <' + id + '>');
10374 }
10375
10376 if (elementsById[id]) {
10377 throw error$1('duplicate ID <' + id + '>');
10378 }
10379
10380 elementsById[id] = element;
10381 }
10382 }
10383 };
10384
10385 /**
10386 * Add an import warning.
10387 *
10388 * @param {Object} warning
10389 * @param {String} warning.message
10390 * @param {Error} [warning.error]
10391 */
10392 this.addWarning = function(warning) {
10393 this.warnings.push(warning);
10394 };
10395 }
10396
10397 function BaseHandler() {}
10398
10399 BaseHandler.prototype.handleEnd = function() {};
10400 BaseHandler.prototype.handleText = function() {};
10401 BaseHandler.prototype.handleNode = function() {};
10402
10403
10404 /**
10405 * A simple pass through handler that does nothing except for
10406 * ignoring all input it receives.
10407 *
10408 * This is used to ignore unknown elements and
10409 * attributes.
10410 */
10411 function NoopHandler() { }
10412
10413 NoopHandler.prototype = Object.create(BaseHandler.prototype);
10414
10415 NoopHandler.prototype.handleNode = function() {
10416 return this;
10417 };
10418
10419 function BodyHandler() {}
10420
10421 BodyHandler.prototype = Object.create(BaseHandler.prototype);
10422
10423 BodyHandler.prototype.handleText = function(text) {
10424 this.body = (this.body || '') + text;
10425 };
10426
10427 function ReferenceHandler(property, context) {
10428 this.property = property;
10429 this.context = context;
10430 }
10431
10432 ReferenceHandler.prototype = Object.create(BodyHandler.prototype);
10433
10434 ReferenceHandler.prototype.handleNode = function(node) {
10435
10436 if (this.element) {
10437 throw error$1('expected no sub nodes');
10438 } else {
10439 this.element = this.createReference(node);
10440 }
10441
10442 return this;
10443 };
10444
10445 ReferenceHandler.prototype.handleEnd = function() {
10446 this.element.id = this.body;
10447 };
10448
10449 ReferenceHandler.prototype.createReference = function(node) {
10450 return {
10451 property: this.property.ns.name,
10452 id: ''
10453 };
10454 };
10455
10456 function ValueHandler(propertyDesc, element) {
10457 this.element = element;
10458 this.propertyDesc = propertyDesc;
10459 }
10460
10461 ValueHandler.prototype = Object.create(BodyHandler.prototype);
10462
10463 ValueHandler.prototype.handleEnd = function() {
10464
10465 var value = this.body || '',
10466 element = this.element,
10467 propertyDesc = this.propertyDesc;
10468
10469 value = coerceType(propertyDesc.type, value);
10470
10471 if (propertyDesc.isMany) {
10472 element.get(propertyDesc.name).push(value);
10473 } else {
10474 element.set(propertyDesc.name, value);
10475 }
10476 };
10477
10478
10479 function BaseElementHandler() {}
10480
10481 BaseElementHandler.prototype = Object.create(BodyHandler.prototype);
10482
10483 BaseElementHandler.prototype.handleNode = function(node) {
10484 var parser = this,
10485 element = this.element;
10486
10487 if (!element) {
10488 element = this.element = this.createElement(node);
10489
10490 this.context.addElement(element);
10491 } else {
10492 parser = this.handleChild(node);
10493 }
10494
10495 return parser;
10496 };
10497
10498 /**
10499 * @class Reader.ElementHandler
10500 *
10501 */
10502 function ElementHandler(model, typeName, context) {
10503 this.model = model;
10504 this.type = model.getType(typeName);
10505 this.context = context;
10506 }
10507
10508 ElementHandler.prototype = Object.create(BaseElementHandler.prototype);
10509
10510 ElementHandler.prototype.addReference = function(reference) {
10511 this.context.addReference(reference);
10512 };
10513
10514 ElementHandler.prototype.handleText = function(text) {
10515
10516 var element = this.element,
10517 descriptor = getModdleDescriptor(element),
10518 bodyProperty = descriptor.bodyProperty;
10519
10520 if (!bodyProperty) {
10521 throw error$1('unexpected body text <' + text + '>');
10522 }
10523
10524 BodyHandler.prototype.handleText.call(this, text);
10525 };
10526
10527 ElementHandler.prototype.handleEnd = function() {
10528
10529 var value = this.body,
10530 element = this.element,
10531 descriptor = getModdleDescriptor(element),
10532 bodyProperty = descriptor.bodyProperty;
10533
10534 if (bodyProperty && value !== undefined) {
10535 value = coerceType(bodyProperty.type, value);
10536 element.set(bodyProperty.name, value);
10537 }
10538 };
10539
10540 /**
10541 * Create an instance of the model from the given node.
10542 *
10543 * @param {Element} node the xml node
10544 */
10545 ElementHandler.prototype.createElement = function(node) {
10546 var attributes = node.attributes,
10547 Type = this.type,
10548 descriptor = getModdleDescriptor(Type),
10549 context = this.context,
10550 instance = new Type({}),
10551 model = this.model,
10552 propNameNs;
10553
10554 forEach$2(attributes, function(value, name) {
10555
10556 var prop = descriptor.propertiesByName[name],
10557 values;
10558
10559 if (prop && prop.isReference) {
10560
10561 if (!prop.isMany) {
10562 context.addReference({
10563 element: instance,
10564 property: prop.ns.name,
10565 id: value
10566 });
10567 } else {
10568
10569 // IDREFS: parse references as whitespace-separated list
10570 values = value.split(' ');
10571
10572 forEach$2(values, function(v) {
10573 context.addReference({
10574 element: instance,
10575 property: prop.ns.name,
10576 id: v
10577 });
10578 });
10579 }
10580
10581 } else {
10582 if (prop) {
10583 value = coerceType(prop.type, value);
10584 } else
10585 if (name !== 'xmlns') {
10586 propNameNs = parseName(name, descriptor.ns.prefix);
10587
10588 // check whether attribute is defined in a well-known namespace
10589 // if that is the case we emit a warning to indicate potential misuse
10590 if (model.getPackage(propNameNs.prefix)) {
10591
10592 context.addWarning({
10593 message: 'unknown attribute <' + name + '>',
10594 element: instance,
10595 property: name,
10596 value: value
10597 });
10598 }
10599 }
10600
10601 instance.set(name, value);
10602 }
10603 });
10604
10605 return instance;
10606 };
10607
10608 ElementHandler.prototype.getPropertyForNode = function(node) {
10609
10610 var name = node.name;
10611 var nameNs = parseName(name);
10612
10613 var type = this.type,
10614 model = this.model,
10615 descriptor = getModdleDescriptor(type);
10616
10617 var propertyName = nameNs.name,
10618 property = descriptor.propertiesByName[propertyName],
10619 elementTypeName,
10620 elementType;
10621
10622 // search for properties by name first
10623
10624 if (property && !property.isAttr) {
10625
10626 if (serializeAsType(property)) {
10627 elementTypeName = node.attributes[XSI_TYPE];
10628
10629 // xsi type is optional, if it does not exists the
10630 // default type is assumed
10631 if (elementTypeName) {
10632
10633 // take possible type prefixes from XML
10634 // into account, i.e.: xsi:type="t{ActualType}"
10635 elementTypeName = normalizeXsiTypeName(elementTypeName, model);
10636
10637 elementType = model.getType(elementTypeName);
10638
10639 return assign({}, property, {
10640 effectiveType: getModdleDescriptor(elementType).name
10641 });
10642 }
10643 }
10644
10645 // search for properties by name first
10646 return property;
10647 }
10648
10649 var pkg = model.getPackage(nameNs.prefix);
10650
10651 if (pkg) {
10652 elementTypeName = aliasToName(nameNs, pkg);
10653 elementType = model.getType(elementTypeName);
10654
10655 // search for collection members later
10656 property = find(descriptor.properties, function(p) {
10657 return !p.isVirtual && !p.isReference && !p.isAttribute && elementType.hasType(p.type);
10658 });
10659
10660 if (property) {
10661 return assign({}, property, {
10662 effectiveType: getModdleDescriptor(elementType).name
10663 });
10664 }
10665 } else {
10666
10667 // parse unknown element (maybe extension)
10668 property = find(descriptor.properties, function(p) {
10669 return !p.isReference && !p.isAttribute && p.type === 'Element';
10670 });
10671
10672 if (property) {
10673 return property;
10674 }
10675 }
10676
10677 throw error$1('unrecognized element <' + nameNs.name + '>');
10678 };
10679
10680 ElementHandler.prototype.toString = function() {
10681 return 'ElementDescriptor[' + getModdleDescriptor(this.type).name + ']';
10682 };
10683
10684 ElementHandler.prototype.valueHandler = function(propertyDesc, element) {
10685 return new ValueHandler(propertyDesc, element);
10686 };
10687
10688 ElementHandler.prototype.referenceHandler = function(propertyDesc) {
10689 return new ReferenceHandler(propertyDesc, this.context);
10690 };
10691
10692 ElementHandler.prototype.handler = function(type) {
10693 if (type === 'Element') {
10694 return new GenericElementHandler(this.model, type, this.context);
10695 } else {
10696 return new ElementHandler(this.model, type, this.context);
10697 }
10698 };
10699
10700 /**
10701 * Handle the child element parsing
10702 *
10703 * @param {Element} node the xml node
10704 */
10705 ElementHandler.prototype.handleChild = function(node) {
10706 var propertyDesc, type, element, childHandler;
10707
10708 propertyDesc = this.getPropertyForNode(node);
10709 element = this.element;
10710
10711 type = propertyDesc.effectiveType || propertyDesc.type;
10712
10713 if (isSimple(type)) {
10714 return this.valueHandler(propertyDesc, element);
10715 }
10716
10717 if (propertyDesc.isReference) {
10718 childHandler = this.referenceHandler(propertyDesc).handleNode(node);
10719 } else {
10720 childHandler = this.handler(type).handleNode(node);
10721 }
10722
10723 var newElement = childHandler.element;
10724
10725 // child handles may decide to skip elements
10726 // by not returning anything
10727 if (newElement !== undefined) {
10728
10729 if (propertyDesc.isMany) {
10730 element.get(propertyDesc.name).push(newElement);
10731 } else {
10732 element.set(propertyDesc.name, newElement);
10733 }
10734
10735 if (propertyDesc.isReference) {
10736 assign(newElement, {
10737 element: element
10738 });
10739
10740 this.context.addReference(newElement);
10741 } else {
10742
10743 // establish child -> parent relationship
10744 newElement.$parent = element;
10745 }
10746 }
10747
10748 return childHandler;
10749 };
10750
10751 /**
10752 * An element handler that performs special validation
10753 * to ensure the node it gets initialized with matches
10754 * the handlers type (namespace wise).
10755 *
10756 * @param {Moddle} model
10757 * @param {String} typeName
10758 * @param {Context} context
10759 */
10760 function RootElementHandler(model, typeName, context) {
10761 ElementHandler.call(this, model, typeName, context);
10762 }
10763
10764 RootElementHandler.prototype = Object.create(ElementHandler.prototype);
10765
10766 RootElementHandler.prototype.createElement = function(node) {
10767
10768 var name = node.name,
10769 nameNs = parseName(name),
10770 model = this.model,
10771 type = this.type,
10772 pkg = model.getPackage(nameNs.prefix),
10773 typeName = pkg && aliasToName(nameNs, pkg) || name;
10774
10775 // verify the correct namespace if we parse
10776 // the first element in the handler tree
10777 //
10778 // this ensures we don't mistakenly import wrong namespace elements
10779 if (!type.hasType(typeName)) {
10780 throw error$1('unexpected element <' + node.originalName + '>');
10781 }
10782
10783 return ElementHandler.prototype.createElement.call(this, node);
10784 };
10785
10786
10787 function GenericElementHandler(model, typeName, context) {
10788 this.model = model;
10789 this.context = context;
10790 }
10791
10792 GenericElementHandler.prototype = Object.create(BaseElementHandler.prototype);
10793
10794 GenericElementHandler.prototype.createElement = function(node) {
10795
10796 var name = node.name,
10797 ns = parseName(name),
10798 prefix = ns.prefix,
10799 uri = node.ns[prefix + '$uri'],
10800 attributes = node.attributes;
10801
10802 return this.model.createAny(name, uri, attributes);
10803 };
10804
10805 GenericElementHandler.prototype.handleChild = function(node) {
10806
10807 var handler = new GenericElementHandler(this.model, 'Element', this.context).handleNode(node),
10808 element = this.element;
10809
10810 var newElement = handler.element,
10811 children;
10812
10813 if (newElement !== undefined) {
10814 children = element.$children = element.$children || [];
10815 children.push(newElement);
10816
10817 // establish child -> parent relationship
10818 newElement.$parent = element;
10819 }
10820
10821 return handler;
10822 };
10823
10824 GenericElementHandler.prototype.handleEnd = function() {
10825 if (this.body) {
10826 this.element.$body = this.body;
10827 }
10828 };
10829
10830 /**
10831 * A reader for a meta-model
10832 *
10833 * @param {Object} options
10834 * @param {Model} options.model used to read xml files
10835 * @param {Boolean} options.lax whether to make parse errors warnings
10836 */
10837 function Reader(options) {
10838
10839 if (options instanceof Moddle) {
10840 options = {
10841 model: options
10842 };
10843 }
10844
10845 assign(this, { lax: false }, options);
10846 }
10847
10848 /**
10849 * The fromXML result.
10850 *
10851 * @typedef {Object} ParseResult
10852 *
10853 * @property {ModdleElement} rootElement
10854 * @property {Array<Object>} references
10855 * @property {Array<Error>} warnings
10856 * @property {Object} elementsById - a mapping containing each ID -> ModdleElement
10857 */
10858
10859 /**
10860 * The fromXML result.
10861 *
10862 * @typedef {Error} ParseError
10863 *
10864 * @property {Array<Error>} warnings
10865 */
10866
10867 /**
10868 * Parse the given XML into a moddle document tree.
10869 *
10870 * @param {String} xml
10871 * @param {ElementHandler|Object} options or rootHandler
10872 *
10873 * @returns {Promise<ParseResult, ParseError>}
10874 */
10875 Reader.prototype.fromXML = function(xml, options, done) {
10876
10877 var rootHandler = options.rootHandler;
10878
10879 if (options instanceof ElementHandler) {
10880
10881 // root handler passed via (xml, { rootHandler: ElementHandler }, ...)
10882 rootHandler = options;
10883 options = {};
10884 } else {
10885 if (typeof options === 'string') {
10886
10887 // rootHandler passed via (xml, 'someString', ...)
10888 rootHandler = this.handler(options);
10889 options = {};
10890 } else if (typeof rootHandler === 'string') {
10891
10892 // rootHandler passed via (xml, { rootHandler: 'someString' }, ...)
10893 rootHandler = this.handler(rootHandler);
10894 }
10895 }
10896
10897 var model = this.model,
10898 lax = this.lax;
10899
10900 var context = new Context(assign({}, options, { rootHandler: rootHandler })),
10901 parser = new Parser({ proxy: true }),
10902 stack = createStack();
10903
10904 rootHandler.context = context;
10905
10906 // push root handler
10907 stack.push(rootHandler);
10908
10909
10910 /**
10911 * Handle error.
10912 *
10913 * @param {Error} err
10914 * @param {Function} getContext
10915 * @param {boolean} lax
10916 *
10917 * @return {boolean} true if handled
10918 */
10919 function handleError(err, getContext, lax) {
10920
10921 var ctx = getContext();
10922
10923 var line = ctx.line,
10924 column = ctx.column,
10925 data = ctx.data;
10926
10927 // we receive the full context data here,
10928 // for elements trim down the information
10929 // to the tag name, only
10930 if (data.charAt(0) === '<' && data.indexOf(' ') !== -1) {
10931 data = data.slice(0, data.indexOf(' ')) + '>';
10932 }
10933
10934 var message =
10935 'unparsable content ' + (data ? data + ' ' : '') + 'detected\n\t' +
10936 'line: ' + line + '\n\t' +
10937 'column: ' + column + '\n\t' +
10938 'nested error: ' + err.message;
10939
10940 if (lax) {
10941 context.addWarning({
10942 message: message,
10943 error: err
10944 });
10945
10946 return true;
10947 } else {
10948 throw error$1(message);
10949 }
10950 }
10951
10952 function handleWarning(err, getContext) {
10953
10954 // just like handling errors in <lax=true> mode
10955 return handleError(err, getContext, true);
10956 }
10957
10958 /**
10959 * Resolve collected references on parse end.
10960 */
10961 function resolveReferences() {
10962
10963 var elementsById = context.elementsById;
10964 var references = context.references;
10965
10966 var i, r;
10967
10968 for (i = 0; (r = references[i]); i++) {
10969 var element = r.element;
10970 var reference = elementsById[r.id];
10971 var property = getModdleDescriptor(element).propertiesByName[r.property];
10972
10973 if (!reference) {
10974 context.addWarning({
10975 message: 'unresolved reference <' + r.id + '>',
10976 element: r.element,
10977 property: r.property,
10978 value: r.id
10979 });
10980 }
10981
10982 if (property.isMany) {
10983 var collection = element.get(property.name),
10984 idx = collection.indexOf(r);
10985
10986 // we replace an existing place holder (idx != -1) or
10987 // append to the collection instead
10988 if (idx === -1) {
10989 idx = collection.length;
10990 }
10991
10992 if (!reference) {
10993
10994 // remove unresolvable reference
10995 collection.splice(idx, 1);
10996 } else {
10997
10998 // add or update reference in collection
10999 collection[idx] = reference;
11000 }
11001 } else {
11002 element.set(property.name, reference);
11003 }
11004 }
11005 }
11006
11007 function handleClose() {
11008 stack.pop().handleEnd();
11009 }
11010
11011 var PREAMBLE_START_PATTERN = /^<\?xml /i;
11012
11013 var ENCODING_PATTERN = / encoding="([^"]+)"/i;
11014
11015 var UTF_8_PATTERN = /^utf-8$/i;
11016
11017 function handleQuestion(question) {
11018
11019 if (!PREAMBLE_START_PATTERN.test(question)) {
11020 return;
11021 }
11022
11023 var match = ENCODING_PATTERN.exec(question);
11024 var encoding = match && match[1];
11025
11026 if (!encoding || UTF_8_PATTERN.test(encoding)) {
11027 return;
11028 }
11029
11030 context.addWarning({
11031 message:
11032 'unsupported document encoding <' + encoding + '>, ' +
11033 'falling back to UTF-8'
11034 });
11035 }
11036
11037 function handleOpen(node, getContext) {
11038 var handler = stack.peek();
11039
11040 try {
11041 stack.push(handler.handleNode(node));
11042 } catch (err) {
11043
11044 if (handleError(err, getContext, lax)) {
11045 stack.push(new NoopHandler());
11046 }
11047 }
11048 }
11049
11050 function handleCData(text, getContext) {
11051
11052 try {
11053 stack.peek().handleText(text);
11054 } catch (err) {
11055 handleWarning(err, getContext);
11056 }
11057 }
11058
11059 function handleText(text, getContext) {
11060
11061 // strip whitespace only nodes, i.e. before
11062 // <!CDATA[ ... ]> sections and in between tags
11063
11064 if (!text.trim()) {
11065 return;
11066 }
11067
11068 handleCData(text, getContext);
11069 }
11070
11071 var uriMap = model.getPackages().reduce(function(uriMap, p) {
11072 uriMap[p.uri] = p.prefix;
11073
11074 return uriMap;
11075 }, {
11076 'http://www.w3.org/XML/1998/namespace': 'xml' // add default xml ns
11077 });
11078 parser
11079 .ns(uriMap)
11080 .on('openTag', function(obj, decodeStr, selfClosing, getContext) {
11081
11082 // gracefully handle unparsable attributes (attrs=false)
11083 var attrs = obj.attrs || {};
11084
11085 var decodedAttrs = Object.keys(attrs).reduce(function(d, key) {
11086 var value = decodeStr(attrs[key]);
11087
11088 d[key] = value;
11089
11090 return d;
11091 }, {});
11092
11093 var node = {
11094 name: obj.name,
11095 originalName: obj.originalName,
11096 attributes: decodedAttrs,
11097 ns: obj.ns
11098 };
11099
11100 handleOpen(node, getContext);
11101 })
11102 .on('question', handleQuestion)
11103 .on('closeTag', handleClose)
11104 .on('cdata', handleCData)
11105 .on('text', function(text, decodeEntities, getContext) {
11106 handleText(decodeEntities(text), getContext);
11107 })
11108 .on('error', handleError)
11109 .on('warn', handleWarning);
11110
11111 // async XML parsing to make sure the execution environment
11112 // (node or brower) is kept responsive and that certain optimization
11113 // strategies can kick in.
11114 return new Promise(function(resolve, reject) {
11115
11116 var err;
11117
11118 try {
11119 parser.parse(xml);
11120
11121 resolveReferences();
11122 } catch (e) {
11123 err = e;
11124 }
11125
11126 var rootElement = rootHandler.element;
11127
11128 if (!err && !rootElement) {
11129 err = error$1('failed to parse document as <' + rootHandler.type.$descriptor.name + '>');
11130 }
11131
11132 var warnings = context.warnings;
11133 var references = context.references;
11134 var elementsById = context.elementsById;
11135
11136 if (err) {
11137 err.warnings = warnings;
11138
11139 return reject(err);
11140 } else {
11141 return resolve({
11142 rootElement: rootElement,
11143 elementsById: elementsById,
11144 references: references,
11145 warnings: warnings
11146 });
11147 }
11148 });
11149 };
11150
11151 Reader.prototype.handler = function(name) {
11152 return new RootElementHandler(this.model, name);
11153 };
11154
11155
11156 // helpers //////////////////////////
11157
11158 function createStack() {
11159 var stack = [];
11160
11161 Object.defineProperty(stack, 'peek', {
11162 value: function() {
11163 return this[this.length - 1];
11164 }
11165 });
11166
11167 return stack;
11168 }
11169
11170 var XML_PREAMBLE = '<?xml version="1.0" encoding="UTF-8"?>\n';
11171
11172 var ESCAPE_ATTR_CHARS = /<|>|'|"|&|\n\r|\n/g;
11173 var ESCAPE_CHARS = /<|>|&/g;
11174
11175
11176 function Namespaces(parent) {
11177
11178 var prefixMap = {};
11179 var uriMap = {};
11180 var used = {};
11181
11182 var wellknown = [];
11183 var custom = [];
11184
11185 // API
11186
11187 this.byUri = function(uri) {
11188 return uriMap[uri] || (
11189 parent && parent.byUri(uri)
11190 );
11191 };
11192
11193 this.add = function(ns, isWellknown) {
11194
11195 uriMap[ns.uri] = ns;
11196
11197 if (isWellknown) {
11198 wellknown.push(ns);
11199 } else {
11200 custom.push(ns);
11201 }
11202
11203 this.mapPrefix(ns.prefix, ns.uri);
11204 };
11205
11206 this.uriByPrefix = function(prefix) {
11207 return prefixMap[prefix || 'xmlns'];
11208 };
11209
11210 this.mapPrefix = function(prefix, uri) {
11211 prefixMap[prefix || 'xmlns'] = uri;
11212 };
11213
11214 this.getNSKey = function(ns) {
11215 return (ns.prefix !== undefined) ? (ns.uri + '|' + ns.prefix) : ns.uri;
11216 };
11217
11218 this.logUsed = function(ns) {
11219
11220 var uri = ns.uri;
11221 var nsKey = this.getNSKey(ns);
11222
11223 used[nsKey] = this.byUri(uri);
11224
11225 // Inform parent recursively about the usage of this NS
11226 if (parent) {
11227 parent.logUsed(ns);
11228 }
11229 };
11230
11231 this.getUsed = function(ns) {
11232
11233 function isUsed(ns) {
11234 var nsKey = self.getNSKey(ns);
11235
11236 return used[nsKey];
11237 }
11238
11239 var self = this;
11240
11241 var allNs = [].concat(wellknown, custom);
11242
11243 return allNs.filter(isUsed);
11244 };
11245
11246 }
11247
11248 function lower(string) {
11249 return string.charAt(0).toLowerCase() + string.slice(1);
11250 }
11251
11252 function nameToAlias(name, pkg) {
11253 if (hasLowerCaseAlias(pkg)) {
11254 return lower(name);
11255 } else {
11256 return name;
11257 }
11258 }
11259
11260 function inherits(ctor, superCtor) {
11261 ctor.super_ = superCtor;
11262 ctor.prototype = Object.create(superCtor.prototype, {
11263 constructor: {
11264 value: ctor,
11265 enumerable: false,
11266 writable: true,
11267 configurable: true
11268 }
11269 });
11270 }
11271
11272 function nsName(ns) {
11273 if (isString(ns)) {
11274 return ns;
11275 } else {
11276 return (ns.prefix ? ns.prefix + ':' : '') + ns.localName;
11277 }
11278 }
11279
11280 function getNsAttrs(namespaces) {
11281
11282 return namespaces.getUsed().filter(function(ns) {
11283
11284 // do not serialize built in <xml> namespace
11285 return ns.prefix !== 'xml';
11286 }).map(function(ns) {
11287 var name = 'xmlns' + (ns.prefix ? ':' + ns.prefix : '');
11288 return { name: name, value: ns.uri };
11289 });
11290
11291 }
11292
11293 function getElementNs(ns, descriptor) {
11294 if (descriptor.isGeneric) {
11295 return assign({ localName: descriptor.ns.localName }, ns);
11296 } else {
11297 return assign({ localName: nameToAlias(descriptor.ns.localName, descriptor.$pkg) }, ns);
11298 }
11299 }
11300
11301 function getPropertyNs(ns, descriptor) {
11302 return assign({ localName: descriptor.ns.localName }, ns);
11303 }
11304
11305 function getSerializableProperties(element) {
11306 var descriptor = element.$descriptor;
11307
11308 return filter(descriptor.properties, function(p) {
11309 var name = p.name;
11310
11311 if (p.isVirtual) {
11312 return false;
11313 }
11314
11315 // do not serialize defaults
11316 if (!has$2(element, name)) {
11317 return false;
11318 }
11319
11320 var value = element[name];
11321
11322 // do not serialize default equals
11323 if (value === p.default) {
11324 return false;
11325 }
11326
11327 // do not serialize null properties
11328 if (value === null) {
11329 return false;
11330 }
11331
11332 return p.isMany ? value.length : true;
11333 });
11334 }
11335
11336 var ESCAPE_ATTR_MAP = {
11337 '\n': '#10',
11338 '\n\r': '#10',
11339 '"': '#34',
11340 '\'': '#39',
11341 '<': '#60',
11342 '>': '#62',
11343 '&': '#38'
11344 };
11345
11346 var ESCAPE_MAP = {
11347 '<': 'lt',
11348 '>': 'gt',
11349 '&': 'amp'
11350 };
11351
11352 function escape(str, charPattern, replaceMap) {
11353
11354 // ensure we are handling strings here
11355 str = isString(str) ? str : '' + str;
11356
11357 return str.replace(charPattern, function(s) {
11358 return '&' + replaceMap[s] + ';';
11359 });
11360 }
11361
11362 /**
11363 * Escape a string attribute to not contain any bad values (line breaks, '"', ...)
11364 *
11365 * @param {String} str the string to escape
11366 * @return {String} the escaped string
11367 */
11368 function escapeAttr(str) {
11369 return escape(str, ESCAPE_ATTR_CHARS, ESCAPE_ATTR_MAP);
11370 }
11371
11372 function escapeBody(str) {
11373 return escape(str, ESCAPE_CHARS, ESCAPE_MAP);
11374 }
11375
11376 function filterAttributes(props) {
11377 return filter(props, function(p) { return p.isAttr; });
11378 }
11379
11380 function filterContained(props) {
11381 return filter(props, function(p) { return !p.isAttr; });
11382 }
11383
11384
11385 function ReferenceSerializer(tagName) {
11386 this.tagName = tagName;
11387 }
11388
11389 ReferenceSerializer.prototype.build = function(element) {
11390 this.element = element;
11391 return this;
11392 };
11393
11394 ReferenceSerializer.prototype.serializeTo = function(writer) {
11395 writer
11396 .appendIndent()
11397 .append('<' + this.tagName + '>' + this.element.id + '</' + this.tagName + '>')
11398 .appendNewLine();
11399 };
11400
11401 function BodySerializer() {}
11402
11403 BodySerializer.prototype.serializeValue =
11404 BodySerializer.prototype.serializeTo = function(writer) {
11405 writer.append(
11406 this.escape
11407 ? escapeBody(this.value)
11408 : this.value
11409 );
11410 };
11411
11412 BodySerializer.prototype.build = function(prop, value) {
11413 this.value = value;
11414
11415 if (prop.type === 'String' && value.search(ESCAPE_CHARS) !== -1) {
11416 this.escape = true;
11417 }
11418
11419 return this;
11420 };
11421
11422 function ValueSerializer(tagName) {
11423 this.tagName = tagName;
11424 }
11425
11426 inherits(ValueSerializer, BodySerializer);
11427
11428 ValueSerializer.prototype.serializeTo = function(writer) {
11429
11430 writer
11431 .appendIndent()
11432 .append('<' + this.tagName + '>');
11433
11434 this.serializeValue(writer);
11435
11436 writer
11437 .append('</' + this.tagName + '>')
11438 .appendNewLine();
11439 };
11440
11441 function ElementSerializer(parent, propertyDescriptor) {
11442 this.body = [];
11443 this.attrs = [];
11444
11445 this.parent = parent;
11446 this.propertyDescriptor = propertyDescriptor;
11447 }
11448
11449 ElementSerializer.prototype.build = function(element) {
11450 this.element = element;
11451
11452 var elementDescriptor = element.$descriptor,
11453 propertyDescriptor = this.propertyDescriptor;
11454
11455 var otherAttrs,
11456 properties;
11457
11458 var isGeneric = elementDescriptor.isGeneric;
11459
11460 if (isGeneric) {
11461 otherAttrs = this.parseGeneric(element);
11462 } else {
11463 otherAttrs = this.parseNsAttributes(element);
11464 }
11465
11466 if (propertyDescriptor) {
11467 this.ns = this.nsPropertyTagName(propertyDescriptor);
11468 } else {
11469 this.ns = this.nsTagName(elementDescriptor);
11470 }
11471
11472 // compute tag name
11473 this.tagName = this.addTagName(this.ns);
11474
11475 if (!isGeneric) {
11476 properties = getSerializableProperties(element);
11477
11478 this.parseAttributes(filterAttributes(properties));
11479 this.parseContainments(filterContained(properties));
11480 }
11481
11482 this.parseGenericAttributes(element, otherAttrs);
11483
11484 return this;
11485 };
11486
11487 ElementSerializer.prototype.nsTagName = function(descriptor) {
11488 var effectiveNs = this.logNamespaceUsed(descriptor.ns);
11489 return getElementNs(effectiveNs, descriptor);
11490 };
11491
11492 ElementSerializer.prototype.nsPropertyTagName = function(descriptor) {
11493 var effectiveNs = this.logNamespaceUsed(descriptor.ns);
11494 return getPropertyNs(effectiveNs, descriptor);
11495 };
11496
11497 ElementSerializer.prototype.isLocalNs = function(ns) {
11498 return ns.uri === this.ns.uri;
11499 };
11500
11501 /**
11502 * Get the actual ns attribute name for the given element.
11503 *
11504 * @param {Object} element
11505 * @param {Boolean} [element.inherited=false]
11506 *
11507 * @return {Object} nsName
11508 */
11509 ElementSerializer.prototype.nsAttributeName = function(element) {
11510
11511 var ns;
11512
11513 if (isString(element)) {
11514 ns = parseName(element);
11515 } else {
11516 ns = element.ns;
11517 }
11518
11519 // return just local name for inherited attributes
11520 if (element.inherited) {
11521 return { localName: ns.localName };
11522 }
11523
11524 // parse + log effective ns
11525 var effectiveNs = this.logNamespaceUsed(ns);
11526
11527 // LOG ACTUAL namespace use
11528 this.getNamespaces().logUsed(effectiveNs);
11529
11530 // strip prefix if same namespace like parent
11531 if (this.isLocalNs(effectiveNs)) {
11532 return { localName: ns.localName };
11533 } else {
11534 return assign({ localName: ns.localName }, effectiveNs);
11535 }
11536 };
11537
11538 ElementSerializer.prototype.parseGeneric = function(element) {
11539
11540 var self = this,
11541 body = this.body;
11542
11543 var attributes = [];
11544
11545 forEach$2(element, function(val, key) {
11546
11547 var nonNsAttr;
11548
11549 if (key === '$body') {
11550 body.push(new BodySerializer().build({ type: 'String' }, val));
11551 } else
11552 if (key === '$children') {
11553 forEach$2(val, function(child) {
11554 body.push(new ElementSerializer(self).build(child));
11555 });
11556 } else
11557 if (key.indexOf('$') !== 0) {
11558 nonNsAttr = self.parseNsAttribute(element, key, val);
11559
11560 if (nonNsAttr) {
11561 attributes.push({ name: key, value: val });
11562 }
11563 }
11564 });
11565
11566 return attributes;
11567 };
11568
11569 ElementSerializer.prototype.parseNsAttribute = function(element, name, value) {
11570 var model = element.$model;
11571
11572 var nameNs = parseName(name);
11573
11574 var ns;
11575
11576 // parse xmlns:foo="http://foo.bar"
11577 if (nameNs.prefix === 'xmlns') {
11578 ns = { prefix: nameNs.localName, uri: value };
11579 }
11580
11581 // parse xmlns="http://foo.bar"
11582 if (!nameNs.prefix && nameNs.localName === 'xmlns') {
11583 ns = { uri: value };
11584 }
11585
11586 if (!ns) {
11587 return {
11588 name: name,
11589 value: value
11590 };
11591 }
11592
11593 if (model && model.getPackage(value)) {
11594
11595 // register well known namespace
11596 this.logNamespace(ns, true, true);
11597 } else {
11598
11599 // log custom namespace directly as used
11600 var actualNs = this.logNamespaceUsed(ns, true);
11601
11602 this.getNamespaces().logUsed(actualNs);
11603 }
11604 };
11605
11606
11607 /**
11608 * Parse namespaces and return a list of left over generic attributes
11609 *
11610 * @param {Object} element
11611 * @return {Array<Object>}
11612 */
11613 ElementSerializer.prototype.parseNsAttributes = function(element, attrs) {
11614 var self = this;
11615
11616 var genericAttrs = element.$attrs;
11617
11618 var attributes = [];
11619
11620 // parse namespace attributes first
11621 // and log them. push non namespace attributes to a list
11622 // and process them later
11623 forEach$2(genericAttrs, function(value, name) {
11624
11625 var nonNsAttr = self.parseNsAttribute(element, name, value);
11626
11627 if (nonNsAttr) {
11628 attributes.push(nonNsAttr);
11629 }
11630 });
11631
11632 return attributes;
11633 };
11634
11635 ElementSerializer.prototype.parseGenericAttributes = function(element, attributes) {
11636
11637 var self = this;
11638
11639 forEach$2(attributes, function(attr) {
11640
11641 // do not serialize xsi:type attribute
11642 // it is set manually based on the actual implementation type
11643 if (attr.name === XSI_TYPE) {
11644 return;
11645 }
11646
11647 try {
11648 self.addAttribute(self.nsAttributeName(attr.name), attr.value);
11649 } catch (e) {
11650 console.warn(
11651 'missing namespace information for ',
11652 attr.name, '=', attr.value, 'on', element,
11653 e);
11654 }
11655 });
11656 };
11657
11658 ElementSerializer.prototype.parseContainments = function(properties) {
11659
11660 var self = this,
11661 body = this.body,
11662 element = this.element;
11663
11664 forEach$2(properties, function(p) {
11665 var value = element.get(p.name),
11666 isReference = p.isReference,
11667 isMany = p.isMany;
11668
11669 if (!isMany) {
11670 value = [ value ];
11671 }
11672
11673 if (p.isBody) {
11674 body.push(new BodySerializer().build(p, value[0]));
11675 } else
11676 if (isSimple(p.type)) {
11677 forEach$2(value, function(v) {
11678 body.push(new ValueSerializer(self.addTagName(self.nsPropertyTagName(p))).build(p, v));
11679 });
11680 } else
11681 if (isReference) {
11682 forEach$2(value, function(v) {
11683 body.push(new ReferenceSerializer(self.addTagName(self.nsPropertyTagName(p))).build(v));
11684 });
11685 } else {
11686
11687 // allow serialization via type
11688 // rather than element name
11689 var asType = serializeAsType(p),
11690 asProperty = serializeAsProperty(p);
11691
11692 forEach$2(value, function(v) {
11693 var serializer;
11694
11695 if (asType) {
11696 serializer = new TypeSerializer(self, p);
11697 } else
11698 if (asProperty) {
11699 serializer = new ElementSerializer(self, p);
11700 } else {
11701 serializer = new ElementSerializer(self);
11702 }
11703
11704 body.push(serializer.build(v));
11705 });
11706 }
11707 });
11708 };
11709
11710 ElementSerializer.prototype.getNamespaces = function(local) {
11711
11712 var namespaces = this.namespaces,
11713 parent = this.parent,
11714 parentNamespaces;
11715
11716 if (!namespaces) {
11717 parentNamespaces = parent && parent.getNamespaces();
11718
11719 if (local || !parentNamespaces) {
11720 this.namespaces = namespaces = new Namespaces(parentNamespaces);
11721 } else {
11722 namespaces = parentNamespaces;
11723 }
11724 }
11725
11726 return namespaces;
11727 };
11728
11729 ElementSerializer.prototype.logNamespace = function(ns, wellknown, local) {
11730 var namespaces = this.getNamespaces(local);
11731
11732 var nsUri = ns.uri,
11733 nsPrefix = ns.prefix;
11734
11735 var existing = namespaces.byUri(nsUri);
11736
11737 if (!existing || local) {
11738 namespaces.add(ns, wellknown);
11739 }
11740
11741 namespaces.mapPrefix(nsPrefix, nsUri);
11742
11743 return ns;
11744 };
11745
11746 ElementSerializer.prototype.logNamespaceUsed = function(ns, local) {
11747 var element = this.element,
11748 model = element.$model,
11749 namespaces = this.getNamespaces(local);
11750
11751 // ns may be
11752 //
11753 // * prefix only
11754 // * prefix:uri
11755 // * localName only
11756
11757 var prefix = ns.prefix,
11758 uri = ns.uri,
11759 newPrefix, idx,
11760 wellknownUri;
11761
11762 // handle anonymous namespaces (elementForm=unqualified), cf. #23
11763 if (!prefix && !uri) {
11764 return { localName: ns.localName };
11765 }
11766
11767 wellknownUri = DEFAULT_NS_MAP[prefix] || model && (model.getPackage(prefix) || {}).uri;
11768
11769 uri = uri || wellknownUri || namespaces.uriByPrefix(prefix);
11770
11771 if (!uri) {
11772 throw new Error('no namespace uri given for prefix <' + prefix + '>');
11773 }
11774
11775 ns = namespaces.byUri(uri);
11776
11777 if (!ns) {
11778 newPrefix = prefix;
11779 idx = 1;
11780
11781 // find a prefix that is not mapped yet
11782 while (namespaces.uriByPrefix(newPrefix)) {
11783 newPrefix = prefix + '_' + idx++;
11784 }
11785
11786 ns = this.logNamespace({ prefix: newPrefix, uri: uri }, wellknownUri === uri);
11787 }
11788
11789 if (prefix) {
11790 namespaces.mapPrefix(prefix, uri);
11791 }
11792
11793 return ns;
11794 };
11795
11796 ElementSerializer.prototype.parseAttributes = function(properties) {
11797 var self = this,
11798 element = this.element;
11799
11800 forEach$2(properties, function(p) {
11801
11802 var value = element.get(p.name);
11803
11804 if (p.isReference) {
11805
11806 if (!p.isMany) {
11807 value = value.id;
11808 }
11809 else {
11810 var values = [];
11811 forEach$2(value, function(v) {
11812 values.push(v.id);
11813 });
11814
11815 // IDREFS is a whitespace-separated list of references.
11816 value = values.join(' ');
11817 }
11818
11819 }
11820
11821 self.addAttribute(self.nsAttributeName(p), value);
11822 });
11823 };
11824
11825 ElementSerializer.prototype.addTagName = function(nsTagName) {
11826 var actualNs = this.logNamespaceUsed(nsTagName);
11827
11828 this.getNamespaces().logUsed(actualNs);
11829
11830 return nsName(nsTagName);
11831 };
11832
11833 ElementSerializer.prototype.addAttribute = function(name, value) {
11834 var attrs = this.attrs;
11835
11836 if (isString(value)) {
11837 value = escapeAttr(value);
11838 }
11839
11840 attrs.push({ name: name, value: value });
11841 };
11842
11843 ElementSerializer.prototype.serializeAttributes = function(writer) {
11844 var attrs = this.attrs,
11845 namespaces = this.namespaces;
11846
11847 if (namespaces) {
11848 attrs = getNsAttrs(namespaces).concat(attrs);
11849 }
11850
11851 forEach$2(attrs, function(a) {
11852 writer
11853 .append(' ')
11854 .append(nsName(a.name)).append('="').append(a.value).append('"');
11855 });
11856 };
11857
11858 ElementSerializer.prototype.serializeTo = function(writer) {
11859 var firstBody = this.body[0],
11860 indent = firstBody && firstBody.constructor !== BodySerializer;
11861
11862 writer
11863 .appendIndent()
11864 .append('<' + this.tagName);
11865
11866 this.serializeAttributes(writer);
11867
11868 writer.append(firstBody ? '>' : ' />');
11869
11870 if (firstBody) {
11871
11872 if (indent) {
11873 writer
11874 .appendNewLine()
11875 .indent();
11876 }
11877
11878 forEach$2(this.body, function(b) {
11879 b.serializeTo(writer);
11880 });
11881
11882 if (indent) {
11883 writer
11884 .unindent()
11885 .appendIndent();
11886 }
11887
11888 writer.append('</' + this.tagName + '>');
11889 }
11890
11891 writer.appendNewLine();
11892 };
11893
11894 /**
11895 * A serializer for types that handles serialization of data types
11896 */
11897 function TypeSerializer(parent, propertyDescriptor) {
11898 ElementSerializer.call(this, parent, propertyDescriptor);
11899 }
11900
11901 inherits(TypeSerializer, ElementSerializer);
11902
11903 TypeSerializer.prototype.parseNsAttributes = function(element) {
11904
11905 // extracted attributes
11906 var attributes = ElementSerializer.prototype.parseNsAttributes.call(this, element);
11907
11908 var descriptor = element.$descriptor;
11909
11910 // only serialize xsi:type if necessary
11911 if (descriptor.name === this.propertyDescriptor.type) {
11912 return attributes;
11913 }
11914
11915 var typeNs = this.typeNs = this.nsTagName(descriptor);
11916 this.getNamespaces().logUsed(this.typeNs);
11917
11918 // add xsi:type attribute to represent the elements
11919 // actual type
11920
11921 var pkg = element.$model.getPackage(typeNs.uri),
11922 typePrefix = (pkg.xml && pkg.xml.typePrefix) || '';
11923
11924 this.addAttribute(
11925 this.nsAttributeName(XSI_TYPE),
11926 (typeNs.prefix ? typeNs.prefix + ':' : '') + typePrefix + descriptor.ns.localName
11927 );
11928
11929 return attributes;
11930 };
11931
11932 TypeSerializer.prototype.isLocalNs = function(ns) {
11933 return ns.uri === (this.typeNs || this.ns).uri;
11934 };
11935
11936 function SavingWriter() {
11937 this.value = '';
11938
11939 this.write = function(str) {
11940 this.value += str;
11941 };
11942 }
11943
11944 function FormatingWriter(out, format) {
11945
11946 var indent = [''];
11947
11948 this.append = function(str) {
11949 out.write(str);
11950
11951 return this;
11952 };
11953
11954 this.appendNewLine = function() {
11955 if (format) {
11956 out.write('\n');
11957 }
11958
11959 return this;
11960 };
11961
11962 this.appendIndent = function() {
11963 if (format) {
11964 out.write(indent.join(' '));
11965 }
11966
11967 return this;
11968 };
11969
11970 this.indent = function() {
11971 indent.push('');
11972 return this;
11973 };
11974
11975 this.unindent = function() {
11976 indent.pop();
11977 return this;
11978 };
11979 }
11980
11981 /**
11982 * A writer for meta-model backed document trees
11983 *
11984 * @param {Object} options output options to pass into the writer
11985 */
11986 function Writer(options) {
11987
11988 options = assign({ format: false, preamble: true }, options || {});
11989
11990 function toXML(tree, writer) {
11991 var internalWriter = writer || new SavingWriter();
11992 var formatingWriter = new FormatingWriter(internalWriter, options.format);
11993
11994 if (options.preamble) {
11995 formatingWriter.append(XML_PREAMBLE);
11996 }
11997
11998 new ElementSerializer().build(tree).serializeTo(formatingWriter);
11999
12000 if (!writer) {
12001 return internalWriter.value;
12002 }
12003 }
12004
12005 return {
12006 toXML: toXML
12007 };
12008 }
12009
12010 /**
12011 * A sub class of {@link Moddle} with support for import and export of BPMN 2.0 xml files.
12012 *
12013 * @class BpmnModdle
12014 * @extends Moddle
12015 *
12016 * @param {Object|Array} packages to use for instantiating the model
12017 * @param {Object} [options] additional options to pass over
12018 */
12019 function BpmnModdle(packages, options) {
12020 Moddle.call(this, packages, options);
12021 }
12022
12023 BpmnModdle.prototype = Object.create(Moddle.prototype);
12024
12025 /**
12026 * The fromXML result.
12027 *
12028 * @typedef {Object} ParseResult
12029 *
12030 * @property {ModdleElement} rootElement
12031 * @property {Array<Object>} references
12032 * @property {Array<Error>} warnings
12033 * @property {Object} elementsById - a mapping containing each ID -> ModdleElement
12034 */
12035
12036 /**
12037 * The fromXML error.
12038 *
12039 * @typedef {Error} ParseError
12040 *
12041 * @property {Array<Error>} warnings
12042 */
12043
12044 /**
12045 * Instantiates a BPMN model tree from a given xml string.
12046 *
12047 * @param {String} xmlStr
12048 * @param {String} [typeName='bpmn:Definitions'] name of the root element
12049 * @param {Object} [options] options to pass to the underlying reader
12050 *
12051 * @returns {Promise<ParseResult, ParseError>}
12052 */
12053 BpmnModdle.prototype.fromXML = function(xmlStr, typeName, options) {
12054
12055 if (!isString(typeName)) {
12056 options = typeName;
12057 typeName = 'bpmn:Definitions';
12058 }
12059
12060 var reader = new Reader(assign({ model: this, lax: true }, options));
12061 var rootHandler = reader.handler(typeName);
12062
12063 return reader.fromXML(xmlStr, rootHandler);
12064 };
12065
12066
12067 /**
12068 * The toXML result.
12069 *
12070 * @typedef {Object} SerializationResult
12071 *
12072 * @property {String} xml
12073 */
12074
12075 /**
12076 * Serializes a BPMN 2.0 object tree to XML.
12077 *
12078 * @param {String} element the root element, typically an instance of `bpmn:Definitions`
12079 * @param {Object} [options] to pass to the underlying writer
12080 *
12081 * @returns {Promise<SerializationResult, Error>}
12082 */
12083 BpmnModdle.prototype.toXML = function(element, options) {
12084
12085 var writer = new Writer(options);
12086
12087 return new Promise(function(resolve, reject) {
12088 try {
12089 var result = writer.toXML(element);
12090
12091 return resolve({
12092 xml: result
12093 });
12094 } catch (err) {
12095 return reject(err);
12096 }
12097 });
12098 };
12099
12100 var name = "BPMN20";
12101 var uri = "http://www.omg.org/spec/BPMN/20100524/MODEL";
12102 var prefix = "bpmn";
12103 var associations = [
12104 ];
12105 var types = [
12106 {
12107 name: "Interface",
12108 superClass: [
12109 "RootElement"
12110 ],
12111 properties: [
12112 {
12113 name: "name",
12114 isAttr: true,
12115 type: "String"
12116 },
12117 {
12118 name: "operations",
12119 type: "Operation",
12120 isMany: true
12121 },
12122 {
12123 name: "implementationRef",
12124 isAttr: true,
12125 type: "String"
12126 }
12127 ]
12128 },
12129 {
12130 name: "Operation",
12131 superClass: [
12132 "BaseElement"
12133 ],
12134 properties: [
12135 {
12136 name: "name",
12137 isAttr: true,
12138 type: "String"
12139 },
12140 {
12141 name: "inMessageRef",
12142 type: "Message",
12143 isReference: true
12144 },
12145 {
12146 name: "outMessageRef",
12147 type: "Message",
12148 isReference: true
12149 },
12150 {
12151 name: "errorRef",
12152 type: "Error",
12153 isMany: true,
12154 isReference: true
12155 },
12156 {
12157 name: "implementationRef",
12158 isAttr: true,
12159 type: "String"
12160 }
12161 ]
12162 },
12163 {
12164 name: "EndPoint",
12165 superClass: [
12166 "RootElement"
12167 ]
12168 },
12169 {
12170 name: "Auditing",
12171 superClass: [
12172 "BaseElement"
12173 ]
12174 },
12175 {
12176 name: "GlobalTask",
12177 superClass: [
12178 "CallableElement"
12179 ],
12180 properties: [
12181 {
12182 name: "resources",
12183 type: "ResourceRole",
12184 isMany: true
12185 }
12186 ]
12187 },
12188 {
12189 name: "Monitoring",
12190 superClass: [
12191 "BaseElement"
12192 ]
12193 },
12194 {
12195 name: "Performer",
12196 superClass: [
12197 "ResourceRole"
12198 ]
12199 },
12200 {
12201 name: "Process",
12202 superClass: [
12203 "FlowElementsContainer",
12204 "CallableElement"
12205 ],
12206 properties: [
12207 {
12208 name: "processType",
12209 type: "ProcessType",
12210 isAttr: true
12211 },
12212 {
12213 name: "isClosed",
12214 isAttr: true,
12215 type: "Boolean"
12216 },
12217 {
12218 name: "auditing",
12219 type: "Auditing"
12220 },
12221 {
12222 name: "monitoring",
12223 type: "Monitoring"
12224 },
12225 {
12226 name: "properties",
12227 type: "Property",
12228 isMany: true
12229 },
12230 {
12231 name: "laneSets",
12232 isMany: true,
12233 replaces: "FlowElementsContainer#laneSets",
12234 type: "LaneSet"
12235 },
12236 {
12237 name: "flowElements",
12238 isMany: true,
12239 replaces: "FlowElementsContainer#flowElements",
12240 type: "FlowElement"
12241 },
12242 {
12243 name: "artifacts",
12244 type: "Artifact",
12245 isMany: true
12246 },
12247 {
12248 name: "resources",
12249 type: "ResourceRole",
12250 isMany: true
12251 },
12252 {
12253 name: "correlationSubscriptions",
12254 type: "CorrelationSubscription",
12255 isMany: true
12256 },
12257 {
12258 name: "supports",
12259 type: "Process",
12260 isMany: true,
12261 isReference: true
12262 },
12263 {
12264 name: "definitionalCollaborationRef",
12265 type: "Collaboration",
12266 isAttr: true,
12267 isReference: true
12268 },
12269 {
12270 name: "isExecutable",
12271 isAttr: true,
12272 type: "Boolean"
12273 }
12274 ]
12275 },
12276 {
12277 name: "LaneSet",
12278 superClass: [
12279 "BaseElement"
12280 ],
12281 properties: [
12282 {
12283 name: "lanes",
12284 type: "Lane",
12285 isMany: true
12286 },
12287 {
12288 name: "name",
12289 isAttr: true,
12290 type: "String"
12291 }
12292 ]
12293 },
12294 {
12295 name: "Lane",
12296 superClass: [
12297 "BaseElement"
12298 ],
12299 properties: [
12300 {
12301 name: "name",
12302 isAttr: true,
12303 type: "String"
12304 },
12305 {
12306 name: "partitionElementRef",
12307 type: "BaseElement",
12308 isAttr: true,
12309 isReference: true
12310 },
12311 {
12312 name: "partitionElement",
12313 type: "BaseElement"
12314 },
12315 {
12316 name: "flowNodeRef",
12317 type: "FlowNode",
12318 isMany: true,
12319 isReference: true
12320 },
12321 {
12322 name: "childLaneSet",
12323 type: "LaneSet",
12324 xml: {
12325 serialize: "xsi:type"
12326 }
12327 }
12328 ]
12329 },
12330 {
12331 name: "GlobalManualTask",
12332 superClass: [
12333 "GlobalTask"
12334 ]
12335 },
12336 {
12337 name: "ManualTask",
12338 superClass: [
12339 "Task"
12340 ]
12341 },
12342 {
12343 name: "UserTask",
12344 superClass: [
12345 "Task"
12346 ],
12347 properties: [
12348 {
12349 name: "renderings",
12350 type: "Rendering",
12351 isMany: true
12352 },
12353 {
12354 name: "implementation",
12355 isAttr: true,
12356 type: "String"
12357 }
12358 ]
12359 },
12360 {
12361 name: "Rendering",
12362 superClass: [
12363 "BaseElement"
12364 ]
12365 },
12366 {
12367 name: "HumanPerformer",
12368 superClass: [
12369 "Performer"
12370 ]
12371 },
12372 {
12373 name: "PotentialOwner",
12374 superClass: [
12375 "HumanPerformer"
12376 ]
12377 },
12378 {
12379 name: "GlobalUserTask",
12380 superClass: [
12381 "GlobalTask"
12382 ],
12383 properties: [
12384 {
12385 name: "implementation",
12386 isAttr: true,
12387 type: "String"
12388 },
12389 {
12390 name: "renderings",
12391 type: "Rendering",
12392 isMany: true
12393 }
12394 ]
12395 },
12396 {
12397 name: "Gateway",
12398 isAbstract: true,
12399 superClass: [
12400 "FlowNode"
12401 ],
12402 properties: [
12403 {
12404 name: "gatewayDirection",
12405 type: "GatewayDirection",
12406 "default": "Unspecified",
12407 isAttr: true
12408 }
12409 ]
12410 },
12411 {
12412 name: "EventBasedGateway",
12413 superClass: [
12414 "Gateway"
12415 ],
12416 properties: [
12417 {
12418 name: "instantiate",
12419 "default": false,
12420 isAttr: true,
12421 type: "Boolean"
12422 },
12423 {
12424 name: "eventGatewayType",
12425 type: "EventBasedGatewayType",
12426 isAttr: true,
12427 "default": "Exclusive"
12428 }
12429 ]
12430 },
12431 {
12432 name: "ComplexGateway",
12433 superClass: [
12434 "Gateway"
12435 ],
12436 properties: [
12437 {
12438 name: "activationCondition",
12439 type: "Expression",
12440 xml: {
12441 serialize: "xsi:type"
12442 }
12443 },
12444 {
12445 name: "default",
12446 type: "SequenceFlow",
12447 isAttr: true,
12448 isReference: true
12449 }
12450 ]
12451 },
12452 {
12453 name: "ExclusiveGateway",
12454 superClass: [
12455 "Gateway"
12456 ],
12457 properties: [
12458 {
12459 name: "default",
12460 type: "SequenceFlow",
12461 isAttr: true,
12462 isReference: true
12463 }
12464 ]
12465 },
12466 {
12467 name: "InclusiveGateway",
12468 superClass: [
12469 "Gateway"
12470 ],
12471 properties: [
12472 {
12473 name: "default",
12474 type: "SequenceFlow",
12475 isAttr: true,
12476 isReference: true
12477 }
12478 ]
12479 },
12480 {
12481 name: "ParallelGateway",
12482 superClass: [
12483 "Gateway"
12484 ]
12485 },
12486 {
12487 name: "RootElement",
12488 isAbstract: true,
12489 superClass: [
12490 "BaseElement"
12491 ]
12492 },
12493 {
12494 name: "Relationship",
12495 superClass: [
12496 "BaseElement"
12497 ],
12498 properties: [
12499 {
12500 name: "type",
12501 isAttr: true,
12502 type: "String"
12503 },
12504 {
12505 name: "direction",
12506 type: "RelationshipDirection",
12507 isAttr: true
12508 },
12509 {
12510 name: "source",
12511 isMany: true,
12512 isReference: true,
12513 type: "Element"
12514 },
12515 {
12516 name: "target",
12517 isMany: true,
12518 isReference: true,
12519 type: "Element"
12520 }
12521 ]
12522 },
12523 {
12524 name: "BaseElement",
12525 isAbstract: true,
12526 properties: [
12527 {
12528 name: "id",
12529 isAttr: true,
12530 type: "String",
12531 isId: true
12532 },
12533 {
12534 name: "documentation",
12535 type: "Documentation",
12536 isMany: true
12537 },
12538 {
12539 name: "extensionDefinitions",
12540 type: "ExtensionDefinition",
12541 isMany: true,
12542 isReference: true
12543 },
12544 {
12545 name: "extensionElements",
12546 type: "ExtensionElements"
12547 }
12548 ]
12549 },
12550 {
12551 name: "Extension",
12552 properties: [
12553 {
12554 name: "mustUnderstand",
12555 "default": false,
12556 isAttr: true,
12557 type: "Boolean"
12558 },
12559 {
12560 name: "definition",
12561 type: "ExtensionDefinition",
12562 isAttr: true,
12563 isReference: true
12564 }
12565 ]
12566 },
12567 {
12568 name: "ExtensionDefinition",
12569 properties: [
12570 {
12571 name: "name",
12572 isAttr: true,
12573 type: "String"
12574 },
12575 {
12576 name: "extensionAttributeDefinitions",
12577 type: "ExtensionAttributeDefinition",
12578 isMany: true
12579 }
12580 ]
12581 },
12582 {
12583 name: "ExtensionAttributeDefinition",
12584 properties: [
12585 {
12586 name: "name",
12587 isAttr: true,
12588 type: "String"
12589 },
12590 {
12591 name: "type",
12592 isAttr: true,
12593 type: "String"
12594 },
12595 {
12596 name: "isReference",
12597 "default": false,
12598 isAttr: true,
12599 type: "Boolean"
12600 },
12601 {
12602 name: "extensionDefinition",
12603 type: "ExtensionDefinition",
12604 isAttr: true,
12605 isReference: true
12606 }
12607 ]
12608 },
12609 {
12610 name: "ExtensionElements",
12611 properties: [
12612 {
12613 name: "valueRef",
12614 isAttr: true,
12615 isReference: true,
12616 type: "Element"
12617 },
12618 {
12619 name: "values",
12620 type: "Element",
12621 isMany: true
12622 },
12623 {
12624 name: "extensionAttributeDefinition",
12625 type: "ExtensionAttributeDefinition",
12626 isAttr: true,
12627 isReference: true
12628 }
12629 ]
12630 },
12631 {
12632 name: "Documentation",
12633 superClass: [
12634 "BaseElement"
12635 ],
12636 properties: [
12637 {
12638 name: "text",
12639 type: "String",
12640 isBody: true
12641 },
12642 {
12643 name: "textFormat",
12644 "default": "text/plain",
12645 isAttr: true,
12646 type: "String"
12647 }
12648 ]
12649 },
12650 {
12651 name: "Event",
12652 isAbstract: true,
12653 superClass: [
12654 "FlowNode",
12655 "InteractionNode"
12656 ],
12657 properties: [
12658 {
12659 name: "properties",
12660 type: "Property",
12661 isMany: true
12662 }
12663 ]
12664 },
12665 {
12666 name: "IntermediateCatchEvent",
12667 superClass: [
12668 "CatchEvent"
12669 ]
12670 },
12671 {
12672 name: "IntermediateThrowEvent",
12673 superClass: [
12674 "ThrowEvent"
12675 ]
12676 },
12677 {
12678 name: "EndEvent",
12679 superClass: [
12680 "ThrowEvent"
12681 ]
12682 },
12683 {
12684 name: "StartEvent",
12685 superClass: [
12686 "CatchEvent"
12687 ],
12688 properties: [
12689 {
12690 name: "isInterrupting",
12691 "default": true,
12692 isAttr: true,
12693 type: "Boolean"
12694 }
12695 ]
12696 },
12697 {
12698 name: "ThrowEvent",
12699 isAbstract: true,
12700 superClass: [
12701 "Event"
12702 ],
12703 properties: [
12704 {
12705 name: "dataInputs",
12706 type: "DataInput",
12707 isMany: true
12708 },
12709 {
12710 name: "dataInputAssociations",
12711 type: "DataInputAssociation",
12712 isMany: true
12713 },
12714 {
12715 name: "inputSet",
12716 type: "InputSet"
12717 },
12718 {
12719 name: "eventDefinitions",
12720 type: "EventDefinition",
12721 isMany: true
12722 },
12723 {
12724 name: "eventDefinitionRef",
12725 type: "EventDefinition",
12726 isMany: true,
12727 isReference: true
12728 }
12729 ]
12730 },
12731 {
12732 name: "CatchEvent",
12733 isAbstract: true,
12734 superClass: [
12735 "Event"
12736 ],
12737 properties: [
12738 {
12739 name: "parallelMultiple",
12740 isAttr: true,
12741 type: "Boolean",
12742 "default": false
12743 },
12744 {
12745 name: "dataOutputs",
12746 type: "DataOutput",
12747 isMany: true
12748 },
12749 {
12750 name: "dataOutputAssociations",
12751 type: "DataOutputAssociation",
12752 isMany: true
12753 },
12754 {
12755 name: "outputSet",
12756 type: "OutputSet"
12757 },
12758 {
12759 name: "eventDefinitions",
12760 type: "EventDefinition",
12761 isMany: true
12762 },
12763 {
12764 name: "eventDefinitionRef",
12765 type: "EventDefinition",
12766 isMany: true,
12767 isReference: true
12768 }
12769 ]
12770 },
12771 {
12772 name: "BoundaryEvent",
12773 superClass: [
12774 "CatchEvent"
12775 ],
12776 properties: [
12777 {
12778 name: "cancelActivity",
12779 "default": true,
12780 isAttr: true,
12781 type: "Boolean"
12782 },
12783 {
12784 name: "attachedToRef",
12785 type: "Activity",
12786 isAttr: true,
12787 isReference: true
12788 }
12789 ]
12790 },
12791 {
12792 name: "EventDefinition",
12793 isAbstract: true,
12794 superClass: [
12795 "RootElement"
12796 ]
12797 },
12798 {
12799 name: "CancelEventDefinition",
12800 superClass: [
12801 "EventDefinition"
12802 ]
12803 },
12804 {
12805 name: "ErrorEventDefinition",
12806 superClass: [
12807 "EventDefinition"
12808 ],
12809 properties: [
12810 {
12811 name: "errorRef",
12812 type: "Error",
12813 isAttr: true,
12814 isReference: true
12815 }
12816 ]
12817 },
12818 {
12819 name: "TerminateEventDefinition",
12820 superClass: [
12821 "EventDefinition"
12822 ]
12823 },
12824 {
12825 name: "EscalationEventDefinition",
12826 superClass: [
12827 "EventDefinition"
12828 ],
12829 properties: [
12830 {
12831 name: "escalationRef",
12832 type: "Escalation",
12833 isAttr: true,
12834 isReference: true
12835 }
12836 ]
12837 },
12838 {
12839 name: "Escalation",
12840 properties: [
12841 {
12842 name: "structureRef",
12843 type: "ItemDefinition",
12844 isAttr: true,
12845 isReference: true
12846 },
12847 {
12848 name: "name",
12849 isAttr: true,
12850 type: "String"
12851 },
12852 {
12853 name: "escalationCode",
12854 isAttr: true,
12855 type: "String"
12856 }
12857 ],
12858 superClass: [
12859 "RootElement"
12860 ]
12861 },
12862 {
12863 name: "CompensateEventDefinition",
12864 superClass: [
12865 "EventDefinition"
12866 ],
12867 properties: [
12868 {
12869 name: "waitForCompletion",
12870 isAttr: true,
12871 type: "Boolean",
12872 "default": true
12873 },
12874 {
12875 name: "activityRef",
12876 type: "Activity",
12877 isAttr: true,
12878 isReference: true
12879 }
12880 ]
12881 },
12882 {
12883 name: "TimerEventDefinition",
12884 superClass: [
12885 "EventDefinition"
12886 ],
12887 properties: [
12888 {
12889 name: "timeDate",
12890 type: "Expression",
12891 xml: {
12892 serialize: "xsi:type"
12893 }
12894 },
12895 {
12896 name: "timeCycle",
12897 type: "Expression",
12898 xml: {
12899 serialize: "xsi:type"
12900 }
12901 },
12902 {
12903 name: "timeDuration",
12904 type: "Expression",
12905 xml: {
12906 serialize: "xsi:type"
12907 }
12908 }
12909 ]
12910 },
12911 {
12912 name: "LinkEventDefinition",
12913 superClass: [
12914 "EventDefinition"
12915 ],
12916 properties: [
12917 {
12918 name: "name",
12919 isAttr: true,
12920 type: "String"
12921 },
12922 {
12923 name: "target",
12924 type: "LinkEventDefinition",
12925 isAttr: true,
12926 isReference: true
12927 },
12928 {
12929 name: "source",
12930 type: "LinkEventDefinition",
12931 isMany: true,
12932 isReference: true
12933 }
12934 ]
12935 },
12936 {
12937 name: "MessageEventDefinition",
12938 superClass: [
12939 "EventDefinition"
12940 ],
12941 properties: [
12942 {
12943 name: "messageRef",
12944 type: "Message",
12945 isAttr: true,
12946 isReference: true
12947 },
12948 {
12949 name: "operationRef",
12950 type: "Operation",
12951 isAttr: true,
12952 isReference: true
12953 }
12954 ]
12955 },
12956 {
12957 name: "ConditionalEventDefinition",
12958 superClass: [
12959 "EventDefinition"
12960 ],
12961 properties: [
12962 {
12963 name: "condition",
12964 type: "Expression",
12965 xml: {
12966 serialize: "xsi:type"
12967 }
12968 }
12969 ]
12970 },
12971 {
12972 name: "SignalEventDefinition",
12973 superClass: [
12974 "EventDefinition"
12975 ],
12976 properties: [
12977 {
12978 name: "signalRef",
12979 type: "Signal",
12980 isAttr: true,
12981 isReference: true
12982 }
12983 ]
12984 },
12985 {
12986 name: "Signal",
12987 superClass: [
12988 "RootElement"
12989 ],
12990 properties: [
12991 {
12992 name: "structureRef",
12993 type: "ItemDefinition",
12994 isAttr: true,
12995 isReference: true
12996 },
12997 {
12998 name: "name",
12999 isAttr: true,
13000 type: "String"
13001 }
13002 ]
13003 },
13004 {
13005 name: "ImplicitThrowEvent",
13006 superClass: [
13007 "ThrowEvent"
13008 ]
13009 },
13010 {
13011 name: "DataState",
13012 superClass: [
13013 "BaseElement"
13014 ],
13015 properties: [
13016 {
13017 name: "name",
13018 isAttr: true,
13019 type: "String"
13020 }
13021 ]
13022 },
13023 {
13024 name: "ItemAwareElement",
13025 superClass: [
13026 "BaseElement"
13027 ],
13028 properties: [
13029 {
13030 name: "itemSubjectRef",
13031 type: "ItemDefinition",
13032 isAttr: true,
13033 isReference: true
13034 },
13035 {
13036 name: "dataState",
13037 type: "DataState"
13038 }
13039 ]
13040 },
13041 {
13042 name: "DataAssociation",
13043 superClass: [
13044 "BaseElement"
13045 ],
13046 properties: [
13047 {
13048 name: "sourceRef",
13049 type: "ItemAwareElement",
13050 isMany: true,
13051 isReference: true
13052 },
13053 {
13054 name: "targetRef",
13055 type: "ItemAwareElement",
13056 isReference: true
13057 },
13058 {
13059 name: "transformation",
13060 type: "FormalExpression",
13061 xml: {
13062 serialize: "property"
13063 }
13064 },
13065 {
13066 name: "assignment",
13067 type: "Assignment",
13068 isMany: true
13069 }
13070 ]
13071 },
13072 {
13073 name: "DataInput",
13074 superClass: [
13075 "ItemAwareElement"
13076 ],
13077 properties: [
13078 {
13079 name: "name",
13080 isAttr: true,
13081 type: "String"
13082 },
13083 {
13084 name: "isCollection",
13085 "default": false,
13086 isAttr: true,
13087 type: "Boolean"
13088 },
13089 {
13090 name: "inputSetRef",
13091 type: "InputSet",
13092 isMany: true,
13093 isVirtual: true,
13094 isReference: true
13095 },
13096 {
13097 name: "inputSetWithOptional",
13098 type: "InputSet",
13099 isMany: true,
13100 isVirtual: true,
13101 isReference: true
13102 },
13103 {
13104 name: "inputSetWithWhileExecuting",
13105 type: "InputSet",
13106 isMany: true,
13107 isVirtual: true,
13108 isReference: true
13109 }
13110 ]
13111 },
13112 {
13113 name: "DataOutput",
13114 superClass: [
13115 "ItemAwareElement"
13116 ],
13117 properties: [
13118 {
13119 name: "name",
13120 isAttr: true,
13121 type: "String"
13122 },
13123 {
13124 name: "isCollection",
13125 "default": false,
13126 isAttr: true,
13127 type: "Boolean"
13128 },
13129 {
13130 name: "outputSetRef",
13131 type: "OutputSet",
13132 isMany: true,
13133 isVirtual: true,
13134 isReference: true
13135 },
13136 {
13137 name: "outputSetWithOptional",
13138 type: "OutputSet",
13139 isMany: true,
13140 isVirtual: true,
13141 isReference: true
13142 },
13143 {
13144 name: "outputSetWithWhileExecuting",
13145 type: "OutputSet",
13146 isMany: true,
13147 isVirtual: true,
13148 isReference: true
13149 }
13150 ]
13151 },
13152 {
13153 name: "InputSet",
13154 superClass: [
13155 "BaseElement"
13156 ],
13157 properties: [
13158 {
13159 name: "name",
13160 isAttr: true,
13161 type: "String"
13162 },
13163 {
13164 name: "dataInputRefs",
13165 type: "DataInput",
13166 isMany: true,
13167 isReference: true
13168 },
13169 {
13170 name: "optionalInputRefs",
13171 type: "DataInput",
13172 isMany: true,
13173 isReference: true
13174 },
13175 {
13176 name: "whileExecutingInputRefs",
13177 type: "DataInput",
13178 isMany: true,
13179 isReference: true
13180 },
13181 {
13182 name: "outputSetRefs",
13183 type: "OutputSet",
13184 isMany: true,
13185 isReference: true
13186 }
13187 ]
13188 },
13189 {
13190 name: "OutputSet",
13191 superClass: [
13192 "BaseElement"
13193 ],
13194 properties: [
13195 {
13196 name: "dataOutputRefs",
13197 type: "DataOutput",
13198 isMany: true,
13199 isReference: true
13200 },
13201 {
13202 name: "name",
13203 isAttr: true,
13204 type: "String"
13205 },
13206 {
13207 name: "inputSetRefs",
13208 type: "InputSet",
13209 isMany: true,
13210 isReference: true
13211 },
13212 {
13213 name: "optionalOutputRefs",
13214 type: "DataOutput",
13215 isMany: true,
13216 isReference: true
13217 },
13218 {
13219 name: "whileExecutingOutputRefs",
13220 type: "DataOutput",
13221 isMany: true,
13222 isReference: true
13223 }
13224 ]
13225 },
13226 {
13227 name: "Property",
13228 superClass: [
13229 "ItemAwareElement"
13230 ],
13231 properties: [
13232 {
13233 name: "name",
13234 isAttr: true,
13235 type: "String"
13236 }
13237 ]
13238 },
13239 {
13240 name: "DataInputAssociation",
13241 superClass: [
13242 "DataAssociation"
13243 ]
13244 },
13245 {
13246 name: "DataOutputAssociation",
13247 superClass: [
13248 "DataAssociation"
13249 ]
13250 },
13251 {
13252 name: "InputOutputSpecification",
13253 superClass: [
13254 "BaseElement"
13255 ],
13256 properties: [
13257 {
13258 name: "dataInputs",
13259 type: "DataInput",
13260 isMany: true
13261 },
13262 {
13263 name: "dataOutputs",
13264 type: "DataOutput",
13265 isMany: true
13266 },
13267 {
13268 name: "inputSets",
13269 type: "InputSet",
13270 isMany: true
13271 },
13272 {
13273 name: "outputSets",
13274 type: "OutputSet",
13275 isMany: true
13276 }
13277 ]
13278 },
13279 {
13280 name: "DataObject",
13281 superClass: [
13282 "FlowElement",
13283 "ItemAwareElement"
13284 ],
13285 properties: [
13286 {
13287 name: "isCollection",
13288 "default": false,
13289 isAttr: true,
13290 type: "Boolean"
13291 }
13292 ]
13293 },
13294 {
13295 name: "InputOutputBinding",
13296 properties: [
13297 {
13298 name: "inputDataRef",
13299 type: "InputSet",
13300 isAttr: true,
13301 isReference: true
13302 },
13303 {
13304 name: "outputDataRef",
13305 type: "OutputSet",
13306 isAttr: true,
13307 isReference: true
13308 },
13309 {
13310 name: "operationRef",
13311 type: "Operation",
13312 isAttr: true,
13313 isReference: true
13314 }
13315 ]
13316 },
13317 {
13318 name: "Assignment",
13319 superClass: [
13320 "BaseElement"
13321 ],
13322 properties: [
13323 {
13324 name: "from",
13325 type: "Expression",
13326 xml: {
13327 serialize: "xsi:type"
13328 }
13329 },
13330 {
13331 name: "to",
13332 type: "Expression",
13333 xml: {
13334 serialize: "xsi:type"
13335 }
13336 }
13337 ]
13338 },
13339 {
13340 name: "DataStore",
13341 superClass: [
13342 "RootElement",
13343 "ItemAwareElement"
13344 ],
13345 properties: [
13346 {
13347 name: "name",
13348 isAttr: true,
13349 type: "String"
13350 },
13351 {
13352 name: "capacity",
13353 isAttr: true,
13354 type: "Integer"
13355 },
13356 {
13357 name: "isUnlimited",
13358 "default": true,
13359 isAttr: true,
13360 type: "Boolean"
13361 }
13362 ]
13363 },
13364 {
13365 name: "DataStoreReference",
13366 superClass: [
13367 "ItemAwareElement",
13368 "FlowElement"
13369 ],
13370 properties: [
13371 {
13372 name: "dataStoreRef",
13373 type: "DataStore",
13374 isAttr: true,
13375 isReference: true
13376 }
13377 ]
13378 },
13379 {
13380 name: "DataObjectReference",
13381 superClass: [
13382 "ItemAwareElement",
13383 "FlowElement"
13384 ],
13385 properties: [
13386 {
13387 name: "dataObjectRef",
13388 type: "DataObject",
13389 isAttr: true,
13390 isReference: true
13391 }
13392 ]
13393 },
13394 {
13395 name: "ConversationLink",
13396 superClass: [
13397 "BaseElement"
13398 ],
13399 properties: [
13400 {
13401 name: "sourceRef",
13402 type: "InteractionNode",
13403 isAttr: true,
13404 isReference: true
13405 },
13406 {
13407 name: "targetRef",
13408 type: "InteractionNode",
13409 isAttr: true,
13410 isReference: true
13411 },
13412 {
13413 name: "name",
13414 isAttr: true,
13415 type: "String"
13416 }
13417 ]
13418 },
13419 {
13420 name: "ConversationAssociation",
13421 superClass: [
13422 "BaseElement"
13423 ],
13424 properties: [
13425 {
13426 name: "innerConversationNodeRef",
13427 type: "ConversationNode",
13428 isAttr: true,
13429 isReference: true
13430 },
13431 {
13432 name: "outerConversationNodeRef",
13433 type: "ConversationNode",
13434 isAttr: true,
13435 isReference: true
13436 }
13437 ]
13438 },
13439 {
13440 name: "CallConversation",
13441 superClass: [
13442 "ConversationNode"
13443 ],
13444 properties: [
13445 {
13446 name: "calledCollaborationRef",
13447 type: "Collaboration",
13448 isAttr: true,
13449 isReference: true
13450 },
13451 {
13452 name: "participantAssociations",
13453 type: "ParticipantAssociation",
13454 isMany: true
13455 }
13456 ]
13457 },
13458 {
13459 name: "Conversation",
13460 superClass: [
13461 "ConversationNode"
13462 ]
13463 },
13464 {
13465 name: "SubConversation",
13466 superClass: [
13467 "ConversationNode"
13468 ],
13469 properties: [
13470 {
13471 name: "conversationNodes",
13472 type: "ConversationNode",
13473 isMany: true
13474 }
13475 ]
13476 },
13477 {
13478 name: "ConversationNode",
13479 isAbstract: true,
13480 superClass: [
13481 "InteractionNode",
13482 "BaseElement"
13483 ],
13484 properties: [
13485 {
13486 name: "name",
13487 isAttr: true,
13488 type: "String"
13489 },
13490 {
13491 name: "participantRef",
13492 type: "Participant",
13493 isMany: true,
13494 isReference: true
13495 },
13496 {
13497 name: "messageFlowRefs",
13498 type: "MessageFlow",
13499 isMany: true,
13500 isReference: true
13501 },
13502 {
13503 name: "correlationKeys",
13504 type: "CorrelationKey",
13505 isMany: true
13506 }
13507 ]
13508 },
13509 {
13510 name: "GlobalConversation",
13511 superClass: [
13512 "Collaboration"
13513 ]
13514 },
13515 {
13516 name: "PartnerEntity",
13517 superClass: [
13518 "RootElement"
13519 ],
13520 properties: [
13521 {
13522 name: "name",
13523 isAttr: true,
13524 type: "String"
13525 },
13526 {
13527 name: "participantRef",
13528 type: "Participant",
13529 isMany: true,
13530 isReference: true
13531 }
13532 ]
13533 },
13534 {
13535 name: "PartnerRole",
13536 superClass: [
13537 "RootElement"
13538 ],
13539 properties: [
13540 {
13541 name: "name",
13542 isAttr: true,
13543 type: "String"
13544 },
13545 {
13546 name: "participantRef",
13547 type: "Participant",
13548 isMany: true,
13549 isReference: true
13550 }
13551 ]
13552 },
13553 {
13554 name: "CorrelationProperty",
13555 superClass: [
13556 "RootElement"
13557 ],
13558 properties: [
13559 {
13560 name: "correlationPropertyRetrievalExpression",
13561 type: "CorrelationPropertyRetrievalExpression",
13562 isMany: true
13563 },
13564 {
13565 name: "name",
13566 isAttr: true,
13567 type: "String"
13568 },
13569 {
13570 name: "type",
13571 type: "ItemDefinition",
13572 isAttr: true,
13573 isReference: true
13574 }
13575 ]
13576 },
13577 {
13578 name: "Error",
13579 superClass: [
13580 "RootElement"
13581 ],
13582 properties: [
13583 {
13584 name: "structureRef",
13585 type: "ItemDefinition",
13586 isAttr: true,
13587 isReference: true
13588 },
13589 {
13590 name: "name",
13591 isAttr: true,
13592 type: "String"
13593 },
13594 {
13595 name: "errorCode",
13596 isAttr: true,
13597 type: "String"
13598 }
13599 ]
13600 },
13601 {
13602 name: "CorrelationKey",
13603 superClass: [
13604 "BaseElement"
13605 ],
13606 properties: [
13607 {
13608 name: "correlationPropertyRef",
13609 type: "CorrelationProperty",
13610 isMany: true,
13611 isReference: true
13612 },
13613 {
13614 name: "name",
13615 isAttr: true,
13616 type: "String"
13617 }
13618 ]
13619 },
13620 {
13621 name: "Expression",
13622 superClass: [
13623 "BaseElement"
13624 ],
13625 isAbstract: false,
13626 properties: [
13627 {
13628 name: "body",
13629 isBody: true,
13630 type: "String"
13631 }
13632 ]
13633 },
13634 {
13635 name: "FormalExpression",
13636 superClass: [
13637 "Expression"
13638 ],
13639 properties: [
13640 {
13641 name: "language",
13642 isAttr: true,
13643 type: "String"
13644 },
13645 {
13646 name: "evaluatesToTypeRef",
13647 type: "ItemDefinition",
13648 isAttr: true,
13649 isReference: true
13650 }
13651 ]
13652 },
13653 {
13654 name: "Message",
13655 superClass: [
13656 "RootElement"
13657 ],
13658 properties: [
13659 {
13660 name: "name",
13661 isAttr: true,
13662 type: "String"
13663 },
13664 {
13665 name: "itemRef",
13666 type: "ItemDefinition",
13667 isAttr: true,
13668 isReference: true
13669 }
13670 ]
13671 },
13672 {
13673 name: "ItemDefinition",
13674 superClass: [
13675 "RootElement"
13676 ],
13677 properties: [
13678 {
13679 name: "itemKind",
13680 type: "ItemKind",
13681 isAttr: true
13682 },
13683 {
13684 name: "structureRef",
13685 isAttr: true,
13686 type: "String"
13687 },
13688 {
13689 name: "isCollection",
13690 "default": false,
13691 isAttr: true,
13692 type: "Boolean"
13693 },
13694 {
13695 name: "import",
13696 type: "Import",
13697 isAttr: true,
13698 isReference: true
13699 }
13700 ]
13701 },
13702 {
13703 name: "FlowElement",
13704 isAbstract: true,
13705 superClass: [
13706 "BaseElement"
13707 ],
13708 properties: [
13709 {
13710 name: "name",
13711 isAttr: true,
13712 type: "String"
13713 },
13714 {
13715 name: "auditing",
13716 type: "Auditing"
13717 },
13718 {
13719 name: "monitoring",
13720 type: "Monitoring"
13721 },
13722 {
13723 name: "categoryValueRef",
13724 type: "CategoryValue",
13725 isMany: true,
13726 isReference: true
13727 }
13728 ]
13729 },
13730 {
13731 name: "SequenceFlow",
13732 superClass: [
13733 "FlowElement"
13734 ],
13735 properties: [
13736 {
13737 name: "isImmediate",
13738 isAttr: true,
13739 type: "Boolean"
13740 },
13741 {
13742 name: "conditionExpression",
13743 type: "Expression",
13744 xml: {
13745 serialize: "xsi:type"
13746 }
13747 },
13748 {
13749 name: "sourceRef",
13750 type: "FlowNode",
13751 isAttr: true,
13752 isReference: true
13753 },
13754 {
13755 name: "targetRef",
13756 type: "FlowNode",
13757 isAttr: true,
13758 isReference: true
13759 }
13760 ]
13761 },
13762 {
13763 name: "FlowElementsContainer",
13764 isAbstract: true,
13765 superClass: [
13766 "BaseElement"
13767 ],
13768 properties: [
13769 {
13770 name: "laneSets",
13771 type: "LaneSet",
13772 isMany: true
13773 },
13774 {
13775 name: "flowElements",
13776 type: "FlowElement",
13777 isMany: true
13778 }
13779 ]
13780 },
13781 {
13782 name: "CallableElement",
13783 isAbstract: true,
13784 superClass: [
13785 "RootElement"
13786 ],
13787 properties: [
13788 {
13789 name: "name",
13790 isAttr: true,
13791 type: "String"
13792 },
13793 {
13794 name: "ioSpecification",
13795 type: "InputOutputSpecification",
13796 xml: {
13797 serialize: "property"
13798 }
13799 },
13800 {
13801 name: "supportedInterfaceRef",
13802 type: "Interface",
13803 isMany: true,
13804 isReference: true
13805 },
13806 {
13807 name: "ioBinding",
13808 type: "InputOutputBinding",
13809 isMany: true,
13810 xml: {
13811 serialize: "property"
13812 }
13813 }
13814 ]
13815 },
13816 {
13817 name: "FlowNode",
13818 isAbstract: true,
13819 superClass: [
13820 "FlowElement"
13821 ],
13822 properties: [
13823 {
13824 name: "incoming",
13825 type: "SequenceFlow",
13826 isMany: true,
13827 isReference: true
13828 },
13829 {
13830 name: "outgoing",
13831 type: "SequenceFlow",
13832 isMany: true,
13833 isReference: true
13834 },
13835 {
13836 name: "lanes",
13837 type: "Lane",
13838 isMany: true,
13839 isVirtual: true,
13840 isReference: true
13841 }
13842 ]
13843 },
13844 {
13845 name: "CorrelationPropertyRetrievalExpression",
13846 superClass: [
13847 "BaseElement"
13848 ],
13849 properties: [
13850 {
13851 name: "messagePath",
13852 type: "FormalExpression"
13853 },
13854 {
13855 name: "messageRef",
13856 type: "Message",
13857 isAttr: true,
13858 isReference: true
13859 }
13860 ]
13861 },
13862 {
13863 name: "CorrelationPropertyBinding",
13864 superClass: [
13865 "BaseElement"
13866 ],
13867 properties: [
13868 {
13869 name: "dataPath",
13870 type: "FormalExpression"
13871 },
13872 {
13873 name: "correlationPropertyRef",
13874 type: "CorrelationProperty",
13875 isAttr: true,
13876 isReference: true
13877 }
13878 ]
13879 },
13880 {
13881 name: "Resource",
13882 superClass: [
13883 "RootElement"
13884 ],
13885 properties: [
13886 {
13887 name: "name",
13888 isAttr: true,
13889 type: "String"
13890 },
13891 {
13892 name: "resourceParameters",
13893 type: "ResourceParameter",
13894 isMany: true
13895 }
13896 ]
13897 },
13898 {
13899 name: "ResourceParameter",
13900 superClass: [
13901 "BaseElement"
13902 ],
13903 properties: [
13904 {
13905 name: "name",
13906 isAttr: true,
13907 type: "String"
13908 },
13909 {
13910 name: "isRequired",
13911 isAttr: true,
13912 type: "Boolean"
13913 },
13914 {
13915 name: "type",
13916 type: "ItemDefinition",
13917 isAttr: true,
13918 isReference: true
13919 }
13920 ]
13921 },
13922 {
13923 name: "CorrelationSubscription",
13924 superClass: [
13925 "BaseElement"
13926 ],
13927 properties: [
13928 {
13929 name: "correlationKeyRef",
13930 type: "CorrelationKey",
13931 isAttr: true,
13932 isReference: true
13933 },
13934 {
13935 name: "correlationPropertyBinding",
13936 type: "CorrelationPropertyBinding",
13937 isMany: true
13938 }
13939 ]
13940 },
13941 {
13942 name: "MessageFlow",
13943 superClass: [
13944 "BaseElement"
13945 ],
13946 properties: [
13947 {
13948 name: "name",
13949 isAttr: true,
13950 type: "String"
13951 },
13952 {
13953 name: "sourceRef",
13954 type: "InteractionNode",
13955 isAttr: true,
13956 isReference: true
13957 },
13958 {
13959 name: "targetRef",
13960 type: "InteractionNode",
13961 isAttr: true,
13962 isReference: true
13963 },
13964 {
13965 name: "messageRef",
13966 type: "Message",
13967 isAttr: true,
13968 isReference: true
13969 }
13970 ]
13971 },
13972 {
13973 name: "MessageFlowAssociation",
13974 superClass: [
13975 "BaseElement"
13976 ],
13977 properties: [
13978 {
13979 name: "innerMessageFlowRef",
13980 type: "MessageFlow",
13981 isAttr: true,
13982 isReference: true
13983 },
13984 {
13985 name: "outerMessageFlowRef",
13986 type: "MessageFlow",
13987 isAttr: true,
13988 isReference: true
13989 }
13990 ]
13991 },
13992 {
13993 name: "InteractionNode",
13994 isAbstract: true,
13995 properties: [
13996 {
13997 name: "incomingConversationLinks",
13998 type: "ConversationLink",
13999 isMany: true,
14000 isVirtual: true,
14001 isReference: true
14002 },
14003 {
14004 name: "outgoingConversationLinks",
14005 type: "ConversationLink",
14006 isMany: true,
14007 isVirtual: true,
14008 isReference: true
14009 }
14010 ]
14011 },
14012 {
14013 name: "Participant",
14014 superClass: [
14015 "InteractionNode",
14016 "BaseElement"
14017 ],
14018 properties: [
14019 {
14020 name: "name",
14021 isAttr: true,
14022 type: "String"
14023 },
14024 {
14025 name: "interfaceRef",
14026 type: "Interface",
14027 isMany: true,
14028 isReference: true
14029 },
14030 {
14031 name: "participantMultiplicity",
14032 type: "ParticipantMultiplicity"
14033 },
14034 {
14035 name: "endPointRefs",
14036 type: "EndPoint",
14037 isMany: true,
14038 isReference: true
14039 },
14040 {
14041 name: "processRef",
14042 type: "Process",
14043 isAttr: true,
14044 isReference: true
14045 }
14046 ]
14047 },
14048 {
14049 name: "ParticipantAssociation",
14050 superClass: [
14051 "BaseElement"
14052 ],
14053 properties: [
14054 {
14055 name: "innerParticipantRef",
14056 type: "Participant",
14057 isAttr: true,
14058 isReference: true
14059 },
14060 {
14061 name: "outerParticipantRef",
14062 type: "Participant",
14063 isAttr: true,
14064 isReference: true
14065 }
14066 ]
14067 },
14068 {
14069 name: "ParticipantMultiplicity",
14070 properties: [
14071 {
14072 name: "minimum",
14073 "default": 0,
14074 isAttr: true,
14075 type: "Integer"
14076 },
14077 {
14078 name: "maximum",
14079 "default": 1,
14080 isAttr: true,
14081 type: "Integer"
14082 }
14083 ],
14084 superClass: [
14085 "BaseElement"
14086 ]
14087 },
14088 {
14089 name: "Collaboration",
14090 superClass: [
14091 "RootElement"
14092 ],
14093 properties: [
14094 {
14095 name: "name",
14096 isAttr: true,
14097 type: "String"
14098 },
14099 {
14100 name: "isClosed",
14101 isAttr: true,
14102 type: "Boolean"
14103 },
14104 {
14105 name: "participants",
14106 type: "Participant",
14107 isMany: true
14108 },
14109 {
14110 name: "messageFlows",
14111 type: "MessageFlow",
14112 isMany: true
14113 },
14114 {
14115 name: "artifacts",
14116 type: "Artifact",
14117 isMany: true
14118 },
14119 {
14120 name: "conversations",
14121 type: "ConversationNode",
14122 isMany: true
14123 },
14124 {
14125 name: "conversationAssociations",
14126 type: "ConversationAssociation"
14127 },
14128 {
14129 name: "participantAssociations",
14130 type: "ParticipantAssociation",
14131 isMany: true
14132 },
14133 {
14134 name: "messageFlowAssociations",
14135 type: "MessageFlowAssociation",
14136 isMany: true
14137 },
14138 {
14139 name: "correlationKeys",
14140 type: "CorrelationKey",
14141 isMany: true
14142 },
14143 {
14144 name: "choreographyRef",
14145 type: "Choreography",
14146 isMany: true,
14147 isReference: true
14148 },
14149 {
14150 name: "conversationLinks",
14151 type: "ConversationLink",
14152 isMany: true
14153 }
14154 ]
14155 },
14156 {
14157 name: "ChoreographyActivity",
14158 isAbstract: true,
14159 superClass: [
14160 "FlowNode"
14161 ],
14162 properties: [
14163 {
14164 name: "participantRef",
14165 type: "Participant",
14166 isMany: true,
14167 isReference: true
14168 },
14169 {
14170 name: "initiatingParticipantRef",
14171 type: "Participant",
14172 isAttr: true,
14173 isReference: true
14174 },
14175 {
14176 name: "correlationKeys",
14177 type: "CorrelationKey",
14178 isMany: true
14179 },
14180 {
14181 name: "loopType",
14182 type: "ChoreographyLoopType",
14183 "default": "None",
14184 isAttr: true
14185 }
14186 ]
14187 },
14188 {
14189 name: "CallChoreography",
14190 superClass: [
14191 "ChoreographyActivity"
14192 ],
14193 properties: [
14194 {
14195 name: "calledChoreographyRef",
14196 type: "Choreography",
14197 isAttr: true,
14198 isReference: true
14199 },
14200 {
14201 name: "participantAssociations",
14202 type: "ParticipantAssociation",
14203 isMany: true
14204 }
14205 ]
14206 },
14207 {
14208 name: "SubChoreography",
14209 superClass: [
14210 "ChoreographyActivity",
14211 "FlowElementsContainer"
14212 ],
14213 properties: [
14214 {
14215 name: "artifacts",
14216 type: "Artifact",
14217 isMany: true
14218 }
14219 ]
14220 },
14221 {
14222 name: "ChoreographyTask",
14223 superClass: [
14224 "ChoreographyActivity"
14225 ],
14226 properties: [
14227 {
14228 name: "messageFlowRef",
14229 type: "MessageFlow",
14230 isMany: true,
14231 isReference: true
14232 }
14233 ]
14234 },
14235 {
14236 name: "Choreography",
14237 superClass: [
14238 "Collaboration",
14239 "FlowElementsContainer"
14240 ]
14241 },
14242 {
14243 name: "GlobalChoreographyTask",
14244 superClass: [
14245 "Choreography"
14246 ],
14247 properties: [
14248 {
14249 name: "initiatingParticipantRef",
14250 type: "Participant",
14251 isAttr: true,
14252 isReference: true
14253 }
14254 ]
14255 },
14256 {
14257 name: "TextAnnotation",
14258 superClass: [
14259 "Artifact"
14260 ],
14261 properties: [
14262 {
14263 name: "text",
14264 type: "String"
14265 },
14266 {
14267 name: "textFormat",
14268 "default": "text/plain",
14269 isAttr: true,
14270 type: "String"
14271 }
14272 ]
14273 },
14274 {
14275 name: "Group",
14276 superClass: [
14277 "Artifact"
14278 ],
14279 properties: [
14280 {
14281 name: "categoryValueRef",
14282 type: "CategoryValue",
14283 isAttr: true,
14284 isReference: true
14285 }
14286 ]
14287 },
14288 {
14289 name: "Association",
14290 superClass: [
14291 "Artifact"
14292 ],
14293 properties: [
14294 {
14295 name: "associationDirection",
14296 type: "AssociationDirection",
14297 isAttr: true
14298 },
14299 {
14300 name: "sourceRef",
14301 type: "BaseElement",
14302 isAttr: true,
14303 isReference: true
14304 },
14305 {
14306 name: "targetRef",
14307 type: "BaseElement",
14308 isAttr: true,
14309 isReference: true
14310 }
14311 ]
14312 },
14313 {
14314 name: "Category",
14315 superClass: [
14316 "RootElement"
14317 ],
14318 properties: [
14319 {
14320 name: "categoryValue",
14321 type: "CategoryValue",
14322 isMany: true
14323 },
14324 {
14325 name: "name",
14326 isAttr: true,
14327 type: "String"
14328 }
14329 ]
14330 },
14331 {
14332 name: "Artifact",
14333 isAbstract: true,
14334 superClass: [
14335 "BaseElement"
14336 ]
14337 },
14338 {
14339 name: "CategoryValue",
14340 superClass: [
14341 "BaseElement"
14342 ],
14343 properties: [
14344 {
14345 name: "categorizedFlowElements",
14346 type: "FlowElement",
14347 isMany: true,
14348 isVirtual: true,
14349 isReference: true
14350 },
14351 {
14352 name: "value",
14353 isAttr: true,
14354 type: "String"
14355 }
14356 ]
14357 },
14358 {
14359 name: "Activity",
14360 isAbstract: true,
14361 superClass: [
14362 "FlowNode"
14363 ],
14364 properties: [
14365 {
14366 name: "isForCompensation",
14367 "default": false,
14368 isAttr: true,
14369 type: "Boolean"
14370 },
14371 {
14372 name: "default",
14373 type: "SequenceFlow",
14374 isAttr: true,
14375 isReference: true
14376 },
14377 {
14378 name: "ioSpecification",
14379 type: "InputOutputSpecification",
14380 xml: {
14381 serialize: "property"
14382 }
14383 },
14384 {
14385 name: "boundaryEventRefs",
14386 type: "BoundaryEvent",
14387 isMany: true,
14388 isReference: true
14389 },
14390 {
14391 name: "properties",
14392 type: "Property",
14393 isMany: true
14394 },
14395 {
14396 name: "dataInputAssociations",
14397 type: "DataInputAssociation",
14398 isMany: true
14399 },
14400 {
14401 name: "dataOutputAssociations",
14402 type: "DataOutputAssociation",
14403 isMany: true
14404 },
14405 {
14406 name: "startQuantity",
14407 "default": 1,
14408 isAttr: true,
14409 type: "Integer"
14410 },
14411 {
14412 name: "resources",
14413 type: "ResourceRole",
14414 isMany: true
14415 },
14416 {
14417 name: "completionQuantity",
14418 "default": 1,
14419 isAttr: true,
14420 type: "Integer"
14421 },
14422 {
14423 name: "loopCharacteristics",
14424 type: "LoopCharacteristics"
14425 }
14426 ]
14427 },
14428 {
14429 name: "ServiceTask",
14430 superClass: [
14431 "Task"
14432 ],
14433 properties: [
14434 {
14435 name: "implementation",
14436 isAttr: true,
14437 type: "String"
14438 },
14439 {
14440 name: "operationRef",
14441 type: "Operation",
14442 isAttr: true,
14443 isReference: true
14444 }
14445 ]
14446 },
14447 {
14448 name: "SubProcess",
14449 superClass: [
14450 "Activity",
14451 "FlowElementsContainer",
14452 "InteractionNode"
14453 ],
14454 properties: [
14455 {
14456 name: "triggeredByEvent",
14457 "default": false,
14458 isAttr: true,
14459 type: "Boolean"
14460 },
14461 {
14462 name: "artifacts",
14463 type: "Artifact",
14464 isMany: true
14465 }
14466 ]
14467 },
14468 {
14469 name: "LoopCharacteristics",
14470 isAbstract: true,
14471 superClass: [
14472 "BaseElement"
14473 ]
14474 },
14475 {
14476 name: "MultiInstanceLoopCharacteristics",
14477 superClass: [
14478 "LoopCharacteristics"
14479 ],
14480 properties: [
14481 {
14482 name: "isSequential",
14483 "default": false,
14484 isAttr: true,
14485 type: "Boolean"
14486 },
14487 {
14488 name: "behavior",
14489 type: "MultiInstanceBehavior",
14490 "default": "All",
14491 isAttr: true
14492 },
14493 {
14494 name: "loopCardinality",
14495 type: "Expression",
14496 xml: {
14497 serialize: "xsi:type"
14498 }
14499 },
14500 {
14501 name: "loopDataInputRef",
14502 type: "ItemAwareElement",
14503 isReference: true
14504 },
14505 {
14506 name: "loopDataOutputRef",
14507 type: "ItemAwareElement",
14508 isReference: true
14509 },
14510 {
14511 name: "inputDataItem",
14512 type: "DataInput",
14513 xml: {
14514 serialize: "property"
14515 }
14516 },
14517 {
14518 name: "outputDataItem",
14519 type: "DataOutput",
14520 xml: {
14521 serialize: "property"
14522 }
14523 },
14524 {
14525 name: "complexBehaviorDefinition",
14526 type: "ComplexBehaviorDefinition",
14527 isMany: true
14528 },
14529 {
14530 name: "completionCondition",
14531 type: "Expression",
14532 xml: {
14533 serialize: "xsi:type"
14534 }
14535 },
14536 {
14537 name: "oneBehaviorEventRef",
14538 type: "EventDefinition",
14539 isAttr: true,
14540 isReference: true
14541 },
14542 {
14543 name: "noneBehaviorEventRef",
14544 type: "EventDefinition",
14545 isAttr: true,
14546 isReference: true
14547 }
14548 ]
14549 },
14550 {
14551 name: "StandardLoopCharacteristics",
14552 superClass: [
14553 "LoopCharacteristics"
14554 ],
14555 properties: [
14556 {
14557 name: "testBefore",
14558 "default": false,
14559 isAttr: true,
14560 type: "Boolean"
14561 },
14562 {
14563 name: "loopCondition",
14564 type: "Expression",
14565 xml: {
14566 serialize: "xsi:type"
14567 }
14568 },
14569 {
14570 name: "loopMaximum",
14571 type: "Integer",
14572 isAttr: true
14573 }
14574 ]
14575 },
14576 {
14577 name: "CallActivity",
14578 superClass: [
14579 "Activity",
14580 "InteractionNode"
14581 ],
14582 properties: [
14583 {
14584 name: "calledElement",
14585 type: "String",
14586 isAttr: true
14587 }
14588 ]
14589 },
14590 {
14591 name: "Task",
14592 superClass: [
14593 "Activity",
14594 "InteractionNode"
14595 ]
14596 },
14597 {
14598 name: "SendTask",
14599 superClass: [
14600 "Task"
14601 ],
14602 properties: [
14603 {
14604 name: "implementation",
14605 isAttr: true,
14606 type: "String"
14607 },
14608 {
14609 name: "operationRef",
14610 type: "Operation",
14611 isAttr: true,
14612 isReference: true
14613 },
14614 {
14615 name: "messageRef",
14616 type: "Message",
14617 isAttr: true,
14618 isReference: true
14619 }
14620 ]
14621 },
14622 {
14623 name: "ReceiveTask",
14624 superClass: [
14625 "Task"
14626 ],
14627 properties: [
14628 {
14629 name: "implementation",
14630 isAttr: true,
14631 type: "String"
14632 },
14633 {
14634 name: "instantiate",
14635 "default": false,
14636 isAttr: true,
14637 type: "Boolean"
14638 },
14639 {
14640 name: "operationRef",
14641 type: "Operation",
14642 isAttr: true,
14643 isReference: true
14644 },
14645 {
14646 name: "messageRef",
14647 type: "Message",
14648 isAttr: true,
14649 isReference: true
14650 }
14651 ]
14652 },
14653 {
14654 name: "ScriptTask",
14655 superClass: [
14656 "Task"
14657 ],
14658 properties: [
14659 {
14660 name: "scriptFormat",
14661 isAttr: true,
14662 type: "String"
14663 },
14664 {
14665 name: "script",
14666 type: "String"
14667 }
14668 ]
14669 },
14670 {
14671 name: "BusinessRuleTask",
14672 superClass: [
14673 "Task"
14674 ],
14675 properties: [
14676 {
14677 name: "implementation",
14678 isAttr: true,
14679 type: "String"
14680 }
14681 ]
14682 },
14683 {
14684 name: "AdHocSubProcess",
14685 superClass: [
14686 "SubProcess"
14687 ],
14688 properties: [
14689 {
14690 name: "completionCondition",
14691 type: "Expression",
14692 xml: {
14693 serialize: "xsi:type"
14694 }
14695 },
14696 {
14697 name: "ordering",
14698 type: "AdHocOrdering",
14699 isAttr: true
14700 },
14701 {
14702 name: "cancelRemainingInstances",
14703 "default": true,
14704 isAttr: true,
14705 type: "Boolean"
14706 }
14707 ]
14708 },
14709 {
14710 name: "Transaction",
14711 superClass: [
14712 "SubProcess"
14713 ],
14714 properties: [
14715 {
14716 name: "protocol",
14717 isAttr: true,
14718 type: "String"
14719 },
14720 {
14721 name: "method",
14722 isAttr: true,
14723 type: "String"
14724 }
14725 ]
14726 },
14727 {
14728 name: "GlobalScriptTask",
14729 superClass: [
14730 "GlobalTask"
14731 ],
14732 properties: [
14733 {
14734 name: "scriptLanguage",
14735 isAttr: true,
14736 type: "String"
14737 },
14738 {
14739 name: "script",
14740 isAttr: true,
14741 type: "String"
14742 }
14743 ]
14744 },
14745 {
14746 name: "GlobalBusinessRuleTask",
14747 superClass: [
14748 "GlobalTask"
14749 ],
14750 properties: [
14751 {
14752 name: "implementation",
14753 isAttr: true,
14754 type: "String"
14755 }
14756 ]
14757 },
14758 {
14759 name: "ComplexBehaviorDefinition",
14760 superClass: [
14761 "BaseElement"
14762 ],
14763 properties: [
14764 {
14765 name: "condition",
14766 type: "FormalExpression"
14767 },
14768 {
14769 name: "event",
14770 type: "ImplicitThrowEvent"
14771 }
14772 ]
14773 },
14774 {
14775 name: "ResourceRole",
14776 superClass: [
14777 "BaseElement"
14778 ],
14779 properties: [
14780 {
14781 name: "resourceRef",
14782 type: "Resource",
14783 isReference: true
14784 },
14785 {
14786 name: "resourceParameterBindings",
14787 type: "ResourceParameterBinding",
14788 isMany: true
14789 },
14790 {
14791 name: "resourceAssignmentExpression",
14792 type: "ResourceAssignmentExpression"
14793 },
14794 {
14795 name: "name",
14796 isAttr: true,
14797 type: "String"
14798 }
14799 ]
14800 },
14801 {
14802 name: "ResourceParameterBinding",
14803 properties: [
14804 {
14805 name: "expression",
14806 type: "Expression",
14807 xml: {
14808 serialize: "xsi:type"
14809 }
14810 },
14811 {
14812 name: "parameterRef",
14813 type: "ResourceParameter",
14814 isAttr: true,
14815 isReference: true
14816 }
14817 ],
14818 superClass: [
14819 "BaseElement"
14820 ]
14821 },
14822 {
14823 name: "ResourceAssignmentExpression",
14824 properties: [
14825 {
14826 name: "expression",
14827 type: "Expression",
14828 xml: {
14829 serialize: "xsi:type"
14830 }
14831 }
14832 ],
14833 superClass: [
14834 "BaseElement"
14835 ]
14836 },
14837 {
14838 name: "Import",
14839 properties: [
14840 {
14841 name: "importType",
14842 isAttr: true,
14843 type: "String"
14844 },
14845 {
14846 name: "location",
14847 isAttr: true,
14848 type: "String"
14849 },
14850 {
14851 name: "namespace",
14852 isAttr: true,
14853 type: "String"
14854 }
14855 ]
14856 },
14857 {
14858 name: "Definitions",
14859 superClass: [
14860 "BaseElement"
14861 ],
14862 properties: [
14863 {
14864 name: "name",
14865 isAttr: true,
14866 type: "String"
14867 },
14868 {
14869 name: "targetNamespace",
14870 isAttr: true,
14871 type: "String"
14872 },
14873 {
14874 name: "expressionLanguage",
14875 "default": "http://www.w3.org/1999/XPath",
14876 isAttr: true,
14877 type: "String"
14878 },
14879 {
14880 name: "typeLanguage",
14881 "default": "http://www.w3.org/2001/XMLSchema",
14882 isAttr: true,
14883 type: "String"
14884 },
14885 {
14886 name: "imports",
14887 type: "Import",
14888 isMany: true
14889 },
14890 {
14891 name: "extensions",
14892 type: "Extension",
14893 isMany: true
14894 },
14895 {
14896 name: "rootElements",
14897 type: "RootElement",
14898 isMany: true
14899 },
14900 {
14901 name: "diagrams",
14902 isMany: true,
14903 type: "bpmndi:BPMNDiagram"
14904 },
14905 {
14906 name: "exporter",
14907 isAttr: true,
14908 type: "String"
14909 },
14910 {
14911 name: "relationships",
14912 type: "Relationship",
14913 isMany: true
14914 },
14915 {
14916 name: "exporterVersion",
14917 isAttr: true,
14918 type: "String"
14919 }
14920 ]
14921 }
14922 ];
14923 var enumerations = [
14924 {
14925 name: "ProcessType",
14926 literalValues: [
14927 {
14928 name: "None"
14929 },
14930 {
14931 name: "Public"
14932 },
14933 {
14934 name: "Private"
14935 }
14936 ]
14937 },
14938 {
14939 name: "GatewayDirection",
14940 literalValues: [
14941 {
14942 name: "Unspecified"
14943 },
14944 {
14945 name: "Converging"
14946 },
14947 {
14948 name: "Diverging"
14949 },
14950 {
14951 name: "Mixed"
14952 }
14953 ]
14954 },
14955 {
14956 name: "EventBasedGatewayType",
14957 literalValues: [
14958 {
14959 name: "Parallel"
14960 },
14961 {
14962 name: "Exclusive"
14963 }
14964 ]
14965 },
14966 {
14967 name: "RelationshipDirection",
14968 literalValues: [
14969 {
14970 name: "None"
14971 },
14972 {
14973 name: "Forward"
14974 },
14975 {
14976 name: "Backward"
14977 },
14978 {
14979 name: "Both"
14980 }
14981 ]
14982 },
14983 {
14984 name: "ItemKind",
14985 literalValues: [
14986 {
14987 name: "Physical"
14988 },
14989 {
14990 name: "Information"
14991 }
14992 ]
14993 },
14994 {
14995 name: "ChoreographyLoopType",
14996 literalValues: [
14997 {
14998 name: "None"
14999 },
15000 {
15001 name: "Standard"
15002 },
15003 {
15004 name: "MultiInstanceSequential"
15005 },
15006 {
15007 name: "MultiInstanceParallel"
15008 }
15009 ]
15010 },
15011 {
15012 name: "AssociationDirection",
15013 literalValues: [
15014 {
15015 name: "None"
15016 },
15017 {
15018 name: "One"
15019 },
15020 {
15021 name: "Both"
15022 }
15023 ]
15024 },
15025 {
15026 name: "MultiInstanceBehavior",
15027 literalValues: [
15028 {
15029 name: "None"
15030 },
15031 {
15032 name: "One"
15033 },
15034 {
15035 name: "All"
15036 },
15037 {
15038 name: "Complex"
15039 }
15040 ]
15041 },
15042 {
15043 name: "AdHocOrdering",
15044 literalValues: [
15045 {
15046 name: "Parallel"
15047 },
15048 {
15049 name: "Sequential"
15050 }
15051 ]
15052 }
15053 ];
15054 var xml = {
15055 tagAlias: "lowerCase",
15056 typePrefix: "t"
15057 };
15058 var BpmnPackage = {
15059 name: name,
15060 uri: uri,
15061 prefix: prefix,
15062 associations: associations,
15063 types: types,
15064 enumerations: enumerations,
15065 xml: xml
15066 };
15067
15068 var name$1 = "BPMNDI";
15069 var uri$1 = "http://www.omg.org/spec/BPMN/20100524/DI";
15070 var prefix$1 = "bpmndi";
15071 var types$1 = [
15072 {
15073 name: "BPMNDiagram",
15074 properties: [
15075 {
15076 name: "plane",
15077 type: "BPMNPlane",
15078 redefines: "di:Diagram#rootElement"
15079 },
15080 {
15081 name: "labelStyle",
15082 type: "BPMNLabelStyle",
15083 isMany: true
15084 }
15085 ],
15086 superClass: [
15087 "di:Diagram"
15088 ]
15089 },
15090 {
15091 name: "BPMNPlane",
15092 properties: [
15093 {
15094 name: "bpmnElement",
15095 isAttr: true,
15096 isReference: true,
15097 type: "bpmn:BaseElement",
15098 redefines: "di:DiagramElement#modelElement"
15099 }
15100 ],
15101 superClass: [
15102 "di:Plane"
15103 ]
15104 },
15105 {
15106 name: "BPMNShape",
15107 properties: [
15108 {
15109 name: "bpmnElement",
15110 isAttr: true,
15111 isReference: true,
15112 type: "bpmn:BaseElement",
15113 redefines: "di:DiagramElement#modelElement"
15114 },
15115 {
15116 name: "isHorizontal",
15117 isAttr: true,
15118 type: "Boolean"
15119 },
15120 {
15121 name: "isExpanded",
15122 isAttr: true,
15123 type: "Boolean"
15124 },
15125 {
15126 name: "isMarkerVisible",
15127 isAttr: true,
15128 type: "Boolean"
15129 },
15130 {
15131 name: "label",
15132 type: "BPMNLabel"
15133 },
15134 {
15135 name: "isMessageVisible",
15136 isAttr: true,
15137 type: "Boolean"
15138 },
15139 {
15140 name: "participantBandKind",
15141 type: "ParticipantBandKind",
15142 isAttr: true
15143 },
15144 {
15145 name: "choreographyActivityShape",
15146 type: "BPMNShape",
15147 isAttr: true,
15148 isReference: true
15149 }
15150 ],
15151 superClass: [
15152 "di:LabeledShape"
15153 ]
15154 },
15155 {
15156 name: "BPMNEdge",
15157 properties: [
15158 {
15159 name: "label",
15160 type: "BPMNLabel"
15161 },
15162 {
15163 name: "bpmnElement",
15164 isAttr: true,
15165 isReference: true,
15166 type: "bpmn:BaseElement",
15167 redefines: "di:DiagramElement#modelElement"
15168 },
15169 {
15170 name: "sourceElement",
15171 isAttr: true,
15172 isReference: true,
15173 type: "di:DiagramElement",
15174 redefines: "di:Edge#source"
15175 },
15176 {
15177 name: "targetElement",
15178 isAttr: true,
15179 isReference: true,
15180 type: "di:DiagramElement",
15181 redefines: "di:Edge#target"
15182 },
15183 {
15184 name: "messageVisibleKind",
15185 type: "MessageVisibleKind",
15186 isAttr: true,
15187 "default": "initiating"
15188 }
15189 ],
15190 superClass: [
15191 "di:LabeledEdge"
15192 ]
15193 },
15194 {
15195 name: "BPMNLabel",
15196 properties: [
15197 {
15198 name: "labelStyle",
15199 type: "BPMNLabelStyle",
15200 isAttr: true,
15201 isReference: true,
15202 redefines: "di:DiagramElement#style"
15203 }
15204 ],
15205 superClass: [
15206 "di:Label"
15207 ]
15208 },
15209 {
15210 name: "BPMNLabelStyle",
15211 properties: [
15212 {
15213 name: "font",
15214 type: "dc:Font"
15215 }
15216 ],
15217 superClass: [
15218 "di:Style"
15219 ]
15220 }
15221 ];
15222 var enumerations$1 = [
15223 {
15224 name: "ParticipantBandKind",
15225 literalValues: [
15226 {
15227 name: "top_initiating"
15228 },
15229 {
15230 name: "middle_initiating"
15231 },
15232 {
15233 name: "bottom_initiating"
15234 },
15235 {
15236 name: "top_non_initiating"
15237 },
15238 {
15239 name: "middle_non_initiating"
15240 },
15241 {
15242 name: "bottom_non_initiating"
15243 }
15244 ]
15245 },
15246 {
15247 name: "MessageVisibleKind",
15248 literalValues: [
15249 {
15250 name: "initiating"
15251 },
15252 {
15253 name: "non_initiating"
15254 }
15255 ]
15256 }
15257 ];
15258 var associations$1 = [
15259 ];
15260 var BpmnDiPackage = {
15261 name: name$1,
15262 uri: uri$1,
15263 prefix: prefix$1,
15264 types: types$1,
15265 enumerations: enumerations$1,
15266 associations: associations$1
15267 };
15268
15269 var name$2 = "DC";
15270 var uri$2 = "http://www.omg.org/spec/DD/20100524/DC";
15271 var prefix$2 = "dc";
15272 var types$2 = [
15273 {
15274 name: "Boolean"
15275 },
15276 {
15277 name: "Integer"
15278 },
15279 {
15280 name: "Real"
15281 },
15282 {
15283 name: "String"
15284 },
15285 {
15286 name: "Font",
15287 properties: [
15288 {
15289 name: "name",
15290 type: "String",
15291 isAttr: true
15292 },
15293 {
15294 name: "size",
15295 type: "Real",
15296 isAttr: true
15297 },
15298 {
15299 name: "isBold",
15300 type: "Boolean",
15301 isAttr: true
15302 },
15303 {
15304 name: "isItalic",
15305 type: "Boolean",
15306 isAttr: true
15307 },
15308 {
15309 name: "isUnderline",
15310 type: "Boolean",
15311 isAttr: true
15312 },
15313 {
15314 name: "isStrikeThrough",
15315 type: "Boolean",
15316 isAttr: true
15317 }
15318 ]
15319 },
15320 {
15321 name: "Point",
15322 properties: [
15323 {
15324 name: "x",
15325 type: "Real",
15326 "default": "0",
15327 isAttr: true
15328 },
15329 {
15330 name: "y",
15331 type: "Real",
15332 "default": "0",
15333 isAttr: true
15334 }
15335 ]
15336 },
15337 {
15338 name: "Bounds",
15339 properties: [
15340 {
15341 name: "x",
15342 type: "Real",
15343 "default": "0",
15344 isAttr: true
15345 },
15346 {
15347 name: "y",
15348 type: "Real",
15349 "default": "0",
15350 isAttr: true
15351 },
15352 {
15353 name: "width",
15354 type: "Real",
15355 isAttr: true
15356 },
15357 {
15358 name: "height",
15359 type: "Real",
15360 isAttr: true
15361 }
15362 ]
15363 }
15364 ];
15365 var associations$2 = [
15366 ];
15367 var DcPackage = {
15368 name: name$2,
15369 uri: uri$2,
15370 prefix: prefix$2,
15371 types: types$2,
15372 associations: associations$2
15373 };
15374
15375 var name$3 = "DI";
15376 var uri$3 = "http://www.omg.org/spec/DD/20100524/DI";
15377 var prefix$3 = "di";
15378 var types$3 = [
15379 {
15380 name: "DiagramElement",
15381 isAbstract: true,
15382 properties: [
15383 {
15384 name: "id",
15385 isAttr: true,
15386 isId: true,
15387 type: "String"
15388 },
15389 {
15390 name: "extension",
15391 type: "Extension"
15392 },
15393 {
15394 name: "owningDiagram",
15395 type: "Diagram",
15396 isReadOnly: true,
15397 isVirtual: true,
15398 isReference: true
15399 },
15400 {
15401 name: "owningElement",
15402 type: "DiagramElement",
15403 isReadOnly: true,
15404 isVirtual: true,
15405 isReference: true
15406 },
15407 {
15408 name: "modelElement",
15409 isReadOnly: true,
15410 isVirtual: true,
15411 isReference: true,
15412 type: "Element"
15413 },
15414 {
15415 name: "style",
15416 type: "Style",
15417 isReadOnly: true,
15418 isVirtual: true,
15419 isReference: true
15420 },
15421 {
15422 name: "ownedElement",
15423 type: "DiagramElement",
15424 isReadOnly: true,
15425 isMany: true,
15426 isVirtual: true
15427 }
15428 ]
15429 },
15430 {
15431 name: "Node",
15432 isAbstract: true,
15433 superClass: [
15434 "DiagramElement"
15435 ]
15436 },
15437 {
15438 name: "Edge",
15439 isAbstract: true,
15440 superClass: [
15441 "DiagramElement"
15442 ],
15443 properties: [
15444 {
15445 name: "source",
15446 type: "DiagramElement",
15447 isReadOnly: true,
15448 isVirtual: true,
15449 isReference: true
15450 },
15451 {
15452 name: "target",
15453 type: "DiagramElement",
15454 isReadOnly: true,
15455 isVirtual: true,
15456 isReference: true
15457 },
15458 {
15459 name: "waypoint",
15460 isUnique: false,
15461 isMany: true,
15462 type: "dc:Point",
15463 xml: {
15464 serialize: "xsi:type"
15465 }
15466 }
15467 ]
15468 },
15469 {
15470 name: "Diagram",
15471 isAbstract: true,
15472 properties: [
15473 {
15474 name: "id",
15475 isAttr: true,
15476 isId: true,
15477 type: "String"
15478 },
15479 {
15480 name: "rootElement",
15481 type: "DiagramElement",
15482 isReadOnly: true,
15483 isVirtual: true
15484 },
15485 {
15486 name: "name",
15487 isAttr: true,
15488 type: "String"
15489 },
15490 {
15491 name: "documentation",
15492 isAttr: true,
15493 type: "String"
15494 },
15495 {
15496 name: "resolution",
15497 isAttr: true,
15498 type: "Real"
15499 },
15500 {
15501 name: "ownedStyle",
15502 type: "Style",
15503 isReadOnly: true,
15504 isMany: true,
15505 isVirtual: true
15506 }
15507 ]
15508 },
15509 {
15510 name: "Shape",
15511 isAbstract: true,
15512 superClass: [
15513 "Node"
15514 ],
15515 properties: [
15516 {
15517 name: "bounds",
15518 type: "dc:Bounds"
15519 }
15520 ]
15521 },
15522 {
15523 name: "Plane",
15524 isAbstract: true,
15525 superClass: [
15526 "Node"
15527 ],
15528 properties: [
15529 {
15530 name: "planeElement",
15531 type: "DiagramElement",
15532 subsettedProperty: "DiagramElement-ownedElement",
15533 isMany: true
15534 }
15535 ]
15536 },
15537 {
15538 name: "LabeledEdge",
15539 isAbstract: true,
15540 superClass: [
15541 "Edge"
15542 ],
15543 properties: [
15544 {
15545 name: "ownedLabel",
15546 type: "Label",
15547 isReadOnly: true,
15548 subsettedProperty: "DiagramElement-ownedElement",
15549 isMany: true,
15550 isVirtual: true
15551 }
15552 ]
15553 },
15554 {
15555 name: "LabeledShape",
15556 isAbstract: true,
15557 superClass: [
15558 "Shape"
15559 ],
15560 properties: [
15561 {
15562 name: "ownedLabel",
15563 type: "Label",
15564 isReadOnly: true,
15565 subsettedProperty: "DiagramElement-ownedElement",
15566 isMany: true,
15567 isVirtual: true
15568 }
15569 ]
15570 },
15571 {
15572 name: "Label",
15573 isAbstract: true,
15574 superClass: [
15575 "Node"
15576 ],
15577 properties: [
15578 {
15579 name: "bounds",
15580 type: "dc:Bounds"
15581 }
15582 ]
15583 },
15584 {
15585 name: "Style",
15586 isAbstract: true,
15587 properties: [
15588 {
15589 name: "id",
15590 isAttr: true,
15591 isId: true,
15592 type: "String"
15593 }
15594 ]
15595 },
15596 {
15597 name: "Extension",
15598 properties: [
15599 {
15600 name: "values",
15601 isMany: true,
15602 type: "Element"
15603 }
15604 ]
15605 }
15606 ];
15607 var associations$3 = [
15608 ];
15609 var xml$1 = {
15610 tagAlias: "lowerCase"
15611 };
15612 var DiPackage = {
15613 name: name$3,
15614 uri: uri$3,
15615 prefix: prefix$3,
15616 types: types$3,
15617 associations: associations$3,
15618 xml: xml$1
15619 };
15620
15621 var name$4 = "bpmn.io colors for BPMN";
15622 var uri$4 = "http://bpmn.io/schema/bpmn/biocolor/1.0";
15623 var prefix$4 = "bioc";
15624 var types$4 = [
15625 {
15626 name: "ColoredShape",
15627 "extends": [
15628 "bpmndi:BPMNShape"
15629 ],
15630 properties: [
15631 {
15632 name: "stroke",
15633 isAttr: true,
15634 type: "String"
15635 },
15636 {
15637 name: "fill",
15638 isAttr: true,
15639 type: "String"
15640 }
15641 ]
15642 },
15643 {
15644 name: "ColoredEdge",
15645 "extends": [
15646 "bpmndi:BPMNEdge"
15647 ],
15648 properties: [
15649 {
15650 name: "stroke",
15651 isAttr: true,
15652 type: "String"
15653 },
15654 {
15655 name: "fill",
15656 isAttr: true,
15657 type: "String"
15658 }
15659 ]
15660 }
15661 ];
15662 var enumerations$2 = [
15663 ];
15664 var associations$4 = [
15665 ];
15666 var BiocPackage = {
15667 name: name$4,
15668 uri: uri$4,
15669 prefix: prefix$4,
15670 types: types$4,
15671 enumerations: enumerations$2,
15672 associations: associations$4
15673 };
15674
15675 var name$5 = "BPMN in Color";
15676 var uri$5 = "http://www.omg.org/spec/BPMN/non-normative/color/1.0";
15677 var prefix$5 = "color";
15678 var types$5 = [
15679 {
15680 name: "ColoredLabel",
15681 "extends": [
15682 "bpmndi:BPMNLabel"
15683 ],
15684 properties: [
15685 {
15686 name: "color",
15687 isAttr: true,
15688 type: "String"
15689 }
15690 ]
15691 },
15692 {
15693 name: "ColoredShape",
15694 "extends": [
15695 "bpmndi:BPMNShape"
15696 ],
15697 properties: [
15698 {
15699 name: "background-color",
15700 isAttr: true,
15701 type: "String"
15702 },
15703 {
15704 name: "border-color",
15705 isAttr: true,
15706 type: "String"
15707 }
15708 ]
15709 },
15710 {
15711 name: "ColoredEdge",
15712 "extends": [
15713 "bpmndi:BPMNEdge"
15714 ],
15715 properties: [
15716 {
15717 name: "border-color",
15718 isAttr: true,
15719 type: "String"
15720 }
15721 ]
15722 }
15723 ];
15724 var enumerations$3 = [
15725 ];
15726 var associations$5 = [
15727 ];
15728 var BpmnInColorPackage = {
15729 name: name$5,
15730 uri: uri$5,
15731 prefix: prefix$5,
15732 types: types$5,
15733 enumerations: enumerations$3,
15734 associations: associations$5
15735 };
15736
15737 var packages = {
15738 bpmn: BpmnPackage,
15739 bpmndi: BpmnDiPackage,
15740 dc: DcPackage,
15741 di: DiPackage,
15742 bioc: BiocPackage,
15743 color: BpmnInColorPackage
15744 };
15745
15746 function simple(additionalPackages, options) {
15747 var pks = assign({}, packages, additionalPackages);
15748
15749 return new BpmnModdle(pks, options);
15750 }
15751
15752 function elementToString(e) {
15753 if (!e) {
15754 return '<null>';
15755 }
15756
15757 return '<' + e.$type + (e.id ? ' id="' + e.id : '') + '" />';
15758 }
15759
15760 // TODO(nikku): remove with future bpmn-js version
15761
15762 /**
15763 * Wraps APIs to check:
15764 *
15765 * 1) If a callback is passed -> Warn users about callback deprecation.
15766 * 2) If Promise class is implemented in current environment.
15767 *
15768 * @private
15769 */
15770 function wrapForCompatibility(api) {
15771
15772 return function() {
15773
15774 if (!window.Promise) {
15775 throw new Error('Promises is not supported in this environment. Please polyfill Promise.');
15776 }
15777
15778 var argLen = arguments.length;
15779 if (argLen >= 1 && isFunction(arguments[argLen - 1])) {
15780
15781 var callback = arguments[argLen - 1];
15782
15783 console.warn(new Error(
15784 'Passing callbacks to ' + api.name + ' is deprecated and will be removed in a future major release. ' +
15785 'Please switch to promises: https://bpmn.io/l/moving-to-promises.html'
15786 ));
15787
15788 var argsWithoutCallback = Array.prototype.slice.call(arguments, 0, -1);
15789
15790 api.apply(this, argsWithoutCallback).then(function(result) {
15791
15792 var firstKey = Object.keys(result)[0];
15793
15794 // The APIs we are wrapping all resolve a single item depending on the API.
15795 // For instance, importXML resolves { warnings } and saveXML returns { xml }.
15796 // That's why we can call the callback with the first item of result.
15797 return callback(null, result[firstKey]);
15798
15799 // Passing a second paramter instead of catch because we don't want to
15800 // catch errors thrown by callback().
15801 }, function(err) {
15802
15803 return callback(err, err.warnings);
15804 });
15805 } else {
15806
15807 return api.apply(this, arguments);
15808 }
15809 };
15810 }
15811
15812
15813 // TODO(nikku): remove with future bpmn-js version
15814
15815 var DI_ERROR_MESSAGE = 'Tried to access di from the businessObject. The di is available through the diagram element only. For more information, see https://github.com/bpmn-io/bpmn-js/issues/1472';
15816
15817 function ensureCompatDiRef(businessObject) {
15818
15819 // bpmnElement can have multiple independent DIs
15820 if (!has$2(businessObject, 'di')) {
15821 Object.defineProperty(businessObject, 'di', {
15822 get: function() {
15823 throw new Error(DI_ERROR_MESSAGE);
15824 }
15825 });
15826 }
15827 }
15828
15829 /**
15830 * Returns true if an element has the given meta-model type
15831 *
15832 * @param {ModdleElement} element
15833 * @param {string} type
15834 *
15835 * @return {boolean}
15836 */
15837 function is$2(element, type) {
15838 return element.$instanceOf(type);
15839 }
15840
15841
15842 /**
15843 * Find a suitable display candidate for definitions where the DI does not
15844 * correctly specify one.
15845 */
15846 function findDisplayCandidate(definitions) {
15847 return find(definitions.rootElements, function(e) {
15848 return is$2(e, 'bpmn:Process') || is$2(e, 'bpmn:Collaboration');
15849 });
15850 }
15851
15852
15853 function BpmnTreeWalker(handler, translate) {
15854
15855 // list of containers already walked
15856 var handledElements = {};
15857
15858 // list of elements to handle deferred to ensure
15859 // prerequisites are drawn
15860 var deferred = [];
15861
15862 var diMap = {};
15863
15864 // Helpers //////////////////////
15865
15866 function contextual(fn, ctx) {
15867 return function(e) {
15868 fn(e, ctx);
15869 };
15870 }
15871
15872 function handled(element) {
15873 handledElements[element.id] = element;
15874 }
15875
15876 function isHandled(element) {
15877 return handledElements[element.id];
15878 }
15879
15880 function visit(element, ctx) {
15881
15882 var gfx = element.gfx;
15883
15884 // avoid multiple rendering of elements
15885 if (gfx) {
15886 throw new Error(
15887 translate('already rendered {element}', { element: elementToString(element) })
15888 );
15889 }
15890
15891 // call handler
15892 return handler.element(element, diMap[element.id], ctx);
15893 }
15894
15895 function visitRoot(element, diagram) {
15896 return handler.root(element, diMap[element.id], diagram);
15897 }
15898
15899 function visitIfDi(element, ctx) {
15900
15901 try {
15902 var gfx = diMap[element.id] && visit(element, ctx);
15903
15904 handled(element);
15905
15906 return gfx;
15907 } catch (e) {
15908 logError(e.message, { element: element, error: e });
15909
15910 console.error(translate('failed to import {element}', { element: elementToString(element) }));
15911 console.error(e);
15912 }
15913 }
15914
15915 function logError(message, context) {
15916 handler.error(message, context);
15917 }
15918
15919 // DI handling //////////////////////
15920
15921 function registerDi(di) {
15922 var bpmnElement = di.bpmnElement;
15923
15924 if (bpmnElement) {
15925 if (diMap[bpmnElement.id]) {
15926 logError(
15927 translate('multiple DI elements defined for {element}', {
15928 element: elementToString(bpmnElement)
15929 }),
15930 { element: bpmnElement }
15931 );
15932 } else {
15933 diMap[bpmnElement.id] = di;
15934
15935 ensureCompatDiRef(bpmnElement);
15936 }
15937 } else {
15938 logError(
15939 translate('no bpmnElement referenced in {element}', {
15940 element: elementToString(di)
15941 }),
15942 { element: di }
15943 );
15944 }
15945 }
15946
15947 function handleDiagram(diagram) {
15948 handlePlane(diagram.plane);
15949 }
15950
15951 function handlePlane(plane) {
15952 registerDi(plane);
15953
15954 forEach$2(plane.planeElement, handlePlaneElement);
15955 }
15956
15957 function handlePlaneElement(planeElement) {
15958 registerDi(planeElement);
15959 }
15960
15961
15962 // Semantic handling //////////////////////
15963
15964 /**
15965 * Handle definitions and return the rendered diagram (if any)
15966 *
15967 * @param {ModdleElement} definitions to walk and import
15968 * @param {ModdleElement} [diagram] specific diagram to import and display
15969 *
15970 * @throws {Error} if no diagram to display could be found
15971 */
15972 function handleDefinitions(definitions, diagram) {
15973
15974 // make sure we walk the correct bpmnElement
15975
15976 var diagrams = definitions.diagrams;
15977
15978 if (diagram && diagrams.indexOf(diagram) === -1) {
15979 throw new Error(translate('diagram not part of bpmn:Definitions'));
15980 }
15981
15982 if (!diagram && diagrams && diagrams.length) {
15983 diagram = diagrams[0];
15984 }
15985
15986 // no diagram -> nothing to import
15987 if (!diagram) {
15988 throw new Error(translate('no diagram to display'));
15989 }
15990
15991 // load DI from selected diagram only
15992 diMap = {};
15993 handleDiagram(diagram);
15994
15995
15996 var plane = diagram.plane;
15997
15998 if (!plane) {
15999 throw new Error(translate(
16000 'no plane for {element}',
16001 { element: elementToString(diagram) }
16002 ));
16003 }
16004
16005 var rootElement = plane.bpmnElement;
16006
16007 // ensure we default to a suitable display candidate (process or collaboration),
16008 // even if non is specified in DI
16009 if (!rootElement) {
16010 rootElement = findDisplayCandidate(definitions);
16011
16012 if (!rootElement) {
16013 throw new Error(translate('no process or collaboration to display'));
16014 } else {
16015
16016 logError(
16017 translate('correcting missing bpmnElement on {plane} to {rootElement}', {
16018 plane: elementToString(plane),
16019 rootElement: elementToString(rootElement)
16020 })
16021 );
16022
16023 // correct DI on the fly
16024 plane.bpmnElement = rootElement;
16025 registerDi(plane);
16026 }
16027 }
16028
16029
16030 var ctx = visitRoot(rootElement, plane);
16031
16032 if (is$2(rootElement, 'bpmn:Process') || is$2(rootElement, 'bpmn:SubProcess')) {
16033 handleProcess(rootElement, ctx);
16034 } else if (is$2(rootElement, 'bpmn:Collaboration')) {
16035 handleCollaboration(rootElement, ctx);
16036
16037 // force drawing of everything not yet drawn that is part of the target DI
16038 handleUnhandledProcesses(definitions.rootElements, ctx);
16039 } else {
16040 throw new Error(
16041 translate('unsupported bpmnElement for {plane}: {rootElement}', {
16042 plane: elementToString(plane),
16043 rootElement: elementToString(rootElement)
16044 })
16045 );
16046 }
16047
16048 // handle all deferred elements
16049 handleDeferred();
16050 }
16051
16052 function handleDeferred() {
16053
16054 var fn;
16055
16056 // drain deferred until empty
16057 while (deferred.length) {
16058 fn = deferred.shift();
16059
16060 fn();
16061 }
16062 }
16063
16064 function handleProcess(process, context) {
16065 handleFlowElementsContainer(process, context);
16066 handleIoSpecification(process.ioSpecification, context);
16067
16068 handleArtifacts(process.artifacts, context);
16069
16070 // log process handled
16071 handled(process);
16072 }
16073
16074 function handleUnhandledProcesses(rootElements, ctx) {
16075
16076 // walk through all processes that have not yet been drawn and draw them
16077 // if they contain lanes with DI information.
16078 // we do this to pass the free-floating lane test cases in the MIWG test suite
16079 var processes = filter(rootElements, function(e) {
16080 return !isHandled(e) && is$2(e, 'bpmn:Process') && e.laneSets;
16081 });
16082
16083 processes.forEach(contextual(handleProcess, ctx));
16084 }
16085
16086 function handleMessageFlow(messageFlow, context) {
16087 visitIfDi(messageFlow, context);
16088 }
16089
16090 function handleMessageFlows(messageFlows, context) {
16091 forEach$2(messageFlows, contextual(handleMessageFlow, context));
16092 }
16093
16094 function handleDataAssociation(association, context) {
16095 visitIfDi(association, context);
16096 }
16097
16098 function handleDataInput(dataInput, context) {
16099 visitIfDi(dataInput, context);
16100 }
16101
16102 function handleDataOutput(dataOutput, context) {
16103 visitIfDi(dataOutput, context);
16104 }
16105
16106 function handleArtifact(artifact, context) {
16107
16108 // bpmn:TextAnnotation
16109 // bpmn:Group
16110 // bpmn:Association
16111
16112 visitIfDi(artifact, context);
16113 }
16114
16115 function handleArtifacts(artifacts, context) {
16116
16117 forEach$2(artifacts, function(e) {
16118 if (is$2(e, 'bpmn:Association')) {
16119 deferred.push(function() {
16120 handleArtifact(e, context);
16121 });
16122 } else {
16123 handleArtifact(e, context);
16124 }
16125 });
16126 }
16127
16128 function handleIoSpecification(ioSpecification, context) {
16129
16130 if (!ioSpecification) {
16131 return;
16132 }
16133
16134 forEach$2(ioSpecification.dataInputs, contextual(handleDataInput, context));
16135 forEach$2(ioSpecification.dataOutputs, contextual(handleDataOutput, context));
16136 }
16137
16138 function handleSubProcess(subProcess, context) {
16139 handleFlowElementsContainer(subProcess, context);
16140 handleArtifacts(subProcess.artifacts, context);
16141 }
16142
16143 function handleFlowNode(flowNode, context) {
16144 var childCtx = visitIfDi(flowNode, context);
16145
16146 if (is$2(flowNode, 'bpmn:SubProcess')) {
16147 handleSubProcess(flowNode, childCtx || context);
16148 }
16149
16150 if (is$2(flowNode, 'bpmn:Activity')) {
16151 handleIoSpecification(flowNode.ioSpecification, context);
16152 }
16153
16154 // defer handling of associations
16155 // affected types:
16156 //
16157 // * bpmn:Activity
16158 // * bpmn:ThrowEvent
16159 // * bpmn:CatchEvent
16160 //
16161 deferred.push(function() {
16162 forEach$2(flowNode.dataInputAssociations, contextual(handleDataAssociation, context));
16163 forEach$2(flowNode.dataOutputAssociations, contextual(handleDataAssociation, context));
16164 });
16165 }
16166
16167 function handleSequenceFlow(sequenceFlow, context) {
16168 visitIfDi(sequenceFlow, context);
16169 }
16170
16171 function handleDataElement(dataObject, context) {
16172 visitIfDi(dataObject, context);
16173 }
16174
16175 function handleLane(lane, context) {
16176
16177 deferred.push(function() {
16178
16179 var newContext = visitIfDi(lane, context);
16180
16181 if (lane.childLaneSet) {
16182 handleLaneSet(lane.childLaneSet, newContext || context);
16183 }
16184
16185 wireFlowNodeRefs(lane);
16186 });
16187 }
16188
16189 function handleLaneSet(laneSet, context) {
16190 forEach$2(laneSet.lanes, contextual(handleLane, context));
16191 }
16192
16193 function handleLaneSets(laneSets, context) {
16194 forEach$2(laneSets, contextual(handleLaneSet, context));
16195 }
16196
16197 function handleFlowElementsContainer(container, context) {
16198 handleFlowElements(container.flowElements, context);
16199
16200 if (container.laneSets) {
16201 handleLaneSets(container.laneSets, context);
16202 }
16203 }
16204
16205 function handleFlowElements(flowElements, context) {
16206 forEach$2(flowElements, function(e) {
16207 if (is$2(e, 'bpmn:SequenceFlow')) {
16208 deferred.push(function() {
16209 handleSequenceFlow(e, context);
16210 });
16211 } else if (is$2(e, 'bpmn:BoundaryEvent')) {
16212 deferred.unshift(function() {
16213 handleFlowNode(e, context);
16214 });
16215 } else if (is$2(e, 'bpmn:FlowNode')) {
16216 handleFlowNode(e, context);
16217 } else if (is$2(e, 'bpmn:DataObject')) ; else if (is$2(e, 'bpmn:DataStoreReference')) {
16218 handleDataElement(e, context);
16219 } else if (is$2(e, 'bpmn:DataObjectReference')) {
16220 handleDataElement(e, context);
16221 } else {
16222 logError(
16223 translate('unrecognized flowElement {element} in context {context}', {
16224 element: elementToString(e),
16225 context: (context ? elementToString(context.businessObject) : 'null')
16226 }),
16227 { element: e, context: context }
16228 );
16229 }
16230 });
16231 }
16232
16233 function handleParticipant(participant, context) {
16234 var newCtx = visitIfDi(participant, context);
16235
16236 var process = participant.processRef;
16237 if (process) {
16238 handleProcess(process, newCtx || context);
16239 }
16240 }
16241
16242 function handleCollaboration(collaboration, context) {
16243
16244 forEach$2(collaboration.participants, contextual(handleParticipant, context));
16245
16246 handleArtifacts(collaboration.artifacts, context);
16247
16248 // handle message flows latest in the process
16249 deferred.push(function() {
16250 handleMessageFlows(collaboration.messageFlows, context);
16251 });
16252 }
16253
16254
16255 function wireFlowNodeRefs(lane) {
16256
16257 // wire the virtual flowNodeRefs <-> relationship
16258 forEach$2(lane.flowNodeRef, function(flowNode) {
16259 var lanes = flowNode.get('lanes');
16260
16261 if (lanes) {
16262 lanes.push(lane);
16263 }
16264 });
16265 }
16266
16267 // API //////////////////////
16268
16269 return {
16270 handleDeferred: handleDeferred,
16271 handleDefinitions: handleDefinitions,
16272 handleSubProcess: handleSubProcess,
16273 registerDi: registerDi
16274 };
16275 }
16276
16277 /**
16278 * Is an element of the given BPMN type?
16279 *
16280 * @param {djs.model.Base|ModdleElement} element
16281 * @param {string} type
16282 *
16283 * @return {boolean}
16284 */
16285 function is$1(element, type) {
16286 var bo = getBusinessObject(element);
16287
16288 return bo && (typeof bo.$instanceOf === 'function') && bo.$instanceOf(type);
16289 }
16290
16291
16292 /**
16293 * Return true if element has any of the given types.
16294 *
16295 * @param {djs.model.Base} element
16296 * @param {Array<string>} types
16297 *
16298 * @return {boolean}
16299 */
16300 function isAny(element, types) {
16301 return some(types, function(t) {
16302 return is$1(element, t);
16303 });
16304 }
16305
16306 /**
16307 * Return the business object for a given element.
16308 *
16309 * @param {djs.model.Base|ModdleElement} element
16310 *
16311 * @return {ModdleElement}
16312 */
16313 function getBusinessObject(element) {
16314 return (element && element.businessObject) || element;
16315 }
16316
16317 /**
16318 * Return the di object for a given element.
16319 *
16320 * @param {djs.model.Base} element
16321 *
16322 * @return {ModdleElement}
16323 */
16324 function getDi(element) {
16325 return element && element.di;
16326 }
16327
16328 /**
16329 * The importBpmnDiagram result.
16330 *
16331 * @typedef {Object} ImportBPMNDiagramResult
16332 *
16333 * @property {Array<string>} warnings
16334 */
16335
16336 /**
16337 * The importBpmnDiagram error.
16338 *
16339 * @typedef {Error} ImportBPMNDiagramError
16340 *
16341 * @property {Array<string>} warnings
16342 */
16343
16344 /**
16345 * Import the definitions into a diagram.
16346 *
16347 * Errors and warnings are reported through the specified callback.
16348 *
16349 * @param {djs.Diagram} diagram
16350 * @param {ModdleElement<Definitions>} definitions
16351 * @param {ModdleElement<BPMNDiagram>} [bpmnDiagram] the diagram to be rendered
16352 * (if not provided, the first one will be rendered)
16353 *
16354 * Returns {Promise<ImportBPMNDiagramResult, ImportBPMNDiagramError>}
16355 */
16356 function importBpmnDiagram(diagram, definitions, bpmnDiagram) {
16357
16358 var importer,
16359 eventBus,
16360 translate,
16361 canvas;
16362
16363 var error,
16364 warnings = [];
16365
16366 /**
16367 * Walk the diagram semantically, importing (=drawing)
16368 * all elements you encounter.
16369 *
16370 * @param {ModdleElement<Definitions>} definitions
16371 * @param {ModdleElement<BPMNDiagram>} bpmnDiagram
16372 */
16373 function render(definitions, bpmnDiagram) {
16374
16375 var visitor = {
16376
16377 root: function(element, di) {
16378 return importer.add(element, di);
16379 },
16380
16381 element: function(element, di, parentShape) {
16382 return importer.add(element, di, parentShape);
16383 },
16384
16385 error: function(message, context) {
16386 warnings.push({ message: message, context: context });
16387 }
16388 };
16389
16390 var walker = new BpmnTreeWalker(visitor, translate);
16391
16392
16393 bpmnDiagram = bpmnDiagram || (definitions.diagrams && definitions.diagrams[0]);
16394
16395 var diagramsToImport = getDiagramsToImport(definitions, bpmnDiagram);
16396
16397 if (!diagramsToImport) {
16398 throw new Error(translate('no diagram to display'));
16399 }
16400
16401 // traverse BPMN 2.0 document model,
16402 // starting at definitions
16403 forEach$2(diagramsToImport, function(diagram) {
16404 walker.handleDefinitions(definitions, diagram);
16405 });
16406
16407 var rootId = bpmnDiagram.plane.bpmnElement.id;
16408
16409 // we do need to account for different ways we create root elements
16410 // each nested imported <root> do have the `_plane` suffix, while
16411 // the root <root> is found under the business object ID
16412 canvas.setRootElement(
16413 canvas.findRoot(rootId + '_plane') || canvas.findRoot(rootId)
16414 );
16415 }
16416
16417 return new Promise(function(resolve, reject) {
16418 try {
16419 importer = diagram.get('bpmnImporter');
16420 eventBus = diagram.get('eventBus');
16421 translate = diagram.get('translate');
16422 canvas = diagram.get('canvas');
16423
16424 eventBus.fire('import.render.start', { definitions: definitions });
16425
16426 render(definitions, bpmnDiagram);
16427
16428 eventBus.fire('import.render.complete', {
16429 error: error,
16430 warnings: warnings
16431 });
16432
16433 return resolve({ warnings: warnings });
16434 } catch (e) {
16435
16436 e.warnings = warnings;
16437 return reject(e);
16438 }
16439 });
16440 }
16441
16442 /**
16443 * Returns all diagrams in the same hierarchy as the requested diagram.
16444 * Includes all parent and sub process diagrams.
16445 *
16446 * @param {Array} definitions
16447 * @param {Object} bpmnDiagram
16448 *
16449 * @returns {Array<Object>}
16450 */
16451 function getDiagramsToImport(definitions, bpmnDiagram) {
16452 if (!bpmnDiagram) {
16453 return;
16454 }
16455
16456 var bpmnElement = bpmnDiagram.plane.bpmnElement,
16457 rootElement = bpmnElement;
16458
16459 if (!is$1(bpmnElement, 'bpmn:Process') && !is$1(bpmnElement, 'bpmn:Collaboration')) {
16460 rootElement = findRootProcess(bpmnElement);
16461 }
16462
16463 // in case the process is part of a collaboration, the plane references the
16464 // collaboration, not the process
16465 var collaboration;
16466
16467 if (is$1(rootElement, 'bpmn:Collaboration')) {
16468 collaboration = rootElement;
16469 } else {
16470 collaboration = find(definitions.rootElements, function(element) {
16471 if (!is$1(element, 'bpmn:Collaboration')) {
16472 return;
16473 }
16474
16475 return find(element.participants, function(participant) {
16476 return participant.processRef === rootElement;
16477 });
16478 });
16479 }
16480
16481 var rootElements = [ rootElement ];
16482
16483 // all collaboration processes can contain sub-diagrams
16484 if (collaboration) {
16485 rootElements = map(collaboration.participants, function(participant) {
16486 return participant.processRef;
16487 });
16488
16489 rootElements.push(collaboration);
16490 }
16491
16492 var allChildren = selfAndAllFlowElements(rootElements);
16493
16494 // if we have multiple diagrams referencing the same element, we
16495 // use the first in the file
16496 var diagramsToImport = [ bpmnDiagram ];
16497 var handledElements = [ bpmnElement ];
16498
16499 forEach$2(definitions.diagrams, function(diagram) {
16500 var businessObject = diagram.plane.bpmnElement;
16501
16502 if (
16503 allChildren.indexOf(businessObject) !== -1 &&
16504 handledElements.indexOf(businessObject) === -1
16505 ) {
16506 diagramsToImport.push(diagram);
16507 handledElements.push(businessObject);
16508 }
16509 });
16510
16511
16512 return diagramsToImport;
16513 }
16514
16515 function selfAndAllFlowElements(elements) {
16516 var result = [];
16517
16518 forEach$2(elements, function(element) {
16519 if (!element) {
16520 return;
16521 }
16522
16523 result.push(element);
16524
16525 result = result.concat(selfAndAllFlowElements(element.flowElements));
16526 });
16527
16528 return result;
16529 }
16530
16531 function findRootProcess(element) {
16532 var parent = element;
16533
16534 while (parent) {
16535 if (is$1(parent, 'bpmn:Process')) {
16536 return parent;
16537 }
16538
16539 parent = parent.$parent;
16540 }
16541 }
16542
16543 /**
16544 * This file must not be changed or exchanged.
16545 *
16546 * @see http://bpmn.io/license for more information.
16547 */
16548
16549
16550 // inlined ../../resources/logo.svg
16551 var BPMNIO_LOGO_SVG = '<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 14.02 5.57" width="53" height="21"><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>';
16552
16553 var BPMNIO_IMG = BPMNIO_LOGO_SVG;
16554
16555 var LOGO_STYLES = {
16556 verticalAlign: 'middle'
16557 };
16558
16559 var LINK_STYLES = {
16560 'color': '#404040'
16561 };
16562
16563 var LIGHTBOX_STYLES = {
16564 'zIndex': '1001',
16565 'position': 'fixed',
16566 'top': '0',
16567 'left': '0',
16568 'right': '0',
16569 'bottom': '0'
16570 };
16571
16572 var BACKDROP_STYLES = {
16573 'width': '100%',
16574 'height': '100%',
16575 'background': 'rgba(40,40,40,0.2)'
16576 };
16577
16578 var NOTICE_STYLES = {
16579 'position': 'absolute',
16580 'left': '50%',
16581 'top': '40%',
16582 'transform': 'translate(-50%)',
16583 'width': '260px',
16584 'padding': '10px',
16585 'background': 'white',
16586 'boxShadow': '0 1px 4px rgba(0,0,0,0.3)',
16587 'fontFamily': 'Helvetica, Arial, sans-serif',
16588 'fontSize': '14px',
16589 'display': 'flex',
16590 'lineHeight': '1.3'
16591 };
16592
16593 var LIGHTBOX_MARKUP =
16594 '<div class="bjs-powered-by-lightbox">' +
16595 '<div class="backdrop"></div>' +
16596 '<div class="notice">' +
16597 '<a href="https://bpmn.io" target="_blank" rel="noopener" class="link">' +
16598 BPMNIO_IMG +
16599 '</a>' +
16600 '<span>' +
16601 'Web-based tooling for BPMN, DMN and CMMN diagrams ' +
16602 'powered by <a href="https://bpmn.io" target="_blank" rel="noopener">bpmn.io</a>.' +
16603 '</span>' +
16604 '</div>' +
16605 '</div>';
16606
16607
16608 var lightbox;
16609
16610 function createLightbox() {
16611 lightbox = domify$1(LIGHTBOX_MARKUP);
16612
16613 assign$1$1(lightbox, LIGHTBOX_STYLES);
16614 assign$1$1(query$1('svg', lightbox), LOGO_STYLES);
16615 assign$1$1(query$1('.backdrop', lightbox), BACKDROP_STYLES);
16616 assign$1$1(query$1('.notice', lightbox), NOTICE_STYLES);
16617 assign$1$1(query$1('.link', lightbox), LINK_STYLES, {
16618 'margin': '15px 20px 15px 10px',
16619 'alignSelf': 'center'
16620 });
16621 }
16622
16623 function open() {
16624
16625 if (!lightbox) {
16626 createLightbox();
16627
16628 delegate$1.bind(lightbox, '.backdrop', 'click', function(event) {
16629 document.body.removeChild(lightbox);
16630 });
16631 }
16632
16633 document.body.appendChild(lightbox);
16634 }
16635
16636 /**
16637 * The code in the <project-logo></project-logo> area
16638 * must not be changed.
16639 *
16640 * @see http://bpmn.io/license for more information.
16641 */
16642
16643 /**
16644 * A base viewer for BPMN 2.0 diagrams.
16645 *
16646 * Have a look at {@link Viewer}, {@link NavigatedViewer} or {@link Modeler} for
16647 * bundles that include actual features.
16648 *
16649 * @param {Object} [options] configuration options to pass to the viewer
16650 * @param {DOMElement} [options.container] the container to render the viewer in, defaults to body.
16651 * @param {string|number} [options.width] the width of the viewer
16652 * @param {string|number} [options.height] the height of the viewer
16653 * @param {Object} [options.moddleExtensions] extension packages to provide
16654 * @param {Array<didi.Module>} [options.modules] a list of modules to override the default modules
16655 * @param {Array<didi.Module>} [options.additionalModules] a list of modules to use with the default modules
16656 */
16657 function BaseViewer(options) {
16658
16659 options = assign({}, DEFAULT_OPTIONS, options);
16660
16661 this._moddle = this._createModdle(options);
16662
16663 this._container = this._createContainer(options);
16664
16665 /* <project-logo> */
16666
16667 addProjectLogo(this._container);
16668
16669 /* </project-logo> */
16670
16671 this._init(this._container, this._moddle, options);
16672 }
16673
16674 inherits$1(BaseViewer, Diagram);
16675
16676 /**
16677 * The importXML result.
16678 *
16679 * @typedef {Object} ImportXMLResult
16680 *
16681 * @property {Array<string>} warnings
16682 */
16683
16684 /**
16685 * The importXML error.
16686 *
16687 * @typedef {Error} ImportXMLError
16688 *
16689 * @property {Array<string>} warnings
16690 */
16691
16692 /**
16693 * Parse and render a BPMN 2.0 diagram.
16694 *
16695 * Once finished the viewer reports back the result to the
16696 * provided callback function with (err, warnings).
16697 *
16698 * ## Life-Cycle Events
16699 *
16700 * During import the viewer will fire life-cycle events:
16701 *
16702 * * import.parse.start (about to read model from xml)
16703 * * import.parse.complete (model read; may have worked or not)
16704 * * import.render.start (graphical import start)
16705 * * import.render.complete (graphical import finished)
16706 * * import.done (everything done)
16707 *
16708 * You can use these events to hook into the life-cycle.
16709 *
16710 * @param {string} xml the BPMN 2.0 xml
16711 * @param {ModdleElement<BPMNDiagram>|string} [bpmnDiagram] BPMN diagram or id of diagram to render (if not provided, the first one will be rendered)
16712 *
16713 * Returns {Promise<ImportXMLResult, ImportXMLError>}
16714 */
16715 BaseViewer.prototype.importXML = wrapForCompatibility(function importXML(xml, bpmnDiagram) {
16716
16717 var self = this;
16718
16719 function ParseCompleteEvent(data) {
16720
16721 var event = self.get('eventBus').createEvent(data);
16722
16723 // TODO(nikku): remove with future bpmn-js version
16724 Object.defineProperty(event, 'context', {
16725 enumerable: true,
16726 get: function() {
16727
16728 console.warn(new Error(
16729 'import.parse.complete <context> is deprecated ' +
16730 'and will be removed in future library versions'
16731 ));
16732
16733 return {
16734 warnings: data.warnings,
16735 references: data.references,
16736 elementsById: data.elementsById
16737 };
16738 }
16739 });
16740
16741 return event;
16742 }
16743
16744 return new Promise(function(resolve, reject) {
16745
16746 // hook in pre-parse listeners +
16747 // allow xml manipulation
16748 xml = self._emit('import.parse.start', { xml: xml }) || xml;
16749
16750 self._moddle.fromXML(xml, 'bpmn:Definitions').then(function(result) {
16751 var definitions = result.rootElement;
16752 var references = result.references;
16753 var parseWarnings = result.warnings;
16754 var elementsById = result.elementsById;
16755
16756 // hook in post parse listeners +
16757 // allow definitions manipulation
16758 definitions = self._emit('import.parse.complete', ParseCompleteEvent({
16759 error: null,
16760 definitions: definitions,
16761 elementsById: elementsById,
16762 references: references,
16763 warnings: parseWarnings
16764 })) || definitions;
16765
16766 self.importDefinitions(definitions, bpmnDiagram).then(function(result) {
16767 var allWarnings = [].concat(parseWarnings, result.warnings || []);
16768
16769 self._emit('import.done', { error: null, warnings: allWarnings });
16770
16771 return resolve({ warnings: allWarnings });
16772 }).catch(function(err) {
16773 var allWarnings = [].concat(parseWarnings, err.warnings || []);
16774
16775 self._emit('import.done', { error: err, warnings: allWarnings });
16776
16777 return reject(addWarningsToError(err, allWarnings));
16778 });
16779 }).catch(function(err) {
16780
16781 self._emit('import.parse.complete', {
16782 error: err
16783 });
16784
16785 err = checkValidationError(err);
16786
16787 self._emit('import.done', { error: err, warnings: err.warnings });
16788
16789 return reject(err);
16790 });
16791 });
16792 });
16793
16794 /**
16795 * The importDefinitions result.
16796 *
16797 * @typedef {Object} ImportDefinitionsResult
16798 *
16799 * @property {Array<string>} warnings
16800 */
16801
16802 /**
16803 * The importDefinitions error.
16804 *
16805 * @typedef {Error} ImportDefinitionsError
16806 *
16807 * @property {Array<string>} warnings
16808 */
16809
16810 /**
16811 * Import parsed definitions and render a BPMN 2.0 diagram.
16812 *
16813 * Once finished the viewer reports back the result to the
16814 * provided callback function with (err, warnings).
16815 *
16816 * ## Life-Cycle Events
16817 *
16818 * During import the viewer will fire life-cycle events:
16819 *
16820 * * import.render.start (graphical import start)
16821 * * import.render.complete (graphical import finished)
16822 *
16823 * You can use these events to hook into the life-cycle.
16824 *
16825 * @param {ModdleElement<Definitions>} definitions parsed BPMN 2.0 definitions
16826 * @param {ModdleElement<BPMNDiagram>|string} [bpmnDiagram] BPMN diagram or id of diagram to render (if not provided, the first one will be rendered)
16827 *
16828 * Returns {Promise<ImportDefinitionsResult, ImportDefinitionsError>}
16829 */
16830 BaseViewer.prototype.importDefinitions = wrapForCompatibility(function importDefinitions(definitions, bpmnDiagram) {
16831
16832 var self = this;
16833
16834 return new Promise(function(resolve, reject) {
16835
16836 self._setDefinitions(definitions);
16837
16838 self.open(bpmnDiagram).then(function(result) {
16839
16840 var warnings = result.warnings;
16841
16842 return resolve({ warnings: warnings });
16843 }).catch(function(err) {
16844
16845 return reject(err);
16846 });
16847 });
16848 });
16849
16850 /**
16851 * The open result.
16852 *
16853 * @typedef {Object} OpenResult
16854 *
16855 * @property {Array<string>} warnings
16856 */
16857
16858 /**
16859 * The open error.
16860 *
16861 * @typedef {Error} OpenError
16862 *
16863 * @property {Array<string>} warnings
16864 */
16865
16866 /**
16867 * Open diagram of previously imported XML.
16868 *
16869 * Once finished the viewer reports back the result to the
16870 * provided callback function with (err, warnings).
16871 *
16872 * ## Life-Cycle Events
16873 *
16874 * During switch the viewer will fire life-cycle events:
16875 *
16876 * * import.render.start (graphical import start)
16877 * * import.render.complete (graphical import finished)
16878 *
16879 * You can use these events to hook into the life-cycle.
16880 *
16881 * @param {string|ModdleElement<BPMNDiagram>} [bpmnDiagramOrId] id or the diagram to open
16882 *
16883 * Returns {Promise<OpenResult, OpenError>}
16884 */
16885 BaseViewer.prototype.open = wrapForCompatibility(function open(bpmnDiagramOrId) {
16886
16887 var definitions = this._definitions;
16888 var bpmnDiagram = bpmnDiagramOrId;
16889
16890 var self = this;
16891
16892 return new Promise(function(resolve, reject) {
16893 if (!definitions) {
16894 var err1 = new Error('no XML imported');
16895
16896 return reject(addWarningsToError(err1, []));
16897 }
16898
16899 if (typeof bpmnDiagramOrId === 'string') {
16900 bpmnDiagram = findBPMNDiagram(definitions, bpmnDiagramOrId);
16901
16902 if (!bpmnDiagram) {
16903 var err2 = new Error('BPMNDiagram <' + bpmnDiagramOrId + '> not found');
16904
16905 return reject(addWarningsToError(err2, []));
16906 }
16907 }
16908
16909 // clear existing rendered diagram
16910 // catch synchronous exceptions during #clear()
16911 try {
16912 self.clear();
16913 } catch (error) {
16914
16915 return reject(addWarningsToError(error, []));
16916 }
16917
16918 // perform graphical import
16919 importBpmnDiagram(self, definitions, bpmnDiagram).then(function(result) {
16920
16921 var warnings = result.warnings;
16922
16923 return resolve({ warnings: warnings });
16924 }).catch(function(err) {
16925
16926 return reject(err);
16927 });
16928 });
16929 });
16930
16931 /**
16932 * The saveXML result.
16933 *
16934 * @typedef {Object} SaveXMLResult
16935 *
16936 * @property {string} xml
16937 */
16938
16939 /**
16940 * Export the currently displayed BPMN 2.0 diagram as
16941 * a BPMN 2.0 XML document.
16942 *
16943 * ## Life-Cycle Events
16944 *
16945 * During XML saving the viewer will fire life-cycle events:
16946 *
16947 * * saveXML.start (before serialization)
16948 * * saveXML.serialized (after xml generation)
16949 * * saveXML.done (everything done)
16950 *
16951 * You can use these events to hook into the life-cycle.
16952 *
16953 * @param {Object} [options] export options
16954 * @param {boolean} [options.format=false] output formatted XML
16955 * @param {boolean} [options.preamble=true] output preamble
16956 *
16957 * Returns {Promise<SaveXMLResult, Error>}
16958 */
16959 BaseViewer.prototype.saveXML = wrapForCompatibility(function saveXML(options) {
16960
16961 options = options || {};
16962
16963 var self = this;
16964
16965 var definitions = this._definitions;
16966
16967 return new Promise(function(resolve) {
16968
16969 if (!definitions) {
16970 return resolve({
16971 error: new Error('no definitions loaded')
16972 });
16973 }
16974
16975 // allow to fiddle around with definitions
16976 definitions = self._emit('saveXML.start', {
16977 definitions: definitions
16978 }) || definitions;
16979
16980 self._moddle.toXML(definitions, options).then(function(result) {
16981
16982 var xml = result.xml;
16983
16984 xml = self._emit('saveXML.serialized', {
16985 xml: xml
16986 }) || xml;
16987
16988 return resolve({
16989 xml: xml
16990 });
16991 });
16992 }).catch(function(error) {
16993 return { error: error };
16994 }).then(function(result) {
16995
16996 self._emit('saveXML.done', result);
16997
16998 var error = result.error;
16999
17000 if (error) {
17001 return Promise.reject(error);
17002 }
17003
17004 return result;
17005 });
17006 });
17007
17008 /**
17009 * The saveSVG result.
17010 *
17011 * @typedef {Object} SaveSVGResult
17012 *
17013 * @property {string} svg
17014 */
17015
17016 /**
17017 * Export the currently displayed BPMN 2.0 diagram as
17018 * an SVG image.
17019 *
17020 * ## Life-Cycle Events
17021 *
17022 * During SVG saving the viewer will fire life-cycle events:
17023 *
17024 * * saveSVG.start (before serialization)
17025 * * saveSVG.done (everything done)
17026 *
17027 * You can use these events to hook into the life-cycle.
17028 *
17029 * @param {Object} [options]
17030 *
17031 * Returns {Promise<SaveSVGResult, Error>}
17032 */
17033 BaseViewer.prototype.saveSVG = wrapForCompatibility(function saveSVG(options) {
17034
17035 var self = this;
17036
17037 return new Promise(function(resolve, reject) {
17038
17039 self._emit('saveSVG.start');
17040
17041 var svg, err;
17042
17043 try {
17044 var canvas = self.get('canvas');
17045
17046 var contentNode = canvas.getActiveLayer(),
17047 defsNode = query$1('defs', canvas._svg);
17048
17049 var contents = innerSVG(contentNode),
17050 defs = defsNode ? '<defs>' + innerSVG(defsNode) + '</defs>' : '';
17051
17052 var bbox = contentNode.getBBox();
17053
17054 svg =
17055 '<?xml version="1.0" encoding="utf-8"?>\n' +
17056 '<!-- created with bpmn-js / http://bpmn.io -->\n' +
17057 '<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">\n' +
17058 '<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" ' +
17059 'width="' + bbox.width + '" height="' + bbox.height + '" ' +
17060 'viewBox="' + bbox.x + ' ' + bbox.y + ' ' + bbox.width + ' ' + bbox.height + '" version="1.1">' +
17061 defs + contents +
17062 '</svg>';
17063 } catch (e) {
17064 err = e;
17065 }
17066
17067 self._emit('saveSVG.done', {
17068 error: err,
17069 svg: svg
17070 });
17071
17072 if (!err) {
17073 return resolve({ svg: svg });
17074 }
17075
17076 return reject(err);
17077 });
17078 });
17079
17080 /**
17081 * Get a named diagram service.
17082 *
17083 * @example
17084 *
17085 * var elementRegistry = viewer.get('elementRegistry');
17086 * var startEventShape = elementRegistry.get('StartEvent_1');
17087 *
17088 * @param {string} name
17089 *
17090 * @return {Object} diagram service instance
17091 *
17092 * @method BaseViewer#get
17093 */
17094
17095 /**
17096 * Invoke a function in the context of this viewer.
17097 *
17098 * @example
17099 *
17100 * viewer.invoke(function(elementRegistry) {
17101 * var startEventShape = elementRegistry.get('StartEvent_1');
17102 * });
17103 *
17104 * @param {Function} fn to be invoked
17105 *
17106 * @return {Object} the functions return value
17107 *
17108 * @method BaseViewer#invoke
17109 */
17110
17111
17112 BaseViewer.prototype._setDefinitions = function(definitions) {
17113 this._definitions = definitions;
17114 };
17115
17116 BaseViewer.prototype.getModules = function() {
17117 return this._modules;
17118 };
17119
17120 /**
17121 * Remove all drawn elements from the viewer.
17122 *
17123 * After calling this method the viewer can still
17124 * be reused for opening another diagram.
17125 *
17126 * @method BaseViewer#clear
17127 */
17128 BaseViewer.prototype.clear = function() {
17129 if (!this.getDefinitions()) {
17130
17131 // no diagram to clear
17132 return;
17133 }
17134
17135 // remove drawn elements
17136 Diagram.prototype.clear.call(this);
17137 };
17138
17139 /**
17140 * Destroy the viewer instance and remove all its
17141 * remainders from the document tree.
17142 */
17143 BaseViewer.prototype.destroy = function() {
17144
17145 // diagram destroy
17146 Diagram.prototype.destroy.call(this);
17147
17148 // dom detach
17149 remove$3(this._container);
17150 };
17151
17152 /**
17153 * Register an event listener
17154 *
17155 * Remove a previously added listener via {@link #off(event, callback)}.
17156 *
17157 * @param {string} event
17158 * @param {number} [priority]
17159 * @param {Function} callback
17160 * @param {Object} [that]
17161 */
17162 BaseViewer.prototype.on = function(event, priority, callback, target) {
17163 return this.get('eventBus').on(event, priority, callback, target);
17164 };
17165
17166 /**
17167 * De-register an event listener
17168 *
17169 * @param {string} event
17170 * @param {Function} callback
17171 */
17172 BaseViewer.prototype.off = function(event, callback) {
17173 this.get('eventBus').off(event, callback);
17174 };
17175
17176 BaseViewer.prototype.attachTo = function(parentNode) {
17177
17178 if (!parentNode) {
17179 throw new Error('parentNode required');
17180 }
17181
17182 // ensure we detach from the
17183 // previous, old parent
17184 this.detach();
17185
17186 // unwrap jQuery if provided
17187 if (parentNode.get && parentNode.constructor.prototype.jquery) {
17188 parentNode = parentNode.get(0);
17189 }
17190
17191 if (typeof parentNode === 'string') {
17192 parentNode = query$1(parentNode);
17193 }
17194
17195 parentNode.appendChild(this._container);
17196
17197 this._emit('attach', {});
17198
17199 this.get('canvas').resized();
17200 };
17201
17202 BaseViewer.prototype.getDefinitions = function() {
17203 return this._definitions;
17204 };
17205
17206 BaseViewer.prototype.detach = function() {
17207
17208 var container = this._container,
17209 parentNode = container.parentNode;
17210
17211 if (!parentNode) {
17212 return;
17213 }
17214
17215 this._emit('detach', {});
17216
17217 parentNode.removeChild(container);
17218 };
17219
17220 BaseViewer.prototype._init = function(container, moddle, options) {
17221
17222 var baseModules = options.modules || this.getModules(),
17223 additionalModules = options.additionalModules || [],
17224 staticModules = [
17225 {
17226 bpmnjs: [ 'value', this ],
17227 moddle: [ 'value', moddle ]
17228 }
17229 ];
17230
17231 var diagramModules = [].concat(staticModules, baseModules, additionalModules);
17232
17233 var diagramOptions = assign(omit(options, [ 'additionalModules' ]), {
17234 canvas: assign({}, options.canvas, { container: container }),
17235 modules: diagramModules
17236 });
17237
17238 // invoke diagram constructor
17239 Diagram.call(this, diagramOptions);
17240
17241 if (options && options.container) {
17242 this.attachTo(options.container);
17243 }
17244 };
17245
17246 /**
17247 * Emit an event on the underlying {@link EventBus}
17248 *
17249 * @param {string} type
17250 * @param {Object} event
17251 *
17252 * @return {Object} event processing result (if any)
17253 */
17254 BaseViewer.prototype._emit = function(type, event) {
17255 return this.get('eventBus').fire(type, event);
17256 };
17257
17258 BaseViewer.prototype._createContainer = function(options) {
17259
17260 var container = domify$1('<div class="bjs-container"></div>');
17261
17262 assign$1$1(container, {
17263 width: ensureUnit(options.width),
17264 height: ensureUnit(options.height),
17265 position: options.position
17266 });
17267
17268 return container;
17269 };
17270
17271 BaseViewer.prototype._createModdle = function(options) {
17272 var moddleOptions = assign({}, this._moddleExtensions, options.moddleExtensions);
17273
17274 return new simple(moddleOptions);
17275 };
17276
17277 BaseViewer.prototype._modules = [];
17278
17279 // helpers ///////////////
17280
17281 function addWarningsToError(err, warningsAry) {
17282 err.warnings = warningsAry;
17283 return err;
17284 }
17285
17286 function checkValidationError(err) {
17287
17288 // check if we can help the user by indicating wrong BPMN 2.0 xml
17289 // (in case he or the exporting tool did not get that right)
17290
17291 var pattern = /unparsable content <([^>]+)> detected([\s\S]*)$/;
17292 var match = pattern.exec(err.message);
17293
17294 if (match) {
17295 err.message =
17296 'unparsable content <' + match[1] + '> detected; ' +
17297 'this may indicate an invalid BPMN 2.0 diagram file' + match[2];
17298 }
17299
17300 return err;
17301 }
17302
17303 var DEFAULT_OPTIONS = {
17304 width: '100%',
17305 height: '100%',
17306 position: 'relative'
17307 };
17308
17309
17310 /**
17311 * Ensure the passed argument is a proper unit (defaulting to px)
17312 */
17313 function ensureUnit(val) {
17314 return val + (isNumber(val) ? 'px' : '');
17315 }
17316
17317
17318 /**
17319 * Find BPMNDiagram in definitions by ID
17320 *
17321 * @param {ModdleElement<Definitions>} definitions
17322 * @param {string} diagramId
17323 *
17324 * @return {ModdleElement<BPMNDiagram>|null}
17325 */
17326 function findBPMNDiagram(definitions, diagramId) {
17327 if (!diagramId) {
17328 return null;
17329 }
17330
17331 return find(definitions.diagrams, function(element) {
17332 return element.id === diagramId;
17333 }) || null;
17334 }
17335
17336 /**
17337 * Adds the project logo to the diagram container as
17338 * required by the bpmn.io license.
17339 *
17340 * @see http://bpmn.io/license
17341 *
17342 * @param {Element} container
17343 */
17344 function addProjectLogo(container) {
17345 var img = BPMNIO_IMG;
17346
17347 var linkMarkup =
17348 '<a href="http://bpmn.io" ' +
17349 'target="_blank" ' +
17350 'class="bjs-powered-by" ' +
17351 'title="Powered by bpmn.io" ' +
17352 '>' +
17353 img +
17354 '</a>';
17355
17356 var linkElement = domify$1(linkMarkup);
17357
17358 assign$1$1(query$1('svg', linkElement), LOGO_STYLES);
17359 assign$1$1(linkElement, LINK_STYLES, {
17360 position: 'absolute',
17361 bottom: '15px',
17362 right: '15px',
17363 zIndex: '100'
17364 });
17365
17366 container.appendChild(linkElement);
17367
17368 componentEvent$1.bind(linkElement, 'click', function(event) {
17369 open();
17370
17371 event.preventDefault();
17372 });
17373 }
17374
17375 /* </project-logo> */
17376
17377 /**
17378 * A base modeler for BPMN 2.0 diagrams.
17379 *
17380 * Have a look at {@link Modeler} for a bundle that includes actual features.
17381 *
17382 * @param {Object} [options] configuration options to pass to the viewer
17383 * @param {DOMElement} [options.container] the container to render the viewer in, defaults to body.
17384 * @param {string|number} [options.width] the width of the viewer
17385 * @param {string|number} [options.height] the height of the viewer
17386 * @param {Object} [options.moddleExtensions] extension packages to provide
17387 * @param {Array<didi.Module>} [options.modules] a list of modules to override the default modules
17388 * @param {Array<didi.Module>} [options.additionalModules] a list of modules to use with the default modules
17389 */
17390 function BaseModeler(options) {
17391 BaseViewer.call(this, options);
17392
17393 // hook ID collection into the modeler
17394 this.on('import.parse.complete', function(event) {
17395 if (!event.error) {
17396 this._collectIds(event.definitions, event.elementsById);
17397 }
17398 }, this);
17399
17400 this.on('diagram.destroy', function() {
17401 this.get('moddle').ids.clear();
17402 }, this);
17403 }
17404
17405 inherits$1(BaseModeler, BaseViewer);
17406
17407
17408 /**
17409 * Create a moddle instance, attaching ids to it.
17410 *
17411 * @param {Object} options
17412 */
17413 BaseModeler.prototype._createModdle = function(options) {
17414 var moddle = BaseViewer.prototype._createModdle.call(this, options);
17415
17416 // attach ids to moddle to be able to track
17417 // and validated ids in the BPMN 2.0 XML document
17418 // tree
17419 moddle.ids = new Ids([ 32, 36, 1 ]);
17420
17421 return moddle;
17422 };
17423
17424 /**
17425 * Collect ids processed during parsing of the
17426 * definitions object.
17427 *
17428 * @param {ModdleElement} definitions
17429 * @param {Context} context
17430 */
17431 BaseModeler.prototype._collectIds = function(definitions, elementsById) {
17432
17433 var moddle = definitions.$model,
17434 ids = moddle.ids,
17435 id;
17436
17437 // remove references from previous import
17438 ids.clear();
17439
17440 for (id in elementsById) {
17441 ids.claim(id, elementsById[id]);
17442 }
17443 };
17444
17445 function isExpanded(element, di) {
17446
17447 if (is$1(element, 'bpmn:CallActivity')) {
17448 return false;
17449 }
17450
17451 if (is$1(element, 'bpmn:SubProcess')) {
17452 di = di || getDi(element);
17453
17454 if (di && is$1(di, 'bpmndi:BPMNPlane')) {
17455 return true;
17456 }
17457
17458 return di && !!di.isExpanded;
17459 }
17460
17461 if (is$1(element, 'bpmn:Participant')) {
17462 return !!getBusinessObject(element).processRef;
17463 }
17464
17465 return true;
17466 }
17467
17468 function isInterrupting(element) {
17469 return element && getBusinessObject(element).isInterrupting !== false;
17470 }
17471
17472 function isEventSubProcess(element) {
17473 return element && !!getBusinessObject(element).triggeredByEvent;
17474 }
17475
17476 function hasEventDefinition$2(element, eventType) {
17477 var bo = getBusinessObject(element),
17478 hasEventDefinition = false;
17479
17480 if (bo.eventDefinitions) {
17481 forEach$2(bo.eventDefinitions, function(event) {
17482 if (is$1(event, eventType)) {
17483 hasEventDefinition = true;
17484 }
17485 });
17486 }
17487
17488 return hasEventDefinition;
17489 }
17490
17491 function hasErrorEventDefinition(element) {
17492 return hasEventDefinition$2(element, 'bpmn:ErrorEventDefinition');
17493 }
17494
17495 function hasEscalationEventDefinition(element) {
17496 return hasEventDefinition$2(element, 'bpmn:EscalationEventDefinition');
17497 }
17498
17499 function hasCompensateEventDefinition(element) {
17500 return hasEventDefinition$2(element, 'bpmn:CompensateEventDefinition');
17501 }
17502
17503 function getLabelAttr(semantic) {
17504 if (
17505 is$1(semantic, 'bpmn:FlowElement') ||
17506 is$1(semantic, 'bpmn:Participant') ||
17507 is$1(semantic, 'bpmn:Lane') ||
17508 is$1(semantic, 'bpmn:SequenceFlow') ||
17509 is$1(semantic, 'bpmn:MessageFlow') ||
17510 is$1(semantic, 'bpmn:DataInput') ||
17511 is$1(semantic, 'bpmn:DataOutput')
17512 ) {
17513 return 'name';
17514 }
17515
17516 if (is$1(semantic, 'bpmn:TextAnnotation')) {
17517 return 'text';
17518 }
17519
17520 if (is$1(semantic, 'bpmn:Group')) {
17521 return 'categoryValueRef';
17522 }
17523 }
17524
17525 function getCategoryValue(semantic) {
17526 var categoryValueRef = semantic['categoryValueRef'];
17527
17528 if (!categoryValueRef) {
17529 return '';
17530 }
17531
17532
17533 return categoryValueRef.value || '';
17534 }
17535
17536 function getLabel(element) {
17537 var semantic = element.businessObject,
17538 attr = getLabelAttr(semantic);
17539
17540 if (attr) {
17541
17542 if (attr === 'categoryValueRef') {
17543
17544 return getCategoryValue(semantic);
17545 }
17546
17547 return semantic[attr] || '';
17548 }
17549 }
17550
17551
17552 function setLabel(element, text, isExternal) {
17553 var semantic = element.businessObject,
17554 attr = getLabelAttr(semantic);
17555
17556 if (attr) {
17557
17558 if (attr === 'categoryValueRef') {
17559 semantic['categoryValueRef'].value = text;
17560 } else {
17561 semantic[attr] = text;
17562 }
17563
17564 }
17565
17566 return element;
17567 }
17568
17569 // element utils //////////////////////
17570
17571 /**
17572 * Checks if eventDefinition of the given element matches with semantic type.
17573 *
17574 * @return {boolean} true if element is of the given semantic type
17575 */
17576 function isTypedEvent(event, eventDefinitionType, filter) {
17577
17578 function matches(definition, filter) {
17579 return every(filter, function(val, key) {
17580
17581 // we want a == conversion here, to be able to catch
17582 // undefined == false and friends
17583 /* jshint -W116 */
17584 return definition[key] == val;
17585 });
17586 }
17587
17588 return some(event.eventDefinitions, function(definition) {
17589 return definition.$type === eventDefinitionType && matches(event, filter);
17590 });
17591 }
17592
17593 function isThrowEvent(event) {
17594 return (event.$type === 'bpmn:IntermediateThrowEvent') || (event.$type === 'bpmn:EndEvent');
17595 }
17596
17597 function isCollection(element) {
17598 var dataObject = element.dataObjectRef;
17599
17600 return element.isCollection || (dataObject && dataObject.isCollection);
17601 }
17602
17603 function getSemantic(element) {
17604 return element.businessObject;
17605 }
17606
17607
17608 // color access //////////////////////
17609
17610 function getFillColor(element, defaultColor) {
17611 var di = getDi(element);
17612
17613 return di.get('color:background-color') || di.get('bioc:fill') || defaultColor || 'white';
17614 }
17615
17616 function getStrokeColor$1(element, defaultColor) {
17617 var di = getDi(element);
17618
17619 return di.get('color:border-color') || di.get('bioc:stroke') || defaultColor || 'black';
17620 }
17621
17622 function getLabelColor(element, defaultColor, defaultStrokeColor) {
17623 var di = getDi(element),
17624 label = di.get('label');
17625
17626 return label && label.get('color:color') || defaultColor ||
17627 getStrokeColor$1(element, defaultStrokeColor);
17628 }
17629
17630 // cropping path customizations //////////////////////
17631
17632 function getCirclePath(shape) {
17633
17634 var cx = shape.x + shape.width / 2,
17635 cy = shape.y + shape.height / 2,
17636 radius = shape.width / 2;
17637
17638 var circlePath = [
17639 ['M', cx, cy],
17640 ['m', 0, -radius],
17641 ['a', radius, radius, 0, 1, 1, 0, 2 * radius],
17642 ['a', radius, radius, 0, 1, 1, 0, -2 * radius],
17643 ['z']
17644 ];
17645
17646 return componentsToPath(circlePath);
17647 }
17648
17649 function getRoundRectPath(shape, borderRadius) {
17650
17651 var x = shape.x,
17652 y = shape.y,
17653 width = shape.width,
17654 height = shape.height;
17655
17656 var roundRectPath = [
17657 ['M', x + borderRadius, y],
17658 ['l', width - borderRadius * 2, 0],
17659 ['a', borderRadius, borderRadius, 0, 0, 1, borderRadius, borderRadius],
17660 ['l', 0, height - borderRadius * 2],
17661 ['a', borderRadius, borderRadius, 0, 0, 1, -borderRadius, borderRadius],
17662 ['l', borderRadius * 2 - width, 0],
17663 ['a', borderRadius, borderRadius, 0, 0, 1, -borderRadius, -borderRadius],
17664 ['l', 0, borderRadius * 2 - height],
17665 ['a', borderRadius, borderRadius, 0, 0, 1, borderRadius, -borderRadius],
17666 ['z']
17667 ];
17668
17669 return componentsToPath(roundRectPath);
17670 }
17671
17672 function getDiamondPath(shape) {
17673
17674 var width = shape.width,
17675 height = shape.height,
17676 x = shape.x,
17677 y = shape.y,
17678 halfWidth = width / 2,
17679 halfHeight = height / 2;
17680
17681 var diamondPath = [
17682 ['M', x + halfWidth, y],
17683 ['l', halfWidth, halfHeight],
17684 ['l', -halfWidth, halfHeight],
17685 ['l', -halfWidth, -halfHeight],
17686 ['z']
17687 ];
17688
17689 return componentsToPath(diamondPath);
17690 }
17691
17692 function getRectPath(shape) {
17693 var x = shape.x,
17694 y = shape.y,
17695 width = shape.width,
17696 height = shape.height;
17697
17698 var rectPath = [
17699 ['M', x, y],
17700 ['l', width, 0],
17701 ['l', 0, height],
17702 ['l', -width, 0],
17703 ['z']
17704 ];
17705
17706 return componentsToPath(rectPath);
17707 }
17708
17709 var RENDERER_IDS = new Ids();
17710
17711 var TASK_BORDER_RADIUS = 10;
17712 var INNER_OUTER_DIST = 3;
17713
17714 var DEFAULT_FILL_OPACITY = .95,
17715 HIGH_FILL_OPACITY = .35;
17716
17717 var ELEMENT_LABEL_DISTANCE$1 = 10;
17718
17719 function BpmnRenderer(
17720 config, eventBus, styles, pathMap,
17721 canvas, textRenderer, priority) {
17722
17723 BaseRenderer.call(this, eventBus, priority);
17724
17725 var defaultFillColor = config && config.defaultFillColor,
17726 defaultStrokeColor = config && config.defaultStrokeColor,
17727 defaultLabelColor = config && config.defaultLabelColor;
17728
17729 var rendererId = RENDERER_IDS.next();
17730
17731 var markers = {};
17732
17733 var computeStyle = styles.computeStyle;
17734
17735 function addMarker(id, options) {
17736 var attrs = assign({
17737 fill: 'black',
17738 strokeWidth: 1,
17739 strokeLinecap: 'round',
17740 strokeDasharray: 'none'
17741 }, options.attrs);
17742
17743 var ref = options.ref || { x: 0, y: 0 };
17744
17745 var scale = options.scale || 1;
17746
17747 // fix for safari / chrome / firefox bug not correctly
17748 // resetting stroke dash array
17749 if (attrs.strokeDasharray === 'none') {
17750 attrs.strokeDasharray = [10000, 1];
17751 }
17752
17753 var marker = create$1('marker');
17754
17755 attr$1(options.element, attrs);
17756
17757 append(marker, options.element);
17758
17759 attr$1(marker, {
17760 id: id,
17761 viewBox: '0 0 20 20',
17762 refX: ref.x,
17763 refY: ref.y,
17764 markerWidth: 20 * scale,
17765 markerHeight: 20 * scale,
17766 orient: 'auto'
17767 });
17768
17769 var defs = query$1('defs', canvas._svg);
17770
17771 if (!defs) {
17772 defs = create$1('defs');
17773
17774 append(canvas._svg, defs);
17775 }
17776
17777 append(defs, marker);
17778
17779 markers[id] = marker;
17780 }
17781
17782 function colorEscape(str) {
17783
17784 // only allow characters and numbers
17785 return str.replace(/[^0-9a-zA-z]+/g, '_');
17786 }
17787
17788 function marker(type, fill, stroke) {
17789 var id = type + '-' + colorEscape(fill) + '-' + colorEscape(stroke) + '-' + rendererId;
17790
17791 if (!markers[id]) {
17792 createMarker(id, type, fill, stroke);
17793 }
17794
17795 return 'url(#' + id + ')';
17796 }
17797
17798 function createMarker(id, type, fill, stroke) {
17799
17800 if (type === 'sequenceflow-end') {
17801 var sequenceflowEnd = create$1('path');
17802 attr$1(sequenceflowEnd, { d: 'M 1 5 L 11 10 L 1 15 Z' });
17803
17804 addMarker(id, {
17805 element: sequenceflowEnd,
17806 ref: { x: 11, y: 10 },
17807 scale: 0.5,
17808 attrs: {
17809 fill: stroke,
17810 stroke: stroke
17811 }
17812 });
17813 }
17814
17815 if (type === 'messageflow-start') {
17816 var messageflowStart = create$1('circle');
17817 attr$1(messageflowStart, { cx: 6, cy: 6, r: 3.5 });
17818
17819 addMarker(id, {
17820 element: messageflowStart,
17821 attrs: {
17822 fill: fill,
17823 stroke: stroke
17824 },
17825 ref: { x: 6, y: 6 }
17826 });
17827 }
17828
17829 if (type === 'messageflow-end') {
17830 var messageflowEnd = create$1('path');
17831 attr$1(messageflowEnd, { d: 'm 1 5 l 0 -3 l 7 3 l -7 3 z' });
17832
17833 addMarker(id, {
17834 element: messageflowEnd,
17835 attrs: {
17836 fill: fill,
17837 stroke: stroke,
17838 strokeLinecap: 'butt'
17839 },
17840 ref: { x: 8.5, y: 5 }
17841 });
17842 }
17843
17844 if (type === 'association-start') {
17845 var associationStart = create$1('path');
17846 attr$1(associationStart, { d: 'M 11 5 L 1 10 L 11 15' });
17847
17848 addMarker(id, {
17849 element: associationStart,
17850 attrs: {
17851 fill: 'none',
17852 stroke: stroke,
17853 strokeWidth: 1.5
17854 },
17855 ref: { x: 1, y: 10 },
17856 scale: 0.5
17857 });
17858 }
17859
17860 if (type === 'association-end') {
17861 var associationEnd = create$1('path');
17862 attr$1(associationEnd, { d: 'M 1 5 L 11 10 L 1 15' });
17863
17864 addMarker(id, {
17865 element: associationEnd,
17866 attrs: {
17867 fill: 'none',
17868 stroke: stroke,
17869 strokeWidth: 1.5
17870 },
17871 ref: { x: 12, y: 10 },
17872 scale: 0.5
17873 });
17874 }
17875
17876 if (type === 'conditional-flow-marker') {
17877 var conditionalflowMarker = create$1('path');
17878 attr$1(conditionalflowMarker, { d: 'M 0 10 L 8 6 L 16 10 L 8 14 Z' });
17879
17880 addMarker(id, {
17881 element: conditionalflowMarker,
17882 attrs: {
17883 fill: fill,
17884 stroke: stroke
17885 },
17886 ref: { x: -1, y: 10 },
17887 scale: 0.5
17888 });
17889 }
17890
17891 if (type === 'conditional-default-flow-marker') {
17892 var conditionaldefaultflowMarker = create$1('path');
17893 attr$1(conditionaldefaultflowMarker, { d: 'M 6 4 L 10 16' });
17894
17895 addMarker(id, {
17896 element: conditionaldefaultflowMarker,
17897 attrs: {
17898 stroke: stroke
17899 },
17900 ref: { x: 0, y: 10 },
17901 scale: 0.5
17902 });
17903 }
17904 }
17905
17906 function drawCircle(parentGfx, width, height, offset, attrs) {
17907
17908 if (isObject(offset)) {
17909 attrs = offset;
17910 offset = 0;
17911 }
17912
17913 offset = offset || 0;
17914
17915 attrs = computeStyle(attrs, {
17916 stroke: 'black',
17917 strokeWidth: 2,
17918 fill: 'white'
17919 });
17920
17921 if (attrs.fill === 'none') {
17922 delete attrs.fillOpacity;
17923 }
17924
17925 var cx = width / 2,
17926 cy = height / 2;
17927
17928 var circle = create$1('circle');
17929 attr$1(circle, {
17930 cx: cx,
17931 cy: cy,
17932 r: Math.round((width + height) / 4 - offset)
17933 });
17934 attr$1(circle, attrs);
17935
17936 append(parentGfx, circle);
17937
17938 return circle;
17939 }
17940
17941 function drawRect(parentGfx, width, height, r, offset, attrs) {
17942
17943 if (isObject(offset)) {
17944 attrs = offset;
17945 offset = 0;
17946 }
17947
17948 offset = offset || 0;
17949
17950 attrs = computeStyle(attrs, {
17951 stroke: 'black',
17952 strokeWidth: 2,
17953 fill: 'white'
17954 });
17955
17956 var rect = create$1('rect');
17957 attr$1(rect, {
17958 x: offset,
17959 y: offset,
17960 width: width - offset * 2,
17961 height: height - offset * 2,
17962 rx: r,
17963 ry: r
17964 });
17965 attr$1(rect, attrs);
17966
17967 append(parentGfx, rect);
17968
17969 return rect;
17970 }
17971
17972 function drawDiamond(parentGfx, width, height, attrs) {
17973
17974 var x_2 = width / 2;
17975 var y_2 = height / 2;
17976
17977 var points = [{ x: x_2, y: 0 }, { x: width, y: y_2 }, { x: x_2, y: height }, { x: 0, y: y_2 }];
17978
17979 var pointsString = points.map(function(point) {
17980 return point.x + ',' + point.y;
17981 }).join(' ');
17982
17983 attrs = computeStyle(attrs, {
17984 stroke: 'black',
17985 strokeWidth: 2,
17986 fill: 'white'
17987 });
17988
17989 var polygon = create$1('polygon');
17990 attr$1(polygon, {
17991 points: pointsString
17992 });
17993 attr$1(polygon, attrs);
17994
17995 append(parentGfx, polygon);
17996
17997 return polygon;
17998 }
17999
18000 function drawLine(parentGfx, waypoints, attrs) {
18001 attrs = computeStyle(attrs, [ 'no-fill' ], {
18002 stroke: 'black',
18003 strokeWidth: 2,
18004 fill: 'none'
18005 });
18006
18007 var line = createLine(waypoints, attrs);
18008
18009 append(parentGfx, line);
18010
18011 return line;
18012 }
18013
18014 function drawPath(parentGfx, d, attrs) {
18015
18016 attrs = computeStyle(attrs, [ 'no-fill' ], {
18017 strokeWidth: 2,
18018 stroke: 'black'
18019 });
18020
18021 var path = create$1('path');
18022 attr$1(path, { d: d });
18023 attr$1(path, attrs);
18024
18025 append(parentGfx, path);
18026
18027 return path;
18028 }
18029
18030 function drawMarker(type, parentGfx, path, attrs) {
18031 return drawPath(parentGfx, path, assign({ 'data-marker': type }, attrs));
18032 }
18033
18034 function renderer(type) {
18035 return handlers[type];
18036 }
18037
18038 function as(type) {
18039 return function(parentGfx, element) {
18040 return renderer(type)(parentGfx, element);
18041 };
18042 }
18043
18044 function renderEventContent(element, parentGfx) {
18045
18046 var event = getSemantic(element);
18047 var isThrowing = isThrowEvent(event);
18048
18049 if (event.eventDefinitions && event.eventDefinitions.length>1) {
18050 if (event.parallelMultiple) {
18051 return renderer('bpmn:ParallelMultipleEventDefinition')(parentGfx, element, isThrowing);
18052 }
18053 else {
18054 return renderer('bpmn:MultipleEventDefinition')(parentGfx, element, isThrowing);
18055 }
18056 }
18057
18058 if (isTypedEvent(event, 'bpmn:MessageEventDefinition')) {
18059 return renderer('bpmn:MessageEventDefinition')(parentGfx, element, isThrowing);
18060 }
18061
18062 if (isTypedEvent(event, 'bpmn:TimerEventDefinition')) {
18063 return renderer('bpmn:TimerEventDefinition')(parentGfx, element, isThrowing);
18064 }
18065
18066 if (isTypedEvent(event, 'bpmn:ConditionalEventDefinition')) {
18067 return renderer('bpmn:ConditionalEventDefinition')(parentGfx, element);
18068 }
18069
18070 if (isTypedEvent(event, 'bpmn:SignalEventDefinition')) {
18071 return renderer('bpmn:SignalEventDefinition')(parentGfx, element, isThrowing);
18072 }
18073
18074 if (isTypedEvent(event, 'bpmn:EscalationEventDefinition')) {
18075 return renderer('bpmn:EscalationEventDefinition')(parentGfx, element, isThrowing);
18076 }
18077
18078 if (isTypedEvent(event, 'bpmn:LinkEventDefinition')) {
18079 return renderer('bpmn:LinkEventDefinition')(parentGfx, element, isThrowing);
18080 }
18081
18082 if (isTypedEvent(event, 'bpmn:ErrorEventDefinition')) {
18083 return renderer('bpmn:ErrorEventDefinition')(parentGfx, element, isThrowing);
18084 }
18085
18086 if (isTypedEvent(event, 'bpmn:CancelEventDefinition')) {
18087 return renderer('bpmn:CancelEventDefinition')(parentGfx, element, isThrowing);
18088 }
18089
18090 if (isTypedEvent(event, 'bpmn:CompensateEventDefinition')) {
18091 return renderer('bpmn:CompensateEventDefinition')(parentGfx, element, isThrowing);
18092 }
18093
18094 if (isTypedEvent(event, 'bpmn:TerminateEventDefinition')) {
18095 return renderer('bpmn:TerminateEventDefinition')(parentGfx, element, isThrowing);
18096 }
18097
18098 return null;
18099 }
18100
18101 function renderLabel(parentGfx, label, options) {
18102
18103 options = assign({
18104 size: {
18105 width: 100
18106 }
18107 }, options);
18108
18109 var text = textRenderer.createText(label || '', options);
18110
18111 classes$1(text).add('djs-label');
18112
18113 append(parentGfx, text);
18114
18115 return text;
18116 }
18117
18118 function renderEmbeddedLabel(parentGfx, element, align) {
18119 var semantic = getSemantic(element);
18120
18121 return renderLabel(parentGfx, semantic.name, {
18122 box: element,
18123 align: align,
18124 padding: 5,
18125 style: {
18126 fill: getLabelColor(element, defaultLabelColor, defaultStrokeColor)
18127 }
18128 });
18129 }
18130
18131 function renderExternalLabel(parentGfx, element) {
18132
18133 var box = {
18134 width: 90,
18135 height: 30,
18136 x: element.width / 2 + element.x,
18137 y: element.height / 2 + element.y
18138 };
18139
18140 return renderLabel(parentGfx, getLabel(element), {
18141 box: box,
18142 fitBox: true,
18143 style: assign(
18144 {},
18145 textRenderer.getExternalStyle(),
18146 {
18147 fill: getLabelColor(element, defaultLabelColor, defaultStrokeColor)
18148 }
18149 )
18150 });
18151 }
18152
18153 function renderLaneLabel(parentGfx, text, element) {
18154 var textBox = renderLabel(parentGfx, text, {
18155 box: {
18156 height: 30,
18157 width: element.height
18158 },
18159 align: 'center-middle',
18160 style: {
18161 fill: getLabelColor(element, defaultLabelColor, defaultStrokeColor)
18162 }
18163 });
18164
18165 var top = -1 * element.height;
18166
18167 transform(textBox, 0, -top, 270);
18168 }
18169
18170 function createPathFromConnection(connection) {
18171 var waypoints = connection.waypoints;
18172
18173 var pathData = 'm ' + waypoints[0].x + ',' + waypoints[0].y;
18174 for (var i = 1; i < waypoints.length; i++) {
18175 pathData += 'L' + waypoints[i].x + ',' + waypoints[i].y + ' ';
18176 }
18177 return pathData;
18178 }
18179
18180 var handlers = this.handlers = {
18181 'bpmn:Event': function(parentGfx, element, attrs) {
18182
18183 if (!('fillOpacity' in attrs)) {
18184 attrs.fillOpacity = DEFAULT_FILL_OPACITY;
18185 }
18186
18187 return drawCircle(parentGfx, element.width, element.height, attrs);
18188 },
18189 'bpmn:StartEvent': function(parentGfx, element) {
18190 var attrs = {
18191 fill: getFillColor(element, defaultFillColor),
18192 stroke: getStrokeColor$1(element, defaultStrokeColor)
18193 };
18194
18195 var semantic = getSemantic(element);
18196
18197 if (!semantic.isInterrupting) {
18198 attrs = {
18199 strokeDasharray: '6',
18200 strokeLinecap: 'round',
18201 fill: getFillColor(element, defaultFillColor),
18202 stroke: getStrokeColor$1(element, defaultStrokeColor)
18203 };
18204 }
18205
18206 var circle = renderer('bpmn:Event')(parentGfx, element, attrs);
18207
18208 renderEventContent(element, parentGfx);
18209
18210 return circle;
18211 },
18212 'bpmn:MessageEventDefinition': function(parentGfx, element, isThrowing) {
18213 var pathData = pathMap.getScaledPath('EVENT_MESSAGE', {
18214 xScaleFactor: 0.9,
18215 yScaleFactor: 0.9,
18216 containerWidth: element.width,
18217 containerHeight: element.height,
18218 position: {
18219 mx: 0.235,
18220 my: 0.315
18221 }
18222 });
18223
18224 var fill = isThrowing ? getStrokeColor$1(element, defaultStrokeColor) : getFillColor(element, defaultFillColor);
18225 var stroke = isThrowing ? getFillColor(element, defaultFillColor) : getStrokeColor$1(element, defaultStrokeColor);
18226
18227 var messagePath = drawPath(parentGfx, pathData, {
18228 strokeWidth: 1,
18229 fill: fill,
18230 stroke: stroke
18231 });
18232
18233 return messagePath;
18234 },
18235 'bpmn:TimerEventDefinition': function(parentGfx, element) {
18236 var circle = drawCircle(parentGfx, element.width, element.height, 0.2 * element.height, {
18237 strokeWidth: 2,
18238 fill: getFillColor(element, defaultFillColor),
18239 stroke: getStrokeColor$1(element, defaultStrokeColor)
18240 });
18241
18242 var pathData = pathMap.getScaledPath('EVENT_TIMER_WH', {
18243 xScaleFactor: 0.75,
18244 yScaleFactor: 0.75,
18245 containerWidth: element.width,
18246 containerHeight: element.height,
18247 position: {
18248 mx: 0.5,
18249 my: 0.5
18250 }
18251 });
18252
18253 drawPath(parentGfx, pathData, {
18254 strokeWidth: 2,
18255 strokeLinecap: 'square',
18256 stroke: getStrokeColor$1(element, defaultStrokeColor)
18257 });
18258
18259 for (var i = 0;i < 12; i++) {
18260
18261 var linePathData = pathMap.getScaledPath('EVENT_TIMER_LINE', {
18262 xScaleFactor: 0.75,
18263 yScaleFactor: 0.75,
18264 containerWidth: element.width,
18265 containerHeight: element.height,
18266 position: {
18267 mx: 0.5,
18268 my: 0.5
18269 }
18270 });
18271
18272 var width = element.width / 2;
18273 var height = element.height / 2;
18274
18275 drawPath(parentGfx, linePathData, {
18276 strokeWidth: 1,
18277 strokeLinecap: 'square',
18278 transform: 'rotate(' + (i * 30) + ',' + height + ',' + width + ')',
18279 stroke: getStrokeColor$1(element, defaultStrokeColor)
18280 });
18281 }
18282
18283 return circle;
18284 },
18285 'bpmn:EscalationEventDefinition': function(parentGfx, event, isThrowing) {
18286 var pathData = pathMap.getScaledPath('EVENT_ESCALATION', {
18287 xScaleFactor: 1,
18288 yScaleFactor: 1,
18289 containerWidth: event.width,
18290 containerHeight: event.height,
18291 position: {
18292 mx: 0.5,
18293 my: 0.2
18294 }
18295 });
18296
18297 var fill = isThrowing ? getStrokeColor$1(event, defaultStrokeColor) : 'none';
18298
18299 return drawPath(parentGfx, pathData, {
18300 strokeWidth: 1,
18301 fill: fill,
18302 stroke: getStrokeColor$1(event, defaultStrokeColor)
18303 });
18304 },
18305 'bpmn:ConditionalEventDefinition': function(parentGfx, event) {
18306 var pathData = pathMap.getScaledPath('EVENT_CONDITIONAL', {
18307 xScaleFactor: 1,
18308 yScaleFactor: 1,
18309 containerWidth: event.width,
18310 containerHeight: event.height,
18311 position: {
18312 mx: 0.5,
18313 my: 0.222
18314 }
18315 });
18316
18317 return drawPath(parentGfx, pathData, {
18318 strokeWidth: 1,
18319 stroke: getStrokeColor$1(event, defaultStrokeColor)
18320 });
18321 },
18322 'bpmn:LinkEventDefinition': function(parentGfx, event, isThrowing) {
18323 var pathData = pathMap.getScaledPath('EVENT_LINK', {
18324 xScaleFactor: 1,
18325 yScaleFactor: 1,
18326 containerWidth: event.width,
18327 containerHeight: event.height,
18328 position: {
18329 mx: 0.57,
18330 my: 0.263
18331 }
18332 });
18333
18334 var fill = isThrowing ? getStrokeColor$1(event, defaultStrokeColor) : 'none';
18335
18336 return drawPath(parentGfx, pathData, {
18337 strokeWidth: 1,
18338 fill: fill,
18339 stroke: getStrokeColor$1(event, defaultStrokeColor)
18340 });
18341 },
18342 'bpmn:ErrorEventDefinition': function(parentGfx, event, isThrowing) {
18343 var pathData = pathMap.getScaledPath('EVENT_ERROR', {
18344 xScaleFactor: 1.1,
18345 yScaleFactor: 1.1,
18346 containerWidth: event.width,
18347 containerHeight: event.height,
18348 position: {
18349 mx: 0.2,
18350 my: 0.722
18351 }
18352 });
18353
18354 var fill = isThrowing ? getStrokeColor$1(event, defaultStrokeColor) : 'none';
18355
18356 return drawPath(parentGfx, pathData, {
18357 strokeWidth: 1,
18358 fill: fill,
18359 stroke: getStrokeColor$1(event, defaultStrokeColor)
18360 });
18361 },
18362 'bpmn:CancelEventDefinition': function(parentGfx, event, isThrowing) {
18363 var pathData = pathMap.getScaledPath('EVENT_CANCEL_45', {
18364 xScaleFactor: 1.0,
18365 yScaleFactor: 1.0,
18366 containerWidth: event.width,
18367 containerHeight: event.height,
18368 position: {
18369 mx: 0.638,
18370 my: -0.055
18371 }
18372 });
18373
18374 var fill = isThrowing ? getStrokeColor$1(event, defaultStrokeColor) : 'none';
18375
18376 var path = drawPath(parentGfx, pathData, {
18377 strokeWidth: 1,
18378 fill: fill,
18379 stroke: getStrokeColor$1(event, defaultStrokeColor)
18380 });
18381
18382 rotate(path, 45);
18383
18384 return path;
18385 },
18386 'bpmn:CompensateEventDefinition': function(parentGfx, event, isThrowing) {
18387 var pathData = pathMap.getScaledPath('EVENT_COMPENSATION', {
18388 xScaleFactor: 1,
18389 yScaleFactor: 1,
18390 containerWidth: event.width,
18391 containerHeight: event.height,
18392 position: {
18393 mx: 0.22,
18394 my: 0.5
18395 }
18396 });
18397
18398 var fill = isThrowing ? getStrokeColor$1(event, defaultStrokeColor) : 'none';
18399
18400 return drawPath(parentGfx, pathData, {
18401 strokeWidth: 1,
18402 fill: fill,
18403 stroke: getStrokeColor$1(event, defaultStrokeColor)
18404 });
18405 },
18406 'bpmn:SignalEventDefinition': function(parentGfx, event, isThrowing) {
18407 var pathData = pathMap.getScaledPath('EVENT_SIGNAL', {
18408 xScaleFactor: 0.9,
18409 yScaleFactor: 0.9,
18410 containerWidth: event.width,
18411 containerHeight: event.height,
18412 position: {
18413 mx: 0.5,
18414 my: 0.2
18415 }
18416 });
18417
18418 var fill = isThrowing ? getStrokeColor$1(event, defaultStrokeColor) : 'none';
18419
18420 return drawPath(parentGfx, pathData, {
18421 strokeWidth: 1,
18422 fill: fill,
18423 stroke: getStrokeColor$1(event, defaultStrokeColor)
18424 });
18425 },
18426 'bpmn:MultipleEventDefinition': function(parentGfx, event, isThrowing) {
18427 var pathData = pathMap.getScaledPath('EVENT_MULTIPLE', {
18428 xScaleFactor: 1.1,
18429 yScaleFactor: 1.1,
18430 containerWidth: event.width,
18431 containerHeight: event.height,
18432 position: {
18433 mx: 0.222,
18434 my: 0.36
18435 }
18436 });
18437
18438 var fill = isThrowing ? getStrokeColor$1(event, defaultStrokeColor) : 'none';
18439
18440 return drawPath(parentGfx, pathData, {
18441 strokeWidth: 1,
18442 fill: fill
18443 });
18444 },
18445 'bpmn:ParallelMultipleEventDefinition': function(parentGfx, event) {
18446 var pathData = pathMap.getScaledPath('EVENT_PARALLEL_MULTIPLE', {
18447 xScaleFactor: 1.2,
18448 yScaleFactor: 1.2,
18449 containerWidth: event.width,
18450 containerHeight: event.height,
18451 position: {
18452 mx: 0.458,
18453 my: 0.194
18454 }
18455 });
18456
18457 return drawPath(parentGfx, pathData, {
18458 strokeWidth: 1,
18459 fill: getStrokeColor$1(event, defaultStrokeColor),
18460 stroke: getStrokeColor$1(event, defaultStrokeColor)
18461 });
18462 },
18463 'bpmn:EndEvent': function(parentGfx, element) {
18464 var circle = renderer('bpmn:Event')(parentGfx, element, {
18465 strokeWidth: 4,
18466 fill: getFillColor(element, defaultFillColor),
18467 stroke: getStrokeColor$1(element, defaultStrokeColor)
18468 });
18469
18470 renderEventContent(element, parentGfx);
18471
18472 return circle;
18473 },
18474 'bpmn:TerminateEventDefinition': function(parentGfx, element) {
18475 var circle = drawCircle(parentGfx, element.width, element.height, 8, {
18476 strokeWidth: 4,
18477 fill: getStrokeColor$1(element, defaultStrokeColor),
18478 stroke: getStrokeColor$1(element, defaultStrokeColor)
18479 });
18480
18481 return circle;
18482 },
18483 'bpmn:IntermediateEvent': function(parentGfx, element) {
18484 var outer = renderer('bpmn:Event')(parentGfx, element, {
18485 strokeWidth: 1,
18486 fill: getFillColor(element, defaultFillColor),
18487 stroke: getStrokeColor$1(element, defaultStrokeColor)
18488 });
18489
18490 /* inner */
18491 drawCircle(parentGfx, element.width, element.height, INNER_OUTER_DIST, {
18492 strokeWidth: 1,
18493 fill: getFillColor(element, 'none'),
18494 stroke: getStrokeColor$1(element, defaultStrokeColor)
18495 });
18496
18497 renderEventContent(element, parentGfx);
18498
18499 return outer;
18500 },
18501 'bpmn:IntermediateCatchEvent': as('bpmn:IntermediateEvent'),
18502 'bpmn:IntermediateThrowEvent': as('bpmn:IntermediateEvent'),
18503
18504 'bpmn:Activity': function(parentGfx, element, attrs) {
18505
18506 attrs = attrs || {};
18507
18508 if (!('fillOpacity' in attrs)) {
18509 attrs.fillOpacity = DEFAULT_FILL_OPACITY;
18510 }
18511
18512 return drawRect(parentGfx, element.width, element.height, TASK_BORDER_RADIUS, attrs);
18513 },
18514
18515 'bpmn:Task': function(parentGfx, element) {
18516 var attrs = {
18517 fill: getFillColor(element, defaultFillColor),
18518 stroke: getStrokeColor$1(element, defaultStrokeColor)
18519 };
18520
18521 var rect = renderer('bpmn:Activity')(parentGfx, element, attrs);
18522
18523 renderEmbeddedLabel(parentGfx, element, 'center-middle');
18524 attachTaskMarkers(parentGfx, element);
18525
18526 return rect;
18527 },
18528 'bpmn:ServiceTask': function(parentGfx, element) {
18529 var task = renderer('bpmn:Task')(parentGfx, element);
18530
18531 var pathDataBG = pathMap.getScaledPath('TASK_TYPE_SERVICE', {
18532 abspos: {
18533 x: 12,
18534 y: 18
18535 }
18536 });
18537
18538 /* service bg */ drawPath(parentGfx, pathDataBG, {
18539 strokeWidth: 1,
18540 fill: getFillColor(element, defaultFillColor),
18541 stroke: getStrokeColor$1(element, defaultStrokeColor)
18542 });
18543
18544 var fillPathData = pathMap.getScaledPath('TASK_TYPE_SERVICE_FILL', {
18545 abspos: {
18546 x: 17.2,
18547 y: 18
18548 }
18549 });
18550
18551 /* service fill */ drawPath(parentGfx, fillPathData, {
18552 strokeWidth: 0,
18553 fill: getFillColor(element, defaultFillColor)
18554 });
18555
18556 var pathData = pathMap.getScaledPath('TASK_TYPE_SERVICE', {
18557 abspos: {
18558 x: 17,
18559 y: 22
18560 }
18561 });
18562
18563 /* service */ drawPath(parentGfx, pathData, {
18564 strokeWidth: 1,
18565 fill: getFillColor(element, defaultFillColor),
18566 stroke: getStrokeColor$1(element, defaultStrokeColor)
18567 });
18568
18569 return task;
18570 },
18571 'bpmn:UserTask': function(parentGfx, element) {
18572 var task = renderer('bpmn:Task')(parentGfx, element);
18573
18574 var x = 15;
18575 var y = 12;
18576
18577 var pathData = pathMap.getScaledPath('TASK_TYPE_USER_1', {
18578 abspos: {
18579 x: x,
18580 y: y
18581 }
18582 });
18583
18584 /* user path */ drawPath(parentGfx, pathData, {
18585 strokeWidth: 0.5,
18586 fill: getFillColor(element, defaultFillColor),
18587 stroke: getStrokeColor$1(element, defaultStrokeColor)
18588 });
18589
18590 var pathData2 = pathMap.getScaledPath('TASK_TYPE_USER_2', {
18591 abspos: {
18592 x: x,
18593 y: y
18594 }
18595 });
18596
18597 /* user2 path */ drawPath(parentGfx, pathData2, {
18598 strokeWidth: 0.5,
18599 fill: getFillColor(element, defaultFillColor),
18600 stroke: getStrokeColor$1(element, defaultStrokeColor)
18601 });
18602
18603 var pathData3 = pathMap.getScaledPath('TASK_TYPE_USER_3', {
18604 abspos: {
18605 x: x,
18606 y: y
18607 }
18608 });
18609
18610 /* user3 path */ drawPath(parentGfx, pathData3, {
18611 strokeWidth: 0.5,
18612 fill: getStrokeColor$1(element, defaultStrokeColor),
18613 stroke: getStrokeColor$1(element, defaultStrokeColor)
18614 });
18615
18616 return task;
18617 },
18618 'bpmn:ManualTask': function(parentGfx, element) {
18619 var task = renderer('bpmn:Task')(parentGfx, element);
18620
18621 var pathData = pathMap.getScaledPath('TASK_TYPE_MANUAL', {
18622 abspos: {
18623 x: 17,
18624 y: 15
18625 }
18626 });
18627
18628 /* manual path */ drawPath(parentGfx, pathData, {
18629 strokeWidth: 0.5, // 0.25,
18630 fill: getFillColor(element, defaultFillColor),
18631 stroke: getStrokeColor$1(element, defaultStrokeColor)
18632 });
18633
18634 return task;
18635 },
18636 'bpmn:SendTask': function(parentGfx, element) {
18637 var task = renderer('bpmn:Task')(parentGfx, element);
18638
18639 var pathData = pathMap.getScaledPath('TASK_TYPE_SEND', {
18640 xScaleFactor: 1,
18641 yScaleFactor: 1,
18642 containerWidth: 21,
18643 containerHeight: 14,
18644 position: {
18645 mx: 0.285,
18646 my: 0.357
18647 }
18648 });
18649
18650 /* send path */ drawPath(parentGfx, pathData, {
18651 strokeWidth: 1,
18652 fill: getStrokeColor$1(element, defaultStrokeColor),
18653 stroke: getFillColor(element, defaultFillColor)
18654 });
18655
18656 return task;
18657 },
18658 'bpmn:ReceiveTask' : function(parentGfx, element) {
18659 var semantic = getSemantic(element);
18660
18661 var task = renderer('bpmn:Task')(parentGfx, element);
18662 var pathData;
18663
18664 if (semantic.instantiate) {
18665 drawCircle(parentGfx, 28, 28, 20 * 0.22, { strokeWidth: 1 });
18666
18667 pathData = pathMap.getScaledPath('TASK_TYPE_INSTANTIATING_SEND', {
18668 abspos: {
18669 x: 7.77,
18670 y: 9.52
18671 }
18672 });
18673 } else {
18674
18675 pathData = pathMap.getScaledPath('TASK_TYPE_SEND', {
18676 xScaleFactor: 0.9,
18677 yScaleFactor: 0.9,
18678 containerWidth: 21,
18679 containerHeight: 14,
18680 position: {
18681 mx: 0.3,
18682 my: 0.4
18683 }
18684 });
18685 }
18686
18687 /* receive path */ drawPath(parentGfx, pathData, {
18688 strokeWidth: 1,
18689 fill: getFillColor(element, defaultFillColor),
18690 stroke: getStrokeColor$1(element, defaultStrokeColor)
18691 });
18692
18693 return task;
18694 },
18695 'bpmn:ScriptTask': function(parentGfx, element) {
18696 var task = renderer('bpmn:Task')(parentGfx, element);
18697
18698 var pathData = pathMap.getScaledPath('TASK_TYPE_SCRIPT', {
18699 abspos: {
18700 x: 15,
18701 y: 20
18702 }
18703 });
18704
18705 /* script path */ drawPath(parentGfx, pathData, {
18706 strokeWidth: 1,
18707 stroke: getStrokeColor$1(element, defaultStrokeColor)
18708 });
18709
18710 return task;
18711 },
18712 'bpmn:BusinessRuleTask': function(parentGfx, element) {
18713 var task = renderer('bpmn:Task')(parentGfx, element);
18714
18715 var headerPathData = pathMap.getScaledPath('TASK_TYPE_BUSINESS_RULE_HEADER', {
18716 abspos: {
18717 x: 8,
18718 y: 8
18719 }
18720 });
18721
18722 var businessHeaderPath = drawPath(parentGfx, headerPathData);
18723 attr$1(businessHeaderPath, {
18724 strokeWidth: 1,
18725 fill: getFillColor(element, '#aaaaaa'),
18726 stroke: getStrokeColor$1(element, defaultStrokeColor)
18727 });
18728
18729 var headerData = pathMap.getScaledPath('TASK_TYPE_BUSINESS_RULE_MAIN', {
18730 abspos: {
18731 x: 8,
18732 y: 8
18733 }
18734 });
18735
18736 var businessPath = drawPath(parentGfx, headerData);
18737 attr$1(businessPath, {
18738 strokeWidth: 1,
18739 stroke: getStrokeColor$1(element, defaultStrokeColor)
18740 });
18741
18742 return task;
18743 },
18744 'bpmn:SubProcess': function(parentGfx, element, attrs) {
18745 attrs = assign({
18746 fill: getFillColor(element, defaultFillColor),
18747 stroke: getStrokeColor$1(element, defaultStrokeColor)
18748 }, attrs);
18749
18750 var rect = renderer('bpmn:Activity')(parentGfx, element, attrs);
18751
18752 var expanded = isExpanded(element);
18753
18754 if (isEventSubProcess(element)) {
18755 attr$1(rect, {
18756 strokeDasharray: '1,2'
18757 });
18758 }
18759
18760 renderEmbeddedLabel(parentGfx, element, expanded ? 'center-top' : 'center-middle');
18761
18762 if (expanded) {
18763 attachTaskMarkers(parentGfx, element);
18764 } else {
18765 attachTaskMarkers(parentGfx, element, ['SubProcessMarker']);
18766 }
18767
18768 return rect;
18769 },
18770 'bpmn:AdHocSubProcess': function(parentGfx, element) {
18771 return renderer('bpmn:SubProcess')(parentGfx, element);
18772 },
18773 'bpmn:Transaction': function(parentGfx, element) {
18774 var outer = renderer('bpmn:SubProcess')(parentGfx, element);
18775
18776 var innerAttrs = styles.style([ 'no-fill', 'no-events' ], {
18777 stroke: getStrokeColor$1(element, defaultStrokeColor)
18778 });
18779
18780 /* inner path */ drawRect(parentGfx, element.width, element.height, TASK_BORDER_RADIUS - 2, INNER_OUTER_DIST, innerAttrs);
18781
18782 return outer;
18783 },
18784 'bpmn:CallActivity': function(parentGfx, element) {
18785 return renderer('bpmn:SubProcess')(parentGfx, element, {
18786 strokeWidth: 5
18787 });
18788 },
18789 'bpmn:Participant': function(parentGfx, element) {
18790
18791 var attrs = {
18792 fillOpacity: DEFAULT_FILL_OPACITY,
18793 fill: getFillColor(element, defaultFillColor),
18794 stroke: getStrokeColor$1(element, defaultStrokeColor)
18795 };
18796
18797 var lane = renderer('bpmn:Lane')(parentGfx, element, attrs);
18798
18799 var expandedPool = isExpanded(element);
18800
18801 if (expandedPool) {
18802 drawLine(parentGfx, [
18803 { x: 30, y: 0 },
18804 { x: 30, y: element.height }
18805 ], {
18806 stroke: getStrokeColor$1(element, defaultStrokeColor)
18807 });
18808 var text = getSemantic(element).name;
18809 renderLaneLabel(parentGfx, text, element);
18810 } else {
18811
18812 // Collapsed pool draw text inline
18813 var text2 = getSemantic(element).name;
18814 renderLabel(parentGfx, text2, {
18815 box: element, align: 'center-middle',
18816 style: {
18817 fill: getLabelColor(element, defaultLabelColor, defaultStrokeColor)
18818 }
18819 });
18820 }
18821
18822 var participantMultiplicity = !!(getSemantic(element).participantMultiplicity);
18823
18824 if (participantMultiplicity) {
18825 renderer('ParticipantMultiplicityMarker')(parentGfx, element);
18826 }
18827
18828 return lane;
18829 },
18830 'bpmn:Lane': function(parentGfx, element, attrs) {
18831 var rect = drawRect(parentGfx, element.width, element.height, 0, assign({
18832 fill: getFillColor(element, defaultFillColor),
18833 fillOpacity: HIGH_FILL_OPACITY,
18834 stroke: getStrokeColor$1(element, defaultStrokeColor)
18835 }, attrs));
18836
18837 var semantic = getSemantic(element);
18838
18839 if (semantic.$type === 'bpmn:Lane') {
18840 var text = semantic.name;
18841 renderLaneLabel(parentGfx, text, element);
18842 }
18843
18844 return rect;
18845 },
18846 'bpmn:InclusiveGateway': function(parentGfx, element) {
18847 var diamond = renderer('bpmn:Gateway')(parentGfx, element);
18848
18849 /* circle path */
18850 drawCircle(parentGfx, element.width, element.height, element.height * 0.24, {
18851 strokeWidth: 2.5,
18852 fill: getFillColor(element, defaultFillColor),
18853 stroke: getStrokeColor$1(element, defaultStrokeColor)
18854 });
18855
18856 return diamond;
18857 },
18858 'bpmn:ExclusiveGateway': function(parentGfx, element) {
18859 var diamond = renderer('bpmn:Gateway')(parentGfx, element);
18860
18861 var pathData = pathMap.getScaledPath('GATEWAY_EXCLUSIVE', {
18862 xScaleFactor: 0.4,
18863 yScaleFactor: 0.4,
18864 containerWidth: element.width,
18865 containerHeight: element.height,
18866 position: {
18867 mx: 0.32,
18868 my: 0.3
18869 }
18870 });
18871
18872 if ((getDi(element).isMarkerVisible)) {
18873 drawPath(parentGfx, pathData, {
18874 strokeWidth: 1,
18875 fill: getStrokeColor$1(element, defaultStrokeColor),
18876 stroke: getStrokeColor$1(element, defaultStrokeColor)
18877 });
18878 }
18879
18880 return diamond;
18881 },
18882 'bpmn:ComplexGateway': function(parentGfx, element) {
18883 var diamond = renderer('bpmn:Gateway')(parentGfx, element);
18884
18885 var pathData = pathMap.getScaledPath('GATEWAY_COMPLEX', {
18886 xScaleFactor: 0.5,
18887 yScaleFactor:0.5,
18888 containerWidth: element.width,
18889 containerHeight: element.height,
18890 position: {
18891 mx: 0.46,
18892 my: 0.26
18893 }
18894 });
18895
18896 /* complex path */ drawPath(parentGfx, pathData, {
18897 strokeWidth: 1,
18898 fill: getStrokeColor$1(element, defaultStrokeColor),
18899 stroke: getStrokeColor$1(element, defaultStrokeColor)
18900 });
18901
18902 return diamond;
18903 },
18904 'bpmn:ParallelGateway': function(parentGfx, element) {
18905 var diamond = renderer('bpmn:Gateway')(parentGfx, element);
18906
18907 var pathData = pathMap.getScaledPath('GATEWAY_PARALLEL', {
18908 xScaleFactor: 0.6,
18909 yScaleFactor:0.6,
18910 containerWidth: element.width,
18911 containerHeight: element.height,
18912 position: {
18913 mx: 0.46,
18914 my: 0.2
18915 }
18916 });
18917
18918 /* parallel path */ drawPath(parentGfx, pathData, {
18919 strokeWidth: 1,
18920 fill: getStrokeColor$1(element, defaultStrokeColor),
18921 stroke: getStrokeColor$1(element, defaultStrokeColor)
18922 });
18923
18924 return diamond;
18925 },
18926 'bpmn:EventBasedGateway': function(parentGfx, element) {
18927
18928 var semantic = getSemantic(element);
18929
18930 var diamond = renderer('bpmn:Gateway')(parentGfx, element);
18931
18932 /* outer circle path */ drawCircle(parentGfx, element.width, element.height, element.height * 0.20, {
18933 strokeWidth: 1,
18934 fill: 'none',
18935 stroke: getStrokeColor$1(element, defaultStrokeColor)
18936 });
18937
18938 var type = semantic.eventGatewayType;
18939 var instantiate = !!semantic.instantiate;
18940
18941 function drawEvent() {
18942
18943 var pathData = pathMap.getScaledPath('GATEWAY_EVENT_BASED', {
18944 xScaleFactor: 0.18,
18945 yScaleFactor: 0.18,
18946 containerWidth: element.width,
18947 containerHeight: element.height,
18948 position: {
18949 mx: 0.36,
18950 my: 0.44
18951 }
18952 });
18953
18954 var attrs = {
18955 strokeWidth: 2,
18956 fill: getFillColor(element, 'none'),
18957 stroke: getStrokeColor$1(element, defaultStrokeColor)
18958 };
18959
18960 /* event path */ drawPath(parentGfx, pathData, attrs);
18961 }
18962
18963 if (type === 'Parallel') {
18964
18965 var pathData = pathMap.getScaledPath('GATEWAY_PARALLEL', {
18966 xScaleFactor: 0.4,
18967 yScaleFactor:0.4,
18968 containerWidth: element.width,
18969 containerHeight: element.height,
18970 position: {
18971 mx: 0.474,
18972 my: 0.296
18973 }
18974 });
18975
18976 var parallelPath = drawPath(parentGfx, pathData);
18977 attr$1(parallelPath, {
18978 strokeWidth: 1,
18979 fill: 'none'
18980 });
18981 } else if (type === 'Exclusive') {
18982
18983 if (!instantiate) {
18984 var innerCircle = drawCircle(parentGfx, element.width, element.height, element.height * 0.26);
18985 attr$1(innerCircle, {
18986 strokeWidth: 1,
18987 fill: 'none',
18988 stroke: getStrokeColor$1(element, defaultStrokeColor)
18989 });
18990 }
18991
18992 drawEvent();
18993 }
18994
18995
18996 return diamond;
18997 },
18998 'bpmn:Gateway': function(parentGfx, element) {
18999 var attrs = {
19000 fill: getFillColor(element, defaultFillColor),
19001 fillOpacity: DEFAULT_FILL_OPACITY,
19002 stroke: getStrokeColor$1(element, defaultStrokeColor)
19003 };
19004
19005 return drawDiamond(parentGfx, element.width, element.height, attrs);
19006 },
19007 'bpmn:SequenceFlow': function(parentGfx, element) {
19008 var pathData = createPathFromConnection(element);
19009
19010 var fill = getFillColor(element, defaultFillColor),
19011 stroke = getStrokeColor$1(element, defaultStrokeColor);
19012
19013 var attrs = {
19014 strokeLinejoin: 'round',
19015 markerEnd: marker('sequenceflow-end', fill, stroke),
19016 stroke: getStrokeColor$1(element, defaultStrokeColor)
19017 };
19018
19019 var path = drawPath(parentGfx, pathData, attrs);
19020
19021 var sequenceFlow = getSemantic(element);
19022
19023 var source;
19024
19025 if (element.source) {
19026 source = element.source.businessObject;
19027
19028 // conditional flow marker
19029 if (sequenceFlow.conditionExpression && source.$instanceOf('bpmn:Activity')) {
19030 attr$1(path, {
19031 markerStart: marker('conditional-flow-marker', fill, stroke)
19032 });
19033 }
19034
19035 // default marker
19036 if (source.default && (source.$instanceOf('bpmn:Gateway') || source.$instanceOf('bpmn:Activity')) &&
19037 source.default === sequenceFlow) {
19038 attr$1(path, {
19039 markerStart: marker('conditional-default-flow-marker', fill, stroke)
19040 });
19041 }
19042 }
19043
19044 return path;
19045 },
19046 'bpmn:Association': function(parentGfx, element, attrs) {
19047
19048 var semantic = getSemantic(element);
19049
19050 var fill = getFillColor(element, defaultFillColor),
19051 stroke = getStrokeColor$1(element, defaultStrokeColor);
19052
19053 attrs = assign({
19054 strokeDasharray: '0.5, 5',
19055 strokeLinecap: 'round',
19056 strokeLinejoin: 'round',
19057 stroke: getStrokeColor$1(element, defaultStrokeColor)
19058 }, attrs || {});
19059
19060 if (semantic.associationDirection === 'One' ||
19061 semantic.associationDirection === 'Both') {
19062 attrs.markerEnd = marker('association-end', fill, stroke);
19063 }
19064
19065 if (semantic.associationDirection === 'Both') {
19066 attrs.markerStart = marker('association-start', fill, stroke);
19067 }
19068
19069 return drawLine(parentGfx, element.waypoints, attrs);
19070 },
19071 'bpmn:DataInputAssociation': function(parentGfx, element) {
19072 var fill = getFillColor(element, defaultFillColor),
19073 stroke = getStrokeColor$1(element, defaultStrokeColor);
19074
19075 return renderer('bpmn:Association')(parentGfx, element, {
19076 markerEnd: marker('association-end', fill, stroke)
19077 });
19078 },
19079 'bpmn:DataOutputAssociation': function(parentGfx, element) {
19080 var fill = getFillColor(element, defaultFillColor),
19081 stroke = getStrokeColor$1(element, defaultStrokeColor);
19082
19083 return renderer('bpmn:Association')(parentGfx, element, {
19084 markerEnd: marker('association-end', fill, stroke)
19085 });
19086 },
19087 'bpmn:MessageFlow': function(parentGfx, element) {
19088
19089 var semantic = getSemantic(element),
19090 di = getDi(element);
19091
19092 var fill = getFillColor(element, defaultFillColor),
19093 stroke = getStrokeColor$1(element, defaultStrokeColor);
19094
19095 var pathData = createPathFromConnection(element);
19096
19097 var attrs = {
19098 markerEnd: marker('messageflow-end', fill, stroke),
19099 markerStart: marker('messageflow-start', fill, stroke),
19100 strokeDasharray: '10, 12',
19101 strokeLinecap: 'round',
19102 strokeLinejoin: 'round',
19103 strokeWidth: '1.5px',
19104 stroke: getStrokeColor$1(element, defaultStrokeColor)
19105 };
19106
19107 var path = drawPath(parentGfx, pathData, attrs);
19108
19109 if (semantic.messageRef) {
19110 var midPoint = path.getPointAtLength(path.getTotalLength() / 2);
19111
19112 var markerPathData = pathMap.getScaledPath('MESSAGE_FLOW_MARKER', {
19113 abspos: {
19114 x: midPoint.x,
19115 y: midPoint.y
19116 }
19117 });
19118
19119 var messageAttrs = { strokeWidth: 1 };
19120
19121 if (di.messageVisibleKind === 'initiating') {
19122 messageAttrs.fill = 'white';
19123 messageAttrs.stroke = 'black';
19124 } else {
19125 messageAttrs.fill = '#888';
19126 messageAttrs.stroke = 'white';
19127 }
19128
19129 var message = drawPath(parentGfx, markerPathData, messageAttrs);
19130
19131 var labelText = semantic.messageRef.name;
19132 var label = renderLabel(parentGfx, labelText, {
19133 align: 'center-top',
19134 fitBox: true,
19135 style: {
19136 fill: getStrokeColor$1(element, defaultLabelColor)
19137 }
19138 });
19139
19140 var messageBounds = message.getBBox(),
19141 labelBounds = label.getBBox();
19142
19143 var translateX = midPoint.x - labelBounds.width / 2,
19144 translateY = midPoint.y + messageBounds.height / 2 + ELEMENT_LABEL_DISTANCE$1;
19145
19146 transform(label, translateX, translateY, 0);
19147
19148 }
19149
19150 return path;
19151 },
19152 'bpmn:DataObject': function(parentGfx, element) {
19153 var pathData = pathMap.getScaledPath('DATA_OBJECT_PATH', {
19154 xScaleFactor: 1,
19155 yScaleFactor: 1,
19156 containerWidth: element.width,
19157 containerHeight: element.height,
19158 position: {
19159 mx: 0.474,
19160 my: 0.296
19161 }
19162 });
19163
19164 var elementObject = drawPath(parentGfx, pathData, {
19165 fill: getFillColor(element, defaultFillColor),
19166 fillOpacity: DEFAULT_FILL_OPACITY,
19167 stroke: getStrokeColor$1(element, defaultStrokeColor)
19168 });
19169
19170 var semantic = getSemantic(element);
19171
19172 if (isCollection(semantic)) {
19173 renderDataItemCollection(parentGfx, element);
19174 }
19175
19176 return elementObject;
19177 },
19178 'bpmn:DataObjectReference': as('bpmn:DataObject'),
19179 'bpmn:DataInput': function(parentGfx, element) {
19180
19181 var arrowPathData = pathMap.getRawPath('DATA_ARROW');
19182
19183 // page
19184 var elementObject = renderer('bpmn:DataObject')(parentGfx, element);
19185
19186 /* input arrow path */ drawPath(parentGfx, arrowPathData, { strokeWidth: 1 });
19187
19188 return elementObject;
19189 },
19190 'bpmn:DataOutput': function(parentGfx, element) {
19191 var arrowPathData = pathMap.getRawPath('DATA_ARROW');
19192
19193 // page
19194 var elementObject = renderer('bpmn:DataObject')(parentGfx, element);
19195
19196 /* output arrow path */ drawPath(parentGfx, arrowPathData, {
19197 strokeWidth: 1,
19198 fill: 'black'
19199 });
19200
19201 return elementObject;
19202 },
19203 'bpmn:DataStoreReference': function(parentGfx, element) {
19204 var DATA_STORE_PATH = pathMap.getScaledPath('DATA_STORE', {
19205 xScaleFactor: 1,
19206 yScaleFactor: 1,
19207 containerWidth: element.width,
19208 containerHeight: element.height,
19209 position: {
19210 mx: 0,
19211 my: 0.133
19212 }
19213 });
19214
19215 var elementStore = drawPath(parentGfx, DATA_STORE_PATH, {
19216 strokeWidth: 2,
19217 fill: getFillColor(element, defaultFillColor),
19218 fillOpacity: DEFAULT_FILL_OPACITY,
19219 stroke: getStrokeColor$1(element, defaultStrokeColor)
19220 });
19221
19222 return elementStore;
19223 },
19224 'bpmn:BoundaryEvent': function(parentGfx, element) {
19225
19226 var semantic = getSemantic(element),
19227 cancel = semantic.cancelActivity;
19228
19229 var attrs = {
19230 strokeWidth: 1,
19231 fill: getFillColor(element, defaultFillColor),
19232 stroke: getStrokeColor$1(element, defaultStrokeColor)
19233 };
19234
19235 if (!cancel) {
19236 attrs.strokeDasharray = '6';
19237 attrs.strokeLinecap = 'round';
19238 }
19239
19240 // apply fillOpacity
19241 var outerAttrs = assign({}, attrs, {
19242 fillOpacity: 1
19243 });
19244
19245 // apply no-fill
19246 var innerAttrs = assign({}, attrs, {
19247 fill: 'none'
19248 });
19249
19250 var outer = renderer('bpmn:Event')(parentGfx, element, outerAttrs);
19251
19252 /* inner path */ drawCircle(parentGfx, element.width, element.height, INNER_OUTER_DIST, innerAttrs);
19253
19254 renderEventContent(element, parentGfx);
19255
19256 return outer;
19257 },
19258 'bpmn:Group': function(parentGfx, element) {
19259
19260 var group = drawRect(parentGfx, element.width, element.height, TASK_BORDER_RADIUS, {
19261 stroke: getStrokeColor$1(element, defaultStrokeColor),
19262 strokeWidth: 1,
19263 strokeDasharray: '8,3,1,3',
19264 fill: 'none',
19265 pointerEvents: 'none'
19266 });
19267
19268 return group;
19269 },
19270 'label': function(parentGfx, element) {
19271 return renderExternalLabel(parentGfx, element);
19272 },
19273 'bpmn:TextAnnotation': function(parentGfx, element) {
19274 var style = {
19275 'fill': 'none',
19276 'stroke': 'none'
19277 };
19278
19279 var textElement = drawRect(parentGfx, element.width, element.height, 0, 0, style);
19280
19281 var textPathData = pathMap.getScaledPath('TEXT_ANNOTATION', {
19282 xScaleFactor: 1,
19283 yScaleFactor: 1,
19284 containerWidth: element.width,
19285 containerHeight: element.height,
19286 position: {
19287 mx: 0.0,
19288 my: 0.0
19289 }
19290 });
19291
19292 drawPath(parentGfx, textPathData, {
19293 stroke: getStrokeColor$1(element, defaultStrokeColor)
19294 });
19295
19296 var text = getSemantic(element).text || '';
19297 renderLabel(parentGfx, text, {
19298 box: element,
19299 align: 'left-top',
19300 padding: 5,
19301 style: {
19302 fill: getLabelColor(element, defaultLabelColor, defaultStrokeColor)
19303 }
19304 });
19305
19306 return textElement;
19307 },
19308 'ParticipantMultiplicityMarker': function(parentGfx, element) {
19309 var markerPath = pathMap.getScaledPath('MARKER_PARALLEL', {
19310 xScaleFactor: 1,
19311 yScaleFactor: 1,
19312 containerWidth: element.width,
19313 containerHeight: element.height,
19314 position: {
19315 mx: ((element.width / 2) / element.width),
19316 my: (element.height - 15) / element.height
19317 }
19318 });
19319
19320 drawMarker('participant-multiplicity', parentGfx, markerPath, {
19321 strokeWidth: 2,
19322 fill: getFillColor(element, defaultFillColor),
19323 stroke: getStrokeColor$1(element, defaultStrokeColor)
19324 });
19325 },
19326 'SubProcessMarker': function(parentGfx, element) {
19327 var markerRect = drawRect(parentGfx, 14, 14, 0, {
19328 strokeWidth: 1,
19329 fill: getFillColor(element, defaultFillColor),
19330 stroke: getStrokeColor$1(element, defaultStrokeColor)
19331 });
19332
19333 // Process marker is placed in the middle of the box
19334 // therefore fixed values can be used here
19335 translate$2(markerRect, element.width / 2 - 7.5, element.height - 20);
19336
19337 var markerPath = pathMap.getScaledPath('MARKER_SUB_PROCESS', {
19338 xScaleFactor: 1.5,
19339 yScaleFactor: 1.5,
19340 containerWidth: element.width,
19341 containerHeight: element.height,
19342 position: {
19343 mx: (element.width / 2 - 7.5) / element.width,
19344 my: (element.height - 20) / element.height
19345 }
19346 });
19347
19348 drawMarker('sub-process', parentGfx, markerPath, {
19349 fill: getFillColor(element, defaultFillColor),
19350 stroke: getStrokeColor$1(element, defaultStrokeColor)
19351 });
19352 },
19353 'ParallelMarker': function(parentGfx, element, position) {
19354 var markerPath = pathMap.getScaledPath('MARKER_PARALLEL', {
19355 xScaleFactor: 1,
19356 yScaleFactor: 1,
19357 containerWidth: element.width,
19358 containerHeight: element.height,
19359 position: {
19360 mx: ((element.width / 2 + position.parallel) / element.width),
19361 my: (element.height - 20) / element.height
19362 }
19363 });
19364
19365 drawMarker('parallel', parentGfx, markerPath, {
19366 fill: getFillColor(element, defaultFillColor),
19367 stroke: getStrokeColor$1(element, defaultStrokeColor)
19368 });
19369 },
19370 'SequentialMarker': function(parentGfx, element, position) {
19371 var markerPath = pathMap.getScaledPath('MARKER_SEQUENTIAL', {
19372 xScaleFactor: 1,
19373 yScaleFactor: 1,
19374 containerWidth: element.width,
19375 containerHeight: element.height,
19376 position: {
19377 mx: ((element.width / 2 + position.seq) / element.width),
19378 my: (element.height - 19) / element.height
19379 }
19380 });
19381
19382 drawMarker('sequential', parentGfx, markerPath, {
19383 fill: getFillColor(element, defaultFillColor),
19384 stroke: getStrokeColor$1(element, defaultStrokeColor)
19385 });
19386 },
19387 'CompensationMarker': function(parentGfx, element, position) {
19388 var markerMath = pathMap.getScaledPath('MARKER_COMPENSATION', {
19389 xScaleFactor: 1,
19390 yScaleFactor: 1,
19391 containerWidth: element.width,
19392 containerHeight: element.height,
19393 position: {
19394 mx: ((element.width / 2 + position.compensation) / element.width),
19395 my: (element.height - 13) / element.height
19396 }
19397 });
19398
19399 drawMarker('compensation', parentGfx, markerMath, {
19400 strokeWidth: 1,
19401 fill: getFillColor(element, defaultFillColor),
19402 stroke: getStrokeColor$1(element, defaultStrokeColor)
19403 });
19404 },
19405 'LoopMarker': function(parentGfx, element, position) {
19406 var markerPath = pathMap.getScaledPath('MARKER_LOOP', {
19407 xScaleFactor: 1,
19408 yScaleFactor: 1,
19409 containerWidth: element.width,
19410 containerHeight: element.height,
19411 position: {
19412 mx: ((element.width / 2 + position.loop) / element.width),
19413 my: (element.height - 7) / element.height
19414 }
19415 });
19416
19417 drawMarker('loop', parentGfx, markerPath, {
19418 strokeWidth: 1,
19419 fill: getFillColor(element, defaultFillColor),
19420 stroke: getStrokeColor$1(element, defaultStrokeColor),
19421 strokeLinecap: 'round',
19422 strokeMiterlimit: 0.5
19423 });
19424 },
19425 'AdhocMarker': function(parentGfx, element, position) {
19426 var markerPath = pathMap.getScaledPath('MARKER_ADHOC', {
19427 xScaleFactor: 1,
19428 yScaleFactor: 1,
19429 containerWidth: element.width,
19430 containerHeight: element.height,
19431 position: {
19432 mx: ((element.width / 2 + position.adhoc) / element.width),
19433 my: (element.height - 15) / element.height
19434 }
19435 });
19436
19437 drawMarker('adhoc', parentGfx, markerPath, {
19438 strokeWidth: 1,
19439 fill: getStrokeColor$1(element, defaultStrokeColor),
19440 stroke: getStrokeColor$1(element, defaultStrokeColor)
19441 });
19442 }
19443 };
19444
19445 function attachTaskMarkers(parentGfx, element, taskMarkers) {
19446 var obj = getSemantic(element);
19447
19448 var subprocess = taskMarkers && taskMarkers.indexOf('SubProcessMarker') !== -1;
19449 var position;
19450
19451 if (subprocess) {
19452 position = {
19453 seq: -21,
19454 parallel: -22,
19455 compensation: -42,
19456 loop: -18,
19457 adhoc: 10
19458 };
19459 } else {
19460 position = {
19461 seq: -3,
19462 parallel: -6,
19463 compensation: -27,
19464 loop: 0,
19465 adhoc: 10
19466 };
19467 }
19468
19469 forEach$2(taskMarkers, function(marker) {
19470 renderer(marker)(parentGfx, element, position);
19471 });
19472
19473 if (obj.isForCompensation) {
19474 renderer('CompensationMarker')(parentGfx, element, position);
19475 }
19476
19477 if (obj.$type === 'bpmn:AdHocSubProcess') {
19478 renderer('AdhocMarker')(parentGfx, element, position);
19479 }
19480
19481 var loopCharacteristics = obj.loopCharacteristics,
19482 isSequential = loopCharacteristics && loopCharacteristics.isSequential;
19483
19484 if (loopCharacteristics) {
19485
19486 if (isSequential === undefined) {
19487 renderer('LoopMarker')(parentGfx, element, position);
19488 }
19489
19490 if (isSequential === false) {
19491 renderer('ParallelMarker')(parentGfx, element, position);
19492 }
19493
19494 if (isSequential === true) {
19495 renderer('SequentialMarker')(parentGfx, element, position);
19496 }
19497 }
19498 }
19499
19500 function renderDataItemCollection(parentGfx, element) {
19501
19502 var yPosition = (element.height - 18) / element.height;
19503
19504 var pathData = pathMap.getScaledPath('DATA_OBJECT_COLLECTION_PATH', {
19505 xScaleFactor: 1,
19506 yScaleFactor: 1,
19507 containerWidth: element.width,
19508 containerHeight: element.height,
19509 position: {
19510 mx: 0.33,
19511 my: yPosition
19512 }
19513 });
19514
19515 /* collection path */ drawPath(parentGfx, pathData, {
19516 strokeWidth: 2
19517 });
19518 }
19519
19520
19521 // extension API, use at your own risk
19522 this._drawPath = drawPath;
19523
19524 this._renderer = renderer;
19525 }
19526
19527
19528 inherits$1(BpmnRenderer, BaseRenderer);
19529
19530 BpmnRenderer.$inject = [
19531 'config.bpmnRenderer',
19532 'eventBus',
19533 'styles',
19534 'pathMap',
19535 'canvas',
19536 'textRenderer'
19537 ];
19538
19539
19540 BpmnRenderer.prototype.canRender = function(element) {
19541 return is$1(element, 'bpmn:BaseElement');
19542 };
19543
19544 BpmnRenderer.prototype.drawShape = function(parentGfx, element) {
19545 var type = element.type;
19546 var h = this._renderer(type);
19547
19548 /* jshint -W040 */
19549 return h(parentGfx, element);
19550 };
19551
19552 BpmnRenderer.prototype.drawConnection = function(parentGfx, element) {
19553 var type = element.type;
19554 var h = this._renderer(type);
19555
19556 /* jshint -W040 */
19557 return h(parentGfx, element);
19558 };
19559
19560 BpmnRenderer.prototype.getShapePath = function(element) {
19561
19562 if (is$1(element, 'bpmn:Event')) {
19563 return getCirclePath(element);
19564 }
19565
19566 if (is$1(element, 'bpmn:Activity')) {
19567 return getRoundRectPath(element, TASK_BORDER_RADIUS);
19568 }
19569
19570 if (is$1(element, 'bpmn:Gateway')) {
19571 return getDiamondPath(element);
19572 }
19573
19574 return getRectPath(element);
19575 };
19576
19577 var DEFAULT_BOX_PADDING = 0;
19578
19579 var DEFAULT_LABEL_SIZE$1 = {
19580 width: 150,
19581 height: 50
19582 };
19583
19584
19585 function parseAlign(align) {
19586
19587 var parts = align.split('-');
19588
19589 return {
19590 horizontal: parts[0] || 'center',
19591 vertical: parts[1] || 'top'
19592 };
19593 }
19594
19595 function parsePadding(padding) {
19596
19597 if (isObject(padding)) {
19598 return assign({ top: 0, left: 0, right: 0, bottom: 0 }, padding);
19599 } else {
19600 return {
19601 top: padding,
19602 left: padding,
19603 right: padding,
19604 bottom: padding
19605 };
19606 }
19607 }
19608
19609 function getTextBBox(text, fakeText) {
19610
19611 fakeText.textContent = text;
19612
19613 var textBBox;
19614
19615 try {
19616 var bbox,
19617 emptyLine = text === '';
19618
19619 // add dummy text, when line is empty to
19620 // determine correct height
19621 fakeText.textContent = emptyLine ? 'dummy' : text;
19622
19623 textBBox = fakeText.getBBox();
19624
19625 // take text rendering related horizontal
19626 // padding into account
19627 bbox = {
19628 width: textBBox.width + textBBox.x * 2,
19629 height: textBBox.height
19630 };
19631
19632 if (emptyLine) {
19633
19634 // correct width
19635 bbox.width = 0;
19636 }
19637
19638 return bbox;
19639 } catch (e) {
19640 return { width: 0, height: 0 };
19641 }
19642 }
19643
19644
19645 /**
19646 * Layout the next line and return the layouted element.
19647 *
19648 * Alters the lines passed.
19649 *
19650 * @param {Array<string>} lines
19651 * @return {Object} the line descriptor, an object { width, height, text }
19652 */
19653 function layoutNext(lines, maxWidth, fakeText) {
19654
19655 var originalLine = lines.shift(),
19656 fitLine = originalLine;
19657
19658 var textBBox;
19659
19660 for (;;) {
19661 textBBox = getTextBBox(fitLine, fakeText);
19662
19663 textBBox.width = fitLine ? textBBox.width : 0;
19664
19665 // try to fit
19666 if (fitLine === ' ' || fitLine === '' || textBBox.width < Math.round(maxWidth) || fitLine.length < 2) {
19667 return fit(lines, fitLine, originalLine, textBBox);
19668 }
19669
19670 fitLine = shortenLine(fitLine, textBBox.width, maxWidth);
19671 }
19672 }
19673
19674 function fit(lines, fitLine, originalLine, textBBox) {
19675 if (fitLine.length < originalLine.length) {
19676 var remainder = originalLine.slice(fitLine.length).trim();
19677
19678 lines.unshift(remainder);
19679 }
19680
19681 return {
19682 width: textBBox.width,
19683 height: textBBox.height,
19684 text: fitLine
19685 };
19686 }
19687
19688 var SOFT_BREAK = '\u00AD';
19689
19690
19691 /**
19692 * Shortens a line based on spacing and hyphens.
19693 * Returns the shortened result on success.
19694 *
19695 * @param {string} line
19696 * @param {number} maxLength the maximum characters of the string
19697 * @return {string} the shortened string
19698 */
19699 function semanticShorten(line, maxLength) {
19700
19701 var parts = line.split(/(\s|-|\u00AD)/g),
19702 part,
19703 shortenedParts = [],
19704 length = 0;
19705
19706 // try to shorten via break chars
19707 if (parts.length > 1) {
19708
19709 while ((part = parts.shift())) {
19710 if (part.length + length < maxLength) {
19711 shortenedParts.push(part);
19712 length += part.length;
19713 } else {
19714
19715 // remove previous part, too if hyphen does not fit anymore
19716 if (part === '-' || part === SOFT_BREAK) {
19717 shortenedParts.pop();
19718 }
19719
19720 break;
19721 }
19722 }
19723 }
19724
19725 var last = shortenedParts[shortenedParts.length - 1];
19726
19727 // translate trailing soft break to actual hyphen
19728 if (last && last === SOFT_BREAK) {
19729 shortenedParts[shortenedParts.length - 1] = '-';
19730 }
19731
19732 return shortenedParts.join('');
19733 }
19734
19735
19736 function shortenLine(line, width, maxWidth) {
19737 var length = Math.max(line.length * (maxWidth / width), 1);
19738
19739 // try to shorten semantically (i.e. based on spaces and hyphens)
19740 var shortenedLine = semanticShorten(line, length);
19741
19742 if (!shortenedLine) {
19743
19744 // force shorten by cutting the long word
19745 shortenedLine = line.slice(0, Math.max(Math.round(length - 1), 1));
19746 }
19747
19748 return shortenedLine;
19749 }
19750
19751
19752 function getHelperSvg() {
19753 var helperSvg = document.getElementById('helper-svg');
19754
19755 if (!helperSvg) {
19756 helperSvg = create$1('svg');
19757
19758 attr$1(helperSvg, {
19759 id: 'helper-svg',
19760 width: 0,
19761 height: 0,
19762 visibility: 'hidden',
19763 position: 'fixed'
19764 });
19765
19766 document.body.appendChild(helperSvg);
19767 }
19768
19769 return helperSvg;
19770 }
19771
19772
19773 /**
19774 * Creates a new label utility
19775 *
19776 * @param {Object} config
19777 * @param {Dimensions} config.size
19778 * @param {number} config.padding
19779 * @param {Object} config.style
19780 * @param {string} config.align
19781 */
19782 function Text(config) {
19783
19784 this._config = assign({}, {
19785 size: DEFAULT_LABEL_SIZE$1,
19786 padding: DEFAULT_BOX_PADDING,
19787 style: {},
19788 align: 'center-top'
19789 }, config || {});
19790 }
19791
19792 /**
19793 * Returns the layouted text as an SVG element.
19794 *
19795 * @param {string} text
19796 * @param {Object} options
19797 *
19798 * @return {SVGElement}
19799 */
19800 Text.prototype.createText = function(text, options) {
19801 return this.layoutText(text, options).element;
19802 };
19803
19804 /**
19805 * Returns a labels layouted dimensions.
19806 *
19807 * @param {string} text to layout
19808 * @param {Object} options
19809 *
19810 * @return {Dimensions}
19811 */
19812 Text.prototype.getDimensions = function(text, options) {
19813 return this.layoutText(text, options).dimensions;
19814 };
19815
19816 /**
19817 * Creates and returns a label and its bounding box.
19818 *
19819 * @method Text#createText
19820 *
19821 * @param {string} text the text to render on the label
19822 * @param {Object} options
19823 * @param {string} options.align how to align in the bounding box.
19824 * Any of { 'center-middle', 'center-top' },
19825 * defaults to 'center-top'.
19826 * @param {string} options.style style to be applied to the text
19827 * @param {boolean} options.fitBox indicates if box will be recalculated to
19828 * fit text
19829 *
19830 * @return {Object} { element, dimensions }
19831 */
19832 Text.prototype.layoutText = function(text, options) {
19833 var box = assign({}, this._config.size, options.box),
19834 style = assign({}, this._config.style, options.style),
19835 align = parseAlign(options.align || this._config.align),
19836 padding = parsePadding(options.padding !== undefined ? options.padding : this._config.padding),
19837 fitBox = options.fitBox || false;
19838
19839 var lineHeight = getLineHeight(style);
19840
19841 // we split text by lines and normalize
19842 // {soft break} + {line break} => { line break }
19843 var lines = text.split(/\u00AD?\r?\n/),
19844 layouted = [];
19845
19846 var maxWidth = box.width - padding.left - padding.right;
19847
19848 // ensure correct rendering by attaching helper text node to invisible SVG
19849 var helperText = create$1('text');
19850 attr$1(helperText, { x: 0, y: 0 });
19851 attr$1(helperText, style);
19852
19853 var helperSvg = getHelperSvg();
19854
19855 append(helperSvg, helperText);
19856
19857 while (lines.length) {
19858 layouted.push(layoutNext(lines, maxWidth, helperText));
19859 }
19860
19861 if (align.vertical === 'middle') {
19862 padding.top = padding.bottom = 0;
19863 }
19864
19865 var totalHeight = reduce(layouted, function(sum, line, idx) {
19866 return sum + (lineHeight || line.height);
19867 }, 0) + padding.top + padding.bottom;
19868
19869 var maxLineWidth = reduce(layouted, function(sum, line, idx) {
19870 return line.width > sum ? line.width : sum;
19871 }, 0);
19872
19873 // the y position of the next line
19874 var y = padding.top;
19875
19876 if (align.vertical === 'middle') {
19877 y += (box.height - totalHeight) / 2;
19878 }
19879
19880 // magic number initial offset
19881 y -= (lineHeight || layouted[0].height) / 4;
19882
19883
19884 var textElement = create$1('text');
19885
19886 attr$1(textElement, style);
19887
19888 // layout each line taking into account that parent
19889 // shape might resize to fit text size
19890 forEach$2(layouted, function(line) {
19891
19892 var x;
19893
19894 y += (lineHeight || line.height);
19895
19896 switch (align.horizontal) {
19897 case 'left':
19898 x = padding.left;
19899 break;
19900
19901 case 'right':
19902 x = ((fitBox ? maxLineWidth : maxWidth)
19903 - padding.right - line.width);
19904 break;
19905
19906 default:
19907
19908 // aka center
19909 x = Math.max((((fitBox ? maxLineWidth : maxWidth)
19910 - line.width) / 2 + padding.left), 0);
19911 }
19912
19913 var tspan = create$1('tspan');
19914 attr$1(tspan, { x: x, y: y });
19915
19916 tspan.textContent = line.text;
19917
19918 append(textElement, tspan);
19919 });
19920
19921 remove$2(helperText);
19922
19923 var dimensions = {
19924 width: maxLineWidth,
19925 height: totalHeight
19926 };
19927
19928 return {
19929 dimensions: dimensions,
19930 element: textElement
19931 };
19932 };
19933
19934
19935 function getLineHeight(style) {
19936 if ('fontSize' in style && 'lineHeight' in style) {
19937 return style.lineHeight * parseInt(style.fontSize, 10);
19938 }
19939 }
19940
19941 var DEFAULT_FONT_SIZE = 12;
19942 var LINE_HEIGHT_RATIO = 1.2;
19943
19944 var MIN_TEXT_ANNOTATION_HEIGHT = 30;
19945
19946
19947 function TextRenderer(config) {
19948
19949 var defaultStyle = assign({
19950 fontFamily: 'Arial, sans-serif',
19951 fontSize: DEFAULT_FONT_SIZE,
19952 fontWeight: 'normal',
19953 lineHeight: LINE_HEIGHT_RATIO
19954 }, config && config.defaultStyle || {});
19955
19956 var fontSize = parseInt(defaultStyle.fontSize, 10) - 1;
19957
19958 var externalStyle = assign({}, defaultStyle, {
19959 fontSize: fontSize
19960 }, config && config.externalStyle || {});
19961
19962 var textUtil = new Text({
19963 style: defaultStyle
19964 });
19965
19966 /**
19967 * Get the new bounds of an externally rendered,
19968 * layouted label.
19969 *
19970 * @param {Bounds} bounds
19971 * @param {string} text
19972 *
19973 * @return {Bounds}
19974 */
19975 this.getExternalLabelBounds = function(bounds, text) {
19976
19977 var layoutedDimensions = textUtil.getDimensions(text, {
19978 box: {
19979 width: 90,
19980 height: 30,
19981 x: bounds.width / 2 + bounds.x,
19982 y: bounds.height / 2 + bounds.y
19983 },
19984 style: externalStyle
19985 });
19986
19987 // resize label shape to fit label text
19988 return {
19989 x: Math.round(bounds.x + bounds.width / 2 - layoutedDimensions.width / 2),
19990 y: Math.round(bounds.y),
19991 width: Math.ceil(layoutedDimensions.width),
19992 height: Math.ceil(layoutedDimensions.height)
19993 };
19994
19995 };
19996
19997 /**
19998 * Get the new bounds of text annotation.
19999 *
20000 * @param {Bounds} bounds
20001 * @param {string} text
20002 *
20003 * @return {Bounds}
20004 */
20005 this.getTextAnnotationBounds = function(bounds, text) {
20006
20007 var layoutedDimensions = textUtil.getDimensions(text, {
20008 box: bounds,
20009 style: defaultStyle,
20010 align: 'left-top',
20011 padding: 5
20012 });
20013
20014 return {
20015 x: bounds.x,
20016 y: bounds.y,
20017 width: bounds.width,
20018 height: Math.max(MIN_TEXT_ANNOTATION_HEIGHT, Math.round(layoutedDimensions.height))
20019 };
20020 };
20021
20022 /**
20023 * Create a layouted text element.
20024 *
20025 * @param {string} text
20026 * @param {Object} [options]
20027 *
20028 * @return {SVGElement} rendered text
20029 */
20030 this.createText = function(text, options) {
20031 return textUtil.createText(text, options || {});
20032 };
20033
20034 /**
20035 * Get default text style.
20036 */
20037 this.getDefaultStyle = function() {
20038 return defaultStyle;
20039 };
20040
20041 /**
20042 * Get the external text style.
20043 */
20044 this.getExternalStyle = function() {
20045 return externalStyle;
20046 };
20047
20048 }
20049
20050 TextRenderer.$inject = [
20051 'config.textRenderer'
20052 ];
20053
20054 /**
20055 * Map containing SVG paths needed by BpmnRenderer.
20056 */
20057
20058 function PathMap() {
20059
20060 /**
20061 * Contains a map of path elements
20062 *
20063 * <h1>Path definition</h1>
20064 * A parameterized path is defined like this:
20065 * <pre>
20066 * 'GATEWAY_PARALLEL': {
20067 * d: 'm {mx},{my} {e.x0},0 0,{e.x1} {e.x1},0 0,{e.y0} -{e.x1},0 0,{e.y1} ' +
20068 '-{e.x0},0 0,-{e.y1} -{e.x1},0 0,-{e.y0} {e.x1},0 z',
20069 * height: 17.5,
20070 * width: 17.5,
20071 * heightElements: [2.5, 7.5],
20072 * widthElements: [2.5, 7.5]
20073 * }
20074 * </pre>
20075 * <p>It's important to specify a correct <b>height and width</b> for the path as the scaling
20076 * is based on the ratio between the specified height and width in this object and the
20077 * height and width that is set as scale target (Note x,y coordinates will be scaled with
20078 * individual ratios).</p>
20079 * <p>The '<b>heightElements</b>' and '<b>widthElements</b>' array must contain the values that will be scaled.
20080 * The scaling is based on the computed ratios.
20081 * Coordinates on the y axis should be in the <b>heightElement</b>'s array, they will be scaled using
20082 * the computed ratio coefficient.
20083 * In the parameterized path the scaled values can be accessed through the 'e' object in {} brackets.
20084 * <ul>
20085 * <li>The values for the y axis can be accessed in the path string using {e.y0}, {e.y1}, ....</li>
20086 * <li>The values for the x axis can be accessed in the path string using {e.x0}, {e.x1}, ....</li>
20087 * </ul>
20088 * The numbers x0, x1 respectively y0, y1, ... map to the corresponding array index.
20089 * </p>
20090 */
20091 this.pathMap = {
20092 'EVENT_MESSAGE': {
20093 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}',
20094 height: 36,
20095 width: 36,
20096 heightElements: [6, 14],
20097 widthElements: [10.5, 21]
20098 },
20099 'EVENT_SIGNAL': {
20100 d: 'M {mx},{my} l {e.x0},{e.y0} l -{e.x1},0 Z',
20101 height: 36,
20102 width: 36,
20103 heightElements: [18],
20104 widthElements: [10, 20]
20105 },
20106 'EVENT_ESCALATION': {
20107 d: 'M {mx},{my} l {e.x0},{e.y0} l -{e.x0},-{e.y1} l -{e.x0},{e.y1} Z',
20108 height: 36,
20109 width: 36,
20110 heightElements: [20, 7],
20111 widthElements: [8]
20112 },
20113 'EVENT_CONDITIONAL': {
20114 d: 'M {e.x0},{e.y0} l {e.x1},0 l 0,{e.y2} l -{e.x1},0 Z ' +
20115 'M {e.x2},{e.y3} l {e.x0},0 ' +
20116 'M {e.x2},{e.y4} l {e.x0},0 ' +
20117 'M {e.x2},{e.y5} l {e.x0},0 ' +
20118 'M {e.x2},{e.y6} l {e.x0},0 ' +
20119 'M {e.x2},{e.y7} l {e.x0},0 ' +
20120 'M {e.x2},{e.y8} l {e.x0},0 ',
20121 height: 36,
20122 width: 36,
20123 heightElements: [8.5, 14.5, 18, 11.5, 14.5, 17.5, 20.5, 23.5, 26.5],
20124 widthElements: [10.5, 14.5, 12.5]
20125 },
20126 'EVENT_LINK': {
20127 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',
20128 height: 36,
20129 width: 36,
20130 heightElements: [4.4375, 6.75, 7.8125],
20131 widthElements: [9.84375, 13.5]
20132 },
20133 'EVENT_ERROR': {
20134 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',
20135 height: 36,
20136 width: 36,
20137 heightElements: [0.023, 8.737, 8.151, 16.564, 10.591, 8.714],
20138 widthElements: [0.085, 6.672, 6.97, 4.273, 5.337, 6.636]
20139 },
20140 'EVENT_CANCEL_45': {
20141 d: 'm {mx},{my} -{e.x1},0 0,{e.x0} {e.x1},0 0,{e.y1} {e.x0},0 ' +
20142 '0,-{e.y1} {e.x1},0 0,-{e.y0} -{e.x1},0 0,-{e.y1} -{e.x0},0 z',
20143 height: 36,
20144 width: 36,
20145 heightElements: [4.75, 8.5],
20146 widthElements: [4.75, 8.5]
20147 },
20148 'EVENT_COMPENSATION': {
20149 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',
20150 height: 36,
20151 width: 36,
20152 heightElements: [6.5, 13, 0.4, 6.1],
20153 widthElements: [9, 9.3, 8.7]
20154 },
20155 'EVENT_TIMER_WH': {
20156 d: 'M {mx},{my} l {e.x0},-{e.y0} m -{e.x0},{e.y0} l {e.x1},{e.y1} ',
20157 height: 36,
20158 width: 36,
20159 heightElements: [10, 2],
20160 widthElements: [3, 7]
20161 },
20162 'EVENT_TIMER_LINE': {
20163 d: 'M {mx},{my} ' +
20164 'm {e.x0},{e.y0} l -{e.x1},{e.y1} ',
20165 height: 36,
20166 width: 36,
20167 heightElements: [10, 3],
20168 widthElements: [0, 0]
20169 },
20170 'EVENT_MULTIPLE': {
20171 d:'m {mx},{my} {e.x1},-{e.y0} {e.x1},{e.y0} -{e.x0},{e.y1} -{e.x2},0 z',
20172 height: 36,
20173 width: 36,
20174 heightElements: [6.28099, 12.56199],
20175 widthElements: [3.1405, 9.42149, 12.56198]
20176 },
20177 'EVENT_PARALLEL_MULTIPLE': {
20178 d:'m {mx},{my} {e.x0},0 0,{e.y1} {e.x1},0 0,{e.y0} -{e.x1},0 0,{e.y1} ' +
20179 '-{e.x0},0 0,-{e.y1} -{e.x1},0 0,-{e.y0} {e.x1},0 z',
20180 height: 36,
20181 width: 36,
20182 heightElements: [2.56228, 7.68683],
20183 widthElements: [2.56228, 7.68683]
20184 },
20185 'GATEWAY_EXCLUSIVE': {
20186 d:'m {mx},{my} {e.x0},{e.y0} {e.x1},{e.y0} {e.x2},0 {e.x4},{e.y2} ' +
20187 '{e.x4},{e.y1} {e.x2},0 {e.x1},{e.y3} {e.x0},{e.y3} ' +
20188 '{e.x3},0 {e.x5},{e.y1} {e.x5},{e.y2} {e.x3},0 z',
20189 height: 17.5,
20190 width: 17.5,
20191 heightElements: [8.5, 6.5312, -6.5312, -8.5],
20192 widthElements: [6.5, -6.5, 3, -3, 5, -5]
20193 },
20194 'GATEWAY_PARALLEL': {
20195 d:'m {mx},{my} 0,{e.y1} -{e.x1},0 0,{e.y0} {e.x1},0 0,{e.y1} {e.x0},0 ' +
20196 '0,-{e.y1} {e.x1},0 0,-{e.y0} -{e.x1},0 0,-{e.y1} -{e.x0},0 z',
20197 height: 30,
20198 width: 30,
20199 heightElements: [5, 12.5],
20200 widthElements: [5, 12.5]
20201 },
20202 'GATEWAY_EVENT_BASED': {
20203 d:'m {mx},{my} {e.x0},{e.y0} {e.x0},{e.y1} {e.x1},{e.y2} {e.x2},0 z',
20204 height: 11,
20205 width: 11,
20206 heightElements: [-6, 6, 12, -12],
20207 widthElements: [9, -3, -12]
20208 },
20209 'GATEWAY_COMPLEX': {
20210 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} ' +
20211 '{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} ' +
20212 '{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} ' +
20213 '-{e.x0},{e.y1} 0,-{e.y0} -{e.x3},0 z',
20214 height: 17.125,
20215 width: 17.125,
20216 heightElements: [4.875, 3.4375, 2.125, 3],
20217 widthElements: [3.4375, 2.125, 4.875, 3]
20218 },
20219 'DATA_OBJECT_PATH': {
20220 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',
20221 height: 61,
20222 width: 51,
20223 heightElements: [10, 50, 60],
20224 widthElements: [10, 40, 50, 60]
20225 },
20226 'DATA_OBJECT_COLLECTION_PATH': {
20227 d: 'm{mx},{my} m 3,2 l 0,10 m 3,-10 l 0,10 m 3,-10 l 0,10',
20228 height: 10,
20229 width: 10,
20230 heightElements: [],
20231 widthElements: []
20232 },
20233 'DATA_ARROW': {
20234 d:'m 5,9 9,0 0,-3 5,5 -5,5 0,-3 -9,0 z',
20235 height: 61,
20236 width: 51,
20237 heightElements: [],
20238 widthElements: []
20239 },
20240 'DATA_STORE': {
20241 d:'m {mx},{my} ' +
20242 'l 0,{e.y2} ' +
20243 'c {e.x0},{e.y1} {e.x1},{e.y1} {e.x2},0 ' +
20244 'l 0,-{e.y2} ' +
20245 'c -{e.x0},-{e.y1} -{e.x1},-{e.y1} -{e.x2},0' +
20246 'c {e.x0},{e.y1} {e.x1},{e.y1} {e.x2},0 ' +
20247 'm -{e.x2},{e.y0}' +
20248 'c {e.x0},{e.y1} {e.x1},{e.y1} {e.x2},0' +
20249 'm -{e.x2},{e.y0}' +
20250 'c {e.x0},{e.y1} {e.x1},{e.y1} {e.x2},0',
20251 height: 61,
20252 width: 61,
20253 heightElements: [7, 10, 45],
20254 widthElements: [2, 58, 60]
20255 },
20256 'TEXT_ANNOTATION': {
20257 d: 'm {mx}, {my} m 10,0 l -10,0 l 0,{e.y0} l 10,0',
20258 height: 30,
20259 width: 10,
20260 heightElements: [30],
20261 widthElements: [10]
20262 },
20263 'MARKER_SUB_PROCESS': {
20264 d: 'm{mx},{my} m 7,2 l 0,10 m -5,-5 l 10,0',
20265 height: 10,
20266 width: 10,
20267 heightElements: [],
20268 widthElements: []
20269 },
20270 'MARKER_PARALLEL': {
20271 d: 'm{mx},{my} m 3,2 l 0,10 m 3,-10 l 0,10 m 3,-10 l 0,10',
20272 height: 10,
20273 width: 10,
20274 heightElements: [],
20275 widthElements: []
20276 },
20277 'MARKER_SEQUENTIAL': {
20278 d: 'm{mx},{my} m 0,3 l 10,0 m -10,3 l 10,0 m -10,3 l 10,0',
20279 height: 10,
20280 width: 10,
20281 heightElements: [],
20282 widthElements: []
20283 },
20284 'MARKER_COMPENSATION': {
20285 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',
20286 height: 10,
20287 width: 21,
20288 heightElements: [],
20289 widthElements: []
20290 },
20291 'MARKER_LOOP': {
20292 d: 'm {mx},{my} c 3.526979,0 6.386161,-2.829858 6.386161,-6.320661 0,-3.490806 -2.859182,-6.320661 ' +
20293 '-6.386161,-6.320661 -3.526978,0 -6.38616,2.829855 -6.38616,6.320661 0,1.745402 ' +
20294 '0.714797,3.325567 1.870463,4.469381 0.577834,0.571908 1.265885,1.034728 2.029916,1.35457 ' +
20295 'l -0.718163,-3.909793 m 0.718163,3.909793 -3.885211,0.802902',
20296 height: 13.9,
20297 width: 13.7,
20298 heightElements: [],
20299 widthElements: []
20300 },
20301 'MARKER_ADHOC': {
20302 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 ' +
20303 '3.85579,1.15803 5.76082,1.79107 1.06385,0.34139996 2.24454,0.1438 3.18759,-0.43767 0.61743,-0.33642 ' +
20304 '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 ' +
20305 '-3.6918,1.66181996 -1.24459,0.0927 -2.46671,-0.2491 -3.59505,-0.74812 -1.35789,-0.55965 ' +
20306 '-2.75133,-1.33436996 -4.27027,-1.18121996 -1.37741,0.14601 -2.41842,1.13685996 -3.44288,1.96782996 z',
20307 height: 4,
20308 width: 15,
20309 heightElements: [],
20310 widthElements: []
20311 },
20312 'TASK_TYPE_SEND': {
20313 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}',
20314 height: 14,
20315 width: 21,
20316 heightElements: [6, 14],
20317 widthElements: [10.5, 21]
20318 },
20319 'TASK_TYPE_SCRIPT': {
20320 d: 'm {mx},{my} c 9.966553,-6.27276 -8.000926,-7.91932 2.968968,-14.938 l -8.802728,0 ' +
20321 'c -10.969894,7.01868 6.997585,8.66524 -2.968967,14.938 z ' +
20322 'm -7,-12 l 5,0 ' +
20323 'm -4.5,3 l 4.5,0 ' +
20324 'm -3,3 l 5,0' +
20325 'm -4,3 l 5,0',
20326 height: 15,
20327 width: 12.6,
20328 heightElements: [6, 14],
20329 widthElements: [10.5, 21]
20330 },
20331 'TASK_TYPE_USER_1': {
20332 d: 'm {mx},{my} c 0.909,-0.845 1.594,-2.049 1.594,-3.385 0,-2.554 -1.805,-4.62199999 ' +
20333 '-4.357,-4.62199999 -2.55199998,0 -4.28799998,2.06799999 -4.28799998,4.62199999 0,1.348 ' +
20334 '0.974,2.562 1.89599998,3.405 -0.52899998,0.187 -5.669,2.097 -5.794,4.7560005 v 6.718 ' +
20335 'h 17 v -6.718 c 0,-2.2980005 -5.5279996,-4.5950005 -6.0509996,-4.7760005 z' +
20336 'm -8,6 l 0,5.5 m 11,0 l 0,-5'
20337 },
20338 'TASK_TYPE_USER_2': {
20339 d: 'm {mx},{my} m 2.162,1.009 c 0,2.4470005 -2.158,4.4310005 -4.821,4.4310005 ' +
20340 '-2.66499998,0 -4.822,-1.981 -4.822,-4.4310005 '
20341 },
20342 'TASK_TYPE_USER_3': {
20343 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 ' +
20344 '4.124,0.965 -0.098,-0.57 -0.117,-3.79099999 -4.191,-4.13599999 -3.57499998,0.001 ' +
20345 '-4.20799998,3.36699999 -4.20699998,4.34799999 z'
20346 },
20347 'TASK_TYPE_MANUAL': {
20348 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 ' +
20349 '-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 ' +
20350 '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 ' +
20351 '-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 ' +
20352 '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 ' +
20353 '-10.86,-0.003 -11.0829995,-0.003 -0.022,-0.047 -0.045,-0.094 -0.069,-0.139 0.3939995,-0.319 ' +
20354 '2.0409995,-1.626 2.4149995,-2.017 0.469,-0.4870005 0.519,-1.1650005 0.162,-1.6040005 -0.414,-0.511 ' +
20355 '-0.973,-0.5 -1.48,-0.236 -1.4609995,0.764 -6.5999995,3.6430005 -7.7329995,4.2710005 -0.9,0.499 ' +
20356 '-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 ' +
20357 '-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 ' +
20358 '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 ' +
20359 '-5.824,-0.004 -6.04,-0.004 10e-4,-0.084 0.003,-0.586 10e-4,-0.67 z'
20360 },
20361 'TASK_TYPE_INSTANTIATING_SEND': {
20362 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'
20363 },
20364 'TASK_TYPE_SERVICE': {
20365 d: 'm {mx},{my} v -1.71335 c 0.352326,-0.0705 0.703932,-0.17838 1.047628,-0.32133 ' +
20366 '0.344416,-0.14465 0.665822,-0.32133 0.966377,-0.52145 l 1.19431,1.18005 1.567487,-1.57688 ' +
20367 '-1.195028,-1.18014 c 0.403376,-0.61394 0.683079,-1.29908 0.825447,-2.01824 l 1.622133,-0.01 ' +
20368 'v -2.2196 l -1.636514,0.01 c -0.07333,-0.35153 -0.178319,-0.70024 -0.323564,-1.04372 ' +
20369 '-0.145244,-0.34406 -0.321407,-0.6644 -0.522735,-0.96217 l 1.131035,-1.13631 -1.583305,-1.56293 ' +
20370 '-1.129598,1.13589 c -0.614052,-0.40108 -1.302883,-0.68093 -2.022633,-0.82247 l 0.0093,-1.61852 ' +
20371 '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 ' +
20372 '-0.665102,0.32092 -0.9635006,0.52046 l -1.1698628,-1.15823 -1.5667691,1.5792 1.1684265,1.15669 ' +
20373 'c -0.4026573,0.61283 -0.68308,1.29797 -0.8247287,2.01713 l -1.6588041,0.003 v 2.22174 ' +
20374 'l 1.6724648,-0.006 c 0.073327,0.35077 0.1797598,0.70243 0.3242851,1.04472 0.1452428,0.34448 ' +
20375 '0.3214064,0.6644 0.5227339,0.96066 l -1.1993431,1.19723 1.5840256,1.56011 1.1964668,-1.19348 ' +
20376 'c 0.6140517,0.40346 1.3028827,0.68232 2.0233517,0.82331 l 7.19e-4,1.69892 h 2.226848 z ' +
20377 'm 0.221462,-3.9957 c -1.788948,0.7502 -3.8576,-0.0928 -4.6097055,-1.87438 -0.7521065,-1.78321 ' +
20378 '0.090598,-3.84627 1.8802645,-4.59604 1.78823,-0.74936 3.856881,0.0929 4.608987,1.87437 ' +
20379 '0.752106,1.78165 -0.0906,3.84612 -1.879546,4.59605 z'
20380 },
20381 'TASK_TYPE_SERVICE_FILL': {
20382 d: 'm {mx},{my} c -1.788948,0.7502 -3.8576,-0.0928 -4.6097055,-1.87438 -0.7521065,-1.78321 ' +
20383 '0.090598,-3.84627 1.8802645,-4.59604 1.78823,-0.74936 3.856881,0.0929 4.608987,1.87437 ' +
20384 '0.752106,1.78165 -0.0906,3.84612 -1.879546,4.59605 z'
20385 },
20386 'TASK_TYPE_BUSINESS_RULE_HEADER': {
20387 d: 'm {mx},{my} 0,4 20,0 0,-4 z'
20388 },
20389 'TASK_TYPE_BUSINESS_RULE_MAIN': {
20390 d: 'm {mx},{my} 0,12 20,0 0,-12 z' +
20391 'm 0,8 l 20,0 ' +
20392 'm -13,-4 l 0,8'
20393 },
20394 'MESSAGE_FLOW_MARKER': {
20395 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'
20396 }
20397 };
20398
20399 this.getRawPath = function getRawPath(pathId) {
20400 return this.pathMap[pathId].d;
20401 };
20402
20403 /**
20404 * Scales the path to the given height and width.
20405 * <h1>Use case</h1>
20406 * <p>Use case is to scale the content of elements (event, gateways) based
20407 * on the element bounding box's size.
20408 * </p>
20409 * <h1>Why not transform</h1>
20410 * <p>Scaling a path with transform() will also scale the stroke and IE does not support
20411 * the option 'non-scaling-stroke' to prevent this.
20412 * Also there are use cases where only some parts of a path should be
20413 * scaled.</p>
20414 *
20415 * @param {string} pathId The ID of the path.
20416 * @param {Object} param <p>
20417 * Example param object scales the path to 60% size of the container (data.width, data.height).
20418 * <pre>
20419 * {
20420 * xScaleFactor: 0.6,
20421 * yScaleFactor:0.6,
20422 * containerWidth: data.width,
20423 * containerHeight: data.height,
20424 * position: {
20425 * mx: 0.46,
20426 * my: 0.2,
20427 * }
20428 * }
20429 * </pre>
20430 * <ul>
20431 * <li>targetpathwidth = xScaleFactor * containerWidth</li>
20432 * <li>targetpathheight = yScaleFactor * containerHeight</li>
20433 * <li>Position is used to set the starting coordinate of the path. M is computed:
20434 * <ul>
20435 * <li>position.x * containerWidth</li>
20436 * <li>position.y * containerHeight</li>
20437 * </ul>
20438 * Center of the container <pre> position: {
20439 * mx: 0.5,
20440 * my: 0.5,
20441 * }</pre>
20442 * Upper left corner of the container
20443 * <pre> position: {
20444 * mx: 0.0,
20445 * my: 0.0,
20446 * }</pre>
20447 * </li>
20448 * </ul>
20449 * </p>
20450 *
20451 */
20452 this.getScaledPath = function getScaledPath(pathId, param) {
20453 var rawPath = this.pathMap[pathId];
20454
20455 // positioning
20456 // compute the start point of the path
20457 var mx, my;
20458
20459 if (param.abspos) {
20460 mx = param.abspos.x;
20461 my = param.abspos.y;
20462 } else {
20463 mx = param.containerWidth * param.position.mx;
20464 my = param.containerHeight * param.position.my;
20465 }
20466
20467 var coordinates = {}; // map for the scaled coordinates
20468 if (param.position) {
20469
20470 // path
20471 var heightRatio = (param.containerHeight / rawPath.height) * param.yScaleFactor;
20472 var widthRatio = (param.containerWidth / rawPath.width) * param.xScaleFactor;
20473
20474
20475 // Apply height ratio
20476 for (var heightIndex = 0; heightIndex < rawPath.heightElements.length; heightIndex++) {
20477 coordinates['y' + heightIndex] = rawPath.heightElements[heightIndex] * heightRatio;
20478 }
20479
20480 // Apply width ratio
20481 for (var widthIndex = 0; widthIndex < rawPath.widthElements.length; widthIndex++) {
20482 coordinates['x' + widthIndex] = rawPath.widthElements[widthIndex] * widthRatio;
20483 }
20484 }
20485
20486 // Apply value to raw path
20487 var path = format(
20488 rawPath.d, {
20489 mx: mx,
20490 my: my,
20491 e: coordinates
20492 }
20493 );
20494 return path;
20495 };
20496 }
20497
20498 // helpers //////////////////////
20499
20500 // copied and adjusted from https://github.com/adobe-webplatform/Snap.svg/blob/master/src/svg.js
20501 var tokenRegex = /\{([^{}]+)\}/g,
20502 objNotationRegex = /(?:(?:^|\.)(.+?)(?=\[|\.|$|\()|\[('|")(.+?)\2\])(\(\))?/g; // matches .xxxxx or ["xxxxx"] to run over object properties
20503
20504 function replacer(all, key, obj) {
20505 var res = obj;
20506 key.replace(objNotationRegex, function(all, name, quote, quotedName, isFunc) {
20507 name = name || quotedName;
20508 if (res) {
20509 if (name in res) {
20510 res = res[name];
20511 }
20512 typeof res == 'function' && isFunc && (res = res());
20513 }
20514 });
20515 res = (res == null || res == obj ? all : res) + '';
20516
20517 return res;
20518 }
20519
20520 function format(str, obj) {
20521 return String(str).replace(tokenRegex, function(all, key) {
20522 return replacer(all, key, obj);
20523 });
20524 }
20525
20526 var DrawModule = {
20527 __init__: [ 'bpmnRenderer' ],
20528 bpmnRenderer: [ 'type', BpmnRenderer ],
20529 textRenderer: [ 'type', TextRenderer ],
20530 pathMap: [ 'type', PathMap ]
20531 };
20532
20533 /**
20534 * A simple translation stub to be used for multi-language support
20535 * in diagrams. Can be easily replaced with a more sophisticated
20536 * solution.
20537 *
20538 * @example
20539 *
20540 * // use it inside any diagram component by injecting `translate`.
20541 *
20542 * function MyService(translate) {
20543 * alert(translate('HELLO {you}', { you: 'You!' }));
20544 * }
20545 *
20546 * @param {string} template to interpolate
20547 * @param {Object} [replacements] a map with substitutes
20548 *
20549 * @return {string} the translated string
20550 */
20551 function translate$1(template, replacements) {
20552
20553 replacements = replacements || {};
20554
20555 return template.replace(/{([^}]+)}/g, function(_, key) {
20556 return replacements[key] || '{' + key + '}';
20557 });
20558 }
20559
20560 var translate = {
20561 translate: [ 'value', translate$1 ]
20562 };
20563
20564 var DEFAULT_LABEL_SIZE = {
20565 width: 90,
20566 height: 20
20567 };
20568
20569 var FLOW_LABEL_INDENT = 15;
20570
20571
20572 /**
20573 * Returns true if the given semantic has an external label
20574 *
20575 * @param {BpmnElement} semantic
20576 * @return {boolean} true if has label
20577 */
20578 function isLabelExternal(semantic) {
20579 return is$1(semantic, 'bpmn:Event') ||
20580 is$1(semantic, 'bpmn:Gateway') ||
20581 is$1(semantic, 'bpmn:DataStoreReference') ||
20582 is$1(semantic, 'bpmn:DataObjectReference') ||
20583 is$1(semantic, 'bpmn:DataInput') ||
20584 is$1(semantic, 'bpmn:DataOutput') ||
20585 is$1(semantic, 'bpmn:SequenceFlow') ||
20586 is$1(semantic, 'bpmn:MessageFlow') ||
20587 is$1(semantic, 'bpmn:Group');
20588 }
20589
20590 /**
20591 * Returns true if the given element has an external label
20592 *
20593 * @param {djs.model.shape} element
20594 * @return {boolean} true if has label
20595 */
20596 function hasExternalLabel(element) {
20597 return isLabel$6(element.label);
20598 }
20599
20600 /**
20601 * Get the position for sequence flow labels
20602 *
20603 * @param {Array<Point>} waypoints
20604 * @return {Point} the label position
20605 */
20606 function getFlowLabelPosition(waypoints) {
20607
20608 // get the waypoints mid
20609 var mid = waypoints.length / 2 - 1;
20610
20611 var first = waypoints[Math.floor(mid)];
20612 var second = waypoints[Math.ceil(mid + 0.01)];
20613
20614 // get position
20615 var position = getWaypointsMid(waypoints);
20616
20617 // calculate angle
20618 var angle = Math.atan((second.y - first.y) / (second.x - first.x));
20619
20620 var x = position.x,
20621 y = position.y;
20622
20623 if (Math.abs(angle) < Math.PI / 2) {
20624 y -= FLOW_LABEL_INDENT;
20625 } else {
20626 x += FLOW_LABEL_INDENT;
20627 }
20628
20629 return { x: x, y: y };
20630 }
20631
20632
20633 /**
20634 * Get the middle of a number of waypoints
20635 *
20636 * @param {Array<Point>} waypoints
20637 * @return {Point} the mid point
20638 */
20639 function getWaypointsMid(waypoints) {
20640
20641 var mid = waypoints.length / 2 - 1;
20642
20643 var first = waypoints[Math.floor(mid)];
20644 var second = waypoints[Math.ceil(mid + 0.01)];
20645
20646 return {
20647 x: first.x + (second.x - first.x) / 2,
20648 y: first.y + (second.y - first.y) / 2
20649 };
20650 }
20651
20652
20653 function getExternalLabelMid(element) {
20654
20655 if (element.waypoints) {
20656 return getFlowLabelPosition(element.waypoints);
20657 } else if (is$1(element, 'bpmn:Group')) {
20658 return {
20659 x: element.x + element.width / 2,
20660 y: element.y + DEFAULT_LABEL_SIZE.height / 2
20661 };
20662 } else {
20663 return {
20664 x: element.x + element.width / 2,
20665 y: element.y + element.height + DEFAULT_LABEL_SIZE.height / 2
20666 };
20667 }
20668 }
20669
20670
20671 /**
20672 * Returns the bounds of an elements label, parsed from the elements DI or
20673 * generated from its bounds.
20674 *
20675 * @param {BpmndDi} di
20676 * @param {djs.model.Base} element
20677 */
20678 function getExternalLabelBounds(di, element) {
20679
20680 var mid,
20681 size,
20682 bounds,
20683 label = di.label;
20684
20685 if (label && label.bounds) {
20686 bounds = label.bounds;
20687
20688 size = {
20689 width: Math.max(DEFAULT_LABEL_SIZE.width, bounds.width),
20690 height: bounds.height
20691 };
20692
20693 mid = {
20694 x: bounds.x + bounds.width / 2,
20695 y: bounds.y + bounds.height / 2
20696 };
20697 } else {
20698
20699 mid = getExternalLabelMid(element);
20700
20701 size = DEFAULT_LABEL_SIZE;
20702 }
20703
20704 return assign({
20705 x: mid.x - size.width / 2,
20706 y: mid.y - size.height / 2
20707 }, size);
20708 }
20709
20710 function isLabel$6(element) {
20711 return element && !!element.labelTarget;
20712 }
20713
20714 /**
20715 * @param {ModdleElement} semantic
20716 * @param {ModdleElement} di
20717 * @param {Object} [attrs=null]
20718 *
20719 * @return {Object}
20720 */
20721 function elementData(semantic, di, attrs) {
20722 return assign({
20723 id: semantic.id,
20724 type: semantic.$type,
20725 businessObject: semantic,
20726 di: di
20727 }, attrs);
20728 }
20729
20730 function getWaypoints(di, source, target) {
20731
20732 var waypoints = di.waypoint;
20733
20734 if (!waypoints || waypoints.length < 2) {
20735 return [ getMid(source), getMid(target) ];
20736 }
20737
20738 return waypoints.map(function(p) {
20739 return { x: p.x, y: p.y };
20740 });
20741 }
20742
20743 function notYetDrawn(translate, semantic, refSemantic, property) {
20744 return new Error(translate('element {element} referenced by {referenced}#{property} not yet drawn', {
20745 element: elementToString(refSemantic),
20746 referenced: elementToString(semantic),
20747 property: property
20748 }));
20749 }
20750
20751
20752 /**
20753 * An importer that adds bpmn elements to the canvas
20754 *
20755 * @param {EventBus} eventBus
20756 * @param {Canvas} canvas
20757 * @param {ElementFactory} elementFactory
20758 * @param {ElementRegistry} elementRegistry
20759 * @param {Function} translate
20760 * @param {TextRenderer} textRenderer
20761 */
20762 function BpmnImporter(
20763 eventBus, canvas, elementFactory,
20764 elementRegistry, translate, textRenderer) {
20765
20766 this._eventBus = eventBus;
20767 this._canvas = canvas;
20768 this._elementFactory = elementFactory;
20769 this._elementRegistry = elementRegistry;
20770 this._translate = translate;
20771 this._textRenderer = textRenderer;
20772 }
20773
20774 BpmnImporter.$inject = [
20775 'eventBus',
20776 'canvas',
20777 'elementFactory',
20778 'elementRegistry',
20779 'translate',
20780 'textRenderer'
20781 ];
20782
20783
20784 /**
20785 * Add bpmn element (semantic) to the canvas onto the
20786 * specified parent shape.
20787 */
20788 BpmnImporter.prototype.add = function(semantic, di, parentElement) {
20789 var element,
20790 translate = this._translate,
20791 hidden;
20792
20793 var parentIndex;
20794
20795 // ROOT ELEMENT
20796 // handle the special case that we deal with a
20797 // invisible root element (process, subprocess or collaboration)
20798 if (is$1(di, 'bpmndi:BPMNPlane')) {
20799
20800 var attrs = is$1(semantic, 'bpmn:SubProcess')
20801 ? { id: semantic.id + '_plane' }
20802 : {};
20803
20804 // add a virtual element (not being drawn)
20805 element = this._elementFactory.createRoot(elementData(semantic, di, attrs));
20806
20807 this._canvas.addRootElement(element);
20808 }
20809
20810 // SHAPE
20811 else if (is$1(di, 'bpmndi:BPMNShape')) {
20812
20813 var collapsed = !isExpanded(semantic, di),
20814 isFrame = isFrameElement(semantic);
20815
20816 hidden = parentElement && (parentElement.hidden || parentElement.collapsed);
20817
20818 var bounds = di.bounds;
20819
20820 element = this._elementFactory.createShape(elementData(semantic, di, {
20821 collapsed: collapsed,
20822 hidden: hidden,
20823 x: Math.round(bounds.x),
20824 y: Math.round(bounds.y),
20825 width: Math.round(bounds.width),
20826 height: Math.round(bounds.height),
20827 isFrame: isFrame
20828 }));
20829
20830 if (is$1(semantic, 'bpmn:BoundaryEvent')) {
20831 this._attachBoundary(semantic, element);
20832 }
20833
20834 // insert lanes behind other flow nodes (cf. #727)
20835 if (is$1(semantic, 'bpmn:Lane')) {
20836 parentIndex = 0;
20837 }
20838
20839 if (is$1(semantic, 'bpmn:DataStoreReference')) {
20840
20841 // check whether data store is inside our outside of its semantic parent
20842 if (!isPointInsideBBox$1(parentElement, getMid(bounds))) {
20843 parentElement = this._canvas.findRoot(parentElement);
20844 }
20845 }
20846
20847 this._canvas.addShape(element, parentElement, parentIndex);
20848 }
20849
20850 // CONNECTION
20851 else if (is$1(di, 'bpmndi:BPMNEdge')) {
20852
20853 var source = this._getSource(semantic),
20854 target = this._getTarget(semantic);
20855
20856 hidden = parentElement && (parentElement.hidden || parentElement.collapsed);
20857
20858 element = this._elementFactory.createConnection(elementData(semantic, di, {
20859 hidden: hidden,
20860 source: source,
20861 target: target,
20862 waypoints: getWaypoints(di, source, target)
20863 }));
20864
20865 if (is$1(semantic, 'bpmn:DataAssociation')) {
20866
20867 // render always on top; this ensures DataAssociations
20868 // are rendered correctly across different "hacks" people
20869 // love to model such as cross participant / sub process
20870 // associations
20871 parentElement = this._canvas.findRoot(parentElement);
20872 }
20873
20874 // insert sequence flows behind other flow nodes (cf. #727)
20875 if (is$1(semantic, 'bpmn:SequenceFlow')) {
20876 parentIndex = 0;
20877 }
20878
20879 this._canvas.addConnection(element, parentElement, parentIndex);
20880 } else {
20881 throw new Error(translate('unknown di {di} for element {semantic}', {
20882 di: elementToString(di),
20883 semantic: elementToString(semantic)
20884 }));
20885 }
20886
20887 // (optional) LABEL
20888 if (isLabelExternal(semantic) && getLabel(element)) {
20889 this.addLabel(semantic, di, element);
20890 }
20891
20892
20893 this._eventBus.fire('bpmnElement.added', { element: element });
20894
20895 return element;
20896 };
20897
20898
20899 /**
20900 * Attach the boundary element to the given host
20901 *
20902 * @param {ModdleElement} boundarySemantic
20903 * @param {djs.model.Base} boundaryElement
20904 */
20905 BpmnImporter.prototype._attachBoundary = function(boundarySemantic, boundaryElement) {
20906 var translate = this._translate;
20907 var hostSemantic = boundarySemantic.attachedToRef;
20908
20909 if (!hostSemantic) {
20910 throw new Error(translate('missing {semantic}#attachedToRef', {
20911 semantic: elementToString(boundarySemantic)
20912 }));
20913 }
20914
20915 var host = this._elementRegistry.get(hostSemantic.id),
20916 attachers = host && host.attachers;
20917
20918 if (!host) {
20919 throw notYetDrawn(translate, boundarySemantic, hostSemantic, 'attachedToRef');
20920 }
20921
20922 // wire element.host <> host.attachers
20923 boundaryElement.host = host;
20924
20925 if (!attachers) {
20926 host.attachers = attachers = [];
20927 }
20928
20929 if (attachers.indexOf(boundaryElement) === -1) {
20930 attachers.push(boundaryElement);
20931 }
20932 };
20933
20934
20935 /**
20936 * add label for an element
20937 */
20938 BpmnImporter.prototype.addLabel = function(semantic, di, element) {
20939 var bounds,
20940 text,
20941 label;
20942
20943 bounds = getExternalLabelBounds(di, element);
20944
20945 text = getLabel(element);
20946
20947 if (text) {
20948
20949 // get corrected bounds from actual layouted text
20950 bounds = this._textRenderer.getExternalLabelBounds(bounds, text);
20951 }
20952
20953 label = this._elementFactory.createLabel(elementData(semantic, di, {
20954 id: semantic.id + '_label',
20955 labelTarget: element,
20956 type: 'label',
20957 hidden: element.hidden || !getLabel(element),
20958 x: Math.round(bounds.x),
20959 y: Math.round(bounds.y),
20960 width: Math.round(bounds.width),
20961 height: Math.round(bounds.height)
20962 }));
20963
20964 return this._canvas.addShape(label, element.parent);
20965 };
20966
20967 /**
20968 * Return the drawn connection end based on the given side.
20969 *
20970 * @throws {Error} if the end is not yet drawn
20971 */
20972 BpmnImporter.prototype._getEnd = function(semantic, side) {
20973
20974 var element,
20975 refSemantic,
20976 type = semantic.$type,
20977 translate = this._translate;
20978
20979 refSemantic = semantic[side + 'Ref'];
20980
20981 // handle mysterious isMany DataAssociation#sourceRef
20982 if (side === 'source' && type === 'bpmn:DataInputAssociation') {
20983 refSemantic = refSemantic && refSemantic[0];
20984 }
20985
20986 // fix source / target for DataInputAssociation / DataOutputAssociation
20987 if (side === 'source' && type === 'bpmn:DataOutputAssociation' ||
20988 side === 'target' && type === 'bpmn:DataInputAssociation') {
20989
20990 refSemantic = semantic.$parent;
20991 }
20992
20993 element = refSemantic && this._getElement(refSemantic);
20994
20995 if (element) {
20996 return element;
20997 }
20998
20999 if (refSemantic) {
21000 throw notYetDrawn(translate, semantic, refSemantic, side + 'Ref');
21001 } else {
21002 throw new Error(translate('{semantic}#{side} Ref not specified', {
21003 semantic: elementToString(semantic),
21004 side: side
21005 }));
21006 }
21007 };
21008
21009 BpmnImporter.prototype._getSource = function(semantic) {
21010 return this._getEnd(semantic, 'source');
21011 };
21012
21013 BpmnImporter.prototype._getTarget = function(semantic) {
21014 return this._getEnd(semantic, 'target');
21015 };
21016
21017
21018 BpmnImporter.prototype._getElement = function(semantic) {
21019 return this._elementRegistry.get(semantic.id);
21020 };
21021
21022
21023 // helpers ////////////////////
21024
21025 function isPointInsideBBox$1(bbox, point) {
21026 var x = point.x,
21027 y = point.y;
21028
21029 return x >= bbox.x &&
21030 x <= bbox.x + bbox.width &&
21031 y >= bbox.y &&
21032 y <= bbox.y + bbox.height;
21033 }
21034
21035 function isFrameElement(semantic) {
21036 return is$1(semantic, 'bpmn:Group');
21037 }
21038
21039 var ImportModule = {
21040 __depends__: [
21041 translate
21042 ],
21043 bpmnImporter: [ 'type', BpmnImporter ]
21044 };
21045
21046 var CoreModule = {
21047 __depends__: [
21048 DrawModule,
21049 ImportModule
21050 ]
21051 };
21052
21053 function __stopPropagation(event) {
21054 if (!event || typeof event.stopPropagation !== 'function') {
21055 return;
21056 }
21057
21058 event.stopPropagation();
21059 }
21060
21061
21062 function getOriginal$1(event) {
21063 return event.originalEvent || event.srcEvent;
21064 }
21065
21066
21067 function stopPropagation$1(event, immediate) {
21068 __stopPropagation(event);
21069 __stopPropagation(getOriginal$1(event));
21070 }
21071
21072
21073 function toPoint(event) {
21074
21075 if (event.pointers && event.pointers.length) {
21076 event = event.pointers[0];
21077 }
21078
21079 if (event.touches && event.touches.length) {
21080 event = event.touches[0];
21081 }
21082
21083 return event ? {
21084 x: event.clientX,
21085 y: event.clientY
21086 } : null;
21087 }
21088
21089 function isMac() {
21090 return (/mac/i).test(navigator.platform);
21091 }
21092
21093 function isButton(event, button) {
21094 return (getOriginal$1(event) || event).button === button;
21095 }
21096
21097 function isPrimaryButton(event) {
21098
21099 // button === 0 -> left áka primary mouse button
21100 return isButton(event, 0);
21101 }
21102
21103 function isAuxiliaryButton(event) {
21104
21105 // button === 1 -> auxiliary áka wheel button
21106 return isButton(event, 1);
21107 }
21108
21109 function hasPrimaryModifier(event) {
21110 var originalEvent = getOriginal$1(event) || event;
21111
21112 if (!isPrimaryButton(event)) {
21113 return false;
21114 }
21115
21116 // Use cmd as primary modifier key for mac OS
21117 if (isMac()) {
21118 return originalEvent.metaKey;
21119 } else {
21120 return originalEvent.ctrlKey;
21121 }
21122 }
21123
21124
21125 function hasSecondaryModifier(event) {
21126 var originalEvent = getOriginal$1(event) || event;
21127
21128 return isPrimaryButton(event) && originalEvent.shiftKey;
21129 }
21130
21131 function allowAll(event) { return true; }
21132
21133 function allowPrimaryAndAuxiliary(event) {
21134 return isPrimaryButton(event) || isAuxiliaryButton(event);
21135 }
21136
21137 var LOW_PRIORITY$o = 500;
21138
21139
21140 /**
21141 * A plugin that provides interaction events for diagram elements.
21142 *
21143 * It emits the following events:
21144 *
21145 * * element.click
21146 * * element.contextmenu
21147 * * element.dblclick
21148 * * element.hover
21149 * * element.mousedown
21150 * * element.mousemove
21151 * * element.mouseup
21152 * * element.out
21153 *
21154 * Each event is a tuple { element, gfx, originalEvent }.
21155 *
21156 * Canceling the event via Event#preventDefault()
21157 * prevents the original DOM operation.
21158 *
21159 * @param {EventBus} eventBus
21160 */
21161 function InteractionEvents(eventBus, elementRegistry, styles) {
21162
21163 var self = this;
21164
21165 /**
21166 * Fire an interaction event.
21167 *
21168 * @param {string} type local event name, e.g. element.click.
21169 * @param {DOMEvent} event native event
21170 * @param {djs.model.Base} [element] the diagram element to emit the event on;
21171 * defaults to the event target
21172 */
21173 function fire(type, event, element) {
21174
21175 if (isIgnored(type, event)) {
21176 return;
21177 }
21178
21179 var target, gfx, returnValue;
21180
21181 if (!element) {
21182 target = event.delegateTarget || event.target;
21183
21184 if (target) {
21185 gfx = target;
21186 element = elementRegistry.get(gfx);
21187 }
21188 } else {
21189 gfx = elementRegistry.getGraphics(element);
21190 }
21191
21192 if (!gfx || !element) {
21193 return;
21194 }
21195
21196 returnValue = eventBus.fire(type, {
21197 element: element,
21198 gfx: gfx,
21199 originalEvent: event
21200 });
21201
21202 if (returnValue === false) {
21203 event.stopPropagation();
21204 event.preventDefault();
21205 }
21206 }
21207
21208 // TODO(nikku): document this
21209 var handlers = {};
21210
21211 function mouseHandler(localEventName) {
21212 return handlers[localEventName];
21213 }
21214
21215 function isIgnored(localEventName, event) {
21216
21217 var filter = ignoredFilters[localEventName] || isPrimaryButton;
21218
21219 // only react on left mouse button interactions
21220 // except for interaction events that are enabled
21221 // for secundary mouse button
21222 return !filter(event);
21223 }
21224
21225 var bindings = {
21226 click: 'element.click',
21227 contextmenu: 'element.contextmenu',
21228 dblclick: 'element.dblclick',
21229 mousedown: 'element.mousedown',
21230 mousemove: 'element.mousemove',
21231 mouseover: 'element.hover',
21232 mouseout: 'element.out',
21233 mouseup: 'element.mouseup',
21234 };
21235
21236 var ignoredFilters = {
21237 'element.contextmenu': allowAll,
21238 'element.mousedown': allowPrimaryAndAuxiliary,
21239 'element.mouseup': allowPrimaryAndAuxiliary,
21240 'element.click': allowPrimaryAndAuxiliary,
21241 'element.dblclick': allowPrimaryAndAuxiliary
21242 };
21243
21244
21245 // manual event trigger //////////
21246
21247 /**
21248 * Trigger an interaction event (based on a native dom event)
21249 * on the target shape or connection.
21250 *
21251 * @param {string} eventName the name of the triggered DOM event
21252 * @param {MouseEvent} event
21253 * @param {djs.model.Base} targetElement
21254 */
21255 function triggerMouseEvent(eventName, event, targetElement) {
21256
21257 // i.e. element.mousedown...
21258 var localEventName = bindings[eventName];
21259
21260 if (!localEventName) {
21261 throw new Error('unmapped DOM event name <' + eventName + '>');
21262 }
21263
21264 return fire(localEventName, event, targetElement);
21265 }
21266
21267
21268 var ELEMENT_SELECTOR = 'svg, .djs-element';
21269
21270 // event handling ///////
21271
21272 function registerEvent(node, event, localEvent, ignoredFilter) {
21273
21274 var handler = handlers[localEvent] = function(event) {
21275 fire(localEvent, event);
21276 };
21277
21278 if (ignoredFilter) {
21279 ignoredFilters[localEvent] = ignoredFilter;
21280 }
21281
21282 handler.$delegate = delegate.bind(node, ELEMENT_SELECTOR, event, handler);
21283 }
21284
21285 function unregisterEvent(node, event, localEvent) {
21286
21287 var handler = mouseHandler(localEvent);
21288
21289 if (!handler) {
21290 return;
21291 }
21292
21293 delegate.unbind(node, event, handler.$delegate);
21294 }
21295
21296 function registerEvents(svg) {
21297 forEach$2(bindings, function(val, key) {
21298 registerEvent(svg, key, val);
21299 });
21300 }
21301
21302 function unregisterEvents(svg) {
21303 forEach$2(bindings, function(val, key) {
21304 unregisterEvent(svg, key, val);
21305 });
21306 }
21307
21308 eventBus.on('canvas.destroy', function(event) {
21309 unregisterEvents(event.svg);
21310 });
21311
21312 eventBus.on('canvas.init', function(event) {
21313 registerEvents(event.svg);
21314 });
21315
21316
21317 // hit box updating ////////////////
21318
21319 eventBus.on([ 'shape.added', 'connection.added' ], function(event) {
21320 var element = event.element,
21321 gfx = event.gfx;
21322
21323 eventBus.fire('interactionEvents.createHit', { element: element, gfx: gfx });
21324 });
21325
21326 // Update djs-hit on change.
21327 // A low priortity is necessary, because djs-hit of labels has to be updated
21328 // after the label bounds have been updated in the renderer.
21329 eventBus.on([
21330 'shape.changed',
21331 'connection.changed'
21332 ], LOW_PRIORITY$o, function(event) {
21333
21334 var element = event.element,
21335 gfx = event.gfx;
21336
21337 eventBus.fire('interactionEvents.updateHit', { element: element, gfx: gfx });
21338 });
21339
21340 eventBus.on('interactionEvents.createHit', LOW_PRIORITY$o, function(event) {
21341 var element = event.element,
21342 gfx = event.gfx;
21343
21344 self.createDefaultHit(element, gfx);
21345 });
21346
21347 eventBus.on('interactionEvents.updateHit', function(event) {
21348 var element = event.element,
21349 gfx = event.gfx;
21350
21351 self.updateDefaultHit(element, gfx);
21352 });
21353
21354
21355 // hit styles ////////////
21356
21357 var STROKE_HIT_STYLE = createHitStyle('djs-hit djs-hit-stroke');
21358
21359 var CLICK_STROKE_HIT_STYLE = createHitStyle('djs-hit djs-hit-click-stroke');
21360
21361 var ALL_HIT_STYLE = createHitStyle('djs-hit djs-hit-all');
21362
21363 var NO_MOVE_HIT_STYLE = createHitStyle('djs-hit djs-hit-no-move');
21364
21365 var HIT_TYPES = {
21366 'all': ALL_HIT_STYLE,
21367 'click-stroke': CLICK_STROKE_HIT_STYLE,
21368 'stroke': STROKE_HIT_STYLE,
21369 'no-move': NO_MOVE_HIT_STYLE
21370 };
21371
21372 function createHitStyle(classNames, attrs) {
21373
21374 attrs = assign({
21375 stroke: 'white',
21376 strokeWidth: 15
21377 }, attrs || {});
21378
21379 return styles.cls(classNames, [ 'no-fill', 'no-border' ], attrs);
21380 }
21381
21382
21383 // style helpers ///////////////
21384
21385 function applyStyle(hit, type) {
21386
21387 var attrs = HIT_TYPES[type];
21388
21389 if (!attrs) {
21390 throw new Error('invalid hit type <' + type + '>');
21391 }
21392
21393 attr$1(hit, attrs);
21394
21395 return hit;
21396 }
21397
21398 function appendHit(gfx, hit) {
21399 append(gfx, hit);
21400 }
21401
21402
21403 // API
21404
21405 /**
21406 * Remove hints on the given graphics.
21407 *
21408 * @param {SVGElement} gfx
21409 */
21410 this.removeHits = function(gfx) {
21411 var hits = all('.djs-hit', gfx);
21412
21413 forEach$2(hits, remove$2);
21414 };
21415
21416 /**
21417 * Create default hit for the given element.
21418 *
21419 * @param {djs.model.Base} element
21420 * @param {SVGElement} gfx
21421 *
21422 * @return {SVGElement} created hit
21423 */
21424 this.createDefaultHit = function(element, gfx) {
21425 var waypoints = element.waypoints,
21426 isFrame = element.isFrame,
21427 boxType;
21428
21429 if (waypoints) {
21430 return this.createWaypointsHit(gfx, waypoints);
21431 } else {
21432
21433 boxType = isFrame ? 'stroke' : 'all';
21434
21435 return this.createBoxHit(gfx, boxType, {
21436 width: element.width,
21437 height: element.height
21438 });
21439 }
21440 };
21441
21442 /**
21443 * Create hits for the given waypoints.
21444 *
21445 * @param {SVGElement} gfx
21446 * @param {Array<Point>} waypoints
21447 *
21448 * @return {SVGElement}
21449 */
21450 this.createWaypointsHit = function(gfx, waypoints) {
21451
21452 var hit = createLine(waypoints);
21453
21454 applyStyle(hit, 'stroke');
21455
21456 appendHit(gfx, hit);
21457
21458 return hit;
21459 };
21460
21461 /**
21462 * Create hits for a box.
21463 *
21464 * @param {SVGElement} gfx
21465 * @param {string} hitType
21466 * @param {Object} attrs
21467 *
21468 * @return {SVGElement}
21469 */
21470 this.createBoxHit = function(gfx, type, attrs) {
21471
21472 attrs = assign({
21473 x: 0,
21474 y: 0
21475 }, attrs);
21476
21477 var hit = create$1('rect');
21478
21479 applyStyle(hit, type);
21480
21481 attr$1(hit, attrs);
21482
21483 appendHit(gfx, hit);
21484
21485 return hit;
21486 };
21487
21488 /**
21489 * Update default hit of the element.
21490 *
21491 * @param {djs.model.Base} element
21492 * @param {SVGElement} gfx
21493 *
21494 * @return {SVGElement} updated hit
21495 */
21496 this.updateDefaultHit = function(element, gfx) {
21497
21498 var hit = query('.djs-hit', gfx);
21499
21500 if (!hit) {
21501 return;
21502 }
21503
21504 if (element.waypoints) {
21505 updateLine(hit, element.waypoints);
21506 } else {
21507 attr$1(hit, {
21508 width: element.width,
21509 height: element.height
21510 });
21511 }
21512
21513 return hit;
21514 };
21515
21516 this.fire = fire;
21517
21518 this.triggerMouseEvent = triggerMouseEvent;
21519
21520 this.mouseHandler = mouseHandler;
21521
21522 this.registerEvent = registerEvent;
21523 this.unregisterEvent = unregisterEvent;
21524 }
21525
21526
21527 InteractionEvents.$inject = [
21528 'eventBus',
21529 'elementRegistry',
21530 'styles'
21531 ];
21532
21533
21534 /**
21535 * An event indicating that the mouse hovered over an element
21536 *
21537 * @event element.hover
21538 *
21539 * @type {Object}
21540 * @property {djs.model.Base} element
21541 * @property {SVGElement} gfx
21542 * @property {Event} originalEvent
21543 */
21544
21545 /**
21546 * An event indicating that the mouse has left an element
21547 *
21548 * @event element.out
21549 *
21550 * @type {Object}
21551 * @property {djs.model.Base} element
21552 * @property {SVGElement} gfx
21553 * @property {Event} originalEvent
21554 */
21555
21556 /**
21557 * An event indicating that the mouse has clicked an element
21558 *
21559 * @event element.click
21560 *
21561 * @type {Object}
21562 * @property {djs.model.Base} element
21563 * @property {SVGElement} gfx
21564 * @property {Event} originalEvent
21565 */
21566
21567 /**
21568 * An event indicating that the mouse has double clicked an element
21569 *
21570 * @event element.dblclick
21571 *
21572 * @type {Object}
21573 * @property {djs.model.Base} element
21574 * @property {SVGElement} gfx
21575 * @property {Event} originalEvent
21576 */
21577
21578 /**
21579 * An event indicating that the mouse has gone down on an element.
21580 *
21581 * @event element.mousedown
21582 *
21583 * @type {Object}
21584 * @property {djs.model.Base} element
21585 * @property {SVGElement} gfx
21586 * @property {Event} originalEvent
21587 */
21588
21589 /**
21590 * An event indicating that the mouse has gone up on an element.
21591 *
21592 * @event element.mouseup
21593 *
21594 * @type {Object}
21595 * @property {djs.model.Base} element
21596 * @property {SVGElement} gfx
21597 * @property {Event} originalEvent
21598 */
21599
21600 /**
21601 * An event indicating that the context menu action is triggered
21602 * via mouse or touch controls.
21603 *
21604 * @event element.contextmenu
21605 *
21606 * @type {Object}
21607 * @property {djs.model.Base} element
21608 * @property {SVGElement} gfx
21609 * @property {Event} originalEvent
21610 */
21611
21612 var InteractionEventsModule$1 = {
21613 __init__: [ 'interactionEvents' ],
21614 interactionEvents: [ 'type', InteractionEvents ]
21615 };
21616
21617 var LOW_PRIORITY$n = 500;
21618
21619
21620 /**
21621 * @class
21622 *
21623 * A plugin that adds an outline to shapes and connections that may be activated and styled
21624 * via CSS classes.
21625 *
21626 * @param {EventBus} eventBus
21627 * @param {Styles} styles
21628 * @param {ElementRegistry} elementRegistry
21629 */
21630 function Outline(eventBus, styles, elementRegistry) {
21631
21632 this.offset = 6;
21633
21634 var OUTLINE_STYLE = styles.cls('djs-outline', [ 'no-fill' ]);
21635
21636 var self = this;
21637
21638 function createOutline(gfx, bounds) {
21639 var outline = create$1('rect');
21640
21641 attr$1(outline, assign({
21642 x: 10,
21643 y: 10,
21644 width: 100,
21645 height: 100
21646 }, OUTLINE_STYLE));
21647
21648 append(gfx, outline);
21649
21650 return outline;
21651 }
21652
21653 // A low priortity is necessary, because outlines of labels have to be updated
21654 // after the label bounds have been updated in the renderer.
21655 eventBus.on([ 'shape.added', 'shape.changed' ], LOW_PRIORITY$n, function(event) {
21656 var element = event.element,
21657 gfx = event.gfx;
21658
21659 var outline = query('.djs-outline', gfx);
21660
21661 if (!outline) {
21662 outline = createOutline(gfx);
21663 }
21664
21665 self.updateShapeOutline(outline, element);
21666 });
21667
21668 eventBus.on([ 'connection.added', 'connection.changed' ], function(event) {
21669 var element = event.element,
21670 gfx = event.gfx;
21671
21672 var outline = query('.djs-outline', gfx);
21673
21674 if (!outline) {
21675 outline = createOutline(gfx);
21676 }
21677
21678 self.updateConnectionOutline(outline, element);
21679 });
21680 }
21681
21682
21683 /**
21684 * Updates the outline of a shape respecting the dimension of the
21685 * element and an outline offset.
21686 *
21687 * @param {SVGElement} outline
21688 * @param {djs.model.Base} element
21689 */
21690 Outline.prototype.updateShapeOutline = function(outline, element) {
21691
21692 attr$1(outline, {
21693 x: -this.offset,
21694 y: -this.offset,
21695 width: element.width + this.offset * 2,
21696 height: element.height + this.offset * 2
21697 });
21698
21699 };
21700
21701
21702 /**
21703 * Updates the outline of a connection respecting the bounding box of
21704 * the connection and an outline offset.
21705 *
21706 * @param {SVGElement} outline
21707 * @param {djs.model.Base} element
21708 */
21709 Outline.prototype.updateConnectionOutline = function(outline, connection) {
21710
21711 var bbox = getBBox(connection);
21712
21713 attr$1(outline, {
21714 x: bbox.x - this.offset,
21715 y: bbox.y - this.offset,
21716 width: bbox.width + this.offset * 2,
21717 height: bbox.height + this.offset * 2
21718 });
21719
21720 };
21721
21722
21723 Outline.$inject = [ 'eventBus', 'styles', 'elementRegistry' ];
21724
21725 var OutlineModule = {
21726 __init__: [ 'outline' ],
21727 outline: [ 'type', Outline ]
21728 };
21729
21730 /**
21731 * A service that offers the current selection in a diagram.
21732 * Offers the api to control the selection, too.
21733 *
21734 * @class
21735 *
21736 * @param {EventBus} eventBus the event bus
21737 */
21738 function Selection(eventBus, canvas) {
21739
21740 this._eventBus = eventBus;
21741 this._canvas = canvas;
21742
21743 this._selectedElements = [];
21744
21745 var self = this;
21746
21747 eventBus.on([ 'shape.remove', 'connection.remove' ], function(e) {
21748 var element = e.element;
21749 self.deselect(element);
21750 });
21751
21752 eventBus.on([ 'diagram.clear', 'root.set' ], function(e) {
21753 self.select(null);
21754 });
21755 }
21756
21757 Selection.$inject = [ 'eventBus', 'canvas' ];
21758
21759
21760 Selection.prototype.deselect = function(element) {
21761 var selectedElements = this._selectedElements;
21762
21763 var idx = selectedElements.indexOf(element);
21764
21765 if (idx !== -1) {
21766 var oldSelection = selectedElements.slice();
21767
21768 selectedElements.splice(idx, 1);
21769
21770 this._eventBus.fire('selection.changed', { oldSelection: oldSelection, newSelection: selectedElements });
21771 }
21772 };
21773
21774
21775 Selection.prototype.get = function() {
21776 return this._selectedElements;
21777 };
21778
21779 Selection.prototype.isSelected = function(element) {
21780 return this._selectedElements.indexOf(element) !== -1;
21781 };
21782
21783
21784 /**
21785 * This method selects one or more elements on the diagram.
21786 *
21787 * By passing an additional add parameter you can decide whether or not the element(s)
21788 * should be added to the already existing selection or not.
21789 *
21790 * @method Selection#select
21791 *
21792 * @param {Object|Object[]} elements element or array of elements to be selected
21793 * @param {boolean} [add] whether the element(s) should be appended to the current selection, defaults to false
21794 */
21795 Selection.prototype.select = function(elements, add) {
21796 var selectedElements = this._selectedElements,
21797 oldSelection = selectedElements.slice();
21798
21799 if (!isArray$4(elements)) {
21800 elements = elements ? [ elements ] : [];
21801 }
21802
21803 var canvas = this._canvas;
21804
21805 var rootElement = canvas.getRootElement();
21806
21807 elements = elements.filter(function(element) {
21808 var elementRoot = canvas.findRoot(element);
21809
21810 return rootElement === elementRoot;
21811 });
21812
21813 // selection may be cleared by passing an empty array or null
21814 // to the method
21815 if (add) {
21816 forEach$2(elements, function(element) {
21817 if (selectedElements.indexOf(element) !== -1) {
21818
21819 // already selected
21820 return;
21821 } else {
21822 selectedElements.push(element);
21823 }
21824 });
21825 } else {
21826 this._selectedElements = selectedElements = elements.slice();
21827 }
21828
21829 this._eventBus.fire('selection.changed', { oldSelection: oldSelection, newSelection: selectedElements });
21830 };
21831
21832 var MARKER_HOVER = 'hover',
21833 MARKER_SELECTED = 'selected';
21834
21835
21836 /**
21837 * A plugin that adds a visible selection UI to shapes and connections
21838 * by appending the <code>hover</code> and <code>selected</code> classes to them.
21839 *
21840 * @class
21841 *
21842 * Makes elements selectable, too.
21843 *
21844 * @param {EventBus} events
21845 * @param {SelectionService} selection
21846 * @param {Canvas} canvas
21847 */
21848 function SelectionVisuals(events, canvas, selection, styles) {
21849
21850 this._multiSelectionBox = null;
21851
21852 function addMarker(e, cls) {
21853 canvas.addMarker(e, cls);
21854 }
21855
21856 function removeMarker(e, cls) {
21857 canvas.removeMarker(e, cls);
21858 }
21859
21860 events.on('element.hover', function(event) {
21861 addMarker(event.element, MARKER_HOVER);
21862 });
21863
21864 events.on('element.out', function(event) {
21865 removeMarker(event.element, MARKER_HOVER);
21866 });
21867
21868 events.on('selection.changed', function(event) {
21869
21870 function deselect(s) {
21871 removeMarker(s, MARKER_SELECTED);
21872 }
21873
21874 function select(s) {
21875 addMarker(s, MARKER_SELECTED);
21876 }
21877
21878 var oldSelection = event.oldSelection,
21879 newSelection = event.newSelection;
21880
21881 forEach$2(oldSelection, function(e) {
21882 if (newSelection.indexOf(e) === -1) {
21883 deselect(e);
21884 }
21885 });
21886
21887 forEach$2(newSelection, function(e) {
21888 if (oldSelection.indexOf(e) === -1) {
21889 select(e);
21890 }
21891 });
21892 });
21893 }
21894
21895 SelectionVisuals.$inject = [
21896 'eventBus',
21897 'canvas',
21898 'selection',
21899 'styles'
21900 ];
21901
21902 function SelectionBehavior(eventBus, selection, canvas, elementRegistry) {
21903
21904 // Select elements on create
21905 eventBus.on('create.end', 500, function(event) {
21906 var context = event.context,
21907 canExecute = context.canExecute,
21908 elements = context.elements,
21909 hints = context.hints || {},
21910 autoSelect = hints.autoSelect;
21911
21912 if (canExecute) {
21913 if (autoSelect === false) {
21914
21915 // Select no elements
21916 return;
21917 }
21918
21919 if (isArray$4(autoSelect)) {
21920 selection.select(autoSelect);
21921 } else {
21922
21923 // Select all elements by default
21924 selection.select(elements.filter(isShown));
21925 }
21926 }
21927 });
21928
21929 // Select connection targets on connect
21930 eventBus.on('connect.end', 500, function(event) {
21931 var context = event.context,
21932 canExecute = context.canExecute,
21933 hover = context.hover;
21934
21935 if (canExecute && hover) {
21936 selection.select(hover);
21937 }
21938 });
21939
21940 // Select shapes on move
21941 eventBus.on('shape.move.end', 500, function(event) {
21942 var previousSelection = event.previousSelection || [];
21943
21944 var shape = elementRegistry.get(event.context.shape.id);
21945
21946 // Always select main shape on move
21947 var isSelected = find(previousSelection, function(selectedShape) {
21948 return shape.id === selectedShape.id;
21949 });
21950
21951 if (!isSelected) {
21952 selection.select(shape);
21953 }
21954 });
21955
21956 // Select elements on click
21957 eventBus.on('element.click', function(event) {
21958
21959 if (!isPrimaryButton(event)) {
21960 return;
21961 }
21962
21963 var element = event.element;
21964
21965 if (element === canvas.getRootElement()) {
21966 element = null;
21967 }
21968
21969 var isSelected = selection.isSelected(element),
21970 isMultiSelect = selection.get().length > 1;
21971
21972 // Add to selection if CTRL or SHIFT pressed
21973 var add = hasPrimaryModifier(event) || hasSecondaryModifier(event);
21974
21975 if (isSelected && isMultiSelect) {
21976 if (add) {
21977
21978 // Deselect element
21979 return selection.deselect(element);
21980 } else {
21981
21982 // Select element only
21983 return selection.select(element);
21984 }
21985 } else if (!isSelected) {
21986
21987 // Select element
21988 selection.select(element, add);
21989 } else {
21990
21991 // Deselect element
21992 selection.deselect(element);
21993 }
21994 });
21995 }
21996
21997 SelectionBehavior.$inject = [
21998 'eventBus',
21999 'selection',
22000 'canvas',
22001 'elementRegistry'
22002 ];
22003
22004
22005 function isShown(element) {
22006 return !element.hidden;
22007 }
22008
22009 var SelectionModule = {
22010 __init__: [ 'selectionVisuals', 'selectionBehavior' ],
22011 __depends__: [
22012 InteractionEventsModule$1,
22013 OutlineModule
22014 ],
22015 selection: [ 'type', Selection ],
22016 selectionVisuals: [ 'type', SelectionVisuals ],
22017 selectionBehavior: [ 'type', SelectionBehavior ]
22018 };
22019
22020 /**
22021 * Util that provides unique IDs.
22022 *
22023 * @class djs.util.IdGenerator
22024 * @constructor
22025 * @memberOf djs.util
22026 *
22027 * The ids can be customized via a given prefix and contain a random value to avoid collisions.
22028 *
22029 * @param {string} prefix a prefix to prepend to generated ids (for better readability)
22030 */
22031 function IdGenerator(prefix) {
22032
22033 this._counter = 0;
22034 this._prefix = (prefix ? prefix + '-' : '') + Math.floor(Math.random() * 1000000000) + '-';
22035 }
22036
22037 /**
22038 * Returns a next unique ID.
22039 *
22040 * @method djs.util.IdGenerator#next
22041 *
22042 * @returns {string} the id
22043 */
22044 IdGenerator.prototype.next = function() {
22045 return this._prefix + (++this._counter);
22046 };
22047
22048 // document wide unique overlay ids
22049 var ids$1 = new IdGenerator('ov');
22050
22051 var LOW_PRIORITY$m = 500;
22052
22053
22054 /**
22055 * A service that allows users to attach overlays to diagram elements.
22056 *
22057 * The overlay service will take care of overlay positioning during updates.
22058 *
22059 * @example
22060 *
22061 * // add a pink badge on the top left of the shape
22062 * overlays.add(someShape, {
22063 * position: {
22064 * top: -5,
22065 * left: -5
22066 * },
22067 * html: '<div style="width: 10px; background: fuchsia; color: white;">0</div>'
22068 * });
22069 *
22070 * // or add via shape id
22071 *
22072 * overlays.add('some-element-id', {
22073 * position: {
22074 * top: -5,
22075 * left: -5
22076 * }
22077 * html: '<div style="width: 10px; background: fuchsia; color: white;">0</div>'
22078 * });
22079 *
22080 * // or add with optional type
22081 *
22082 * overlays.add(someShape, 'badge', {
22083 * position: {
22084 * top: -5,
22085 * left: -5
22086 * }
22087 * html: '<div style="width: 10px; background: fuchsia; color: white;">0</div>'
22088 * });
22089 *
22090 *
22091 * // remove an overlay
22092 *
22093 * var id = overlays.add(...);
22094 * overlays.remove(id);
22095 *
22096 *
22097 * You may configure overlay defaults during tool by providing a `config` module
22098 * with `overlays.defaults` as an entry:
22099 *
22100 * {
22101 * overlays: {
22102 * defaults: {
22103 * show: {
22104 * minZoom: 0.7,
22105 * maxZoom: 5.0
22106 * },
22107 * scale: {
22108 * min: 1
22109 * }
22110 * }
22111 * }
22112 *
22113 * @param {Object} config
22114 * @param {EventBus} eventBus
22115 * @param {Canvas} canvas
22116 * @param {ElementRegistry} elementRegistry
22117 */
22118 function Overlays(config, eventBus, canvas, elementRegistry) {
22119
22120 this._eventBus = eventBus;
22121 this._canvas = canvas;
22122 this._elementRegistry = elementRegistry;
22123
22124 this._ids = ids$1;
22125
22126 this._overlayDefaults = assign({
22127
22128 // no show constraints
22129 show: null,
22130
22131 // always scale
22132 scale: true
22133 }, config && config.defaults);
22134
22135 /**
22136 * Mapping overlayId -> overlay
22137 */
22138 this._overlays = {};
22139
22140 /**
22141 * Mapping elementId -> overlay container
22142 */
22143 this._overlayContainers = [];
22144
22145 // root html element for all overlays
22146 this._overlayRoot = createRoot$1(canvas.getContainer());
22147
22148 this._init();
22149 }
22150
22151
22152 Overlays.$inject = [
22153 'config.overlays',
22154 'eventBus',
22155 'canvas',
22156 'elementRegistry'
22157 ];
22158
22159
22160 /**
22161 * Returns the overlay with the specified id or a list of overlays
22162 * for an element with a given type.
22163 *
22164 * @example
22165 *
22166 * // return the single overlay with the given id
22167 * overlays.get('some-id');
22168 *
22169 * // return all overlays for the shape
22170 * overlays.get({ element: someShape });
22171 *
22172 * // return all overlays on shape with type 'badge'
22173 * overlays.get({ element: someShape, type: 'badge' });
22174 *
22175 * // shape can also be specified as id
22176 * overlays.get({ element: 'element-id', type: 'badge' });
22177 *
22178 *
22179 * @param {Object} search
22180 * @param {string} [search.id]
22181 * @param {string|djs.model.Base} [search.element]
22182 * @param {string} [search.type]
22183 *
22184 * @return {Object|Array<Object>} the overlay(s)
22185 */
22186 Overlays.prototype.get = function(search) {
22187
22188 if (isString(search)) {
22189 search = { id: search };
22190 }
22191
22192 if (isString(search.element)) {
22193 search.element = this._elementRegistry.get(search.element);
22194 }
22195
22196 if (search.element) {
22197 var container = this._getOverlayContainer(search.element, true);
22198
22199 // return a list of overlays when searching by element (+type)
22200 if (container) {
22201 return search.type ? filter(container.overlays, matchPattern({ type: search.type })) : container.overlays.slice();
22202 } else {
22203 return [];
22204 }
22205 } else
22206 if (search.type) {
22207 return filter(this._overlays, matchPattern({ type: search.type }));
22208 } else {
22209
22210 // return single element when searching by id
22211 return search.id ? this._overlays[search.id] : null;
22212 }
22213 };
22214
22215 /**
22216 * Adds a HTML overlay to an element.
22217 *
22218 * @param {string|djs.model.Base} element attach overlay to this shape
22219 * @param {string} [type] optional type to assign to the overlay
22220 * @param {Object} overlay the overlay configuration
22221 *
22222 * @param {string|DOMElement} overlay.html html element to use as an overlay
22223 * @param {Object} [overlay.show] show configuration
22224 * @param {number} [overlay.show.minZoom] minimal zoom level to show the overlay
22225 * @param {number} [overlay.show.maxZoom] maximum zoom level to show the overlay
22226 * @param {Object} overlay.position where to attach the overlay
22227 * @param {number} [overlay.position.left] relative to element bbox left attachment
22228 * @param {number} [overlay.position.top] relative to element bbox top attachment
22229 * @param {number} [overlay.position.bottom] relative to element bbox bottom attachment
22230 * @param {number} [overlay.position.right] relative to element bbox right attachment
22231 * @param {boolean|Object} [overlay.scale=true] false to preserve the same size regardless of
22232 * diagram zoom
22233 * @param {number} [overlay.scale.min]
22234 * @param {number} [overlay.scale.max]
22235 *
22236 * @return {string} id that may be used to reference the overlay for update or removal
22237 */
22238 Overlays.prototype.add = function(element, type, overlay) {
22239
22240 if (isObject(type)) {
22241 overlay = type;
22242 type = null;
22243 }
22244
22245 if (!element.id) {
22246 element = this._elementRegistry.get(element);
22247 }
22248
22249 if (!overlay.position) {
22250 throw new Error('must specifiy overlay position');
22251 }
22252
22253 if (!overlay.html) {
22254 throw new Error('must specifiy overlay html');
22255 }
22256
22257 if (!element) {
22258 throw new Error('invalid element specified');
22259 }
22260
22261 var id = this._ids.next();
22262
22263 overlay = assign({}, this._overlayDefaults, overlay, {
22264 id: id,
22265 type: type,
22266 element: element,
22267 html: overlay.html
22268 });
22269
22270 this._addOverlay(overlay);
22271
22272 return id;
22273 };
22274
22275
22276 /**
22277 * Remove an overlay with the given id or all overlays matching the given filter.
22278 *
22279 * @see Overlays#get for filter options.
22280 *
22281 * @param {string|object} [filter]
22282 */
22283 Overlays.prototype.remove = function(filter) {
22284
22285 var overlays = this.get(filter) || [];
22286
22287 if (!isArray$4(overlays)) {
22288 overlays = [ overlays ];
22289 }
22290
22291 var self = this;
22292
22293 forEach$2(overlays, function(overlay) {
22294
22295 var container = self._getOverlayContainer(overlay.element, true);
22296
22297 if (overlay) {
22298 remove$1(overlay.html);
22299 remove$1(overlay.htmlContainer);
22300
22301 delete overlay.htmlContainer;
22302 delete overlay.element;
22303
22304 delete self._overlays[overlay.id];
22305 }
22306
22307 if (container) {
22308 var idx = container.overlays.indexOf(overlay);
22309 if (idx !== -1) {
22310 container.overlays.splice(idx, 1);
22311 }
22312 }
22313 });
22314
22315 };
22316
22317
22318 Overlays.prototype.show = function() {
22319 setVisible$1(this._overlayRoot);
22320 };
22321
22322
22323 Overlays.prototype.hide = function() {
22324 setVisible$1(this._overlayRoot, false);
22325 };
22326
22327 Overlays.prototype.clear = function() {
22328 this._overlays = {};
22329
22330 this._overlayContainers = [];
22331
22332 clear(this._overlayRoot);
22333 };
22334
22335 Overlays.prototype._updateOverlayContainer = function(container) {
22336 var element = container.element,
22337 html = container.html;
22338
22339 // update container left,top according to the elements x,y coordinates
22340 // this ensures we can attach child elements relative to this container
22341
22342 var x = element.x,
22343 y = element.y;
22344
22345 if (element.waypoints) {
22346 var bbox = getBBox(element);
22347 x = bbox.x;
22348 y = bbox.y;
22349 }
22350
22351 setPosition$1(html, x, y);
22352
22353 attr(container.html, 'data-container-id', element.id);
22354 };
22355
22356
22357 Overlays.prototype._updateOverlay = function(overlay) {
22358
22359 var position = overlay.position,
22360 htmlContainer = overlay.htmlContainer,
22361 element = overlay.element;
22362
22363 // update overlay html relative to shape because
22364 // it is already positioned on the element
22365
22366 // update relative
22367 var left = position.left,
22368 top = position.top;
22369
22370 if (position.right !== undefined) {
22371
22372 var width;
22373
22374 if (element.waypoints) {
22375 width = getBBox(element).width;
22376 } else {
22377 width = element.width;
22378 }
22379
22380 left = position.right * -1 + width;
22381 }
22382
22383 if (position.bottom !== undefined) {
22384
22385 var height;
22386
22387 if (element.waypoints) {
22388 height = getBBox(element).height;
22389 } else {
22390 height = element.height;
22391 }
22392
22393 top = position.bottom * -1 + height;
22394 }
22395
22396 setPosition$1(htmlContainer, left || 0, top || 0);
22397 this._updateOverlayVisibilty(overlay, this._canvas.viewbox());
22398 };
22399
22400
22401 Overlays.prototype._createOverlayContainer = function(element) {
22402 var html = domify('<div class="djs-overlays" />');
22403 assign$1(html, { position: 'absolute' });
22404
22405 this._overlayRoot.appendChild(html);
22406
22407 var container = {
22408 html: html,
22409 element: element,
22410 overlays: []
22411 };
22412
22413 this._updateOverlayContainer(container);
22414
22415 this._overlayContainers.push(container);
22416
22417 return container;
22418 };
22419
22420
22421 Overlays.prototype._updateRoot = function(viewbox) {
22422 var scale = viewbox.scale || 1;
22423
22424 var matrix = 'matrix(' +
22425 [
22426 scale,
22427 0,
22428 0,
22429 scale,
22430 -1 * viewbox.x * scale,
22431 -1 * viewbox.y * scale
22432 ].join(',') +
22433 ')';
22434
22435 setTransform$1(this._overlayRoot, matrix);
22436 };
22437
22438
22439 Overlays.prototype._getOverlayContainer = function(element, raw) {
22440 var container = find(this._overlayContainers, function(c) {
22441 return c.element === element;
22442 });
22443
22444
22445 if (!container && !raw) {
22446 return this._createOverlayContainer(element);
22447 }
22448
22449 return container;
22450 };
22451
22452
22453 Overlays.prototype._addOverlay = function(overlay) {
22454
22455 var id = overlay.id,
22456 element = overlay.element,
22457 html = overlay.html,
22458 htmlContainer,
22459 overlayContainer;
22460
22461 // unwrap jquery (for those who need it)
22462 if (html.get && html.constructor.prototype.jquery) {
22463 html = html.get(0);
22464 }
22465
22466 // create proper html elements from
22467 // overlay HTML strings
22468 if (isString(html)) {
22469 html = domify(html);
22470 }
22471
22472 overlayContainer = this._getOverlayContainer(element);
22473
22474 htmlContainer = domify('<div class="djs-overlay" data-overlay-id="' + id + '">');
22475 assign$1(htmlContainer, { position: 'absolute' });
22476
22477 htmlContainer.appendChild(html);
22478
22479 if (overlay.type) {
22480 classes(htmlContainer).add('djs-overlay-' + overlay.type);
22481 }
22482
22483 var elementRoot = this._canvas.findRoot(element);
22484 var activeRoot = this._canvas.getRootElement();
22485
22486 setVisible$1(htmlContainer, elementRoot === activeRoot);
22487
22488 overlay.htmlContainer = htmlContainer;
22489
22490 overlayContainer.overlays.push(overlay);
22491 overlayContainer.html.appendChild(htmlContainer);
22492
22493 this._overlays[id] = overlay;
22494
22495 this._updateOverlay(overlay);
22496 this._updateOverlayVisibilty(overlay, this._canvas.viewbox());
22497 };
22498
22499
22500 Overlays.prototype._updateOverlayVisibilty = function(overlay, viewbox) {
22501 var show = overlay.show,
22502 rootElement = this._canvas.findRoot(overlay.element),
22503 minZoom = show && show.minZoom,
22504 maxZoom = show && show.maxZoom,
22505 htmlContainer = overlay.htmlContainer,
22506 activeRootElement = this._canvas.getRootElement(),
22507 visible = true;
22508
22509 if (rootElement !== activeRootElement) {
22510 visible = false;
22511 } else if (show) {
22512 if (
22513 (isDefined(minZoom) && minZoom > viewbox.scale) ||
22514 (isDefined(maxZoom) && maxZoom < viewbox.scale)
22515 ) {
22516 visible = false;
22517 }
22518 }
22519
22520 setVisible$1(htmlContainer, visible);
22521
22522 this._updateOverlayScale(overlay, viewbox);
22523 };
22524
22525
22526 Overlays.prototype._updateOverlayScale = function(overlay, viewbox) {
22527 var shouldScale = overlay.scale,
22528 minScale,
22529 maxScale,
22530 htmlContainer = overlay.htmlContainer;
22531
22532 var scale, transform = '';
22533
22534 if (shouldScale !== true) {
22535
22536 if (shouldScale === false) {
22537 minScale = 1;
22538 maxScale = 1;
22539 } else {
22540 minScale = shouldScale.min;
22541 maxScale = shouldScale.max;
22542 }
22543
22544 if (isDefined(minScale) && viewbox.scale < minScale) {
22545 scale = (1 / viewbox.scale || 1) * minScale;
22546 }
22547
22548 if (isDefined(maxScale) && viewbox.scale > maxScale) {
22549 scale = (1 / viewbox.scale || 1) * maxScale;
22550 }
22551 }
22552
22553 if (isDefined(scale)) {
22554 transform = 'scale(' + scale + ',' + scale + ')';
22555 }
22556
22557 setTransform$1(htmlContainer, transform);
22558 };
22559
22560
22561 Overlays.prototype._updateOverlaysVisibilty = function(viewbox) {
22562
22563 var self = this;
22564
22565 forEach$2(this._overlays, function(overlay) {
22566 self._updateOverlayVisibilty(overlay, viewbox);
22567 });
22568 };
22569
22570
22571 Overlays.prototype._init = function() {
22572
22573 var eventBus = this._eventBus;
22574
22575 var self = this;
22576
22577
22578 // scroll/zoom integration
22579
22580 function updateViewbox(viewbox) {
22581 self._updateRoot(viewbox);
22582 self._updateOverlaysVisibilty(viewbox);
22583
22584 self.show();
22585 }
22586
22587 eventBus.on('canvas.viewbox.changing', function(event) {
22588 self.hide();
22589 });
22590
22591 eventBus.on('canvas.viewbox.changed', function(event) {
22592 updateViewbox(event.viewbox);
22593 });
22594
22595
22596 // remove integration
22597
22598 eventBus.on([ 'shape.remove', 'connection.remove' ], function(e) {
22599 var element = e.element;
22600 var overlays = self.get({ element: element });
22601
22602 forEach$2(overlays, function(o) {
22603 self.remove(o.id);
22604 });
22605
22606 var container = self._getOverlayContainer(element);
22607
22608 if (container) {
22609 remove$1(container.html);
22610 var i = self._overlayContainers.indexOf(container);
22611 if (i !== -1) {
22612 self._overlayContainers.splice(i, 1);
22613 }
22614 }
22615 });
22616
22617
22618 // move integration
22619
22620 eventBus.on('element.changed', LOW_PRIORITY$m, function(e) {
22621 var element = e.element;
22622
22623 var container = self._getOverlayContainer(element, true);
22624
22625 if (container) {
22626 forEach$2(container.overlays, function(overlay) {
22627 self._updateOverlay(overlay);
22628 });
22629
22630 self._updateOverlayContainer(container);
22631 }
22632 });
22633
22634
22635 // marker integration, simply add them on the overlays as classes, too.
22636
22637 eventBus.on('element.marker.update', function(e) {
22638 var container = self._getOverlayContainer(e.element, true);
22639 if (container) {
22640 classes(container.html)[e.add ? 'add' : 'remove'](e.marker);
22641 }
22642 });
22643
22644
22645 eventBus.on('root.set', function() {
22646 self._updateOverlaysVisibilty(self._canvas.viewbox());
22647 });
22648
22649 // clear overlays with diagram
22650
22651 eventBus.on('diagram.clear', this.clear, this);
22652 };
22653
22654
22655
22656 // helpers /////////////////////////////
22657
22658 function createRoot$1(parentNode) {
22659 var root = domify(
22660 '<div class="djs-overlay-container" />'
22661 );
22662
22663 assign$1(root, {
22664 position: 'absolute',
22665 width: 0,
22666 height: 0
22667 });
22668
22669 parentNode.insertBefore(root, parentNode.firstChild);
22670
22671 return root;
22672 }
22673
22674 function setPosition$1(el, x, y) {
22675 assign$1(el, { left: x + 'px', top: y + 'px' });
22676 }
22677
22678 function setVisible$1(el, visible) {
22679 el.style.display = visible === false ? 'none' : '';
22680 }
22681
22682 function setTransform$1(el, transform) {
22683
22684 el.style['transform-origin'] = 'top left';
22685
22686 [ '', '-ms-', '-webkit-' ].forEach(function(prefix) {
22687 el.style[prefix + 'transform'] = transform;
22688 });
22689 }
22690
22691 var OverlaysModule = {
22692 __init__: [ 'overlays' ],
22693 overlays: [ 'type', Overlays ]
22694 };
22695
22696 /**
22697 * Adds change support to the diagram, including
22698 *
22699 * <ul>
22700 * <li>redrawing shapes and connections on change</li>
22701 * </ul>
22702 *
22703 * @param {EventBus} eventBus
22704 * @param {Canvas} canvas
22705 * @param {ElementRegistry} elementRegistry
22706 * @param {GraphicsFactory} graphicsFactory
22707 */
22708 function ChangeSupport(
22709 eventBus, canvas, elementRegistry,
22710 graphicsFactory) {
22711
22712
22713 // redraw shapes / connections on change
22714
22715 eventBus.on('element.changed', function(event) {
22716
22717 var element = event.element;
22718
22719 // element might have been deleted and replaced by new element with same ID
22720 // thus check for parent of element except for root element
22721 if (element.parent || element === canvas.getRootElement()) {
22722 event.gfx = elementRegistry.getGraphics(element);
22723 }
22724
22725 // shape + gfx may have been deleted
22726 if (!event.gfx) {
22727 return;
22728 }
22729
22730 eventBus.fire(getType(element) + '.changed', event);
22731 });
22732
22733 eventBus.on('elements.changed', function(event) {
22734
22735 var elements = event.elements;
22736
22737 elements.forEach(function(e) {
22738 eventBus.fire('element.changed', { element: e });
22739 });
22740
22741 graphicsFactory.updateContainments(elements);
22742 });
22743
22744 eventBus.on('shape.changed', function(event) {
22745 graphicsFactory.update('shape', event.element, event.gfx);
22746 });
22747
22748 eventBus.on('connection.changed', function(event) {
22749 graphicsFactory.update('connection', event.element, event.gfx);
22750 });
22751 }
22752
22753 ChangeSupport.$inject = [
22754 'eventBus',
22755 'canvas',
22756 'elementRegistry',
22757 'graphicsFactory'
22758 ];
22759
22760 var ChangeSupportModule = {
22761 __init__: [ 'changeSupport' ],
22762 changeSupport: [ 'type', ChangeSupport ]
22763 };
22764
22765 var DEFAULT_PRIORITY$4 = 1000;
22766
22767 /**
22768 * A utility that can be used to plug-in into the command execution for
22769 * extension and/or validation.
22770 *
22771 * @param {EventBus} eventBus
22772 *
22773 * @example
22774 *
22775 * import inherits from 'inherits';
22776 *
22777 * import CommandInterceptor from 'diagram-js/lib/command/CommandInterceptor';
22778 *
22779 * function CommandLogger(eventBus) {
22780 * CommandInterceptor.call(this, eventBus);
22781 *
22782 * this.preExecute(function(event) {
22783 * console.log('command pre-execute', event);
22784 * });
22785 * }
22786 *
22787 * inherits(CommandLogger, CommandInterceptor);
22788 *
22789 */
22790 function CommandInterceptor(eventBus) {
22791 this._eventBus = eventBus;
22792 }
22793
22794 CommandInterceptor.$inject = [ 'eventBus' ];
22795
22796 function unwrapEvent(fn, that) {
22797 return function(event) {
22798 return fn.call(that || null, event.context, event.command, event);
22799 };
22800 }
22801
22802 /**
22803 * Register an interceptor for a command execution
22804 *
22805 * @param {string|Array<string>} [events] list of commands to register on
22806 * @param {string} [hook] command hook, i.e. preExecute, executed to listen on
22807 * @param {number} [priority] the priority on which to hook into the execution
22808 * @param {Function} handlerFn interceptor to be invoked with (event)
22809 * @param {boolean} unwrap if true, unwrap the event and pass (context, command, event) to the
22810 * listener instead
22811 * @param {Object} [that] Pass context (`this`) to the handler function
22812 */
22813 CommandInterceptor.prototype.on = function(events, hook, priority, handlerFn, unwrap, that) {
22814
22815 if (isFunction(hook) || isNumber(hook)) {
22816 that = unwrap;
22817 unwrap = handlerFn;
22818 handlerFn = priority;
22819 priority = hook;
22820 hook = null;
22821 }
22822
22823 if (isFunction(priority)) {
22824 that = unwrap;
22825 unwrap = handlerFn;
22826 handlerFn = priority;
22827 priority = DEFAULT_PRIORITY$4;
22828 }
22829
22830 if (isObject(unwrap)) {
22831 that = unwrap;
22832 unwrap = false;
22833 }
22834
22835 if (!isFunction(handlerFn)) {
22836 throw new Error('handlerFn must be a function');
22837 }
22838
22839 if (!isArray$4(events)) {
22840 events = [ events ];
22841 }
22842
22843 var eventBus = this._eventBus;
22844
22845 forEach$2(events, function(event) {
22846
22847 // concat commandStack(.event)?(.hook)?
22848 var fullEvent = [ 'commandStack', event, hook ].filter(function(e) { return e; }).join('.');
22849
22850 eventBus.on(fullEvent, priority, unwrap ? unwrapEvent(handlerFn, that) : handlerFn, that);
22851 });
22852 };
22853
22854
22855 var hooks = [
22856 'canExecute',
22857 'preExecute',
22858 'preExecuted',
22859 'execute',
22860 'executed',
22861 'postExecute',
22862 'postExecuted',
22863 'revert',
22864 'reverted'
22865 ];
22866
22867 /*
22868 * Install hook shortcuts
22869 *
22870 * This will generate the CommandInterceptor#(preExecute|...|reverted) methods
22871 * which will in term forward to CommandInterceptor#on.
22872 */
22873 forEach$2(hooks, function(hook) {
22874
22875 /**
22876 * {canExecute|preExecute|preExecuted|execute|executed|postExecute|postExecuted|revert|reverted}
22877 *
22878 * A named hook for plugging into the command execution
22879 *
22880 * @param {string|Array<string>} [events] list of commands to register on
22881 * @param {number} [priority] the priority on which to hook into the execution
22882 * @param {Function} handlerFn interceptor to be invoked with (event)
22883 * @param {boolean} [unwrap=false] if true, unwrap the event and pass (context, command, event) to the
22884 * listener instead
22885 * @param {Object} [that] Pass context (`this`) to the handler function
22886 */
22887 CommandInterceptor.prototype[hook] = function(events, priority, handlerFn, unwrap, that) {
22888
22889 if (isFunction(events) || isNumber(events)) {
22890 that = unwrap;
22891 unwrap = handlerFn;
22892 handlerFn = priority;
22893 priority = events;
22894 events = null;
22895 }
22896
22897 this.on(events, hook, priority, handlerFn, unwrap, that);
22898 };
22899 });
22900
22901 /**
22902 * A modeling behavior that ensures we set the correct root element
22903 * as we undo and redo commands.
22904 *
22905 * @param {Canvas} canvas
22906 * @param {didi.Injector} injector
22907 */
22908 function RootElementsBehavior(canvas, injector) {
22909
22910 injector.invoke(CommandInterceptor, this);
22911
22912 this.executed(function(event) {
22913 var context = event.context;
22914
22915 if (context.rootElement) {
22916 canvas.setRootElement(context.rootElement);
22917 } else {
22918 context.rootElement = canvas.getRootElement();
22919 }
22920 });
22921
22922 this.revert(function(event) {
22923 var context = event.context;
22924
22925 if (context.rootElement) {
22926 canvas.setRootElement(context.rootElement);
22927 }
22928 });
22929 }
22930
22931 inherits$1(RootElementsBehavior, CommandInterceptor);
22932
22933 RootElementsBehavior.$inject = [ 'canvas', 'injector' ];
22934
22935 var RootElementsModule = {
22936 __init__: [ 'rootElementsBehavior' ],
22937 rootElementsBehavior: [ 'type', RootElementsBehavior ]
22938 };
22939
22940 var css_escape = {exports: {}};
22941
22942 /*! https://mths.be/cssescape v1.5.1 by @mathias | MIT license */
22943
22944 (function (module, exports) {
22945 (function(root, factory) {
22946 // https://github.com/umdjs/umd/blob/master/returnExports.js
22947 {
22948 // For Node.js.
22949 module.exports = factory(root);
22950 }
22951 }(typeof commonjsGlobal != 'undefined' ? commonjsGlobal : commonjsGlobal, function(root) {
22952
22953 if (root.CSS && root.CSS.escape) {
22954 return root.CSS.escape;
22955 }
22956
22957 // https://drafts.csswg.org/cssom/#serialize-an-identifier
22958 var cssEscape = function(value) {
22959 if (arguments.length == 0) {
22960 throw new TypeError('`CSS.escape` requires an argument.');
22961 }
22962 var string = String(value);
22963 var length = string.length;
22964 var index = -1;
22965 var codeUnit;
22966 var result = '';
22967 var firstCodeUnit = string.charCodeAt(0);
22968 while (++index < length) {
22969 codeUnit = string.charCodeAt(index);
22970 // Note: there’s no need to special-case astral symbols, surrogate
22971 // pairs, or lone surrogates.
22972
22973 // If the character is NULL (U+0000), then the REPLACEMENT CHARACTER
22974 // (U+FFFD).
22975 if (codeUnit == 0x0000) {
22976 result += '\uFFFD';
22977 continue;
22978 }
22979
22980 if (
22981 // If the character is in the range [\1-\1F] (U+0001 to U+001F) or is
22982 // U+007F, […]
22983 (codeUnit >= 0x0001 && codeUnit <= 0x001F) || codeUnit == 0x007F ||
22984 // If the character is the first character and is in the range [0-9]
22985 // (U+0030 to U+0039), […]
22986 (index == 0 && codeUnit >= 0x0030 && codeUnit <= 0x0039) ||
22987 // If the character is the second character and is in the range [0-9]
22988 // (U+0030 to U+0039) and the first character is a `-` (U+002D), […]
22989 (
22990 index == 1 &&
22991 codeUnit >= 0x0030 && codeUnit <= 0x0039 &&
22992 firstCodeUnit == 0x002D
22993 )
22994 ) {
22995 // https://drafts.csswg.org/cssom/#escape-a-character-as-code-point
22996 result += '\\' + codeUnit.toString(16) + ' ';
22997 continue;
22998 }
22999
23000 if (
23001 // If the character is the first character and is a `-` (U+002D), and
23002 // there is no second character, […]
23003 index == 0 &&
23004 length == 1 &&
23005 codeUnit == 0x002D
23006 ) {
23007 result += '\\' + string.charAt(index);
23008 continue;
23009 }
23010
23011 // If the character is not handled by one of the above rules and is
23012 // greater than or equal to U+0080, is `-` (U+002D) or `_` (U+005F), or
23013 // is in one of the ranges [0-9] (U+0030 to U+0039), [A-Z] (U+0041 to
23014 // U+005A), or [a-z] (U+0061 to U+007A), […]
23015 if (
23016 codeUnit >= 0x0080 ||
23017 codeUnit == 0x002D ||
23018 codeUnit == 0x005F ||
23019 codeUnit >= 0x0030 && codeUnit <= 0x0039 ||
23020 codeUnit >= 0x0041 && codeUnit <= 0x005A ||
23021 codeUnit >= 0x0061 && codeUnit <= 0x007A
23022 ) {
23023 // the character itself
23024 result += string.charAt(index);
23025 continue;
23026 }
23027
23028 // Otherwise, the escaped character.
23029 // https://drafts.csswg.org/cssom/#escape-a-character
23030 result += '\\' + string.charAt(index);
23031
23032 }
23033 return result;
23034 };
23035
23036 if (!root.CSS) {
23037 root.CSS = {};
23038 }
23039
23040 root.CSS.escape = cssEscape;
23041 return cssEscape;
23042
23043 }));
23044 }(css_escape));
23045
23046 var cssEscape = css_escape.exports;
23047
23048 var HTML_ESCAPE_MAP = {
23049 '&': '&amp;',
23050 '<': '&lt;',
23051 '>': '&gt;',
23052 '"': '&quot;',
23053 '\'': '&#39;'
23054 };
23055
23056 function escapeHTML(str) {
23057 str = '' + str;
23058
23059 return str && str.replace(/[&<>"']/g, function(match) {
23060 return HTML_ESCAPE_MAP[match];
23061 });
23062 }
23063
23064 var planeSuffix = '_plane';
23065
23066 /**
23067 * Get primary shape ID for a plane.
23068 *
23069 * @param {djs.model.Base|ModdleElement} element
23070 *
23071 * @returns {String}
23072 */
23073 function getShapeIdFromPlane(element) {
23074 var id = element.id;
23075
23076 return removePlaneSuffix(id);
23077 }
23078
23079 /**
23080 * Get plane ID for a primary shape.
23081 *
23082 * @param {djs.model.Base|ModdleElement} element
23083 *
23084 * @returns {String}
23085 */
23086 function getPlaneIdFromShape(element) {
23087 var id = element.id;
23088
23089 if (is$1(element, 'bpmn:SubProcess')) {
23090 return addPlaneSuffix(id);
23091 }
23092
23093 return id;
23094 }
23095
23096 /**
23097 * Get plane ID for primary shape ID.
23098 *
23099 * @param {String} id
23100 *
23101 * @returns {String}
23102 */
23103 function toPlaneId(id) {
23104 return addPlaneSuffix(id);
23105 }
23106
23107 /**
23108 * Check wether element is plane.
23109 *
23110 * @param {djs.model.Base|ModdleElement} element
23111 *
23112 * @returns {Boolean}
23113 */
23114 function isPlane(element) {
23115 var di = getDi(element);
23116
23117 return is$1(di, 'bpmndi:BPMNPlane');
23118 }
23119
23120 function addPlaneSuffix(id) {
23121 return id + planeSuffix;
23122 }
23123
23124 function removePlaneSuffix(id) {
23125 return id.replace(new RegExp(planeSuffix + '$'), '');
23126 }
23127
23128 var OPEN_CLASS = 'bjs-breadcrumbs-shown';
23129
23130
23131 /**
23132 * Adds overlays that allow switching planes on collapsed subprocesses.
23133 *
23134 * @param {eventBus} eventBus
23135 * @param {elementRegistry} elementRegistry
23136 * @param {overlays} overlays
23137 * @param {canvas} canvas
23138 */
23139 function DrilldownBreadcrumbs(eventBus, elementRegistry, overlays, canvas) {
23140 var breadcrumbs = domify$1('<ul class="bjs-breadcrumbs"></ul>');
23141 var container = canvas.getContainer();
23142 var containerClasses = classes$2(container);
23143 container.appendChild(breadcrumbs);
23144
23145 var boParents = [];
23146
23147 // update breadcrumbs if name or ID of the primary shape changes
23148 eventBus.on('element.changed', function(e) {
23149 var shape = e.element,
23150 bo = getBusinessObject(shape);
23151
23152 var isPresent = find(boParents, function(el) {
23153 return el === bo;
23154 });
23155
23156 if (!isPresent) {
23157 return;
23158 }
23159
23160 updateBreadcrumbs();
23161 });
23162
23163 /**
23164 * Updates the displayed breadcrumbs. If no element is provided, only the
23165 * labels are updated.
23166 *
23167 * @param {djs.model.Base} [element]
23168 */
23169 function updateBreadcrumbs(element) {
23170 if (element) {
23171 boParents = getBoParentChain(element);
23172 }
23173
23174 var path = boParents.map(function(parent) {
23175 var title = escapeHTML(parent.name || parent.id);
23176 var link = domify$1('<li><span class="bjs-crumb"><a title="' + title + '">' + title + '</a></span></li>');
23177
23178 var parentPlane = canvas.findRoot(getPlaneIdFromShape(parent)) || canvas.findRoot(parent.id);
23179
23180 // when the root is a collaboration, the process does not have a corresponding
23181 // element in the elementRegisty. Instead, we search for the corresponding participant
23182 if (!parentPlane && is$1(parent, 'bpmn:Process')) {
23183 var participant = elementRegistry.find(function(element) {
23184 var bo = getBusinessObject(element);
23185 return bo && bo.processRef && bo.processRef === parent;
23186 });
23187
23188 parentPlane = canvas.findRoot(participant.id);
23189 }
23190
23191 link.addEventListener('click', function() {
23192 canvas.setRootElement(parentPlane);
23193 });
23194
23195 return link;
23196 });
23197
23198 breadcrumbs.innerHTML = '';
23199
23200 // show breadcrumbs and expose state to .djs-container
23201 var visible = path.length > 1;
23202 containerClasses.toggle(OPEN_CLASS, visible);
23203
23204 path.forEach(function(el) {
23205 breadcrumbs.appendChild(el);
23206 });
23207 }
23208
23209 eventBus.on('root.set', function(event) {
23210 updateBreadcrumbs(event.element);
23211 });
23212
23213 }
23214
23215 DrilldownBreadcrumbs.$inject = [ 'eventBus', 'elementRegistry', 'overlays', 'canvas' ];
23216
23217
23218 // helpers //////////
23219
23220 /**
23221 * Returns the parents for the element using the business object chain,
23222 * starting with the root element.
23223 *
23224 * @param {djs.model.Shape} child
23225 *
23226 * @returns {Array<djs.model.Shape>} parents
23227 */
23228 function getBoParentChain(child) {
23229 var bo = getBusinessObject(child);
23230
23231 var parents = [];
23232
23233 for (var element = bo; element; element = element.$parent) {
23234 if (is$1(element, 'bpmn:SubProcess') || is$1(element, 'bpmn:Process')) {
23235 parents.push(element);
23236 }
23237 }
23238
23239 return parents.reverse();
23240 }
23241
23242 /**
23243 * Move collapsed subprocesses into view when drilling down.
23244 *
23245 * Zoom and scroll are saved in a session.
23246 *
23247 * @param {eventBus} eventBus
23248 * @param {canvas} canvas
23249 */
23250 function DrilldownCentering(eventBus, canvas) {
23251
23252 var currentRoot = null;
23253 var positionMap = new Map();
23254
23255 eventBus.on('root.set', function(event) {
23256 var newRoot = event.element;
23257 var currentViewbox = canvas.viewbox();
23258 var storedViewbox = positionMap.get(newRoot);
23259
23260 positionMap.set(currentRoot, {
23261 x: currentViewbox.x,
23262 y: currentViewbox.y,
23263 zoom: currentViewbox.scale
23264 });
23265
23266 currentRoot = newRoot;
23267
23268 // current root was replaced with a collaboration, we don't update the viewbox
23269 if (is$1(newRoot, 'bpmn:Collaboration') && !storedViewbox) {
23270 return;
23271 }
23272
23273 storedViewbox = storedViewbox || { x: 0, y: 0, zoom: 1 };
23274
23275 var dx = (currentViewbox.x - storedViewbox.x) * currentViewbox.scale,
23276 dy = (currentViewbox.y - storedViewbox.y) * currentViewbox.scale;
23277
23278 if (dx !== 0 || dy !== 0) {
23279 canvas.scroll({
23280 dx: dx,
23281 dy: dy
23282 });
23283 }
23284
23285 if (storedViewbox.zoom !== currentViewbox.scale) {
23286 canvas.zoom(storedViewbox.zoom, { x: 0, y: 0 });
23287 }
23288 });
23289
23290 eventBus.on('diagram.clear', function() {
23291 positionMap.clear();
23292 currentRoot = null;
23293 });
23294
23295 }
23296
23297 DrilldownCentering.$inject = [ 'eventBus', 'canvas' ];
23298
23299
23300 /**
23301 * ES5 Map implementation. Works.
23302 */
23303 function Map() {
23304
23305 this._entries = [];
23306
23307 this.set = function(key, value) {
23308
23309 var found = false;
23310
23311 for (var k in this._entries) {
23312 if (this._entries[k][0] === key) {
23313 this._entries[k][1] = value;
23314
23315 found = true;
23316
23317 break;
23318 }
23319 }
23320
23321 if (!found) {
23322 this._entries.push([ key, value ]);
23323 }
23324 };
23325
23326 this.get = function(key) {
23327
23328 for (var k in this._entries) {
23329 if (this._entries[k][0] === key) {
23330 return this._entries[k][1];
23331 }
23332 }
23333
23334 return null;
23335 };
23336
23337 this.clear = function() {
23338 this._entries.length = 0;
23339 };
23340
23341 this.remove = function(key) {
23342
23343 var idx = -1;
23344
23345 for (var k in this._entries) {
23346 if (this._entries[k][0] === key) {
23347 idx = k;
23348
23349 break;
23350 }
23351 }
23352
23353 if (idx !== -1) {
23354 this._entries.splice(idx, 1);
23355 }
23356 };
23357 }
23358
23359 var DEFAULT_POSITION$1 = {
23360 x: 180,
23361 y: 160
23362 };
23363
23364 /**
23365 * Hook into `import.render.start` and create new planes for diagrams with
23366 * collapsed subprocesses and all dis on the same plane.
23367 *
23368 * @param {eventBus} eventBus
23369 * @param {moddle} moddle
23370 */
23371 function SubprocessCompatibility(eventBus, moddle) {
23372 this._eventBus = eventBus;
23373 this._moddle = moddle;
23374
23375 var self = this;
23376
23377 eventBus.on('import.render.start', 1500, function(e, context) {
23378 self.handleImport(context.definitions);
23379 });
23380 }
23381
23382 SubprocessCompatibility.prototype.handleImport = function(definitions) {
23383 if (!definitions.diagrams) {
23384 return;
23385 }
23386
23387 var self = this;
23388 this._definitions = definitions;
23389 this._processToDiagramMap = {};
23390
23391 definitions.diagrams.forEach(function(diagram) {
23392 if (!diagram.plane || !diagram.plane.bpmnElement) {
23393 return;
23394 }
23395
23396 self._processToDiagramMap[diagram.plane.bpmnElement.id] = diagram;
23397 });
23398
23399 var newDiagrams = [];
23400 definitions.diagrams.forEach(function(diagram) {
23401 var createdDiagrams = self.createNewDiagrams(diagram.plane);
23402 Array.prototype.push.apply(newDiagrams, createdDiagrams);
23403 });
23404
23405 newDiagrams.forEach(function(diagram) {
23406 self.movePlaneElementsToOrigin(diagram.plane);
23407 });
23408 };
23409
23410
23411 /**
23412 * Moves all DI elements from collapsed subprocesses to a new plane.
23413 *
23414 * @param {Object} plane
23415 * @return {Array} new diagrams created for the collapsed subprocesses
23416 */
23417 SubprocessCompatibility.prototype.createNewDiagrams = function(plane) {
23418 var self = this;
23419
23420 var collapsedElements = [];
23421 var elementsToMove = [];
23422
23423 plane.get('planeElement').forEach(function(diElement) {
23424 var bo = diElement.bpmnElement;
23425
23426 if (!bo) {
23427 return;
23428 }
23429
23430 var parent = bo.$parent;
23431
23432 if (is$1(bo, 'bpmn:SubProcess') && !diElement.isExpanded) {
23433 collapsedElements.push(bo);
23434 }
23435
23436 if (shouldMoveToPlane(bo, plane)) {
23437
23438 // don't change the array while we iterate over it
23439 elementsToMove.push({ diElement: diElement, parent: parent });
23440 }
23441 });
23442
23443 var newDiagrams = [];
23444
23445 // create new planes for all collapsed subprocesses, even when they are empty
23446 collapsedElements.forEach(function(element) {
23447 if (!self._processToDiagramMap[element.id]) {
23448 var diagram = self.createDiagram(element);
23449 self._processToDiagramMap[element.id] = diagram;
23450 newDiagrams.push(diagram);
23451 }
23452 });
23453
23454 elementsToMove.forEach(function(element) {
23455 var diElement = element.diElement;
23456 var parent = element.parent;
23457
23458 // parent is expanded, get nearest collapsed parent
23459 while (parent && collapsedElements.indexOf(parent) === -1) {
23460 parent = parent.$parent;
23461 }
23462
23463 // false positive, all parents are expanded
23464 if (!parent) {
23465 return;
23466 }
23467
23468 var diagram = self._processToDiagramMap[parent.id];
23469 self.moveToDiPlane(diElement, diagram.plane);
23470 });
23471
23472 return newDiagrams;
23473 };
23474
23475 SubprocessCompatibility.prototype.movePlaneElementsToOrigin = function(plane) {
23476 var elements = plane.get('planeElement');
23477
23478 // get bounding box of all elements
23479 var planeBounds = getPlaneBounds(plane);
23480
23481 var offset = {
23482 x: planeBounds.x - DEFAULT_POSITION$1.x,
23483 y: planeBounds.y - DEFAULT_POSITION$1.y
23484 };
23485
23486 elements.forEach(function(diElement) {
23487 if (diElement.waypoint) {
23488 diElement.waypoint.forEach(function(waypoint) {
23489 waypoint.x = waypoint.x - offset.x;
23490 waypoint.y = waypoint.y - offset.y;
23491 });
23492 } else if (diElement.bounds) {
23493 diElement.bounds.x = diElement.bounds.x - offset.x;
23494 diElement.bounds.y = diElement.bounds.y - offset.y;
23495 }
23496 });
23497 };
23498
23499
23500 SubprocessCompatibility.prototype.moveToDiPlane = function(diElement, newPlane) {
23501 var containingDiagram = findRootDiagram(diElement);
23502
23503 // remove DI from old Plane and add it to the new one
23504 var parentPlaneElement = containingDiagram.plane.get('planeElement');
23505 parentPlaneElement.splice(parentPlaneElement.indexOf(diElement), 1);
23506 newPlane.get('planeElement').push(diElement);
23507 };
23508
23509
23510 SubprocessCompatibility.prototype.createDiagram = function(bo) {
23511 var plane = this._moddle.create('bpmndi:BPMNPlane', { bpmnElement: bo });
23512 var diagram = this._moddle.create('bpmndi:BPMNDiagram', {
23513 plane: plane
23514 });
23515 plane.$parent = diagram;
23516 plane.bpmnElement = bo;
23517 diagram.$parent = this._definitions;
23518 this._definitions.diagrams.push(diagram);
23519 return diagram;
23520 };
23521
23522 SubprocessCompatibility.$inject = [ 'eventBus', 'moddle' ];
23523
23524
23525 // helpers //////////////////////////
23526
23527 function findRootDiagram(element) {
23528 if (is$1(element, 'bpmndi:BPMNDiagram')) {
23529 return element;
23530 } else {
23531 return findRootDiagram(element.$parent);
23532 }
23533 }
23534
23535 function getPlaneBounds(plane) {
23536 var planeTrbl = {
23537 top: Infinity,
23538 right: -Infinity,
23539 bottom: -Infinity,
23540 left: Infinity
23541 };
23542
23543 plane.planeElement.forEach(function(element) {
23544 if (!element.bounds) {
23545 return;
23546 }
23547
23548 var trbl = asTRBL(element.bounds);
23549
23550 planeTrbl.top = Math.min(trbl.top, planeTrbl.top);
23551 planeTrbl.left = Math.min(trbl.left, planeTrbl.left);
23552 });
23553
23554 return asBounds(planeTrbl);
23555 }
23556
23557 function shouldMoveToPlane(bo, plane) {
23558 var parent = bo.$parent;
23559
23560 // don't move elements that are already on the plane
23561 if (!is$1(parent, 'bpmn:SubProcess') || parent === plane.bpmnElement) {
23562 return false;
23563 }
23564
23565 // dataAssociations are children of the subprocess but rendered on process level
23566 // cf. https://github.com/bpmn-io/bpmn-js/issues/1619
23567 if (isAny(bo, ['bpmn:DataInputAssociation', 'bpmn:DataOutputAssociation'])) {
23568 return false;
23569 }
23570
23571 return true;
23572 }
23573
23574 var LOW_PRIORITY$l = 250;
23575 var ARROW_DOWN_SVG = '<svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 16 16"><path fill-rule="evenodd" d="M4.81801948,3.50735931 L10.4996894,9.1896894 L10.5,4 L12,4 L12,12 L4,12 L4,10.5 L9.6896894,10.4996894 L3.75735931,4.56801948 C3.46446609,4.27512627 3.46446609,3.80025253 3.75735931,3.50735931 C4.05025253,3.21446609 4.52512627,3.21446609 4.81801948,3.50735931 Z"/></svg>';
23576
23577 var EMPTY_MARKER = 'bjs-drilldown-empty';
23578
23579 function DrilldownOverlayBehavior(
23580 canvas, eventBus, elementRegistry, overlays
23581 ) {
23582 CommandInterceptor.call(this, eventBus);
23583
23584 this._canvas = canvas;
23585 this._eventBus = eventBus;
23586 this._elementRegistry = elementRegistry;
23587 this._overlays = overlays;
23588
23589 var self = this;
23590
23591 this.executed('shape.toggleCollapse', LOW_PRIORITY$l, function(context) {
23592 var shape = context.shape;
23593
23594 // Add overlay to the collapsed shape
23595 if (self.canDrillDown(shape)) {
23596 self.addOverlay(shape);
23597 } else {
23598 self.removeOverlay(shape);
23599 }
23600 }, true);
23601
23602
23603 this.reverted('shape.toggleCollapse', LOW_PRIORITY$l, function(context) {
23604 var shape = context.shape;
23605
23606 // Add overlay to the collapsed shape
23607 if (self.canDrillDown(shape)) {
23608 self.addOverlay(shape);
23609 } else {
23610 self.removeOverlay(shape);
23611 }
23612 }, true);
23613
23614
23615 this.executed(['shape.create', 'shape.move', 'shape.delete'], LOW_PRIORITY$l,
23616 function(context) {
23617 var oldParent = context.oldParent,
23618 newParent = context.newParent || context.parent,
23619 shape = context.shape;
23620
23621 // Add overlay to the collapsed shape
23622 if (self.canDrillDown(shape)) {
23623 self.addOverlay(shape);
23624 }
23625
23626 self.updateDrilldownOverlay(oldParent);
23627 self.updateDrilldownOverlay(newParent);
23628 self.updateDrilldownOverlay(shape);
23629 }, true);
23630
23631
23632 this.reverted(['shape.create', 'shape.move', 'shape.delete'], LOW_PRIORITY$l,
23633 function(context) {
23634 var oldParent = context.oldParent,
23635 newParent = context.newParent || context.parent,
23636 shape = context.shape;
23637
23638 // Add overlay to the collapsed shape
23639 if (self.canDrillDown(shape)) {
23640 self.addOverlay(shape);
23641 }
23642
23643 self.updateDrilldownOverlay(oldParent);
23644 self.updateDrilldownOverlay(newParent);
23645 self.updateDrilldownOverlay(shape);
23646 }, true);
23647
23648
23649 eventBus.on('import.render.complete', function() {
23650 elementRegistry.filter(function(e) {
23651 return self.canDrillDown(e);
23652 }).map(function(el) {
23653 self.addOverlay(el);
23654 });
23655 });
23656
23657 }
23658
23659 inherits$1(DrilldownOverlayBehavior, CommandInterceptor);
23660
23661 DrilldownOverlayBehavior.prototype.updateDrilldownOverlay = function(shape) {
23662 var canvas = this._canvas;
23663
23664 if (!shape) {
23665 return;
23666 }
23667
23668 var root = canvas.findRoot(shape);
23669 if (root) {
23670 this.updateOverlayVisibility(root);
23671 }
23672 };
23673
23674
23675 DrilldownOverlayBehavior.prototype.canDrillDown = function(element) {
23676 var canvas = this._canvas;
23677 return is$1(element, 'bpmn:SubProcess') && canvas.findRoot(getPlaneIdFromShape(element));
23678 };
23679
23680 /**
23681 * Updates visibility of the drilldown overlay. If the plane has no elements,
23682 * the drilldown will be only shown when the element is selected.
23683 *
23684 * @param {djs.model.Shape|djs.model.Root} element collapsed shape or root element
23685 */
23686 DrilldownOverlayBehavior.prototype.updateOverlayVisibility = function(element) {
23687 var overlays = this._overlays;
23688
23689 var bo = element.businessObject;
23690
23691 var overlay = overlays.get({ element: bo.id, type: 'drilldown' })[0];
23692
23693 if (!overlay) {
23694 return;
23695 }
23696
23697 var hasContent = bo && bo.flowElements && bo.flowElements.length;
23698 classes$2(overlay.html).toggle(EMPTY_MARKER, !hasContent);
23699 };
23700
23701 /**
23702 * Attaches a drilldown button to the given element. We assume that the plane has
23703 * the same id as the element.
23704 *
23705 * @param {djs.model.Shape} element collapsed shape
23706 */
23707 DrilldownOverlayBehavior.prototype.addOverlay = function(element) {
23708 var canvas = this._canvas;
23709 var overlays = this._overlays;
23710
23711 var existingOverlays = overlays.get({ element: element, type: 'drilldown' });
23712 if (existingOverlays.length) {
23713 this.removeOverlay(element);
23714 }
23715
23716 var button = domify$1('<button class="bjs-drilldown">' + ARROW_DOWN_SVG + '</button>');
23717
23718 button.addEventListener('click', function() {
23719 canvas.setRootElement(canvas.findRoot(getPlaneIdFromShape(element)));
23720 });
23721
23722 overlays.add(element, 'drilldown', {
23723 position: {
23724 bottom: -7,
23725 right: -8
23726 },
23727 html: button
23728 });
23729
23730 this.updateOverlayVisibility(element);
23731 };
23732
23733 DrilldownOverlayBehavior.prototype.removeOverlay = function(element) {
23734 var overlays = this._overlays;
23735
23736 overlays.remove({
23737 element: element,
23738 type: 'drilldown'
23739 });
23740 };
23741
23742 DrilldownOverlayBehavior.$inject = [
23743 'canvas',
23744 'eventBus',
23745 'elementRegistry',
23746 'overlays'
23747 ];
23748
23749 var DrilldownModdule = {
23750 __depends__: [ OverlaysModule, ChangeSupportModule, RootElementsModule ],
23751 __init__: [ 'drilldownBreadcrumbs', 'drilldownOverlayBehavior', 'drilldownCentering', 'subprocessCompatibility'],
23752 drilldownBreadcrumbs: [ 'type', DrilldownBreadcrumbs ],
23753 drilldownCentering: [ 'type', DrilldownCentering ],
23754 drilldownOverlayBehavior: [ 'type', DrilldownOverlayBehavior ],
23755 subprocessCompatibility: [ 'type', SubprocessCompatibility ]
23756 };
23757
23758 /**
23759 * A viewer for BPMN 2.0 diagrams.
23760 *
23761 * Have a look at {@link NavigatedViewer} or {@link Modeler} for bundles that include
23762 * additional features.
23763 *
23764 *
23765 * ## Extending the Viewer
23766 *
23767 * In order to extend the viewer pass extension modules to bootstrap via the
23768 * `additionalModules` option. An extension module is an object that exposes
23769 * named services.
23770 *
23771 * The following example depicts the integration of a simple
23772 * logging component that integrates with interaction events:
23773 *
23774 *
23775 * ```javascript
23776 *
23777 * // logging component
23778 * function InteractionLogger(eventBus) {
23779 * eventBus.on('element.hover', function(event) {
23780 * console.log()
23781 * })
23782 * }
23783 *
23784 * InteractionLogger.$inject = [ 'eventBus' ]; // minification save
23785 *
23786 * // extension module
23787 * var extensionModule = {
23788 * __init__: [ 'interactionLogger' ],
23789 * interactionLogger: [ 'type', InteractionLogger ]
23790 * };
23791 *
23792 * // extend the viewer
23793 * var bpmnViewer = new Viewer({ additionalModules: [ extensionModule ] });
23794 * bpmnViewer.importXML(...);
23795 * ```
23796 *
23797 * @param {Object} [options] configuration options to pass to the viewer
23798 * @param {DOMElement} [options.container] the container to render the viewer in, defaults to body.
23799 * @param {string|number} [options.width] the width of the viewer
23800 * @param {string|number} [options.height] the height of the viewer
23801 * @param {Object} [options.moddleExtensions] extension packages to provide
23802 * @param {Array<didi.Module>} [options.modules] a list of modules to override the default modules
23803 * @param {Array<didi.Module>} [options.additionalModules] a list of modules to use with the default modules
23804 */
23805 function Viewer(options) {
23806 BaseViewer.call(this, options);
23807 }
23808
23809 inherits$1(Viewer, BaseViewer);
23810
23811 // modules the viewer is composed of
23812 Viewer.prototype._modules = [
23813 CoreModule,
23814 translate,
23815 SelectionModule,
23816 OverlaysModule,
23817 DrilldownModdule
23818 ];
23819
23820 // default moddle extensions the viewer is composed of
23821 Viewer.prototype._moddleExtensions = {};
23822
23823 /**
23824 * Returns true if event was triggered with any modifier
23825 * @param {KeyboardEvent} event
23826 */
23827 function hasModifier(event) {
23828 return (event.ctrlKey || event.metaKey || event.shiftKey || event.altKey);
23829 }
23830
23831 /**
23832 * @param {KeyboardEvent} event
23833 */
23834 function isCmd(event) {
23835
23836 // ensure we don't react to AltGr
23837 // (mapped to CTRL + ALT)
23838 if (event.altKey) {
23839 return false;
23840 }
23841
23842 return event.ctrlKey || event.metaKey;
23843 }
23844
23845 /**
23846 * Checks if key pressed is one of provided keys.
23847 *
23848 * @param {string|Array<string>} keys
23849 * @param {KeyboardEvent} event
23850 */
23851 function isKey(keys, event) {
23852 keys = isArray$4(keys) ? keys : [ keys ];
23853
23854 return keys.indexOf(event.key) !== -1 || keys.indexOf(event.keyCode) !== -1;
23855 }
23856
23857 /**
23858 * @param {KeyboardEvent} event
23859 */
23860 function isShift(event) {
23861 return event.shiftKey;
23862 }
23863
23864 var KEYDOWN_EVENT = 'keyboard.keydown',
23865 KEYUP_EVENT = 'keyboard.keyup';
23866
23867 var HANDLE_MODIFIER_ATTRIBUTE = 'input-handle-modified-keys';
23868
23869 var DEFAULT_PRIORITY$3 = 1000;
23870
23871 /**
23872 * A keyboard abstraction that may be activated and
23873 * deactivated by users at will, consuming key events
23874 * and triggering diagram actions.
23875 *
23876 * For keys pressed down, keyboard fires `keyboard.keydown` event.
23877 * The event context contains one field which is `KeyboardEvent` event.
23878 *
23879 * The implementation fires the following key events that allow
23880 * other components to hook into key handling:
23881 *
23882 * - keyboard.bind
23883 * - keyboard.unbind
23884 * - keyboard.init
23885 * - keyboard.destroy
23886 *
23887 * All events contain one field which is node.
23888 *
23889 * A default binding for the keyboard may be specified via the
23890 * `keyboard.bindTo` configuration option.
23891 *
23892 * @param {Config} config
23893 * @param {EventBus} eventBus
23894 */
23895 function Keyboard(config, eventBus) {
23896 var self = this;
23897
23898 this._config = config || {};
23899 this._eventBus = eventBus;
23900
23901 this._keydownHandler = this._keydownHandler.bind(this);
23902 this._keyupHandler = this._keyupHandler.bind(this);
23903
23904 // properly clean dom registrations
23905 eventBus.on('diagram.destroy', function() {
23906 self._fire('destroy');
23907
23908 self.unbind();
23909 });
23910
23911 eventBus.on('diagram.init', function() {
23912 self._fire('init');
23913 });
23914
23915 eventBus.on('attach', function() {
23916 if (config && config.bindTo) {
23917 self.bind(config.bindTo);
23918 }
23919 });
23920
23921 eventBus.on('detach', function() {
23922 self.unbind();
23923 });
23924 }
23925
23926 Keyboard.$inject = [
23927 'config.keyboard',
23928 'eventBus'
23929 ];
23930
23931 Keyboard.prototype._keydownHandler = function(event) {
23932 this._keyHandler(event, KEYDOWN_EVENT);
23933 };
23934
23935 Keyboard.prototype._keyupHandler = function(event) {
23936 this._keyHandler(event, KEYUP_EVENT);
23937 };
23938
23939 Keyboard.prototype._keyHandler = function(event, type) {
23940 var eventBusResult;
23941
23942 if (this._isEventIgnored(event)) {
23943 return;
23944 }
23945
23946 var context = {
23947 keyEvent: event
23948 };
23949
23950 eventBusResult = this._eventBus.fire(type || KEYDOWN_EVENT, context);
23951
23952 if (eventBusResult) {
23953 event.preventDefault();
23954 }
23955 };
23956
23957 Keyboard.prototype._isEventIgnored = function(event) {
23958 return isInput(event.target) && this._isModifiedKeyIgnored(event);
23959 };
23960
23961 Keyboard.prototype._isModifiedKeyIgnored = function(event) {
23962 if (!isCmd(event)) {
23963 return true;
23964 }
23965
23966 var allowedModifiers = this._getAllowedModifiers(event.target);
23967 return allowedModifiers.indexOf(event.key) === -1;
23968 };
23969
23970 Keyboard.prototype._getAllowedModifiers = function(element) {
23971 var modifierContainer = closest(element, '[' + HANDLE_MODIFIER_ATTRIBUTE + ']', true);
23972
23973 if (!modifierContainer || (this._node && !this._node.contains(modifierContainer))) {
23974 return [];
23975 }
23976
23977 return modifierContainer.getAttribute(HANDLE_MODIFIER_ATTRIBUTE).split(',');
23978 };
23979
23980 Keyboard.prototype.bind = function(node) {
23981
23982 // make sure that the keyboard is only bound once to the DOM
23983 this.unbind();
23984
23985 this._node = node;
23986
23987 // bind key events
23988 componentEvent.bind(node, 'keydown', this._keydownHandler, true);
23989 componentEvent.bind(node, 'keyup', this._keyupHandler, true);
23990
23991 this._fire('bind');
23992 };
23993
23994 Keyboard.prototype.getBinding = function() {
23995 return this._node;
23996 };
23997
23998 Keyboard.prototype.unbind = function() {
23999 var node = this._node;
24000
24001 if (node) {
24002 this._fire('unbind');
24003
24004 // unbind key events
24005 componentEvent.unbind(node, 'keydown', this._keydownHandler, true);
24006 componentEvent.unbind(node, 'keyup', this._keyupHandler, true);
24007 }
24008
24009 this._node = null;
24010 };
24011
24012 Keyboard.prototype._fire = function(event) {
24013 this._eventBus.fire('keyboard.' + event, { node: this._node });
24014 };
24015
24016 /**
24017 * Add a listener function that is notified with `KeyboardEvent` whenever
24018 * the keyboard is bound and the user presses a key. If no priority is
24019 * provided, the default value of 1000 is used.
24020 *
24021 * @param {number} [priority]
24022 * @param {Function} listener
24023 * @param {string} type
24024 */
24025 Keyboard.prototype.addListener = function(priority, listener, type) {
24026 if (isFunction(priority)) {
24027 type = listener;
24028 listener = priority;
24029 priority = DEFAULT_PRIORITY$3;
24030 }
24031
24032 this._eventBus.on(type || KEYDOWN_EVENT, priority, listener);
24033 };
24034
24035 Keyboard.prototype.removeListener = function(listener, type) {
24036 this._eventBus.off(type || KEYDOWN_EVENT, listener);
24037 };
24038
24039 Keyboard.prototype.hasModifier = hasModifier;
24040 Keyboard.prototype.isCmd = isCmd;
24041 Keyboard.prototype.isShift = isShift;
24042 Keyboard.prototype.isKey = isKey;
24043
24044
24045
24046 // helpers ///////
24047
24048 function isInput(target) {
24049 return target && (matchesSelector(target, 'input, textarea') || target.contentEditable === 'true');
24050 }
24051
24052 var LOW_PRIORITY$k = 500;
24053
24054 var KEYCODE_C = 67;
24055 var KEYCODE_V = 86;
24056 var KEYCODE_Y = 89;
24057 var KEYCODE_Z = 90;
24058
24059 var KEYS_COPY = [ 'c', 'C', KEYCODE_C ];
24060 var KEYS_PASTE = [ 'v', 'V', KEYCODE_V ];
24061 var KEYS_REDO = [ 'y', 'Y', KEYCODE_Y ];
24062 var KEYS_UNDO = [ 'z', 'Z', KEYCODE_Z ];
24063
24064
24065 /**
24066 * Adds default keyboard bindings.
24067 *
24068 * This does not pull in any features will bind only actions that
24069 * have previously been registered against the editorActions component.
24070 *
24071 * @param {EventBus} eventBus
24072 * @param {Keyboard} keyboard
24073 */
24074 function KeyboardBindings(eventBus, keyboard) {
24075
24076 var self = this;
24077
24078 eventBus.on('editorActions.init', LOW_PRIORITY$k, function(event) {
24079
24080 var editorActions = event.editorActions;
24081
24082 self.registerBindings(keyboard, editorActions);
24083 });
24084 }
24085
24086 KeyboardBindings.$inject = [
24087 'eventBus',
24088 'keyboard'
24089 ];
24090
24091
24092 /**
24093 * Register available keyboard bindings.
24094 *
24095 * @param {Keyboard} keyboard
24096 * @param {EditorActions} editorActions
24097 */
24098 KeyboardBindings.prototype.registerBindings = function(keyboard, editorActions) {
24099
24100 /**
24101 * Add keyboard binding if respective editor action
24102 * is registered.
24103 *
24104 * @param {string} action name
24105 * @param {Function} fn that implements the key binding
24106 */
24107 function addListener(action, fn) {
24108
24109 if (editorActions.isRegistered(action)) {
24110 keyboard.addListener(fn);
24111 }
24112 }
24113
24114
24115 // undo
24116 // (CTRL|CMD) + Z
24117 addListener('undo', function(context) {
24118
24119 var event = context.keyEvent;
24120
24121 if (isCmd(event) && !isShift(event) && isKey(KEYS_UNDO, event)) {
24122 editorActions.trigger('undo');
24123
24124 return true;
24125 }
24126 });
24127
24128 // redo
24129 // CTRL + Y
24130 // CMD + SHIFT + Z
24131 addListener('redo', function(context) {
24132
24133 var event = context.keyEvent;
24134
24135 if (isCmd(event) && (isKey(KEYS_REDO, event) || (isKey(KEYS_UNDO, event) && isShift(event)))) {
24136 editorActions.trigger('redo');
24137
24138 return true;
24139 }
24140 });
24141
24142 // copy
24143 // CTRL/CMD + C
24144 addListener('copy', function(context) {
24145
24146 var event = context.keyEvent;
24147
24148 if (isCmd(event) && isKey(KEYS_COPY, event)) {
24149 editorActions.trigger('copy');
24150
24151 return true;
24152 }
24153 });
24154
24155 // paste
24156 // CTRL/CMD + V
24157 addListener('paste', function(context) {
24158
24159 var event = context.keyEvent;
24160
24161 if (isCmd(event) && isKey(KEYS_PASTE, event)) {
24162 editorActions.trigger('paste');
24163
24164 return true;
24165 }
24166 });
24167
24168 // zoom in one step
24169 // CTRL/CMD + +
24170 addListener('stepZoom', function(context) {
24171
24172 var event = context.keyEvent;
24173
24174 // quirk: it has to be triggered by `=` as well to work on international keyboard layout
24175 // cf: https://github.com/bpmn-io/bpmn-js/issues/1362#issuecomment-722989754
24176 if (isKey([ '+', 'Add', '=' ], event) && isCmd(event)) {
24177 editorActions.trigger('stepZoom', { value: 1 });
24178
24179 return true;
24180 }
24181 });
24182
24183 // zoom out one step
24184 // CTRL + -
24185 addListener('stepZoom', function(context) {
24186
24187 var event = context.keyEvent;
24188
24189 if (isKey([ '-', 'Subtract' ], event) && isCmd(event)) {
24190 editorActions.trigger('stepZoom', { value: -1 });
24191
24192 return true;
24193 }
24194 });
24195
24196 // zoom to the default level
24197 // CTRL + 0
24198 addListener('zoom', function(context) {
24199
24200 var event = context.keyEvent;
24201
24202 if (isKey('0', event) && isCmd(event)) {
24203 editorActions.trigger('zoom', { value: 1 });
24204
24205 return true;
24206 }
24207 });
24208
24209 // delete selected element
24210 // DEL
24211 addListener('removeSelection', function(context) {
24212
24213 var event = context.keyEvent;
24214
24215 if (isKey([ 'Backspace', 'Delete', 'Del' ], event)) {
24216 editorActions.trigger('removeSelection');
24217
24218 return true;
24219 }
24220 });
24221 };
24222
24223 var KeyboardModule$1 = {
24224 __init__: [ 'keyboard', 'keyboardBindings' ],
24225 keyboard: [ 'type', Keyboard ],
24226 keyboardBindings: [ 'type', KeyboardBindings ]
24227 };
24228
24229 var DEFAULT_CONFIG$1 = {
24230 moveSpeed: 50,
24231 moveSpeedAccelerated: 200
24232 };
24233
24234
24235 /**
24236 * A feature that allows users to move the canvas using the keyboard.
24237 *
24238 * @param {Object} config
24239 * @param {number} [config.moveSpeed=50]
24240 * @param {number} [config.moveSpeedAccelerated=200]
24241 * @param {Keyboard} keyboard
24242 * @param {Canvas} canvas
24243 */
24244 function KeyboardMove(
24245 config,
24246 keyboard,
24247 canvas
24248 ) {
24249
24250 var self = this;
24251
24252 this._config = assign({}, DEFAULT_CONFIG$1, config || {});
24253
24254 keyboard.addListener(arrowsListener);
24255
24256
24257 function arrowsListener(context) {
24258
24259 var event = context.keyEvent,
24260 config = self._config;
24261
24262 if (!keyboard.isCmd(event)) {
24263 return;
24264 }
24265
24266 if (keyboard.isKey([
24267 'ArrowLeft', 'Left',
24268 'ArrowUp', 'Up',
24269 'ArrowDown', 'Down',
24270 'ArrowRight', 'Right'
24271 ], event)) {
24272
24273 var speed = (
24274 keyboard.isShift(event) ?
24275 config.moveSpeedAccelerated :
24276 config.moveSpeed
24277 );
24278
24279 var direction;
24280
24281 switch (event.key) {
24282 case 'ArrowLeft':
24283 case 'Left':
24284 direction = 'left';
24285 break;
24286 case 'ArrowUp':
24287 case 'Up':
24288 direction = 'up';
24289 break;
24290 case 'ArrowRight':
24291 case 'Right':
24292 direction = 'right';
24293 break;
24294 case 'ArrowDown':
24295 case 'Down':
24296 direction = 'down';
24297 break;
24298 }
24299
24300 self.moveCanvas({
24301 speed: speed,
24302 direction: direction
24303 });
24304
24305 return true;
24306 }
24307 }
24308
24309 this.moveCanvas = function(opts) {
24310
24311 var dx = 0,
24312 dy = 0,
24313 speed = opts.speed;
24314
24315 var actualSpeed = speed / Math.min(Math.sqrt(canvas.viewbox().scale), 1);
24316
24317 switch (opts.direction) {
24318 case 'left': // Left
24319 dx = actualSpeed;
24320 break;
24321 case 'up': // Up
24322 dy = actualSpeed;
24323 break;
24324 case 'right': // Right
24325 dx = -actualSpeed;
24326 break;
24327 case 'down': // Down
24328 dy = -actualSpeed;
24329 break;
24330 }
24331
24332 canvas.scroll({
24333 dx: dx,
24334 dy: dy
24335 });
24336 };
24337
24338 }
24339
24340
24341 KeyboardMove.$inject = [
24342 'config.keyboardMove',
24343 'keyboard',
24344 'canvas'
24345 ];
24346
24347 var KeyboardMoveModule = {
24348 __depends__: [
24349 KeyboardModule$1
24350 ],
24351 __init__: [ 'keyboardMove' ],
24352 keyboardMove: [ 'type', KeyboardMove ]
24353 };
24354
24355 var CURSOR_CLS_PATTERN = /^djs-cursor-.*$/;
24356
24357
24358 function set(mode) {
24359 var classes$1 = classes(document.body);
24360
24361 classes$1.removeMatching(CURSOR_CLS_PATTERN);
24362
24363 if (mode) {
24364 classes$1.add('djs-cursor-' + mode);
24365 }
24366 }
24367
24368 function unset() {
24369 set(null);
24370 }
24371
24372 var TRAP_PRIORITY = 5000;
24373
24374 /**
24375 * Installs a click trap that prevents a ghost click following a dragging operation.
24376 *
24377 * @return {Function} a function to immediately remove the installed trap.
24378 */
24379 function install(eventBus, eventName) {
24380
24381 eventName = eventName || 'element.click';
24382
24383 function trap() {
24384 return false;
24385 }
24386
24387 eventBus.once(eventName, TRAP_PRIORITY, trap);
24388
24389 return function() {
24390 eventBus.off(eventName, trap);
24391 };
24392 }
24393
24394 function center(bounds) {
24395 return {
24396 x: bounds.x + (bounds.width / 2),
24397 y: bounds.y + (bounds.height / 2)
24398 };
24399 }
24400
24401
24402 function delta(a, b) {
24403 return {
24404 x: a.x - b.x,
24405 y: a.y - b.y
24406 };
24407 }
24408
24409 var THRESHOLD$1 = 15;
24410
24411
24412 /**
24413 * Move the canvas via mouse.
24414 *
24415 * @param {EventBus} eventBus
24416 * @param {Canvas} canvas
24417 */
24418 function MoveCanvas(eventBus, canvas) {
24419
24420 var context;
24421
24422
24423 // listen for move on element mouse down;
24424 // allow others to hook into the event before us though
24425 // (dragging / element moving will do this)
24426 eventBus.on('element.mousedown', 500, function(e) {
24427 return handleStart(e.originalEvent);
24428 });
24429
24430
24431 function handleMove(event) {
24432
24433 var start = context.start,
24434 button = context.button,
24435 position = toPoint(event),
24436 delta$1 = delta(position, start);
24437
24438 if (!context.dragging && length(delta$1) > THRESHOLD$1) {
24439 context.dragging = true;
24440
24441 if (button === 0) {
24442 install(eventBus);
24443 }
24444
24445 set('grab');
24446 }
24447
24448 if (context.dragging) {
24449
24450 var lastPosition = context.last || context.start;
24451
24452 delta$1 = delta(position, lastPosition);
24453
24454 canvas.scroll({
24455 dx: delta$1.x,
24456 dy: delta$1.y
24457 });
24458
24459 context.last = position;
24460 }
24461
24462 // prevent select
24463 event.preventDefault();
24464 }
24465
24466
24467 function handleEnd(event) {
24468 componentEvent.unbind(document, 'mousemove', handleMove);
24469 componentEvent.unbind(document, 'mouseup', handleEnd);
24470
24471 context = null;
24472
24473 unset();
24474 }
24475
24476 function handleStart(event) {
24477
24478 // event is already handled by '.djs-draggable'
24479 if (closest(event.target, '.djs-draggable')) {
24480 return;
24481 }
24482
24483 var button = event.button;
24484
24485 // reject right mouse button or modifier key
24486 if (button >= 2 || event.ctrlKey || event.shiftKey || event.altKey) {
24487 return;
24488 }
24489
24490 context = {
24491 button: button,
24492 start: toPoint(event)
24493 };
24494
24495 componentEvent.bind(document, 'mousemove', handleMove);
24496 componentEvent.bind(document, 'mouseup', handleEnd);
24497
24498 // we've handled the event
24499 return true;
24500 }
24501
24502 this.isActive = function() {
24503 return !!context;
24504 };
24505
24506 }
24507
24508
24509 MoveCanvas.$inject = [
24510 'eventBus',
24511 'canvas'
24512 ];
24513
24514
24515
24516 // helpers ///////
24517
24518 function length(point) {
24519 return Math.sqrt(Math.pow(point.x, 2) + Math.pow(point.y, 2));
24520 }
24521
24522 var MoveCanvasModule = {
24523 __init__: [ 'moveCanvas' ],
24524 moveCanvas: [ 'type', MoveCanvas ]
24525 };
24526
24527 /**
24528 * Get the logarithm of x with base 10
24529 * @param {Integer} value
24530 */
24531 function log10(x) {
24532 return Math.log(x) / Math.log(10);
24533 }
24534
24535 /**
24536 * Get step size for given range and number of steps.
24537 *
24538 * @param {Object} range
24539 * @param {number} range.min
24540 * @param {number} range.max
24541 */
24542 function getStepSize(range, steps) {
24543
24544 var minLinearRange = log10(range.min),
24545 maxLinearRange = log10(range.max);
24546
24547 var absoluteLinearRange = Math.abs(minLinearRange) + Math.abs(maxLinearRange);
24548
24549 return absoluteLinearRange / steps;
24550 }
24551
24552 function cap(range, scale) {
24553 return Math.max(range.min, Math.min(range.max, scale));
24554 }
24555
24556 var sign = Math.sign || function(n) {
24557 return n >= 0 ? 1 : -1;
24558 };
24559
24560 var RANGE = { min: 0.2, max: 4 },
24561 NUM_STEPS = 10;
24562
24563 var DELTA_THRESHOLD = 0.1;
24564
24565 var DEFAULT_SCALE = 0.75;
24566
24567 /**
24568 * An implementation of zooming and scrolling within the
24569 * {@link Canvas} via the mouse wheel.
24570 *
24571 * Mouse wheel zooming / scrolling may be disabled using
24572 * the {@link toggle(enabled)} method.
24573 *
24574 * @param {Object} [config]
24575 * @param {boolean} [config.enabled=true] default enabled state
24576 * @param {number} [config.scale=.75] scroll sensivity
24577 * @param {EventBus} eventBus
24578 * @param {Canvas} canvas
24579 */
24580 function ZoomScroll(config, eventBus, canvas) {
24581
24582 config = config || {};
24583
24584 this._enabled = false;
24585
24586 this._canvas = canvas;
24587 this._container = canvas._container;
24588
24589 this._handleWheel = bind(this._handleWheel, this);
24590
24591 this._totalDelta = 0;
24592 this._scale = config.scale || DEFAULT_SCALE;
24593
24594 var self = this;
24595
24596 eventBus.on('canvas.init', function(e) {
24597 self._init(config.enabled !== false);
24598 });
24599 }
24600
24601 ZoomScroll.$inject = [
24602 'config.zoomScroll',
24603 'eventBus',
24604 'canvas'
24605 ];
24606
24607 ZoomScroll.prototype.scroll = function scroll(delta) {
24608 this._canvas.scroll(delta);
24609 };
24610
24611
24612 ZoomScroll.prototype.reset = function reset() {
24613 this._canvas.zoom('fit-viewport');
24614 };
24615
24616 /**
24617 * Zoom depending on delta.
24618 *
24619 * @param {number} delta
24620 * @param {Object} position
24621 */
24622 ZoomScroll.prototype.zoom = function zoom(delta, position) {
24623
24624 // zoom with half the step size of stepZoom
24625 var stepSize = getStepSize(RANGE, NUM_STEPS * 2);
24626
24627 // add until threshold reached
24628 this._totalDelta += delta;
24629
24630 if (Math.abs(this._totalDelta) > DELTA_THRESHOLD) {
24631 this._zoom(delta, position, stepSize);
24632
24633 // reset
24634 this._totalDelta = 0;
24635 }
24636 };
24637
24638
24639 ZoomScroll.prototype._handleWheel = function handleWheel(event) {
24640
24641 // event is already handled by '.djs-scrollable'
24642 if (closest(event.target, '.djs-scrollable', true)) {
24643 return;
24644 }
24645
24646 var element = this._container;
24647
24648 event.preventDefault();
24649
24650 // pinch to zoom is mapped to wheel + ctrlKey = true
24651 // in modern browsers (!)
24652
24653 var isZoom = event.ctrlKey;
24654
24655 var isHorizontalScroll = event.shiftKey;
24656
24657 var factor = -1 * this._scale,
24658 delta;
24659
24660 if (isZoom) {
24661 factor *= event.deltaMode === 0 ? 0.020 : 0.32;
24662 } else {
24663 factor *= event.deltaMode === 0 ? 1.0 : 16.0;
24664 }
24665
24666 if (isZoom) {
24667 var elementRect = element.getBoundingClientRect();
24668
24669 var offset = {
24670 x: event.clientX - elementRect.left,
24671 y: event.clientY - elementRect.top
24672 };
24673
24674 delta = (
24675 Math.sqrt(
24676 Math.pow(event.deltaY, 2) +
24677 Math.pow(event.deltaX, 2)
24678 ) * sign(event.deltaY) * factor
24679 );
24680
24681 // zoom in relative to diagram {x,y} coordinates
24682 this.zoom(delta, offset);
24683 } else {
24684
24685 if (isHorizontalScroll) {
24686 delta = {
24687 dx: factor * event.deltaY,
24688 dy: 0
24689 };
24690 } else {
24691 delta = {
24692 dx: factor * event.deltaX,
24693 dy: factor * event.deltaY
24694 };
24695 }
24696
24697 this.scroll(delta);
24698 }
24699 };
24700
24701 /**
24702 * Zoom with fixed step size.
24703 *
24704 * @param {number} delta - Zoom delta (1 for zooming in, -1 for out).
24705 * @param {Object} position
24706 */
24707 ZoomScroll.prototype.stepZoom = function stepZoom(delta, position) {
24708
24709 var stepSize = getStepSize(RANGE, NUM_STEPS);
24710
24711 this._zoom(delta, position, stepSize);
24712 };
24713
24714
24715 /**
24716 * Zoom in/out given a step size.
24717 *
24718 * @param {number} delta
24719 * @param {Object} position
24720 * @param {number} stepSize
24721 */
24722 ZoomScroll.prototype._zoom = function(delta, position, stepSize) {
24723 var canvas = this._canvas;
24724
24725 var direction = delta > 0 ? 1 : -1;
24726
24727 var currentLinearZoomLevel = log10(canvas.zoom());
24728
24729 // snap to a proximate zoom step
24730 var newLinearZoomLevel = Math.round(currentLinearZoomLevel / stepSize) * stepSize;
24731
24732 // increase or decrease one zoom step in the given direction
24733 newLinearZoomLevel += stepSize * direction;
24734
24735 // calculate the absolute logarithmic zoom level based on the linear zoom level
24736 // (e.g. 2 for an absolute x2 zoom)
24737 var newLogZoomLevel = Math.pow(10, newLinearZoomLevel);
24738
24739 canvas.zoom(cap(RANGE, newLogZoomLevel), position);
24740 };
24741
24742
24743 /**
24744 * Toggle the zoom scroll ability via mouse wheel.
24745 *
24746 * @param {boolean} [newEnabled] new enabled state
24747 */
24748 ZoomScroll.prototype.toggle = function toggle(newEnabled) {
24749
24750 var element = this._container;
24751 var handleWheel = this._handleWheel;
24752
24753 var oldEnabled = this._enabled;
24754
24755 if (typeof newEnabled === 'undefined') {
24756 newEnabled = !oldEnabled;
24757 }
24758
24759 // only react on actual changes
24760 if (oldEnabled !== newEnabled) {
24761
24762 // add or remove wheel listener based on
24763 // changed enabled state
24764 componentEvent[newEnabled ? 'bind' : 'unbind'](element, 'wheel', handleWheel, false);
24765 }
24766
24767 this._enabled = newEnabled;
24768
24769 return newEnabled;
24770 };
24771
24772
24773 ZoomScroll.prototype._init = function(newEnabled) {
24774 this.toggle(newEnabled);
24775 };
24776
24777 var ZoomScrollModule = {
24778 __init__: [ 'zoomScroll' ],
24779 zoomScroll: [ 'type', ZoomScroll ]
24780 };
24781
24782 /**
24783 * A viewer that includes mouse navigation facilities
24784 *
24785 * @param {Object} options
24786 */
24787 function NavigatedViewer(options) {
24788 Viewer.call(this, options);
24789 }
24790
24791 inherits$1(NavigatedViewer, Viewer);
24792
24793
24794 NavigatedViewer.prototype._navigationModules = [
24795 KeyboardMoveModule,
24796 MoveCanvasModule,
24797 ZoomScrollModule
24798 ];
24799
24800 NavigatedViewer.prototype._modules = [].concat(
24801 Viewer.prototype._modules,
24802 NavigatedViewer.prototype._navigationModules
24803 );
24804
24805 var hammer = {exports: {}};
24806
24807 /*! Hammer.JS - v2.0.7 - 2016-04-22
24808 * http://hammerjs.github.io/
24809 *
24810 * Copyright (c) 2016 Jorik Tangelder;
24811 * Licensed under the MIT license */
24812
24813 (function (module) {
24814 (function(window, document, exportName, undefined$1) {
24815
24816 var VENDOR_PREFIXES = ['', 'webkit', 'Moz', 'MS', 'ms', 'o'];
24817 var TEST_ELEMENT = document.createElement('div');
24818
24819 var TYPE_FUNCTION = 'function';
24820
24821 var round = Math.round;
24822 var abs = Math.abs;
24823 var now = Date.now;
24824
24825 /**
24826 * set a timeout with a given scope
24827 * @param {Function} fn
24828 * @param {Number} timeout
24829 * @param {Object} context
24830 * @returns {number}
24831 */
24832 function setTimeoutContext(fn, timeout, context) {
24833 return setTimeout(bindFn(fn, context), timeout);
24834 }
24835
24836 /**
24837 * if the argument is an array, we want to execute the fn on each entry
24838 * if it aint an array we don't want to do a thing.
24839 * this is used by all the methods that accept a single and array argument.
24840 * @param {*|Array} arg
24841 * @param {String} fn
24842 * @param {Object} [context]
24843 * @returns {Boolean}
24844 */
24845 function invokeArrayArg(arg, fn, context) {
24846 if (Array.isArray(arg)) {
24847 each(arg, context[fn], context);
24848 return true;
24849 }
24850 return false;
24851 }
24852
24853 /**
24854 * walk objects and arrays
24855 * @param {Object} obj
24856 * @param {Function} iterator
24857 * @param {Object} context
24858 */
24859 function each(obj, iterator, context) {
24860 var i;
24861
24862 if (!obj) {
24863 return;
24864 }
24865
24866 if (obj.forEach) {
24867 obj.forEach(iterator, context);
24868 } else if (obj.length !== undefined$1) {
24869 i = 0;
24870 while (i < obj.length) {
24871 iterator.call(context, obj[i], i, obj);
24872 i++;
24873 }
24874 } else {
24875 for (i in obj) {
24876 obj.hasOwnProperty(i) && iterator.call(context, obj[i], i, obj);
24877 }
24878 }
24879 }
24880
24881 /**
24882 * wrap a method with a deprecation warning and stack trace
24883 * @param {Function} method
24884 * @param {String} name
24885 * @param {String} message
24886 * @returns {Function} A new function wrapping the supplied method.
24887 */
24888 function deprecate(method, name, message) {
24889 var deprecationMessage = 'DEPRECATED METHOD: ' + name + '\n' + message + ' AT \n';
24890 return function() {
24891 var e = new Error('get-stack-trace');
24892 var stack = e && e.stack ? e.stack.replace(/^[^\(]+?[\n$]/gm, '')
24893 .replace(/^\s+at\s+/gm, '')
24894 .replace(/^Object.<anonymous>\s*\(/gm, '{anonymous}()@') : 'Unknown Stack Trace';
24895
24896 var log = window.console && (window.console.warn || window.console.log);
24897 if (log) {
24898 log.call(window.console, deprecationMessage, stack);
24899 }
24900 return method.apply(this, arguments);
24901 };
24902 }
24903
24904 /**
24905 * extend object.
24906 * means that properties in dest will be overwritten by the ones in src.
24907 * @param {Object} target
24908 * @param {...Object} objects_to_assign
24909 * @returns {Object} target
24910 */
24911 var assign;
24912 if (typeof Object.assign !== 'function') {
24913 assign = function assign(target) {
24914 if (target === undefined$1 || target === null) {
24915 throw new TypeError('Cannot convert undefined or null to object');
24916 }
24917
24918 var output = Object(target);
24919 for (var index = 1; index < arguments.length; index++) {
24920 var source = arguments[index];
24921 if (source !== undefined$1 && source !== null) {
24922 for (var nextKey in source) {
24923 if (source.hasOwnProperty(nextKey)) {
24924 output[nextKey] = source[nextKey];
24925 }
24926 }
24927 }
24928 }
24929 return output;
24930 };
24931 } else {
24932 assign = Object.assign;
24933 }
24934
24935 /**
24936 * extend object.
24937 * means that properties in dest will be overwritten by the ones in src.
24938 * @param {Object} dest
24939 * @param {Object} src
24940 * @param {Boolean} [merge=false]
24941 * @returns {Object} dest
24942 */
24943 var extend = deprecate(function extend(dest, src, merge) {
24944 var keys = Object.keys(src);
24945 var i = 0;
24946 while (i < keys.length) {
24947 if (!merge || (merge && dest[keys[i]] === undefined$1)) {
24948 dest[keys[i]] = src[keys[i]];
24949 }
24950 i++;
24951 }
24952 return dest;
24953 }, 'extend', 'Use `assign`.');
24954
24955 /**
24956 * merge the values from src in the dest.
24957 * means that properties that exist in dest will not be overwritten by src
24958 * @param {Object} dest
24959 * @param {Object} src
24960 * @returns {Object} dest
24961 */
24962 var merge = deprecate(function merge(dest, src) {
24963 return extend(dest, src, true);
24964 }, 'merge', 'Use `assign`.');
24965
24966 /**
24967 * simple class inheritance
24968 * @param {Function} child
24969 * @param {Function} base
24970 * @param {Object} [properties]
24971 */
24972 function inherit(child, base, properties) {
24973 var baseP = base.prototype,
24974 childP;
24975
24976 childP = child.prototype = Object.create(baseP);
24977 childP.constructor = child;
24978 childP._super = baseP;
24979
24980 if (properties) {
24981 assign(childP, properties);
24982 }
24983 }
24984
24985 /**
24986 * simple function bind
24987 * @param {Function} fn
24988 * @param {Object} context
24989 * @returns {Function}
24990 */
24991 function bindFn(fn, context) {
24992 return function boundFn() {
24993 return fn.apply(context, arguments);
24994 };
24995 }
24996
24997 /**
24998 * let a boolean value also be a function that must return a boolean
24999 * this first item in args will be used as the context
25000 * @param {Boolean|Function} val
25001 * @param {Array} [args]
25002 * @returns {Boolean}
25003 */
25004 function boolOrFn(val, args) {
25005 if (typeof val == TYPE_FUNCTION) {
25006 return val.apply(args ? args[0] || undefined$1 : undefined$1, args);
25007 }
25008 return val;
25009 }
25010
25011 /**
25012 * use the val2 when val1 is undefined
25013 * @param {*} val1
25014 * @param {*} val2
25015 * @returns {*}
25016 */
25017 function ifUndefined(val1, val2) {
25018 return (val1 === undefined$1) ? val2 : val1;
25019 }
25020
25021 /**
25022 * addEventListener with multiple events at once
25023 * @param {EventTarget} target
25024 * @param {String} types
25025 * @param {Function} handler
25026 */
25027 function addEventListeners(target, types, handler) {
25028 each(splitStr(types), function(type) {
25029 target.addEventListener(type, handler, false);
25030 });
25031 }
25032
25033 /**
25034 * removeEventListener with multiple events at once
25035 * @param {EventTarget} target
25036 * @param {String} types
25037 * @param {Function} handler
25038 */
25039 function removeEventListeners(target, types, handler) {
25040 each(splitStr(types), function(type) {
25041 target.removeEventListener(type, handler, false);
25042 });
25043 }
25044
25045 /**
25046 * find if a node is in the given parent
25047 * @method hasParent
25048 * @param {HTMLElement} node
25049 * @param {HTMLElement} parent
25050 * @return {Boolean} found
25051 */
25052 function hasParent(node, parent) {
25053 while (node) {
25054 if (node == parent) {
25055 return true;
25056 }
25057 node = node.parentNode;
25058 }
25059 return false;
25060 }
25061
25062 /**
25063 * small indexOf wrapper
25064 * @param {String} str
25065 * @param {String} find
25066 * @returns {Boolean} found
25067 */
25068 function inStr(str, find) {
25069 return str.indexOf(find) > -1;
25070 }
25071
25072 /**
25073 * split string on whitespace
25074 * @param {String} str
25075 * @returns {Array} words
25076 */
25077 function splitStr(str) {
25078 return str.trim().split(/\s+/g);
25079 }
25080
25081 /**
25082 * find if a array contains the object using indexOf or a simple polyFill
25083 * @param {Array} src
25084 * @param {String} find
25085 * @param {String} [findByKey]
25086 * @return {Boolean|Number} false when not found, or the index
25087 */
25088 function inArray(src, find, findByKey) {
25089 if (src.indexOf && !findByKey) {
25090 return src.indexOf(find);
25091 } else {
25092 var i = 0;
25093 while (i < src.length) {
25094 if ((findByKey && src[i][findByKey] == find) || (!findByKey && src[i] === find)) {
25095 return i;
25096 }
25097 i++;
25098 }
25099 return -1;
25100 }
25101 }
25102
25103 /**
25104 * convert array-like objects to real arrays
25105 * @param {Object} obj
25106 * @returns {Array}
25107 */
25108 function toArray(obj) {
25109 return Array.prototype.slice.call(obj, 0);
25110 }
25111
25112 /**
25113 * unique array with objects based on a key (like 'id') or just by the array's value
25114 * @param {Array} src [{id:1},{id:2},{id:1}]
25115 * @param {String} [key]
25116 * @param {Boolean} [sort=False]
25117 * @returns {Array} [{id:1},{id:2}]
25118 */
25119 function uniqueArray(src, key, sort) {
25120 var results = [];
25121 var values = [];
25122 var i = 0;
25123
25124 while (i < src.length) {
25125 var val = key ? src[i][key] : src[i];
25126 if (inArray(values, val) < 0) {
25127 results.push(src[i]);
25128 }
25129 values[i] = val;
25130 i++;
25131 }
25132
25133 if (sort) {
25134 if (!key) {
25135 results = results.sort();
25136 } else {
25137 results = results.sort(function sortUniqueArray(a, b) {
25138 return a[key] > b[key];
25139 });
25140 }
25141 }
25142
25143 return results;
25144 }
25145
25146 /**
25147 * get the prefixed property
25148 * @param {Object} obj
25149 * @param {String} property
25150 * @returns {String|Undefined} prefixed
25151 */
25152 function prefixed(obj, property) {
25153 var prefix, prop;
25154 var camelProp = property[0].toUpperCase() + property.slice(1);
25155
25156 var i = 0;
25157 while (i < VENDOR_PREFIXES.length) {
25158 prefix = VENDOR_PREFIXES[i];
25159 prop = (prefix) ? prefix + camelProp : property;
25160
25161 if (prop in obj) {
25162 return prop;
25163 }
25164 i++;
25165 }
25166 return undefined$1;
25167 }
25168
25169 /**
25170 * get a unique id
25171 * @returns {number} uniqueId
25172 */
25173 var _uniqueId = 1;
25174 function uniqueId() {
25175 return _uniqueId++;
25176 }
25177
25178 /**
25179 * get the window object of an element
25180 * @param {HTMLElement} element
25181 * @returns {DocumentView|Window}
25182 */
25183 function getWindowForElement(element) {
25184 var doc = element.ownerDocument || element;
25185 return (doc.defaultView || doc.parentWindow || window);
25186 }
25187
25188 var MOBILE_REGEX = /mobile|tablet|ip(ad|hone|od)|android/i;
25189
25190 var SUPPORT_TOUCH = ('ontouchstart' in window);
25191 var SUPPORT_POINTER_EVENTS = prefixed(window, 'PointerEvent') !== undefined$1;
25192 var SUPPORT_ONLY_TOUCH = SUPPORT_TOUCH && MOBILE_REGEX.test(navigator.userAgent);
25193
25194 var INPUT_TYPE_TOUCH = 'touch';
25195 var INPUT_TYPE_PEN = 'pen';
25196 var INPUT_TYPE_MOUSE = 'mouse';
25197 var INPUT_TYPE_KINECT = 'kinect';
25198
25199 var COMPUTE_INTERVAL = 25;
25200
25201 var INPUT_START = 1;
25202 var INPUT_MOVE = 2;
25203 var INPUT_END = 4;
25204 var INPUT_CANCEL = 8;
25205
25206 var DIRECTION_NONE = 1;
25207 var DIRECTION_LEFT = 2;
25208 var DIRECTION_RIGHT = 4;
25209 var DIRECTION_UP = 8;
25210 var DIRECTION_DOWN = 16;
25211
25212 var DIRECTION_HORIZONTAL = DIRECTION_LEFT | DIRECTION_RIGHT;
25213 var DIRECTION_VERTICAL = DIRECTION_UP | DIRECTION_DOWN;
25214 var DIRECTION_ALL = DIRECTION_HORIZONTAL | DIRECTION_VERTICAL;
25215
25216 var PROPS_XY = ['x', 'y'];
25217 var PROPS_CLIENT_XY = ['clientX', 'clientY'];
25218
25219 /**
25220 * create new input type manager
25221 * @param {Manager} manager
25222 * @param {Function} callback
25223 * @returns {Input}
25224 * @constructor
25225 */
25226 function Input(manager, callback) {
25227 var self = this;
25228 this.manager = manager;
25229 this.callback = callback;
25230 this.element = manager.element;
25231 this.target = manager.options.inputTarget;
25232
25233 // smaller wrapper around the handler, for the scope and the enabled state of the manager,
25234 // so when disabled the input events are completely bypassed.
25235 this.domHandler = function(ev) {
25236 if (boolOrFn(manager.options.enable, [manager])) {
25237 self.handler(ev);
25238 }
25239 };
25240
25241 this.init();
25242
25243 }
25244
25245 Input.prototype = {
25246 /**
25247 * should handle the inputEvent data and trigger the callback
25248 * @virtual
25249 */
25250 handler: function() { },
25251
25252 /**
25253 * bind the events
25254 */
25255 init: function() {
25256 this.evEl && addEventListeners(this.element, this.evEl, this.domHandler);
25257 this.evTarget && addEventListeners(this.target, this.evTarget, this.domHandler);
25258 this.evWin && addEventListeners(getWindowForElement(this.element), this.evWin, this.domHandler);
25259 },
25260
25261 /**
25262 * unbind the events
25263 */
25264 destroy: function() {
25265 this.evEl && removeEventListeners(this.element, this.evEl, this.domHandler);
25266 this.evTarget && removeEventListeners(this.target, this.evTarget, this.domHandler);
25267 this.evWin && removeEventListeners(getWindowForElement(this.element), this.evWin, this.domHandler);
25268 }
25269 };
25270
25271 /**
25272 * create new input type manager
25273 * called by the Manager constructor
25274 * @param {Hammer} manager
25275 * @returns {Input}
25276 */
25277 function createInputInstance(manager) {
25278 var Type;
25279 var inputClass = manager.options.inputClass;
25280
25281 if (inputClass) {
25282 Type = inputClass;
25283 } else if (SUPPORT_POINTER_EVENTS) {
25284 Type = PointerEventInput;
25285 } else if (SUPPORT_ONLY_TOUCH) {
25286 Type = TouchInput;
25287 } else if (!SUPPORT_TOUCH) {
25288 Type = MouseInput;
25289 } else {
25290 Type = TouchMouseInput;
25291 }
25292 return new (Type)(manager, inputHandler);
25293 }
25294
25295 /**
25296 * handle input events
25297 * @param {Manager} manager
25298 * @param {String} eventType
25299 * @param {Object} input
25300 */
25301 function inputHandler(manager, eventType, input) {
25302 var pointersLen = input.pointers.length;
25303 var changedPointersLen = input.changedPointers.length;
25304 var isFirst = (eventType & INPUT_START && (pointersLen - changedPointersLen === 0));
25305 var isFinal = (eventType & (INPUT_END | INPUT_CANCEL) && (pointersLen - changedPointersLen === 0));
25306
25307 input.isFirst = !!isFirst;
25308 input.isFinal = !!isFinal;
25309
25310 if (isFirst) {
25311 manager.session = {};
25312 }
25313
25314 // source event is the normalized value of the domEvents
25315 // like 'touchstart, mouseup, pointerdown'
25316 input.eventType = eventType;
25317
25318 // compute scale, rotation etc
25319 computeInputData(manager, input);
25320
25321 // emit secret event
25322 manager.emit('hammer.input', input);
25323
25324 manager.recognize(input);
25325 manager.session.prevInput = input;
25326 }
25327
25328 /**
25329 * extend the data with some usable properties like scale, rotate, velocity etc
25330 * @param {Object} manager
25331 * @param {Object} input
25332 */
25333 function computeInputData(manager, input) {
25334 var session = manager.session;
25335 var pointers = input.pointers;
25336 var pointersLength = pointers.length;
25337
25338 // store the first input to calculate the distance and direction
25339 if (!session.firstInput) {
25340 session.firstInput = simpleCloneInputData(input);
25341 }
25342
25343 // to compute scale and rotation we need to store the multiple touches
25344 if (pointersLength > 1 && !session.firstMultiple) {
25345 session.firstMultiple = simpleCloneInputData(input);
25346 } else if (pointersLength === 1) {
25347 session.firstMultiple = false;
25348 }
25349
25350 var firstInput = session.firstInput;
25351 var firstMultiple = session.firstMultiple;
25352 var offsetCenter = firstMultiple ? firstMultiple.center : firstInput.center;
25353
25354 var center = input.center = getCenter(pointers);
25355 input.timeStamp = now();
25356 input.deltaTime = input.timeStamp - firstInput.timeStamp;
25357
25358 input.angle = getAngle(offsetCenter, center);
25359 input.distance = getDistance(offsetCenter, center);
25360
25361 computeDeltaXY(session, input);
25362 input.offsetDirection = getDirection(input.deltaX, input.deltaY);
25363
25364 var overallVelocity = getVelocity(input.deltaTime, input.deltaX, input.deltaY);
25365 input.overallVelocityX = overallVelocity.x;
25366 input.overallVelocityY = overallVelocity.y;
25367 input.overallVelocity = (abs(overallVelocity.x) > abs(overallVelocity.y)) ? overallVelocity.x : overallVelocity.y;
25368
25369 input.scale = firstMultiple ? getScale(firstMultiple.pointers, pointers) : 1;
25370 input.rotation = firstMultiple ? getRotation(firstMultiple.pointers, pointers) : 0;
25371
25372 input.maxPointers = !session.prevInput ? input.pointers.length : ((input.pointers.length >
25373 session.prevInput.maxPointers) ? input.pointers.length : session.prevInput.maxPointers);
25374
25375 computeIntervalInputData(session, input);
25376
25377 // find the correct target
25378 var target = manager.element;
25379 if (hasParent(input.srcEvent.target, target)) {
25380 target = input.srcEvent.target;
25381 }
25382 input.target = target;
25383 }
25384
25385 function computeDeltaXY(session, input) {
25386 var center = input.center;
25387 var offset = session.offsetDelta || {};
25388 var prevDelta = session.prevDelta || {};
25389 var prevInput = session.prevInput || {};
25390
25391 if (input.eventType === INPUT_START || prevInput.eventType === INPUT_END) {
25392 prevDelta = session.prevDelta = {
25393 x: prevInput.deltaX || 0,
25394 y: prevInput.deltaY || 0
25395 };
25396
25397 offset = session.offsetDelta = {
25398 x: center.x,
25399 y: center.y
25400 };
25401 }
25402
25403 input.deltaX = prevDelta.x + (center.x - offset.x);
25404 input.deltaY = prevDelta.y + (center.y - offset.y);
25405 }
25406
25407 /**
25408 * velocity is calculated every x ms
25409 * @param {Object} session
25410 * @param {Object} input
25411 */
25412 function computeIntervalInputData(session, input) {
25413 var last = session.lastInterval || input,
25414 deltaTime = input.timeStamp - last.timeStamp,
25415 velocity, velocityX, velocityY, direction;
25416
25417 if (input.eventType != INPUT_CANCEL && (deltaTime > COMPUTE_INTERVAL || last.velocity === undefined$1)) {
25418 var deltaX = input.deltaX - last.deltaX;
25419 var deltaY = input.deltaY - last.deltaY;
25420
25421 var v = getVelocity(deltaTime, deltaX, deltaY);
25422 velocityX = v.x;
25423 velocityY = v.y;
25424 velocity = (abs(v.x) > abs(v.y)) ? v.x : v.y;
25425 direction = getDirection(deltaX, deltaY);
25426
25427 session.lastInterval = input;
25428 } else {
25429 // use latest velocity info if it doesn't overtake a minimum period
25430 velocity = last.velocity;
25431 velocityX = last.velocityX;
25432 velocityY = last.velocityY;
25433 direction = last.direction;
25434 }
25435
25436 input.velocity = velocity;
25437 input.velocityX = velocityX;
25438 input.velocityY = velocityY;
25439 input.direction = direction;
25440 }
25441
25442 /**
25443 * create a simple clone from the input used for storage of firstInput and firstMultiple
25444 * @param {Object} input
25445 * @returns {Object} clonedInputData
25446 */
25447 function simpleCloneInputData(input) {
25448 // make a simple copy of the pointers because we will get a reference if we don't
25449 // we only need clientXY for the calculations
25450 var pointers = [];
25451 var i = 0;
25452 while (i < input.pointers.length) {
25453 pointers[i] = {
25454 clientX: round(input.pointers[i].clientX),
25455 clientY: round(input.pointers[i].clientY)
25456 };
25457 i++;
25458 }
25459
25460 return {
25461 timeStamp: now(),
25462 pointers: pointers,
25463 center: getCenter(pointers),
25464 deltaX: input.deltaX,
25465 deltaY: input.deltaY
25466 };
25467 }
25468
25469 /**
25470 * get the center of all the pointers
25471 * @param {Array} pointers
25472 * @return {Object} center contains `x` and `y` properties
25473 */
25474 function getCenter(pointers) {
25475 var pointersLength = pointers.length;
25476
25477 // no need to loop when only one touch
25478 if (pointersLength === 1) {
25479 return {
25480 x: round(pointers[0].clientX),
25481 y: round(pointers[0].clientY)
25482 };
25483 }
25484
25485 var x = 0, y = 0, i = 0;
25486 while (i < pointersLength) {
25487 x += pointers[i].clientX;
25488 y += pointers[i].clientY;
25489 i++;
25490 }
25491
25492 return {
25493 x: round(x / pointersLength),
25494 y: round(y / pointersLength)
25495 };
25496 }
25497
25498 /**
25499 * calculate the velocity between two points. unit is in px per ms.
25500 * @param {Number} deltaTime
25501 * @param {Number} x
25502 * @param {Number} y
25503 * @return {Object} velocity `x` and `y`
25504 */
25505 function getVelocity(deltaTime, x, y) {
25506 return {
25507 x: x / deltaTime || 0,
25508 y: y / deltaTime || 0
25509 };
25510 }
25511
25512 /**
25513 * get the direction between two points
25514 * @param {Number} x
25515 * @param {Number} y
25516 * @return {Number} direction
25517 */
25518 function getDirection(x, y) {
25519 if (x === y) {
25520 return DIRECTION_NONE;
25521 }
25522
25523 if (abs(x) >= abs(y)) {
25524 return x < 0 ? DIRECTION_LEFT : DIRECTION_RIGHT;
25525 }
25526 return y < 0 ? DIRECTION_UP : DIRECTION_DOWN;
25527 }
25528
25529 /**
25530 * calculate the absolute distance between two points
25531 * @param {Object} p1 {x, y}
25532 * @param {Object} p2 {x, y}
25533 * @param {Array} [props] containing x and y keys
25534 * @return {Number} distance
25535 */
25536 function getDistance(p1, p2, props) {
25537 if (!props) {
25538 props = PROPS_XY;
25539 }
25540 var x = p2[props[0]] - p1[props[0]],
25541 y = p2[props[1]] - p1[props[1]];
25542
25543 return Math.sqrt((x * x) + (y * y));
25544 }
25545
25546 /**
25547 * calculate the angle between two coordinates
25548 * @param {Object} p1
25549 * @param {Object} p2
25550 * @param {Array} [props] containing x and y keys
25551 * @return {Number} angle
25552 */
25553 function getAngle(p1, p2, props) {
25554 if (!props) {
25555 props = PROPS_XY;
25556 }
25557 var x = p2[props[0]] - p1[props[0]],
25558 y = p2[props[1]] - p1[props[1]];
25559 return Math.atan2(y, x) * 180 / Math.PI;
25560 }
25561
25562 /**
25563 * calculate the rotation degrees between two pointersets
25564 * @param {Array} start array of pointers
25565 * @param {Array} end array of pointers
25566 * @return {Number} rotation
25567 */
25568 function getRotation(start, end) {
25569 return getAngle(end[1], end[0], PROPS_CLIENT_XY) + getAngle(start[1], start[0], PROPS_CLIENT_XY);
25570 }
25571
25572 /**
25573 * calculate the scale factor between two pointersets
25574 * no scale is 1, and goes down to 0 when pinched together, and bigger when pinched out
25575 * @param {Array} start array of pointers
25576 * @param {Array} end array of pointers
25577 * @return {Number} scale
25578 */
25579 function getScale(start, end) {
25580 return getDistance(end[0], end[1], PROPS_CLIENT_XY) / getDistance(start[0], start[1], PROPS_CLIENT_XY);
25581 }
25582
25583 var MOUSE_INPUT_MAP = {
25584 mousedown: INPUT_START,
25585 mousemove: INPUT_MOVE,
25586 mouseup: INPUT_END
25587 };
25588
25589 var MOUSE_ELEMENT_EVENTS = 'mousedown';
25590 var MOUSE_WINDOW_EVENTS = 'mousemove mouseup';
25591
25592 /**
25593 * Mouse events input
25594 * @constructor
25595 * @extends Input
25596 */
25597 function MouseInput() {
25598 this.evEl = MOUSE_ELEMENT_EVENTS;
25599 this.evWin = MOUSE_WINDOW_EVENTS;
25600
25601 this.pressed = false; // mousedown state
25602
25603 Input.apply(this, arguments);
25604 }
25605
25606 inherit(MouseInput, Input, {
25607 /**
25608 * handle mouse events
25609 * @param {Object} ev
25610 */
25611 handler: function MEhandler(ev) {
25612 var eventType = MOUSE_INPUT_MAP[ev.type];
25613
25614 // on start we want to have the left mouse button down
25615 if (eventType & INPUT_START && ev.button === 0) {
25616 this.pressed = true;
25617 }
25618
25619 if (eventType & INPUT_MOVE && ev.which !== 1) {
25620 eventType = INPUT_END;
25621 }
25622
25623 // mouse must be down
25624 if (!this.pressed) {
25625 return;
25626 }
25627
25628 if (eventType & INPUT_END) {
25629 this.pressed = false;
25630 }
25631
25632 this.callback(this.manager, eventType, {
25633 pointers: [ev],
25634 changedPointers: [ev],
25635 pointerType: INPUT_TYPE_MOUSE,
25636 srcEvent: ev
25637 });
25638 }
25639 });
25640
25641 var POINTER_INPUT_MAP = {
25642 pointerdown: INPUT_START,
25643 pointermove: INPUT_MOVE,
25644 pointerup: INPUT_END,
25645 pointercancel: INPUT_CANCEL,
25646 pointerout: INPUT_CANCEL
25647 };
25648
25649 // in IE10 the pointer types is defined as an enum
25650 var IE10_POINTER_TYPE_ENUM = {
25651 2: INPUT_TYPE_TOUCH,
25652 3: INPUT_TYPE_PEN,
25653 4: INPUT_TYPE_MOUSE,
25654 5: INPUT_TYPE_KINECT // see https://twitter.com/jacobrossi/status/480596438489890816
25655 };
25656
25657 var POINTER_ELEMENT_EVENTS = 'pointerdown';
25658 var POINTER_WINDOW_EVENTS = 'pointermove pointerup pointercancel';
25659
25660 // IE10 has prefixed support, and case-sensitive
25661 if (window.MSPointerEvent && !window.PointerEvent) {
25662 POINTER_ELEMENT_EVENTS = 'MSPointerDown';
25663 POINTER_WINDOW_EVENTS = 'MSPointerMove MSPointerUp MSPointerCancel';
25664 }
25665
25666 /**
25667 * Pointer events input
25668 * @constructor
25669 * @extends Input
25670 */
25671 function PointerEventInput() {
25672 this.evEl = POINTER_ELEMENT_EVENTS;
25673 this.evWin = POINTER_WINDOW_EVENTS;
25674
25675 Input.apply(this, arguments);
25676
25677 this.store = (this.manager.session.pointerEvents = []);
25678 }
25679
25680 inherit(PointerEventInput, Input, {
25681 /**
25682 * handle mouse events
25683 * @param {Object} ev
25684 */
25685 handler: function PEhandler(ev) {
25686 var store = this.store;
25687 var removePointer = false;
25688
25689 var eventTypeNormalized = ev.type.toLowerCase().replace('ms', '');
25690 var eventType = POINTER_INPUT_MAP[eventTypeNormalized];
25691 var pointerType = IE10_POINTER_TYPE_ENUM[ev.pointerType] || ev.pointerType;
25692
25693 var isTouch = (pointerType == INPUT_TYPE_TOUCH);
25694
25695 // get index of the event in the store
25696 var storeIndex = inArray(store, ev.pointerId, 'pointerId');
25697
25698 // start and mouse must be down
25699 if (eventType & INPUT_START && (ev.button === 0 || isTouch)) {
25700 if (storeIndex < 0) {
25701 store.push(ev);
25702 storeIndex = store.length - 1;
25703 }
25704 } else if (eventType & (INPUT_END | INPUT_CANCEL)) {
25705 removePointer = true;
25706 }
25707
25708 // it not found, so the pointer hasn't been down (so it's probably a hover)
25709 if (storeIndex < 0) {
25710 return;
25711 }
25712
25713 // update the event in the store
25714 store[storeIndex] = ev;
25715
25716 this.callback(this.manager, eventType, {
25717 pointers: store,
25718 changedPointers: [ev],
25719 pointerType: pointerType,
25720 srcEvent: ev
25721 });
25722
25723 if (removePointer) {
25724 // remove from the store
25725 store.splice(storeIndex, 1);
25726 }
25727 }
25728 });
25729
25730 var SINGLE_TOUCH_INPUT_MAP = {
25731 touchstart: INPUT_START,
25732 touchmove: INPUT_MOVE,
25733 touchend: INPUT_END,
25734 touchcancel: INPUT_CANCEL
25735 };
25736
25737 var SINGLE_TOUCH_TARGET_EVENTS = 'touchstart';
25738 var SINGLE_TOUCH_WINDOW_EVENTS = 'touchstart touchmove touchend touchcancel';
25739
25740 /**
25741 * Touch events input
25742 * @constructor
25743 * @extends Input
25744 */
25745 function SingleTouchInput() {
25746 this.evTarget = SINGLE_TOUCH_TARGET_EVENTS;
25747 this.evWin = SINGLE_TOUCH_WINDOW_EVENTS;
25748 this.started = false;
25749
25750 Input.apply(this, arguments);
25751 }
25752
25753 inherit(SingleTouchInput, Input, {
25754 handler: function TEhandler(ev) {
25755 var type = SINGLE_TOUCH_INPUT_MAP[ev.type];
25756
25757 // should we handle the touch events?
25758 if (type === INPUT_START) {
25759 this.started = true;
25760 }
25761
25762 if (!this.started) {
25763 return;
25764 }
25765
25766 var touches = normalizeSingleTouches.call(this, ev, type);
25767
25768 // when done, reset the started state
25769 if (type & (INPUT_END | INPUT_CANCEL) && touches[0].length - touches[1].length === 0) {
25770 this.started = false;
25771 }
25772
25773 this.callback(this.manager, type, {
25774 pointers: touches[0],
25775 changedPointers: touches[1],
25776 pointerType: INPUT_TYPE_TOUCH,
25777 srcEvent: ev
25778 });
25779 }
25780 });
25781
25782 /**
25783 * @this {TouchInput}
25784 * @param {Object} ev
25785 * @param {Number} type flag
25786 * @returns {undefined|Array} [all, changed]
25787 */
25788 function normalizeSingleTouches(ev, type) {
25789 var all = toArray(ev.touches);
25790 var changed = toArray(ev.changedTouches);
25791
25792 if (type & (INPUT_END | INPUT_CANCEL)) {
25793 all = uniqueArray(all.concat(changed), 'identifier', true);
25794 }
25795
25796 return [all, changed];
25797 }
25798
25799 var TOUCH_INPUT_MAP = {
25800 touchstart: INPUT_START,
25801 touchmove: INPUT_MOVE,
25802 touchend: INPUT_END,
25803 touchcancel: INPUT_CANCEL
25804 };
25805
25806 var TOUCH_TARGET_EVENTS = 'touchstart touchmove touchend touchcancel';
25807
25808 /**
25809 * Multi-user touch events input
25810 * @constructor
25811 * @extends Input
25812 */
25813 function TouchInput() {
25814 this.evTarget = TOUCH_TARGET_EVENTS;
25815 this.targetIds = {};
25816
25817 Input.apply(this, arguments);
25818 }
25819
25820 inherit(TouchInput, Input, {
25821 handler: function MTEhandler(ev) {
25822 var type = TOUCH_INPUT_MAP[ev.type];
25823 var touches = getTouches.call(this, ev, type);
25824 if (!touches) {
25825 return;
25826 }
25827
25828 this.callback(this.manager, type, {
25829 pointers: touches[0],
25830 changedPointers: touches[1],
25831 pointerType: INPUT_TYPE_TOUCH,
25832 srcEvent: ev
25833 });
25834 }
25835 });
25836
25837 /**
25838 * @this {TouchInput}
25839 * @param {Object} ev
25840 * @param {Number} type flag
25841 * @returns {undefined|Array} [all, changed]
25842 */
25843 function getTouches(ev, type) {
25844 var allTouches = toArray(ev.touches);
25845 var targetIds = this.targetIds;
25846
25847 // when there is only one touch, the process can be simplified
25848 if (type & (INPUT_START | INPUT_MOVE) && allTouches.length === 1) {
25849 targetIds[allTouches[0].identifier] = true;
25850 return [allTouches, allTouches];
25851 }
25852
25853 var i,
25854 targetTouches,
25855 changedTouches = toArray(ev.changedTouches),
25856 changedTargetTouches = [],
25857 target = this.target;
25858
25859 // get target touches from touches
25860 targetTouches = allTouches.filter(function(touch) {
25861 return hasParent(touch.target, target);
25862 });
25863
25864 // collect touches
25865 if (type === INPUT_START) {
25866 i = 0;
25867 while (i < targetTouches.length) {
25868 targetIds[targetTouches[i].identifier] = true;
25869 i++;
25870 }
25871 }
25872
25873 // filter changed touches to only contain touches that exist in the collected target ids
25874 i = 0;
25875 while (i < changedTouches.length) {
25876 if (targetIds[changedTouches[i].identifier]) {
25877 changedTargetTouches.push(changedTouches[i]);
25878 }
25879
25880 // cleanup removed touches
25881 if (type & (INPUT_END | INPUT_CANCEL)) {
25882 delete targetIds[changedTouches[i].identifier];
25883 }
25884 i++;
25885 }
25886
25887 if (!changedTargetTouches.length) {
25888 return;
25889 }
25890
25891 return [
25892 // merge targetTouches with changedTargetTouches so it contains ALL touches, including 'end' and 'cancel'
25893 uniqueArray(targetTouches.concat(changedTargetTouches), 'identifier', true),
25894 changedTargetTouches
25895 ];
25896 }
25897
25898 /**
25899 * Combined touch and mouse input
25900 *
25901 * Touch has a higher priority then mouse, and while touching no mouse events are allowed.
25902 * This because touch devices also emit mouse events while doing a touch.
25903 *
25904 * @constructor
25905 * @extends Input
25906 */
25907
25908 var DEDUP_TIMEOUT = 2500;
25909 var DEDUP_DISTANCE = 25;
25910
25911 function TouchMouseInput() {
25912 Input.apply(this, arguments);
25913
25914 var handler = bindFn(this.handler, this);
25915 this.touch = new TouchInput(this.manager, handler);
25916 this.mouse = new MouseInput(this.manager, handler);
25917
25918 this.primaryTouch = null;
25919 this.lastTouches = [];
25920 }
25921
25922 inherit(TouchMouseInput, Input, {
25923 /**
25924 * handle mouse and touch events
25925 * @param {Hammer} manager
25926 * @param {String} inputEvent
25927 * @param {Object} inputData
25928 */
25929 handler: function TMEhandler(manager, inputEvent, inputData) {
25930 var isTouch = (inputData.pointerType == INPUT_TYPE_TOUCH),
25931 isMouse = (inputData.pointerType == INPUT_TYPE_MOUSE);
25932
25933 if (isMouse && inputData.sourceCapabilities && inputData.sourceCapabilities.firesTouchEvents) {
25934 return;
25935 }
25936
25937 // when we're in a touch event, record touches to de-dupe synthetic mouse event
25938 if (isTouch) {
25939 recordTouches.call(this, inputEvent, inputData);
25940 } else if (isMouse && isSyntheticEvent.call(this, inputData)) {
25941 return;
25942 }
25943
25944 this.callback(manager, inputEvent, inputData);
25945 },
25946
25947 /**
25948 * remove the event listeners
25949 */
25950 destroy: function destroy() {
25951 this.touch.destroy();
25952 this.mouse.destroy();
25953 }
25954 });
25955
25956 function recordTouches(eventType, eventData) {
25957 if (eventType & INPUT_START) {
25958 this.primaryTouch = eventData.changedPointers[0].identifier;
25959 setLastTouch.call(this, eventData);
25960 } else if (eventType & (INPUT_END | INPUT_CANCEL)) {
25961 setLastTouch.call(this, eventData);
25962 }
25963 }
25964
25965 function setLastTouch(eventData) {
25966 var touch = eventData.changedPointers[0];
25967
25968 if (touch.identifier === this.primaryTouch) {
25969 var lastTouch = {x: touch.clientX, y: touch.clientY};
25970 this.lastTouches.push(lastTouch);
25971 var lts = this.lastTouches;
25972 var removeLastTouch = function() {
25973 var i = lts.indexOf(lastTouch);
25974 if (i > -1) {
25975 lts.splice(i, 1);
25976 }
25977 };
25978 setTimeout(removeLastTouch, DEDUP_TIMEOUT);
25979 }
25980 }
25981
25982 function isSyntheticEvent(eventData) {
25983 var x = eventData.srcEvent.clientX, y = eventData.srcEvent.clientY;
25984 for (var i = 0; i < this.lastTouches.length; i++) {
25985 var t = this.lastTouches[i];
25986 var dx = Math.abs(x - t.x), dy = Math.abs(y - t.y);
25987 if (dx <= DEDUP_DISTANCE && dy <= DEDUP_DISTANCE) {
25988 return true;
25989 }
25990 }
25991 return false;
25992 }
25993
25994 var PREFIXED_TOUCH_ACTION = prefixed(TEST_ELEMENT.style, 'touchAction');
25995 var NATIVE_TOUCH_ACTION = PREFIXED_TOUCH_ACTION !== undefined$1;
25996
25997 // magical touchAction value
25998 var TOUCH_ACTION_COMPUTE = 'compute';
25999 var TOUCH_ACTION_AUTO = 'auto';
26000 var TOUCH_ACTION_MANIPULATION = 'manipulation'; // not implemented
26001 var TOUCH_ACTION_NONE = 'none';
26002 var TOUCH_ACTION_PAN_X = 'pan-x';
26003 var TOUCH_ACTION_PAN_Y = 'pan-y';
26004 var TOUCH_ACTION_MAP = getTouchActionProps();
26005
26006 /**
26007 * Touch Action
26008 * sets the touchAction property or uses the js alternative
26009 * @param {Manager} manager
26010 * @param {String} value
26011 * @constructor
26012 */
26013 function TouchAction(manager, value) {
26014 this.manager = manager;
26015 this.set(value);
26016 }
26017
26018 TouchAction.prototype = {
26019 /**
26020 * set the touchAction value on the element or enable the polyfill
26021 * @param {String} value
26022 */
26023 set: function(value) {
26024 // find out the touch-action by the event handlers
26025 if (value == TOUCH_ACTION_COMPUTE) {
26026 value = this.compute();
26027 }
26028
26029 if (NATIVE_TOUCH_ACTION && this.manager.element.style && TOUCH_ACTION_MAP[value]) {
26030 this.manager.element.style[PREFIXED_TOUCH_ACTION] = value;
26031 }
26032 this.actions = value.toLowerCase().trim();
26033 },
26034
26035 /**
26036 * just re-set the touchAction value
26037 */
26038 update: function() {
26039 this.set(this.manager.options.touchAction);
26040 },
26041
26042 /**
26043 * compute the value for the touchAction property based on the recognizer's settings
26044 * @returns {String} value
26045 */
26046 compute: function() {
26047 var actions = [];
26048 each(this.manager.recognizers, function(recognizer) {
26049 if (boolOrFn(recognizer.options.enable, [recognizer])) {
26050 actions = actions.concat(recognizer.getTouchAction());
26051 }
26052 });
26053 return cleanTouchActions(actions.join(' '));
26054 },
26055
26056 /**
26057 * this method is called on each input cycle and provides the preventing of the browser behavior
26058 * @param {Object} input
26059 */
26060 preventDefaults: function(input) {
26061 var srcEvent = input.srcEvent;
26062 var direction = input.offsetDirection;
26063
26064 // if the touch action did prevented once this session
26065 if (this.manager.session.prevented) {
26066 srcEvent.preventDefault();
26067 return;
26068 }
26069
26070 var actions = this.actions;
26071 var hasNone = inStr(actions, TOUCH_ACTION_NONE) && !TOUCH_ACTION_MAP[TOUCH_ACTION_NONE];
26072 var hasPanY = inStr(actions, TOUCH_ACTION_PAN_Y) && !TOUCH_ACTION_MAP[TOUCH_ACTION_PAN_Y];
26073 var hasPanX = inStr(actions, TOUCH_ACTION_PAN_X) && !TOUCH_ACTION_MAP[TOUCH_ACTION_PAN_X];
26074
26075 if (hasNone) {
26076 //do not prevent defaults if this is a tap gesture
26077
26078 var isTapPointer = input.pointers.length === 1;
26079 var isTapMovement = input.distance < 2;
26080 var isTapTouchTime = input.deltaTime < 250;
26081
26082 if (isTapPointer && isTapMovement && isTapTouchTime) {
26083 return;
26084 }
26085 }
26086
26087 if (hasPanX && hasPanY) {
26088 // `pan-x pan-y` means browser handles all scrolling/panning, do not prevent
26089 return;
26090 }
26091
26092 if (hasNone ||
26093 (hasPanY && direction & DIRECTION_HORIZONTAL) ||
26094 (hasPanX && direction & DIRECTION_VERTICAL)) {
26095 return this.preventSrc(srcEvent);
26096 }
26097 },
26098
26099 /**
26100 * call preventDefault to prevent the browser's default behavior (scrolling in most cases)
26101 * @param {Object} srcEvent
26102 */
26103 preventSrc: function(srcEvent) {
26104 this.manager.session.prevented = true;
26105 srcEvent.preventDefault();
26106 }
26107 };
26108
26109 /**
26110 * when the touchActions are collected they are not a valid value, so we need to clean things up. *
26111 * @param {String} actions
26112 * @returns {*}
26113 */
26114 function cleanTouchActions(actions) {
26115 // none
26116 if (inStr(actions, TOUCH_ACTION_NONE)) {
26117 return TOUCH_ACTION_NONE;
26118 }
26119
26120 var hasPanX = inStr(actions, TOUCH_ACTION_PAN_X);
26121 var hasPanY = inStr(actions, TOUCH_ACTION_PAN_Y);
26122
26123 // if both pan-x and pan-y are set (different recognizers
26124 // for different directions, e.g. horizontal pan but vertical swipe?)
26125 // we need none (as otherwise with pan-x pan-y combined none of these
26126 // recognizers will work, since the browser would handle all panning
26127 if (hasPanX && hasPanY) {
26128 return TOUCH_ACTION_NONE;
26129 }
26130
26131 // pan-x OR pan-y
26132 if (hasPanX || hasPanY) {
26133 return hasPanX ? TOUCH_ACTION_PAN_X : TOUCH_ACTION_PAN_Y;
26134 }
26135
26136 // manipulation
26137 if (inStr(actions, TOUCH_ACTION_MANIPULATION)) {
26138 return TOUCH_ACTION_MANIPULATION;
26139 }
26140
26141 return TOUCH_ACTION_AUTO;
26142 }
26143
26144 function getTouchActionProps() {
26145 if (!NATIVE_TOUCH_ACTION) {
26146 return false;
26147 }
26148 var touchMap = {};
26149 var cssSupports = window.CSS && window.CSS.supports;
26150 ['auto', 'manipulation', 'pan-y', 'pan-x', 'pan-x pan-y', 'none'].forEach(function(val) {
26151
26152 // If css.supports is not supported but there is native touch-action assume it supports
26153 // all values. This is the case for IE 10 and 11.
26154 touchMap[val] = cssSupports ? window.CSS.supports('touch-action', val) : true;
26155 });
26156 return touchMap;
26157 }
26158
26159 /**
26160 * Recognizer flow explained; *
26161 * All recognizers have the initial state of POSSIBLE when a input session starts.
26162 * The definition of a input session is from the first input until the last input, with all it's movement in it. *
26163 * Example session for mouse-input: mousedown -> mousemove -> mouseup
26164 *
26165 * On each recognizing cycle (see Manager.recognize) the .recognize() method is executed
26166 * which determines with state it should be.
26167 *
26168 * If the recognizer has the state FAILED, CANCELLED or RECOGNIZED (equals ENDED), it is reset to
26169 * POSSIBLE to give it another change on the next cycle.
26170 *
26171 * Possible
26172 * |
26173 * +-----+---------------+
26174 * | |
26175 * +-----+-----+ |
26176 * | | |
26177 * Failed Cancelled |
26178 * +-------+------+
26179 * | |
26180 * Recognized Began
26181 * |
26182 * Changed
26183 * |
26184 * Ended/Recognized
26185 */
26186 var STATE_POSSIBLE = 1;
26187 var STATE_BEGAN = 2;
26188 var STATE_CHANGED = 4;
26189 var STATE_ENDED = 8;
26190 var STATE_RECOGNIZED = STATE_ENDED;
26191 var STATE_CANCELLED = 16;
26192 var STATE_FAILED = 32;
26193
26194 /**
26195 * Recognizer
26196 * Every recognizer needs to extend from this class.
26197 * @constructor
26198 * @param {Object} options
26199 */
26200 function Recognizer(options) {
26201 this.options = assign({}, this.defaults, options || {});
26202
26203 this.id = uniqueId();
26204
26205 this.manager = null;
26206
26207 // default is enable true
26208 this.options.enable = ifUndefined(this.options.enable, true);
26209
26210 this.state = STATE_POSSIBLE;
26211
26212 this.simultaneous = {};
26213 this.requireFail = [];
26214 }
26215
26216 Recognizer.prototype = {
26217 /**
26218 * @virtual
26219 * @type {Object}
26220 */
26221 defaults: {},
26222
26223 /**
26224 * set options
26225 * @param {Object} options
26226 * @return {Recognizer}
26227 */
26228 set: function(options) {
26229 assign(this.options, options);
26230
26231 // also update the touchAction, in case something changed about the directions/enabled state
26232 this.manager && this.manager.touchAction.update();
26233 return this;
26234 },
26235
26236 /**
26237 * recognize simultaneous with an other recognizer.
26238 * @param {Recognizer} otherRecognizer
26239 * @returns {Recognizer} this
26240 */
26241 recognizeWith: function(otherRecognizer) {
26242 if (invokeArrayArg(otherRecognizer, 'recognizeWith', this)) {
26243 return this;
26244 }
26245
26246 var simultaneous = this.simultaneous;
26247 otherRecognizer = getRecognizerByNameIfManager(otherRecognizer, this);
26248 if (!simultaneous[otherRecognizer.id]) {
26249 simultaneous[otherRecognizer.id] = otherRecognizer;
26250 otherRecognizer.recognizeWith(this);
26251 }
26252 return this;
26253 },
26254
26255 /**
26256 * drop the simultaneous link. it doesnt remove the link on the other recognizer.
26257 * @param {Recognizer} otherRecognizer
26258 * @returns {Recognizer} this
26259 */
26260 dropRecognizeWith: function(otherRecognizer) {
26261 if (invokeArrayArg(otherRecognizer, 'dropRecognizeWith', this)) {
26262 return this;
26263 }
26264
26265 otherRecognizer = getRecognizerByNameIfManager(otherRecognizer, this);
26266 delete this.simultaneous[otherRecognizer.id];
26267 return this;
26268 },
26269
26270 /**
26271 * recognizer can only run when an other is failing
26272 * @param {Recognizer} otherRecognizer
26273 * @returns {Recognizer} this
26274 */
26275 requireFailure: function(otherRecognizer) {
26276 if (invokeArrayArg(otherRecognizer, 'requireFailure', this)) {
26277 return this;
26278 }
26279
26280 var requireFail = this.requireFail;
26281 otherRecognizer = getRecognizerByNameIfManager(otherRecognizer, this);
26282 if (inArray(requireFail, otherRecognizer) === -1) {
26283 requireFail.push(otherRecognizer);
26284 otherRecognizer.requireFailure(this);
26285 }
26286 return this;
26287 },
26288
26289 /**
26290 * drop the requireFailure link. it does not remove the link on the other recognizer.
26291 * @param {Recognizer} otherRecognizer
26292 * @returns {Recognizer} this
26293 */
26294 dropRequireFailure: function(otherRecognizer) {
26295 if (invokeArrayArg(otherRecognizer, 'dropRequireFailure', this)) {
26296 return this;
26297 }
26298
26299 otherRecognizer = getRecognizerByNameIfManager(otherRecognizer, this);
26300 var index = inArray(this.requireFail, otherRecognizer);
26301 if (index > -1) {
26302 this.requireFail.splice(index, 1);
26303 }
26304 return this;
26305 },
26306
26307 /**
26308 * has require failures boolean
26309 * @returns {boolean}
26310 */
26311 hasRequireFailures: function() {
26312 return this.requireFail.length > 0;
26313 },
26314
26315 /**
26316 * if the recognizer can recognize simultaneous with an other recognizer
26317 * @param {Recognizer} otherRecognizer
26318 * @returns {Boolean}
26319 */
26320 canRecognizeWith: function(otherRecognizer) {
26321 return !!this.simultaneous[otherRecognizer.id];
26322 },
26323
26324 /**
26325 * You should use `tryEmit` instead of `emit` directly to check
26326 * that all the needed recognizers has failed before emitting.
26327 * @param {Object} input
26328 */
26329 emit: function(input) {
26330 var self = this;
26331 var state = this.state;
26332
26333 function emit(event) {
26334 self.manager.emit(event, input);
26335 }
26336
26337 // 'panstart' and 'panmove'
26338 if (state < STATE_ENDED) {
26339 emit(self.options.event + stateStr(state));
26340 }
26341
26342 emit(self.options.event); // simple 'eventName' events
26343
26344 if (input.additionalEvent) { // additional event(panleft, panright, pinchin, pinchout...)
26345 emit(input.additionalEvent);
26346 }
26347
26348 // panend and pancancel
26349 if (state >= STATE_ENDED) {
26350 emit(self.options.event + stateStr(state));
26351 }
26352 },
26353
26354 /**
26355 * Check that all the require failure recognizers has failed,
26356 * if true, it emits a gesture event,
26357 * otherwise, setup the state to FAILED.
26358 * @param {Object} input
26359 */
26360 tryEmit: function(input) {
26361 if (this.canEmit()) {
26362 return this.emit(input);
26363 }
26364 // it's failing anyway
26365 this.state = STATE_FAILED;
26366 },
26367
26368 /**
26369 * can we emit?
26370 * @returns {boolean}
26371 */
26372 canEmit: function() {
26373 var i = 0;
26374 while (i < this.requireFail.length) {
26375 if (!(this.requireFail[i].state & (STATE_FAILED | STATE_POSSIBLE))) {
26376 return false;
26377 }
26378 i++;
26379 }
26380 return true;
26381 },
26382
26383 /**
26384 * update the recognizer
26385 * @param {Object} inputData
26386 */
26387 recognize: function(inputData) {
26388 // make a new copy of the inputData
26389 // so we can change the inputData without messing up the other recognizers
26390 var inputDataClone = assign({}, inputData);
26391
26392 // is is enabled and allow recognizing?
26393 if (!boolOrFn(this.options.enable, [this, inputDataClone])) {
26394 this.reset();
26395 this.state = STATE_FAILED;
26396 return;
26397 }
26398
26399 // reset when we've reached the end
26400 if (this.state & (STATE_RECOGNIZED | STATE_CANCELLED | STATE_FAILED)) {
26401 this.state = STATE_POSSIBLE;
26402 }
26403
26404 this.state = this.process(inputDataClone);
26405
26406 // the recognizer has recognized a gesture
26407 // so trigger an event
26408 if (this.state & (STATE_BEGAN | STATE_CHANGED | STATE_ENDED | STATE_CANCELLED)) {
26409 this.tryEmit(inputDataClone);
26410 }
26411 },
26412
26413 /**
26414 * return the state of the recognizer
26415 * the actual recognizing happens in this method
26416 * @virtual
26417 * @param {Object} inputData
26418 * @returns {Const} STATE
26419 */
26420 process: function(inputData) { }, // jshint ignore:line
26421
26422 /**
26423 * return the preferred touch-action
26424 * @virtual
26425 * @returns {Array}
26426 */
26427 getTouchAction: function() { },
26428
26429 /**
26430 * called when the gesture isn't allowed to recognize
26431 * like when another is being recognized or it is disabled
26432 * @virtual
26433 */
26434 reset: function() { }
26435 };
26436
26437 /**
26438 * get a usable string, used as event postfix
26439 * @param {Const} state
26440 * @returns {String} state
26441 */
26442 function stateStr(state) {
26443 if (state & STATE_CANCELLED) {
26444 return 'cancel';
26445 } else if (state & STATE_ENDED) {
26446 return 'end';
26447 } else if (state & STATE_CHANGED) {
26448 return 'move';
26449 } else if (state & STATE_BEGAN) {
26450 return 'start';
26451 }
26452 return '';
26453 }
26454
26455 /**
26456 * direction cons to string
26457 * @param {Const} direction
26458 * @returns {String}
26459 */
26460 function directionStr(direction) {
26461 if (direction == DIRECTION_DOWN) {
26462 return 'down';
26463 } else if (direction == DIRECTION_UP) {
26464 return 'up';
26465 } else if (direction == DIRECTION_LEFT) {
26466 return 'left';
26467 } else if (direction == DIRECTION_RIGHT) {
26468 return 'right';
26469 }
26470 return '';
26471 }
26472
26473 /**
26474 * get a recognizer by name if it is bound to a manager
26475 * @param {Recognizer|String} otherRecognizer
26476 * @param {Recognizer} recognizer
26477 * @returns {Recognizer}
26478 */
26479 function getRecognizerByNameIfManager(otherRecognizer, recognizer) {
26480 var manager = recognizer.manager;
26481 if (manager) {
26482 return manager.get(otherRecognizer);
26483 }
26484 return otherRecognizer;
26485 }
26486
26487 /**
26488 * This recognizer is just used as a base for the simple attribute recognizers.
26489 * @constructor
26490 * @extends Recognizer
26491 */
26492 function AttrRecognizer() {
26493 Recognizer.apply(this, arguments);
26494 }
26495
26496 inherit(AttrRecognizer, Recognizer, {
26497 /**
26498 * @namespace
26499 * @memberof AttrRecognizer
26500 */
26501 defaults: {
26502 /**
26503 * @type {Number}
26504 * @default 1
26505 */
26506 pointers: 1
26507 },
26508
26509 /**
26510 * Used to check if it the recognizer receives valid input, like input.distance > 10.
26511 * @memberof AttrRecognizer
26512 * @param {Object} input
26513 * @returns {Boolean} recognized
26514 */
26515 attrTest: function(input) {
26516 var optionPointers = this.options.pointers;
26517 return optionPointers === 0 || input.pointers.length === optionPointers;
26518 },
26519
26520 /**
26521 * Process the input and return the state for the recognizer
26522 * @memberof AttrRecognizer
26523 * @param {Object} input
26524 * @returns {*} State
26525 */
26526 process: function(input) {
26527 var state = this.state;
26528 var eventType = input.eventType;
26529
26530 var isRecognized = state & (STATE_BEGAN | STATE_CHANGED);
26531 var isValid = this.attrTest(input);
26532
26533 // on cancel input and we've recognized before, return STATE_CANCELLED
26534 if (isRecognized && (eventType & INPUT_CANCEL || !isValid)) {
26535 return state | STATE_CANCELLED;
26536 } else if (isRecognized || isValid) {
26537 if (eventType & INPUT_END) {
26538 return state | STATE_ENDED;
26539 } else if (!(state & STATE_BEGAN)) {
26540 return STATE_BEGAN;
26541 }
26542 return state | STATE_CHANGED;
26543 }
26544 return STATE_FAILED;
26545 }
26546 });
26547
26548 /**
26549 * Pan
26550 * Recognized when the pointer is down and moved in the allowed direction.
26551 * @constructor
26552 * @extends AttrRecognizer
26553 */
26554 function PanRecognizer() {
26555 AttrRecognizer.apply(this, arguments);
26556
26557 this.pX = null;
26558 this.pY = null;
26559 }
26560
26561 inherit(PanRecognizer, AttrRecognizer, {
26562 /**
26563 * @namespace
26564 * @memberof PanRecognizer
26565 */
26566 defaults: {
26567 event: 'pan',
26568 threshold: 10,
26569 pointers: 1,
26570 direction: DIRECTION_ALL
26571 },
26572
26573 getTouchAction: function() {
26574 var direction = this.options.direction;
26575 var actions = [];
26576 if (direction & DIRECTION_HORIZONTAL) {
26577 actions.push(TOUCH_ACTION_PAN_Y);
26578 }
26579 if (direction & DIRECTION_VERTICAL) {
26580 actions.push(TOUCH_ACTION_PAN_X);
26581 }
26582 return actions;
26583 },
26584
26585 directionTest: function(input) {
26586 var options = this.options;
26587 var hasMoved = true;
26588 var distance = input.distance;
26589 var direction = input.direction;
26590 var x = input.deltaX;
26591 var y = input.deltaY;
26592
26593 // lock to axis?
26594 if (!(direction & options.direction)) {
26595 if (options.direction & DIRECTION_HORIZONTAL) {
26596 direction = (x === 0) ? DIRECTION_NONE : (x < 0) ? DIRECTION_LEFT : DIRECTION_RIGHT;
26597 hasMoved = x != this.pX;
26598 distance = Math.abs(input.deltaX);
26599 } else {
26600 direction = (y === 0) ? DIRECTION_NONE : (y < 0) ? DIRECTION_UP : DIRECTION_DOWN;
26601 hasMoved = y != this.pY;
26602 distance = Math.abs(input.deltaY);
26603 }
26604 }
26605 input.direction = direction;
26606 return hasMoved && distance > options.threshold && direction & options.direction;
26607 },
26608
26609 attrTest: function(input) {
26610 return AttrRecognizer.prototype.attrTest.call(this, input) &&
26611 (this.state & STATE_BEGAN || (!(this.state & STATE_BEGAN) && this.directionTest(input)));
26612 },
26613
26614 emit: function(input) {
26615
26616 this.pX = input.deltaX;
26617 this.pY = input.deltaY;
26618
26619 var direction = directionStr(input.direction);
26620
26621 if (direction) {
26622 input.additionalEvent = this.options.event + direction;
26623 }
26624 this._super.emit.call(this, input);
26625 }
26626 });
26627
26628 /**
26629 * Pinch
26630 * Recognized when two or more pointers are moving toward (zoom-in) or away from each other (zoom-out).
26631 * @constructor
26632 * @extends AttrRecognizer
26633 */
26634 function PinchRecognizer() {
26635 AttrRecognizer.apply(this, arguments);
26636 }
26637
26638 inherit(PinchRecognizer, AttrRecognizer, {
26639 /**
26640 * @namespace
26641 * @memberof PinchRecognizer
26642 */
26643 defaults: {
26644 event: 'pinch',
26645 threshold: 0,
26646 pointers: 2
26647 },
26648
26649 getTouchAction: function() {
26650 return [TOUCH_ACTION_NONE];
26651 },
26652
26653 attrTest: function(input) {
26654 return this._super.attrTest.call(this, input) &&
26655 (Math.abs(input.scale - 1) > this.options.threshold || this.state & STATE_BEGAN);
26656 },
26657
26658 emit: function(input) {
26659 if (input.scale !== 1) {
26660 var inOut = input.scale < 1 ? 'in' : 'out';
26661 input.additionalEvent = this.options.event + inOut;
26662 }
26663 this._super.emit.call(this, input);
26664 }
26665 });
26666
26667 /**
26668 * Press
26669 * Recognized when the pointer is down for x ms without any movement.
26670 * @constructor
26671 * @extends Recognizer
26672 */
26673 function PressRecognizer() {
26674 Recognizer.apply(this, arguments);
26675
26676 this._timer = null;
26677 this._input = null;
26678 }
26679
26680 inherit(PressRecognizer, Recognizer, {
26681 /**
26682 * @namespace
26683 * @memberof PressRecognizer
26684 */
26685 defaults: {
26686 event: 'press',
26687 pointers: 1,
26688 time: 251, // minimal time of the pointer to be pressed
26689 threshold: 9 // a minimal movement is ok, but keep it low
26690 },
26691
26692 getTouchAction: function() {
26693 return [TOUCH_ACTION_AUTO];
26694 },
26695
26696 process: function(input) {
26697 var options = this.options;
26698 var validPointers = input.pointers.length === options.pointers;
26699 var validMovement = input.distance < options.threshold;
26700 var validTime = input.deltaTime > options.time;
26701
26702 this._input = input;
26703
26704 // we only allow little movement
26705 // and we've reached an end event, so a tap is possible
26706 if (!validMovement || !validPointers || (input.eventType & (INPUT_END | INPUT_CANCEL) && !validTime)) {
26707 this.reset();
26708 } else if (input.eventType & INPUT_START) {
26709 this.reset();
26710 this._timer = setTimeoutContext(function() {
26711 this.state = STATE_RECOGNIZED;
26712 this.tryEmit();
26713 }, options.time, this);
26714 } else if (input.eventType & INPUT_END) {
26715 return STATE_RECOGNIZED;
26716 }
26717 return STATE_FAILED;
26718 },
26719
26720 reset: function() {
26721 clearTimeout(this._timer);
26722 },
26723
26724 emit: function(input) {
26725 if (this.state !== STATE_RECOGNIZED) {
26726 return;
26727 }
26728
26729 if (input && (input.eventType & INPUT_END)) {
26730 this.manager.emit(this.options.event + 'up', input);
26731 } else {
26732 this._input.timeStamp = now();
26733 this.manager.emit(this.options.event, this._input);
26734 }
26735 }
26736 });
26737
26738 /**
26739 * Rotate
26740 * Recognized when two or more pointer are moving in a circular motion.
26741 * @constructor
26742 * @extends AttrRecognizer
26743 */
26744 function RotateRecognizer() {
26745 AttrRecognizer.apply(this, arguments);
26746 }
26747
26748 inherit(RotateRecognizer, AttrRecognizer, {
26749 /**
26750 * @namespace
26751 * @memberof RotateRecognizer
26752 */
26753 defaults: {
26754 event: 'rotate',
26755 threshold: 0,
26756 pointers: 2
26757 },
26758
26759 getTouchAction: function() {
26760 return [TOUCH_ACTION_NONE];
26761 },
26762
26763 attrTest: function(input) {
26764 return this._super.attrTest.call(this, input) &&
26765 (Math.abs(input.rotation) > this.options.threshold || this.state & STATE_BEGAN);
26766 }
26767 });
26768
26769 /**
26770 * Swipe
26771 * Recognized when the pointer is moving fast (velocity), with enough distance in the allowed direction.
26772 * @constructor
26773 * @extends AttrRecognizer
26774 */
26775 function SwipeRecognizer() {
26776 AttrRecognizer.apply(this, arguments);
26777 }
26778
26779 inherit(SwipeRecognizer, AttrRecognizer, {
26780 /**
26781 * @namespace
26782 * @memberof SwipeRecognizer
26783 */
26784 defaults: {
26785 event: 'swipe',
26786 threshold: 10,
26787 velocity: 0.3,
26788 direction: DIRECTION_HORIZONTAL | DIRECTION_VERTICAL,
26789 pointers: 1
26790 },
26791
26792 getTouchAction: function() {
26793 return PanRecognizer.prototype.getTouchAction.call(this);
26794 },
26795
26796 attrTest: function(input) {
26797 var direction = this.options.direction;
26798 var velocity;
26799
26800 if (direction & (DIRECTION_HORIZONTAL | DIRECTION_VERTICAL)) {
26801 velocity = input.overallVelocity;
26802 } else if (direction & DIRECTION_HORIZONTAL) {
26803 velocity = input.overallVelocityX;
26804 } else if (direction & DIRECTION_VERTICAL) {
26805 velocity = input.overallVelocityY;
26806 }
26807
26808 return this._super.attrTest.call(this, input) &&
26809 direction & input.offsetDirection &&
26810 input.distance > this.options.threshold &&
26811 input.maxPointers == this.options.pointers &&
26812 abs(velocity) > this.options.velocity && input.eventType & INPUT_END;
26813 },
26814
26815 emit: function(input) {
26816 var direction = directionStr(input.offsetDirection);
26817 if (direction) {
26818 this.manager.emit(this.options.event + direction, input);
26819 }
26820
26821 this.manager.emit(this.options.event, input);
26822 }
26823 });
26824
26825 /**
26826 * A tap is ecognized when the pointer is doing a small tap/click. Multiple taps are recognized if they occur
26827 * between the given interval and position. The delay option can be used to recognize multi-taps without firing
26828 * a single tap.
26829 *
26830 * The eventData from the emitted event contains the property `tapCount`, which contains the amount of
26831 * multi-taps being recognized.
26832 * @constructor
26833 * @extends Recognizer
26834 */
26835 function TapRecognizer() {
26836 Recognizer.apply(this, arguments);
26837
26838 // previous time and center,
26839 // used for tap counting
26840 this.pTime = false;
26841 this.pCenter = false;
26842
26843 this._timer = null;
26844 this._input = null;
26845 this.count = 0;
26846 }
26847
26848 inherit(TapRecognizer, Recognizer, {
26849 /**
26850 * @namespace
26851 * @memberof PinchRecognizer
26852 */
26853 defaults: {
26854 event: 'tap',
26855 pointers: 1,
26856 taps: 1,
26857 interval: 300, // max time between the multi-tap taps
26858 time: 250, // max time of the pointer to be down (like finger on the screen)
26859 threshold: 9, // a minimal movement is ok, but keep it low
26860 posThreshold: 10 // a multi-tap can be a bit off the initial position
26861 },
26862
26863 getTouchAction: function() {
26864 return [TOUCH_ACTION_MANIPULATION];
26865 },
26866
26867 process: function(input) {
26868 var options = this.options;
26869
26870 var validPointers = input.pointers.length === options.pointers;
26871 var validMovement = input.distance < options.threshold;
26872 var validTouchTime = input.deltaTime < options.time;
26873
26874 this.reset();
26875
26876 if ((input.eventType & INPUT_START) && (this.count === 0)) {
26877 return this.failTimeout();
26878 }
26879
26880 // we only allow little movement
26881 // and we've reached an end event, so a tap is possible
26882 if (validMovement && validTouchTime && validPointers) {
26883 if (input.eventType != INPUT_END) {
26884 return this.failTimeout();
26885 }
26886
26887 var validInterval = this.pTime ? (input.timeStamp - this.pTime < options.interval) : true;
26888 var validMultiTap = !this.pCenter || getDistance(this.pCenter, input.center) < options.posThreshold;
26889
26890 this.pTime = input.timeStamp;
26891 this.pCenter = input.center;
26892
26893 if (!validMultiTap || !validInterval) {
26894 this.count = 1;
26895 } else {
26896 this.count += 1;
26897 }
26898
26899 this._input = input;
26900
26901 // if tap count matches we have recognized it,
26902 // else it has began recognizing...
26903 var tapCount = this.count % options.taps;
26904 if (tapCount === 0) {
26905 // no failing requirements, immediately trigger the tap event
26906 // or wait as long as the multitap interval to trigger
26907 if (!this.hasRequireFailures()) {
26908 return STATE_RECOGNIZED;
26909 } else {
26910 this._timer = setTimeoutContext(function() {
26911 this.state = STATE_RECOGNIZED;
26912 this.tryEmit();
26913 }, options.interval, this);
26914 return STATE_BEGAN;
26915 }
26916 }
26917 }
26918 return STATE_FAILED;
26919 },
26920
26921 failTimeout: function() {
26922 this._timer = setTimeoutContext(function() {
26923 this.state = STATE_FAILED;
26924 }, this.options.interval, this);
26925 return STATE_FAILED;
26926 },
26927
26928 reset: function() {
26929 clearTimeout(this._timer);
26930 },
26931
26932 emit: function() {
26933 if (this.state == STATE_RECOGNIZED) {
26934 this._input.tapCount = this.count;
26935 this.manager.emit(this.options.event, this._input);
26936 }
26937 }
26938 });
26939
26940 /**
26941 * Simple way to create a manager with a default set of recognizers.
26942 * @param {HTMLElement} element
26943 * @param {Object} [options]
26944 * @constructor
26945 */
26946 function Hammer(element, options) {
26947 options = options || {};
26948 options.recognizers = ifUndefined(options.recognizers, Hammer.defaults.preset);
26949 return new Manager(element, options);
26950 }
26951
26952 /**
26953 * @const {string}
26954 */
26955 Hammer.VERSION = '2.0.7';
26956
26957 /**
26958 * default settings
26959 * @namespace
26960 */
26961 Hammer.defaults = {
26962 /**
26963 * set if DOM events are being triggered.
26964 * But this is slower and unused by simple implementations, so disabled by default.
26965 * @type {Boolean}
26966 * @default false
26967 */
26968 domEvents: false,
26969
26970 /**
26971 * The value for the touchAction property/fallback.
26972 * When set to `compute` it will magically set the correct value based on the added recognizers.
26973 * @type {String}
26974 * @default compute
26975 */
26976 touchAction: TOUCH_ACTION_COMPUTE,
26977
26978 /**
26979 * @type {Boolean}
26980 * @default true
26981 */
26982 enable: true,
26983
26984 /**
26985 * EXPERIMENTAL FEATURE -- can be removed/changed
26986 * Change the parent input target element.
26987 * If Null, then it is being set the to main element.
26988 * @type {Null|EventTarget}
26989 * @default null
26990 */
26991 inputTarget: null,
26992
26993 /**
26994 * force an input class
26995 * @type {Null|Function}
26996 * @default null
26997 */
26998 inputClass: null,
26999
27000 /**
27001 * Default recognizer setup when calling `Hammer()`
27002 * When creating a new Manager these will be skipped.
27003 * @type {Array}
27004 */
27005 preset: [
27006 // RecognizerClass, options, [recognizeWith, ...], [requireFailure, ...]
27007 [RotateRecognizer, {enable: false}],
27008 [PinchRecognizer, {enable: false}, ['rotate']],
27009 [SwipeRecognizer, {direction: DIRECTION_HORIZONTAL}],
27010 [PanRecognizer, {direction: DIRECTION_HORIZONTAL}, ['swipe']],
27011 [TapRecognizer],
27012 [TapRecognizer, {event: 'doubletap', taps: 2}, ['tap']],
27013 [PressRecognizer]
27014 ],
27015
27016 /**
27017 * Some CSS properties can be used to improve the working of Hammer.
27018 * Add them to this method and they will be set when creating a new Manager.
27019 * @namespace
27020 */
27021 cssProps: {
27022 /**
27023 * Disables text selection to improve the dragging gesture. Mainly for desktop browsers.
27024 * @type {String}
27025 * @default 'none'
27026 */
27027 userSelect: 'none',
27028
27029 /**
27030 * Disable the Windows Phone grippers when pressing an element.
27031 * @type {String}
27032 * @default 'none'
27033 */
27034 touchSelect: 'none',
27035
27036 /**
27037 * Disables the default callout shown when you touch and hold a touch target.
27038 * On iOS, when you touch and hold a touch target such as a link, Safari displays
27039 * a callout containing information about the link. This property allows you to disable that callout.
27040 * @type {String}
27041 * @default 'none'
27042 */
27043 touchCallout: 'none',
27044
27045 /**
27046 * Specifies whether zooming is enabled. Used by IE10>
27047 * @type {String}
27048 * @default 'none'
27049 */
27050 contentZooming: 'none',
27051
27052 /**
27053 * Specifies that an entire element should be draggable instead of its contents. Mainly for desktop browsers.
27054 * @type {String}
27055 * @default 'none'
27056 */
27057 userDrag: 'none',
27058
27059 /**
27060 * Overrides the highlight color shown when the user taps a link or a JavaScript
27061 * clickable element in iOS. This property obeys the alpha value, if specified.
27062 * @type {String}
27063 * @default 'rgba(0,0,0,0)'
27064 */
27065 tapHighlightColor: 'rgba(0,0,0,0)'
27066 }
27067 };
27068
27069 var STOP = 1;
27070 var FORCED_STOP = 2;
27071
27072 /**
27073 * Manager
27074 * @param {HTMLElement} element
27075 * @param {Object} [options]
27076 * @constructor
27077 */
27078 function Manager(element, options) {
27079 this.options = assign({}, Hammer.defaults, options || {});
27080
27081 this.options.inputTarget = this.options.inputTarget || element;
27082
27083 this.handlers = {};
27084 this.session = {};
27085 this.recognizers = [];
27086 this.oldCssProps = {};
27087
27088 this.element = element;
27089 this.input = createInputInstance(this);
27090 this.touchAction = new TouchAction(this, this.options.touchAction);
27091
27092 toggleCssProps(this, true);
27093
27094 each(this.options.recognizers, function(item) {
27095 var recognizer = this.add(new (item[0])(item[1]));
27096 item[2] && recognizer.recognizeWith(item[2]);
27097 item[3] && recognizer.requireFailure(item[3]);
27098 }, this);
27099 }
27100
27101 Manager.prototype = {
27102 /**
27103 * set options
27104 * @param {Object} options
27105 * @returns {Manager}
27106 */
27107 set: function(options) {
27108 assign(this.options, options);
27109
27110 // Options that need a little more setup
27111 if (options.touchAction) {
27112 this.touchAction.update();
27113 }
27114 if (options.inputTarget) {
27115 // Clean up existing event listeners and reinitialize
27116 this.input.destroy();
27117 this.input.target = options.inputTarget;
27118 this.input.init();
27119 }
27120 return this;
27121 },
27122
27123 /**
27124 * stop recognizing for this session.
27125 * This session will be discarded, when a new [input]start event is fired.
27126 * When forced, the recognizer cycle is stopped immediately.
27127 * @param {Boolean} [force]
27128 */
27129 stop: function(force) {
27130 this.session.stopped = force ? FORCED_STOP : STOP;
27131 },
27132
27133 /**
27134 * run the recognizers!
27135 * called by the inputHandler function on every movement of the pointers (touches)
27136 * it walks through all the recognizers and tries to detect the gesture that is being made
27137 * @param {Object} inputData
27138 */
27139 recognize: function(inputData) {
27140 var session = this.session;
27141 if (session.stopped) {
27142 return;
27143 }
27144
27145 // run the touch-action polyfill
27146 this.touchAction.preventDefaults(inputData);
27147
27148 var recognizer;
27149 var recognizers = this.recognizers;
27150
27151 // this holds the recognizer that is being recognized.
27152 // so the recognizer's state needs to be BEGAN, CHANGED, ENDED or RECOGNIZED
27153 // if no recognizer is detecting a thing, it is set to `null`
27154 var curRecognizer = session.curRecognizer;
27155
27156 // reset when the last recognizer is recognized
27157 // or when we're in a new session
27158 if (!curRecognizer || (curRecognizer && curRecognizer.state & STATE_RECOGNIZED)) {
27159 curRecognizer = session.curRecognizer = null;
27160 }
27161
27162 var i = 0;
27163 while (i < recognizers.length) {
27164 recognizer = recognizers[i];
27165
27166 // find out if we are allowed try to recognize the input for this one.
27167 // 1. allow if the session is NOT forced stopped (see the .stop() method)
27168 // 2. allow if we still haven't recognized a gesture in this session, or the this recognizer is the one
27169 // that is being recognized.
27170 // 3. allow if the recognizer is allowed to run simultaneous with the current recognized recognizer.
27171 // this can be setup with the `recognizeWith()` method on the recognizer.
27172 if (session.stopped !== FORCED_STOP && ( // 1
27173 !curRecognizer || recognizer == curRecognizer || // 2
27174 recognizer.canRecognizeWith(curRecognizer))) { // 3
27175 recognizer.recognize(inputData);
27176 } else {
27177 recognizer.reset();
27178 }
27179
27180 // if the recognizer has been recognizing the input as a valid gesture, we want to store this one as the
27181 // current active recognizer. but only if we don't already have an active recognizer
27182 if (!curRecognizer && recognizer.state & (STATE_BEGAN | STATE_CHANGED | STATE_ENDED)) {
27183 curRecognizer = session.curRecognizer = recognizer;
27184 }
27185 i++;
27186 }
27187 },
27188
27189 /**
27190 * get a recognizer by its event name.
27191 * @param {Recognizer|String} recognizer
27192 * @returns {Recognizer|Null}
27193 */
27194 get: function(recognizer) {
27195 if (recognizer instanceof Recognizer) {
27196 return recognizer;
27197 }
27198
27199 var recognizers = this.recognizers;
27200 for (var i = 0; i < recognizers.length; i++) {
27201 if (recognizers[i].options.event == recognizer) {
27202 return recognizers[i];
27203 }
27204 }
27205 return null;
27206 },
27207
27208 /**
27209 * add a recognizer to the manager
27210 * existing recognizers with the same event name will be removed
27211 * @param {Recognizer} recognizer
27212 * @returns {Recognizer|Manager}
27213 */
27214 add: function(recognizer) {
27215 if (invokeArrayArg(recognizer, 'add', this)) {
27216 return this;
27217 }
27218
27219 // remove existing
27220 var existing = this.get(recognizer.options.event);
27221 if (existing) {
27222 this.remove(existing);
27223 }
27224
27225 this.recognizers.push(recognizer);
27226 recognizer.manager = this;
27227
27228 this.touchAction.update();
27229 return recognizer;
27230 },
27231
27232 /**
27233 * remove a recognizer by name or instance
27234 * @param {Recognizer|String} recognizer
27235 * @returns {Manager}
27236 */
27237 remove: function(recognizer) {
27238 if (invokeArrayArg(recognizer, 'remove', this)) {
27239 return this;
27240 }
27241
27242 recognizer = this.get(recognizer);
27243
27244 // let's make sure this recognizer exists
27245 if (recognizer) {
27246 var recognizers = this.recognizers;
27247 var index = inArray(recognizers, recognizer);
27248
27249 if (index !== -1) {
27250 recognizers.splice(index, 1);
27251 this.touchAction.update();
27252 }
27253 }
27254
27255 return this;
27256 },
27257
27258 /**
27259 * bind event
27260 * @param {String} events
27261 * @param {Function} handler
27262 * @returns {EventEmitter} this
27263 */
27264 on: function(events, handler) {
27265 if (events === undefined$1) {
27266 return;
27267 }
27268 if (handler === undefined$1) {
27269 return;
27270 }
27271
27272 var handlers = this.handlers;
27273 each(splitStr(events), function(event) {
27274 handlers[event] = handlers[event] || [];
27275 handlers[event].push(handler);
27276 });
27277 return this;
27278 },
27279
27280 /**
27281 * unbind event, leave emit blank to remove all handlers
27282 * @param {String} events
27283 * @param {Function} [handler]
27284 * @returns {EventEmitter} this
27285 */
27286 off: function(events, handler) {
27287 if (events === undefined$1) {
27288 return;
27289 }
27290
27291 var handlers = this.handlers;
27292 each(splitStr(events), function(event) {
27293 if (!handler) {
27294 delete handlers[event];
27295 } else {
27296 handlers[event] && handlers[event].splice(inArray(handlers[event], handler), 1);
27297 }
27298 });
27299 return this;
27300 },
27301
27302 /**
27303 * emit event to the listeners
27304 * @param {String} event
27305 * @param {Object} data
27306 */
27307 emit: function(event, data) {
27308 // we also want to trigger dom events
27309 if (this.options.domEvents) {
27310 triggerDomEvent(event, data);
27311 }
27312
27313 // no handlers, so skip it all
27314 var handlers = this.handlers[event] && this.handlers[event].slice();
27315 if (!handlers || !handlers.length) {
27316 return;
27317 }
27318
27319 data.type = event;
27320 data.preventDefault = function() {
27321 data.srcEvent.preventDefault();
27322 };
27323
27324 var i = 0;
27325 while (i < handlers.length) {
27326 handlers[i](data);
27327 i++;
27328 }
27329 },
27330
27331 /**
27332 * destroy the manager and unbinds all events
27333 * it doesn't unbind dom events, that is the user own responsibility
27334 */
27335 destroy: function() {
27336 this.element && toggleCssProps(this, false);
27337
27338 this.handlers = {};
27339 this.session = {};
27340 this.input.destroy();
27341 this.element = null;
27342 }
27343 };
27344
27345 /**
27346 * add/remove the css properties as defined in manager.options.cssProps
27347 * @param {Manager} manager
27348 * @param {Boolean} add
27349 */
27350 function toggleCssProps(manager, add) {
27351 var element = manager.element;
27352 if (!element.style) {
27353 return;
27354 }
27355 var prop;
27356 each(manager.options.cssProps, function(value, name) {
27357 prop = prefixed(element.style, name);
27358 if (add) {
27359 manager.oldCssProps[prop] = element.style[prop];
27360 element.style[prop] = value;
27361 } else {
27362 element.style[prop] = manager.oldCssProps[prop] || '';
27363 }
27364 });
27365 if (!add) {
27366 manager.oldCssProps = {};
27367 }
27368 }
27369
27370 /**
27371 * trigger dom event
27372 * @param {String} event
27373 * @param {Object} data
27374 */
27375 function triggerDomEvent(event, data) {
27376 var gestureEvent = document.createEvent('Event');
27377 gestureEvent.initEvent(event, true, true);
27378 gestureEvent.gesture = data;
27379 data.target.dispatchEvent(gestureEvent);
27380 }
27381
27382 assign(Hammer, {
27383 INPUT_START: INPUT_START,
27384 INPUT_MOVE: INPUT_MOVE,
27385 INPUT_END: INPUT_END,
27386 INPUT_CANCEL: INPUT_CANCEL,
27387
27388 STATE_POSSIBLE: STATE_POSSIBLE,
27389 STATE_BEGAN: STATE_BEGAN,
27390 STATE_CHANGED: STATE_CHANGED,
27391 STATE_ENDED: STATE_ENDED,
27392 STATE_RECOGNIZED: STATE_RECOGNIZED,
27393 STATE_CANCELLED: STATE_CANCELLED,
27394 STATE_FAILED: STATE_FAILED,
27395
27396 DIRECTION_NONE: DIRECTION_NONE,
27397 DIRECTION_LEFT: DIRECTION_LEFT,
27398 DIRECTION_RIGHT: DIRECTION_RIGHT,
27399 DIRECTION_UP: DIRECTION_UP,
27400 DIRECTION_DOWN: DIRECTION_DOWN,
27401 DIRECTION_HORIZONTAL: DIRECTION_HORIZONTAL,
27402 DIRECTION_VERTICAL: DIRECTION_VERTICAL,
27403 DIRECTION_ALL: DIRECTION_ALL,
27404
27405 Manager: Manager,
27406 Input: Input,
27407 TouchAction: TouchAction,
27408
27409 TouchInput: TouchInput,
27410 MouseInput: MouseInput,
27411 PointerEventInput: PointerEventInput,
27412 TouchMouseInput: TouchMouseInput,
27413 SingleTouchInput: SingleTouchInput,
27414
27415 Recognizer: Recognizer,
27416 AttrRecognizer: AttrRecognizer,
27417 Tap: TapRecognizer,
27418 Pan: PanRecognizer,
27419 Swipe: SwipeRecognizer,
27420 Pinch: PinchRecognizer,
27421 Rotate: RotateRecognizer,
27422 Press: PressRecognizer,
27423
27424 on: addEventListeners,
27425 off: removeEventListeners,
27426 each: each,
27427 merge: merge,
27428 extend: extend,
27429 assign: assign,
27430 inherit: inherit,
27431 bindFn: bindFn,
27432 prefixed: prefixed
27433 });
27434
27435 // this prevents errors when Hammer is loaded in the presence of an AMD
27436 // style loader but by script tag, not by the loader.
27437 var freeGlobal = (typeof window !== 'undefined' ? window : (typeof self !== 'undefined' ? self : {})); // jshint ignore:line
27438 freeGlobal.Hammer = Hammer;
27439
27440 if (typeof undefined$1 === 'function' && undefined$1.amd) {
27441 undefined$1(function() {
27442 return Hammer;
27443 });
27444 } else if (module.exports) {
27445 module.exports = Hammer;
27446 } else {
27447 window[exportName] = Hammer;
27448 }
27449
27450 })(window, document, 'Hammer');
27451 }(hammer));
27452
27453 var Hammer = hammer.exports;
27454
27455 var MIN_ZOOM = 0.2,
27456 MAX_ZOOM = 4;
27457
27458 var mouseEvents = [
27459 'mousedown',
27460 'mouseup',
27461 'mouseover',
27462 'mouseout',
27463 'click',
27464 'dblclick'
27465 ];
27466
27467 function get(service, injector) {
27468 return injector.get(service, false);
27469 }
27470
27471 function stopEvent(event) {
27472
27473 event.preventDefault();
27474
27475 if (typeof event.stopPropagation === 'function') {
27476 event.stopPropagation();
27477 } else if (event.srcEvent && typeof event.srcEvent.stopPropagation === 'function') {
27478
27479 // iPhone & iPad
27480 event.srcEvent.stopPropagation();
27481 }
27482
27483 if (typeof event.stopImmediatePropagation === 'function') {
27484 event.stopImmediatePropagation();
27485 }
27486 }
27487
27488
27489 function createTouchRecognizer(node) {
27490
27491 function stopMouse(event) {
27492
27493 forEach$2(mouseEvents, function(e) {
27494 componentEvent.bind(node, e, stopEvent, true);
27495 });
27496 }
27497
27498 function allowMouse(event) {
27499 setTimeout(function() {
27500 forEach$2(mouseEvents, function(e) {
27501 componentEvent.unbind(node, e, stopEvent, true);
27502 });
27503 }, 500);
27504 }
27505
27506 componentEvent.bind(node, 'touchstart', stopMouse, true);
27507 componentEvent.bind(node, 'touchend', allowMouse, true);
27508 componentEvent.bind(node, 'touchcancel', allowMouse, true);
27509
27510 // A touch event recognizer that handles
27511 // touch events only (we know, we can already handle
27512 // mouse events out of the box)
27513
27514 var recognizer = new Hammer.Manager(node, {
27515 inputClass: Hammer.TouchInput,
27516 recognizers: [],
27517 domEvents: true
27518 });
27519
27520
27521 var tap = new Hammer.Tap();
27522 var pan = new Hammer.Pan({ threshold: 10 });
27523 var press = new Hammer.Press();
27524 var pinch = new Hammer.Pinch();
27525
27526 var doubleTap = new Hammer.Tap({ event: 'doubletap', taps: 2 });
27527
27528 pinch.requireFailure(pan);
27529 pinch.requireFailure(press);
27530
27531 recognizer.add([ pan, press, pinch, doubleTap, tap ]);
27532
27533 recognizer.reset = function(force) {
27534 var recognizers = this.recognizers,
27535 session = this.session;
27536
27537 if (session.stopped) {
27538 return;
27539 }
27540
27541 recognizer.stop(force);
27542
27543 setTimeout(function() {
27544 var i, r;
27545 for (i = 0; (r = recognizers[i]); i++) {
27546 r.reset();
27547 r.state = 8; // FAILED STATE
27548 }
27549
27550 session.curRecognizer = null;
27551 }, 0);
27552 };
27553
27554 recognizer.on('hammer.input', function(event) {
27555 if (event.srcEvent.defaultPrevented) {
27556 recognizer.reset(true);
27557 }
27558 });
27559
27560 return recognizer;
27561 }
27562
27563 /**
27564 * A plugin that provides touch events for elements.
27565 *
27566 * @param {EventBus} eventBus
27567 * @param {InteractionEvents} interactionEvents
27568 */
27569 function TouchInteractionEvents(
27570 injector, canvas, eventBus,
27571 elementRegistry, interactionEvents) {
27572
27573 // optional integrations
27574 var dragging = get('dragging', injector),
27575 move = get('move', injector),
27576 contextPad = get('contextPad', injector),
27577 palette = get('palette', injector);
27578
27579 // the touch recognizer
27580 var recognizer;
27581
27582 function handler(type, buttonType) {
27583
27584 return function(event) {
27585
27586 var gfx = getGfx(event.target),
27587 element = gfx && elementRegistry.get(gfx);
27588
27589 // translate into an actual mouse click event
27590 if (buttonType) {
27591 event.srcEvent.button = buttonType;
27592 }
27593
27594 return interactionEvents.fire(type, event, element);
27595 };
27596 }
27597
27598
27599 function getGfx(target) {
27600 var node = closest(target, 'svg, .djs-element', true);
27601 return node;
27602 }
27603
27604 function initEvents(svg) {
27605
27606 // touch recognizer
27607 recognizer = createTouchRecognizer(svg);
27608
27609 function startGrabCanvas(event) {
27610
27611 var lx = 0, ly = 0;
27612
27613 function update(e) {
27614
27615 var dx = e.deltaX - lx,
27616 dy = e.deltaY - ly;
27617
27618 canvas.scroll({ dx: dx, dy: dy });
27619
27620 lx = e.deltaX;
27621 ly = e.deltaY;
27622 }
27623
27624 function end(e) {
27625 recognizer.off('panmove', update);
27626 recognizer.off('panend', end);
27627 recognizer.off('pancancel', end);
27628 }
27629
27630 recognizer.on('panmove', update);
27631 recognizer.on('panend', end);
27632 recognizer.on('pancancel', end);
27633 }
27634
27635 function startGrab(event) {
27636
27637 var gfx = getGfx(event.target),
27638 element = gfx && elementRegistry.get(gfx);
27639
27640 // recognizer
27641 if (move && canvas.getRootElement() !== element) {
27642 return move.start(event, element, true);
27643 } else {
27644 startGrabCanvas();
27645 }
27646 }
27647
27648 function startZoom(e) {
27649
27650 var zoom = canvas.zoom(),
27651 mid = e.center;
27652
27653 function update(e) {
27654
27655 var ratio = 1 - (1 - e.scale) / 1.50,
27656 newZoom = Math.max(MIN_ZOOM, Math.min(MAX_ZOOM, ratio * zoom));
27657
27658 canvas.zoom(newZoom, mid);
27659
27660 stopEvent(e);
27661 }
27662
27663 function end(e) {
27664 recognizer.off('pinchmove', update);
27665 recognizer.off('pinchend', end);
27666 recognizer.off('pinchcancel', end);
27667
27668 recognizer.reset(true);
27669 }
27670
27671 recognizer.on('pinchmove', update);
27672 recognizer.on('pinchend', end);
27673 recognizer.on('pinchcancel', end);
27674 }
27675
27676 recognizer.on('tap', handler('element.click'));
27677 recognizer.on('doubletap', handler('element.dblclick', 1));
27678
27679 recognizer.on('panstart', startGrab);
27680 recognizer.on('press', startGrab);
27681
27682 recognizer.on('pinchstart', startZoom);
27683 }
27684
27685 if (dragging) {
27686
27687 // simulate hover during dragging
27688 eventBus.on('drag.move', function(event) {
27689
27690 var originalEvent = event.originalEvent;
27691
27692 if (!originalEvent || originalEvent instanceof MouseEvent) {
27693 return;
27694 }
27695
27696 var position = toPoint(originalEvent);
27697
27698 // this gets really expensive ...
27699 var node = document.elementFromPoint(position.x, position.y),
27700 gfx = getGfx(node),
27701 element = gfx && elementRegistry.get(gfx);
27702
27703 if (element !== event.hover) {
27704 if (event.hover) {
27705 dragging.out(event);
27706 }
27707
27708 if (element) {
27709 dragging.hover({ element: element, gfx: gfx });
27710
27711 event.hover = element;
27712 event.hoverGfx = gfx;
27713 }
27714 }
27715 });
27716 }
27717
27718 if (contextPad) {
27719
27720 eventBus.on('contextPad.create', function(event) {
27721 var node = event.pad.html;
27722
27723 // touch recognizer
27724 var padRecognizer = createTouchRecognizer(node);
27725
27726 padRecognizer.on('panstart', function(event) {
27727 contextPad.trigger('dragstart', event, true);
27728 });
27729
27730 padRecognizer.on('press', function(event) {
27731 contextPad.trigger('dragstart', event, true);
27732 });
27733
27734 padRecognizer.on('tap', function(event) {
27735 contextPad.trigger('click', event);
27736 });
27737 });
27738 }
27739
27740 if (palette) {
27741 eventBus.on('palette.create', function(event) {
27742 var node = event.container;
27743
27744 // touch recognizer
27745 var padRecognizer = createTouchRecognizer(node);
27746
27747 padRecognizer.on('panstart', function(event) {
27748 palette.trigger('dragstart', event, true);
27749 });
27750
27751 padRecognizer.on('press', function(event) {
27752 palette.trigger('dragstart', event, true);
27753 });
27754
27755 padRecognizer.on('tap', function(event) {
27756 palette.trigger('click', event);
27757 });
27758 });
27759 }
27760
27761 eventBus.on('canvas.init', function(event) {
27762 initEvents(event.svg);
27763 });
27764 }
27765
27766
27767 TouchInteractionEvents.$inject = [
27768 'injector',
27769 'canvas',
27770 'eventBus',
27771 'elementRegistry',
27772 'interactionEvents',
27773 'touchFix'
27774 ];
27775
27776 function TouchFix(canvas, eventBus) {
27777
27778 var self = this;
27779
27780 eventBus.on('canvas.init', function(e) {
27781 self.addBBoxMarker(e.svg);
27782 });
27783 }
27784
27785 TouchFix.$inject = [ 'canvas', 'eventBus' ];
27786
27787
27788 /**
27789 * Safari mobile (iOS 7) does not fire touchstart event in <SVG> element
27790 * if there is no shape between 0,0 and viewport elements origin.
27791 *
27792 * So touchstart event is only fired when the <g class="viewport"> element was hit.
27793 * Putting an element over and below the 'viewport' fixes that behavior.
27794 */
27795 TouchFix.prototype.addBBoxMarker = function(svg) {
27796
27797 var markerStyle = {
27798 fill: 'none',
27799 class: 'outer-bound-marker'
27800 };
27801
27802 var rect1 = create$1('rect');
27803 attr$1(rect1, {
27804 x: -10000,
27805 y: 10000,
27806 width: 10,
27807 height: 10
27808 });
27809 attr$1(rect1, markerStyle);
27810
27811 append(svg, rect1);
27812
27813 var rect2 = create$1('rect');
27814 attr$1(rect2, {
27815 x: 10000,
27816 y: 10000,
27817 width: 10,
27818 height: 10
27819 });
27820 attr$1(rect2, markerStyle);
27821
27822 append(svg, rect2);
27823 };
27824
27825 var TouchModule$1 = {
27826 __depends__: [ InteractionEventsModule$1 ],
27827 __init__: [ 'touchInteractionEvents' ],
27828 touchInteractionEvents: [ 'type', TouchInteractionEvents ],
27829 touchFix: [ 'type', TouchFix ]
27830 };
27831
27832 var TouchModule = {
27833 __depends__: [
27834 TouchModule$1
27835 ]
27836 };
27837
27838 function last(arr) {
27839 return arr && arr[arr.length - 1];
27840 }
27841
27842 function sortTopOrMiddle(element) {
27843 return element.y;
27844 }
27845
27846 function sortLeftOrCenter(element) {
27847 return element.x;
27848 }
27849
27850 /**
27851 * Sorting functions for different types of alignment
27852 *
27853 * @type {Object}
27854 *
27855 * @return {Function}
27856 */
27857 var ALIGNMENT_SORTING = {
27858 left: sortLeftOrCenter,
27859 center: sortLeftOrCenter,
27860 right: function(element) {
27861 return element.x + element.width;
27862 },
27863 top: sortTopOrMiddle,
27864 middle: sortTopOrMiddle,
27865 bottom: function(element) {
27866 return element.y + element.height;
27867 }
27868 };
27869
27870
27871 function AlignElements$1(modeling) {
27872 this._modeling = modeling;
27873 }
27874
27875 AlignElements$1.$inject = [ 'modeling' ];
27876
27877
27878 /**
27879 * Get the relevant "axis" and "dimension" related to the current type of alignment
27880 *
27881 * @param {string} type left|right|center|top|bottom|middle
27882 *
27883 * @return {Object} { axis, dimension }
27884 */
27885 AlignElements$1.prototype._getOrientationDetails = function(type) {
27886 var vertical = [ 'top', 'bottom', 'middle' ],
27887 axis = 'x',
27888 dimension = 'width';
27889
27890 if (vertical.indexOf(type) !== -1) {
27891 axis = 'y';
27892 dimension = 'height';
27893 }
27894
27895 return {
27896 axis: axis,
27897 dimension: dimension
27898 };
27899 };
27900
27901 AlignElements$1.prototype._isType = function(type, types) {
27902 return types.indexOf(type) !== -1;
27903 };
27904
27905 /**
27906 * Get a point on the relevant axis where elements should align to
27907 *
27908 * @param {string} type left|right|center|top|bottom|middle
27909 * @param {Array} sortedElements
27910 *
27911 * @return {Object}
27912 */
27913 AlignElements$1.prototype._alignmentPosition = function(type, sortedElements) {
27914 var orientation = this._getOrientationDetails(type),
27915 axis = orientation.axis,
27916 dimension = orientation.dimension,
27917 alignment = {},
27918 centers = {},
27919 hasSharedCenters = false,
27920 centeredElements,
27921 firstElement,
27922 lastElement;
27923
27924 function getMiddleOrTop(first, last) {
27925 return Math.round((first[axis] + last[axis] + last[dimension]) / 2);
27926 }
27927
27928 if (this._isType(type, [ 'left', 'top' ])) {
27929 alignment[type] = sortedElements[0][axis];
27930
27931 } else if (this._isType(type, [ 'right', 'bottom' ])) {
27932 lastElement = last(sortedElements);
27933
27934 alignment[type] = lastElement[axis] + lastElement[dimension];
27935
27936 } else if (this._isType(type, [ 'center', 'middle' ])) {
27937
27938 // check if there is a center shared by more than one shape
27939 // if not, just take the middle of the range
27940 forEach$2(sortedElements, function(element) {
27941 var center = element[axis] + Math.round(element[dimension] / 2);
27942
27943 if (centers[center]) {
27944 centers[center].elements.push(element);
27945 } else {
27946 centers[center] = {
27947 elements: [ element ],
27948 center: center
27949 };
27950 }
27951 });
27952
27953 centeredElements = sortBy(centers, function(center) {
27954 if (center.elements.length > 1) {
27955 hasSharedCenters = true;
27956 }
27957
27958 return center.elements.length;
27959 });
27960
27961 if (hasSharedCenters) {
27962 alignment[type] = last(centeredElements).center;
27963
27964 return alignment;
27965 }
27966
27967 firstElement = sortedElements[0];
27968
27969 sortedElements = sortBy(sortedElements, function(element) {
27970 return element[axis] + element[dimension];
27971 });
27972
27973 lastElement = last(sortedElements);
27974
27975 alignment[type] = getMiddleOrTop(firstElement, lastElement);
27976 }
27977
27978 return alignment;
27979 };
27980
27981 /**
27982 * Executes the alignment of a selection of elements
27983 *
27984 * @param {Array} elements
27985 * @param {string} type left|right|center|top|bottom|middle
27986 */
27987 AlignElements$1.prototype.trigger = function(elements, type) {
27988 var modeling = this._modeling;
27989
27990 var filteredElements = filter(elements, function(element) {
27991 return !(element.waypoints || element.host || element.labelTarget);
27992 });
27993
27994 if (filteredElements.length < 2) {
27995 return;
27996 }
27997
27998 var sortFn = ALIGNMENT_SORTING[type];
27999
28000 var sortedElements = sortBy(filteredElements, sortFn);
28001
28002 var alignment = this._alignmentPosition(type, sortedElements);
28003
28004 modeling.alignElements(sortedElements, alignment);
28005 };
28006
28007 var AlignElementsModule = {
28008 __init__: [ 'alignElements' ],
28009 alignElements: [ 'type', AlignElements$1 ]
28010 };
28011
28012 // padding to detect element placement
28013 var PLACEMENT_DETECTION_PAD = 10;
28014
28015 var DEFAULT_DISTANCE = 50;
28016
28017 var DEFAULT_MAX_DISTANCE = 250;
28018
28019
28020 /**
28021 * Get free position starting from given position.
28022 *
28023 * @param {djs.model.Shape} source
28024 * @param {djs.model.Shape} element
28025 * @param {Point} position
28026 * @param {Function} getNextPosition
28027 *
28028 * @return {Point}
28029 */
28030 function findFreePosition(source, element, position, getNextPosition) {
28031 var connectedAtPosition;
28032
28033 while ((connectedAtPosition = getConnectedAtPosition(source, position, element))) {
28034 position = getNextPosition(element, position, connectedAtPosition);
28035 }
28036
28037 return position;
28038 }
28039
28040 /**
28041 * Returns function that returns next position.
28042 *
28043 * @param {Object} nextPositionDirection
28044 * @param {Object} [nextPositionDirection.x]
28045 * @param {Object} [nextPositionDirection.y]
28046 *
28047 * @returns {Function}
28048 */
28049 function generateGetNextPosition(nextPositionDirection) {
28050 return function(element, previousPosition, connectedAtPosition) {
28051 var nextPosition = {
28052 x: previousPosition.x,
28053 y: previousPosition.y
28054 };
28055
28056 [ 'x', 'y' ].forEach(function(axis) {
28057
28058 var nextPositionDirectionForAxis = nextPositionDirection[ axis ];
28059
28060 if (!nextPositionDirectionForAxis) {
28061 return;
28062 }
28063
28064 var dimension = axis === 'x' ? 'width' : 'height';
28065
28066 var margin = nextPositionDirectionForAxis.margin,
28067 minDistance = nextPositionDirectionForAxis.minDistance;
28068
28069 if (margin < 0) {
28070 nextPosition[ axis ] = Math.min(
28071 connectedAtPosition[ axis ] + margin - element[ dimension ] / 2,
28072 previousPosition[ axis ] - minDistance + margin
28073 );
28074 } else {
28075 nextPosition[ axis ] = Math.max(
28076 connectedAtPosition[ axis ] + connectedAtPosition[ dimension ] + margin + element[ dimension ] / 2,
28077 previousPosition[ axis ] + minDistance + margin
28078 );
28079 }
28080 });
28081
28082 return nextPosition;
28083 };
28084 }
28085
28086 /**
28087 * Return target at given position, if defined.
28088 *
28089 * This takes connected elements from host and attachers
28090 * into account, too.
28091 */
28092 function getConnectedAtPosition(source, position, element) {
28093
28094 var bounds = {
28095 x: position.x - (element.width / 2),
28096 y: position.y - (element.height / 2),
28097 width: element.width,
28098 height: element.height
28099 };
28100
28101 var closure = getAutoPlaceClosure(source);
28102
28103 return find(closure, function(target) {
28104
28105 if (target === element) {
28106 return false;
28107 }
28108
28109 var orientation = getOrientation(target, bounds, PLACEMENT_DETECTION_PAD);
28110
28111 return orientation === 'intersect';
28112 });
28113 }
28114
28115 /**
28116 * Compute optimal distance between source and target based on existing connections to and from source.
28117 * Assumes left-to-right and top-to-down modeling.
28118 *
28119 * @param {djs.model.Shape} source
28120 * @param {Object} [hints]
28121 * @param {number} [hints.defaultDistance]
28122 * @param {string} [hints.direction]
28123 * @param {Function} [hints.filter]
28124 * @param {Function} [hints.getWeight]
28125 * @param {number} [hints.maxDistance]
28126 * @param {string} [hints.reference]
28127 *
28128 * @return {number}
28129 */
28130 function getConnectedDistance(source, hints) {
28131 if (!hints) {
28132 hints = {};
28133 }
28134
28135 // targets > sources by default
28136 function getDefaultWeight(connection) {
28137 return connection.source === source ? 1 : -1;
28138 }
28139
28140 var defaultDistance = hints.defaultDistance || DEFAULT_DISTANCE,
28141 direction = hints.direction || 'e',
28142 filter = hints.filter,
28143 getWeight = hints.getWeight || getDefaultWeight,
28144 maxDistance = hints.maxDistance || DEFAULT_MAX_DISTANCE,
28145 reference = hints.reference || 'start';
28146
28147 if (!filter) {
28148 filter = noneFilter;
28149 }
28150
28151 function getDistance(a, b) {
28152 if (direction === 'n') {
28153 if (reference === 'start') {
28154 return asTRBL(a).top - asTRBL(b).bottom;
28155 } else if (reference === 'center') {
28156 return asTRBL(a).top - getMid(b).y;
28157 } else {
28158 return asTRBL(a).top - asTRBL(b).top;
28159 }
28160 } else if (direction === 'w') {
28161 if (reference === 'start') {
28162 return asTRBL(a).left - asTRBL(b).right;
28163 } else if (reference === 'center') {
28164 return asTRBL(a).left - getMid(b).x;
28165 } else {
28166 return asTRBL(a).left - asTRBL(b).left;
28167 }
28168 } else if (direction === 's') {
28169 if (reference === 'start') {
28170 return asTRBL(b).top - asTRBL(a).bottom;
28171 } else if (reference === 'center') {
28172 return getMid(b).y - asTRBL(a).bottom;
28173 } else {
28174 return asTRBL(b).bottom - asTRBL(a).bottom;
28175 }
28176 } else {
28177 if (reference === 'start') {
28178 return asTRBL(b).left - asTRBL(a).right;
28179 } else if (reference === 'center') {
28180 return getMid(b).x - asTRBL(a).right;
28181 } else {
28182 return asTRBL(b).right - asTRBL(a).right;
28183 }
28184 }
28185 }
28186
28187 var sourcesDistances = source.incoming
28188 .filter(filter)
28189 .map(function(connection) {
28190 var weight = getWeight(connection);
28191
28192 var distance = weight < 0
28193 ? getDistance(connection.source, source)
28194 : getDistance(source, connection.source);
28195
28196 return {
28197 id: connection.source.id,
28198 distance: distance,
28199 weight: weight
28200 };
28201 });
28202
28203 var targetsDistances = source.outgoing
28204 .filter(filter)
28205 .map(function(connection) {
28206 var weight = getWeight(connection);
28207
28208 var distance = weight > 0
28209 ? getDistance(source, connection.target)
28210 : getDistance(connection.target, source);
28211
28212 return {
28213 id: connection.target.id,
28214 distance: distance,
28215 weight: weight
28216 };
28217 });
28218
28219 var distances = sourcesDistances.concat(targetsDistances).reduce(function(accumulator, currentValue) {
28220 accumulator[ currentValue.id + '__weight_' + currentValue.weight ] = currentValue;
28221
28222 return accumulator;
28223 }, {});
28224
28225 var distancesGrouped = reduce(distances, function(accumulator, currentValue) {
28226 var distance = currentValue.distance,
28227 weight = currentValue.weight;
28228
28229 if (distance < 0 || distance > maxDistance) {
28230 return accumulator;
28231 }
28232
28233 if (!accumulator[ String(distance) ]) {
28234 accumulator[ String(distance) ] = 0;
28235 }
28236
28237 accumulator[ String(distance) ] += 1 * weight;
28238
28239 if (!accumulator.distance || accumulator[ accumulator.distance ] < accumulator[ String(distance) ]) {
28240 accumulator.distance = distance;
28241 }
28242
28243 return accumulator;
28244 }, {});
28245
28246 return distancesGrouped.distance || defaultDistance;
28247 }
28248
28249 /**
28250 * Returns all connected elements around the given source.
28251 *
28252 * This includes:
28253 *
28254 * - connected elements
28255 * - host connected elements
28256 * - attachers connected elements
28257 *
28258 * @param {djs.model.Shape} source
28259 *
28260 * @return {Array<djs.model.Shape>}
28261 */
28262 function getAutoPlaceClosure(source) {
28263
28264 var allConnected = getConnected(source);
28265
28266 if (source.host) {
28267 allConnected = allConnected.concat(getConnected(source.host));
28268 }
28269
28270 if (source.attachers) {
28271 allConnected = allConnected.concat(source.attachers.reduce(function(shapes, attacher) {
28272 return shapes.concat(getConnected(attacher));
28273 }, []));
28274 }
28275
28276 return allConnected;
28277 }
28278
28279 function getConnected(element) {
28280 return getTargets(element).concat(getSources(element));
28281 }
28282
28283 function getSources(shape) {
28284 return shape.incoming.map(function(connection) {
28285 return connection.source;
28286 });
28287 }
28288
28289 function getTargets(shape) {
28290 return shape.outgoing.map(function(connection) {
28291 return connection.target;
28292 });
28293 }
28294
28295 function noneFilter() {
28296 return true;
28297 }
28298
28299 var LOW_PRIORITY$j = 100;
28300
28301
28302 /**
28303 * A service that places elements connected to existing ones
28304 * to an appropriate position in an _automated_ fashion.
28305 *
28306 * @param {EventBus} eventBus
28307 * @param {Modeling} modeling
28308 */
28309 function AutoPlace$1(eventBus, modeling, canvas) {
28310
28311 eventBus.on('autoPlace', LOW_PRIORITY$j, function(context) {
28312 var shape = context.shape,
28313 source = context.source;
28314
28315 return getNewShapePosition$1(source, shape);
28316 });
28317
28318 eventBus.on('autoPlace.end', function(event) {
28319 canvas.scrollToElement(event.shape);
28320 });
28321
28322 /**
28323 * Append shape to source at appropriate position.
28324 *
28325 * @param {djs.model.Shape} source
28326 * @param {djs.model.Shape} shape
28327 *
28328 * @return {djs.model.Shape} appended shape
28329 */
28330 this.append = function(source, shape, hints) {
28331
28332 eventBus.fire('autoPlace.start', {
28333 source: source,
28334 shape: shape
28335 });
28336
28337 // allow others to provide the position
28338 var position = eventBus.fire('autoPlace', {
28339 source: source,
28340 shape: shape
28341 });
28342
28343 var newShape = modeling.appendShape(source, shape, position, source.parent, hints);
28344
28345 eventBus.fire('autoPlace.end', {
28346 source: source,
28347 shape: newShape
28348 });
28349
28350 return newShape;
28351 };
28352
28353 }
28354
28355 AutoPlace$1.$inject = [
28356 'eventBus',
28357 'modeling',
28358 'canvas'
28359 ];
28360
28361 // helpers //////////
28362
28363 /**
28364 * Find the new position for the target element to
28365 * connect to source.
28366 *
28367 * @param {djs.model.Shape} source
28368 * @param {djs.model.Shape} element
28369 * @param {Object} [hints]
28370 * @param {Object} [hints.defaultDistance]
28371 *
28372 * @returns {Point}
28373 */
28374 function getNewShapePosition$1(source, element, hints) {
28375 if (!hints) {
28376 hints = {};
28377 }
28378
28379 var distance = hints.defaultDistance || DEFAULT_DISTANCE;
28380
28381 var sourceMid = getMid(source),
28382 sourceTrbl = asTRBL(source);
28383
28384 // simply put element right next to source
28385 return {
28386 x: sourceTrbl.right + distance + element.width / 2,
28387 y: sourceMid.y
28388 };
28389 }
28390
28391 /**
28392 * Select element after auto placement.
28393 *
28394 * @param {EventBus} eventBus
28395 * @param {Selection} selection
28396 */
28397 function AutoPlaceSelectionBehavior(eventBus, selection) {
28398
28399 eventBus.on('autoPlace.end', 500, function(e) {
28400 selection.select(e.shape);
28401 });
28402
28403 }
28404
28405 AutoPlaceSelectionBehavior.$inject = [
28406 'eventBus',
28407 'selection'
28408 ];
28409
28410 var AutoPlaceModule$1 = {
28411 __init__: [ 'autoPlaceSelectionBehavior' ],
28412 autoPlace: [ 'type', AutoPlace$1 ],
28413 autoPlaceSelectionBehavior: [ 'type', AutoPlaceSelectionBehavior ]
28414 };
28415
28416 /**
28417 * Return the parent of the element with any of the given types.
28418 *
28419 * @param {djs.model.Base} element
28420 * @param {string|Array<string>} anyType
28421 *
28422 * @return {djs.model.Base}
28423 */
28424 function getParent(element, anyType) {
28425
28426 if (typeof anyType === 'string') {
28427 anyType = [ anyType ];
28428 }
28429
28430 while ((element = element.parent)) {
28431 if (isAny(element, anyType)) {
28432 return element;
28433 }
28434 }
28435
28436 return null;
28437 }
28438
28439 /**
28440 * Find the new position for the target element to
28441 * connect to source.
28442 *
28443 * @param {djs.model.Shape} source
28444 * @param {djs.model.Shape} element
28445 *
28446 * @return {Point}
28447 */
28448 function getNewShapePosition(source, element) {
28449
28450 if (is$1(element, 'bpmn:TextAnnotation')) {
28451 return getTextAnnotationPosition(source, element);
28452 }
28453
28454 if (isAny(element, [ 'bpmn:DataObjectReference', 'bpmn:DataStoreReference' ])) {
28455 return getDataElementPosition(source, element);
28456 }
28457
28458 if (is$1(element, 'bpmn:FlowNode')) {
28459 return getFlowNodePosition(source, element);
28460 }
28461 }
28462
28463 /**
28464 * Always try to place element right of source;
28465 * compute actual distance from previous nodes in flow.
28466 */
28467 function getFlowNodePosition(source, element) {
28468
28469 var sourceTrbl = asTRBL(source);
28470 var sourceMid = getMid(source);
28471
28472 var horizontalDistance = getConnectedDistance(source, {
28473 filter: function(connection) {
28474 return is$1(connection, 'bpmn:SequenceFlow');
28475 }
28476 });
28477
28478 var margin = 30,
28479 minDistance = 80,
28480 orientation = 'left';
28481
28482 if (is$1(source, 'bpmn:BoundaryEvent')) {
28483 orientation = getOrientation(source, source.host, -25);
28484
28485 if (orientation.indexOf('top') !== -1) {
28486 margin *= -1;
28487 }
28488 }
28489
28490 var position = {
28491 x: sourceTrbl.right + horizontalDistance + element.width / 2,
28492 y: sourceMid.y + getVerticalDistance(orientation, minDistance)
28493 };
28494
28495 var nextPositionDirection = {
28496 y: {
28497 margin: margin,
28498 minDistance: minDistance
28499 }
28500 };
28501
28502 return findFreePosition(source, element, position, generateGetNextPosition(nextPositionDirection));
28503 }
28504
28505
28506 function getVerticalDistance(orientation, minDistance) {
28507 if (orientation.indexOf('top') != -1) {
28508 return -1 * minDistance;
28509 } else if (orientation.indexOf('bottom') != -1) {
28510 return minDistance;
28511 } else {
28512 return 0;
28513 }
28514 }
28515
28516
28517 /**
28518 * Always try to place text annotations top right of source.
28519 */
28520 function getTextAnnotationPosition(source, element) {
28521
28522 var sourceTrbl = asTRBL(source);
28523
28524 var position = {
28525 x: sourceTrbl.right + element.width / 2,
28526 y: sourceTrbl.top - 50 - element.height / 2
28527 };
28528
28529 var nextPositionDirection = {
28530 y: {
28531 margin: -30,
28532 minDistance: 20
28533 }
28534 };
28535
28536 return findFreePosition(source, element, position, generateGetNextPosition(nextPositionDirection));
28537 }
28538
28539
28540 /**
28541 * Always put element bottom right of source.
28542 */
28543 function getDataElementPosition(source, element) {
28544
28545 var sourceTrbl = asTRBL(source);
28546
28547 var position = {
28548 x: sourceTrbl.right - 10 + element.width / 2,
28549 y: sourceTrbl.bottom + 40 + element.width / 2
28550 };
28551
28552 var nextPositionDirection = {
28553 x: {
28554 margin: 30,
28555 minDistance: 30
28556 }
28557 };
28558
28559 return findFreePosition(source, element, position, generateGetNextPosition(nextPositionDirection));
28560 }
28561
28562 /**
28563 * BPMN auto-place behavior.
28564 *
28565 * @param {EventBus} eventBus
28566 */
28567 function AutoPlace(eventBus) {
28568 eventBus.on('autoPlace', function(context) {
28569 var shape = context.shape,
28570 source = context.source;
28571
28572 return getNewShapePosition(source, shape);
28573 });
28574 }
28575
28576 AutoPlace.$inject = [ 'eventBus' ];
28577
28578 var AutoPlaceModule = {
28579 __depends__: [ AutoPlaceModule$1 ],
28580 __init__: [ 'bpmnAutoPlace' ],
28581 bpmnAutoPlace: [ 'type', AutoPlace ]
28582 };
28583
28584 /**
28585 * An auto resize component that takes care of expanding a parent element
28586 * if child elements are created or moved close the parents edge.
28587 *
28588 * @param {EventBus} eventBus
28589 * @param {ElementRegistry} elementRegistry
28590 * @param {Modeling} modeling
28591 * @param {Rules} rules
28592 */
28593 function AutoResize(eventBus, elementRegistry, modeling, rules) {
28594
28595 CommandInterceptor.call(this, eventBus);
28596
28597 this._elementRegistry = elementRegistry;
28598 this._modeling = modeling;
28599 this._rules = rules;
28600
28601 var self = this;
28602
28603 this.postExecuted([ 'shape.create' ], function(event) {
28604 var context = event.context,
28605 hints = context.hints || {},
28606 shape = context.shape,
28607 parent = context.parent || context.newParent;
28608
28609 if (hints.autoResize === false) {
28610 return;
28611 }
28612
28613 self._expand([ shape ], parent);
28614 });
28615
28616 this.postExecuted([ 'elements.move' ], function(event) {
28617 var context = event.context,
28618 elements = flatten(values(context.closure.topLevel)),
28619 hints = context.hints;
28620
28621 var autoResize = hints ? hints.autoResize : true;
28622
28623 if (autoResize === false) {
28624 return;
28625 }
28626
28627 var expandings = groupBy(elements, function(element) {
28628 return element.parent.id;
28629 });
28630
28631 forEach$2(expandings, function(elements, parentId) {
28632
28633 // optionally filter elements to be considered when resizing
28634 if (isArray$4(autoResize)) {
28635 elements = elements.filter(function(element) {
28636 return find(autoResize, matchPattern({ id: element.id }));
28637 });
28638 }
28639
28640 self._expand(elements, parentId);
28641 });
28642 });
28643
28644 this.postExecuted([ 'shape.toggleCollapse' ], function(event) {
28645 var context = event.context,
28646 hints = context.hints,
28647 shape = context.shape;
28648
28649 if (hints && hints.autoResize === false) {
28650 return;
28651 }
28652
28653 if (shape.collapsed) {
28654 return;
28655 }
28656
28657 self._expand(shape.children || [], shape);
28658 });
28659
28660 this.postExecuted([ 'shape.resize' ], function(event) {
28661 var context = event.context,
28662 hints = context.hints,
28663 shape = context.shape,
28664 parent = shape.parent;
28665
28666 if (hints && hints.autoResize === false) {
28667 return;
28668 }
28669
28670 if (parent) {
28671 self._expand([ shape ], parent);
28672 }
28673 });
28674
28675 }
28676
28677 AutoResize.$inject = [
28678 'eventBus',
28679 'elementRegistry',
28680 'modeling',
28681 'rules'
28682 ];
28683
28684 inherits$1(AutoResize, CommandInterceptor);
28685
28686
28687 /**
28688 * Calculate the new bounds of the target shape, given
28689 * a number of elements have been moved or added into the parent.
28690 *
28691 * This method considers the current size, the added elements as well as
28692 * the provided padding for the new bounds.
28693 *
28694 * @param {Array<djs.model.Shape>} elements
28695 * @param {djs.model.Shape} target
28696 */
28697 AutoResize.prototype._getOptimalBounds = function(elements, target) {
28698
28699 var offset = this.getOffset(target),
28700 padding = this.getPadding(target);
28701
28702 var elementsTrbl = asTRBL(getBBox(elements)),
28703 targetTrbl = asTRBL(target);
28704
28705 var newTrbl = {};
28706
28707 if (elementsTrbl.top - targetTrbl.top < padding.top) {
28708 newTrbl.top = elementsTrbl.top - offset.top;
28709 }
28710
28711 if (elementsTrbl.left - targetTrbl.left < padding.left) {
28712 newTrbl.left = elementsTrbl.left - offset.left;
28713 }
28714
28715 if (targetTrbl.right - elementsTrbl.right < padding.right) {
28716 newTrbl.right = elementsTrbl.right + offset.right;
28717 }
28718
28719 if (targetTrbl.bottom - elementsTrbl.bottom < padding.bottom) {
28720 newTrbl.bottom = elementsTrbl.bottom + offset.bottom;
28721 }
28722
28723 return asBounds(assign({}, targetTrbl, newTrbl));
28724 };
28725
28726
28727 /**
28728 * Expand the target shape respecting rules, offset and padding
28729 *
28730 * @param {Array<djs.model.Shape>} elements
28731 * @param {djs.model.Shape|string} target|targetId
28732 */
28733 AutoResize.prototype._expand = function(elements, target) {
28734
28735 if (typeof target === 'string') {
28736 target = this._elementRegistry.get(target);
28737 }
28738
28739 var allowed = this._rules.allowed('element.autoResize', {
28740 elements: elements,
28741 target: target
28742 });
28743
28744 if (!allowed) {
28745 return;
28746 }
28747
28748 // calculate the new bounds
28749 var newBounds = this._getOptimalBounds(elements, target);
28750
28751 if (!boundsChanged$1(newBounds, target)) {
28752 return;
28753 }
28754
28755 var resizeDirections = getResizeDirections(pick(target, [ 'x', 'y', 'width', 'height' ]), newBounds);
28756
28757 // resize the parent shape
28758 this.resize(target, newBounds, {
28759 autoResize: resizeDirections
28760 });
28761
28762 var parent = target.parent;
28763
28764 // recursively expand parent elements
28765 if (parent) {
28766 this._expand([ target ], parent);
28767 }
28768 };
28769
28770
28771 /**
28772 * Get the amount to expand the given shape in each direction.
28773 *
28774 * @param {djs.model.Shape} shape
28775 *
28776 * @return {TRBL}
28777 */
28778 AutoResize.prototype.getOffset = function(shape) {
28779 return { top: 60, bottom: 60, left: 100, right: 100 };
28780 };
28781
28782
28783 /**
28784 * Get the activation threshold for each side for which
28785 * resize triggers.
28786 *
28787 * @param {djs.model.Shape} shape
28788 *
28789 * @return {TRBL}
28790 */
28791 AutoResize.prototype.getPadding = function(shape) {
28792 return { top: 2, bottom: 2, left: 15, right: 15 };
28793 };
28794
28795
28796 /**
28797 * Perform the actual resize operation.
28798 *
28799 * @param {djs.model.Shape} shape
28800 * @param {Bounds} newBounds
28801 * @param {Object} [hints]
28802 * @param {string} [hints.autoResize]
28803 */
28804 AutoResize.prototype.resize = function(shape, newBounds, hints) {
28805 this._modeling.resizeShape(shape, newBounds, null, hints);
28806 };
28807
28808
28809 function boundsChanged$1(newBounds, oldBounds) {
28810 return (
28811 newBounds.x !== oldBounds.x ||
28812 newBounds.y !== oldBounds.y ||
28813 newBounds.width !== oldBounds.width ||
28814 newBounds.height !== oldBounds.height
28815 );
28816 }
28817
28818 /**
28819 * Get directions of resize as {n|w|s|e} e.g. "nw".
28820 *
28821 * @param {Bounds} oldBounds
28822 * @param {Bounds} newBounds
28823 *
28824 * @returns {string} Resize directions as {n|w|s|e}.
28825 */
28826 function getResizeDirections(oldBounds, newBounds) {
28827 var directions = '';
28828
28829 oldBounds = asTRBL(oldBounds);
28830 newBounds = asTRBL(newBounds);
28831
28832 if (oldBounds.top > newBounds.top) {
28833 directions = directions.concat('n');
28834 }
28835
28836 if (oldBounds.right < newBounds.right) {
28837 directions = directions.concat('w');
28838 }
28839
28840 if (oldBounds.bottom < newBounds.bottom) {
28841 directions = directions.concat('s');
28842 }
28843
28844 if (oldBounds.left > newBounds.left) {
28845 directions = directions.concat('e');
28846 }
28847
28848 return directions;
28849 }
28850
28851 /**
28852 * Sub class of the AutoResize module which implements a BPMN
28853 * specific resize function.
28854 */
28855 function BpmnAutoResize(injector) {
28856
28857 injector.invoke(AutoResize, this);
28858 }
28859
28860 BpmnAutoResize.$inject = [
28861 'injector'
28862 ];
28863
28864 inherits$1(BpmnAutoResize, AutoResize);
28865
28866
28867 /**
28868 * Resize shapes and lanes.
28869 *
28870 * @param {djs.model.Shape} target
28871 * @param {Bounds} newBounds
28872 * @param {Object} hints
28873 */
28874 BpmnAutoResize.prototype.resize = function(target, newBounds, hints) {
28875
28876 if (is$1(target, 'bpmn:Participant')) {
28877 this._modeling.resizeLane(target, newBounds, null, hints);
28878 } else {
28879 this._modeling.resizeShape(target, newBounds, null, hints);
28880 }
28881 };
28882
28883 /**
28884 * A basic provider that may be extended to implement modeling rules.
28885 *
28886 * Extensions should implement the init method to actually add their custom
28887 * modeling checks. Checks may be added via the #addRule(action, fn) method.
28888 *
28889 * @param {EventBus} eventBus
28890 */
28891 function RuleProvider(eventBus) {
28892 CommandInterceptor.call(this, eventBus);
28893
28894 this.init();
28895 }
28896
28897 RuleProvider.$inject = [ 'eventBus' ];
28898
28899 inherits$1(RuleProvider, CommandInterceptor);
28900
28901
28902 /**
28903 * Adds a modeling rule for the given action, implemented through
28904 * a callback function.
28905 *
28906 * The function will receive the modeling specific action context
28907 * to perform its check. It must return `false` to disallow the
28908 * action from happening or `true` to allow the action.
28909 *
28910 * A rule provider may pass over the evaluation to lower priority
28911 * rules by returning return nothing (or <code>undefined</code>).
28912 *
28913 * @example
28914 *
28915 * ResizableRules.prototype.init = function() {
28916 *
28917 * \/**
28918 * * Return `true`, `false` or nothing to denote
28919 * * _allowed_, _not allowed_ and _continue evaluating_.
28920 * *\/
28921 * this.addRule('shape.resize', function(context) {
28922 *
28923 * var shape = context.shape;
28924 *
28925 * if (!context.newBounds) {
28926 * // check general resizability
28927 * if (!shape.resizable) {
28928 * return false;
28929 * }
28930 *
28931 * // not returning anything (read: undefined)
28932 * // will continue the evaluation of other rules
28933 * // (with lower priority)
28934 * return;
28935 * } else {
28936 * // element must have minimum size of 10*10 points
28937 * return context.newBounds.width > 10 && context.newBounds.height > 10;
28938 * }
28939 * });
28940 * };
28941 *
28942 * @param {string|Array<string>} actions the identifier for the modeling action to check
28943 * @param {number} [priority] the priority at which this rule is being applied
28944 * @param {Function} fn the callback function that performs the actual check
28945 */
28946 RuleProvider.prototype.addRule = function(actions, priority, fn) {
28947
28948 var self = this;
28949
28950 if (typeof actions === 'string') {
28951 actions = [ actions ];
28952 }
28953
28954 actions.forEach(function(action) {
28955
28956 self.canExecute(action, priority, function(context, action, event) {
28957 return fn(context);
28958 }, true);
28959 });
28960 };
28961
28962 /**
28963 * Implement this method to add new rules during provider initialization.
28964 */
28965 RuleProvider.prototype.init = function() {};
28966
28967 /**
28968 * This is a base rule provider for the element.autoResize rule.
28969 */
28970 function AutoResizeProvider(eventBus) {
28971
28972 RuleProvider.call(this, eventBus);
28973
28974 var self = this;
28975
28976 this.addRule('element.autoResize', function(context) {
28977 return self.canResize(context.elements, context.target);
28978 });
28979 }
28980
28981 AutoResizeProvider.$inject = [ 'eventBus' ];
28982
28983 inherits$1(AutoResizeProvider, RuleProvider);
28984
28985 /**
28986 * Needs to be implemented by sub classes to allow actual auto resize
28987 *
28988 * @param {Array<djs.model.Shape>} elements
28989 * @param {djs.model.Shape} target
28990 *
28991 * @return {boolean}
28992 */
28993 AutoResizeProvider.prototype.canResize = function(elements, target) {
28994 return false;
28995 };
28996
28997 /**
28998 * This module is a provider for automatically resizing parent BPMN elements
28999 */
29000 function BpmnAutoResizeProvider(eventBus, modeling) {
29001 AutoResizeProvider.call(this, eventBus);
29002
29003 this._modeling = modeling;
29004 }
29005
29006 inherits$1(BpmnAutoResizeProvider, AutoResizeProvider);
29007
29008 BpmnAutoResizeProvider.$inject = [
29009 'eventBus',
29010 'modeling'
29011 ];
29012
29013
29014 /**
29015 * Check if the given target can be expanded
29016 *
29017 * @param {djs.model.Shape} target
29018 *
29019 * @return {boolean}
29020 */
29021 BpmnAutoResizeProvider.prototype.canResize = function(elements, target) {
29022
29023 // do not resize plane elements:
29024 // root elements, collapsed sub-processes
29025 if (is$1(target.di, 'bpmndi:BPMNPlane')) {
29026 return false;
29027 }
29028
29029 if (!is$1(target, 'bpmn:Participant') && !is$1(target, 'bpmn:Lane') && !(is$1(target, 'bpmn:SubProcess'))) {
29030 return false;
29031 }
29032
29033 var canResize = true;
29034
29035 forEach$2(elements, function(element) {
29036
29037 if (is$1(element, 'bpmn:Lane') || element.labelTarget) {
29038 canResize = false;
29039 return;
29040 }
29041 });
29042
29043 return canResize;
29044 };
29045
29046 var AutoResizeModule = {
29047 __init__: [
29048 'bpmnAutoResize',
29049 'bpmnAutoResizeProvider'
29050 ],
29051 bpmnAutoResize: [ 'type', BpmnAutoResize ],
29052 bpmnAutoResizeProvider: [ 'type', BpmnAutoResizeProvider ]
29053 };
29054
29055 var HIGH_PRIORITY$k = 1500;
29056
29057
29058 /**
29059 * Browsers may swallow certain events (hover, out ...) if users are to
29060 * fast with the mouse.
29061 *
29062 * @see http://stackoverflow.com/questions/7448468/why-cant-i-reliably-capture-a-mouseout-event
29063 *
29064 * The fix implemented in this component ensure that we
29065 *
29066 * 1) have a hover state after a successful drag.move event
29067 * 2) have an out event when dragging leaves an element
29068 *
29069 * @param {ElementRegistry} elementRegistry
29070 * @param {EventBus} eventBus
29071 * @param {Injector} injector
29072 */
29073 function HoverFix(elementRegistry, eventBus, injector) {
29074
29075 var self = this;
29076
29077 var dragging = injector.get('dragging', false);
29078
29079 /**
29080 * Make sure we are god damn hovering!
29081 *
29082 * @param {Event} dragging event
29083 */
29084 function ensureHover(event) {
29085
29086 if (event.hover) {
29087 return;
29088 }
29089
29090 var originalEvent = event.originalEvent;
29091
29092 var gfx = self._findTargetGfx(originalEvent);
29093
29094 var element = gfx && elementRegistry.get(gfx);
29095
29096 if (gfx && element) {
29097
29098 // 1) cancel current mousemove
29099 event.stopPropagation();
29100
29101 // 2) emit fake hover for new target
29102 dragging.hover({ element: element, gfx: gfx });
29103
29104 // 3) re-trigger move event
29105 dragging.move(originalEvent);
29106 }
29107 }
29108
29109
29110 if (dragging) {
29111
29112 /**
29113 * We wait for a specific sequence of events before
29114 * emitting a fake drag.hover event.
29115 *
29116 * Event Sequence:
29117 *
29118 * drag.start
29119 * drag.move >> ensure we are hovering
29120 */
29121 eventBus.on('drag.start', function(event) {
29122
29123 eventBus.once('drag.move', HIGH_PRIORITY$k, function(event) {
29124
29125 ensureHover(event);
29126
29127 });
29128
29129 });
29130 }
29131
29132
29133 /**
29134 * We make sure that element.out is always fired, even if the
29135 * browser swallows an element.out event.
29136 *
29137 * Event sequence:
29138 *
29139 * element.hover
29140 * (element.out >> sometimes swallowed)
29141 * element.hover >> ensure we fired element.out
29142 */
29143 (function() {
29144 var hoverGfx;
29145 var hover;
29146
29147 eventBus.on('element.hover', function(event) {
29148
29149 // (1) remember current hover element
29150 hoverGfx = event.gfx;
29151 hover = event.element;
29152 });
29153
29154 eventBus.on('element.hover', HIGH_PRIORITY$k, function(event) {
29155
29156 // (3) am I on an element still?
29157 if (hover) {
29158
29159 // (4) that is a problem, gotta "simulate the out"
29160 eventBus.fire('element.out', {
29161 element: hover,
29162 gfx: hoverGfx
29163 });
29164 }
29165
29166 });
29167
29168 eventBus.on('element.out', function() {
29169
29170 // (2) unset hover state if we correctly outed us *GG*
29171 hoverGfx = null;
29172 hover = null;
29173 });
29174
29175 })();
29176
29177 this._findTargetGfx = function(event) {
29178 var position,
29179 target;
29180
29181 if (!(event instanceof MouseEvent)) {
29182 return;
29183 }
29184
29185 position = toPoint(event);
29186
29187 // damn expensive operation, ouch!
29188 target = document.elementFromPoint(position.x, position.y);
29189
29190 return getGfx(target);
29191 };
29192
29193 }
29194
29195 HoverFix.$inject = [
29196 'elementRegistry',
29197 'eventBus',
29198 'injector'
29199 ];
29200
29201
29202 // helpers /////////////////////
29203
29204 function getGfx(target) {
29205 return closest(target, 'svg, .djs-element', true);
29206 }
29207
29208 var HoverFixModule = {
29209 __init__: [
29210 'hoverFix'
29211 ],
29212 hoverFix: [ 'type', HoverFix ],
29213 };
29214
29215 var round$a = Math.round;
29216
29217 var DRAG_ACTIVE_CLS = 'djs-drag-active';
29218
29219
29220 function preventDefault$1(event) {
29221 event.preventDefault();
29222 }
29223
29224 function isTouchEvent(event) {
29225
29226 // check for TouchEvent being available first
29227 // (i.e. not available on desktop Firefox)
29228 return typeof TouchEvent !== 'undefined' && event instanceof TouchEvent;
29229 }
29230
29231 function getLength(point) {
29232 return Math.sqrt(Math.pow(point.x, 2) + Math.pow(point.y, 2));
29233 }
29234
29235 /**
29236 * A helper that fires canvas localized drag events and realizes
29237 * the general "drag-and-drop" look and feel.
29238 *
29239 * Calling {@link Dragging#activate} activates dragging on a canvas.
29240 *
29241 * It provides the following:
29242 *
29243 * * emits life cycle events, namespaced with a prefix assigned
29244 * during dragging activation
29245 * * sets and restores the cursor
29246 * * sets and restores the selection if elements still exist
29247 * * ensures there can be only one drag operation active at a time
29248 *
29249 * Dragging may be canceled manually by calling {@link Dragging#cancel}
29250 * or by pressing ESC.
29251 *
29252 *
29253 * ## Life-cycle events
29254 *
29255 * Dragging can be in three different states, off, initialized
29256 * and active.
29257 *
29258 * (1) off: no dragging operation is in progress
29259 * (2) initialized: a new drag operation got initialized but not yet
29260 * started (i.e. because of no initial move)
29261 * (3) started: dragging is in progress
29262 *
29263 * Eventually dragging will be off again after a drag operation has
29264 * been ended or canceled via user click or ESC key press.
29265 *
29266 * To indicate transitions between these states dragging emits generic
29267 * life-cycle events with the `drag.` prefix _and_ events namespaced
29268 * to a prefix choosen by a user during drag initialization.
29269 *
29270 * The following events are emitted (appropriately prefixed) via
29271 * the {@link EventBus}.
29272 *
29273 * * `init`
29274 * * `start`
29275 * * `move`
29276 * * `end`
29277 * * `ended` (dragging already in off state)
29278 * * `cancel` (only if previously started)
29279 * * `canceled` (dragging already in off state, only if previously started)
29280 * * `cleanup`
29281 *
29282 *
29283 * @example
29284 *
29285 * function MyDragComponent(eventBus, dragging) {
29286 *
29287 * eventBus.on('mydrag.start', function(event) {
29288 * console.log('yes, we start dragging');
29289 * });
29290 *
29291 * eventBus.on('mydrag.move', function(event) {
29292 * console.log('canvas local coordinates', event.x, event.y, event.dx, event.dy);
29293 *
29294 * // local drag data is passed with the event
29295 * event.context.foo; // "BAR"
29296 *
29297 * // the original mouse event, too
29298 * event.originalEvent; // MouseEvent(...)
29299 * });
29300 *
29301 * eventBus.on('element.click', function(event) {
29302 * dragging.init(event, 'mydrag', {
29303 * cursor: 'grabbing',
29304 * data: {
29305 * context: {
29306 * foo: "BAR"
29307 * }
29308 * }
29309 * });
29310 * });
29311 * }
29312 */
29313 function Dragging(eventBus, canvas, selection, elementRegistry) {
29314
29315 var defaultOptions = {
29316 threshold: 5,
29317 trapClick: true
29318 };
29319
29320 // the currently active drag operation
29321 // dragging is active as soon as this context exists.
29322 //
29323 // it is visually _active_ only when a context.active flag is set to true.
29324 var context;
29325
29326 /* convert a global event into local coordinates */
29327 function toLocalPoint(globalPosition) {
29328
29329 var viewbox = canvas.viewbox();
29330
29331 var clientRect = canvas._container.getBoundingClientRect();
29332
29333 return {
29334 x: viewbox.x + (globalPosition.x - clientRect.left) / viewbox.scale,
29335 y: viewbox.y + (globalPosition.y - clientRect.top) / viewbox.scale
29336 };
29337 }
29338
29339 // helpers
29340
29341 function fire(type, dragContext) {
29342 dragContext = dragContext || context;
29343
29344 var event = eventBus.createEvent(
29345 assign(
29346 {},
29347 dragContext.payload,
29348 dragContext.data,
29349 { isTouch: dragContext.isTouch }
29350 )
29351 );
29352
29353 // default integration
29354 if (eventBus.fire('drag.' + type, event) === false) {
29355 return false;
29356 }
29357
29358 return eventBus.fire(dragContext.prefix + '.' + type, event);
29359 }
29360
29361 function restoreSelection(previousSelection) {
29362 var existingSelection = previousSelection.filter(function(element) {
29363 return elementRegistry.get(element.id);
29364 });
29365
29366 existingSelection.length && selection.select(existingSelection);
29367 }
29368
29369 // event listeners
29370
29371 function move(event, activate) {
29372 var payload = context.payload,
29373 displacement = context.displacement;
29374
29375 var globalStart = context.globalStart,
29376 globalCurrent = toPoint(event),
29377 globalDelta = delta(globalCurrent, globalStart);
29378
29379 var localStart = context.localStart,
29380 localCurrent = toLocalPoint(globalCurrent),
29381 localDelta = delta(localCurrent, localStart);
29382
29383
29384 // activate context explicitly or once threshold is reached
29385 if (!context.active && (activate || getLength(globalDelta) > context.threshold)) {
29386
29387 // fire start event with original
29388 // starting coordinates
29389
29390 assign(payload, {
29391 x: round$a(localStart.x + displacement.x),
29392 y: round$a(localStart.y + displacement.y),
29393 dx: 0,
29394 dy: 0
29395 }, { originalEvent: event });
29396
29397 if (false === fire('start')) {
29398 return cancel();
29399 }
29400
29401 context.active = true;
29402
29403 // unset selection and remember old selection
29404 // the previous (old) selection will always passed
29405 // with the event via the event.previousSelection property
29406 if (!context.keepSelection) {
29407 payload.previousSelection = selection.get();
29408 selection.select(null);
29409 }
29410
29411 // allow custom cursor
29412 if (context.cursor) {
29413 set(context.cursor);
29414 }
29415
29416 // indicate dragging via marker on root element
29417 canvas.addMarker(canvas.getRootElement(), DRAG_ACTIVE_CLS);
29418 }
29419
29420 stopPropagation$1(event);
29421
29422 if (context.active) {
29423
29424 // update payload with actual coordinates
29425 assign(payload, {
29426 x: round$a(localCurrent.x + displacement.x),
29427 y: round$a(localCurrent.y + displacement.y),
29428 dx: round$a(localDelta.x),
29429 dy: round$a(localDelta.y)
29430 }, { originalEvent: event });
29431
29432 // emit move event
29433 fire('move');
29434 }
29435 }
29436
29437 function end(event) {
29438 var previousContext,
29439 returnValue = true;
29440
29441 if (context.active) {
29442
29443 if (event) {
29444 context.payload.originalEvent = event;
29445
29446 // suppress original event (click, ...)
29447 // because we just ended a drag operation
29448 stopPropagation$1(event);
29449 }
29450
29451 // implementations may stop restoring the
29452 // original state (selections, ...) by preventing the
29453 // end events default action
29454 returnValue = fire('end');
29455 }
29456
29457 if (returnValue === false) {
29458 fire('rejected');
29459 }
29460
29461 previousContext = cleanup(returnValue !== true);
29462
29463 // last event to be fired when all drag operations are done
29464 // at this point in time no drag operation is in progress anymore
29465 fire('ended', previousContext);
29466 }
29467
29468
29469 // cancel active drag operation if the user presses
29470 // the ESC key on the keyboard
29471
29472 function checkCancel(event) {
29473
29474 if (event.which === 27) {
29475 preventDefault$1(event);
29476
29477 cancel();
29478 }
29479 }
29480
29481
29482 // prevent ghost click that might occur after a finished
29483 // drag and drop session
29484
29485 function trapClickAndEnd(event) {
29486
29487 var untrap;
29488
29489 // trap the click in case we are part of an active
29490 // drag operation. This will effectively prevent
29491 // the ghost click that cannot be canceled otherwise.
29492 if (context.active) {
29493
29494 untrap = install(eventBus);
29495
29496 // remove trap after minimal delay
29497 setTimeout(untrap, 400);
29498
29499 // prevent default action (click)
29500 preventDefault$1(event);
29501 }
29502
29503 end(event);
29504 }
29505
29506 function trapTouch(event) {
29507 move(event);
29508 }
29509
29510 // update the drag events hover (djs.model.Base) and hoverGfx (Snap<SVGElement>)
29511 // properties during hover and out and fire {prefix}.hover and {prefix}.out properties
29512 // respectively
29513
29514 function hover(event) {
29515 var payload = context.payload;
29516
29517 payload.hoverGfx = event.gfx;
29518 payload.hover = event.element;
29519
29520 fire('hover');
29521 }
29522
29523 function out(event) {
29524 fire('out');
29525
29526 var payload = context.payload;
29527
29528 payload.hoverGfx = null;
29529 payload.hover = null;
29530 }
29531
29532
29533 // life-cycle methods
29534
29535 function cancel(restore) {
29536 var previousContext;
29537
29538 if (!context) {
29539 return;
29540 }
29541
29542 var wasActive = context.active;
29543
29544 if (wasActive) {
29545 fire('cancel');
29546 }
29547
29548 previousContext = cleanup(restore);
29549
29550 if (wasActive) {
29551
29552 // last event to be fired when all drag operations are done
29553 // at this point in time no drag operation is in progress anymore
29554 fire('canceled', previousContext);
29555 }
29556 }
29557
29558 function cleanup(restore) {
29559 var previousContext,
29560 endDrag;
29561
29562 fire('cleanup');
29563
29564 // reset cursor
29565 unset();
29566
29567 if (context.trapClick) {
29568 endDrag = trapClickAndEnd;
29569 } else {
29570 endDrag = end;
29571 }
29572
29573 // reset dom listeners
29574 componentEvent.unbind(document, 'mousemove', move);
29575
29576 componentEvent.unbind(document, 'dragstart', preventDefault$1);
29577 componentEvent.unbind(document, 'selectstart', preventDefault$1);
29578
29579 componentEvent.unbind(document, 'mousedown', endDrag, true);
29580 componentEvent.unbind(document, 'mouseup', endDrag, true);
29581
29582 componentEvent.unbind(document, 'keyup', checkCancel);
29583
29584 componentEvent.unbind(document, 'touchstart', trapTouch, true);
29585 componentEvent.unbind(document, 'touchcancel', cancel, true);
29586 componentEvent.unbind(document, 'touchmove', move, true);
29587 componentEvent.unbind(document, 'touchend', end, true);
29588
29589 eventBus.off('element.hover', hover);
29590 eventBus.off('element.out', out);
29591
29592 // remove drag marker on root element
29593 canvas.removeMarker(canvas.getRootElement(), DRAG_ACTIVE_CLS);
29594
29595 // restore selection, unless it has changed
29596 var previousSelection = context.payload.previousSelection;
29597
29598 if (restore !== false && previousSelection && !selection.get().length) {
29599 restoreSelection(previousSelection);
29600 }
29601
29602 previousContext = context;
29603
29604 context = null;
29605
29606 return previousContext;
29607 }
29608
29609 /**
29610 * Initialize a drag operation.
29611 *
29612 * If `localPosition` is given, drag events will be emitted
29613 * relative to it.
29614 *
29615 * @param {MouseEvent|TouchEvent} [event]
29616 * @param {Point} [localPosition] actual diagram local position this drag operation should start at
29617 * @param {string} prefix
29618 * @param {Object} [options]
29619 */
29620 function init(event, relativeTo, prefix, options) {
29621
29622 // only one drag operation may be active, at a time
29623 if (context) {
29624 cancel(false);
29625 }
29626
29627 if (typeof relativeTo === 'string') {
29628 options = prefix;
29629 prefix = relativeTo;
29630 relativeTo = null;
29631 }
29632
29633 options = assign({}, defaultOptions, options || {});
29634
29635 var data = options.data || {},
29636 originalEvent,
29637 globalStart,
29638 localStart,
29639 endDrag,
29640 isTouch;
29641
29642 if (options.trapClick) {
29643 endDrag = trapClickAndEnd;
29644 } else {
29645 endDrag = end;
29646 }
29647
29648 if (event) {
29649 originalEvent = getOriginal$1(event) || event;
29650 globalStart = toPoint(event);
29651
29652 stopPropagation$1(event);
29653
29654 // prevent default browser dragging behavior
29655 if (originalEvent.type === 'dragstart') {
29656 preventDefault$1(originalEvent);
29657 }
29658 } else {
29659 originalEvent = null;
29660 globalStart = { x: 0, y: 0 };
29661 }
29662
29663 localStart = toLocalPoint(globalStart);
29664
29665 if (!relativeTo) {
29666 relativeTo = localStart;
29667 }
29668
29669 isTouch = isTouchEvent(originalEvent);
29670
29671 context = assign({
29672 prefix: prefix,
29673 data: data,
29674 payload: {},
29675 globalStart: globalStart,
29676 displacement: delta(relativeTo, localStart),
29677 localStart: localStart,
29678 isTouch: isTouch
29679 }, options);
29680
29681 // skip dom registration if trigger
29682 // is set to manual (during testing)
29683 if (!options.manual) {
29684
29685 // add dom listeners
29686
29687 if (isTouch) {
29688 componentEvent.bind(document, 'touchstart', trapTouch, true);
29689 componentEvent.bind(document, 'touchcancel', cancel, true);
29690 componentEvent.bind(document, 'touchmove', move, true);
29691 componentEvent.bind(document, 'touchend', end, true);
29692 } else {
29693
29694 // assume we use the mouse to interact per default
29695 componentEvent.bind(document, 'mousemove', move);
29696
29697 // prevent default browser drag and text selection behavior
29698 componentEvent.bind(document, 'dragstart', preventDefault$1);
29699 componentEvent.bind(document, 'selectstart', preventDefault$1);
29700
29701 componentEvent.bind(document, 'mousedown', endDrag, true);
29702 componentEvent.bind(document, 'mouseup', endDrag, true);
29703 }
29704
29705 componentEvent.bind(document, 'keyup', checkCancel);
29706
29707 eventBus.on('element.hover', hover);
29708 eventBus.on('element.out', out);
29709 }
29710
29711 fire('init');
29712
29713 if (options.autoActivate) {
29714 move(event, true);
29715 }
29716 }
29717
29718 // cancel on diagram destruction
29719 eventBus.on('diagram.destroy', cancel);
29720
29721
29722 // API
29723
29724 this.init = init;
29725 this.move = move;
29726 this.hover = hover;
29727 this.out = out;
29728 this.end = end;
29729
29730 this.cancel = cancel;
29731
29732 // for introspection
29733
29734 this.context = function() {
29735 return context;
29736 };
29737
29738 this.setOptions = function(options) {
29739 assign(defaultOptions, options);
29740 };
29741 }
29742
29743 Dragging.$inject = [
29744 'eventBus',
29745 'canvas',
29746 'selection',
29747 'elementRegistry'
29748 ];
29749
29750 var DraggingModule = {
29751 __depends__: [
29752 HoverFixModule,
29753 SelectionModule,
29754 ],
29755 dragging: [ 'type', Dragging ],
29756 };
29757
29758 /**
29759 * Initiates canvas scrolling if current cursor point is close to a border.
29760 * Cancelled when current point moves back inside the scrolling borders
29761 * or cancelled manually.
29762 *
29763 * Default options :
29764 * scrollThresholdIn: [ 20, 20, 20, 20 ],
29765 * scrollThresholdOut: [ 0, 0, 0, 0 ],
29766 * scrollRepeatTimeout: 15,
29767 * scrollStep: 10
29768 *
29769 * Threshold order:
29770 * [ left, top, right, bottom ]
29771 */
29772 function AutoScroll(config, eventBus, canvas) {
29773
29774 this._canvas = canvas;
29775
29776 this._opts = assign({
29777 scrollThresholdIn: [ 20, 20, 20, 20 ],
29778 scrollThresholdOut: [ 0, 0, 0, 0 ],
29779 scrollRepeatTimeout: 15,
29780 scrollStep: 10
29781 }, config);
29782
29783 var self = this;
29784
29785 eventBus.on('drag.move', function(e) {
29786 var point = self._toBorderPoint(e);
29787
29788 self.startScroll(point);
29789 });
29790
29791 eventBus.on([ 'drag.cleanup' ], function() {
29792 self.stopScroll();
29793 });
29794 }
29795
29796 AutoScroll.$inject = [
29797 'config.autoScroll',
29798 'eventBus',
29799 'canvas'
29800 ];
29801
29802
29803 /**
29804 * Starts scrolling loop.
29805 * Point is given in global scale in canvas container box plane.
29806 *
29807 * @param {Object} point { x: X, y: Y }
29808 */
29809 AutoScroll.prototype.startScroll = function(point) {
29810
29811 var canvas = this._canvas;
29812 var opts = this._opts;
29813 var self = this;
29814
29815 var clientRect = canvas.getContainer().getBoundingClientRect();
29816
29817 var diff = [
29818 point.x,
29819 point.y,
29820 clientRect.width - point.x,
29821 clientRect.height - point.y
29822 ];
29823
29824 this.stopScroll();
29825
29826 var dx = 0,
29827 dy = 0;
29828
29829 for (var i = 0; i < 4; i++) {
29830 if (between(diff[i], opts.scrollThresholdOut[i], opts.scrollThresholdIn[i])) {
29831 if (i === 0) {
29832 dx = opts.scrollStep;
29833 } else if (i == 1) {
29834 dy = opts.scrollStep;
29835 } else if (i == 2) {
29836 dx = -opts.scrollStep;
29837 } else if (i == 3) {
29838 dy = -opts.scrollStep;
29839 }
29840 }
29841 }
29842
29843 if (dx !== 0 || dy !== 0) {
29844 canvas.scroll({ dx: dx, dy: dy });
29845
29846 this._scrolling = setTimeout(function() {
29847 self.startScroll(point);
29848 }, opts.scrollRepeatTimeout);
29849 }
29850 };
29851
29852 function between(val, start, end) {
29853 if (start < val && val < end) {
29854 return true;
29855 }
29856
29857 return false;
29858 }
29859
29860
29861 /**
29862 * Stops scrolling loop.
29863 */
29864 AutoScroll.prototype.stopScroll = function() {
29865 clearTimeout(this._scrolling);
29866 };
29867
29868
29869 /**
29870 * Overrides defaults options.
29871 *
29872 * @param {Object} options
29873 */
29874 AutoScroll.prototype.setOptions = function(options) {
29875 this._opts = assign({}, this._opts, options);
29876 };
29877
29878
29879 /**
29880 * Converts event to a point in canvas container plane in global scale.
29881 *
29882 * @param {Event} event
29883 * @return {Point}
29884 */
29885 AutoScroll.prototype._toBorderPoint = function(event) {
29886 var clientRect = this._canvas._container.getBoundingClientRect();
29887
29888 var globalPosition = toPoint(event.originalEvent);
29889
29890 return {
29891 x: globalPosition.x - clientRect.left,
29892 y: globalPosition.y - clientRect.top
29893 };
29894 };
29895
29896 var AutoScrollModule = {
29897 __depends__: [
29898 DraggingModule,
29899 ],
29900 __init__: [ 'autoScroll' ],
29901 autoScroll: [ 'type', AutoScroll ]
29902 };
29903
29904 /**
29905 * A service that provides rules for certain diagram actions.
29906 *
29907 * The default implementation will hook into the {@link CommandStack}
29908 * to perform the actual rule evaluation. Make sure to provide the
29909 * `commandStack` service with this module if you plan to use it.
29910 *
29911 * Together with this implementation you may use the {@link RuleProvider}
29912 * to implement your own rule checkers.
29913 *
29914 * This module is ment to be easily replaced, thus the tiny foot print.
29915 *
29916 * @param {Injector} injector
29917 */
29918 function Rules(injector) {
29919 this._commandStack = injector.get('commandStack', false);
29920 }
29921
29922 Rules.$inject = [ 'injector' ];
29923
29924
29925 /**
29926 * Returns whether or not a given modeling action can be executed
29927 * in the specified context.
29928 *
29929 * This implementation will respond with allow unless anyone
29930 * objects.
29931 *
29932 * @param {string} action the action to be checked
29933 * @param {Object} [context] the context to check the action in
29934 *
29935 * @return {boolean} returns true, false or null depending on whether the
29936 * operation is allowed, not allowed or should be ignored.
29937 */
29938 Rules.prototype.allowed = function(action, context) {
29939 var allowed = true;
29940
29941 var commandStack = this._commandStack;
29942
29943 if (commandStack) {
29944 allowed = commandStack.canExecute(action, context);
29945 }
29946
29947 // map undefined to true, i.e. no rules
29948 return allowed === undefined ? true : allowed;
29949 };
29950
29951 var RulesModule$1 = {
29952 __init__: [ 'rules' ],
29953 rules: [ 'type', Rules ]
29954 };
29955
29956 var round$9 = Math.round,
29957 max$6 = Math.max;
29958
29959
29960 function circlePath(center, r) {
29961 var x = center.x,
29962 y = center.y;
29963
29964 return [
29965 [ 'M', x, y ],
29966 [ 'm', 0, -r ],
29967 [ 'a', r, r, 0, 1, 1, 0, 2 * r ],
29968 [ 'a', r, r, 0, 1, 1, 0, -2 * r ],
29969 [ 'z' ]
29970 ];
29971 }
29972
29973 function linePath(points) {
29974 var segments = [];
29975
29976 points.forEach(function(p, idx) {
29977 segments.push([ idx === 0 ? 'M' : 'L', p.x, p.y ]);
29978 });
29979
29980 return segments;
29981 }
29982
29983
29984 var INTERSECTION_THRESHOLD$1 = 10;
29985
29986 function getBendpointIntersection(waypoints, reference) {
29987
29988 var i, w;
29989
29990 for (i = 0; (w = waypoints[i]); i++) {
29991
29992 if (pointDistance(w, reference) <= INTERSECTION_THRESHOLD$1) {
29993 return {
29994 point: waypoints[i],
29995 bendpoint: true,
29996 index: i
29997 };
29998 }
29999 }
30000
30001 return null;
30002 }
30003
30004 function getPathIntersection(waypoints, reference) {
30005
30006 var intersections = intersect(circlePath(reference, INTERSECTION_THRESHOLD$1), linePath(waypoints));
30007
30008 var a = intersections[0],
30009 b = intersections[intersections.length - 1],
30010 idx;
30011
30012 if (!a) {
30013
30014 // no intersection
30015 return null;
30016 }
30017
30018 if (a !== b) {
30019
30020 if (a.segment2 !== b.segment2) {
30021
30022 // we use the bendpoint in between both segments
30023 // as the intersection point
30024
30025 idx = max$6(a.segment2, b.segment2) - 1;
30026
30027 return {
30028 point: waypoints[idx],
30029 bendpoint: true,
30030 index: idx
30031 };
30032 }
30033
30034 return {
30035 point: {
30036 x: (round$9(a.x + b.x) / 2),
30037 y: (round$9(a.y + b.y) / 2)
30038 },
30039 index: a.segment2
30040 };
30041 }
30042
30043 return {
30044 point: {
30045 x: round$9(a.x),
30046 y: round$9(a.y)
30047 },
30048 index: a.segment2
30049 };
30050 }
30051
30052 /**
30053 * Returns the closest point on the connection towards a given reference point.
30054 *
30055 * @param {Array<Point>} waypoints
30056 * @param {Point} reference
30057 *
30058 * @return {Object} intersection data (segment, point)
30059 */
30060 function getApproxIntersection(waypoints, reference) {
30061 return getBendpointIntersection(waypoints, reference) || getPathIntersection(waypoints, reference);
30062 }
30063
30064 var BENDPOINT_CLS = 'djs-bendpoint';
30065 var SEGMENT_DRAGGER_CLS = 'djs-segment-dragger';
30066
30067 function toCanvasCoordinates(canvas, event) {
30068
30069 var position = toPoint(event),
30070 clientRect = canvas._container.getBoundingClientRect(),
30071 offset;
30072
30073 // canvas relative position
30074
30075 offset = {
30076 x: clientRect.left,
30077 y: clientRect.top
30078 };
30079
30080 // update actual event payload with canvas relative measures
30081
30082 var viewbox = canvas.viewbox();
30083
30084 return {
30085 x: viewbox.x + (position.x - offset.x) / viewbox.scale,
30086 y: viewbox.y + (position.y - offset.y) / viewbox.scale
30087 };
30088 }
30089
30090 function getConnectionIntersection(canvas, waypoints, event) {
30091 var localPosition = toCanvasCoordinates(canvas, event),
30092 intersection = getApproxIntersection(waypoints, localPosition);
30093
30094 return intersection;
30095 }
30096
30097 function addBendpoint(parentGfx, cls) {
30098 var groupGfx = create$1('g');
30099 classes$1(groupGfx).add(BENDPOINT_CLS);
30100
30101 append(parentGfx, groupGfx);
30102
30103 var visual = create$1('circle');
30104 attr$1(visual, {
30105 cx: 0,
30106 cy: 0,
30107 r: 4
30108 });
30109 classes$1(visual).add('djs-visual');
30110
30111 append(groupGfx, visual);
30112
30113 var hit = create$1('circle');
30114 attr$1(hit, {
30115 cx: 0,
30116 cy: 0,
30117 r: 10
30118 });
30119 classes$1(hit).add('djs-hit');
30120
30121 append(groupGfx, hit);
30122
30123 if (cls) {
30124 classes$1(groupGfx).add(cls);
30125 }
30126
30127 return groupGfx;
30128 }
30129
30130 function createParallelDragger(parentGfx, segmentStart, segmentEnd, alignment) {
30131 var draggerGfx = create$1('g');
30132
30133 append(parentGfx, draggerGfx);
30134
30135 var width = 14,
30136 height = 3,
30137 padding = 11,
30138 hitWidth = calculateHitWidth(segmentStart, segmentEnd, alignment),
30139 hitHeight = height + padding;
30140
30141 var visual = create$1('rect');
30142 attr$1(visual, {
30143 x: -width / 2,
30144 y: -height / 2,
30145 width: width,
30146 height: height
30147 });
30148 classes$1(visual).add('djs-visual');
30149
30150 append(draggerGfx, visual);
30151
30152 var hit = create$1('rect');
30153 attr$1(hit, {
30154 x: -hitWidth / 2,
30155 y: -hitHeight / 2,
30156 width: hitWidth,
30157 height: hitHeight
30158 });
30159 classes$1(hit).add('djs-hit');
30160
30161 append(draggerGfx, hit);
30162
30163 rotate(draggerGfx, alignment === 'v' ? 90 : 0);
30164
30165 return draggerGfx;
30166 }
30167
30168
30169 function addSegmentDragger(parentGfx, segmentStart, segmentEnd) {
30170
30171 var groupGfx = create$1('g'),
30172 mid = getMidPoint(segmentStart, segmentEnd),
30173 alignment = pointsAligned(segmentStart, segmentEnd);
30174
30175 append(parentGfx, groupGfx);
30176
30177 createParallelDragger(groupGfx, segmentStart, segmentEnd, alignment);
30178
30179 classes$1(groupGfx).add(SEGMENT_DRAGGER_CLS);
30180 classes$1(groupGfx).add(alignment === 'h' ? 'horizontal' : 'vertical');
30181
30182 translate$2(groupGfx, mid.x, mid.y);
30183
30184 return groupGfx;
30185 }
30186
30187 /**
30188 * Calculates region for segment move which is 2/3 of the full segment length
30189 * @param {number} segmentLength
30190 *
30191 * @return {number}
30192 */
30193 function calculateSegmentMoveRegion(segmentLength) {
30194 return Math.abs(Math.round(segmentLength * 2 / 3));
30195 }
30196
30197 // helper //////////
30198
30199 function calculateHitWidth(segmentStart, segmentEnd, alignment) {
30200 var segmentLengthXAxis = segmentEnd.x - segmentStart.x,
30201 segmentLengthYAxis = segmentEnd.y - segmentStart.y;
30202
30203 return alignment === 'h' ?
30204 calculateSegmentMoveRegion(segmentLengthXAxis) :
30205 calculateSegmentMoveRegion(segmentLengthYAxis);
30206 }
30207
30208 /**
30209 * A service that adds editable bendpoints to connections.
30210 */
30211 function Bendpoints(
30212 eventBus, canvas, interactionEvents,
30213 bendpointMove, connectionSegmentMove) {
30214
30215 /**
30216 * Returns true if intersection point is inside middle region of segment, adjusted by
30217 * optional threshold
30218 */
30219 function isIntersectionMiddle(intersection, waypoints, treshold) {
30220 var idx = intersection.index,
30221 p = intersection.point,
30222 p0, p1, mid, aligned, xDelta, yDelta;
30223
30224 if (idx <= 0 || intersection.bendpoint) {
30225 return false;
30226 }
30227
30228 p0 = waypoints[idx - 1];
30229 p1 = waypoints[idx];
30230 mid = getMidPoint(p0, p1),
30231 aligned = pointsAligned(p0, p1);
30232 xDelta = Math.abs(p.x - mid.x);
30233 yDelta = Math.abs(p.y - mid.y);
30234
30235 return aligned && xDelta <= treshold && yDelta <= treshold;
30236 }
30237
30238 /**
30239 * Calculates the threshold from a connection's middle which fits the two-third-region
30240 */
30241 function calculateIntersectionThreshold(connection, intersection) {
30242 var waypoints = connection.waypoints,
30243 relevantSegment, alignment, segmentLength, threshold;
30244
30245 if (intersection.index <= 0 || intersection.bendpoint) {
30246 return null;
30247 }
30248
30249 // segment relative to connection intersection
30250 relevantSegment = {
30251 start: waypoints[intersection.index - 1],
30252 end: waypoints[intersection.index]
30253 };
30254
30255 alignment = pointsAligned(relevantSegment.start, relevantSegment.end);
30256
30257 if (!alignment) {
30258 return null;
30259 }
30260
30261 if (alignment === 'h') {
30262 segmentLength = relevantSegment.end.x - relevantSegment.start.x;
30263 } else {
30264 segmentLength = relevantSegment.end.y - relevantSegment.start.y;
30265 }
30266
30267 // calculate threshold relative to 2/3 of segment length
30268 threshold = calculateSegmentMoveRegion(segmentLength) / 2;
30269
30270 return threshold;
30271 }
30272
30273 function activateBendpointMove(event, connection) {
30274 var waypoints = connection.waypoints,
30275 intersection = getConnectionIntersection(canvas, waypoints, event),
30276 threshold;
30277
30278 if (!intersection) {
30279 return;
30280 }
30281
30282 threshold = calculateIntersectionThreshold(connection, intersection);
30283
30284 if (isIntersectionMiddle(intersection, waypoints, threshold)) {
30285 connectionSegmentMove.start(event, connection, intersection.index);
30286 } else {
30287 bendpointMove.start(event, connection, intersection.index, !intersection.bendpoint);
30288 }
30289
30290 // we've handled the event
30291 return true;
30292 }
30293
30294 function bindInteractionEvents(node, eventName, element) {
30295
30296 componentEvent.bind(node, eventName, function(event) {
30297 interactionEvents.triggerMouseEvent(eventName, event, element);
30298 event.stopPropagation();
30299 });
30300 }
30301
30302 function getBendpointsContainer(element, create) {
30303
30304 var layer = canvas.getLayer('overlays'),
30305 gfx = query('.djs-bendpoints[data-element-id="' + cssEscape(element.id) + '"]', layer);
30306
30307 if (!gfx && create) {
30308 gfx = create$1('g');
30309 attr$1(gfx, { 'data-element-id': element.id });
30310 classes$1(gfx).add('djs-bendpoints');
30311
30312 append(layer, gfx);
30313
30314 bindInteractionEvents(gfx, 'mousedown', element);
30315 bindInteractionEvents(gfx, 'click', element);
30316 bindInteractionEvents(gfx, 'dblclick', element);
30317 }
30318
30319 return gfx;
30320 }
30321
30322 function getSegmentDragger(idx, parentGfx) {
30323 return query(
30324 '.djs-segment-dragger[data-segment-idx="' + idx + '"]',
30325 parentGfx
30326 );
30327 }
30328
30329 function createBendpoints(gfx, connection) {
30330 connection.waypoints.forEach(function(p, idx) {
30331 var bendpoint = addBendpoint(gfx);
30332
30333 append(gfx, bendpoint);
30334
30335 translate$2(bendpoint, p.x, p.y);
30336 });
30337
30338 // add floating bendpoint
30339 addBendpoint(gfx, 'floating');
30340 }
30341
30342 function createSegmentDraggers(gfx, connection) {
30343
30344 var waypoints = connection.waypoints;
30345
30346 var segmentStart,
30347 segmentEnd,
30348 segmentDraggerGfx;
30349
30350 for (var i = 1; i < waypoints.length; i++) {
30351
30352 segmentStart = waypoints[i - 1];
30353 segmentEnd = waypoints[i];
30354
30355 if (pointsAligned(segmentStart, segmentEnd)) {
30356 segmentDraggerGfx = addSegmentDragger(gfx, segmentStart, segmentEnd);
30357
30358 attr$1(segmentDraggerGfx, { 'data-segment-idx': i });
30359
30360 bindInteractionEvents(segmentDraggerGfx, 'mousemove', connection);
30361 }
30362 }
30363 }
30364
30365 function clearBendpoints(gfx) {
30366 forEach$2(all('.' + BENDPOINT_CLS, gfx), function(node) {
30367 remove$2(node);
30368 });
30369 }
30370
30371 function clearSegmentDraggers(gfx) {
30372 forEach$2(all('.' + SEGMENT_DRAGGER_CLS, gfx), function(node) {
30373 remove$2(node);
30374 });
30375 }
30376
30377 function addHandles(connection) {
30378
30379 var gfx = getBendpointsContainer(connection);
30380
30381 if (!gfx) {
30382 gfx = getBendpointsContainer(connection, true);
30383
30384 createBendpoints(gfx, connection);
30385 createSegmentDraggers(gfx, connection);
30386 }
30387
30388 return gfx;
30389 }
30390
30391 function updateHandles(connection) {
30392
30393 var gfx = getBendpointsContainer(connection);
30394
30395 if (gfx) {
30396 clearSegmentDraggers(gfx);
30397 clearBendpoints(gfx);
30398 createSegmentDraggers(gfx, connection);
30399 createBendpoints(gfx, connection);
30400 }
30401 }
30402
30403 function updateFloatingBendpointPosition(parentGfx, intersection) {
30404 var floating = query('.floating', parentGfx),
30405 point = intersection.point;
30406
30407 if (!floating) {
30408 return;
30409 }
30410
30411 translate$2(floating, point.x, point.y);
30412
30413 }
30414
30415 function updateSegmentDraggerPosition(parentGfx, intersection, waypoints) {
30416
30417 var draggerGfx = getSegmentDragger(intersection.index, parentGfx),
30418 segmentStart = waypoints[intersection.index - 1],
30419 segmentEnd = waypoints[intersection.index],
30420 point = intersection.point,
30421 mid = getMidPoint(segmentStart, segmentEnd),
30422 alignment = pointsAligned(segmentStart, segmentEnd),
30423 draggerVisual, relativePosition;
30424
30425 if (!draggerGfx) {
30426 return;
30427 }
30428
30429 draggerVisual = getDraggerVisual(draggerGfx);
30430
30431 relativePosition = {
30432 x: point.x - mid.x,
30433 y: point.y - mid.y
30434 };
30435
30436 if (alignment === 'v') {
30437
30438 // rotate position
30439 relativePosition = {
30440 x: relativePosition.y,
30441 y: relativePosition.x
30442 };
30443 }
30444
30445 translate$2(draggerVisual, relativePosition.x, relativePosition.y);
30446 }
30447
30448 eventBus.on('connection.changed', function(event) {
30449 updateHandles(event.element);
30450 });
30451
30452 eventBus.on('connection.remove', function(event) {
30453 var gfx = getBendpointsContainer(event.element);
30454
30455 if (gfx) {
30456 remove$2(gfx);
30457 }
30458 });
30459
30460 eventBus.on('element.marker.update', function(event) {
30461
30462 var element = event.element,
30463 bendpointsGfx;
30464
30465 if (!element.waypoints) {
30466 return;
30467 }
30468
30469 bendpointsGfx = addHandles(element);
30470
30471 if (event.add) {
30472 classes$1(bendpointsGfx).add(event.marker);
30473 } else {
30474 classes$1(bendpointsGfx).remove(event.marker);
30475 }
30476 });
30477
30478 eventBus.on('element.mousemove', function(event) {
30479
30480 var element = event.element,
30481 waypoints = element.waypoints,
30482 bendpointsGfx,
30483 intersection;
30484
30485 if (waypoints) {
30486 bendpointsGfx = getBendpointsContainer(element, true);
30487
30488 intersection = getConnectionIntersection(canvas, waypoints, event.originalEvent);
30489
30490 if (!intersection) {
30491 return;
30492 }
30493
30494 updateFloatingBendpointPosition(bendpointsGfx, intersection);
30495
30496 if (!intersection.bendpoint) {
30497 updateSegmentDraggerPosition(bendpointsGfx, intersection, waypoints);
30498 }
30499
30500 }
30501 });
30502
30503 eventBus.on('element.mousedown', function(event) {
30504
30505 if (!isPrimaryButton(event)) {
30506 return;
30507 }
30508
30509 var originalEvent = event.originalEvent,
30510 element = event.element;
30511
30512 if (!element.waypoints) {
30513 return;
30514 }
30515
30516 return activateBendpointMove(originalEvent, element);
30517 });
30518
30519 eventBus.on('selection.changed', function(event) {
30520 var newSelection = event.newSelection,
30521 primary = newSelection[0];
30522
30523 if (primary && primary.waypoints) {
30524 addHandles(primary);
30525 }
30526 });
30527
30528 eventBus.on('element.hover', function(event) {
30529 var element = event.element;
30530
30531 if (element.waypoints) {
30532 addHandles(element);
30533 interactionEvents.registerEvent(event.gfx, 'mousemove', 'element.mousemove');
30534 }
30535 });
30536
30537 eventBus.on('element.out', function(event) {
30538 interactionEvents.unregisterEvent(event.gfx, 'mousemove', 'element.mousemove');
30539 });
30540
30541 // update bendpoint container data attribute on element ID change
30542 eventBus.on('element.updateId', function(context) {
30543 var element = context.element,
30544 newId = context.newId;
30545
30546 if (element.waypoints) {
30547 var bendpointContainer = getBendpointsContainer(element);
30548
30549 if (bendpointContainer) {
30550 attr$1(bendpointContainer, { 'data-element-id': newId });
30551 }
30552 }
30553 });
30554
30555 // API
30556
30557 this.addHandles = addHandles;
30558 this.updateHandles = updateHandles;
30559 this.getBendpointsContainer = getBendpointsContainer;
30560 this.getSegmentDragger = getSegmentDragger;
30561 }
30562
30563 Bendpoints.$inject = [
30564 'eventBus',
30565 'canvas',
30566 'interactionEvents',
30567 'bendpointMove',
30568 'connectionSegmentMove'
30569 ];
30570
30571
30572
30573 // helper /////////////
30574
30575 function getDraggerVisual(draggerGfx) {
30576 return query('.djs-visual', draggerGfx);
30577 }
30578
30579 var round$8 = Math.round;
30580
30581 var RECONNECT_START$1 = 'reconnectStart',
30582 RECONNECT_END$1 = 'reconnectEnd',
30583 UPDATE_WAYPOINTS$1 = 'updateWaypoints';
30584
30585
30586 /**
30587 * Move bendpoints through drag and drop to add/remove bendpoints or reconnect connection.
30588 */
30589 function BendpointMove(injector, eventBus, canvas, dragging, rules, modeling) {
30590 this._injector = injector;
30591
30592 this.start = function(event, connection, bendpointIndex, insert) {
30593 var gfx = canvas.getGraphics(connection),
30594 source = connection.source,
30595 target = connection.target,
30596 waypoints = connection.waypoints,
30597 type;
30598
30599 if (!insert && bendpointIndex === 0) {
30600 type = RECONNECT_START$1;
30601 } else
30602 if (!insert && bendpointIndex === waypoints.length - 1) {
30603 type = RECONNECT_END$1;
30604 } else {
30605 type = UPDATE_WAYPOINTS$1;
30606 }
30607
30608 var command = type === UPDATE_WAYPOINTS$1 ? 'connection.updateWaypoints' : 'connection.reconnect';
30609
30610 var allowed = rules.allowed(command, {
30611 connection: connection,
30612 source: source,
30613 target: target
30614 });
30615
30616 if (allowed === false) {
30617 allowed = rules.allowed(command, {
30618 connection: connection,
30619 source: target,
30620 target: source
30621 });
30622 }
30623
30624 if (allowed === false) {
30625 return;
30626 }
30627
30628 dragging.init(event, 'bendpoint.move', {
30629 data: {
30630 connection: connection,
30631 connectionGfx: gfx,
30632 context: {
30633 allowed: allowed,
30634 bendpointIndex: bendpointIndex,
30635 connection: connection,
30636 source: source,
30637 target: target,
30638 insert: insert,
30639 type: type
30640 }
30641 }
30642 });
30643 };
30644
30645 eventBus.on('bendpoint.move.hover', function(event) {
30646 var context = event.context,
30647 connection = context.connection,
30648 source = connection.source,
30649 target = connection.target,
30650 hover = event.hover,
30651 type = context.type;
30652
30653 // cache hover state
30654 context.hover = hover;
30655
30656 var allowed;
30657
30658 if (!hover) {
30659 return;
30660 }
30661
30662 var command = type === UPDATE_WAYPOINTS$1 ? 'connection.updateWaypoints' : 'connection.reconnect';
30663
30664 allowed = context.allowed = rules.allowed(command, {
30665 connection: connection,
30666 source: type === RECONNECT_START$1 ? hover : source,
30667 target: type === RECONNECT_END$1 ? hover : target
30668 });
30669
30670 if (allowed) {
30671 context.source = type === RECONNECT_START$1 ? hover : source;
30672 context.target = type === RECONNECT_END$1 ? hover : target;
30673
30674 return;
30675 }
30676
30677 if (allowed === false) {
30678 allowed = context.allowed = rules.allowed(command, {
30679 connection: connection,
30680 source: type === RECONNECT_END$1 ? hover : target,
30681 target: type === RECONNECT_START$1 ? hover : source
30682 });
30683 }
30684
30685 if (allowed) {
30686 context.source = type === RECONNECT_END$1 ? hover : target;
30687 context.target = type === RECONNECT_START$1 ? hover : source;
30688 }
30689 });
30690
30691 eventBus.on([ 'bendpoint.move.out', 'bendpoint.move.cleanup' ], function(event) {
30692 var context = event.context,
30693 type = context.type;
30694
30695 context.hover = null;
30696 context.source = null;
30697 context.target = null;
30698
30699 if (type !== UPDATE_WAYPOINTS$1) {
30700 context.allowed = false;
30701 }
30702 });
30703
30704 eventBus.on('bendpoint.move.end', function(event) {
30705 var context = event.context,
30706 allowed = context.allowed,
30707 bendpointIndex = context.bendpointIndex,
30708 connection = context.connection,
30709 insert = context.insert,
30710 newWaypoints = connection.waypoints.slice(),
30711 source = context.source,
30712 target = context.target,
30713 type = context.type,
30714 hints = context.hints || {};
30715
30716 // ensure integer values (important if zoom level was > 1 during move)
30717 var docking = {
30718 x: round$8(event.x),
30719 y: round$8(event.y)
30720 };
30721
30722 if (!allowed) {
30723 return false;
30724 }
30725
30726 if (type === UPDATE_WAYPOINTS$1) {
30727 if (insert) {
30728
30729 // insert new bendpoint
30730 newWaypoints.splice(bendpointIndex, 0, docking);
30731 } else {
30732
30733 // swap previous waypoint with moved one
30734 newWaypoints[bendpointIndex] = docking;
30735 }
30736
30737 // pass hints about actual moved bendpoint
30738 // useful for connection/label layout
30739 hints.bendpointMove = {
30740 insert: insert,
30741 bendpointIndex: bendpointIndex
30742 };
30743
30744 newWaypoints = this.cropWaypoints(connection, newWaypoints);
30745
30746 modeling.updateWaypoints(connection, filterRedundantWaypoints(newWaypoints), hints);
30747 } else {
30748 if (type === RECONNECT_START$1) {
30749 hints.docking = 'source';
30750
30751 if (isReverse$2(context)) {
30752 hints.docking = 'target';
30753
30754 hints.newWaypoints = newWaypoints.reverse();
30755 }
30756 } else if (type === RECONNECT_END$1) {
30757 hints.docking = 'target';
30758
30759 if (isReverse$2(context)) {
30760 hints.docking = 'source';
30761
30762 hints.newWaypoints = newWaypoints.reverse();
30763 }
30764 }
30765
30766 modeling.reconnect(connection, source, target, docking, hints);
30767 }
30768 }, this);
30769 }
30770
30771 BendpointMove.$inject = [
30772 'injector',
30773 'eventBus',
30774 'canvas',
30775 'dragging',
30776 'rules',
30777 'modeling'
30778 ];
30779
30780 BendpointMove.prototype.cropWaypoints = function(connection, newWaypoints) {
30781 var connectionDocking = this._injector.get('connectionDocking', false);
30782
30783 if (!connectionDocking) {
30784 return newWaypoints;
30785 }
30786
30787 var waypoints = connection.waypoints;
30788
30789 connection.waypoints = newWaypoints;
30790
30791 connection.waypoints = connectionDocking.getCroppedWaypoints(connection);
30792
30793 newWaypoints = connection.waypoints;
30794
30795 connection.waypoints = waypoints;
30796
30797 return newWaypoints;
30798 };
30799
30800
30801 // helpers //////////
30802
30803 function isReverse$2(context) {
30804 var hover = context.hover,
30805 source = context.source,
30806 target = context.target,
30807 type = context.type;
30808
30809 if (type === RECONNECT_START$1) {
30810 return hover && target && hover === target && source !== target;
30811 }
30812
30813 if (type === RECONNECT_END$1) {
30814 return hover && source && hover === source && source !== target;
30815 }
30816 }
30817
30818 var RECONNECT_START = 'reconnectStart',
30819 RECONNECT_END = 'reconnectEnd',
30820 UPDATE_WAYPOINTS = 'updateWaypoints';
30821
30822 var MARKER_OK$4 = 'connect-ok',
30823 MARKER_NOT_OK$4 = 'connect-not-ok',
30824 MARKER_CONNECT_HOVER$1 = 'connect-hover',
30825 MARKER_CONNECT_UPDATING$1 = 'djs-updating',
30826 MARKER_ELEMENT_HIDDEN = 'djs-element-hidden';
30827
30828 var HIGH_PRIORITY$j = 1100;
30829
30830 /**
30831 * Preview connection while moving bendpoints.
30832 */
30833 function BendpointMovePreview(bendpointMove, injector, eventBus, canvas) {
30834 this._injector = injector;
30835
30836 var connectionPreview = injector.get('connectionPreview', false);
30837
30838 eventBus.on('bendpoint.move.start', function(event) {
30839 var context = event.context,
30840 bendpointIndex = context.bendpointIndex,
30841 connection = context.connection,
30842 insert = context.insert,
30843 waypoints = connection.waypoints,
30844 newWaypoints = waypoints.slice();
30845
30846 context.waypoints = waypoints;
30847
30848 if (insert) {
30849
30850 // insert placeholder for new bendpoint
30851 newWaypoints.splice(bendpointIndex, 0, { x: event.x, y: event.y });
30852 }
30853
30854 connection.waypoints = newWaypoints;
30855
30856 // add dragger gfx
30857 var draggerGfx = context.draggerGfx = addBendpoint(canvas.getLayer('overlays'));
30858
30859 classes$1(draggerGfx).add('djs-dragging');
30860
30861 canvas.addMarker(connection, MARKER_ELEMENT_HIDDEN);
30862 canvas.addMarker(connection, MARKER_CONNECT_UPDATING$1);
30863 });
30864
30865 eventBus.on('bendpoint.move.hover', function(event) {
30866 var context = event.context,
30867 allowed = context.allowed,
30868 hover = context.hover,
30869 type = context.type;
30870
30871 if (hover) {
30872 canvas.addMarker(hover, MARKER_CONNECT_HOVER$1);
30873
30874 if (type === UPDATE_WAYPOINTS) {
30875 return;
30876 }
30877
30878 if (allowed) {
30879 canvas.removeMarker(hover, MARKER_NOT_OK$4);
30880 canvas.addMarker(hover, MARKER_OK$4);
30881 } else if (allowed === false) {
30882 canvas.removeMarker(hover, MARKER_OK$4);
30883 canvas.addMarker(hover, MARKER_NOT_OK$4);
30884 }
30885 }
30886 });
30887
30888 eventBus.on([
30889 'bendpoint.move.out',
30890 'bendpoint.move.cleanup'
30891 ], HIGH_PRIORITY$j, function(event) {
30892 var context = event.context,
30893 hover = context.hover,
30894 target = context.target;
30895
30896 if (hover) {
30897 canvas.removeMarker(hover, MARKER_CONNECT_HOVER$1);
30898 canvas.removeMarker(hover, target ? MARKER_OK$4 : MARKER_NOT_OK$4);
30899 }
30900 });
30901
30902 eventBus.on('bendpoint.move.move', function(event) {
30903 var context = event.context,
30904 allowed = context.allowed,
30905 bendpointIndex = context.bendpointIndex,
30906 draggerGfx = context.draggerGfx,
30907 hover = context.hover,
30908 type = context.type,
30909 connection = context.connection,
30910 source = connection.source,
30911 target = connection.target,
30912 newWaypoints = connection.waypoints.slice(),
30913 bendpoint = { x: event.x, y: event.y },
30914 hints = context.hints || {},
30915 drawPreviewHints = {};
30916
30917 if (connectionPreview) {
30918 if (hints.connectionStart) {
30919 drawPreviewHints.connectionStart = hints.connectionStart;
30920 }
30921
30922 if (hints.connectionEnd) {
30923 drawPreviewHints.connectionEnd = hints.connectionEnd;
30924 }
30925
30926
30927 if (type === RECONNECT_START) {
30928 if (isReverse$2(context)) {
30929 drawPreviewHints.connectionEnd = drawPreviewHints.connectionEnd || bendpoint;
30930
30931 drawPreviewHints.source = target;
30932 drawPreviewHints.target = hover || source;
30933
30934 newWaypoints = newWaypoints.reverse();
30935 } else {
30936 drawPreviewHints.connectionStart = drawPreviewHints.connectionStart || bendpoint;
30937
30938 drawPreviewHints.source = hover || source;
30939 drawPreviewHints.target = target;
30940 }
30941 } else if (type === RECONNECT_END) {
30942 if (isReverse$2(context)) {
30943 drawPreviewHints.connectionStart = drawPreviewHints.connectionStart || bendpoint;
30944
30945 drawPreviewHints.source = hover || target;
30946 drawPreviewHints.target = source;
30947
30948 newWaypoints = newWaypoints.reverse();
30949 } else {
30950 drawPreviewHints.connectionEnd = drawPreviewHints.connectionEnd || bendpoint;
30951
30952 drawPreviewHints.source = source;
30953 drawPreviewHints.target = hover || target;
30954 }
30955
30956 } else {
30957 drawPreviewHints.noCropping = true;
30958 drawPreviewHints.noLayout = true;
30959 newWaypoints[ bendpointIndex ] = bendpoint;
30960 }
30961
30962 if (type === UPDATE_WAYPOINTS) {
30963 newWaypoints = bendpointMove.cropWaypoints(connection, newWaypoints);
30964 }
30965
30966 drawPreviewHints.waypoints = newWaypoints;
30967
30968 connectionPreview.drawPreview(context, allowed, drawPreviewHints);
30969 }
30970
30971 translate$2(draggerGfx, event.x, event.y);
30972 }, this);
30973
30974 eventBus.on([
30975 'bendpoint.move.end',
30976 'bendpoint.move.cancel'
30977 ], HIGH_PRIORITY$j, function(event) {
30978 var context = event.context,
30979 connection = context.connection,
30980 draggerGfx = context.draggerGfx,
30981 hover = context.hover,
30982 target = context.target,
30983 waypoints = context.waypoints;
30984
30985 connection.waypoints = waypoints;
30986
30987 // remove dragger gfx
30988 remove$2(draggerGfx);
30989
30990 canvas.removeMarker(connection, MARKER_CONNECT_UPDATING$1);
30991 canvas.removeMarker(connection, MARKER_ELEMENT_HIDDEN);
30992
30993 if (hover) {
30994 canvas.removeMarker(hover, MARKER_OK$4);
30995 canvas.removeMarker(hover, target ? MARKER_OK$4 : MARKER_NOT_OK$4);
30996 }
30997
30998 if (connectionPreview) {
30999 connectionPreview.cleanUp(context);
31000 }
31001 });
31002 }
31003
31004 BendpointMovePreview.$inject = [
31005 'bendpointMove',
31006 'injector',
31007 'eventBus',
31008 'canvas'
31009 ];
31010
31011 var MARKER_CONNECT_HOVER = 'connect-hover',
31012 MARKER_CONNECT_UPDATING = 'djs-updating';
31013
31014
31015 function axisAdd(point, axis, delta) {
31016 return axisSet(point, axis, point[axis] + delta);
31017 }
31018
31019 function axisSet(point, axis, value) {
31020 return {
31021 x: (axis === 'x' ? value : point.x),
31022 y: (axis === 'y' ? value : point.y)
31023 };
31024 }
31025
31026 function axisFenced(position, segmentStart, segmentEnd, axis) {
31027
31028 var maxValue = Math.max(segmentStart[axis], segmentEnd[axis]),
31029 minValue = Math.min(segmentStart[axis], segmentEnd[axis]);
31030
31031 var padding = 20;
31032
31033 var fencedValue = Math.min(Math.max(minValue + padding, position[axis]), maxValue - padding);
31034
31035 return axisSet(segmentStart, axis, fencedValue);
31036 }
31037
31038 function flipAxis(axis) {
31039 return axis === 'x' ? 'y' : 'x';
31040 }
31041
31042 /**
31043 * Get the docking point on the given element.
31044 *
31045 * Compute a reasonable docking, if non exists.
31046 *
31047 * @param {Point} point
31048 * @param {djs.model.Shape} referenceElement
31049 * @param {string} moveAxis (x|y)
31050 *
31051 * @return {Point}
31052 */
31053 function getDocking$2(point, referenceElement, moveAxis) {
31054
31055 var referenceMid,
31056 inverseAxis;
31057
31058 if (point.original) {
31059 return point.original;
31060 } else {
31061 referenceMid = getMid(referenceElement);
31062 inverseAxis = flipAxis(moveAxis);
31063
31064 return axisSet(point, inverseAxis, referenceMid[inverseAxis]);
31065 }
31066 }
31067
31068 /**
31069 * A component that implements moving of bendpoints
31070 */
31071 function ConnectionSegmentMove(
31072 injector, eventBus, canvas,
31073 dragging, graphicsFactory, modeling) {
31074
31075 // optional connection docking integration
31076 var connectionDocking = injector.get('connectionDocking', false);
31077
31078
31079 // API
31080
31081 this.start = function(event, connection, idx) {
31082
31083 var context,
31084 gfx = canvas.getGraphics(connection),
31085 segmentStartIndex = idx - 1,
31086 segmentEndIndex = idx,
31087 waypoints = connection.waypoints,
31088 segmentStart = waypoints[segmentStartIndex],
31089 segmentEnd = waypoints[segmentEndIndex],
31090 intersection = getConnectionIntersection(canvas, waypoints, event),
31091 direction, axis, dragPosition;
31092
31093 direction = pointsAligned(segmentStart, segmentEnd);
31094
31095 // do not move diagonal connection
31096 if (!direction) {
31097 return;
31098 }
31099
31100 // the axis where we are going to move things
31101 axis = direction === 'v' ? 'x' : 'y';
31102
31103 if (segmentStartIndex === 0) {
31104 segmentStart = getDocking$2(segmentStart, connection.source, axis);
31105 }
31106
31107 if (segmentEndIndex === waypoints.length - 1) {
31108 segmentEnd = getDocking$2(segmentEnd, connection.target, axis);
31109 }
31110
31111 if (intersection) {
31112 dragPosition = intersection.point;
31113 } else {
31114
31115 // set to segment center as default
31116 dragPosition = {
31117 x: (segmentStart.x + segmentEnd.x) / 2,
31118 y: (segmentStart.y + segmentEnd.y) / 2
31119 };
31120 }
31121
31122 context = {
31123 connection: connection,
31124 segmentStartIndex: segmentStartIndex,
31125 segmentEndIndex: segmentEndIndex,
31126 segmentStart: segmentStart,
31127 segmentEnd: segmentEnd,
31128 axis: axis,
31129 dragPosition: dragPosition
31130 };
31131
31132 dragging.init(event, dragPosition, 'connectionSegment.move', {
31133 cursor: axis === 'x' ? 'resize-ew' : 'resize-ns',
31134 data: {
31135 connection: connection,
31136 connectionGfx: gfx,
31137 context: context
31138 }
31139 });
31140 };
31141
31142 /**
31143 * Crop connection if connection cropping is provided.
31144 *
31145 * @param {Connection} connection
31146 * @param {Array<Point>} newWaypoints
31147 *
31148 * @return {Array<Point>} cropped connection waypoints
31149 */
31150 function cropConnection(connection, newWaypoints) {
31151
31152 // crop connection, if docking service is provided only
31153 if (!connectionDocking) {
31154 return newWaypoints;
31155 }
31156
31157 var oldWaypoints = connection.waypoints,
31158 croppedWaypoints;
31159
31160 // temporary set new waypoints
31161 connection.waypoints = newWaypoints;
31162
31163 croppedWaypoints = connectionDocking.getCroppedWaypoints(connection);
31164
31165 // restore old waypoints
31166 connection.waypoints = oldWaypoints;
31167
31168 return croppedWaypoints;
31169 }
31170
31171 // DRAGGING IMPLEMENTATION
31172
31173 function redrawConnection(data) {
31174 graphicsFactory.update('connection', data.connection, data.connectionGfx);
31175 }
31176
31177 function updateDragger(context, segmentOffset, event) {
31178
31179 var newWaypoints = context.newWaypoints,
31180 segmentStartIndex = context.segmentStartIndex + segmentOffset,
31181 segmentStart = newWaypoints[segmentStartIndex],
31182 segmentEndIndex = context.segmentEndIndex + segmentOffset,
31183 segmentEnd = newWaypoints[segmentEndIndex],
31184 axis = flipAxis(context.axis);
31185
31186 // make sure the dragger does not move
31187 // outside the connection
31188 var draggerPosition = axisFenced(event, segmentStart, segmentEnd, axis);
31189
31190 // update dragger
31191 translate$2(context.draggerGfx, draggerPosition.x, draggerPosition.y);
31192 }
31193
31194 /**
31195 * Filter waypoints for redundant ones (i.e. on the same axis).
31196 * Returns the filtered waypoints and the offset related to the segment move.
31197 *
31198 * @param {Array<Point>} waypoints
31199 * @param {Integer} segmentStartIndex of moved segment start
31200 *
31201 * @return {Object} { filteredWaypoints, segmentOffset }
31202 */
31203 function filterRedundantWaypoints(waypoints, segmentStartIndex) {
31204
31205 var segmentOffset = 0;
31206
31207 var filteredWaypoints = waypoints.filter(function(r, idx) {
31208 if (pointsOnLine(waypoints[idx - 1], waypoints[idx + 1], r)) {
31209
31210 // remove point and increment offset
31211 segmentOffset = idx <= segmentStartIndex ? segmentOffset - 1 : segmentOffset;
31212 return false;
31213 }
31214
31215 // dont remove point
31216 return true;
31217 });
31218
31219 return {
31220 waypoints: filteredWaypoints,
31221 segmentOffset: segmentOffset
31222 };
31223 }
31224
31225 eventBus.on('connectionSegment.move.start', function(event) {
31226
31227 var context = event.context,
31228 connection = event.connection,
31229 layer = canvas.getLayer('overlays');
31230
31231 context.originalWaypoints = connection.waypoints.slice();
31232
31233 // add dragger gfx
31234 context.draggerGfx = addSegmentDragger(layer, context.segmentStart, context.segmentEnd);
31235 classes$1(context.draggerGfx).add('djs-dragging');
31236
31237 canvas.addMarker(connection, MARKER_CONNECT_UPDATING);
31238 });
31239
31240 eventBus.on('connectionSegment.move.move', function(event) {
31241
31242 var context = event.context,
31243 connection = context.connection,
31244 segmentStartIndex = context.segmentStartIndex,
31245 segmentEndIndex = context.segmentEndIndex,
31246 segmentStart = context.segmentStart,
31247 segmentEnd = context.segmentEnd,
31248 axis = context.axis;
31249
31250 var newWaypoints = context.originalWaypoints.slice(),
31251 newSegmentStart = axisAdd(segmentStart, axis, event['d' + axis]),
31252 newSegmentEnd = axisAdd(segmentEnd, axis, event['d' + axis]);
31253
31254 // original waypoint count and added / removed
31255 // from start waypoint delta. We use the later
31256 // to retrieve the updated segmentStartIndex / segmentEndIndex
31257 var waypointCount = newWaypoints.length,
31258 segmentOffset = 0;
31259
31260 // move segment start / end by axis delta
31261 newWaypoints[segmentStartIndex] = newSegmentStart;
31262 newWaypoints[segmentEndIndex] = newSegmentEnd;
31263
31264 var sourceToSegmentOrientation,
31265 targetToSegmentOrientation;
31266
31267 // handle first segment
31268 if (segmentStartIndex < 2) {
31269 sourceToSegmentOrientation = getOrientation(connection.source, newSegmentStart);
31270
31271 // first bendpoint, remove first segment if intersecting
31272 if (segmentStartIndex === 1) {
31273
31274 if (sourceToSegmentOrientation === 'intersect') {
31275 newWaypoints.shift();
31276 newWaypoints[0] = newSegmentStart;
31277 segmentOffset--;
31278 }
31279 }
31280
31281 // docking point, add segment if not intersecting anymore
31282 else {
31283 if (sourceToSegmentOrientation !== 'intersect') {
31284 newWaypoints.unshift(segmentStart);
31285 segmentOffset++;
31286 }
31287 }
31288 }
31289
31290 // handle last segment
31291 if (segmentEndIndex > waypointCount - 3) {
31292 targetToSegmentOrientation = getOrientation(connection.target, newSegmentEnd);
31293
31294 // last bendpoint, remove last segment if intersecting
31295 if (segmentEndIndex === waypointCount - 2) {
31296
31297 if (targetToSegmentOrientation === 'intersect') {
31298 newWaypoints.pop();
31299 newWaypoints[newWaypoints.length - 1] = newSegmentEnd;
31300 }
31301 }
31302
31303 // last bendpoint, remove last segment if intersecting
31304 else {
31305 if (targetToSegmentOrientation !== 'intersect') {
31306 newWaypoints.push(segmentEnd);
31307 }
31308 }
31309 }
31310
31311 // update connection waypoints
31312 context.newWaypoints = connection.waypoints = cropConnection(connection, newWaypoints);
31313
31314 // update dragger position
31315 updateDragger(context, segmentOffset, event);
31316
31317 // save segmentOffset in context
31318 context.newSegmentStartIndex = segmentStartIndex + segmentOffset;
31319
31320 // redraw connection
31321 redrawConnection(event);
31322 });
31323
31324 eventBus.on('connectionSegment.move.hover', function(event) {
31325
31326 event.context.hover = event.hover;
31327 canvas.addMarker(event.hover, MARKER_CONNECT_HOVER);
31328 });
31329
31330 eventBus.on([
31331 'connectionSegment.move.out',
31332 'connectionSegment.move.cleanup'
31333 ], function(event) {
31334
31335 // remove connect marker
31336 // if it was added
31337 var hover = event.context.hover;
31338
31339 if (hover) {
31340 canvas.removeMarker(hover, MARKER_CONNECT_HOVER);
31341 }
31342 });
31343
31344 eventBus.on('connectionSegment.move.cleanup', function(event) {
31345
31346 var context = event.context,
31347 connection = context.connection;
31348
31349 // remove dragger gfx
31350 if (context.draggerGfx) {
31351 remove$2(context.draggerGfx);
31352 }
31353
31354 canvas.removeMarker(connection, MARKER_CONNECT_UPDATING);
31355 });
31356
31357 eventBus.on([
31358 'connectionSegment.move.cancel',
31359 'connectionSegment.move.end'
31360 ], function(event) {
31361 var context = event.context,
31362 connection = context.connection;
31363
31364 connection.waypoints = context.originalWaypoints;
31365
31366 redrawConnection(event);
31367 });
31368
31369 eventBus.on('connectionSegment.move.end', function(event) {
31370
31371 var context = event.context,
31372 connection = context.connection,
31373 newWaypoints = context.newWaypoints,
31374 newSegmentStartIndex = context.newSegmentStartIndex;
31375
31376 // ensure we have actual pixel values bendpoint
31377 // coordinates (important when zoom level was > 1 during move)
31378 newWaypoints = newWaypoints.map(function(p) {
31379 return {
31380 original: p.original,
31381 x: Math.round(p.x),
31382 y: Math.round(p.y)
31383 };
31384 });
31385
31386 // apply filter redunant waypoints
31387 var filtered = filterRedundantWaypoints(newWaypoints, newSegmentStartIndex);
31388
31389 // get filtered waypoints
31390 var filteredWaypoints = filtered.waypoints,
31391 croppedWaypoints = cropConnection(connection, filteredWaypoints),
31392 segmentOffset = filtered.segmentOffset;
31393
31394 var hints = {
31395 segmentMove: {
31396 segmentStartIndex: context.segmentStartIndex,
31397 newSegmentStartIndex: newSegmentStartIndex + segmentOffset
31398 }
31399 };
31400
31401 modeling.updateWaypoints(connection, croppedWaypoints, hints);
31402 });
31403 }
31404
31405 ConnectionSegmentMove.$inject = [
31406 'injector',
31407 'eventBus',
31408 'canvas',
31409 'dragging',
31410 'graphicsFactory',
31411 'modeling'
31412 ];
31413
31414 var abs$6 = Math.abs,
31415 round$7 = Math.round;
31416
31417
31418 /**
31419 * Snap value to a collection of reference values.
31420 *
31421 * @param {number} value
31422 * @param {Array<number>} values
31423 * @param {number} [tolerance=10]
31424 *
31425 * @return {number} the value we snapped to or null, if none snapped
31426 */
31427 function snapTo(value, values, tolerance) {
31428 tolerance = tolerance === undefined ? 10 : tolerance;
31429
31430 var idx, snapValue;
31431
31432 for (idx = 0; idx < values.length; idx++) {
31433 snapValue = values[idx];
31434
31435 if (abs$6(snapValue - value) <= tolerance) {
31436 return snapValue;
31437 }
31438 }
31439 }
31440
31441
31442 function topLeft(bounds) {
31443 return {
31444 x: bounds.x,
31445 y: bounds.y
31446 };
31447 }
31448
31449 function bottomRight(bounds) {
31450 return {
31451 x: bounds.x + bounds.width,
31452 y: bounds.y + bounds.height
31453 };
31454 }
31455
31456 function mid$2(bounds, defaultValue) {
31457
31458 if (!bounds || isNaN(bounds.x) || isNaN(bounds.y)) {
31459 return defaultValue;
31460 }
31461
31462 return {
31463 x: round$7(bounds.x + bounds.width / 2),
31464 y: round$7(bounds.y + bounds.height / 2)
31465 };
31466 }
31467
31468
31469 /**
31470 * Retrieve the snap state of the given event.
31471 *
31472 * @param {Event} event
31473 * @param {string} axis
31474 *
31475 * @return {boolean} the snapped state
31476 *
31477 */
31478 function isSnapped(event, axis) {
31479 var snapped = event.snapped;
31480
31481 if (!snapped) {
31482 return false;
31483 }
31484
31485 if (typeof axis === 'string') {
31486 return snapped[axis];
31487 }
31488
31489 return snapped.x && snapped.y;
31490 }
31491
31492
31493 /**
31494 * Set the given event as snapped.
31495 *
31496 * This method may change the x and/or y position of the shape
31497 * from the given event!
31498 *
31499 * @param {Event} event
31500 * @param {string} axis
31501 * @param {number|boolean} value
31502 *
31503 * @return {number} old value
31504 */
31505 function setSnapped(event, axis, value) {
31506 if (typeof axis !== 'string') {
31507 throw new Error('axis must be in [x, y]');
31508 }
31509
31510 if (typeof value !== 'number' && value !== false) {
31511 throw new Error('value must be Number or false');
31512 }
31513
31514 var delta,
31515 previousValue = event[axis];
31516
31517 var snapped = event.snapped = (event.snapped || {});
31518
31519
31520 if (value === false) {
31521 snapped[axis] = false;
31522 } else {
31523 snapped[axis] = true;
31524
31525 delta = value - previousValue;
31526
31527 event[axis] += delta;
31528 event['d' + axis] += delta;
31529 }
31530
31531 return previousValue;
31532 }
31533
31534 /**
31535 * Get children of a shape.
31536 *
31537 * @param {djs.model.Shape} parent
31538 *
31539 * @returns {Array<djs.model.Shape|djs.model.Connection>}
31540 */
31541 function getChildren(parent) {
31542 return parent.children || [];
31543 }
31544
31545 var abs$5 = Math.abs,
31546 round$6 = Math.round;
31547
31548 var TOLERANCE = 10;
31549
31550
31551 function BendpointSnapping(eventBus) {
31552
31553 function snapTo(values, value) {
31554
31555 if (isArray$4(values)) {
31556 var i = values.length;
31557
31558 while (i--) if (abs$5(values[i] - value) <= TOLERANCE) {
31559 return values[i];
31560 }
31561 } else {
31562 values = +values;
31563 var rem = value % values;
31564
31565 if (rem < TOLERANCE) {
31566 return value - rem;
31567 }
31568
31569 if (rem > values - TOLERANCE) {
31570 return value - rem + values;
31571 }
31572 }
31573
31574 return value;
31575 }
31576
31577 function mid(element) {
31578 if (element.width) {
31579 return {
31580 x: round$6(element.width / 2 + element.x),
31581 y: round$6(element.height / 2 + element.y)
31582 };
31583 }
31584 }
31585
31586 // connection segment snapping //////////////////////
31587
31588 function getConnectionSegmentSnaps(context) {
31589
31590 var snapPoints = context.snapPoints,
31591 connection = context.connection,
31592 waypoints = connection.waypoints,
31593 segmentStart = context.segmentStart,
31594 segmentStartIndex = context.segmentStartIndex,
31595 segmentEnd = context.segmentEnd,
31596 segmentEndIndex = context.segmentEndIndex,
31597 axis = context.axis;
31598
31599 if (snapPoints) {
31600 return snapPoints;
31601 }
31602
31603 var referenceWaypoints = [
31604 waypoints[segmentStartIndex - 1],
31605 segmentStart,
31606 segmentEnd,
31607 waypoints[segmentEndIndex + 1]
31608 ];
31609
31610 if (segmentStartIndex < 2) {
31611 referenceWaypoints.unshift(mid(connection.source));
31612 }
31613
31614 if (segmentEndIndex > waypoints.length - 3) {
31615 referenceWaypoints.unshift(mid(connection.target));
31616 }
31617
31618 context.snapPoints = snapPoints = { horizontal: [] , vertical: [] };
31619
31620 forEach$2(referenceWaypoints, function(p) {
31621
31622 // we snap on existing bendpoints only,
31623 // not placeholders that are inserted during add
31624 if (p) {
31625 p = p.original || p;
31626
31627 if (axis === 'y') {
31628 snapPoints.horizontal.push(p.y);
31629 }
31630
31631 if (axis === 'x') {
31632 snapPoints.vertical.push(p.x);
31633 }
31634 }
31635 });
31636
31637 return snapPoints;
31638 }
31639
31640 eventBus.on('connectionSegment.move.move', 1500, function(event) {
31641 var context = event.context,
31642 snapPoints = getConnectionSegmentSnaps(context),
31643 x = event.x,
31644 y = event.y,
31645 sx, sy;
31646
31647 if (!snapPoints) {
31648 return;
31649 }
31650
31651 // snap
31652 sx = snapTo(snapPoints.vertical, x);
31653 sy = snapTo(snapPoints.horizontal, y);
31654
31655
31656 // correction x/y
31657 var cx = (x - sx),
31658 cy = (y - sy);
31659
31660 // update delta
31661 assign(event, {
31662 dx: event.dx - cx,
31663 dy: event.dy - cy,
31664 x: sx,
31665 y: sy
31666 });
31667
31668 // only set snapped if actually snapped
31669 if (cx || snapPoints.vertical.indexOf(x) !== -1) {
31670 setSnapped(event, 'x', sx);
31671 }
31672
31673 if (cy || snapPoints.horizontal.indexOf(y) !== -1) {
31674 setSnapped(event, 'y', sy);
31675 }
31676 });
31677
31678
31679 // bendpoint snapping //////////////////////
31680
31681 function getBendpointSnaps(context) {
31682
31683 var snapPoints = context.snapPoints,
31684 waypoints = context.connection.waypoints,
31685 bendpointIndex = context.bendpointIndex;
31686
31687 if (snapPoints) {
31688 return snapPoints;
31689 }
31690
31691 var referenceWaypoints = [ waypoints[bendpointIndex - 1], waypoints[bendpointIndex + 1] ];
31692
31693 context.snapPoints = snapPoints = { horizontal: [] , vertical: [] };
31694
31695 forEach$2(referenceWaypoints, function(p) {
31696
31697 // we snap on existing bendpoints only,
31698 // not placeholders that are inserted during add
31699 if (p) {
31700 p = p.original || p;
31701
31702 snapPoints.horizontal.push(p.y);
31703 snapPoints.vertical.push(p.x);
31704 }
31705 });
31706
31707 return snapPoints;
31708 }
31709
31710
31711 eventBus.on([ 'bendpoint.move.move', 'bendpoint.move.end' ], 1500, function(event) {
31712
31713 var context = event.context,
31714 snapPoints = getBendpointSnaps(context),
31715 hover = context.hover,
31716 hoverMid = hover && mid(hover),
31717 x = event.x,
31718 y = event.y,
31719 sx, sy;
31720
31721 if (!snapPoints) {
31722 return;
31723 }
31724
31725 // snap to hover mid
31726 sx = snapTo(hoverMid ? snapPoints.vertical.concat([ hoverMid.x ]) : snapPoints.vertical, x);
31727 sy = snapTo(hoverMid ? snapPoints.horizontal.concat([ hoverMid.y ]) : snapPoints.horizontal, y);
31728
31729 // correction x/y
31730 var cx = (x - sx),
31731 cy = (y - sy);
31732
31733 // update delta
31734 assign(event, {
31735 dx: event.dx - cx,
31736 dy: event.dy - cy,
31737 x: event.x - cx,
31738 y: event.y - cy
31739 });
31740
31741 // only set snapped if actually snapped
31742 if (cx || snapPoints.vertical.indexOf(x) !== -1) {
31743 setSnapped(event, 'x', sx);
31744 }
31745
31746 if (cy || snapPoints.horizontal.indexOf(y) !== -1) {
31747 setSnapped(event, 'y', sy);
31748 }
31749 });
31750 }
31751
31752
31753 BendpointSnapping.$inject = [ 'eventBus' ];
31754
31755 var BendpointsModule = {
31756 __depends__: [
31757 DraggingModule,
31758 RulesModule$1
31759 ],
31760 __init__: [ 'bendpoints', 'bendpointSnapping', 'bendpointMovePreview' ],
31761 bendpoints: [ 'type', Bendpoints ],
31762 bendpointMove: [ 'type', BendpointMove ],
31763 bendpointMovePreview: [ 'type', BendpointMovePreview ],
31764 connectionSegmentMove: [ 'type', ConnectionSegmentMove ],
31765 bendpointSnapping: [ 'type', BendpointSnapping ]
31766 };
31767
31768 function Connect(eventBus, dragging, modeling, rules) {
31769
31770 // rules
31771
31772 function canConnect(source, target) {
31773 return rules.allowed('connection.create', {
31774 source: source,
31775 target: target
31776 });
31777 }
31778
31779 function canConnectReverse(source, target) {
31780 return canConnect(target, source);
31781 }
31782
31783
31784 // event handlers
31785
31786 eventBus.on('connect.hover', function(event) {
31787 var context = event.context,
31788 start = context.start,
31789 hover = event.hover,
31790 canExecute;
31791
31792 // cache hover state
31793 context.hover = hover;
31794
31795 canExecute = context.canExecute = canConnect(start, hover);
31796
31797 // ignore hover
31798 if (isNil(canExecute)) {
31799 return;
31800 }
31801
31802 if (canExecute !== false) {
31803 context.source = start;
31804 context.target = hover;
31805
31806 return;
31807 }
31808
31809 canExecute = context.canExecute = canConnectReverse(start, hover);
31810
31811 // ignore hover
31812 if (isNil(canExecute)) {
31813 return;
31814 }
31815
31816 if (canExecute !== false) {
31817 context.source = hover;
31818 context.target = start;
31819 }
31820 });
31821
31822 eventBus.on([ 'connect.out', 'connect.cleanup' ], function(event) {
31823 var context = event.context;
31824
31825 context.hover = null;
31826 context.source = null;
31827 context.target = null;
31828
31829 context.canExecute = false;
31830 });
31831
31832 eventBus.on('connect.end', function(event) {
31833 var context = event.context,
31834 canExecute = context.canExecute,
31835 connectionStart = context.connectionStart,
31836 connectionEnd = {
31837 x: event.x,
31838 y: event.y
31839 },
31840 source = context.source,
31841 target = context.target;
31842
31843 if (!canExecute) {
31844 return false;
31845 }
31846
31847 var attrs = null,
31848 hints = {
31849 connectionStart: isReverse$1(context) ? connectionEnd : connectionStart,
31850 connectionEnd: isReverse$1(context) ? connectionStart : connectionEnd
31851 };
31852
31853 if (isObject(canExecute)) {
31854 attrs = canExecute;
31855 }
31856
31857 modeling.connect(source, target, attrs, hints);
31858 });
31859
31860
31861 // API
31862
31863 /**
31864 * Start connect operation.
31865 *
31866 * @param {DOMEvent} event
31867 * @param {djs.model.Base} start
31868 * @param {Point} [connectionStart]
31869 * @param {boolean} [autoActivate=false]
31870 */
31871 this.start = function(event, start, connectionStart, autoActivate) {
31872 if (!isObject(connectionStart)) {
31873 autoActivate = connectionStart;
31874 connectionStart = getMid(start);
31875 }
31876
31877 dragging.init(event, 'connect', {
31878 autoActivate: autoActivate,
31879 data: {
31880 shape: start,
31881 context: {
31882 start: start,
31883 connectionStart: connectionStart
31884 }
31885 }
31886 });
31887 };
31888 }
31889
31890 Connect.$inject = [
31891 'eventBus',
31892 'dragging',
31893 'modeling',
31894 'rules'
31895 ];
31896
31897
31898 // helpers //////////
31899
31900 function isReverse$1(context) {
31901 var hover = context.hover,
31902 source = context.source,
31903 target = context.target;
31904
31905 return hover && source && hover === source && source !== target;
31906 }
31907
31908 var HIGH_PRIORITY$i = 1100,
31909 LOW_PRIORITY$i = 900;
31910
31911 var MARKER_OK$3 = 'connect-ok',
31912 MARKER_NOT_OK$3 = 'connect-not-ok';
31913
31914 /**
31915 * Shows connection preview during connect.
31916 *
31917 * @param {didi.Injector} injector
31918 * @param {EventBus} eventBus
31919 * @param {Canvas} canvas
31920 */
31921 function ConnectPreview(injector, eventBus, canvas) {
31922 var connectionPreview = injector.get('connectionPreview', false);
31923
31924 connectionPreview && eventBus.on('connect.move', function(event) {
31925 var context = event.context,
31926 canConnect = context.canExecute,
31927 hover = context.hover,
31928 source = context.source,
31929 start = context.start,
31930 startPosition = context.startPosition,
31931 target = context.target,
31932 connectionStart = context.connectionStart || startPosition,
31933 connectionEnd = context.connectionEnd || {
31934 x: event.x,
31935 y: event.y
31936 },
31937 previewStart = connectionStart,
31938 previewEnd = connectionEnd;
31939
31940 if (isReverse$1(context)) {
31941 previewStart = connectionEnd;
31942 previewEnd = connectionStart;
31943 }
31944
31945 connectionPreview.drawPreview(context, canConnect, {
31946 source: source || start,
31947 target: target || hover,
31948 connectionStart: previewStart,
31949 connectionEnd: previewEnd
31950 });
31951 });
31952
31953 eventBus.on('connect.hover', LOW_PRIORITY$i, function(event) {
31954 var context = event.context,
31955 hover = event.hover,
31956 canExecute = context.canExecute;
31957
31958 // ignore hover
31959 if (canExecute === null) {
31960 return;
31961 }
31962
31963 canvas.addMarker(hover, canExecute ? MARKER_OK$3 : MARKER_NOT_OK$3);
31964 });
31965
31966 eventBus.on([
31967 'connect.out',
31968 'connect.cleanup'
31969 ], HIGH_PRIORITY$i, function(event) {
31970 var hover = event.hover;
31971
31972 if (hover) {
31973 canvas.removeMarker(hover, MARKER_OK$3);
31974 canvas.removeMarker(hover, MARKER_NOT_OK$3);
31975 }
31976 });
31977
31978 connectionPreview && eventBus.on('connect.cleanup', function(event) {
31979 connectionPreview.cleanUp(event.context);
31980 });
31981 }
31982
31983 ConnectPreview.$inject = [
31984 'injector',
31985 'eventBus',
31986 'canvas'
31987 ];
31988
31989 var ConnectModule = {
31990 __depends__: [
31991 SelectionModule,
31992 RulesModule$1,
31993 DraggingModule
31994 ],
31995 __init__: [
31996 'connectPreview'
31997 ],
31998 connect: [ 'type', Connect ],
31999 connectPreview: [ 'type', ConnectPreview ]
32000 };
32001
32002 var MARKER_CONNECTION_PREVIEW = 'djs-connection-preview';
32003
32004 /**
32005 * Draws connection preview. Optionally, this can use layouter and connection docking to draw
32006 * better looking previews.
32007 *
32008 * @param {didi.Injector} injector
32009 * @param {Canvas} canvas
32010 * @param {GraphicsFactory} graphicsFactory
32011 * @param {ElementFactory} elementFactory
32012 */
32013 function ConnectionPreview(
32014 injector,
32015 canvas,
32016 graphicsFactory,
32017 elementFactory
32018 ) {
32019 this._canvas = canvas;
32020 this._graphicsFactory = graphicsFactory;
32021 this._elementFactory = elementFactory;
32022
32023 // optional components
32024 this._connectionDocking = injector.get('connectionDocking', false);
32025 this._layouter = injector.get('layouter', false);
32026 }
32027
32028 ConnectionPreview.$inject = [
32029 'injector',
32030 'canvas',
32031 'graphicsFactory',
32032 'elementFactory'
32033 ];
32034
32035 /**
32036 * Draw connection preview.
32037 *
32038 * Provide at least one of <source, connectionStart> and <target, connectionEnd> to create a preview.
32039 * In the clean up stage, call `connectionPreview#cleanUp` with the context to remove preview.
32040 *
32041 * @param {Object} context
32042 * @param {Object|boolean} canConnect
32043 * @param {Object} hints
32044 * @param {djs.model.shape} [hints.source] source element
32045 * @param {djs.model.shape} [hints.target] target element
32046 * @param {Point} [hints.connectionStart] connection preview start
32047 * @param {Point} [hints.connectionEnd] connection preview end
32048 * @param {Array<Point>} [hints.waypoints] provided waypoints for preview
32049 * @param {boolean} [hints.noLayout] true if preview should not be laid out
32050 * @param {boolean} [hints.noCropping] true if preview should not be cropped
32051 * @param {boolean} [hints.noNoop] true if simple connection should not be drawn
32052 */
32053 ConnectionPreview.prototype.drawPreview = function(context, canConnect, hints) {
32054
32055 hints = hints || {};
32056
32057 var connectionPreviewGfx = context.connectionPreviewGfx,
32058 getConnection = context.getConnection,
32059 source = hints.source,
32060 target = hints.target,
32061 waypoints = hints.waypoints,
32062 connectionStart = hints.connectionStart,
32063 connectionEnd = hints.connectionEnd,
32064 noLayout = hints.noLayout,
32065 noCropping = hints.noCropping,
32066 noNoop = hints.noNoop,
32067 connection;
32068
32069 var self = this;
32070
32071 if (!connectionPreviewGfx) {
32072 connectionPreviewGfx = context.connectionPreviewGfx = this.createConnectionPreviewGfx();
32073 }
32074
32075 clear$1(connectionPreviewGfx);
32076
32077 if (!getConnection) {
32078 getConnection = context.getConnection = cacheReturnValues(function(canConnect, source, target) {
32079 return self.getConnection(canConnect, source, target);
32080 });
32081 }
32082
32083 if (canConnect) {
32084 connection = getConnection(canConnect, source, target);
32085 }
32086
32087 if (!connection) {
32088 !noNoop && this.drawNoopPreview(connectionPreviewGfx, hints);
32089 return;
32090 }
32091
32092 connection.waypoints = waypoints || [];
32093
32094 // optional layout
32095 if (this._layouter && !noLayout) {
32096 connection.waypoints = this._layouter.layoutConnection(connection, {
32097 source: source,
32098 target: target,
32099 connectionStart: connectionStart,
32100 connectionEnd: connectionEnd,
32101 waypoints: hints.waypoints || connection.waypoints
32102 });
32103 }
32104
32105 // fallback if no waypoints were provided nor created with layouter
32106 if (!connection.waypoints || !connection.waypoints.length) {
32107 connection.waypoints = [
32108 source ? getMid(source) : connectionStart,
32109 target ? getMid(target) : connectionEnd
32110 ];
32111 }
32112
32113 // optional cropping
32114 if (this._connectionDocking && (source || target) && !noCropping) {
32115 connection.waypoints = this._connectionDocking.getCroppedWaypoints(connection, source, target);
32116 }
32117
32118 this._graphicsFactory.drawConnection(connectionPreviewGfx, connection);
32119 };
32120
32121 /**
32122 * Draw simple connection between source and target or provided points.
32123 *
32124 * @param {SVGElement} connectionPreviewGfx container for the connection
32125 * @param {Object} hints
32126 * @param {djs.model.shape} [hints.source] source element
32127 * @param {djs.model.shape} [hints.target] target element
32128 * @param {Point} [hints.connectionStart] required if source is not provided
32129 * @param {Point} [hints.connectionEnd] required if target is not provided
32130 */
32131 ConnectionPreview.prototype.drawNoopPreview = function(connectionPreviewGfx, hints) {
32132 var source = hints.source,
32133 target = hints.target,
32134 start = hints.connectionStart || getMid(source),
32135 end = hints.connectionEnd || getMid(target);
32136
32137 var waypoints = this.cropWaypoints(start, end, source, target);
32138
32139 var connection = this.createNoopConnection(waypoints[0], waypoints[1]);
32140
32141 append(connectionPreviewGfx, connection);
32142 };
32143
32144 /**
32145 * Return cropped waypoints.
32146 *
32147 * @param {Point} start
32148 * @param {Point} end
32149 * @param {djs.model.shape} source
32150 * @param {djs.model.shape} target
32151 *
32152 * @returns {Array}
32153 */
32154 ConnectionPreview.prototype.cropWaypoints = function(start, end, source, target) {
32155 var graphicsFactory = this._graphicsFactory,
32156 sourcePath = source && graphicsFactory.getShapePath(source),
32157 targetPath = target && graphicsFactory.getShapePath(target),
32158 connectionPath = graphicsFactory.getConnectionPath({ waypoints: [ start, end ] });
32159
32160 start = (source && getElementLineIntersection(sourcePath, connectionPath, true)) || start;
32161 end = (target && getElementLineIntersection(targetPath, connectionPath, false)) || end;
32162
32163 return [ start, end ];
32164 };
32165
32166 /**
32167 * Remove connection preview container if it exists.
32168 *
32169 * @param {Object} [context]
32170 * @param {SVGElement} [context.connectionPreviewGfx] preview container
32171 */
32172 ConnectionPreview.prototype.cleanUp = function(context) {
32173 if (context && context.connectionPreviewGfx) {
32174 remove$2(context.connectionPreviewGfx);
32175 }
32176 };
32177
32178 /**
32179 * Get connection that connects source and target.
32180 *
32181 * @param {Object|boolean} canConnect
32182 *
32183 * @returns {djs.model.connection}
32184 */
32185 ConnectionPreview.prototype.getConnection = function(canConnect) {
32186 var attrs = ensureConnectionAttrs(canConnect);
32187
32188 return this._elementFactory.createConnection(attrs);
32189 };
32190
32191
32192 /**
32193 * Add and return preview graphics.
32194 *
32195 * @returns {SVGElement}
32196 */
32197 ConnectionPreview.prototype.createConnectionPreviewGfx = function() {
32198 var gfx = create$1('g');
32199
32200 attr$1(gfx, {
32201 pointerEvents: 'none'
32202 });
32203
32204 classes$1(gfx).add(MARKER_CONNECTION_PREVIEW);
32205
32206 append(this._canvas.getActiveLayer(), gfx);
32207
32208 return gfx;
32209 };
32210
32211 /**
32212 * Create and return simple connection.
32213 *
32214 * @param {Point} start
32215 * @param {Point} end
32216 *
32217 * @returns {SVGElement}
32218 */
32219 ConnectionPreview.prototype.createNoopConnection = function(start, end) {
32220 var connection = create$1('polyline');
32221
32222 attr$1(connection, {
32223 'stroke': '#333',
32224 'strokeDasharray': [ 1 ],
32225 'strokeWidth': 2,
32226 'pointer-events': 'none'
32227 });
32228
32229 attr$1(connection, { 'points': [ start.x, start.y, end.x, end.y ] });
32230
32231 return connection;
32232 };
32233
32234 // helpers //////////
32235
32236 /**
32237 * Returns function that returns cached return values referenced by stringified first argument.
32238 *
32239 * @param {Function} fn
32240 *
32241 * @return {Function}
32242 */
32243 function cacheReturnValues(fn) {
32244 var returnValues = {};
32245
32246 /**
32247 * Return cached return value referenced by stringified first argument.
32248 *
32249 * @returns {*}
32250 */
32251 return function(firstArgument) {
32252 var key = JSON.stringify(firstArgument);
32253
32254 var returnValue = returnValues[key];
32255
32256 if (!returnValue) {
32257 returnValue = returnValues[key] = fn.apply(null, arguments);
32258 }
32259
32260 return returnValue;
32261 };
32262 }
32263
32264 /**
32265 * Ensure connection attributes is object.
32266 *
32267 * @param {Object|boolean} canConnect
32268 *
32269 * @returns {Object}
32270 */
32271 function ensureConnectionAttrs(canConnect) {
32272 if (isObject(canConnect)) {
32273 return canConnect;
32274 } else {
32275 return {};
32276 }
32277 }
32278
32279 var ConnectionPreviewModule = {
32280 __init__: [ 'connectionPreview' ],
32281 connectionPreview: [ 'type', ConnectionPreview ]
32282 };
32283
32284 var min$3 = Math.min,
32285 max$5 = Math.max;
32286
32287 function preventDefault(e) {
32288 e.preventDefault();
32289 }
32290
32291 function stopPropagation(e) {
32292 e.stopPropagation();
32293 }
32294
32295 function isTextNode(node) {
32296 return node.nodeType === Node.TEXT_NODE;
32297 }
32298
32299 function toArray(nodeList) {
32300 return [].slice.call(nodeList);
32301 }
32302
32303 /**
32304 * Initializes a container for a content editable div.
32305 *
32306 * Structure:
32307 *
32308 * container
32309 * parent
32310 * content
32311 * resize-handle
32312 *
32313 * @param {object} options
32314 * @param {DOMElement} options.container The DOM element to append the contentContainer to
32315 * @param {Function} options.keyHandler Handler for key events
32316 * @param {Function} options.resizeHandler Handler for resize events
32317 */
32318 function TextBox(options) {
32319 this.container = options.container;
32320
32321 this.parent = domify$1(
32322 '<div class="djs-direct-editing-parent">' +
32323 '<div class="djs-direct-editing-content" contenteditable="true"></div>' +
32324 '</div>'
32325 );
32326
32327 this.content = query$1('[contenteditable]', this.parent);
32328
32329 this.keyHandler = options.keyHandler || function() {};
32330 this.resizeHandler = options.resizeHandler || function() {};
32331
32332 this.autoResize = bind(this.autoResize, this);
32333 this.handlePaste = bind(this.handlePaste, this);
32334 }
32335
32336
32337 /**
32338 * Create a text box with the given position, size, style and text content
32339 *
32340 * @param {Object} bounds
32341 * @param {Number} bounds.x absolute x position
32342 * @param {Number} bounds.y absolute y position
32343 * @param {Number} [bounds.width] fixed width value
32344 * @param {Number} [bounds.height] fixed height value
32345 * @param {Number} [bounds.maxWidth] maximum width value
32346 * @param {Number} [bounds.maxHeight] maximum height value
32347 * @param {Number} [bounds.minWidth] minimum width value
32348 * @param {Number} [bounds.minHeight] minimum height value
32349 * @param {Object} [style]
32350 * @param {String} value text content
32351 *
32352 * @return {DOMElement} The created content DOM element
32353 */
32354 TextBox.prototype.create = function(bounds, style, value, options) {
32355 var self = this;
32356
32357 var parent = this.parent,
32358 content = this.content,
32359 container = this.container;
32360
32361 options = this.options = options || {};
32362
32363 style = this.style = style || {};
32364
32365 var parentStyle = pick(style, [
32366 'width',
32367 'height',
32368 'maxWidth',
32369 'maxHeight',
32370 'minWidth',
32371 'minHeight',
32372 'left',
32373 'top',
32374 'backgroundColor',
32375 'position',
32376 'overflow',
32377 'border',
32378 'wordWrap',
32379 'textAlign',
32380 'outline',
32381 'transform'
32382 ]);
32383
32384 assign(parent.style, {
32385 width: bounds.width + 'px',
32386 height: bounds.height + 'px',
32387 maxWidth: bounds.maxWidth + 'px',
32388 maxHeight: bounds.maxHeight + 'px',
32389 minWidth: bounds.minWidth + 'px',
32390 minHeight: bounds.minHeight + 'px',
32391 left: bounds.x + 'px',
32392 top: bounds.y + 'px',
32393 backgroundColor: '#ffffff',
32394 position: 'absolute',
32395 overflow: 'visible',
32396 border: '1px solid #ccc',
32397 boxSizing: 'border-box',
32398 wordWrap: 'normal',
32399 textAlign: 'center',
32400 outline: 'none'
32401 }, parentStyle);
32402
32403 var contentStyle = pick(style, [
32404 'fontFamily',
32405 'fontSize',
32406 'fontWeight',
32407 'lineHeight',
32408 'padding',
32409 'paddingTop',
32410 'paddingRight',
32411 'paddingBottom',
32412 'paddingLeft'
32413 ]);
32414
32415 assign(content.style, {
32416 boxSizing: 'border-box',
32417 width: '100%',
32418 outline: 'none',
32419 wordWrap: 'break-word'
32420 }, contentStyle);
32421
32422 if (options.centerVertically) {
32423 assign(content.style, {
32424 position: 'absolute',
32425 top: '50%',
32426 transform: 'translate(0, -50%)'
32427 }, contentStyle);
32428 }
32429
32430 content.innerText = value;
32431
32432 componentEvent$1.bind(content, 'keydown', this.keyHandler);
32433 componentEvent$1.bind(content, 'mousedown', stopPropagation);
32434 componentEvent$1.bind(content, 'paste', self.handlePaste);
32435
32436 if (options.autoResize) {
32437 componentEvent$1.bind(content, 'input', this.autoResize);
32438 }
32439
32440 if (options.resizable) {
32441 this.resizable(style);
32442 }
32443
32444 container.appendChild(parent);
32445
32446 // set selection to end of text
32447 this.setSelection(content.lastChild, content.lastChild && content.lastChild.length);
32448
32449 return parent;
32450 };
32451
32452 /**
32453 * Intercept paste events to remove formatting from pasted text.
32454 */
32455 TextBox.prototype.handlePaste = function(e) {
32456 var options = this.options,
32457 style = this.style;
32458
32459 e.preventDefault();
32460
32461 var text;
32462
32463 if (e.clipboardData) {
32464
32465 // Chrome, Firefox, Safari
32466 text = e.clipboardData.getData('text/plain');
32467 } else {
32468
32469 // Internet Explorer
32470 text = window.clipboardData.getData('Text');
32471 }
32472
32473 this.insertText(text);
32474
32475 if (options.autoResize) {
32476 var hasResized = this.autoResize(style);
32477
32478 if (hasResized) {
32479 this.resizeHandler(hasResized);
32480 }
32481 }
32482 };
32483
32484 TextBox.prototype.insertText = function(text) {
32485 text = normalizeEndOfLineSequences(text);
32486
32487 // insertText command not supported by Internet Explorer
32488 var success = document.execCommand('insertText', false, text);
32489
32490 if (success) {
32491 return;
32492 }
32493
32494 this._insertTextIE(text);
32495 };
32496
32497 TextBox.prototype._insertTextIE = function(text) {
32498
32499 // Internet Explorer
32500 var range = this.getSelection(),
32501 startContainer = range.startContainer,
32502 endContainer = range.endContainer,
32503 startOffset = range.startOffset,
32504 endOffset = range.endOffset,
32505 commonAncestorContainer = range.commonAncestorContainer;
32506
32507 var childNodesArray = toArray(commonAncestorContainer.childNodes);
32508
32509 var container,
32510 offset;
32511
32512 if (isTextNode(commonAncestorContainer)) {
32513 var containerTextContent = startContainer.textContent;
32514
32515 startContainer.textContent =
32516 containerTextContent.substring(0, startOffset)
32517 + text
32518 + containerTextContent.substring(endOffset);
32519
32520 container = startContainer;
32521 offset = startOffset + text.length;
32522
32523 } else if (startContainer === this.content && endContainer === this.content) {
32524 var textNode = document.createTextNode(text);
32525
32526 this.content.insertBefore(textNode, childNodesArray[startOffset]);
32527
32528 container = textNode;
32529 offset = textNode.textContent.length;
32530 } else {
32531 var startContainerChildIndex = childNodesArray.indexOf(startContainer),
32532 endContainerChildIndex = childNodesArray.indexOf(endContainer);
32533
32534 childNodesArray.forEach(function(childNode, index) {
32535
32536 if (index === startContainerChildIndex) {
32537 childNode.textContent =
32538 startContainer.textContent.substring(0, startOffset) +
32539 text +
32540 endContainer.textContent.substring(endOffset);
32541 } else if (index > startContainerChildIndex && index <= endContainerChildIndex) {
32542 remove$3(childNode);
32543 }
32544 });
32545
32546 container = startContainer;
32547 offset = startOffset + text.length;
32548 }
32549
32550 if (container && offset !== undefined) {
32551
32552 // is necessary in Internet Explorer
32553 setTimeout(function() {
32554 self.setSelection(container, offset);
32555 });
32556 }
32557 };
32558
32559 /**
32560 * Automatically resize element vertically to fit its content.
32561 */
32562 TextBox.prototype.autoResize = function() {
32563 var parent = this.parent,
32564 content = this.content;
32565
32566 var fontSize = parseInt(this.style.fontSize) || 12;
32567
32568 if (content.scrollHeight > parent.offsetHeight ||
32569 content.scrollHeight < parent.offsetHeight - fontSize) {
32570 var bounds = parent.getBoundingClientRect();
32571
32572 var height = content.scrollHeight;
32573 parent.style.height = height + 'px';
32574
32575 this.resizeHandler({
32576 width: bounds.width,
32577 height: bounds.height,
32578 dx: 0,
32579 dy: height - bounds.height
32580 });
32581 }
32582 };
32583
32584 /**
32585 * Make an element resizable by adding a resize handle.
32586 */
32587 TextBox.prototype.resizable = function() {
32588 var self = this;
32589
32590 var parent = this.parent,
32591 resizeHandle = this.resizeHandle;
32592
32593 var minWidth = parseInt(this.style.minWidth) || 0,
32594 minHeight = parseInt(this.style.minHeight) || 0,
32595 maxWidth = parseInt(this.style.maxWidth) || Infinity,
32596 maxHeight = parseInt(this.style.maxHeight) || Infinity;
32597
32598 if (!resizeHandle) {
32599 resizeHandle = this.resizeHandle = domify$1(
32600 '<div class="djs-direct-editing-resize-handle"></div>'
32601 );
32602
32603 var startX, startY, startWidth, startHeight;
32604
32605 var onMouseDown = function(e) {
32606 preventDefault(e);
32607 stopPropagation(e);
32608
32609 startX = e.clientX;
32610 startY = e.clientY;
32611
32612 var bounds = parent.getBoundingClientRect();
32613
32614 startWidth = bounds.width;
32615 startHeight = bounds.height;
32616
32617 componentEvent$1.bind(document, 'mousemove', onMouseMove);
32618 componentEvent$1.bind(document, 'mouseup', onMouseUp);
32619 };
32620
32621 var onMouseMove = function(e) {
32622 preventDefault(e);
32623 stopPropagation(e);
32624
32625 var newWidth = min$3(max$5(startWidth + e.clientX - startX, minWidth), maxWidth);
32626 var newHeight = min$3(max$5(startHeight + e.clientY - startY, minHeight), maxHeight);
32627
32628 parent.style.width = newWidth + 'px';
32629 parent.style.height = newHeight + 'px';
32630
32631 self.resizeHandler({
32632 width: startWidth,
32633 height: startHeight,
32634 dx: e.clientX - startX,
32635 dy: e.clientY - startY
32636 });
32637 };
32638
32639 var onMouseUp = function(e) {
32640 preventDefault(e);
32641 stopPropagation(e);
32642
32643 componentEvent$1.unbind(document,'mousemove', onMouseMove, false);
32644 componentEvent$1.unbind(document, 'mouseup', onMouseUp, false);
32645 };
32646
32647 componentEvent$1.bind(resizeHandle, 'mousedown', onMouseDown);
32648 }
32649
32650 assign(resizeHandle.style, {
32651 position: 'absolute',
32652 bottom: '0px',
32653 right: '0px',
32654 cursor: 'nwse-resize',
32655 width: '0',
32656 height: '0',
32657 borderTop: (parseInt(this.style.fontSize) / 4 || 3) + 'px solid transparent',
32658 borderRight: (parseInt(this.style.fontSize) / 4 || 3) + 'px solid #ccc',
32659 borderBottom: (parseInt(this.style.fontSize) / 4 || 3) + 'px solid #ccc',
32660 borderLeft: (parseInt(this.style.fontSize) / 4 || 3) + 'px solid transparent'
32661 });
32662
32663 parent.appendChild(resizeHandle);
32664 };
32665
32666
32667 /**
32668 * Clear content and style of the textbox, unbind listeners and
32669 * reset CSS style.
32670 */
32671 TextBox.prototype.destroy = function() {
32672 var parent = this.parent,
32673 content = this.content,
32674 resizeHandle = this.resizeHandle;
32675
32676 // clear content
32677 content.innerText = '';
32678
32679 // clear styles
32680 parent.removeAttribute('style');
32681 content.removeAttribute('style');
32682
32683 componentEvent$1.unbind(content, 'keydown', this.keyHandler);
32684 componentEvent$1.unbind(content, 'mousedown', stopPropagation);
32685 componentEvent$1.unbind(content, 'input', this.autoResize);
32686 componentEvent$1.unbind(content, 'paste', this.handlePaste);
32687
32688 if (resizeHandle) {
32689 resizeHandle.removeAttribute('style');
32690
32691 remove$3(resizeHandle);
32692 }
32693
32694 remove$3(parent);
32695 };
32696
32697
32698 TextBox.prototype.getValue = function() {
32699 return this.content.innerText.trim();
32700 };
32701
32702
32703 TextBox.prototype.getSelection = function() {
32704 var selection = window.getSelection(),
32705 range = selection.getRangeAt(0);
32706
32707 return range;
32708 };
32709
32710
32711 TextBox.prototype.setSelection = function(container, offset) {
32712 var range = document.createRange();
32713
32714 if (container === null) {
32715 range.selectNodeContents(this.content);
32716 } else {
32717 range.setStart(container, offset);
32718 range.setEnd(container, offset);
32719 }
32720
32721 var selection = window.getSelection();
32722
32723 selection.removeAllRanges();
32724 selection.addRange(range);
32725 };
32726
32727 // helpers //////////
32728
32729 function normalizeEndOfLineSequences(string) {
32730 return string.replace(/\r\n|\r|\n/g, '\n');
32731 }
32732
32733 /**
32734 * A direct editing component that allows users
32735 * to edit an elements text directly in the diagram
32736 *
32737 * @param {EventBus} eventBus the event bus
32738 */
32739 function DirectEditing(eventBus, canvas) {
32740
32741 this._eventBus = eventBus;
32742
32743 this._providers = [];
32744 this._textbox = new TextBox({
32745 container: canvas.getContainer(),
32746 keyHandler: bind(this._handleKey, this),
32747 resizeHandler: bind(this._handleResize, this)
32748 });
32749 }
32750
32751 DirectEditing.$inject = [ 'eventBus', 'canvas' ];
32752
32753
32754 /**
32755 * Register a direct editing provider
32756
32757 * @param {Object} provider the provider, must expose an #activate(element) method that returns
32758 * an activation context ({ bounds: {x, y, width, height }, text }) if
32759 * direct editing is available for the given element.
32760 * Additionally the provider must expose a #update(element, value) method
32761 * to receive direct editing updates.
32762 */
32763 DirectEditing.prototype.registerProvider = function(provider) {
32764 this._providers.push(provider);
32765 };
32766
32767
32768 /**
32769 * Returns true if direct editing is currently active
32770 *
32771 * @return {Boolean}
32772 */
32773 DirectEditing.prototype.isActive = function() {
32774 return !!this._active;
32775 };
32776
32777
32778 /**
32779 * Cancel direct editing, if it is currently active
32780 */
32781 DirectEditing.prototype.cancel = function() {
32782 if (!this._active) {
32783 return;
32784 }
32785
32786 this._fire('cancel');
32787 this.close();
32788 };
32789
32790
32791 DirectEditing.prototype._fire = function(event, context) {
32792 this._eventBus.fire('directEditing.' + event, context || { active: this._active });
32793 };
32794
32795 DirectEditing.prototype.close = function() {
32796 this._textbox.destroy();
32797
32798 this._fire('deactivate');
32799
32800 this._active = null;
32801
32802 this.resizable = undefined;
32803 };
32804
32805
32806 DirectEditing.prototype.complete = function() {
32807
32808 var active = this._active;
32809
32810 if (!active) {
32811 return;
32812 }
32813
32814 var containerBounds,
32815 previousBounds = active.context.bounds,
32816 newBounds = this.$textbox.getBoundingClientRect(),
32817 newText = this.getValue(),
32818 previousText = active.context.text;
32819
32820 if (
32821 newText !== previousText ||
32822 newBounds.height !== previousBounds.height ||
32823 newBounds.width !== previousBounds.width
32824 ) {
32825 containerBounds = this._textbox.container.getBoundingClientRect();
32826
32827 active.provider.update(active.element, newText, active.context.text, {
32828 x: newBounds.left - containerBounds.left,
32829 y: newBounds.top - containerBounds.top,
32830 width: newBounds.width,
32831 height: newBounds.height
32832 });
32833 }
32834
32835 this._fire('complete');
32836
32837 this.close();
32838 };
32839
32840
32841 DirectEditing.prototype.getValue = function() {
32842 return this._textbox.getValue();
32843 };
32844
32845
32846 DirectEditing.prototype._handleKey = function(e) {
32847
32848 // stop bubble
32849 e.stopPropagation();
32850
32851 var key = e.keyCode || e.charCode;
32852
32853 // ESC
32854 if (key === 27) {
32855 e.preventDefault();
32856 return this.cancel();
32857 }
32858
32859 // Enter
32860 if (key === 13 && !e.shiftKey) {
32861 e.preventDefault();
32862 return this.complete();
32863 }
32864 };
32865
32866
32867 DirectEditing.prototype._handleResize = function(event) {
32868 this._fire('resize', event);
32869 };
32870
32871
32872 /**
32873 * Activate direct editing on the given element
32874 *
32875 * @param {Object} ElementDescriptor the descriptor for a shape or connection
32876 * @return {Boolean} true if the activation was possible
32877 */
32878 DirectEditing.prototype.activate = function(element) {
32879 if (this.isActive()) {
32880 this.cancel();
32881 }
32882
32883 // the direct editing context
32884 var context;
32885
32886 var provider = find(this._providers, function(p) {
32887 return (context = p.activate(element)) ? p : null;
32888 });
32889
32890 // check if activation took place
32891 if (context) {
32892 this.$textbox = this._textbox.create(
32893 context.bounds,
32894 context.style,
32895 context.text,
32896 context.options
32897 );
32898
32899 this._active = {
32900 element: element,
32901 context: context,
32902 provider: provider
32903 };
32904
32905 if (context.options && context.options.resizable) {
32906 this.resizable = true;
32907 }
32908
32909 this._fire('activate');
32910 }
32911
32912 return !!context;
32913 };
32914
32915 var DirectEditingModule = {
32916 __depends__: [
32917 InteractionEventsModule$1
32918 ],
32919 __init__: [ 'directEditing' ],
32920 directEditing: [ 'type', DirectEditing ]
32921 };
32922
32923 var entrySelector = '.entry';
32924
32925 var DEFAULT_PRIORITY$2 = 1000;
32926
32927
32928 /**
32929 * A context pad that displays element specific, contextual actions next
32930 * to a diagram element.
32931 *
32932 * @param {Object} config
32933 * @param {boolean|Object} [config.scale={ min: 1.0, max: 1.5 }]
32934 * @param {number} [config.scale.min]
32935 * @param {number} [config.scale.max]
32936 * @param {EventBus} eventBus
32937 * @param {Overlays} overlays
32938 */
32939 function ContextPad(config, eventBus, overlays) {
32940
32941 this._eventBus = eventBus;
32942 this._overlays = overlays;
32943
32944 var scale = isDefined(config && config.scale) ? config.scale : {
32945 min: 1,
32946 max: 1.5
32947 };
32948
32949 this._overlaysConfig = {
32950 position: {
32951 right: -9,
32952 top: -6
32953 },
32954 scale: scale
32955 };
32956
32957 this._current = null;
32958
32959 this._init();
32960 }
32961
32962 ContextPad.$inject = [
32963 'config.contextPad',
32964 'eventBus',
32965 'overlays'
32966 ];
32967
32968
32969 /**
32970 * Registers events needed for interaction with other components
32971 */
32972 ContextPad.prototype._init = function() {
32973
32974 var eventBus = this._eventBus;
32975
32976 var self = this;
32977
32978 eventBus.on('selection.changed', function(e) {
32979
32980 var selection = e.newSelection;
32981
32982 if (selection.length === 1) {
32983 self.open(selection[0]);
32984 } else {
32985 self.close();
32986 }
32987 });
32988
32989 eventBus.on('elements.delete', function(event) {
32990 var elements = event.elements;
32991
32992 forEach$2(elements, function(e) {
32993 if (self.isOpen(e)) {
32994 self.close();
32995 }
32996 });
32997 });
32998
32999 eventBus.on('element.changed', function(event) {
33000 var element = event.element,
33001 current = self._current;
33002
33003 // force reopen if element for which we are currently opened changed
33004 if (current && current.element === element) {
33005 self.open(element, true);
33006 }
33007 });
33008 };
33009
33010
33011 /**
33012 * Register a provider with the context pad
33013 *
33014 * @param {number} [priority=1000]
33015 * @param {ContextPadProvider} provider
33016 *
33017 * @example
33018 * const contextPadProvider = {
33019 * getContextPadEntries: function(element) {
33020 * return function(entries) {
33021 * return {
33022 * ...entries,
33023 * 'entry-1': {
33024 * label: 'My Entry',
33025 * action: function() { alert("I have been clicked!"); }
33026 * }
33027 * };
33028 * }
33029 * }
33030 * };
33031 *
33032 * contextPad.registerProvider(800, contextPadProvider);
33033 */
33034 ContextPad.prototype.registerProvider = function(priority, provider) {
33035 if (!provider) {
33036 provider = priority;
33037 priority = DEFAULT_PRIORITY$2;
33038 }
33039
33040 this._eventBus.on('contextPad.getProviders', priority, function(event) {
33041 event.providers.push(provider);
33042 });
33043 };
33044
33045
33046 /**
33047 * Returns the context pad entries for a given element
33048 *
33049 * @param {djs.element.Base} element
33050 *
33051 * @return {Array<ContextPadEntryDescriptor>} list of entries
33052 */
33053 ContextPad.prototype.getEntries = function(element) {
33054 var providers = this._getProviders();
33055
33056 var entries = {};
33057
33058 // loop through all providers and their entries.
33059 // group entries by id so that overriding an entry is possible
33060 forEach$2(providers, function(provider) {
33061 var entriesOrUpdater = provider.getContextPadEntries(element);
33062
33063 if (isFunction(entriesOrUpdater)) {
33064 entries = entriesOrUpdater(entries);
33065 } else {
33066 forEach$2(entriesOrUpdater, function(entry, id) {
33067 entries[id] = entry;
33068 });
33069 }
33070 });
33071
33072 return entries;
33073 };
33074
33075
33076 /**
33077 * Trigger an action available on the opened context pad
33078 *
33079 * @param {string} action
33080 * @param {Event} event
33081 * @param {boolean} [autoActivate=false]
33082 */
33083 ContextPad.prototype.trigger = function(action, event, autoActivate) {
33084
33085 var element = this._current.element,
33086 entries = this._current.entries,
33087 entry,
33088 handler,
33089 originalEvent,
33090 button = event.delegateTarget || event.target;
33091
33092 if (!button) {
33093 return event.preventDefault();
33094 }
33095
33096 entry = entries[attr(button, 'data-action')];
33097 handler = entry.action;
33098
33099 originalEvent = event.originalEvent || event;
33100
33101 // simple action (via callback function)
33102 if (isFunction(handler)) {
33103 if (action === 'click') {
33104 return handler(originalEvent, element, autoActivate);
33105 }
33106 } else {
33107 if (handler[action]) {
33108 return handler[action](originalEvent, element, autoActivate);
33109 }
33110 }
33111
33112 // silence other actions
33113 event.preventDefault();
33114 };
33115
33116
33117 /**
33118 * Open the context pad for the given element
33119 *
33120 * @param {djs.model.Base} element
33121 * @param {boolean} force if true, force reopening the context pad
33122 */
33123 ContextPad.prototype.open = function(element, force) {
33124 if (!force && this.isOpen(element)) {
33125 return;
33126 }
33127
33128 this.close();
33129 this._updateAndOpen(element);
33130 };
33131
33132 ContextPad.prototype._getProviders = function() {
33133
33134 var event = this._eventBus.createEvent({
33135 type: 'contextPad.getProviders',
33136 providers: []
33137 });
33138
33139 this._eventBus.fire(event);
33140
33141 return event.providers;
33142 };
33143
33144 ContextPad.prototype._updateAndOpen = function(element) {
33145
33146 var entries = this.getEntries(element),
33147 pad = this.getPad(element),
33148 html = pad.html;
33149
33150 forEach$2(entries, function(entry, id) {
33151 var grouping = entry.group || 'default',
33152 control = domify(entry.html || '<div class="entry" draggable="true"></div>'),
33153 container;
33154
33155 attr(control, 'data-action', id);
33156
33157 container = query('[data-group=' + grouping + ']', html);
33158 if (!container) {
33159 container = domify('<div class="group" data-group="' + grouping + '"></div>');
33160 html.appendChild(container);
33161 }
33162
33163 container.appendChild(control);
33164
33165 if (entry.className) {
33166 addClasses$1(control, entry.className);
33167 }
33168
33169 if (entry.title) {
33170 attr(control, 'title', entry.title);
33171 }
33172
33173 if (entry.imageUrl) {
33174 control.appendChild(domify('<img src="' + entry.imageUrl + '">'));
33175 }
33176 });
33177
33178 classes(html).add('open');
33179
33180 this._current = {
33181 element: element,
33182 pad: pad,
33183 entries: entries
33184 };
33185
33186 this._eventBus.fire('contextPad.open', { current: this._current });
33187 };
33188
33189
33190 ContextPad.prototype.getPad = function(element) {
33191 if (this.isOpen()) {
33192 return this._current.pad;
33193 }
33194
33195 var self = this;
33196
33197 var overlays = this._overlays;
33198
33199 var html = domify('<div class="djs-context-pad"></div>');
33200
33201 var overlaysConfig = assign({
33202 html: html
33203 }, this._overlaysConfig);
33204
33205 delegate.bind(html, entrySelector, 'click', function(event) {
33206 self.trigger('click', event);
33207 });
33208
33209 delegate.bind(html, entrySelector, 'dragstart', function(event) {
33210 self.trigger('dragstart', event);
33211 });
33212
33213 // stop propagation of mouse events
33214 componentEvent.bind(html, 'mousedown', function(event) {
33215 event.stopPropagation();
33216 });
33217
33218 this._overlayId = overlays.add(element, 'context-pad', overlaysConfig);
33219
33220 var pad = overlays.get(this._overlayId);
33221
33222 this._eventBus.fire('contextPad.create', { element: element, pad: pad });
33223
33224 return pad;
33225 };
33226
33227
33228 /**
33229 * Close the context pad
33230 */
33231 ContextPad.prototype.close = function() {
33232 if (!this.isOpen()) {
33233 return;
33234 }
33235
33236 this._overlays.remove(this._overlayId);
33237
33238 this._overlayId = null;
33239
33240 this._eventBus.fire('contextPad.close', { current: this._current });
33241
33242 this._current = null;
33243 };
33244
33245 /**
33246 * Check if pad is open. If element is given, will check
33247 * if pad is opened with given element.
33248 *
33249 * @param {Element} element
33250 * @return {boolean}
33251 */
33252 ContextPad.prototype.isOpen = function(element) {
33253 return !!this._current && (!element ? true : this._current.element === element);
33254 };
33255
33256
33257
33258
33259 // helpers //////////////////////
33260
33261 function addClasses$1(element, classNames) {
33262
33263 var classes$1 = classes(element);
33264
33265 var actualClassNames = isArray$4(classNames) ? classNames : classNames.split(/\s+/g);
33266 actualClassNames.forEach(function(cls) {
33267 classes$1.add(cls);
33268 });
33269 }
33270
33271 var ContextPadModule$1 = {
33272 __depends__: [
33273 InteractionEventsModule$1,
33274 OverlaysModule
33275 ],
33276 contextPad: [ 'type', ContextPad ]
33277 };
33278
33279 var MARKER_TYPES = [
33280 'marker-start',
33281 'marker-mid',
33282 'marker-end'
33283 ];
33284
33285 var NODES_CAN_HAVE_MARKER = [
33286 'circle',
33287 'ellipse',
33288 'line',
33289 'path',
33290 'polygon',
33291 'polyline',
33292 'rect'
33293 ];
33294
33295
33296 /**
33297 * Adds support for previews of moving/resizing elements.
33298 */
33299 function PreviewSupport(elementRegistry, eventBus, canvas, styles) {
33300 this._elementRegistry = elementRegistry;
33301 this._canvas = canvas;
33302 this._styles = styles;
33303
33304 this._clonedMarkers = {};
33305
33306 var self = this;
33307
33308 eventBus.on('drag.cleanup', function() {
33309 forEach$2(self._clonedMarkers, function(clonedMarker) {
33310 remove$2(clonedMarker);
33311 });
33312
33313 self._clonedMarkers = {};
33314 });
33315 }
33316
33317 PreviewSupport.$inject = [
33318 'elementRegistry',
33319 'eventBus',
33320 'canvas',
33321 'styles'
33322 ];
33323
33324
33325 /**
33326 * Returns graphics of an element.
33327 *
33328 * @param {djs.model.Base} element
33329 *
33330 * @return {SVGElement}
33331 */
33332 PreviewSupport.prototype.getGfx = function(element) {
33333 return this._elementRegistry.getGraphics(element);
33334 };
33335
33336 /**
33337 * Adds a move preview of a given shape to a given svg group.
33338 *
33339 * @param {djs.model.Base} element
33340 * @param {SVGElement} group
33341 * @param {SVGElement} [gfx]
33342 *
33343 * @return {SVGElement} dragger
33344 */
33345 PreviewSupport.prototype.addDragger = function(element, group, gfx) {
33346 gfx = gfx || this.getGfx(element);
33347
33348 var dragger = clone$1(gfx);
33349 var bbox = gfx.getBoundingClientRect();
33350
33351 this._cloneMarkers(getVisual(dragger));
33352
33353 attr$1(dragger, this._styles.cls('djs-dragger', [], {
33354 x: bbox.top,
33355 y: bbox.left
33356 }));
33357
33358 append(group, dragger);
33359
33360 return dragger;
33361 };
33362
33363 /**
33364 * Adds a resize preview of a given shape to a given svg group.
33365 *
33366 * @param {djs.model.Base} element
33367 * @param {SVGElement} group
33368 *
33369 * @return {SVGElement} frame
33370 */
33371 PreviewSupport.prototype.addFrame = function(shape, group) {
33372
33373 var frame = create$1('rect', {
33374 class: 'djs-resize-overlay',
33375 width: shape.width,
33376 height: shape.height,
33377 x: shape.x,
33378 y: shape.y
33379 });
33380
33381 append(group, frame);
33382
33383 return frame;
33384 };
33385
33386 /**
33387 * Clone all markers referenced by a node and its child nodes.
33388 *
33389 * @param {SVGElement} gfx
33390 */
33391 PreviewSupport.prototype._cloneMarkers = function(gfx) {
33392 var self = this;
33393
33394 if (gfx.childNodes) {
33395
33396 // TODO: use forEach once we drop PhantomJS
33397 for (var i = 0; i < gfx.childNodes.length; i++) {
33398
33399 // recursively clone markers of child nodes
33400 self._cloneMarkers(gfx.childNodes[ i ]);
33401 }
33402 }
33403
33404 if (!canHaveMarker(gfx)) {
33405 return;
33406 }
33407
33408 MARKER_TYPES.forEach(function(markerType) {
33409 if (attr$1(gfx, markerType)) {
33410 var marker = getMarker(gfx, markerType, self._canvas.getContainer());
33411
33412 self._cloneMarker(gfx, marker, markerType);
33413 }
33414 });
33415 };
33416
33417 /**
33418 * Clone marker referenced by an element.
33419 *
33420 * @param {SVGElement} gfx
33421 * @param {SVGElement} marker
33422 * @param {string} markerType
33423 */
33424 PreviewSupport.prototype._cloneMarker = function(gfx, marker, markerType) {
33425 var markerId = marker.id;
33426
33427 var clonedMarker = this._clonedMarkers[ markerId ];
33428
33429 if (!clonedMarker) {
33430 clonedMarker = clone$1(marker);
33431
33432 var clonedMarkerId = markerId + '-clone';
33433
33434 clonedMarker.id = clonedMarkerId;
33435
33436 classes$1(clonedMarker)
33437 .add('djs-dragger')
33438 .add('djs-dragger-marker');
33439
33440 this._clonedMarkers[ markerId ] = clonedMarker;
33441
33442 var defs = query('defs', this._canvas._svg);
33443
33444 if (!defs) {
33445 defs = create$1('defs');
33446
33447 append(this._canvas._svg, defs);
33448 }
33449
33450 append(defs, clonedMarker);
33451 }
33452
33453 var reference = idToReference(this._clonedMarkers[ markerId ].id);
33454
33455 attr$1(gfx, markerType, reference);
33456 };
33457
33458 // helpers //////////
33459
33460 /**
33461 * Get marker of given type referenced by node.
33462 *
33463 * @param {Node} node
33464 * @param {string} markerType
33465 * @param {Node} [parentNode]
33466 *
33467 * @param {Node}
33468 */
33469 function getMarker(node, markerType, parentNode) {
33470 var id = referenceToId(attr$1(node, markerType));
33471
33472 return query('marker#' + id, parentNode || document);
33473 }
33474
33475 /**
33476 * Get ID of fragment within current document from its functional IRI reference.
33477 * References may use single or double quotes.
33478 *
33479 * @param {string} reference
33480 *
33481 * @returns {string}
33482 */
33483 function referenceToId(reference) {
33484 return reference.match(/url\(['"]?#([^'"]*)['"]?\)/)[1];
33485 }
33486
33487 /**
33488 * Get functional IRI reference for given ID of fragment within current document.
33489 *
33490 * @param {string} id
33491 *
33492 * @returns {string}
33493 */
33494 function idToReference(id) {
33495 return 'url(#' + id + ')';
33496 }
33497
33498 /**
33499 * Check wether node type can have marker attributes.
33500 *
33501 * @param {Node} node
33502 *
33503 * @returns {boolean}
33504 */
33505 function canHaveMarker(node) {
33506 return NODES_CAN_HAVE_MARKER.indexOf(node.nodeName) !== -1;
33507 }
33508
33509 var PreviewSupportModule = {
33510 __init__: [ 'previewSupport' ],
33511 previewSupport: [ 'type', PreviewSupport ]
33512 };
33513
33514 var MARKER_OK$2 = 'drop-ok',
33515 MARKER_NOT_OK$2 = 'drop-not-ok',
33516 MARKER_ATTACH$2 = 'attach-ok',
33517 MARKER_NEW_PARENT$1 = 'new-parent';
33518
33519 var PREFIX = 'create';
33520
33521 var HIGH_PRIORITY$h = 2000;
33522
33523
33524 /**
33525 * Create new elements through drag and drop.
33526 *
33527 * @param {Canvas} canvas
33528 * @param {Dragging} dragging
33529 * @param {EventBus} eventBus
33530 * @param {Modeling} modeling
33531 * @param {Rules} rules
33532 */
33533 function Create(
33534 canvas,
33535 dragging,
33536 eventBus,
33537 modeling,
33538 rules
33539 ) {
33540
33541 // rules //////////
33542
33543 /**
33544 * Check wether elements can be created.
33545 *
33546 * @param {Array<djs.model.Base>} elements
33547 * @param {djs.model.Base} target
33548 * @param {Point} position
33549 * @param {djs.model.Base} [source]
33550 *
33551 * @returns {boolean|null|Object}
33552 */
33553 function canCreate(elements, target, position, source, hints) {
33554 if (!target) {
33555 return false;
33556 }
33557
33558 // ignore child elements and external labels
33559 elements = filter(elements, function(element) {
33560 var labelTarget = element.labelTarget;
33561
33562 return !element.parent && !(isLabel$5(element) && elements.indexOf(labelTarget) !== -1);
33563 });
33564
33565 var shape = find(elements, function(element) {
33566 return !isConnection$b(element);
33567 });
33568
33569 var attach = false,
33570 connect = false,
33571 create = false;
33572
33573 // (1) attaching single shapes
33574 if (isSingleShape(elements)) {
33575 attach = rules.allowed('shape.attach', {
33576 position: position,
33577 shape: shape,
33578 target: target
33579 });
33580 }
33581
33582 if (!attach) {
33583
33584 // (2) creating elements
33585 if (isSingleShape(elements)) {
33586 create = rules.allowed('shape.create', {
33587 position: position,
33588 shape: shape,
33589 source: source,
33590 target: target
33591 });
33592 } else {
33593 create = rules.allowed('elements.create', {
33594 elements: elements,
33595 position: position,
33596 target: target
33597 });
33598 }
33599
33600 }
33601
33602 var connectionTarget = hints.connectionTarget;
33603
33604 // (3) appending single shapes
33605 if (create || attach) {
33606 if (shape && source) {
33607 connect = rules.allowed('connection.create', {
33608 source: connectionTarget === source ? shape : source,
33609 target: connectionTarget === source ? source : shape,
33610 hints: {
33611 targetParent: target,
33612 targetAttach: attach
33613 }
33614 });
33615 }
33616
33617 return {
33618 attach: attach,
33619 connect: connect
33620 };
33621 }
33622
33623 // ignore wether or not elements can be created
33624 if (create === null || attach === null) {
33625 return null;
33626 }
33627
33628 return false;
33629 }
33630
33631 function setMarker(element, marker) {
33632 [ MARKER_ATTACH$2, MARKER_OK$2, MARKER_NOT_OK$2, MARKER_NEW_PARENT$1 ].forEach(function(m) {
33633
33634 if (m === marker) {
33635 canvas.addMarker(element, m);
33636 } else {
33637 canvas.removeMarker(element, m);
33638 }
33639 });
33640 }
33641
33642 // event handling //////////
33643
33644 eventBus.on([ 'create.move', 'create.hover' ], function(event) {
33645 var context = event.context,
33646 elements = context.elements,
33647 hover = event.hover,
33648 source = context.source,
33649 hints = context.hints || {};
33650
33651 if (!hover) {
33652 context.canExecute = false;
33653 context.target = null;
33654
33655 return;
33656 }
33657
33658 ensureConstraints$2(event);
33659
33660 var position = {
33661 x: event.x,
33662 y: event.y
33663 };
33664
33665 var canExecute = context.canExecute = hover && canCreate(elements, hover, position, source, hints);
33666
33667 if (hover && canExecute !== null) {
33668 context.target = hover;
33669
33670 if (canExecute && canExecute.attach) {
33671 setMarker(hover, MARKER_ATTACH$2);
33672 } else {
33673 setMarker(hover, canExecute ? MARKER_NEW_PARENT$1 : MARKER_NOT_OK$2);
33674 }
33675 }
33676 });
33677
33678 eventBus.on([ 'create.end', 'create.out', 'create.cleanup' ], function(event) {
33679 var hover = event.hover;
33680
33681 if (hover) {
33682 setMarker(hover, null);
33683 }
33684 });
33685
33686 eventBus.on('create.end', function(event) {
33687 var context = event.context,
33688 source = context.source,
33689 shape = context.shape,
33690 elements = context.elements,
33691 target = context.target,
33692 canExecute = context.canExecute,
33693 attach = canExecute && canExecute.attach,
33694 connect = canExecute && canExecute.connect,
33695 hints = context.hints || {};
33696
33697 if (canExecute === false || !target) {
33698 return false;
33699 }
33700
33701 ensureConstraints$2(event);
33702
33703 var position = {
33704 x: event.x,
33705 y: event.y
33706 };
33707
33708 if (connect) {
33709 shape = modeling.appendShape(source, shape, position, target, {
33710 attach: attach,
33711 connection: connect === true ? {} : connect,
33712 connectionTarget: hints.connectionTarget
33713 });
33714 } else {
33715 elements = modeling.createElements(elements, position, target, assign({}, hints, {
33716 attach: attach
33717 }));
33718
33719 // update shape
33720 shape = find(elements, function(element) {
33721 return !isConnection$b(element);
33722 });
33723 }
33724
33725 // update elements and shape
33726 assign(context, {
33727 elements: elements,
33728 shape: shape
33729 });
33730
33731 assign(event, {
33732 elements: elements,
33733 shape: shape
33734 });
33735 });
33736
33737 function cancel() {
33738 var context = dragging.context();
33739
33740 if (context && context.prefix === PREFIX) {
33741 dragging.cancel();
33742 }
33743 }
33744
33745 // cancel on <elements.changed> that is not result of <drag.end>
33746 eventBus.on('create.init', function() {
33747 eventBus.on('elements.changed', cancel);
33748
33749 eventBus.once([ 'create.cancel', 'create.end' ], HIGH_PRIORITY$h, function() {
33750 eventBus.off('elements.changed', cancel);
33751 });
33752 });
33753
33754 // API //////////
33755
33756 this.start = function(event, elements, context) {
33757 if (!isArray$4(elements)) {
33758 elements = [ elements ];
33759 }
33760
33761 var shape = find(elements, function(element) {
33762 return !isConnection$b(element);
33763 });
33764
33765 if (!shape) {
33766
33767 // at least one shape is required
33768 return;
33769 }
33770
33771 context = assign({
33772 elements: elements,
33773 hints: {},
33774 shape: shape
33775 }, context || {});
33776
33777 // make sure each element has x and y
33778 forEach$2(elements, function(element) {
33779 if (!isNumber(element.x)) {
33780 element.x = 0;
33781 }
33782
33783 if (!isNumber(element.y)) {
33784 element.y = 0;
33785 }
33786 });
33787
33788 var visibleElements = filter(elements, function(element) {
33789 return !element.hidden;
33790 });
33791
33792 var bbox = getBBox(visibleElements);
33793
33794 // center elements around cursor
33795 forEach$2(elements, function(element) {
33796 if (isConnection$b(element)) {
33797 element.waypoints = map(element.waypoints, function(waypoint) {
33798 return {
33799 x: waypoint.x - bbox.x - bbox.width / 2,
33800 y: waypoint.y - bbox.y - bbox.height / 2
33801 };
33802 });
33803 }
33804
33805 assign(element, {
33806 x: element.x - bbox.x - bbox.width / 2,
33807 y: element.y - bbox.y - bbox.height / 2
33808 });
33809 });
33810
33811 dragging.init(event, PREFIX, {
33812 cursor: 'grabbing',
33813 autoActivate: true,
33814 data: {
33815 shape: shape,
33816 elements: elements,
33817 context: context
33818 }
33819 });
33820 };
33821 }
33822
33823 Create.$inject = [
33824 'canvas',
33825 'dragging',
33826 'eventBus',
33827 'modeling',
33828 'rules'
33829 ];
33830
33831 // helpers //////////
33832
33833 function ensureConstraints$2(event) {
33834 var context = event.context,
33835 createConstraints = context.createConstraints;
33836
33837 if (!createConstraints) {
33838 return;
33839 }
33840
33841 if (createConstraints.left) {
33842 event.x = Math.max(event.x, createConstraints.left);
33843 }
33844
33845 if (createConstraints.right) {
33846 event.x = Math.min(event.x, createConstraints.right);
33847 }
33848
33849 if (createConstraints.top) {
33850 event.y = Math.max(event.y, createConstraints.top);
33851 }
33852
33853 if (createConstraints.bottom) {
33854 event.y = Math.min(event.y, createConstraints.bottom);
33855 }
33856 }
33857
33858 function isConnection$b(element) {
33859 return !!element.waypoints;
33860 }
33861
33862 function isSingleShape(elements) {
33863 return elements && elements.length === 1 && !isConnection$b(elements[0]);
33864 }
33865
33866 function isLabel$5(element) {
33867 return !!element.labelTarget;
33868 }
33869
33870 var LOW_PRIORITY$h = 750;
33871
33872
33873 function CreatePreview(
33874 canvas,
33875 eventBus,
33876 graphicsFactory,
33877 previewSupport,
33878 styles
33879 ) {
33880 function createDragGroup(elements) {
33881 var dragGroup = create$1('g');
33882
33883 attr$1(dragGroup, styles.cls('djs-drag-group', [ 'no-events' ]));
33884
33885 var childrenGfx = create$1('g');
33886
33887 elements.forEach(function(element) {
33888
33889 // create graphics
33890 var gfx;
33891
33892 if (element.hidden) {
33893 return;
33894 }
33895
33896 if (element.waypoints) {
33897 gfx = graphicsFactory._createContainer('connection', childrenGfx);
33898
33899 graphicsFactory.drawConnection(getVisual(gfx), element);
33900 } else {
33901 gfx = graphicsFactory._createContainer('shape', childrenGfx);
33902
33903 graphicsFactory.drawShape(getVisual(gfx), element);
33904
33905 translate$2(gfx, element.x, element.y);
33906 }
33907
33908 // add preview
33909 previewSupport.addDragger(element, dragGroup, gfx);
33910 });
33911
33912 return dragGroup;
33913 }
33914
33915 eventBus.on('create.move', LOW_PRIORITY$h, function(event) {
33916
33917 var hover = event.hover,
33918 context = event.context,
33919 elements = context.elements,
33920 dragGroup = context.dragGroup;
33921
33922 // lazily create previews
33923 if (!dragGroup) {
33924 dragGroup = context.dragGroup = createDragGroup(elements);
33925 }
33926
33927 var activeLayer;
33928
33929 if (hover) {
33930 if (!dragGroup.parentNode) {
33931 activeLayer = canvas.getActiveLayer();
33932
33933 append(activeLayer, dragGroup);
33934 }
33935
33936 translate$2(dragGroup, event.x, event.y);
33937 } else {
33938 remove$2(dragGroup);
33939 }
33940 });
33941
33942 eventBus.on('create.cleanup', function(event) {
33943 var context = event.context,
33944 dragGroup = context.dragGroup;
33945
33946 if (dragGroup) {
33947 remove$2(dragGroup);
33948 }
33949 });
33950 }
33951
33952 CreatePreview.$inject = [
33953 'canvas',
33954 'eventBus',
33955 'graphicsFactory',
33956 'previewSupport',
33957 'styles'
33958 ];
33959
33960 var CreateModule = {
33961 __depends__: [
33962 DraggingModule,
33963 PreviewSupportModule,
33964 RulesModule$1,
33965 SelectionModule
33966 ],
33967 __init__: [
33968 'create',
33969 'createPreview'
33970 ],
33971 create: [ 'type', Create ],
33972 createPreview: [ 'type', CreatePreview ]
33973 };
33974
33975 var DATA_REF = 'data-id';
33976
33977 var CLOSE_EVENTS = [
33978 'contextPad.close',
33979 'canvas.viewbox.changing',
33980 'commandStack.changed'
33981 ];
33982
33983 var DEFAULT_PRIORITY$1 = 1000;
33984
33985
33986 /**
33987 * A popup menu that can be used to display a list of actions anywhere in the canvas.
33988 *
33989 * @param {Object} config
33990 * @param {boolean|Object} [config.scale={ min: 1.0, max: 1.5 }]
33991 * @param {number} [config.scale.min]
33992 * @param {number} [config.scale.max]
33993 * @param {EventBus} eventBus
33994 * @param {Canvas} canvas
33995 *
33996 * @class
33997 * @constructor
33998 */
33999 function PopupMenu(config, eventBus, canvas) {
34000
34001 var scale = isDefined(config && config.scale) ? config.scale : {
34002 min: 1,
34003 max: 1.5
34004 };
34005
34006 this._config = {
34007 scale: scale
34008 };
34009
34010 this._eventBus = eventBus;
34011 this._canvas = canvas;
34012 this._providers = {};
34013 this._current = {};
34014 }
34015
34016 PopupMenu.$inject = [
34017 'config.popupMenu',
34018 'eventBus',
34019 'canvas'
34020 ];
34021
34022 /**
34023 * Registers a popup menu provider
34024 *
34025 * @param {string} id
34026 * @param {number} [priority=1000]
34027 * @param {Object} provider
34028 *
34029 * @example
34030 * const popupMenuProvider = {
34031 * getPopupMenuEntries: function(element) {
34032 * return {
34033 * 'entry-1': {
34034 * label: 'My Entry',
34035 * action: function() { alert("I have been clicked!"); }
34036 * }
34037 * }
34038 * }
34039 * };
34040 *
34041 * popupMenu.registerProvider('myMenuID', popupMenuProvider);
34042 */
34043 PopupMenu.prototype.registerProvider = function(id, priority, provider) {
34044 if (!provider) {
34045 provider = priority;
34046 priority = DEFAULT_PRIORITY$1;
34047 }
34048
34049 this._eventBus.on('popupMenu.getProviders.' + id, priority, function(event) {
34050 event.providers.push(provider);
34051 });
34052 };
34053
34054 /**
34055 * Determine if the popup menu has entries.
34056 *
34057 * @return {boolean} true if empty
34058 */
34059 PopupMenu.prototype.isEmpty = function(element, providerId) {
34060 if (!element) {
34061 throw new Error('element parameter is missing');
34062 }
34063
34064 if (!providerId) {
34065 throw new Error('providerId parameter is missing');
34066 }
34067
34068 var providers = this._getProviders(providerId);
34069
34070 if (!providers) {
34071 return true;
34072 }
34073
34074 var entries = this._getEntries(element, providers),
34075 headerEntries = this._getHeaderEntries(element, providers);
34076
34077 var hasEntries = size(entries) > 0,
34078 hasHeaderEntries = headerEntries && size(headerEntries) > 0;
34079
34080 return !hasEntries && !hasHeaderEntries;
34081 };
34082
34083
34084 /**
34085 * Create entries and open popup menu at given position
34086 *
34087 * @param {Object} element
34088 * @param {string} id provider id
34089 * @param {Object} position
34090 *
34091 * @return {Object} popup menu instance
34092 */
34093 PopupMenu.prototype.open = function(element, id, position) {
34094
34095 var providers = this._getProviders(id);
34096
34097 if (!element) {
34098 throw new Error('Element is missing');
34099 }
34100
34101 if (!providers || !providers.length) {
34102 throw new Error('No registered providers for: ' + id);
34103 }
34104
34105 if (!position) {
34106 throw new Error('the position argument is missing');
34107 }
34108
34109 if (this.isOpen()) {
34110 this.close();
34111 }
34112
34113 this._emit('open');
34114
34115 var current = this._current = {
34116 className: id,
34117 element: element,
34118 position: position
34119 };
34120
34121 var entries = this._getEntries(element, providers),
34122 headerEntries = this._getHeaderEntries(element, providers);
34123
34124 current.entries = assign({}, entries, headerEntries);
34125
34126 current.container = this._createContainer();
34127
34128 if (size(headerEntries)) {
34129 current.container.appendChild(
34130 this._createEntries(headerEntries, 'djs-popup-header')
34131 );
34132 }
34133
34134 if (size(entries)) {
34135 current.container.appendChild(
34136 this._createEntries(entries, 'djs-popup-body')
34137 );
34138 }
34139
34140 var canvas = this._canvas,
34141 parent = canvas.getContainer();
34142
34143 this._attachContainer(current.container, parent, position.cursor);
34144 this._bindAutoClose();
34145 };
34146
34147
34148 /**
34149 * Removes the popup menu and unbinds the event handlers.
34150 */
34151 PopupMenu.prototype.close = function() {
34152
34153 if (!this.isOpen()) {
34154 return;
34155 }
34156
34157 this._emit('close');
34158
34159 this._unbindAutoClose();
34160 remove$1(this._current.container);
34161 this._current.container = null;
34162 };
34163
34164
34165 /**
34166 * Determine if an open popup menu exist.
34167 *
34168 * @return {boolean} true if open
34169 */
34170 PopupMenu.prototype.isOpen = function() {
34171 return !!this._current.container;
34172 };
34173
34174
34175 /**
34176 * Trigger an action associated with an entry.
34177 *
34178 * @param {Object} event
34179 *
34180 * @return the result of the action callback, if any
34181 */
34182 PopupMenu.prototype.trigger = function(event) {
34183
34184 // silence other actions
34185 event.preventDefault();
34186
34187 var element = event.delegateTarget || event.target,
34188 entryId = attr(element, DATA_REF);
34189
34190 var entry = this._getEntry(entryId);
34191
34192 if (entry.action) {
34193 return entry.action.call(null, event, entry);
34194 }
34195 };
34196
34197 PopupMenu.prototype._getProviders = function(id) {
34198
34199 var event = this._eventBus.createEvent({
34200 type: 'popupMenu.getProviders.' + id,
34201 providers: []
34202 });
34203
34204 this._eventBus.fire(event);
34205
34206 return event.providers;
34207 };
34208
34209 PopupMenu.prototype._getEntries = function(element, providers) {
34210
34211 var entries = {};
34212
34213 forEach$2(providers, function(provider) {
34214
34215 // handle legacy method
34216 if (!provider.getPopupMenuEntries) {
34217 forEach$2(provider.getEntries(element), function(entry) {
34218 var id = entry.id;
34219
34220 if (!id) {
34221 throw new Error('every entry must have the id property set');
34222 }
34223
34224 entries[id] = omit(entry, [ 'id' ]);
34225 });
34226
34227 return;
34228 }
34229
34230 var entriesOrUpdater = provider.getPopupMenuEntries(element);
34231
34232 if (isFunction(entriesOrUpdater)) {
34233 entries = entriesOrUpdater(entries);
34234 } else {
34235 forEach$2(entriesOrUpdater, function(entry, id) {
34236 entries[id] = entry;
34237 });
34238 }
34239 });
34240
34241 return entries;
34242 };
34243
34244 PopupMenu.prototype._getHeaderEntries = function(element, providers) {
34245
34246 var entries = {};
34247
34248 forEach$2(providers, function(provider) {
34249
34250 // handle legacy method
34251 if (!provider.getPopupMenuHeaderEntries) {
34252 if (!provider.getHeaderEntries) {
34253 return;
34254 }
34255
34256 forEach$2(provider.getHeaderEntries(element), function(entry) {
34257 var id = entry.id;
34258
34259 if (!id) {
34260 throw new Error('every entry must have the id property set');
34261 }
34262
34263 entries[id] = omit(entry, [ 'id' ]);
34264 });
34265
34266 return;
34267 }
34268
34269 var entriesOrUpdater = provider.getPopupMenuHeaderEntries(element);
34270
34271 if (isFunction(entriesOrUpdater)) {
34272 entries = entriesOrUpdater(entries);
34273 } else {
34274 forEach$2(entriesOrUpdater, function(entry, id) {
34275 entries[id] = entry;
34276 });
34277 }
34278 });
34279
34280 return entries;
34281
34282
34283 };
34284
34285 /**
34286 * Gets an entry instance (either entry or headerEntry) by id.
34287 *
34288 * @param {string} entryId
34289 *
34290 * @return {Object} entry instance
34291 */
34292 PopupMenu.prototype._getEntry = function(entryId) {
34293
34294 var entry = this._current.entries[entryId];
34295
34296 if (!entry) {
34297 throw new Error('entry not found');
34298 }
34299
34300 return entry;
34301 };
34302
34303 PopupMenu.prototype._emit = function(eventName) {
34304 this._eventBus.fire('popupMenu.' + eventName);
34305 };
34306
34307 /**
34308 * Creates the popup menu container.
34309 *
34310 * @return {Object} a DOM container
34311 */
34312 PopupMenu.prototype._createContainer = function() {
34313 var container = domify('<div class="djs-popup">'),
34314 position = this._current.position,
34315 className = this._current.className;
34316
34317 assign$1(container, {
34318 position: 'absolute',
34319 left: position.x + 'px',
34320 top: position.y + 'px',
34321 visibility: 'hidden'
34322 });
34323
34324 classes(container).add(className);
34325
34326 return container;
34327 };
34328
34329
34330 /**
34331 * Attaches the container to the DOM.
34332 *
34333 * @param {Object} container
34334 * @param {Object} parent
34335 */
34336 PopupMenu.prototype._attachContainer = function(container, parent, cursor) {
34337 var self = this;
34338
34339 // Event handler
34340 delegate.bind(container, '.entry' ,'click', function(event) {
34341 self.trigger(event);
34342 });
34343
34344 this._updateScale(container);
34345
34346 // Attach to DOM
34347 parent.appendChild(container);
34348
34349 if (cursor) {
34350 this._assureIsInbounds(container, cursor);
34351 }
34352 };
34353
34354
34355 /**
34356 * Updates popup style.transform with respect to the config and zoom level.
34357 *
34358 * @method _updateScale
34359 *
34360 * @param {Object} container
34361 */
34362 PopupMenu.prototype._updateScale = function(container) {
34363 var zoom = this._canvas.zoom();
34364
34365 var scaleConfig = this._config.scale,
34366 minScale,
34367 maxScale,
34368 scale = zoom;
34369
34370 if (scaleConfig !== true) {
34371
34372 if (scaleConfig === false) {
34373 minScale = 1;
34374 maxScale = 1;
34375 } else {
34376 minScale = scaleConfig.min;
34377 maxScale = scaleConfig.max;
34378 }
34379
34380 if (isDefined(minScale) && zoom < minScale) {
34381 scale = minScale;
34382 }
34383
34384 if (isDefined(maxScale) && zoom > maxScale) {
34385 scale = maxScale;
34386 }
34387
34388 }
34389
34390 setTransform(container, 'scale(' + scale + ')');
34391 };
34392
34393
34394 /**
34395 * Make sure that the menu is always fully shown
34396 *
34397 * @method function
34398 *
34399 * @param {Object} container
34400 * @param {Position} cursor {x, y}
34401 */
34402 PopupMenu.prototype._assureIsInbounds = function(container, cursor) {
34403 var canvas = this._canvas,
34404 clientRect = canvas._container.getBoundingClientRect();
34405
34406 var containerX = container.offsetLeft,
34407 containerY = container.offsetTop,
34408 containerWidth = container.scrollWidth,
34409 containerHeight = container.scrollHeight,
34410 overAxis = {},
34411 left, top;
34412
34413 var cursorPosition = {
34414 x: cursor.x - clientRect.left,
34415 y: cursor.y - clientRect.top
34416 };
34417
34418 if (containerX + containerWidth > clientRect.width) {
34419 overAxis.x = true;
34420 }
34421
34422 if (containerY + containerHeight > clientRect.height) {
34423 overAxis.y = true;
34424 }
34425
34426 if (overAxis.x && overAxis.y) {
34427 left = cursorPosition.x - containerWidth + 'px';
34428 top = cursorPosition.y - containerHeight + 'px';
34429 } else if (overAxis.x) {
34430 left = cursorPosition.x - containerWidth + 'px';
34431 top = cursorPosition.y + 'px';
34432 } else if (overAxis.y && cursorPosition.y < containerHeight) {
34433 left = cursorPosition.x + 'px';
34434 top = 10 + 'px';
34435 } else if (overAxis.y) {
34436 left = cursorPosition.x + 'px';
34437 top = cursorPosition.y - containerHeight + 'px';
34438 }
34439
34440 assign$1(container, { left: left, top: top }, { visibility: 'visible', 'zIndex': 1000 });
34441 };
34442
34443
34444 /**
34445 * Creates a list of entries and returns them as a DOM container.
34446 *
34447 * @param {Array<Object>} entries an array of entry objects
34448 * @param {string} className the class name of the entry container
34449 *
34450 * @return {Object} a DOM container
34451 */
34452 PopupMenu.prototype._createEntries = function(entries, className) {
34453
34454 var entriesContainer = domify('<div>'),
34455 self = this;
34456
34457 classes(entriesContainer).add(className);
34458
34459 forEach$2(entries, function(entry, id) {
34460 var entryContainer = self._createEntry(entry, id);
34461 entriesContainer.appendChild(entryContainer);
34462 });
34463
34464 return entriesContainer;
34465 };
34466
34467
34468 /**
34469 * Creates a single entry and returns it as a DOM container.
34470 *
34471 * @param {Object} entry
34472 *
34473 * @return {Object} a DOM container
34474 */
34475 PopupMenu.prototype._createEntry = function(entry, id) {
34476
34477 var entryContainer = domify('<div>'),
34478 entryClasses = classes(entryContainer);
34479
34480 entryClasses.add('entry');
34481
34482 if (entry.className) {
34483 entry.className.split(' ').forEach(function(className) {
34484 entryClasses.add(className);
34485 });
34486 }
34487
34488 attr(entryContainer, DATA_REF, id);
34489
34490 if (entry.label) {
34491 var label = domify('<span>');
34492 label.textContent = entry.label;
34493 entryContainer.appendChild(label);
34494 }
34495
34496 if (entry.imageUrl) {
34497 entryContainer.appendChild(domify('<img src="' + entry.imageUrl + '" />'));
34498 }
34499
34500 if (entry.active === true) {
34501 entryClasses.add('active');
34502 }
34503
34504 if (entry.disabled === true) {
34505 entryClasses.add('disabled');
34506 }
34507
34508 if (entry.title) {
34509 entryContainer.title = entry.title;
34510 }
34511
34512 return entryContainer;
34513 };
34514
34515
34516 /**
34517 * Set up listener to close popup automatically on certain events.
34518 */
34519 PopupMenu.prototype._bindAutoClose = function() {
34520 this._eventBus.once(CLOSE_EVENTS, this.close, this);
34521 };
34522
34523
34524 /**
34525 * Remove the auto-closing listener.
34526 */
34527 PopupMenu.prototype._unbindAutoClose = function() {
34528 this._eventBus.off(CLOSE_EVENTS, this.close, this);
34529 };
34530
34531
34532
34533 // helpers /////////////////////////////
34534
34535 function setTransform(element, transform) {
34536 element.style['transform-origin'] = 'top left';
34537
34538 [ '', '-ms-', '-webkit-' ].forEach(function(prefix) {
34539 element.style[prefix + 'transform'] = transform;
34540 });
34541 }
34542
34543 var PopupMenuModule$1 = {
34544 __init__: [ 'popupMenu' ],
34545 popupMenu: [ 'type', PopupMenu ]
34546 };
34547
34548 /**
34549 * A clip board stub
34550 */
34551 function Clipboard() {}
34552
34553
34554 Clipboard.prototype.get = function() {
34555 return this._data;
34556 };
34557
34558 Clipboard.prototype.set = function(data) {
34559 this._data = data;
34560 };
34561
34562 Clipboard.prototype.clear = function() {
34563 var data = this._data;
34564
34565 delete this._data;
34566
34567 return data;
34568 };
34569
34570 Clipboard.prototype.isEmpty = function() {
34571 return !this._data;
34572 };
34573
34574 var ClipboardModule = {
34575 clipboard: [ 'type', Clipboard ]
34576 };
34577
34578 function Mouse(eventBus) {
34579 var self = this;
34580
34581 this._lastMoveEvent = null;
34582
34583 function setLastMoveEvent(mousemoveEvent) {
34584 self._lastMoveEvent = mousemoveEvent;
34585 }
34586
34587 eventBus.on('canvas.init', function(context) {
34588 var svg = self._svg = context.svg;
34589
34590 svg.addEventListener('mousemove', setLastMoveEvent);
34591 });
34592
34593 eventBus.on('canvas.destroy', function() {
34594 self._lastMouseEvent = null;
34595
34596 self._svg.removeEventListener('mousemove', setLastMoveEvent);
34597 });
34598 }
34599
34600 Mouse.$inject = [ 'eventBus' ];
34601
34602 Mouse.prototype.getLastMoveEvent = function() {
34603 return this._lastMoveEvent || createMoveEvent(0, 0);
34604 };
34605
34606 // helpers //////////
34607
34608 function createMoveEvent(x, y) {
34609 var event = document.createEvent('MouseEvent');
34610
34611 var screenX = x,
34612 screenY = y,
34613 clientX = x,
34614 clientY = y;
34615
34616 if (event.initMouseEvent) {
34617 event.initMouseEvent(
34618 'mousemove',
34619 true,
34620 true,
34621 window,
34622 0,
34623 screenX,
34624 screenY,
34625 clientX,
34626 clientY,
34627 false,
34628 false,
34629 false,
34630 false,
34631 0,
34632 null
34633 );
34634 }
34635
34636 return event;
34637 }
34638
34639 var MouseModule = {
34640 __init__: [ 'mouse' ],
34641 mouse: [ 'type', Mouse ]
34642 };
34643
34644 /**
34645 * @typedef {Function} <copyPaste.canCopyElements> listener
34646 *
34647 * @param {Object} context
34648 * @param {Array<djs.model.Base>} context.elements
34649 *
34650 * @returns {Array<djs.model.Base>|boolean} - Return elements to be copied or false to disallow
34651 * copying.
34652 */
34653
34654 /**
34655 * @typedef {Function} <copyPaste.copyElement> listener
34656 *
34657 * @param {Object} context
34658 * @param {Object} context.descriptor
34659 * @param {djs.model.Base} context.element
34660 * @param {Array<djs.model.Base>} context.elements
34661 */
34662
34663 /**
34664 * @typedef {Function} <copyPaste.createTree> listener
34665 *
34666 * @param {Object} context
34667 * @param {djs.model.Base} context.element
34668 * @param {Array<djs.model.Base>} context.children - Add children to be added to tree.
34669 */
34670
34671 /**
34672 * @typedef {Function} <copyPaste.elementsCopied> listener
34673 *
34674 * @param {Object} context
34675 * @param {Object} context.elements
34676 * @param {Object} context.tree
34677 */
34678
34679 /**
34680 * @typedef {Function} <copyPaste.pasteElement> listener
34681 *
34682 * @param {Object} context
34683 * @param {Object} context.cache - Already created elements.
34684 * @param {Object} context.descriptor
34685 */
34686
34687 /**
34688 * @typedef {Function} <copyPaste.pasteElements> listener
34689 *
34690 * @param {Object} context
34691 * @param {Object} context.hints - Add hints before pasting.
34692 */
34693
34694 /**
34695 * Copy and paste elements.
34696 *
34697 * @param {Canvas} canvas
34698 * @param {Create} create
34699 * @param {Clipboard} clipboard
34700 * @param {ElementFactory} elementFactory
34701 * @param {EventBus} eventBus
34702 * @param {Modeling} modeling
34703 * @param {Mouse} mouse
34704 * @param {Rules} rules
34705 */
34706 function CopyPaste(
34707 canvas,
34708 create,
34709 clipboard,
34710 elementFactory,
34711 eventBus,
34712 modeling,
34713 mouse,
34714 rules
34715 ) {
34716
34717 this._canvas = canvas;
34718 this._create = create;
34719 this._clipboard = clipboard;
34720 this._elementFactory = elementFactory;
34721 this._eventBus = eventBus;
34722 this._modeling = modeling;
34723 this._mouse = mouse;
34724 this._rules = rules;
34725
34726 eventBus.on('copyPaste.copyElement', function(context) {
34727 var descriptor = context.descriptor,
34728 element = context.element,
34729 elements = context.elements;
34730
34731 // default priority (priority = 1)
34732 descriptor.priority = 1;
34733
34734 descriptor.id = element.id;
34735
34736 var parentCopied = find(elements, function(e) {
34737 return e === element.parent;
34738 });
34739
34740 // do NOT reference parent if parent wasn't copied
34741 if (parentCopied) {
34742 descriptor.parent = element.parent.id;
34743 }
34744
34745 // attachers (priority = 2)
34746 if (isAttacher$1(element)) {
34747 descriptor.priority = 2;
34748
34749 descriptor.host = element.host.id;
34750 }
34751
34752 // connections (priority = 3)
34753 if (isConnection$a(element)) {
34754 descriptor.priority = 3;
34755
34756 descriptor.source = element.source.id;
34757 descriptor.target = element.target.id;
34758
34759 descriptor.waypoints = copyWaypoints$1(element);
34760 }
34761
34762 // labels (priority = 4)
34763 if (isLabel$4(element)) {
34764 descriptor.priority = 4;
34765
34766 descriptor.labelTarget = element.labelTarget.id;
34767 }
34768
34769 forEach$2([ 'x', 'y', 'width', 'height' ], function(property) {
34770 if (isNumber(element[ property ])) {
34771 descriptor[ property ] = element[ property ];
34772 }
34773 });
34774
34775 descriptor.hidden = element.hidden;
34776 descriptor.collapsed = element.collapsed;
34777
34778 });
34779
34780 eventBus.on('copyPaste.pasteElements', function(context) {
34781 var hints = context.hints;
34782
34783 assign(hints, {
34784 createElementsBehavior: false
34785 });
34786 });
34787 }
34788
34789 CopyPaste.$inject = [
34790 'canvas',
34791 'create',
34792 'clipboard',
34793 'elementFactory',
34794 'eventBus',
34795 'modeling',
34796 'mouse',
34797 'rules'
34798 ];
34799
34800
34801 /**
34802 * Copy elements.
34803 *
34804 * @param {Array<djs.model.Base>} elements
34805 *
34806 * @returns {Object}
34807 */
34808 CopyPaste.prototype.copy = function(elements) {
34809 var allowed,
34810 tree;
34811
34812 if (!isArray$4(elements)) {
34813 elements = elements ? [ elements ] : [];
34814 }
34815
34816 allowed = this._eventBus.fire('copyPaste.canCopyElements', {
34817 elements: elements
34818 });
34819
34820 if (allowed === false) {
34821 tree = {};
34822 } else {
34823 tree = this.createTree(isArray$4(allowed) ? allowed : elements);
34824 }
34825
34826 // we set an empty tree, selection of elements
34827 // to copy was empty.
34828 this._clipboard.set(tree);
34829
34830 this._eventBus.fire('copyPaste.elementsCopied', {
34831 elements: elements,
34832 tree: tree
34833 });
34834
34835 return tree;
34836 };
34837
34838 /**
34839 * Paste elements.
34840 *
34841 * @param {Object} [context]
34842 * @param {djs.model.base} [context.element] - Parent.
34843 * @param {Point} [context.point] - Position.
34844 * @param {Object} [context.hints] - Hints.
34845 */
34846 CopyPaste.prototype.paste = function(context) {
34847 var tree = this._clipboard.get();
34848
34849 if (this._clipboard.isEmpty()) {
34850 return;
34851 }
34852
34853 var hints = context && context.hints || {};
34854
34855 this._eventBus.fire('copyPaste.pasteElements', {
34856 hints: hints
34857 });
34858
34859 var elements = this._createElements(tree);
34860
34861 // paste directly
34862 if (context && context.element && context.point) {
34863 return this._paste(elements, context.element, context.point, hints);
34864 }
34865
34866 this._create.start(this._mouse.getLastMoveEvent(), elements, {
34867 hints: hints || {}
34868 });
34869 };
34870
34871 /**
34872 * Paste elements directly.
34873 *
34874 * @param {Array<djs.model.Base>} elements
34875 * @param {djs.model.base} target
34876 * @param {Point} position
34877 * @param {Object} [hints]
34878 */
34879 CopyPaste.prototype._paste = function(elements, target, position, hints) {
34880
34881 // make sure each element has x and y
34882 forEach$2(elements, function(element) {
34883 if (!isNumber(element.x)) {
34884 element.x = 0;
34885 }
34886
34887 if (!isNumber(element.y)) {
34888 element.y = 0;
34889 }
34890 });
34891
34892 var bbox = getBBox(elements);
34893
34894 // center elements around cursor
34895 forEach$2(elements, function(element) {
34896 if (isConnection$a(element)) {
34897 element.waypoints = map(element.waypoints, function(waypoint) {
34898 return {
34899 x: waypoint.x - bbox.x - bbox.width / 2,
34900 y: waypoint.y - bbox.y - bbox.height / 2
34901 };
34902 });
34903 }
34904
34905 assign(element, {
34906 x: element.x - bbox.x - bbox.width / 2,
34907 y: element.y - bbox.y - bbox.height / 2
34908 });
34909 });
34910
34911 return this._modeling.createElements(elements, position, target, assign({}, hints));
34912 };
34913
34914 /**
34915 * Create elements from tree.
34916 */
34917 CopyPaste.prototype._createElements = function(tree) {
34918 var self = this;
34919
34920 var eventBus = this._eventBus;
34921
34922 var cache = {};
34923
34924 var elements = [];
34925
34926 forEach$2(tree, function(branch, depth) {
34927
34928 // sort by priority
34929 branch = sortBy(branch, 'priority');
34930
34931 forEach$2(branch, function(descriptor) {
34932
34933 // remove priority
34934 var attrs = assign({}, omit(descriptor, [ 'priority' ]));
34935
34936 if (cache[ descriptor.parent ]) {
34937 attrs.parent = cache[ descriptor.parent ];
34938 } else {
34939 delete attrs.parent;
34940 }
34941
34942 eventBus.fire('copyPaste.pasteElement', {
34943 cache: cache,
34944 descriptor: attrs
34945 });
34946
34947 var element;
34948
34949 if (isConnection$a(attrs)) {
34950 attrs.source = cache[ descriptor.source ];
34951 attrs.target = cache[ descriptor.target ];
34952
34953 element = cache[ descriptor.id ] = self.createConnection(attrs);
34954
34955 elements.push(element);
34956
34957 return;
34958 }
34959
34960 if (isLabel$4(attrs)) {
34961 attrs.labelTarget = cache[ attrs.labelTarget ];
34962
34963 element = cache[ descriptor.id ] = self.createLabel(attrs);
34964
34965 elements.push(element);
34966
34967 return;
34968 }
34969
34970 if (attrs.host) {
34971 attrs.host = cache[ attrs.host ];
34972 }
34973
34974 element = cache[ descriptor.id ] = self.createShape(attrs);
34975
34976 elements.push(element);
34977 });
34978
34979 });
34980
34981 return elements;
34982 };
34983
34984 CopyPaste.prototype.createConnection = function(attrs) {
34985 var connection = this._elementFactory.createConnection(omit(attrs, [ 'id' ]));
34986
34987 return connection;
34988 };
34989
34990 CopyPaste.prototype.createLabel = function(attrs) {
34991 var label = this._elementFactory.createLabel(omit(attrs, [ 'id' ]));
34992
34993 return label;
34994 };
34995
34996 CopyPaste.prototype.createShape = function(attrs) {
34997 var shape = this._elementFactory.createShape(omit(attrs, [ 'id' ]));
34998
34999 return shape;
35000 };
35001
35002 /**
35003 * Check wether element has relations to other elements e.g. attachers, labels and connections.
35004 *
35005 * @param {Object} element
35006 * @param {Array<djs.model.Base>} elements
35007 *
35008 * @returns {boolean}
35009 */
35010 CopyPaste.prototype.hasRelations = function(element, elements) {
35011 var labelTarget,
35012 source,
35013 target;
35014
35015 if (isConnection$a(element)) {
35016 source = find(elements, matchPattern({ id: element.source.id }));
35017 target = find(elements, matchPattern({ id: element.target.id }));
35018
35019 if (!source || !target) {
35020 return false;
35021 }
35022 }
35023
35024 if (isLabel$4(element)) {
35025 labelTarget = find(elements, matchPattern({ id: element.labelTarget.id }));
35026
35027 if (!labelTarget) {
35028 return false;
35029 }
35030 }
35031
35032 return true;
35033 };
35034
35035 /**
35036 * Create a tree-like structure from elements.
35037 *
35038 * @example
35039 * tree: {
35040 * 0: [
35041 * { id: 'Shape_1', priority: 1, ... },
35042 * { id: 'Shape_2', priority: 1, ... },
35043 * { id: 'Connection_1', source: 'Shape_1', target: 'Shape_2', priority: 3, ... },
35044 * ...
35045 * ],
35046 * 1: [
35047 * { id: 'Shape_3', parent: 'Shape1', priority: 1, ... },
35048 * ...
35049 * ]
35050 * };
35051 *
35052 * @param {Array<djs.model.base>} elements
35053 *
35054 * @return {Object}
35055 */
35056 CopyPaste.prototype.createTree = function(elements) {
35057 var rules = this._rules,
35058 self = this;
35059
35060 var tree = {},
35061 elementsData = [];
35062
35063 var parents = getParents$1(elements);
35064
35065 function canCopy(element, elements) {
35066 return rules.allowed('element.copy', {
35067 element: element,
35068 elements: elements
35069 });
35070 }
35071
35072 function addElementData(element, depth) {
35073
35074 // (1) check wether element has already been added
35075 var foundElementData = find(elementsData, function(elementsData) {
35076 return element === elementsData.element;
35077 });
35078
35079 // (2) add element if not already added
35080 if (!foundElementData) {
35081 elementsData.push({
35082 element: element,
35083 depth: depth
35084 });
35085
35086 return;
35087 }
35088
35089 // (3) update depth
35090 if (foundElementData.depth < depth) {
35091 elementsData = removeElementData(foundElementData, elementsData);
35092
35093 elementsData.push({
35094 element: foundElementData.element,
35095 depth: depth
35096 });
35097 }
35098 }
35099
35100 function removeElementData(elementData, elementsData) {
35101 var index = elementsData.indexOf(elementData);
35102
35103 if (index !== -1) {
35104 elementsData.splice(index, 1);
35105 }
35106
35107 return elementsData;
35108 }
35109
35110 // (1) add elements
35111 eachElement(parents, function(element, _index, depth) {
35112
35113 // do NOT add external labels directly
35114 if (isLabel$4(element)) {
35115 return;
35116 }
35117
35118 // always copy external labels
35119 forEach$2(element.labels, function(label) {
35120 addElementData(label, depth);
35121 });
35122
35123 function addRelatedElements(elements) {
35124 elements && elements.length && forEach$2(elements, function(element) {
35125
35126 // add external labels
35127 forEach$2(element.labels, function(label) {
35128 addElementData(label, depth);
35129 });
35130
35131 addElementData(element, depth);
35132 });
35133 }
35134
35135 forEach$2([ element.attachers, element.incoming, element.outgoing ], addRelatedElements);
35136
35137 addElementData(element, depth);
35138
35139 var children = [];
35140
35141 if (element.children) {
35142 children = element.children.slice();
35143 }
35144
35145 // allow others to add children to tree
35146 self._eventBus.fire('copyPaste.createTree', {
35147 element: element,
35148 children: children
35149 });
35150
35151 return children;
35152 });
35153
35154 elements = map(elementsData, function(elementData) {
35155 return elementData.element;
35156 });
35157
35158 // (2) copy elements
35159 elementsData = map(elementsData, function(elementData) {
35160 elementData.descriptor = {};
35161
35162 self._eventBus.fire('copyPaste.copyElement', {
35163 descriptor: elementData.descriptor,
35164 element: elementData.element,
35165 elements: elements
35166 });
35167
35168 return elementData;
35169 });
35170
35171 // (3) sort elements by priority
35172 elementsData = sortBy(elementsData, function(elementData) {
35173 return elementData.descriptor.priority;
35174 });
35175
35176 elements = map(elementsData, function(elementData) {
35177 return elementData.element;
35178 });
35179
35180 // (4) create tree
35181 forEach$2(elementsData, function(elementData) {
35182 var depth = elementData.depth;
35183
35184 if (!self.hasRelations(elementData.element, elements)) {
35185 removeElement(elementData.element, elements);
35186
35187 return;
35188 }
35189
35190 if (!canCopy(elementData.element, elements)) {
35191 removeElement(elementData.element, elements);
35192
35193 return;
35194 }
35195
35196 if (!tree[depth]) {
35197 tree[depth] = [];
35198 }
35199
35200 tree[depth].push(elementData.descriptor);
35201 });
35202
35203 return tree;
35204 };
35205
35206 // helpers //////////
35207
35208 function isAttacher$1(element) {
35209 return !!element.host;
35210 }
35211
35212 function isConnection$a(element) {
35213 return !!element.waypoints;
35214 }
35215
35216 function isLabel$4(element) {
35217 return !!element.labelTarget;
35218 }
35219
35220 function copyWaypoints$1(element) {
35221 return map(element.waypoints, function(waypoint) {
35222
35223 waypoint = copyWaypoint$1(waypoint);
35224
35225 if (waypoint.original) {
35226 waypoint.original = copyWaypoint$1(waypoint.original);
35227 }
35228
35229 return waypoint;
35230 });
35231 }
35232
35233 function copyWaypoint$1(waypoint) {
35234 return assign({}, waypoint);
35235 }
35236
35237 function removeElement(element, elements) {
35238 var index = elements.indexOf(element);
35239
35240 if (index === -1) {
35241 return elements;
35242 }
35243
35244 return elements.splice(index, 1);
35245 }
35246
35247 var CopyPasteModule$1 = {
35248 __depends__: [
35249 ClipboardModule,
35250 CreateModule,
35251 MouseModule,
35252 RulesModule$1
35253 ],
35254 __init__: [ 'copyPaste' ],
35255 copyPaste: [ 'type', CopyPaste ]
35256 };
35257
35258 function copyProperties$1(source, target, properties) {
35259 if (!isArray$4(properties)) {
35260 properties = [ properties ];
35261 }
35262
35263 forEach$2(properties, function(property) {
35264 if (!isUndefined$3(source[property])) {
35265 target[property] = source[property];
35266 }
35267 });
35268 }
35269
35270 function removeProperties(element, properties) {
35271 if (!isArray$4(properties)) {
35272 properties = [ properties ];
35273 }
35274
35275 forEach$2(properties, function(property) {
35276 if (element[property]) {
35277 delete element[property];
35278 }
35279 });
35280 }
35281
35282 var LOW_PRIORITY$g = 750;
35283
35284
35285 function BpmnCopyPaste(bpmnFactory, eventBus, moddleCopy) {
35286
35287 eventBus.on('copyPaste.copyElement', LOW_PRIORITY$g, function(context) {
35288 var descriptor = context.descriptor,
35289 element = context.element;
35290
35291 var businessObject = descriptor.oldBusinessObject = getBusinessObject(element);
35292 var di = descriptor.oldDi = getDi(element);
35293
35294 descriptor.type = element.type;
35295
35296 copyProperties$1(businessObject, descriptor, 'name');
35297
35298 copyProperties$1(di, descriptor, 'isExpanded');
35299
35300 if (isLabel$3(descriptor)) {
35301 return descriptor;
35302 }
35303
35304 // default sequence flow
35305 if (businessObject.default) {
35306 descriptor.default = businessObject.default.id;
35307 }
35308 });
35309
35310 eventBus.on('moddleCopy.canCopyProperty', function(context) {
35311 var parent = context.parent,
35312 property = context.property,
35313 propertyName = context.propertyName,
35314 bpmnProcess;
35315
35316 if (
35317 propertyName === 'processRef' &&
35318 is$1(parent, 'bpmn:Participant') &&
35319 is$1(property, 'bpmn:Process')
35320 ) {
35321 bpmnProcess = bpmnFactory.create('bpmn:Process');
35322
35323 // return copy of process
35324 return moddleCopy.copyElement(property, bpmnProcess);
35325 }
35326 });
35327
35328 var references;
35329
35330 function resolveReferences(descriptor, cache) {
35331 var businessObject = getBusinessObject(descriptor);
35332
35333 // default sequence flows
35334 if (descriptor.default) {
35335
35336 // relationship cannot be resolved immediately
35337 references[ descriptor.default ] = {
35338 element: businessObject,
35339 property: 'default'
35340 };
35341 }
35342
35343 // boundary events
35344 if (descriptor.host) {
35345
35346 // relationship can be resolved immediately
35347 getBusinessObject(descriptor).attachedToRef = getBusinessObject(cache[ descriptor.host ]);
35348 }
35349
35350 references = omit(references, reduce(references, function(array, reference, key) {
35351 var element = reference.element,
35352 property = reference.property;
35353
35354 if (key === descriptor.id) {
35355 element[ property ] = businessObject;
35356
35357 array.push(descriptor.id);
35358 }
35359
35360 return array;
35361 }, []));
35362 }
35363
35364 eventBus.on('copyPaste.pasteElements', function() {
35365 references = {};
35366 });
35367
35368 eventBus.on('copyPaste.pasteElement', function(context) {
35369 var cache = context.cache,
35370 descriptor = context.descriptor,
35371 oldBusinessObject = descriptor.oldBusinessObject,
35372 oldDi = descriptor.oldDi,
35373 newBusinessObject, newDi;
35374
35375 // do NOT copy business object if external label
35376 if (isLabel$3(descriptor)) {
35377 descriptor.businessObject = getBusinessObject(cache[ descriptor.labelTarget ]);
35378 descriptor.di = getDi(cache[ descriptor.labelTarget ]);
35379
35380 return;
35381 }
35382
35383 newBusinessObject = bpmnFactory.create(oldBusinessObject.$type);
35384
35385 descriptor.businessObject = moddleCopy.copyElement(
35386 oldBusinessObject,
35387 newBusinessObject
35388 );
35389
35390 newDi = bpmnFactory.create(oldDi.$type);
35391 newDi.bpmnElement = newBusinessObject;
35392
35393 descriptor.di = moddleCopy.copyElement(
35394 oldDi,
35395 newDi
35396 );
35397
35398 // resolve references e.g. default sequence flow
35399 resolveReferences(descriptor, cache);
35400
35401 copyProperties$1(descriptor, newBusinessObject, [
35402 'isExpanded',
35403 'name'
35404 ]);
35405
35406 removeProperties(descriptor, 'oldBusinessObject');
35407 });
35408
35409 }
35410
35411
35412 BpmnCopyPaste.$inject = [
35413 'bpmnFactory',
35414 'eventBus',
35415 'moddleCopy'
35416 ];
35417
35418 // helpers //////////
35419
35420 function isLabel$3(element) {
35421 return !!element.labelTarget;
35422 }
35423
35424 var DISALLOWED_PROPERTIES = [
35425 'artifacts',
35426 'dataInputAssociations',
35427 'dataOutputAssociations',
35428 'default',
35429 'flowElements',
35430 'lanes',
35431 'incoming',
35432 'outgoing'
35433 ];
35434
35435 /**
35436 * @typedef {Function} <moddleCopy.canCopyProperties> listener
35437 *
35438 * @param {Object} context
35439 * @param {Array<string>} context.propertyNames
35440 * @param {ModdleElement} context.sourceElement
35441 * @param {ModdleElement} context.targetElement
35442 *
35443 * @returns {Array<string>|boolean} - Return properties to be copied or false to disallow
35444 * copying.
35445 */
35446
35447 /**
35448 * @typedef {Function} <moddleCopy.canCopyProperty> listener
35449 *
35450 * @param {Object} context
35451 * @param {ModdleElement} context.parent
35452 * @param {*} context.property
35453 * @param {string} context.propertyName
35454 *
35455 * @returns {*|boolean} - Return copied property or false to disallow
35456 * copying.
35457 */
35458
35459 /**
35460 * @typedef {Function} <moddleCopy.canSetCopiedProperty> listener
35461 *
35462 * @param {Object} context
35463 * @param {ModdleElement} context.parent
35464 * @param {*} context.property
35465 * @param {string} context.propertyName
35466 *
35467 * @returns {boolean} - Return false to disallow
35468 * setting copied property.
35469 */
35470
35471 /**
35472 * Utility for copying model properties from source element to target element.
35473 *
35474 * @param {EventBus} eventBus
35475 * @param {BpmnFactory} bpmnFactory
35476 * @param {BpmnModdle} moddle
35477 */
35478 function ModdleCopy(eventBus, bpmnFactory, moddle) {
35479 this._bpmnFactory = bpmnFactory;
35480 this._eventBus = eventBus;
35481 this._moddle = moddle;
35482
35483 // copy extension elements last
35484 eventBus.on('moddleCopy.canCopyProperties', function(context) {
35485 var propertyNames = context.propertyNames;
35486
35487 if (!propertyNames || !propertyNames.length) {
35488 return;
35489 }
35490
35491 return sortBy(propertyNames, function(propertyName) {
35492 return propertyName === 'extensionElements';
35493 });
35494 });
35495
35496 // default check whether property can be copied
35497 eventBus.on('moddleCopy.canCopyProperty', function(context) {
35498 var parent = context.parent,
35499 parentDescriptor = isObject(parent) && parent.$descriptor,
35500 propertyName = context.propertyName;
35501
35502 if (propertyName && DISALLOWED_PROPERTIES.indexOf(propertyName) !== -1) {
35503
35504 // disallow copying property
35505 return false;
35506 }
35507
35508 if (propertyName &&
35509 parentDescriptor &&
35510 !find(parentDescriptor.properties, matchPattern({ name: propertyName }))) {
35511
35512 // disallow copying property
35513 return false;
35514 }
35515 });
35516
35517 // do NOT allow to copy empty extension elements
35518 eventBus.on('moddleCopy.canSetCopiedProperty', function(context) {
35519 var property = context.property;
35520
35521 if (is(property, 'bpmn:ExtensionElements') && (!property.values || !property.values.length)) {
35522
35523 // disallow setting copied property
35524 return false;
35525 }
35526 });
35527 }
35528
35529 ModdleCopy.$inject = [
35530 'eventBus',
35531 'bpmnFactory',
35532 'moddle'
35533 ];
35534
35535 /**
35536 * Copy model properties of source element to target element.
35537 *
35538 * @param {ModdleElement} sourceElement
35539 * @param {ModdleElement} targetElement
35540 * @param {Array<string>} [propertyNames]
35541 *
35542 * @param {ModdleElement}
35543 */
35544 ModdleCopy.prototype.copyElement = function(sourceElement, targetElement, propertyNames) {
35545 var self = this;
35546
35547 if (propertyNames && !isArray$4(propertyNames)) {
35548 propertyNames = [ propertyNames ];
35549 }
35550
35551 propertyNames = propertyNames || getPropertyNames(sourceElement.$descriptor);
35552
35553 var canCopyProperties = this._eventBus.fire('moddleCopy.canCopyProperties', {
35554 propertyNames: propertyNames,
35555 sourceElement: sourceElement,
35556 targetElement: targetElement
35557 });
35558
35559 if (canCopyProperties === false) {
35560 return targetElement;
35561 }
35562
35563 if (isArray$4(canCopyProperties)) {
35564 propertyNames = canCopyProperties;
35565 }
35566
35567 // copy properties
35568 forEach$2(propertyNames, function(propertyName) {
35569 var sourceProperty;
35570
35571 if (has$2(sourceElement, propertyName)) {
35572 sourceProperty = sourceElement.get(propertyName);
35573 }
35574
35575 var copiedProperty = self.copyProperty(sourceProperty, targetElement, propertyName);
35576
35577 var canSetProperty = self._eventBus.fire('moddleCopy.canSetCopiedProperty', {
35578 parent: targetElement,
35579 property: copiedProperty,
35580 propertyName: propertyName
35581 });
35582
35583 if (canSetProperty === false) {
35584 return;
35585 }
35586
35587 if (isDefined(copiedProperty)) {
35588 targetElement.set(propertyName, copiedProperty);
35589 }
35590 });
35591
35592 return targetElement;
35593 };
35594
35595 /**
35596 * Copy model property.
35597 *
35598 * @param {*} property
35599 * @param {ModdleElement} parent
35600 * @param {string} propertyName
35601 *
35602 * @returns {*}
35603 */
35604 ModdleCopy.prototype.copyProperty = function(property, parent, propertyName) {
35605 var self = this;
35606
35607 // allow others to copy property
35608 var copiedProperty = this._eventBus.fire('moddleCopy.canCopyProperty', {
35609 parent: parent,
35610 property: property,
35611 propertyName: propertyName
35612 });
35613
35614 // return if copying is NOT allowed
35615 if (copiedProperty === false) {
35616 return;
35617 }
35618
35619 if (copiedProperty) {
35620 if (isObject(copiedProperty) && copiedProperty.$type && !copiedProperty.$parent) {
35621 copiedProperty.$parent = parent;
35622 }
35623
35624 return copiedProperty;
35625 }
35626
35627 var propertyDescriptor = this._moddle.getPropertyDescriptor(parent, propertyName);
35628
35629 // do NOT copy references
35630 if (propertyDescriptor.isReference) {
35631 return;
35632 }
35633
35634 // copy id
35635 if (propertyDescriptor.isId) {
35636 return this._copyId(property, parent);
35637 }
35638
35639 // copy arrays
35640 if (isArray$4(property)) {
35641 return reduce(property, function(childProperties, childProperty) {
35642
35643 // recursion
35644 copiedProperty = self.copyProperty(childProperty, parent, propertyName);
35645
35646 // copying might NOT be allowed
35647 if (copiedProperty) {
35648 copiedProperty.$parent = parent;
35649
35650 return childProperties.concat(copiedProperty);
35651 }
35652
35653 return childProperties;
35654 }, []);
35655 }
35656
35657 // copy model elements
35658 if (isObject(property) && property.$type) {
35659 if (this._moddle.getElementDescriptor(property).isGeneric) {
35660 return;
35661 }
35662
35663 copiedProperty = self._bpmnFactory.create(property.$type);
35664
35665 copiedProperty.$parent = parent;
35666
35667 // recursion
35668 copiedProperty = self.copyElement(property, copiedProperty);
35669
35670 return copiedProperty;
35671 }
35672
35673 // copy primitive properties
35674 return property;
35675 };
35676
35677 ModdleCopy.prototype._copyId = function(id, element) {
35678
35679 // disallow if already taken
35680 if (this._moddle.ids.assigned(id)) {
35681 return;
35682 } else {
35683
35684 this._moddle.ids.claim(id, element);
35685 return id;
35686 }
35687 };
35688
35689 // helpers //////////
35690
35691 function getPropertyNames(descriptor, keepDefaultProperties) {
35692 return reduce(descriptor.properties, function(properties, property) {
35693
35694 if (keepDefaultProperties && property.default) {
35695 return properties;
35696 }
35697
35698 return properties.concat(property.name);
35699 }, []);
35700 }
35701
35702 function is(element, type) {
35703 return element && (typeof element.$instanceOf === 'function') && element.$instanceOf(type);
35704 }
35705
35706 var CopyPasteModule = {
35707 __depends__: [
35708 CopyPasteModule$1
35709 ],
35710 __init__: [ 'bpmnCopyPaste', 'moddleCopy' ],
35711 bpmnCopyPaste: [ 'type', BpmnCopyPaste ],
35712 moddleCopy: [ 'type', ModdleCopy ]
35713 };
35714
35715 var round$5 = Math.round;
35716
35717 /**
35718 * Service that allow replacing of elements.
35719 */
35720 function Replace(modeling) {
35721
35722 this._modeling = modeling;
35723 }
35724
35725 Replace.$inject = [ 'modeling' ];
35726
35727 /**
35728 * @param {Element} oldElement - Element to be replaced
35729 * @param {Object} newElementData - Containing information about the new element,
35730 * for example the new bounds and type.
35731 * @param {Object} options - Custom options that will be attached to the context. It can be used to inject data
35732 * that is needed in the command chain. For example it could be used in
35733 * eventbus.on('commandStack.shape.replace.postExecute') to change shape attributes after
35734 * shape creation.
35735 */
35736 Replace.prototype.replaceElement = function(oldElement, newElementData, options) {
35737
35738 if (oldElement.waypoints) {
35739
35740 // TODO(nikku): we do not replace connections, yet
35741 return null;
35742 }
35743
35744 var modeling = this._modeling;
35745
35746 var width = newElementData.width || oldElement.width,
35747 height = newElementData.height || oldElement.height,
35748 x = newElementData.x || oldElement.x,
35749 y = newElementData.y || oldElement.y,
35750 centerX = round$5(x + width / 2),
35751 centerY = round$5(y + height / 2);
35752
35753 // modeling API requires center coordinates,
35754 // account for that when handling shape bounds
35755
35756 return modeling.replaceShape(
35757 oldElement,
35758 assign(
35759 {},
35760 newElementData,
35761 {
35762 x: centerX,
35763 y: centerY,
35764 width: width,
35765 height: height
35766 }
35767 ),
35768 options
35769 );
35770 };
35771
35772 var ReplaceModule$1 = {
35773 __init__: [ 'replace' ],
35774 replace: [ 'type', Replace ]
35775 };
35776
35777 function copyProperties(source, target, properties) {
35778 if (!isArray$4(properties)) {
35779 properties = [ properties ];
35780 }
35781
35782 forEach$2(properties, function(property) {
35783 if (!isUndefined$3(source[property])) {
35784 target[property] = source[property];
35785 }
35786 });
35787 }
35788
35789
35790 var CUSTOM_PROPERTIES = [
35791 'cancelActivity',
35792 'instantiate',
35793 'eventGatewayType',
35794 'triggeredByEvent',
35795 'isInterrupting'
35796 ];
35797
35798 /**
35799 * Check if element should be collapsed or expanded.
35800 */
35801 function shouldToggleCollapsed(element, targetElement) {
35802
35803 var oldCollapsed = (
35804 element && has$2(element, 'collapsed') ? element.collapsed : !isExpanded(element)
35805 );
35806
35807 var targetCollapsed;
35808
35809 if (targetElement && (has$2(targetElement, 'collapsed') || has$2(targetElement, 'isExpanded'))) {
35810
35811 // property is explicitly set so use it
35812 targetCollapsed = (
35813 has$2(targetElement, 'collapsed') ? targetElement.collapsed : !targetElement.isExpanded
35814 );
35815 } else {
35816
35817 // keep old state
35818 targetCollapsed = oldCollapsed;
35819 }
35820
35821 if (oldCollapsed !== targetCollapsed) {
35822 return true;
35823 }
35824
35825 return false;
35826 }
35827
35828
35829 /**
35830 * This module takes care of replacing BPMN elements
35831 */
35832 function BpmnReplace(
35833 bpmnFactory,
35834 elementFactory,
35835 moddleCopy,
35836 modeling,
35837 replace,
35838 rules,
35839 selection
35840 ) {
35841
35842 /**
35843 * Prepares a new business object for the replacement element
35844 * and triggers the replace operation.
35845 *
35846 * @param {djs.model.Base} element
35847 * @param {Object} target
35848 * @param {Object} [hints]
35849 *
35850 * @return {djs.model.Base} the newly created element
35851 */
35852 function replaceElement(element, target, hints) {
35853
35854 hints = hints || {};
35855
35856 var type = target.type,
35857 oldBusinessObject = element.businessObject;
35858
35859 if (isSubProcess(oldBusinessObject) && type === 'bpmn:SubProcess') {
35860 if (shouldToggleCollapsed(element, target)) {
35861
35862 // expanding or collapsing process
35863 modeling.toggleCollapse(element);
35864
35865 return element;
35866 }
35867 }
35868
35869 var newBusinessObject = bpmnFactory.create(type);
35870
35871 var newElement = {
35872 type: type,
35873 businessObject: newBusinessObject,
35874 };
35875
35876 newElement.di = {};
35877
35878 // colors will be set to DI
35879 copyProperties(element.di, newElement.di, [
35880 'fill',
35881 'stroke',
35882 'background-color',
35883 'border-color',
35884 'color'
35885 ]);
35886
35887 var elementProps = getPropertyNames(oldBusinessObject.$descriptor),
35888 newElementProps = getPropertyNames(newBusinessObject.$descriptor, true),
35889 copyProps = intersection(elementProps, newElementProps);
35890
35891 // initialize special properties defined in target definition
35892 assign(newBusinessObject, pick(target, CUSTOM_PROPERTIES));
35893
35894 var properties = filter(copyProps, function(propertyName) {
35895
35896 // copying event definitions, unless we replace
35897 if (propertyName === 'eventDefinitions') {
35898 return hasEventDefinition$1(element, target.eventDefinitionType);
35899 }
35900
35901 // retain loop characteristics if the target element
35902 // is not an event sub process
35903 if (propertyName === 'loopCharacteristics') {
35904 return !isEventSubProcess(newBusinessObject);
35905 }
35906
35907 // so the applied properties from 'target' don't get lost
35908 if (has$2(newBusinessObject, propertyName)) {
35909 return false;
35910 }
35911
35912 if (propertyName === 'processRef' && target.isExpanded === false) {
35913 return false;
35914 }
35915
35916 if (propertyName === 'triggeredByEvent') {
35917 return false;
35918 }
35919
35920 return true;
35921 });
35922
35923 newBusinessObject = moddleCopy.copyElement(
35924 oldBusinessObject,
35925 newBusinessObject,
35926 properties
35927 );
35928
35929 // initialize custom BPMN extensions
35930 if (target.eventDefinitionType) {
35931
35932 // only initialize with new eventDefinition
35933 // if we did not set an event definition yet,
35934 // i.e. because we copied it
35935 if (!hasEventDefinition$1(newBusinessObject, target.eventDefinitionType)) {
35936 newElement.eventDefinitionType = target.eventDefinitionType;
35937 newElement.eventDefinitionAttrs = target.eventDefinitionAttrs;
35938 }
35939 }
35940
35941 if (is$1(oldBusinessObject, 'bpmn:Activity')) {
35942
35943 if (isSubProcess(oldBusinessObject)) {
35944
35945 // no toggeling, so keep old state
35946 newElement.isExpanded = isExpanded(element);
35947 }
35948
35949 // else if property is explicitly set, use it
35950 else if (target && has$2(target, 'isExpanded')) {
35951 newElement.isExpanded = target.isExpanded;
35952 }
35953
35954 // TODO: need also to respect min/max Size
35955 // copy size, from an expanded subprocess to an expanded alternative subprocess
35956 // except bpmn:Task, because Task is always expanded
35957 if ((isExpanded(element) && !is$1(oldBusinessObject, 'bpmn:Task')) && newElement.isExpanded) {
35958 newElement.width = element.width;
35959 newElement.height = element.height;
35960 }
35961 }
35962
35963 // remove children if not expanding sub process
35964 if (isSubProcess(oldBusinessObject) && !isSubProcess(newBusinessObject)) {
35965 hints.moveChildren = false;
35966 }
35967
35968 // transform collapsed/expanded pools
35969 if (is$1(oldBusinessObject, 'bpmn:Participant')) {
35970
35971 // create expanded pool
35972 if (target.isExpanded === true) {
35973 newBusinessObject.processRef = bpmnFactory.create('bpmn:Process');
35974 } else {
35975
35976 // remove children when transforming to collapsed pool
35977 hints.moveChildren = false;
35978 }
35979
35980 // apply same width and default height
35981 newElement.width = element.width;
35982 newElement.height = elementFactory.getDefaultSize(newElement).height;
35983 }
35984
35985 if (!rules.allowed('shape.resize', { shape: newBusinessObject })) {
35986 newElement.height = elementFactory.getDefaultSize(newElement).height;
35987 newElement.width = elementFactory.getDefaultSize(newElement).width;
35988 }
35989
35990 newBusinessObject.name = oldBusinessObject.name;
35991
35992 // retain default flow's reference between inclusive <-> exclusive gateways and activities
35993 if (
35994 isAny(oldBusinessObject, [
35995 'bpmn:ExclusiveGateway',
35996 'bpmn:InclusiveGateway',
35997 'bpmn:Activity'
35998 ]) &&
35999 isAny(newBusinessObject, [
36000 'bpmn:ExclusiveGateway',
36001 'bpmn:InclusiveGateway',
36002 'bpmn:Activity'
36003 ])
36004 ) {
36005 newBusinessObject.default = oldBusinessObject.default;
36006 }
36007
36008 if (
36009 target.host &&
36010 !is$1(oldBusinessObject, 'bpmn:BoundaryEvent') &&
36011 is$1(newBusinessObject, 'bpmn:BoundaryEvent')
36012 ) {
36013 newElement.host = target.host;
36014 }
36015
36016 // The DataStoreReference element is 14px wider than the DataObjectReference element
36017 // This ensures that they stay centered on the x axis when replaced
36018 if (
36019 newElement.type === 'bpmn:DataStoreReference' ||
36020 newElement.type === 'bpmn:DataObjectReference'
36021 ) {
36022 newElement.x = element.x + (element.width - newElement.width) / 2;
36023 }
36024
36025
36026 newElement = replace.replaceElement(element, newElement, hints);
36027
36028 if (hints.select !== false) {
36029 selection.select(newElement);
36030 }
36031
36032 return newElement;
36033 }
36034
36035 this.replaceElement = replaceElement;
36036 }
36037
36038 BpmnReplace.$inject = [
36039 'bpmnFactory',
36040 'elementFactory',
36041 'moddleCopy',
36042 'modeling',
36043 'replace',
36044 'rules',
36045 'selection'
36046 ];
36047
36048
36049 function isSubProcess(bo) {
36050 return is$1(bo, 'bpmn:SubProcess');
36051 }
36052
36053 function hasEventDefinition$1(element, type) {
36054
36055 var bo = getBusinessObject(element);
36056
36057 return type && bo.get('eventDefinitions').some(function(definition) {
36058 return is$1(definition, type);
36059 });
36060 }
36061
36062 /**
36063 * Compute intersection between two arrays.
36064 */
36065 function intersection(a1, a2) {
36066 return a1.filter(function(el) {
36067 return a2.indexOf(el) !== -1;
36068 });
36069 }
36070
36071 var ReplaceModule = {
36072 __depends__: [
36073 CopyPasteModule,
36074 ReplaceModule$1,
36075 SelectionModule
36076 ],
36077 bpmnReplace: [ 'type', BpmnReplace ]
36078 };
36079
36080 /**
36081 * Returns true, if an element is from a different type
36082 * than a target definition. Takes into account the type,
36083 * event definition type and triggeredByEvent property.
36084 *
36085 * @param {djs.model.Base} element
36086 *
36087 * @return {boolean}
36088 */
36089 function isDifferentType(element) {
36090
36091 return function(entry) {
36092 var target = entry.target;
36093
36094 var businessObject = getBusinessObject(element),
36095 eventDefinition = businessObject.eventDefinitions && businessObject.eventDefinitions[0];
36096
36097 var isTypeEqual = businessObject.$type === target.type;
36098
36099 var isEventDefinitionEqual = (
36100 (eventDefinition && eventDefinition.$type) === target.eventDefinitionType
36101 );
36102
36103 var isTriggeredByEventEqual = (
36104 businessObject.triggeredByEvent === target.triggeredByEvent
36105 );
36106
36107 var isExpandedEqual = (
36108 target.isExpanded === undefined ||
36109 target.isExpanded === isExpanded(element)
36110 );
36111
36112 return !isTypeEqual || !isEventDefinitionEqual || !isTriggeredByEventEqual || !isExpandedEqual;
36113 };
36114 }
36115
36116 var START_EVENT = [
36117 {
36118 label: 'Start Event',
36119 actionName: 'replace-with-none-start',
36120 className: 'bpmn-icon-start-event-none',
36121 target: {
36122 type: 'bpmn:StartEvent'
36123 }
36124 },
36125 {
36126 label: 'Intermediate Throw Event',
36127 actionName: 'replace-with-none-intermediate-throwing',
36128 className: 'bpmn-icon-intermediate-event-none',
36129 target: {
36130 type: 'bpmn:IntermediateThrowEvent'
36131 }
36132 },
36133 {
36134 label: 'End Event',
36135 actionName: 'replace-with-none-end',
36136 className: 'bpmn-icon-end-event-none',
36137 target: {
36138 type: 'bpmn:EndEvent'
36139 }
36140 },
36141 {
36142 label: 'Message Start Event',
36143 actionName: 'replace-with-message-start',
36144 className: 'bpmn-icon-start-event-message',
36145 target: {
36146 type: 'bpmn:StartEvent',
36147 eventDefinitionType: 'bpmn:MessageEventDefinition'
36148 }
36149 },
36150 {
36151 label: 'Timer Start Event',
36152 actionName: 'replace-with-timer-start',
36153 className: 'bpmn-icon-start-event-timer',
36154 target: {
36155 type: 'bpmn:StartEvent',
36156 eventDefinitionType: 'bpmn:TimerEventDefinition'
36157 }
36158 },
36159 {
36160 label: 'Conditional Start Event',
36161 actionName: 'replace-with-conditional-start',
36162 className: 'bpmn-icon-start-event-condition',
36163 target: {
36164 type: 'bpmn:StartEvent',
36165 eventDefinitionType: 'bpmn:ConditionalEventDefinition'
36166 }
36167 },
36168 {
36169 label: 'Signal Start Event',
36170 actionName: 'replace-with-signal-start',
36171 className: 'bpmn-icon-start-event-signal',
36172 target: {
36173 type: 'bpmn:StartEvent',
36174 eventDefinitionType: 'bpmn:SignalEventDefinition'
36175 }
36176 }
36177 ];
36178
36179 var START_EVENT_SUB_PROCESS = [
36180 {
36181 label: 'Start Event',
36182 actionName: 'replace-with-none-start',
36183 className: 'bpmn-icon-start-event-none',
36184 target: {
36185 type: 'bpmn:StartEvent'
36186 }
36187 },
36188 {
36189 label: 'Intermediate Throw Event',
36190 actionName: 'replace-with-none-intermediate-throwing',
36191 className: 'bpmn-icon-intermediate-event-none',
36192 target: {
36193 type: 'bpmn:IntermediateThrowEvent'
36194 }
36195 },
36196 {
36197 label: 'End Event',
36198 actionName: 'replace-with-none-end',
36199 className: 'bpmn-icon-end-event-none',
36200 target: {
36201 type: 'bpmn:EndEvent'
36202 }
36203 }
36204 ];
36205
36206 var INTERMEDIATE_EVENT = [
36207 {
36208 label: 'Start Event',
36209 actionName: 'replace-with-none-start',
36210 className: 'bpmn-icon-start-event-none',
36211 target: {
36212 type: 'bpmn:StartEvent'
36213 }
36214 },
36215 {
36216 label: 'Intermediate Throw Event',
36217 actionName: 'replace-with-none-intermediate-throw',
36218 className: 'bpmn-icon-intermediate-event-none',
36219 target: {
36220 type: 'bpmn:IntermediateThrowEvent'
36221 }
36222 },
36223 {
36224 label: 'End Event',
36225 actionName: 'replace-with-none-end',
36226 className: 'bpmn-icon-end-event-none',
36227 target: {
36228 type: 'bpmn:EndEvent'
36229 }
36230 },
36231 {
36232 label: 'Message Intermediate Catch Event',
36233 actionName: 'replace-with-message-intermediate-catch',
36234 className: 'bpmn-icon-intermediate-event-catch-message',
36235 target: {
36236 type: 'bpmn:IntermediateCatchEvent',
36237 eventDefinitionType: 'bpmn:MessageEventDefinition'
36238 }
36239 },
36240 {
36241 label: 'Message Intermediate Throw Event',
36242 actionName: 'replace-with-message-intermediate-throw',
36243 className: 'bpmn-icon-intermediate-event-throw-message',
36244 target: {
36245 type: 'bpmn:IntermediateThrowEvent',
36246 eventDefinitionType: 'bpmn:MessageEventDefinition'
36247 }
36248 },
36249 {
36250 label: 'Timer Intermediate Catch Event',
36251 actionName: 'replace-with-timer-intermediate-catch',
36252 className: 'bpmn-icon-intermediate-event-catch-timer',
36253 target: {
36254 type: 'bpmn:IntermediateCatchEvent',
36255 eventDefinitionType: 'bpmn:TimerEventDefinition'
36256 }
36257 },
36258 {
36259 label: 'Escalation Intermediate Throw Event',
36260 actionName: 'replace-with-escalation-intermediate-throw',
36261 className: 'bpmn-icon-intermediate-event-throw-escalation',
36262 target: {
36263 type: 'bpmn:IntermediateThrowEvent',
36264 eventDefinitionType: 'bpmn:EscalationEventDefinition'
36265 }
36266 },
36267 {
36268 label: 'Conditional Intermediate Catch Event',
36269 actionName: 'replace-with-conditional-intermediate-catch',
36270 className: 'bpmn-icon-intermediate-event-catch-condition',
36271 target: {
36272 type: 'bpmn:IntermediateCatchEvent',
36273 eventDefinitionType: 'bpmn:ConditionalEventDefinition'
36274 }
36275 },
36276 {
36277 label: 'Link Intermediate Catch Event',
36278 actionName: 'replace-with-link-intermediate-catch',
36279 className: 'bpmn-icon-intermediate-event-catch-link',
36280 target: {
36281 type: 'bpmn:IntermediateCatchEvent',
36282 eventDefinitionType: 'bpmn:LinkEventDefinition',
36283 eventDefinitionAttrs: {
36284 name: ''
36285 }
36286 }
36287 },
36288 {
36289 label: 'Link Intermediate Throw Event',
36290 actionName: 'replace-with-link-intermediate-throw',
36291 className: 'bpmn-icon-intermediate-event-throw-link',
36292 target: {
36293 type: 'bpmn:IntermediateThrowEvent',
36294 eventDefinitionType: 'bpmn:LinkEventDefinition',
36295 eventDefinitionAttrs: {
36296 name: ''
36297 }
36298 }
36299 },
36300 {
36301 label: 'Compensation Intermediate Throw Event',
36302 actionName: 'replace-with-compensation-intermediate-throw',
36303 className: 'bpmn-icon-intermediate-event-throw-compensation',
36304 target: {
36305 type: 'bpmn:IntermediateThrowEvent',
36306 eventDefinitionType: 'bpmn:CompensateEventDefinition'
36307 }
36308 },
36309 {
36310 label: 'Signal Intermediate Catch Event',
36311 actionName: 'replace-with-signal-intermediate-catch',
36312 className: 'bpmn-icon-intermediate-event-catch-signal',
36313 target: {
36314 type: 'bpmn:IntermediateCatchEvent',
36315 eventDefinitionType: 'bpmn:SignalEventDefinition'
36316 }
36317 },
36318 {
36319 label: 'Signal Intermediate Throw Event',
36320 actionName: 'replace-with-signal-intermediate-throw',
36321 className: 'bpmn-icon-intermediate-event-throw-signal',
36322 target: {
36323 type: 'bpmn:IntermediateThrowEvent',
36324 eventDefinitionType: 'bpmn:SignalEventDefinition'
36325 }
36326 }
36327 ];
36328
36329 var END_EVENT = [
36330 {
36331 label: 'Start Event',
36332 actionName: 'replace-with-none-start',
36333 className: 'bpmn-icon-start-event-none',
36334 target: {
36335 type: 'bpmn:StartEvent'
36336 }
36337 },
36338 {
36339 label: 'Intermediate Throw Event',
36340 actionName: 'replace-with-none-intermediate-throw',
36341 className: 'bpmn-icon-intermediate-event-none',
36342 target: {
36343 type: 'bpmn:IntermediateThrowEvent'
36344 }
36345 },
36346 {
36347 label: 'End Event',
36348 actionName: 'replace-with-none-end',
36349 className: 'bpmn-icon-end-event-none',
36350 target: {
36351 type: 'bpmn:EndEvent'
36352 }
36353 },
36354 {
36355 label: 'Message End Event',
36356 actionName: 'replace-with-message-end',
36357 className: 'bpmn-icon-end-event-message',
36358 target: {
36359 type: 'bpmn:EndEvent',
36360 eventDefinitionType: 'bpmn:MessageEventDefinition'
36361 }
36362 },
36363 {
36364 label: 'Escalation End Event',
36365 actionName: 'replace-with-escalation-end',
36366 className: 'bpmn-icon-end-event-escalation',
36367 target: {
36368 type: 'bpmn:EndEvent',
36369 eventDefinitionType: 'bpmn:EscalationEventDefinition'
36370 }
36371 },
36372 {
36373 label: 'Error End Event',
36374 actionName: 'replace-with-error-end',
36375 className: 'bpmn-icon-end-event-error',
36376 target: {
36377 type: 'bpmn:EndEvent',
36378 eventDefinitionType: 'bpmn:ErrorEventDefinition'
36379 }
36380 },
36381 {
36382 label: 'Cancel End Event',
36383 actionName: 'replace-with-cancel-end',
36384 className: 'bpmn-icon-end-event-cancel',
36385 target: {
36386 type: 'bpmn:EndEvent',
36387 eventDefinitionType: 'bpmn:CancelEventDefinition'
36388 }
36389 },
36390 {
36391 label: 'Compensation End Event',
36392 actionName: 'replace-with-compensation-end',
36393 className: 'bpmn-icon-end-event-compensation',
36394 target: {
36395 type: 'bpmn:EndEvent',
36396 eventDefinitionType: 'bpmn:CompensateEventDefinition'
36397 }
36398 },
36399 {
36400 label: 'Signal End Event',
36401 actionName: 'replace-with-signal-end',
36402 className: 'bpmn-icon-end-event-signal',
36403 target: {
36404 type: 'bpmn:EndEvent',
36405 eventDefinitionType: 'bpmn:SignalEventDefinition'
36406 }
36407 },
36408 {
36409 label: 'Terminate End Event',
36410 actionName: 'replace-with-terminate-end',
36411 className: 'bpmn-icon-end-event-terminate',
36412 target: {
36413 type: 'bpmn:EndEvent',
36414 eventDefinitionType: 'bpmn:TerminateEventDefinition'
36415 }
36416 }
36417 ];
36418
36419 var GATEWAY = [
36420 {
36421 label: 'Exclusive Gateway',
36422 actionName: 'replace-with-exclusive-gateway',
36423 className: 'bpmn-icon-gateway-xor',
36424 target: {
36425 type: 'bpmn:ExclusiveGateway'
36426 }
36427 },
36428 {
36429 label: 'Parallel Gateway',
36430 actionName: 'replace-with-parallel-gateway',
36431 className: 'bpmn-icon-gateway-parallel',
36432 target: {
36433 type: 'bpmn:ParallelGateway'
36434 }
36435 },
36436 {
36437 label: 'Inclusive Gateway',
36438 actionName: 'replace-with-inclusive-gateway',
36439 className: 'bpmn-icon-gateway-or',
36440 target: {
36441 type: 'bpmn:InclusiveGateway'
36442 }
36443 },
36444 {
36445 label: 'Complex Gateway',
36446 actionName: 'replace-with-complex-gateway',
36447 className: 'bpmn-icon-gateway-complex',
36448 target: {
36449 type: 'bpmn:ComplexGateway'
36450 }
36451 },
36452 {
36453 label: 'Event based Gateway',
36454 actionName: 'replace-with-event-based-gateway',
36455 className: 'bpmn-icon-gateway-eventbased',
36456 target: {
36457 type: 'bpmn:EventBasedGateway',
36458 instantiate: false,
36459 eventGatewayType: 'Exclusive'
36460 }
36461 }
36462
36463 // Gateways deactivated until https://github.com/bpmn-io/bpmn-js/issues/194
36464 // {
36465 // label: 'Event based instantiating Gateway',
36466 // actionName: 'replace-with-exclusive-event-based-gateway',
36467 // className: 'bpmn-icon-exclusive-event-based',
36468 // target: {
36469 // type: 'bpmn:EventBasedGateway'
36470 // },
36471 // options: {
36472 // businessObject: { instantiate: true, eventGatewayType: 'Exclusive' }
36473 // }
36474 // },
36475 // {
36476 // label: 'Parallel Event based instantiating Gateway',
36477 // actionName: 'replace-with-parallel-event-based-instantiate-gateway',
36478 // className: 'bpmn-icon-parallel-event-based-instantiate-gateway',
36479 // target: {
36480 // type: 'bpmn:EventBasedGateway'
36481 // },
36482 // options: {
36483 // businessObject: { instantiate: true, eventGatewayType: 'Parallel' }
36484 // }
36485 // }
36486 ];
36487
36488 var SUBPROCESS_EXPANDED = [
36489 {
36490 label: 'Transaction',
36491 actionName: 'replace-with-transaction',
36492 className: 'bpmn-icon-transaction',
36493 target: {
36494 type: 'bpmn:Transaction',
36495 isExpanded: true
36496 }
36497 },
36498 {
36499 label: 'Event Sub Process',
36500 actionName: 'replace-with-event-subprocess',
36501 className: 'bpmn-icon-event-subprocess-expanded',
36502 target: {
36503 type: 'bpmn:SubProcess',
36504 triggeredByEvent: true,
36505 isExpanded: true
36506 }
36507 },
36508 {
36509 label: 'Sub Process (collapsed)',
36510 actionName: 'replace-with-collapsed-subprocess',
36511 className: 'bpmn-icon-subprocess-collapsed',
36512 target: {
36513 type: 'bpmn:SubProcess',
36514 isExpanded: false
36515 }
36516 }
36517 ];
36518
36519 var TRANSACTION = [
36520 {
36521 label: 'Sub Process',
36522 actionName: 'replace-with-subprocess',
36523 className: 'bpmn-icon-subprocess-expanded',
36524 target: {
36525 type: 'bpmn:SubProcess',
36526 isExpanded: true
36527 }
36528 },
36529 {
36530 label: 'Event Sub Process',
36531 actionName: 'replace-with-event-subprocess',
36532 className: 'bpmn-icon-event-subprocess-expanded',
36533 target: {
36534 type: 'bpmn:SubProcess',
36535 triggeredByEvent: true,
36536 isExpanded: true
36537 }
36538 }
36539 ];
36540
36541 var EVENT_SUB_PROCESS = [
36542 {
36543 label: 'Sub Process',
36544 actionName: 'replace-with-subprocess',
36545 className: 'bpmn-icon-subprocess-expanded',
36546 target: {
36547 type: 'bpmn:SubProcess',
36548 isExpanded: true
36549 }
36550 },
36551 {
36552 label: 'Transaction',
36553 actionName: 'replace-with-transaction',
36554 className: 'bpmn-icon-transaction',
36555 target: {
36556 type: 'bpmn:Transaction',
36557 isExpanded: true
36558 }
36559 }
36560 ];
36561
36562 var TASK = [
36563 {
36564 label: 'Task',
36565 actionName: 'replace-with-task',
36566 className: 'bpmn-icon-task',
36567 target: {
36568 type: 'bpmn:Task'
36569 }
36570 },
36571 {
36572 label: 'Send Task',
36573 actionName: 'replace-with-send-task',
36574 className: 'bpmn-icon-send',
36575 target: {
36576 type: 'bpmn:SendTask'
36577 }
36578 },
36579 {
36580 label: 'Receive Task',
36581 actionName: 'replace-with-receive-task',
36582 className: 'bpmn-icon-receive',
36583 target: {
36584 type: 'bpmn:ReceiveTask'
36585 }
36586 },
36587 {
36588 label: 'User Task',
36589 actionName: 'replace-with-user-task',
36590 className: 'bpmn-icon-user',
36591 target: {
36592 type: 'bpmn:UserTask'
36593 }
36594 },
36595 {
36596 label: 'Manual Task',
36597 actionName: 'replace-with-manual-task',
36598 className: 'bpmn-icon-manual',
36599 target: {
36600 type: 'bpmn:ManualTask'
36601 }
36602 },
36603 {
36604 label: 'Business Rule Task',
36605 actionName: 'replace-with-rule-task',
36606 className: 'bpmn-icon-business-rule',
36607 target: {
36608 type: 'bpmn:BusinessRuleTask'
36609 }
36610 },
36611 {
36612 label: 'Service Task',
36613 actionName: 'replace-with-service-task',
36614 className: 'bpmn-icon-service',
36615 target: {
36616 type: 'bpmn:ServiceTask'
36617 }
36618 },
36619 {
36620 label: 'Script Task',
36621 actionName: 'replace-with-script-task',
36622 className: 'bpmn-icon-script',
36623 target: {
36624 type: 'bpmn:ScriptTask'
36625 }
36626 },
36627 {
36628 label: 'Call Activity',
36629 actionName: 'replace-with-call-activity',
36630 className: 'bpmn-icon-call-activity',
36631 target: {
36632 type: 'bpmn:CallActivity'
36633 }
36634 },
36635 {
36636 label: 'Sub Process (collapsed)',
36637 actionName: 'replace-with-collapsed-subprocess',
36638 className: 'bpmn-icon-subprocess-collapsed',
36639 target: {
36640 type: 'bpmn:SubProcess',
36641 isExpanded: false
36642 }
36643 },
36644 {
36645 label: 'Sub Process (expanded)',
36646 actionName: 'replace-with-expanded-subprocess',
36647 className: 'bpmn-icon-subprocess-expanded',
36648 target: {
36649 type: 'bpmn:SubProcess',
36650 isExpanded: true
36651 }
36652 }
36653 ];
36654
36655 var DATA_OBJECT_REFERENCE = [
36656 {
36657 label: 'Data Store Reference',
36658 actionName: 'replace-with-data-store-reference',
36659 className: 'bpmn-icon-data-store',
36660 target: {
36661 type: 'bpmn:DataStoreReference'
36662 }
36663 }
36664 ];
36665
36666 var DATA_STORE_REFERENCE = [
36667 {
36668 label: 'Data Object Reference',
36669 actionName: 'replace-with-data-object-reference',
36670 className: 'bpmn-icon-data-object',
36671 target: {
36672 type: 'bpmn:DataObjectReference'
36673 }
36674 }
36675 ];
36676
36677 var BOUNDARY_EVENT = [
36678 {
36679 label: 'Message Boundary Event',
36680 actionName: 'replace-with-message-boundary',
36681 className: 'bpmn-icon-intermediate-event-catch-message',
36682 target: {
36683 type: 'bpmn:BoundaryEvent',
36684 eventDefinitionType: 'bpmn:MessageEventDefinition'
36685 }
36686 },
36687 {
36688 label: 'Timer Boundary Event',
36689 actionName: 'replace-with-timer-boundary',
36690 className: 'bpmn-icon-intermediate-event-catch-timer',
36691 target: {
36692 type: 'bpmn:BoundaryEvent',
36693 eventDefinitionType: 'bpmn:TimerEventDefinition'
36694 }
36695 },
36696 {
36697 label: 'Escalation Boundary Event',
36698 actionName: 'replace-with-escalation-boundary',
36699 className: 'bpmn-icon-intermediate-event-catch-escalation',
36700 target: {
36701 type: 'bpmn:BoundaryEvent',
36702 eventDefinitionType: 'bpmn:EscalationEventDefinition'
36703 }
36704 },
36705 {
36706 label: 'Conditional Boundary Event',
36707 actionName: 'replace-with-conditional-boundary',
36708 className: 'bpmn-icon-intermediate-event-catch-condition',
36709 target: {
36710 type: 'bpmn:BoundaryEvent',
36711 eventDefinitionType: 'bpmn:ConditionalEventDefinition'
36712 }
36713 },
36714 {
36715 label: 'Error Boundary Event',
36716 actionName: 'replace-with-error-boundary',
36717 className: 'bpmn-icon-intermediate-event-catch-error',
36718 target: {
36719 type: 'bpmn:BoundaryEvent',
36720 eventDefinitionType: 'bpmn:ErrorEventDefinition'
36721 }
36722 },
36723 {
36724 label: 'Cancel Boundary Event',
36725 actionName: 'replace-with-cancel-boundary',
36726 className: 'bpmn-icon-intermediate-event-catch-cancel',
36727 target: {
36728 type: 'bpmn:BoundaryEvent',
36729 eventDefinitionType: 'bpmn:CancelEventDefinition'
36730 }
36731 },
36732 {
36733 label: 'Signal Boundary Event',
36734 actionName: 'replace-with-signal-boundary',
36735 className: 'bpmn-icon-intermediate-event-catch-signal',
36736 target: {
36737 type: 'bpmn:BoundaryEvent',
36738 eventDefinitionType: 'bpmn:SignalEventDefinition'
36739 }
36740 },
36741 {
36742 label: 'Compensation Boundary Event',
36743 actionName: 'replace-with-compensation-boundary',
36744 className: 'bpmn-icon-intermediate-event-catch-compensation',
36745 target: {
36746 type: 'bpmn:BoundaryEvent',
36747 eventDefinitionType: 'bpmn:CompensateEventDefinition'
36748 }
36749 },
36750 {
36751 label: 'Message Boundary Event (non-interrupting)',
36752 actionName: 'replace-with-non-interrupting-message-boundary',
36753 className: 'bpmn-icon-intermediate-event-catch-non-interrupting-message',
36754 target: {
36755 type: 'bpmn:BoundaryEvent',
36756 eventDefinitionType: 'bpmn:MessageEventDefinition',
36757 cancelActivity: false
36758 }
36759 },
36760 {
36761 label: 'Timer Boundary Event (non-interrupting)',
36762 actionName: 'replace-with-non-interrupting-timer-boundary',
36763 className: 'bpmn-icon-intermediate-event-catch-non-interrupting-timer',
36764 target: {
36765 type: 'bpmn:BoundaryEvent',
36766 eventDefinitionType: 'bpmn:TimerEventDefinition',
36767 cancelActivity: false
36768 }
36769 },
36770 {
36771 label: 'Escalation Boundary Event (non-interrupting)',
36772 actionName: 'replace-with-non-interrupting-escalation-boundary',
36773 className: 'bpmn-icon-intermediate-event-catch-non-interrupting-escalation',
36774 target: {
36775 type: 'bpmn:BoundaryEvent',
36776 eventDefinitionType: 'bpmn:EscalationEventDefinition',
36777 cancelActivity: false
36778 }
36779 },
36780 {
36781 label: 'Conditional Boundary Event (non-interrupting)',
36782 actionName: 'replace-with-non-interrupting-conditional-boundary',
36783 className: 'bpmn-icon-intermediate-event-catch-non-interrupting-condition',
36784 target: {
36785 type: 'bpmn:BoundaryEvent',
36786 eventDefinitionType: 'bpmn:ConditionalEventDefinition',
36787 cancelActivity: false
36788 }
36789 },
36790 {
36791 label: 'Signal Boundary Event (non-interrupting)',
36792 actionName: 'replace-with-non-interrupting-signal-boundary',
36793 className: 'bpmn-icon-intermediate-event-catch-non-interrupting-signal',
36794 target: {
36795 type: 'bpmn:BoundaryEvent',
36796 eventDefinitionType: 'bpmn:SignalEventDefinition',
36797 cancelActivity: false
36798 }
36799 }
36800 ];
36801
36802 var EVENT_SUB_PROCESS_START_EVENT = [
36803 {
36804 label: 'Message Start Event',
36805 actionName: 'replace-with-message-start',
36806 className: 'bpmn-icon-start-event-message',
36807 target: {
36808 type: 'bpmn:StartEvent',
36809 eventDefinitionType: 'bpmn:MessageEventDefinition'
36810 }
36811 },
36812 {
36813 label: 'Timer Start Event',
36814 actionName: 'replace-with-timer-start',
36815 className: 'bpmn-icon-start-event-timer',
36816 target: {
36817 type: 'bpmn:StartEvent',
36818 eventDefinitionType: 'bpmn:TimerEventDefinition'
36819 }
36820 },
36821 {
36822 label: 'Conditional Start Event',
36823 actionName: 'replace-with-conditional-start',
36824 className: 'bpmn-icon-start-event-condition',
36825 target: {
36826 type: 'bpmn:StartEvent',
36827 eventDefinitionType: 'bpmn:ConditionalEventDefinition'
36828 }
36829 },
36830 {
36831 label: 'Signal Start Event',
36832 actionName: 'replace-with-signal-start',
36833 className: 'bpmn-icon-start-event-signal',
36834 target: {
36835 type: 'bpmn:StartEvent',
36836 eventDefinitionType: 'bpmn:SignalEventDefinition'
36837 }
36838 },
36839 {
36840 label: 'Error Start Event',
36841 actionName: 'replace-with-error-start',
36842 className: 'bpmn-icon-start-event-error',
36843 target: {
36844 type: 'bpmn:StartEvent',
36845 eventDefinitionType: 'bpmn:ErrorEventDefinition'
36846 }
36847 },
36848 {
36849 label: 'Escalation Start Event',
36850 actionName: 'replace-with-escalation-start',
36851 className: 'bpmn-icon-start-event-escalation',
36852 target: {
36853 type: 'bpmn:StartEvent',
36854 eventDefinitionType: 'bpmn:EscalationEventDefinition'
36855 }
36856 },
36857 {
36858 label: 'Compensation Start Event',
36859 actionName: 'replace-with-compensation-start',
36860 className: 'bpmn-icon-start-event-compensation',
36861 target: {
36862 type: 'bpmn:StartEvent',
36863 eventDefinitionType: 'bpmn:CompensateEventDefinition'
36864 }
36865 },
36866 {
36867 label: 'Message Start Event (non-interrupting)',
36868 actionName: 'replace-with-non-interrupting-message-start',
36869 className: 'bpmn-icon-start-event-non-interrupting-message',
36870 target: {
36871 type: 'bpmn:StartEvent',
36872 eventDefinitionType: 'bpmn:MessageEventDefinition',
36873 isInterrupting: false
36874 }
36875 },
36876 {
36877 label: 'Timer Start Event (non-interrupting)',
36878 actionName: 'replace-with-non-interrupting-timer-start',
36879 className: 'bpmn-icon-start-event-non-interrupting-timer',
36880 target: {
36881 type: 'bpmn:StartEvent',
36882 eventDefinitionType: 'bpmn:TimerEventDefinition',
36883 isInterrupting: false
36884 }
36885 },
36886 {
36887 label: 'Conditional Start Event (non-interrupting)',
36888 actionName: 'replace-with-non-interrupting-conditional-start',
36889 className: 'bpmn-icon-start-event-non-interrupting-condition',
36890 target: {
36891 type: 'bpmn:StartEvent',
36892 eventDefinitionType: 'bpmn:ConditionalEventDefinition',
36893 isInterrupting: false
36894 }
36895 },
36896 {
36897 label: 'Signal Start Event (non-interrupting)',
36898 actionName: 'replace-with-non-interrupting-signal-start',
36899 className: 'bpmn-icon-start-event-non-interrupting-signal',
36900 target: {
36901 type: 'bpmn:StartEvent',
36902 eventDefinitionType: 'bpmn:SignalEventDefinition',
36903 isInterrupting: false
36904 }
36905 },
36906 {
36907 label: 'Escalation Start Event (non-interrupting)',
36908 actionName: 'replace-with-non-interrupting-escalation-start',
36909 className: 'bpmn-icon-start-event-non-interrupting-escalation',
36910 target: {
36911 type: 'bpmn:StartEvent',
36912 eventDefinitionType: 'bpmn:EscalationEventDefinition',
36913 isInterrupting: false
36914 }
36915 }
36916 ];
36917
36918 var SEQUENCE_FLOW = [
36919 {
36920 label: 'Sequence Flow',
36921 actionName: 'replace-with-sequence-flow',
36922 className: 'bpmn-icon-connection'
36923 },
36924 {
36925 label: 'Default Flow',
36926 actionName: 'replace-with-default-flow',
36927 className: 'bpmn-icon-default-flow'
36928 },
36929 {
36930 label: 'Conditional Flow',
36931 actionName: 'replace-with-conditional-flow',
36932 className: 'bpmn-icon-conditional-flow'
36933 }
36934 ];
36935
36936 var PARTICIPANT = [
36937 {
36938 label: 'Expanded Pool',
36939 actionName: 'replace-with-expanded-pool',
36940 className: 'bpmn-icon-participant',
36941 target: {
36942 type: 'bpmn:Participant',
36943 isExpanded: true
36944 }
36945 },
36946 {
36947 label: function(element) {
36948 var label = 'Empty Pool';
36949
36950 if (element.children && element.children.length) {
36951 label += ' (removes content)';
36952 }
36953
36954 return label;
36955 },
36956 actionName: 'replace-with-collapsed-pool',
36957
36958 // TODO(@janstuemmel): maybe design new icon
36959 className: 'bpmn-icon-lane',
36960 target: {
36961 type: 'bpmn:Participant',
36962 isExpanded: false
36963 }
36964 }
36965 ];
36966
36967 /**
36968 * This module is an element agnostic replace menu provider for the popup menu.
36969 */
36970 function ReplaceMenuProvider(
36971 bpmnFactory, popupMenu, modeling, moddle,
36972 bpmnReplace, rules, translate) {
36973
36974 this._bpmnFactory = bpmnFactory;
36975 this._popupMenu = popupMenu;
36976 this._modeling = modeling;
36977 this._moddle = moddle;
36978 this._bpmnReplace = bpmnReplace;
36979 this._rules = rules;
36980 this._translate = translate;
36981
36982 this.register();
36983 }
36984
36985 ReplaceMenuProvider.$inject = [
36986 'bpmnFactory',
36987 'popupMenu',
36988 'modeling',
36989 'moddle',
36990 'bpmnReplace',
36991 'rules',
36992 'translate'
36993 ];
36994
36995
36996 /**
36997 * Register replace menu provider in the popup menu
36998 */
36999 ReplaceMenuProvider.prototype.register = function() {
37000 this._popupMenu.registerProvider('bpmn-replace', this);
37001 };
37002
37003
37004 /**
37005 * Get all entries from replaceOptions for the given element and apply filters
37006 * on them. Get for example only elements, which are different from the current one.
37007 *
37008 * @param {djs.model.Base} element
37009 *
37010 * @return {Array<Object>} a list of menu entry items
37011 */
37012 ReplaceMenuProvider.prototype.getEntries = function(element) {
37013
37014 var businessObject = element.businessObject;
37015
37016 var rules = this._rules;
37017
37018 var entries;
37019
37020 if (!rules.allowed('shape.replace', { element: element })) {
37021 return [];
37022 }
37023
37024 var differentType = isDifferentType(element);
37025
37026 if (is$1(businessObject, 'bpmn:DataObjectReference')) {
37027 return this._createEntries(element, DATA_OBJECT_REFERENCE);
37028 }
37029
37030 if (is$1(businessObject, 'bpmn:DataStoreReference') && !is$1(element.parent, 'bpmn:Collaboration')) {
37031 return this._createEntries(element, DATA_STORE_REFERENCE);
37032 }
37033
37034 // start events outside sub processes
37035 if (is$1(businessObject, 'bpmn:StartEvent') && !is$1(businessObject.$parent, 'bpmn:SubProcess')) {
37036
37037 entries = filter(START_EVENT, differentType);
37038
37039 return this._createEntries(element, entries);
37040 }
37041
37042 // expanded/collapsed pools
37043 if (is$1(businessObject, 'bpmn:Participant')) {
37044
37045 entries = filter(PARTICIPANT, function(entry) {
37046 return isExpanded(element) !== entry.target.isExpanded;
37047 });
37048
37049 return this._createEntries(element, entries);
37050 }
37051
37052 // start events inside event sub processes
37053 if (is$1(businessObject, 'bpmn:StartEvent') && isEventSubProcess(businessObject.$parent)) {
37054 entries = filter(EVENT_SUB_PROCESS_START_EVENT, function(entry) {
37055
37056 var target = entry.target;
37057
37058 var isInterrupting = target.isInterrupting !== false;
37059
37060 var isInterruptingEqual = getBusinessObject(element).isInterrupting === isInterrupting;
37061
37062 // filters elements which types and event definition are equal but have have different interrupting types
37063 return differentType(entry) || !differentType(entry) && !isInterruptingEqual;
37064
37065 });
37066
37067 return this._createEntries(element, entries);
37068 }
37069
37070 // start events inside sub processes
37071 if (is$1(businessObject, 'bpmn:StartEvent') && !isEventSubProcess(businessObject.$parent)
37072 && is$1(businessObject.$parent, 'bpmn:SubProcess')) {
37073 entries = filter(START_EVENT_SUB_PROCESS, differentType);
37074
37075 return this._createEntries(element, entries);
37076 }
37077
37078 // end events
37079 if (is$1(businessObject, 'bpmn:EndEvent')) {
37080
37081 entries = filter(END_EVENT, function(entry) {
37082 var target = entry.target;
37083
37084 // hide cancel end events outside transactions
37085 if (target.eventDefinitionType == 'bpmn:CancelEventDefinition' && !is$1(businessObject.$parent, 'bpmn:Transaction')) {
37086 return false;
37087 }
37088
37089 return differentType(entry);
37090 });
37091
37092 return this._createEntries(element, entries);
37093 }
37094
37095 // boundary events
37096 if (is$1(businessObject, 'bpmn:BoundaryEvent')) {
37097
37098 entries = filter(BOUNDARY_EVENT, function(entry) {
37099
37100 var target = entry.target;
37101
37102 if (target.eventDefinitionType == 'bpmn:CancelEventDefinition' &&
37103 !is$1(businessObject.attachedToRef, 'bpmn:Transaction')) {
37104 return false;
37105 }
37106 var cancelActivity = target.cancelActivity !== false;
37107
37108 var isCancelActivityEqual = businessObject.cancelActivity == cancelActivity;
37109
37110 return differentType(entry) || !differentType(entry) && !isCancelActivityEqual;
37111 });
37112
37113 return this._createEntries(element, entries);
37114 }
37115
37116 // intermediate events
37117 if (is$1(businessObject, 'bpmn:IntermediateCatchEvent') ||
37118 is$1(businessObject, 'bpmn:IntermediateThrowEvent')) {
37119
37120 entries = filter(INTERMEDIATE_EVENT, differentType);
37121
37122 return this._createEntries(element, entries);
37123 }
37124
37125 // gateways
37126 if (is$1(businessObject, 'bpmn:Gateway')) {
37127
37128 entries = filter(GATEWAY, differentType);
37129
37130 return this._createEntries(element, entries);
37131 }
37132
37133 // transactions
37134 if (is$1(businessObject, 'bpmn:Transaction')) {
37135
37136 entries = filter(TRANSACTION, differentType);
37137
37138 return this._createEntries(element, entries);
37139 }
37140
37141 // expanded event sub processes
37142 if (isEventSubProcess(businessObject) && isExpanded(element)) {
37143
37144 entries = filter(EVENT_SUB_PROCESS, differentType);
37145
37146 return this._createEntries(element, entries);
37147 }
37148
37149 // expanded sub processes
37150 if (is$1(businessObject, 'bpmn:SubProcess') && isExpanded(element)) {
37151
37152 entries = filter(SUBPROCESS_EXPANDED, differentType);
37153
37154 return this._createEntries(element, entries);
37155 }
37156
37157 // collapsed ad hoc sub processes
37158 if (is$1(businessObject, 'bpmn:AdHocSubProcess') && !isExpanded(element)) {
37159
37160 entries = filter(TASK, function(entry) {
37161
37162 var target = entry.target;
37163
37164 var isTargetSubProcess = target.type === 'bpmn:SubProcess';
37165
37166 var isTargetExpanded = target.isExpanded === true;
37167
37168 return isDifferentType(element) && (!isTargetSubProcess || isTargetExpanded);
37169 });
37170
37171 return this._createEntries(element, entries);
37172 }
37173
37174 // sequence flows
37175 if (is$1(businessObject, 'bpmn:SequenceFlow')) {
37176 return this._createSequenceFlowEntries(element, SEQUENCE_FLOW);
37177 }
37178
37179 // flow nodes
37180 if (is$1(businessObject, 'bpmn:FlowNode')) {
37181 entries = filter(TASK, differentType);
37182
37183 // collapsed SubProcess can not be replaced with itself
37184 if (is$1(businessObject, 'bpmn:SubProcess') && !isExpanded(element)) {
37185 entries = filter(entries, function(entry) {
37186 return entry.label !== 'Sub Process (collapsed)';
37187 });
37188 }
37189
37190 return this._createEntries(element, entries);
37191 }
37192
37193 return [];
37194 };
37195
37196
37197 /**
37198 * Get a list of header items for the given element. This includes buttons
37199 * for multi instance markers and for the ad hoc marker.
37200 *
37201 * @param {djs.model.Base} element
37202 *
37203 * @return {Array<Object>} a list of menu entry items
37204 */
37205 ReplaceMenuProvider.prototype.getHeaderEntries = function(element) {
37206
37207 var headerEntries = [];
37208
37209 if (is$1(element, 'bpmn:Activity') && !isEventSubProcess(element)) {
37210 headerEntries = headerEntries.concat(this._getLoopEntries(element));
37211 }
37212
37213 if (is$1(element, 'bpmn:DataObjectReference')) {
37214 headerEntries = headerEntries.concat(this._getDataObjectIsCollection(element));
37215 }
37216
37217 if (is$1(element, 'bpmn:Participant')) {
37218 headerEntries = headerEntries.concat(this._getParticipantMultiplicity(element));
37219 }
37220
37221 if (is$1(element, 'bpmn:SubProcess') &&
37222 !is$1(element, 'bpmn:Transaction') &&
37223 !isEventSubProcess(element)) {
37224 headerEntries.push(this._getAdHocEntry(element));
37225 }
37226
37227 return headerEntries;
37228 };
37229
37230
37231 /**
37232 * Creates an array of menu entry objects for a given element and filters the replaceOptions
37233 * according to a filter function.
37234 *
37235 * @param {djs.model.Base} element
37236 * @param {Object} replaceOptions
37237 *
37238 * @return {Array<Object>} a list of menu items
37239 */
37240 ReplaceMenuProvider.prototype._createEntries = function(element, replaceOptions) {
37241 var menuEntries = [];
37242
37243 var self = this;
37244
37245 forEach$2(replaceOptions, function(definition) {
37246 var entry = self._createMenuEntry(definition, element);
37247
37248 menuEntries.push(entry);
37249 });
37250
37251 return menuEntries;
37252 };
37253
37254 /**
37255 * Creates an array of menu entry objects for a given sequence flow.
37256 *
37257 * @param {djs.model.Base} element
37258 * @param {Object} replaceOptions
37259
37260 * @return {Array<Object>} a list of menu items
37261 */
37262 ReplaceMenuProvider.prototype._createSequenceFlowEntries = function(element, replaceOptions) {
37263
37264 var businessObject = getBusinessObject(element);
37265
37266 var menuEntries = [];
37267
37268 var modeling = this._modeling,
37269 moddle = this._moddle;
37270
37271 var self = this;
37272
37273 forEach$2(replaceOptions, function(entry) {
37274
37275 switch (entry.actionName) {
37276 case 'replace-with-default-flow':
37277 if (businessObject.sourceRef.default !== businessObject &&
37278 (is$1(businessObject.sourceRef, 'bpmn:ExclusiveGateway') ||
37279 is$1(businessObject.sourceRef, 'bpmn:InclusiveGateway') ||
37280 is$1(businessObject.sourceRef, 'bpmn:ComplexGateway') ||
37281 is$1(businessObject.sourceRef, 'bpmn:Activity'))) {
37282
37283 menuEntries.push(self._createMenuEntry(entry, element, function() {
37284 modeling.updateProperties(element.source, { default: businessObject });
37285 }));
37286 }
37287 break;
37288 case 'replace-with-conditional-flow':
37289 if (!businessObject.conditionExpression && is$1(businessObject.sourceRef, 'bpmn:Activity')) {
37290
37291 menuEntries.push(self._createMenuEntry(entry, element, function() {
37292 var conditionExpression = moddle.create('bpmn:FormalExpression', { body: '' });
37293
37294 modeling.updateProperties(element, { conditionExpression: conditionExpression });
37295 }));
37296 }
37297 break;
37298 default:
37299
37300 // default flows
37301 if (is$1(businessObject.sourceRef, 'bpmn:Activity') && businessObject.conditionExpression) {
37302 return menuEntries.push(self._createMenuEntry(entry, element, function() {
37303 modeling.updateProperties(element, { conditionExpression: undefined });
37304 }));
37305 }
37306
37307 // conditional flows
37308 if ((is$1(businessObject.sourceRef, 'bpmn:ExclusiveGateway') ||
37309 is$1(businessObject.sourceRef, 'bpmn:InclusiveGateway') ||
37310 is$1(businessObject.sourceRef, 'bpmn:ComplexGateway') ||
37311 is$1(businessObject.sourceRef, 'bpmn:Activity')) &&
37312 businessObject.sourceRef.default === businessObject) {
37313
37314 return menuEntries.push(self._createMenuEntry(entry, element, function() {
37315 modeling.updateProperties(element.source, { default: undefined });
37316 }));
37317 }
37318 }
37319 });
37320
37321 return menuEntries;
37322 };
37323
37324
37325 /**
37326 * Creates and returns a single menu entry item.
37327 *
37328 * @param {Object} definition a single replace options definition object
37329 * @param {djs.model.Base} element
37330 * @param {Function} [action] an action callback function which gets called when
37331 * the menu entry is being triggered.
37332 *
37333 * @return {Object} menu entry item
37334 */
37335 ReplaceMenuProvider.prototype._createMenuEntry = function(definition, element, action) {
37336 var translate = this._translate;
37337 var replaceElement = this._bpmnReplace.replaceElement;
37338
37339 var replaceAction = function() {
37340 return replaceElement(element, definition.target);
37341 };
37342
37343 var label = definition.label;
37344 if (label && typeof label === 'function') {
37345 label = label(element);
37346 }
37347
37348 action = action || replaceAction;
37349
37350 var menuEntry = {
37351 label: translate(label),
37352 className: definition.className,
37353 id: definition.actionName,
37354 action: action
37355 };
37356
37357 return menuEntry;
37358 };
37359
37360 /**
37361 * Get a list of menu items containing buttons for multi instance markers
37362 *
37363 * @param {djs.model.Base} element
37364 *
37365 * @return {Array<Object>} a list of menu items
37366 */
37367 ReplaceMenuProvider.prototype._getLoopEntries = function(element) {
37368
37369 var self = this;
37370 var translate = this._translate;
37371
37372 function toggleLoopEntry(event, entry) {
37373 var loopCharacteristics = getBusinessObject(element).loopCharacteristics;
37374
37375 if (entry.active) {
37376 loopCharacteristics = undefined;
37377 } else {
37378 if (isUndefined$3(entry.options.isSequential) || !loopCharacteristics) {
37379 loopCharacteristics = self._moddle.create(entry.options.loopCharacteristics);
37380 }
37381
37382 loopCharacteristics.isSequential = entry.options.isSequential;
37383 }
37384 self._modeling.updateProperties(element, { loopCharacteristics: loopCharacteristics });
37385 }
37386
37387 var businessObject = getBusinessObject(element),
37388 loopCharacteristics = businessObject.loopCharacteristics;
37389
37390 var isSequential,
37391 isLoop,
37392 isParallel;
37393
37394 if (loopCharacteristics) {
37395 isSequential = loopCharacteristics.isSequential;
37396 isLoop = loopCharacteristics.isSequential === undefined;
37397 isParallel = loopCharacteristics.isSequential !== undefined && !loopCharacteristics.isSequential;
37398 }
37399
37400
37401 var loopEntries = [
37402 {
37403 id: 'toggle-parallel-mi',
37404 className: 'bpmn-icon-parallel-mi-marker',
37405 title: translate('Parallel Multi Instance'),
37406 active: isParallel,
37407 action: toggleLoopEntry,
37408 options: {
37409 loopCharacteristics: 'bpmn:MultiInstanceLoopCharacteristics',
37410 isSequential: false
37411 }
37412 },
37413 {
37414 id: 'toggle-sequential-mi',
37415 className: 'bpmn-icon-sequential-mi-marker',
37416 title: translate('Sequential Multi Instance'),
37417 active: isSequential,
37418 action: toggleLoopEntry,
37419 options: {
37420 loopCharacteristics: 'bpmn:MultiInstanceLoopCharacteristics',
37421 isSequential: true
37422 }
37423 },
37424 {
37425 id: 'toggle-loop',
37426 className: 'bpmn-icon-loop-marker',
37427 title: translate('Loop'),
37428 active: isLoop,
37429 action: toggleLoopEntry,
37430 options: {
37431 loopCharacteristics: 'bpmn:StandardLoopCharacteristics'
37432 }
37433 }
37434 ];
37435 return loopEntries;
37436 };
37437
37438 /**
37439 * Get a list of menu items containing a button for the collection marker
37440 *
37441 * @param {djs.model.Base} element
37442 *
37443 * @return {Array<Object>} a list of menu items
37444 */
37445 ReplaceMenuProvider.prototype._getDataObjectIsCollection = function(element) {
37446
37447 var self = this;
37448 var translate = this._translate;
37449
37450 function toggleIsCollection(event, entry) {
37451 self._modeling.updateModdleProperties(
37452 element,
37453 dataObject,
37454 { isCollection: !entry.active });
37455 }
37456
37457 var dataObject = element.businessObject.dataObjectRef,
37458 isCollection = dataObject.isCollection;
37459
37460 var dataObjectEntries = [
37461 {
37462 id: 'toggle-is-collection',
37463 className: 'bpmn-icon-parallel-mi-marker',
37464 title: translate('Collection'),
37465 active: isCollection,
37466 action: toggleIsCollection,
37467 }
37468 ];
37469 return dataObjectEntries;
37470 };
37471
37472 /**
37473 * Get a list of menu items containing a button for the participant multiplicity marker
37474 *
37475 * @param {djs.model.Base} element
37476 *
37477 * @return {Array<Object>} a list of menu items
37478 */
37479 ReplaceMenuProvider.prototype._getParticipantMultiplicity = function(element) {
37480
37481 var self = this;
37482 var bpmnFactory = this._bpmnFactory;
37483 var translate = this._translate;
37484
37485 function toggleParticipantMultiplicity(event, entry) {
37486 var isActive = entry.active;
37487 var participantMultiplicity;
37488
37489 if (!isActive) {
37490 participantMultiplicity = bpmnFactory.create('bpmn:ParticipantMultiplicity');
37491 }
37492
37493 self._modeling.updateProperties(
37494 element,
37495 { participantMultiplicity: participantMultiplicity });
37496 }
37497
37498 var participantMultiplicity = element.businessObject.participantMultiplicity;
37499
37500 var participantEntries = [
37501 {
37502 id: 'toggle-participant-multiplicity',
37503 className: 'bpmn-icon-parallel-mi-marker',
37504 title: translate('Participant Multiplicity'),
37505 active: !!participantMultiplicity,
37506 action: toggleParticipantMultiplicity,
37507 }
37508 ];
37509 return participantEntries;
37510 };
37511
37512
37513 /**
37514 * Get the menu items containing a button for the ad hoc marker
37515 *
37516 * @param {djs.model.Base} element
37517 *
37518 * @return {Object} a menu item
37519 */
37520 ReplaceMenuProvider.prototype._getAdHocEntry = function(element) {
37521 var translate = this._translate;
37522 var businessObject = getBusinessObject(element);
37523
37524 var isAdHoc = is$1(businessObject, 'bpmn:AdHocSubProcess');
37525
37526 var replaceElement = this._bpmnReplace.replaceElement;
37527
37528 var adHocEntry = {
37529 id: 'toggle-adhoc',
37530 className: 'bpmn-icon-ad-hoc-marker',
37531 title: translate('Ad-hoc'),
37532 active: isAdHoc,
37533 action: function(event, entry) {
37534 if (isAdHoc) {
37535 return replaceElement(element, { type: 'bpmn:SubProcess' }, {
37536 autoResize: false,
37537 layoutConnection: false
37538 });
37539 } else {
37540 return replaceElement(element, { type: 'bpmn:AdHocSubProcess' }, {
37541 autoResize: false,
37542 layoutConnection: false
37543 });
37544 }
37545 }
37546 };
37547
37548 return adHocEntry;
37549 };
37550
37551 var PopupMenuModule = {
37552 __depends__: [
37553 PopupMenuModule$1,
37554 ReplaceModule
37555 ],
37556 __init__: [ 'replaceMenuProvider' ],
37557 replaceMenuProvider: [ 'type', ReplaceMenuProvider ]
37558 };
37559
37560 var max$4 = Math.max,
37561 min$2 = Math.min;
37562
37563 var DEFAULT_CHILD_BOX_PADDING = 20;
37564
37565
37566 /**
37567 * Substract a TRBL from another
37568 *
37569 * @param {TRBL} trblA
37570 * @param {TRBL} trblB
37571 *
37572 * @return {TRBL}
37573 */
37574 function substractTRBL(trblA, trblB) {
37575 return {
37576 top: trblA.top - trblB.top,
37577 right: trblA.right - trblB.right,
37578 bottom: trblA.bottom - trblB.bottom,
37579 left: trblA.left - trblB.left
37580 };
37581 }
37582
37583 /**
37584 * Resize the given bounds by the specified delta from a given anchor point.
37585 *
37586 * @param {Bounds} bounds the bounding box that should be resized
37587 * @param {string} direction in which the element is resized (nw, ne, se, sw)
37588 * @param {Point} delta of the resize operation
37589 *
37590 * @return {Bounds} resized bounding box
37591 */
37592 function resizeBounds$1(bounds, direction, delta) {
37593 var dx = delta.x,
37594 dy = delta.y;
37595
37596 var newBounds = {
37597 x: bounds.x,
37598 y: bounds.y,
37599 width: bounds.width,
37600 height: bounds.height
37601 };
37602
37603 if (direction.indexOf('n') !== -1) {
37604 newBounds.y = bounds.y + dy;
37605 newBounds.height = bounds.height - dy;
37606 } else if (direction.indexOf('s') !== -1) {
37607 newBounds.height = bounds.height + dy;
37608 }
37609
37610 if (direction.indexOf('e') !== -1) {
37611 newBounds.width = bounds.width + dx;
37612 } else if (direction.indexOf('w') !== -1) {
37613 newBounds.x = bounds.x + dx;
37614 newBounds.width = bounds.width - dx;
37615 }
37616
37617 return newBounds;
37618 }
37619
37620
37621 /**
37622 * Resize the given bounds by applying the passed
37623 * { top, right, bottom, left } delta.
37624 *
37625 * @param {Bounds} bounds
37626 * @param {TRBL} trblResize
37627 *
37628 * @return {Bounds}
37629 */
37630 function resizeTRBL(bounds, resize) {
37631 return {
37632 x: bounds.x + (resize.left || 0),
37633 y: bounds.y + (resize.top || 0),
37634 width: bounds.width - (resize.left || 0) + (resize.right || 0),
37635 height: bounds.height - (resize.top || 0) + (resize.bottom || 0)
37636 };
37637 }
37638
37639
37640 function applyConstraints(attr, trbl, resizeConstraints) {
37641
37642 var value = trbl[attr],
37643 minValue = resizeConstraints.min && resizeConstraints.min[attr],
37644 maxValue = resizeConstraints.max && resizeConstraints.max[attr];
37645
37646 if (isNumber(minValue)) {
37647 value = (/top|left/.test(attr) ? min$2 : max$4)(value, minValue);
37648 }
37649
37650 if (isNumber(maxValue)) {
37651 value = (/top|left/.test(attr) ? max$4 : min$2)(value, maxValue);
37652 }
37653
37654 return value;
37655 }
37656
37657 function ensureConstraints$1(currentBounds, resizeConstraints) {
37658
37659 if (!resizeConstraints) {
37660 return currentBounds;
37661 }
37662
37663 var currentTrbl = asTRBL(currentBounds);
37664
37665 return asBounds({
37666 top: applyConstraints('top', currentTrbl, resizeConstraints),
37667 right: applyConstraints('right', currentTrbl, resizeConstraints),
37668 bottom: applyConstraints('bottom', currentTrbl, resizeConstraints),
37669 left: applyConstraints('left', currentTrbl, resizeConstraints)
37670 });
37671 }
37672
37673
37674 function getMinResizeBounds(direction, currentBounds, minDimensions, childrenBounds) {
37675
37676 var currentBox = asTRBL(currentBounds);
37677
37678 var minBox = {
37679 top: /n/.test(direction) ? currentBox.bottom - minDimensions.height : currentBox.top,
37680 left: /w/.test(direction) ? currentBox.right - minDimensions.width : currentBox.left,
37681 bottom: /s/.test(direction) ? currentBox.top + minDimensions.height : currentBox.bottom,
37682 right: /e/.test(direction) ? currentBox.left + minDimensions.width : currentBox.right
37683 };
37684
37685 var childrenBox = childrenBounds ? asTRBL(childrenBounds) : minBox;
37686
37687 var combinedBox = {
37688 top: min$2(minBox.top, childrenBox.top),
37689 left: min$2(minBox.left, childrenBox.left),
37690 bottom: max$4(minBox.bottom, childrenBox.bottom),
37691 right: max$4(minBox.right, childrenBox.right)
37692 };
37693
37694 return asBounds(combinedBox);
37695 }
37696
37697 function asPadding(mayBePadding, defaultValue) {
37698 if (typeof mayBePadding !== 'undefined') {
37699 return mayBePadding;
37700 } else {
37701 return DEFAULT_CHILD_BOX_PADDING;
37702 }
37703 }
37704
37705 function addPadding$1(bbox, padding) {
37706 var left, right, top, bottom;
37707
37708 if (typeof padding === 'object') {
37709 left = asPadding(padding.left);
37710 right = asPadding(padding.right);
37711 top = asPadding(padding.top);
37712 bottom = asPadding(padding.bottom);
37713 } else {
37714 left = right = top = bottom = asPadding(padding);
37715 }
37716
37717 return {
37718 x: bbox.x - left,
37719 y: bbox.y - top,
37720 width: bbox.width + left + right,
37721 height: bbox.height + top + bottom
37722 };
37723 }
37724
37725
37726 /**
37727 * Is the given element part of the resize
37728 * targets min boundary box?
37729 *
37730 * This is the default implementation which excludes
37731 * connections and labels.
37732 *
37733 * @param {djs.model.Base} element
37734 */
37735 function isBBoxChild(element) {
37736
37737 // exclude connections
37738 if (element.waypoints) {
37739 return false;
37740 }
37741
37742 // exclude labels
37743 if (element.type === 'label') {
37744 return false;
37745 }
37746
37747 return true;
37748 }
37749
37750 /**
37751 * Return children bounding computed from a shapes children
37752 * or a list of prefiltered children.
37753 *
37754 * @param {djs.model.Shape|Array<djs.model.Shape>} shapeOrChildren
37755 * @param {number|Object} padding
37756 *
37757 * @return {Bounds}
37758 */
37759 function computeChildrenBBox(shapeOrChildren, padding) {
37760
37761 var elements;
37762
37763 // compute based on shape
37764 if (shapeOrChildren.length === undefined) {
37765
37766 // grab all the children that are part of the
37767 // parents children box
37768 elements = filter(shapeOrChildren.children, isBBoxChild);
37769
37770 } else {
37771 elements = shapeOrChildren;
37772 }
37773
37774 if (elements.length) {
37775 return addPadding$1(getBBox(elements), padding);
37776 }
37777 }
37778
37779 var abs$4 = Math.abs;
37780
37781
37782 function getTRBLResize(oldBounds, newBounds) {
37783 return substractTRBL(asTRBL(newBounds), asTRBL(oldBounds));
37784 }
37785
37786
37787 var LANE_PARENTS = [
37788 'bpmn:Participant',
37789 'bpmn:Process',
37790 'bpmn:SubProcess'
37791 ];
37792
37793 var LANE_INDENTATION = 30;
37794
37795
37796 /**
37797 * Collect all lane shapes in the given paren
37798 *
37799 * @param {djs.model.Shape} shape
37800 * @param {Array<djs.model.Base>} [collectedShapes]
37801 *
37802 * @return {Array<djs.model.Base>}
37803 */
37804 function collectLanes(shape, collectedShapes) {
37805
37806 collectedShapes = collectedShapes || [];
37807
37808 shape.children.filter(function(s) {
37809 if (is$1(s, 'bpmn:Lane')) {
37810 collectLanes(s, collectedShapes);
37811
37812 collectedShapes.push(s);
37813 }
37814 });
37815
37816 return collectedShapes;
37817 }
37818
37819
37820 /**
37821 * Return the lane children of the given element.
37822 *
37823 * @param {djs.model.Shape} shape
37824 *
37825 * @return {Array<djs.model.Shape>}
37826 */
37827 function getChildLanes(shape) {
37828 return shape.children.filter(function(c) {
37829 return is$1(c, 'bpmn:Lane');
37830 });
37831 }
37832
37833
37834 /**
37835 * Return the root element containing the given lane shape
37836 *
37837 * @param {djs.model.Shape} shape
37838 *
37839 * @return {djs.model.Shape}
37840 */
37841 function getLanesRoot(shape) {
37842 return getParent(shape, LANE_PARENTS) || shape;
37843 }
37844
37845
37846 /**
37847 * Compute the required resize operations for lanes
37848 * adjacent to the given shape, assuming it will be
37849 * resized to the given new bounds.
37850 *
37851 * @param {djs.model.Shape} shape
37852 * @param {Bounds} newBounds
37853 *
37854 * @return {Array<Object>}
37855 */
37856 function computeLanesResize(shape, newBounds) {
37857
37858 var rootElement = getLanesRoot(shape);
37859
37860 var initialShapes = is$1(rootElement, 'bpmn:Process') ? [] : [ rootElement ];
37861
37862 var allLanes = collectLanes(rootElement, initialShapes),
37863 shapeTrbl = asTRBL(shape),
37864 shapeNewTrbl = asTRBL(newBounds),
37865 trblResize = getTRBLResize(shape, newBounds),
37866 resizeNeeded = [];
37867
37868 allLanes.forEach(function(other) {
37869
37870 if (other === shape) {
37871 return;
37872 }
37873
37874 var topResize = 0,
37875 rightResize = trblResize.right,
37876 bottomResize = 0,
37877 leftResize = trblResize.left;
37878
37879 var otherTrbl = asTRBL(other);
37880
37881 if (trblResize.top) {
37882 if (abs$4(otherTrbl.bottom - shapeTrbl.top) < 10) {
37883 bottomResize = shapeNewTrbl.top - otherTrbl.bottom;
37884 }
37885
37886 if (abs$4(otherTrbl.top - shapeTrbl.top) < 5) {
37887 topResize = shapeNewTrbl.top - otherTrbl.top;
37888 }
37889 }
37890
37891 if (trblResize.bottom) {
37892 if (abs$4(otherTrbl.top - shapeTrbl.bottom) < 10) {
37893 topResize = shapeNewTrbl.bottom - otherTrbl.top;
37894 }
37895
37896 if (abs$4(otherTrbl.bottom - shapeTrbl.bottom) < 5) {
37897 bottomResize = shapeNewTrbl.bottom - otherTrbl.bottom;
37898 }
37899 }
37900
37901 if (topResize || rightResize || bottomResize || leftResize) {
37902
37903 resizeNeeded.push({
37904 shape: other,
37905 newBounds: resizeTRBL(other, {
37906 top: topResize,
37907 right: rightResize,
37908 bottom: bottomResize,
37909 left: leftResize
37910 })
37911 });
37912 }
37913
37914 });
37915
37916 return resizeNeeded;
37917 }
37918
37919 /**
37920 * A provider for BPMN 2.0 elements context pad
37921 */
37922 function ContextPadProvider(
37923 config, injector, eventBus,
37924 contextPad, modeling, elementFactory,
37925 connect, create, popupMenu,
37926 canvas, rules, translate) {
37927
37928 config = config || {};
37929
37930 contextPad.registerProvider(this);
37931
37932 this._contextPad = contextPad;
37933
37934 this._modeling = modeling;
37935
37936 this._elementFactory = elementFactory;
37937 this._connect = connect;
37938 this._create = create;
37939 this._popupMenu = popupMenu;
37940 this._canvas = canvas;
37941 this._rules = rules;
37942 this._translate = translate;
37943
37944 if (config.autoPlace !== false) {
37945 this._autoPlace = injector.get('autoPlace', false);
37946 }
37947
37948 eventBus.on('create.end', 250, function(event) {
37949 var context = event.context,
37950 shape = context.shape;
37951
37952 if (!hasPrimaryModifier(event) || !contextPad.isOpen(shape)) {
37953 return;
37954 }
37955
37956 var entries = contextPad.getEntries(shape);
37957
37958 if (entries.replace) {
37959 entries.replace.action.click(event, shape);
37960 }
37961 });
37962 }
37963
37964 ContextPadProvider.$inject = [
37965 'config.contextPad',
37966 'injector',
37967 'eventBus',
37968 'contextPad',
37969 'modeling',
37970 'elementFactory',
37971 'connect',
37972 'create',
37973 'popupMenu',
37974 'canvas',
37975 'rules',
37976 'translate'
37977 ];
37978
37979
37980 ContextPadProvider.prototype.getContextPadEntries = function(element) {
37981
37982 var contextPad = this._contextPad,
37983 modeling = this._modeling,
37984
37985 elementFactory = this._elementFactory,
37986 connect = this._connect,
37987 create = this._create,
37988 popupMenu = this._popupMenu,
37989 canvas = this._canvas,
37990 rules = this._rules,
37991 autoPlace = this._autoPlace,
37992 translate = this._translate;
37993
37994 var actions = {};
37995
37996 if (element.type === 'label') {
37997 return actions;
37998 }
37999
38000 var businessObject = element.businessObject;
38001
38002 function startConnect(event, element) {
38003 connect.start(event, element);
38004 }
38005
38006 function removeElement(e) {
38007 modeling.removeElements([ element ]);
38008 }
38009
38010 function getReplaceMenuPosition(element) {
38011
38012 var Y_OFFSET = 5;
38013
38014 var diagramContainer = canvas.getContainer(),
38015 pad = contextPad.getPad(element).html;
38016
38017 var diagramRect = diagramContainer.getBoundingClientRect(),
38018 padRect = pad.getBoundingClientRect();
38019
38020 var top = padRect.top - diagramRect.top;
38021 var left = padRect.left - diagramRect.left;
38022
38023 var pos = {
38024 x: left,
38025 y: top + padRect.height + Y_OFFSET
38026 };
38027
38028 return pos;
38029 }
38030
38031
38032 /**
38033 * Create an append action
38034 *
38035 * @param {string} type
38036 * @param {string} className
38037 * @param {string} [title]
38038 * @param {Object} [options]
38039 *
38040 * @return {Object} descriptor
38041 */
38042 function appendAction(type, className, title, options) {
38043
38044 if (typeof title !== 'string') {
38045 options = title;
38046 title = translate('Append {type}', { type: type.replace(/^bpmn:/, '') });
38047 }
38048
38049 function appendStart(event, element) {
38050
38051 var shape = elementFactory.createShape(assign({ type: type }, options));
38052 create.start(event, shape, {
38053 source: element
38054 });
38055 }
38056
38057
38058 var append = autoPlace ? function(event, element) {
38059 var shape = elementFactory.createShape(assign({ type: type }, options));
38060
38061 autoPlace.append(element, shape);
38062 } : appendStart;
38063
38064
38065 return {
38066 group: 'model',
38067 className: className,
38068 title: title,
38069 action: {
38070 dragstart: appendStart,
38071 click: append
38072 }
38073 };
38074 }
38075
38076 function splitLaneHandler(count) {
38077
38078 return function(event, element) {
38079
38080 // actual split
38081 modeling.splitLane(element, count);
38082
38083 // refresh context pad after split to
38084 // get rid of split icons
38085 contextPad.open(element, true);
38086 };
38087 }
38088
38089
38090 if (isAny(businessObject, [ 'bpmn:Lane', 'bpmn:Participant' ]) && isExpanded(element)) {
38091
38092 var childLanes = getChildLanes(element);
38093
38094 assign(actions, {
38095 'lane-insert-above': {
38096 group: 'lane-insert-above',
38097 className: 'bpmn-icon-lane-insert-above',
38098 title: translate('Add Lane above'),
38099 action: {
38100 click: function(event, element) {
38101 modeling.addLane(element, 'top');
38102 }
38103 }
38104 }
38105 });
38106
38107 if (childLanes.length < 2) {
38108
38109 if (element.height >= 120) {
38110 assign(actions, {
38111 'lane-divide-two': {
38112 group: 'lane-divide',
38113 className: 'bpmn-icon-lane-divide-two',
38114 title: translate('Divide into two Lanes'),
38115 action: {
38116 click: splitLaneHandler(2)
38117 }
38118 }
38119 });
38120 }
38121
38122 if (element.height >= 180) {
38123 assign(actions, {
38124 'lane-divide-three': {
38125 group: 'lane-divide',
38126 className: 'bpmn-icon-lane-divide-three',
38127 title: translate('Divide into three Lanes'),
38128 action: {
38129 click: splitLaneHandler(3)
38130 }
38131 }
38132 });
38133 }
38134 }
38135
38136 assign(actions, {
38137 'lane-insert-below': {
38138 group: 'lane-insert-below',
38139 className: 'bpmn-icon-lane-insert-below',
38140 title: translate('Add Lane below'),
38141 action: {
38142 click: function(event, element) {
38143 modeling.addLane(element, 'bottom');
38144 }
38145 }
38146 }
38147 });
38148
38149 }
38150
38151 if (is$1(businessObject, 'bpmn:FlowNode')) {
38152
38153 if (is$1(businessObject, 'bpmn:EventBasedGateway')) {
38154
38155 assign(actions, {
38156 'append.receive-task': appendAction(
38157 'bpmn:ReceiveTask',
38158 'bpmn-icon-receive-task',
38159 translate('Append ReceiveTask')
38160 ),
38161 'append.message-intermediate-event': appendAction(
38162 'bpmn:IntermediateCatchEvent',
38163 'bpmn-icon-intermediate-event-catch-message',
38164 translate('Append MessageIntermediateCatchEvent'),
38165 { eventDefinitionType: 'bpmn:MessageEventDefinition' }
38166 ),
38167 'append.timer-intermediate-event': appendAction(
38168 'bpmn:IntermediateCatchEvent',
38169 'bpmn-icon-intermediate-event-catch-timer',
38170 translate('Append TimerIntermediateCatchEvent'),
38171 { eventDefinitionType: 'bpmn:TimerEventDefinition' }
38172 ),
38173 'append.condition-intermediate-event': appendAction(
38174 'bpmn:IntermediateCatchEvent',
38175 'bpmn-icon-intermediate-event-catch-condition',
38176 translate('Append ConditionIntermediateCatchEvent'),
38177 { eventDefinitionType: 'bpmn:ConditionalEventDefinition' }
38178 ),
38179 'append.signal-intermediate-event': appendAction(
38180 'bpmn:IntermediateCatchEvent',
38181 'bpmn-icon-intermediate-event-catch-signal',
38182 translate('Append SignalIntermediateCatchEvent'),
38183 { eventDefinitionType: 'bpmn:SignalEventDefinition' }
38184 )
38185 });
38186 } else
38187
38188 if (isEventType(businessObject, 'bpmn:BoundaryEvent', 'bpmn:CompensateEventDefinition')) {
38189
38190 assign(actions, {
38191 'append.compensation-activity':
38192 appendAction(
38193 'bpmn:Task',
38194 'bpmn-icon-task',
38195 translate('Append compensation activity'),
38196 {
38197 isForCompensation: true
38198 }
38199 )
38200 });
38201 } else
38202
38203 if (!is$1(businessObject, 'bpmn:EndEvent') &&
38204 !businessObject.isForCompensation &&
38205 !isEventType(businessObject, 'bpmn:IntermediateThrowEvent', 'bpmn:LinkEventDefinition') &&
38206 !isEventSubProcess(businessObject)) {
38207
38208 assign(actions, {
38209 'append.end-event': appendAction(
38210 'bpmn:EndEvent',
38211 'bpmn-icon-end-event-none',
38212 translate('Append EndEvent')
38213 ),
38214 'append.gateway': appendAction(
38215 'bpmn:ExclusiveGateway',
38216 'bpmn-icon-gateway-none',
38217 translate('Append Gateway')
38218 ),
38219 'append.append-task': appendAction(
38220 'bpmn:Task',
38221 'bpmn-icon-task',
38222 translate('Append Task')
38223 ),
38224 'append.intermediate-event': appendAction(
38225 'bpmn:IntermediateThrowEvent',
38226 'bpmn-icon-intermediate-event-none',
38227 translate('Append Intermediate/Boundary Event')
38228 )
38229 });
38230 }
38231 }
38232
38233 if (!popupMenu.isEmpty(element, 'bpmn-replace')) {
38234
38235 // Replace menu entry
38236 assign(actions, {
38237 'replace': {
38238 group: 'edit',
38239 className: 'bpmn-icon-screw-wrench',
38240 title: translate('Change type'),
38241 action: {
38242 click: function(event, element) {
38243
38244 var position = assign(getReplaceMenuPosition(element), {
38245 cursor: { x: event.x, y: event.y }
38246 });
38247
38248 popupMenu.open(element, 'bpmn-replace', position);
38249 }
38250 }
38251 }
38252 });
38253 }
38254
38255 if (
38256 isAny(businessObject, [
38257 'bpmn:FlowNode',
38258 'bpmn:InteractionNode',
38259 'bpmn:DataObjectReference',
38260 'bpmn:DataStoreReference',
38261 ])
38262 ) {
38263 assign(actions, {
38264 'append.text-annotation': appendAction(
38265 'bpmn:TextAnnotation',
38266 'bpmn-icon-text-annotation'
38267 ),
38268
38269 'connect': {
38270 group: 'connect',
38271 className: 'bpmn-icon-connection-multi',
38272 title: translate(
38273 'Connect using ' +
38274 (businessObject.isForCompensation
38275 ? ''
38276 : 'Sequence/MessageFlow or ') +
38277 'Association'
38278 ),
38279 action: {
38280 click: startConnect,
38281 dragstart: startConnect,
38282 },
38283 },
38284 });
38285 }
38286
38287 if (is$1(businessObject, 'bpmn:TextAnnotation')) {
38288 assign(actions, {
38289 'connect': {
38290 group: 'connect',
38291 className: 'bpmn-icon-connection-multi',
38292 title: translate('Connect using Association'),
38293 action: {
38294 click: startConnect,
38295 dragstart: startConnect,
38296 },
38297 },
38298 });
38299 }
38300
38301 if (isAny(businessObject, [ 'bpmn:DataObjectReference', 'bpmn:DataStoreReference' ])) {
38302 assign(actions, {
38303 'connect': {
38304 group: 'connect',
38305 className: 'bpmn-icon-connection-multi',
38306 title: translate('Connect using DataInputAssociation'),
38307 action: {
38308 click: startConnect,
38309 dragstart: startConnect
38310 }
38311 }
38312 });
38313 }
38314
38315 if (is$1(businessObject, 'bpmn:Group')) {
38316 assign(actions, {
38317 'append.text-annotation': appendAction('bpmn:TextAnnotation', 'bpmn-icon-text-annotation')
38318 });
38319 }
38320
38321 // delete element entry, only show if allowed by rules
38322 var deleteAllowed = rules.allowed('elements.delete', { elements: [ element ] });
38323
38324 if (isArray$4(deleteAllowed)) {
38325
38326 // was the element returned as a deletion candidate?
38327 deleteAllowed = deleteAllowed[0] === element;
38328 }
38329
38330 if (deleteAllowed) {
38331 assign(actions, {
38332 'delete': {
38333 group: 'edit',
38334 className: 'bpmn-icon-trash',
38335 title: translate('Remove'),
38336 action: {
38337 click: removeElement
38338 }
38339 }
38340 });
38341 }
38342
38343 return actions;
38344 };
38345
38346
38347 // helpers /////////
38348
38349 function isEventType(eventBo, type, definition) {
38350
38351 var isType = eventBo.$instanceOf(type);
38352 var isDefinition = false;
38353
38354 var definitions = eventBo.eventDefinitions || [];
38355 forEach$2(definitions, function(def) {
38356 if (def.$type === definition) {
38357 isDefinition = true;
38358 }
38359 });
38360
38361 return isType && isDefinition;
38362 }
38363
38364 var ContextPadModule = {
38365 __depends__: [
38366 DirectEditingModule,
38367 ContextPadModule$1,
38368 SelectionModule,
38369 ConnectModule,
38370 CreateModule,
38371 PopupMenuModule
38372 ],
38373 __init__: [ 'contextPadProvider' ],
38374 contextPadProvider: [ 'type', ContextPadProvider ]
38375 };
38376
38377 var AXIS_DIMENSIONS = {
38378 horizontal: [ 'x', 'width' ],
38379 vertical: [ 'y', 'height' ]
38380 };
38381
38382 var THRESHOLD = 5;
38383
38384
38385 /**
38386 * Groups and filters elements and then trigger even distribution.
38387 */
38388 function DistributeElements$1(modeling) {
38389 this._modeling = modeling;
38390
38391 this._filters = [];
38392
38393 // register filter for filtering big elements
38394 this.registerFilter(function(elements, axis, dimension) {
38395 var elementsSize = 0,
38396 numOfShapes = 0,
38397 avgDimension;
38398
38399 forEach$2(elements, function(element) {
38400 if (element.waypoints || element.labelTarget) {
38401 return;
38402 }
38403
38404 elementsSize += element[dimension];
38405
38406 numOfShapes += 1;
38407 });
38408
38409 avgDimension = Math.round(elementsSize / numOfShapes);
38410
38411 return filter(elements, function(element) {
38412 return element[dimension] < (avgDimension + 50);
38413 });
38414 });
38415
38416 }
38417
38418 DistributeElements$1.$inject = [ 'modeling' ];
38419
38420
38421 /**
38422 * Registers filter functions that allow external parties to filter
38423 * out certain elements.
38424 *
38425 * @param {Function} filterFn
38426 */
38427 DistributeElements$1.prototype.registerFilter = function(filterFn) {
38428 if (typeof filterFn !== 'function') {
38429 throw new Error('the filter has to be a function');
38430 }
38431
38432 this._filters.push(filterFn);
38433 };
38434
38435 /**
38436 * Distributes the elements with a given orientation
38437 *
38438 * @param {Array} elements
38439 * @param {string} orientation
38440 */
38441 DistributeElements$1.prototype.trigger = function(elements, orientation) {
38442 var modeling = this._modeling;
38443
38444 var groups,
38445 distributableElements;
38446
38447 if (elements.length < 3) {
38448 return;
38449 }
38450
38451 this._setOrientation(orientation);
38452
38453 distributableElements = this._filterElements(elements);
38454
38455 groups = this._createGroups(distributableElements);
38456
38457 // nothing to distribute
38458 if (groups.length <= 2) {
38459 return;
38460 }
38461
38462 modeling.distributeElements(groups, this._axis, this._dimension);
38463
38464 return groups;
38465 };
38466
38467 /**
38468 * Filters the elements with provided filters by external parties
38469 *
38470 * @param {Array[Elements]} elements
38471 *
38472 * @return {Array[Elements]}
38473 */
38474 DistributeElements$1.prototype._filterElements = function(elements) {
38475 var filters = this._filters,
38476 axis = this._axis,
38477 dimension = this._dimension,
38478 distributableElements = [].concat(elements);
38479
38480 if (!filters.length) {
38481 return elements;
38482 }
38483
38484 forEach$2(filters, function(filterFn) {
38485 distributableElements = filterFn(distributableElements, axis, dimension);
38486 });
38487
38488 return distributableElements;
38489 };
38490
38491
38492 /**
38493 * Create range (min, max) groups. Also tries to group elements
38494 * together that share the same range.
38495 *
38496 * @example
38497 * var distributableElements = [
38498 * {
38499 * range: {
38500 * min: 100,
38501 * max: 200
38502 * },
38503 * elements: [ { id: 'shape1', .. }]
38504 * }
38505 * ]
38506 *
38507 * @param {Array} elements
38508 *
38509 * @return {Array[Objects]}
38510 */
38511 DistributeElements$1.prototype._createGroups = function(elements) {
38512 var rangeGroups = [],
38513 self = this,
38514 axis = this._axis,
38515 dimension = this._dimension;
38516
38517 if (!axis) {
38518 throw new Error('must have a defined "axis" and "dimension"');
38519 }
38520
38521 // sort by 'left->right' or 'top->bottom'
38522 var sortedElements = sortBy(elements, axis);
38523
38524 forEach$2(sortedElements, function(element, idx) {
38525 var elementRange = self._findRange(element, axis, dimension),
38526 range;
38527
38528 var previous = rangeGroups[rangeGroups.length - 1];
38529
38530 if (previous && self._hasIntersection(previous.range, elementRange)) {
38531 rangeGroups[rangeGroups.length - 1].elements.push(element);
38532 } else {
38533 range = { range: elementRange, elements: [ element ] };
38534
38535 rangeGroups.push(range);
38536 }
38537 });
38538
38539 return rangeGroups;
38540 };
38541
38542
38543 /**
38544 * Maps a direction to the according axis and dimension
38545 *
38546 * @param {string} direction 'horizontal' or 'vertical'
38547 */
38548 DistributeElements$1.prototype._setOrientation = function(direction) {
38549 var orientation = AXIS_DIMENSIONS[direction];
38550
38551 this._axis = orientation[0];
38552 this._dimension = orientation[1];
38553 };
38554
38555
38556 /**
38557 * Checks if the two ranges intercept each other
38558 *
38559 * @param {Object} rangeA {min, max}
38560 * @param {Object} rangeB {min, max}
38561 *
38562 * @return {boolean}
38563 */
38564 DistributeElements$1.prototype._hasIntersection = function(rangeA, rangeB) {
38565 return Math.max(rangeA.min, rangeA.max) >= Math.min(rangeB.min, rangeB.max) &&
38566 Math.min(rangeA.min, rangeA.max) <= Math.max(rangeB.min, rangeB.max);
38567 };
38568
38569
38570 /**
38571 * Returns the min and max values for an element
38572 *
38573 * @param {Bounds} element
38574 * @param {string} axis
38575 * @param {string} dimension
38576 *
38577 * @return {{ min: number, max: number }}
38578 */
38579 DistributeElements$1.prototype._findRange = function(element) {
38580 var axis = element[this._axis],
38581 dimension = element[this._dimension];
38582
38583 return {
38584 min: axis + THRESHOLD,
38585 max: axis + dimension - THRESHOLD
38586 };
38587 };
38588
38589 var DistributeElementsModule$1 = {
38590 __init__: [ 'distributeElements' ],
38591 distributeElements: [ 'type', DistributeElements$1 ]
38592 };
38593
38594 /**
38595 * Registers element exclude filters for elements that
38596 * currently do not support distribution.
38597 */
38598 function BpmnDistributeElements(distributeElements) {
38599
38600 distributeElements.registerFilter(function(elements) {
38601 return filter(elements, function(element) {
38602 var cannotDistribute = isAny(element, [
38603 'bpmn:Association',
38604 'bpmn:BoundaryEvent',
38605 'bpmn:DataInputAssociation',
38606 'bpmn:DataOutputAssociation',
38607 'bpmn:Lane',
38608 'bpmn:MessageFlow',
38609 'bpmn:Participant',
38610 'bpmn:SequenceFlow',
38611 'bpmn:TextAnnotation'
38612 ]);
38613
38614 return !(element.labelTarget || cannotDistribute);
38615 });
38616 });
38617 }
38618
38619 BpmnDistributeElements.$inject = [ 'distributeElements' ];
38620
38621 var DistributeElementsModule = {
38622 __depends__: [
38623 DistributeElementsModule$1
38624 ],
38625 __init__: [ 'bpmnDistributeElements' ],
38626 bpmnDistributeElements: [ 'type', BpmnDistributeElements ]
38627 };
38628
38629 var NOT_REGISTERED_ERROR = 'is not a registered action',
38630 IS_REGISTERED_ERROR = 'is already registered';
38631
38632
38633 /**
38634 * An interface that provides access to modeling actions by decoupling
38635 * the one who requests the action to be triggered and the trigger itself.
38636 *
38637 * It's possible to add new actions by registering them with ´registerAction´
38638 * and likewise unregister existing ones with ´unregisterAction´.
38639 *
38640 *
38641 * ## Life-Cycle and configuration
38642 *
38643 * The editor actions will wait for diagram initialization before
38644 * registering default actions _and_ firing an `editorActions.init` event.
38645 *
38646 * Interested parties may listen to the `editorActions.init` event with
38647 * low priority to check, which actions got registered. Other components
38648 * may use the event to register their own actions via `registerAction`.
38649 *
38650 * @param {EventBus} eventBus
38651 * @param {Injector} injector
38652 */
38653 function EditorActions(eventBus, injector) {
38654
38655 // initialize actions
38656 this._actions = {};
38657
38658 var self = this;
38659
38660 eventBus.on('diagram.init', function() {
38661
38662 // all diagram modules got loaded; check which ones
38663 // are available and register the respective default actions
38664 self._registerDefaultActions(injector);
38665
38666 // ask interested parties to register available editor
38667 // actions on diagram initialization
38668 eventBus.fire('editorActions.init', {
38669 editorActions: self
38670 });
38671 });
38672
38673 }
38674
38675 EditorActions.$inject = [
38676 'eventBus',
38677 'injector'
38678 ];
38679
38680 /**
38681 * Register default actions.
38682 *
38683 * @param {Injector} injector
38684 */
38685 EditorActions.prototype._registerDefaultActions = function(injector) {
38686
38687 // (1) retrieve optional components to integrate with
38688
38689 var commandStack = injector.get('commandStack', false);
38690 var modeling = injector.get('modeling', false);
38691 var selection = injector.get('selection', false);
38692 var zoomScroll = injector.get('zoomScroll', false);
38693 var copyPaste = injector.get('copyPaste', false);
38694 var canvas = injector.get('canvas', false);
38695 var rules = injector.get('rules', false);
38696 var keyboardMove = injector.get('keyboardMove', false);
38697 var keyboardMoveSelection = injector.get('keyboardMoveSelection', false);
38698
38699 // (2) check components and register actions
38700
38701 if (commandStack) {
38702 this.register('undo', function() {
38703 commandStack.undo();
38704 });
38705
38706 this.register('redo', function() {
38707 commandStack.redo();
38708 });
38709 }
38710
38711 if (copyPaste && selection) {
38712 this.register('copy', function() {
38713 var selectedElements = selection.get();
38714
38715 copyPaste.copy(selectedElements);
38716 });
38717 }
38718
38719 if (copyPaste) {
38720 this.register('paste', function() {
38721 copyPaste.paste();
38722 });
38723 }
38724
38725 if (zoomScroll) {
38726 this.register('stepZoom', function(opts) {
38727 zoomScroll.stepZoom(opts.value);
38728 });
38729 }
38730
38731 if (canvas) {
38732 this.register('zoom', function(opts) {
38733 canvas.zoom(opts.value);
38734 });
38735 }
38736
38737 if (modeling && selection && rules) {
38738 this.register('removeSelection', function() {
38739
38740 var selectedElements = selection.get();
38741
38742 if (!selectedElements.length) {
38743 return;
38744 }
38745
38746 var allowed = rules.allowed('elements.delete', { elements: selectedElements }),
38747 removableElements;
38748
38749 if (allowed === false) {
38750 return;
38751 }
38752 else if (isArray$4(allowed)) {
38753 removableElements = allowed;
38754 }
38755 else {
38756 removableElements = selectedElements;
38757 }
38758
38759 if (removableElements.length) {
38760 modeling.removeElements(removableElements.slice());
38761 }
38762 });
38763 }
38764
38765 if (keyboardMove) {
38766 this.register('moveCanvas', function(opts) {
38767 keyboardMove.moveCanvas(opts);
38768 });
38769 }
38770
38771 if (keyboardMoveSelection) {
38772 this.register('moveSelection', function(opts) {
38773 keyboardMoveSelection.moveSelection(opts.direction, opts.accelerated);
38774 });
38775 }
38776
38777 };
38778
38779
38780 /**
38781 * Triggers a registered action
38782 *
38783 * @param {string} action
38784 * @param {Object} opts
38785 *
38786 * @return {Unknown} Returns what the registered listener returns
38787 */
38788 EditorActions.prototype.trigger = function(action, opts) {
38789 if (!this._actions[action]) {
38790 throw error(action, NOT_REGISTERED_ERROR);
38791 }
38792
38793 return this._actions[action](opts);
38794 };
38795
38796
38797 /**
38798 * Registers a collections of actions.
38799 * The key of the object will be the name of the action.
38800 *
38801 * @example
38802 * ´´´
38803 * var actions = {
38804 * spaceTool: function() {
38805 * spaceTool.activateSelection();
38806 * },
38807 * lassoTool: function() {
38808 * lassoTool.activateSelection();
38809 * }
38810 * ];
38811 *
38812 * editorActions.register(actions);
38813 *
38814 * editorActions.isRegistered('spaceTool'); // true
38815 * ´´´
38816 *
38817 * @param {Object} actions
38818 */
38819 EditorActions.prototype.register = function(actions, listener) {
38820 var self = this;
38821
38822 if (typeof actions === 'string') {
38823 return this._registerAction(actions, listener);
38824 }
38825
38826 forEach$2(actions, function(listener, action) {
38827 self._registerAction(action, listener);
38828 });
38829 };
38830
38831 /**
38832 * Registers a listener to an action key
38833 *
38834 * @param {string} action
38835 * @param {Function} listener
38836 */
38837 EditorActions.prototype._registerAction = function(action, listener) {
38838 if (this.isRegistered(action)) {
38839 throw error(action, IS_REGISTERED_ERROR);
38840 }
38841
38842 this._actions[action] = listener;
38843 };
38844
38845 /**
38846 * Unregister an existing action
38847 *
38848 * @param {string} action
38849 */
38850 EditorActions.prototype.unregister = function(action) {
38851 if (!this.isRegistered(action)) {
38852 throw error(action, NOT_REGISTERED_ERROR);
38853 }
38854
38855 this._actions[action] = undefined;
38856 };
38857
38858 /**
38859 * Returns the number of actions that are currently registered
38860 *
38861 * @return {number}
38862 */
38863 EditorActions.prototype.getActions = function() {
38864 return Object.keys(this._actions);
38865 };
38866
38867 /**
38868 * Checks wether the given action is registered
38869 *
38870 * @param {string} action
38871 *
38872 * @return {boolean}
38873 */
38874 EditorActions.prototype.isRegistered = function(action) {
38875 return !!this._actions[action];
38876 };
38877
38878
38879 function error(action, message) {
38880 return new Error(action + ' ' + message);
38881 }
38882
38883 var EditorActionsModule$1 = {
38884 __init__: [ 'editorActions' ],
38885 editorActions: [ 'type', EditorActions ]
38886 };
38887
38888 /**
38889 * Registers and executes BPMN specific editor actions.
38890 *
38891 * @param {Injector} injector
38892 */
38893 function BpmnEditorActions(injector) {
38894 injector.invoke(EditorActions, this);
38895 }
38896
38897 inherits$1(BpmnEditorActions, EditorActions);
38898
38899 BpmnEditorActions.$inject = [
38900 'injector'
38901 ];
38902
38903 /**
38904 * Register default actions.
38905 *
38906 * @param {Injector} injector
38907 */
38908 BpmnEditorActions.prototype._registerDefaultActions = function(injector) {
38909
38910 // (0) invoke super method
38911
38912 EditorActions.prototype._registerDefaultActions.call(this, injector);
38913
38914 // (1) retrieve optional components to integrate with
38915
38916 var canvas = injector.get('canvas', false);
38917 var elementRegistry = injector.get('elementRegistry', false);
38918 var selection = injector.get('selection', false);
38919 var spaceTool = injector.get('spaceTool', false);
38920 var lassoTool = injector.get('lassoTool', false);
38921 var handTool = injector.get('handTool', false);
38922 var globalConnect = injector.get('globalConnect', false);
38923 var distributeElements = injector.get('distributeElements', false);
38924 var alignElements = injector.get('alignElements', false);
38925 var directEditing = injector.get('directEditing', false);
38926 var searchPad = injector.get('searchPad', false);
38927 var modeling = injector.get('modeling', false);
38928
38929 // (2) check components and register actions
38930
38931 if (canvas && elementRegistry && selection) {
38932 this._registerAction('selectElements', function() {
38933
38934 // select all elements except for the invisible
38935 // root element
38936 var rootElement = canvas.getRootElement();
38937
38938 var elements = elementRegistry.filter(function(element) {
38939 return element !== rootElement;
38940 });
38941
38942 selection.select(elements);
38943
38944 return elements;
38945 });
38946 }
38947
38948 if (spaceTool) {
38949 this._registerAction('spaceTool', function() {
38950 spaceTool.toggle();
38951 });
38952 }
38953
38954 if (lassoTool) {
38955 this._registerAction('lassoTool', function() {
38956 lassoTool.toggle();
38957 });
38958 }
38959
38960 if (handTool) {
38961 this._registerAction('handTool', function() {
38962 handTool.toggle();
38963 });
38964 }
38965
38966 if (globalConnect) {
38967 this._registerAction('globalConnectTool', function() {
38968 globalConnect.toggle();
38969 });
38970 }
38971
38972 if (selection && distributeElements) {
38973 this._registerAction('distributeElements', function(opts) {
38974 var currentSelection = selection.get(),
38975 type = opts.type;
38976
38977 if (currentSelection.length) {
38978 distributeElements.trigger(currentSelection, type);
38979 }
38980 });
38981 }
38982
38983 if (selection && alignElements) {
38984 this._registerAction('alignElements', function(opts) {
38985 var currentSelection = selection.get(),
38986 aligneableElements = [],
38987 type = opts.type;
38988
38989 if (currentSelection.length) {
38990 aligneableElements = filter(currentSelection, function(element) {
38991 return !is$1(element, 'bpmn:Lane');
38992 });
38993
38994 alignElements.trigger(aligneableElements, type);
38995 }
38996 });
38997 }
38998
38999 if (selection && modeling) {
39000 this._registerAction('setColor', function(opts) {
39001 var currentSelection = selection.get();
39002
39003 if (currentSelection.length) {
39004 modeling.setColor(currentSelection, opts);
39005 }
39006 });
39007 }
39008
39009 if (selection && directEditing) {
39010 this._registerAction('directEditing', function() {
39011 var currentSelection = selection.get();
39012
39013 if (currentSelection.length) {
39014 directEditing.activate(currentSelection[0]);
39015 }
39016 });
39017 }
39018
39019 if (searchPad) {
39020 this._registerAction('find', function() {
39021 searchPad.toggle();
39022 });
39023 }
39024
39025 if (canvas && modeling) {
39026 this._registerAction('moveToOrigin', function() {
39027 var rootElement = canvas.getRootElement(),
39028 boundingBox,
39029 elements;
39030
39031 if (is$1(rootElement, 'bpmn:Collaboration')) {
39032 elements = elementRegistry.filter(function(element) {
39033 return is$1(element.parent, 'bpmn:Collaboration');
39034 });
39035 } else {
39036 elements = elementRegistry.filter(function(element) {
39037 return element !== rootElement && !is$1(element.parent, 'bpmn:SubProcess');
39038 });
39039 }
39040
39041 boundingBox = getBBox(elements);
39042
39043 modeling.moveElements(
39044 elements,
39045 { x: -boundingBox.x, y: -boundingBox.y },
39046 rootElement
39047 );
39048 });
39049 }
39050
39051 };
39052
39053 var EditorActionsModule = {
39054 __depends__: [
39055 EditorActionsModule$1
39056 ],
39057 editorActions: [ 'type', BpmnEditorActions ]
39058 };
39059
39060 function BpmnGridSnapping(eventBus) {
39061 eventBus.on([
39062 'create.init',
39063 'shape.move.init'
39064 ], function(event) {
39065 var context = event.context,
39066 shape = event.shape;
39067
39068 if (isAny(shape, [
39069 'bpmn:Participant',
39070 'bpmn:SubProcess',
39071 'bpmn:TextAnnotation'
39072 ])) {
39073 if (!context.gridSnappingContext) {
39074 context.gridSnappingContext = {};
39075 }
39076
39077 context.gridSnappingContext.snapLocation = 'top-left';
39078 }
39079 });
39080 }
39081
39082 BpmnGridSnapping.$inject = [ 'eventBus' ];
39083
39084 var SPACING = 10;
39085
39086 function quantize(value, quantum, fn) {
39087 if (!fn) {
39088 fn = 'round';
39089 }
39090
39091 return Math[ fn ](value / quantum) * quantum;
39092 }
39093
39094 var LOWER_PRIORITY = 1200;
39095 var LOW_PRIORITY$f = 800;
39096
39097 /**
39098 * Basic grid snapping that covers connecting, creating, moving, resizing shapes, moving bendpoints
39099 * and connection segments.
39100 */
39101 function GridSnapping(elementRegistry, eventBus, config) {
39102
39103 var active = !config || config.active !== false;
39104
39105 this._eventBus = eventBus;
39106
39107 var self = this;
39108
39109 eventBus.on('diagram.init', LOW_PRIORITY$f, function() {
39110 self.setActive(active);
39111 });
39112
39113 eventBus.on([
39114 'create.move',
39115 'create.end',
39116 'bendpoint.move.move',
39117 'bendpoint.move.end',
39118 'connect.move',
39119 'connect.end',
39120 'connectionSegment.move.move',
39121 'connectionSegment.move.end',
39122 'resize.move',
39123 'resize.end',
39124 'shape.move.move',
39125 'shape.move.end'
39126 ], LOWER_PRIORITY, function(event) {
39127 var originalEvent = event.originalEvent;
39128
39129 if (!self.active || (originalEvent && isCmd(originalEvent))) {
39130 return;
39131 }
39132
39133 var context = event.context,
39134 gridSnappingContext = context.gridSnappingContext;
39135
39136 if (!gridSnappingContext) {
39137 gridSnappingContext = context.gridSnappingContext = {};
39138 }
39139
39140 [ 'x', 'y' ].forEach(function(axis) {
39141 var options = {};
39142
39143 // allow snapping with offset
39144 var snapOffset = getSnapOffset(event, axis, elementRegistry);
39145
39146 if (snapOffset) {
39147 options.offset = snapOffset;
39148 }
39149
39150 // allow snapping with min and max
39151 var snapConstraints = getSnapConstraints(event, axis);
39152
39153 if (snapConstraints) {
39154 assign(options, snapConstraints);
39155 }
39156
39157 if (!isSnapped(event, axis)) {
39158 self.snapEvent(event, axis, options);
39159 }
39160 });
39161 });
39162 }
39163
39164 /**
39165 * Snap an events x or y with optional min, max and offset.
39166 *
39167 * @param {Object} event
39168 * @param {string} axis
39169 * @param {number} [options.min]
39170 * @param {number} [options.max]
39171 * @param {number} [options.offset]
39172 */
39173 GridSnapping.prototype.snapEvent = function(event, axis, options) {
39174 var snappedValue = this.snapValue(event[ axis ], options);
39175
39176 setSnapped(event, axis, snappedValue);
39177 };
39178
39179 /**
39180 * Expose grid spacing for third parties (i.e. extensions).
39181 *
39182 * @return {number} spacing of grid dots
39183 */
39184 GridSnapping.prototype.getGridSpacing = function() {
39185 return SPACING;
39186 };
39187
39188 /**
39189 * Snap value with optional min, max and offset.
39190 *
39191 * @param {number} value
39192 * @param {Object} options
39193 * @param {number} [options.min]
39194 * @param {number} [options.max]
39195 * @param {number} [options.offset]
39196 */
39197 GridSnapping.prototype.snapValue = function(value, options) {
39198 var offset = 0;
39199
39200 if (options && options.offset) {
39201 offset = options.offset;
39202 }
39203
39204 value += offset;
39205
39206 value = quantize(value, SPACING);
39207
39208 var min, max;
39209
39210 if (options && options.min) {
39211 min = options.min;
39212
39213 if (isNumber(min)) {
39214 min = quantize(min + offset, SPACING, 'ceil');
39215
39216 value = Math.max(value, min);
39217 }
39218 }
39219
39220 if (options && options.max) {
39221 max = options.max;
39222
39223 if (isNumber(max)) {
39224 max = quantize(max + offset, SPACING, 'floor');
39225
39226 value = Math.min(value, max);
39227 }
39228 }
39229
39230 value -= offset;
39231
39232 return value;
39233 };
39234
39235 GridSnapping.prototype.isActive = function() {
39236 return this.active;
39237 };
39238
39239 GridSnapping.prototype.setActive = function(active) {
39240 this.active = active;
39241
39242 this._eventBus.fire('gridSnapping.toggle', { active: active });
39243 };
39244
39245 GridSnapping.prototype.toggleActive = function() {
39246 this.setActive(!this.active);
39247 };
39248
39249 GridSnapping.$inject = [
39250 'elementRegistry',
39251 'eventBus',
39252 'config.gridSnapping'
39253 ];
39254
39255 // helpers //////////
39256
39257 /**
39258 * Get minimum and maximum snap constraints.
39259 * Constraints are cached.
39260 *
39261 * @param {Object} event
39262 * @param {Object} event.context
39263 * @param {string} axis
39264 *
39265 * @returns {boolean|Object}
39266 */
39267 function getSnapConstraints(event, axis) {
39268 var context = event.context,
39269 createConstraints = context.createConstraints,
39270 resizeConstraints = context.resizeConstraints || {},
39271 gridSnappingContext = context.gridSnappingContext,
39272 snapConstraints = gridSnappingContext.snapConstraints;
39273
39274 // cache snap constraints
39275 if (snapConstraints && snapConstraints[ axis ]) {
39276 return snapConstraints[ axis ];
39277 }
39278
39279 if (!snapConstraints) {
39280 snapConstraints = gridSnappingContext.snapConstraints = {};
39281 }
39282
39283 if (!snapConstraints[ axis ]) {
39284 snapConstraints[ axis ] = {};
39285 }
39286
39287 var direction = context.direction;
39288
39289 // create
39290 if (createConstraints) {
39291 if (isHorizontal$3(axis)) {
39292 snapConstraints.x.min = createConstraints.left;
39293 snapConstraints.x.max = createConstraints.right;
39294 } else {
39295 snapConstraints.y.min = createConstraints.top;
39296 snapConstraints.y.max = createConstraints.bottom;
39297 }
39298 }
39299
39300 // resize
39301 var minResizeConstraints = resizeConstraints.min,
39302 maxResizeConstraints = resizeConstraints.max;
39303
39304 if (minResizeConstraints) {
39305 if (isHorizontal$3(axis)) {
39306
39307 if (isWest(direction)) {
39308 snapConstraints.x.max = minResizeConstraints.left;
39309 } else {
39310 snapConstraints.x.min = minResizeConstraints.right;
39311 }
39312
39313 } else {
39314
39315 if (isNorth(direction)) {
39316 snapConstraints.y.max = minResizeConstraints.top;
39317 } else {
39318 snapConstraints.y.min = minResizeConstraints.bottom;
39319 }
39320
39321 }
39322 }
39323
39324 if (maxResizeConstraints) {
39325 if (isHorizontal$3(axis)) {
39326
39327 if (isWest(direction)) {
39328 snapConstraints.x.min = maxResizeConstraints.left;
39329 } else {
39330 snapConstraints.x.max = maxResizeConstraints.right;
39331 }
39332
39333 } else {
39334
39335 if (isNorth(direction)) {
39336 snapConstraints.y.min = maxResizeConstraints.top;
39337 } else {
39338 snapConstraints.y.max = maxResizeConstraints.bottom;
39339 }
39340
39341 }
39342 }
39343
39344 return snapConstraints[ axis ];
39345 }
39346
39347 /**
39348 * Get snap offset.
39349 * Offset is cached.
39350 *
39351 * @param {Object} event
39352 * @param {string} axis
39353 * @param {ElementRegistry} elementRegistry
39354 *
39355 * @returns {number}
39356 */
39357 function getSnapOffset(event, axis, elementRegistry) {
39358 var context = event.context,
39359 shape = event.shape,
39360 gridSnappingContext = context.gridSnappingContext,
39361 snapLocation = gridSnappingContext.snapLocation,
39362 snapOffset = gridSnappingContext.snapOffset;
39363
39364 // cache snap offset
39365 if (snapOffset && isNumber(snapOffset[ axis ])) {
39366 return snapOffset[ axis ];
39367 }
39368
39369 if (!snapOffset) {
39370 snapOffset = gridSnappingContext.snapOffset = {};
39371 }
39372
39373 if (!isNumber(snapOffset[ axis ])) {
39374 snapOffset[ axis ] = 0;
39375 }
39376
39377 if (!shape) {
39378 return snapOffset[ axis ];
39379 }
39380
39381 if (!elementRegistry.get(shape.id)) {
39382
39383 if (isHorizontal$3(axis)) {
39384 snapOffset[ axis ] += shape[ axis ] + shape.width / 2;
39385 } else {
39386 snapOffset[ axis ] += shape[ axis ] + shape.height / 2;
39387 }
39388 }
39389
39390 if (!snapLocation) {
39391 return snapOffset[ axis ];
39392 }
39393
39394 if (axis === 'x') {
39395 if (/left/.test(snapLocation)) {
39396 snapOffset[ axis ] -= shape.width / 2;
39397 } else if (/right/.test(snapLocation)) {
39398 snapOffset[ axis ] += shape.width / 2;
39399 }
39400 } else {
39401 if (/top/.test(snapLocation)) {
39402 snapOffset[ axis ] -= shape.height / 2;
39403 } else if (/bottom/.test(snapLocation)) {
39404 snapOffset[ axis ] += shape.height / 2;
39405 }
39406 }
39407
39408 return snapOffset[ axis ];
39409 }
39410
39411 function isHorizontal$3(axis) {
39412 return axis === 'x';
39413 }
39414
39415 function isNorth(direction) {
39416 return direction.indexOf('n') !== -1;
39417 }
39418
39419 function isWest(direction) {
39420 return direction.indexOf('w') !== -1;
39421 }
39422
39423 /**
39424 * Integrates resizing with grid snapping.
39425 */
39426 function ResizeBehavior$1(eventBus, gridSnapping) {
39427 CommandInterceptor.call(this, eventBus);
39428
39429 this._gridSnapping = gridSnapping;
39430
39431 var self = this;
39432
39433 this.preExecute('shape.resize', function(event) {
39434 var context = event.context,
39435 hints = context.hints || {},
39436 autoResize = hints.autoResize;
39437
39438 if (!autoResize) {
39439 return;
39440 }
39441
39442 var shape = context.shape,
39443 newBounds = context.newBounds;
39444
39445 if (isString(autoResize)) {
39446 context.newBounds = self.snapComplex(newBounds, autoResize);
39447 } else {
39448 context.newBounds = self.snapSimple(shape, newBounds);
39449 }
39450 });
39451 }
39452
39453 ResizeBehavior$1.$inject = [
39454 'eventBus',
39455 'gridSnapping',
39456 'modeling'
39457 ];
39458
39459 inherits$1(ResizeBehavior$1, CommandInterceptor);
39460
39461 /**
39462 * Snap width and height in relation to center.
39463 *
39464 * @param {djs.model.shape} shape
39465 * @param {Bounds} newBounds
39466 *
39467 * @returns {Bounds} Snapped bounds.
39468 */
39469 ResizeBehavior$1.prototype.snapSimple = function(shape, newBounds) {
39470 var gridSnapping = this._gridSnapping;
39471
39472 newBounds.width = gridSnapping.snapValue(newBounds.width, {
39473 min: newBounds.width
39474 });
39475
39476 newBounds.height = gridSnapping.snapValue(newBounds.height, {
39477 min: newBounds.height
39478 });
39479
39480 newBounds.x = shape.x + (shape.width / 2) - (newBounds.width / 2);
39481 newBounds.y = shape.y + (shape.height / 2) - (newBounds.height / 2);
39482
39483 return newBounds;
39484 };
39485
39486 /**
39487 * Snap x, y, width and height according to given directions.
39488 *
39489 * @param {Bounds} newBounds
39490 * @param {string} directions - Directions as {n|w|s|e}.
39491 *
39492 * @returns {Bounds} Snapped bounds.
39493 */
39494 ResizeBehavior$1.prototype.snapComplex = function(newBounds, directions) {
39495 if (/w|e/.test(directions)) {
39496 newBounds = this.snapHorizontally(newBounds, directions);
39497 }
39498
39499 if (/n|s/.test(directions)) {
39500 newBounds = this.snapVertically(newBounds, directions);
39501 }
39502
39503 return newBounds;
39504 };
39505
39506 /**
39507 * Snap in one or both directions horizontally.
39508 *
39509 * @param {Bounds} newBounds
39510 * @param {string} directions - Directions as {n|w|s|e}.
39511 *
39512 * @returns {Bounds} Snapped bounds.
39513 */
39514 ResizeBehavior$1.prototype.snapHorizontally = function(newBounds, directions) {
39515 var gridSnapping = this._gridSnapping,
39516 west = /w/.test(directions),
39517 east = /e/.test(directions);
39518
39519 var snappedNewBounds = {};
39520
39521 snappedNewBounds.width = gridSnapping.snapValue(newBounds.width, {
39522 min: newBounds.width
39523 });
39524
39525 if (east) {
39526
39527 // handle <we>
39528 if (west) {
39529 snappedNewBounds.x = gridSnapping.snapValue(newBounds.x, {
39530 max: newBounds.x
39531 });
39532
39533 snappedNewBounds.width += gridSnapping.snapValue(newBounds.x - snappedNewBounds.x, {
39534 min: newBounds.x - snappedNewBounds.x
39535 });
39536 }
39537
39538 // handle <e>
39539 else {
39540 newBounds.x = newBounds.x + newBounds.width - snappedNewBounds.width;
39541 }
39542 }
39543
39544 // assign snapped x and width
39545 assign(newBounds, snappedNewBounds);
39546
39547 return newBounds;
39548 };
39549
39550 /**
39551 * Snap in one or both directions vertically.
39552 *
39553 * @param {Bounds} newBounds
39554 * @param {string} directions - Directions as {n|w|s|e}.
39555 *
39556 * @returns {Bounds} Snapped bounds.
39557 */
39558 ResizeBehavior$1.prototype.snapVertically = function(newBounds, directions) {
39559 var gridSnapping = this._gridSnapping,
39560 north = /n/.test(directions),
39561 south = /s/.test(directions);
39562
39563 var snappedNewBounds = {};
39564
39565 snappedNewBounds.height = gridSnapping.snapValue(newBounds.height, {
39566 min: newBounds.height
39567 });
39568
39569 if (north) {
39570
39571 // handle <ns>
39572 if (south) {
39573 snappedNewBounds.y = gridSnapping.snapValue(newBounds.y, {
39574 max: newBounds.y
39575 });
39576
39577 snappedNewBounds.height += gridSnapping.snapValue(newBounds.y - snappedNewBounds.y, {
39578 min: newBounds.y - snappedNewBounds.y
39579 });
39580 }
39581
39582 // handle <n>
39583 else {
39584 newBounds.y = newBounds.y + newBounds.height - snappedNewBounds.height;
39585 }
39586 }
39587
39588 // assign snapped y and height
39589 assign(newBounds, snappedNewBounds);
39590
39591 return newBounds;
39592 };
39593
39594 var HIGH_PRIORITY$g = 2000;
39595
39596 /**
39597 * Integrates space tool with grid snapping.
39598 */
39599 function SpaceToolBehavior$1(eventBus, gridSnapping) {
39600 eventBus.on([
39601 'spaceTool.move',
39602 'spaceTool.end'
39603 ], HIGH_PRIORITY$g, function(event) {
39604 var context = event.context;
39605
39606 if (!context.initialized) {
39607 return;
39608 }
39609
39610 var axis = context.axis;
39611
39612 var snapped;
39613
39614 if (axis === 'x') {
39615
39616 // snap delta x to multiple of 10
39617 snapped = gridSnapping.snapValue(event.dx);
39618
39619 event.x = event.x + snapped - event.dx;
39620 event.dx = snapped;
39621 } else {
39622
39623 // snap delta y to multiple of 10
39624 snapped = gridSnapping.snapValue(event.dy);
39625
39626 event.y = event.y + snapped - event.dy;
39627 event.dy = snapped;
39628 }
39629 });
39630 }
39631
39632 SpaceToolBehavior$1.$inject = [
39633 'eventBus',
39634 'gridSnapping'
39635 ];
39636
39637 var GridSnappingBehaviorModule$1 = {
39638 __init__: [
39639 'gridSnappingResizeBehavior',
39640 'gridSnappingSpaceToolBehavior'
39641 ],
39642 gridSnappingResizeBehavior: [ 'type', ResizeBehavior$1 ],
39643 gridSnappingSpaceToolBehavior: [ 'type', SpaceToolBehavior$1 ]
39644 };
39645
39646 var GridSnappingModule$1 = {
39647 __depends__: [ GridSnappingBehaviorModule$1 ],
39648 __init__: [ 'gridSnapping' ],
39649 gridSnapping: [ 'type', GridSnapping ]
39650 };
39651
39652 var HIGH_PRIORITY$f = 2000;
39653
39654
39655 function AutoPlaceBehavior(eventBus, gridSnapping) {
39656 eventBus.on('autoPlace', HIGH_PRIORITY$f, function(context) {
39657 var source = context.source,
39658 sourceMid = getMid(source),
39659 shape = context.shape;
39660
39661 var position = getNewShapePosition(source, shape);
39662
39663 [ 'x', 'y' ].forEach(function(axis) {
39664 var options = {};
39665
39666 // do not snap if x/y equal
39667 if (position[ axis ] === sourceMid[ axis ]) {
39668 return;
39669 }
39670
39671 if (position[ axis ] > sourceMid[ axis ]) {
39672 options.min = position[ axis ];
39673 } else {
39674 options.max = position[ axis ];
39675 }
39676
39677 if (is$1(shape, 'bpmn:TextAnnotation')) {
39678
39679 if (isHorizontal$2(axis)) {
39680 options.offset = -shape.width / 2;
39681 } else {
39682 options.offset = -shape.height / 2;
39683 }
39684
39685 }
39686
39687 position[ axis ] = gridSnapping.snapValue(position[ axis ], options);
39688
39689 });
39690
39691 // must be returned to be considered by auto place
39692 return position;
39693 });
39694 }
39695
39696 AutoPlaceBehavior.$inject = [
39697 'eventBus',
39698 'gridSnapping'
39699 ];
39700
39701 // helpers //////////
39702
39703 function isHorizontal$2(axis) {
39704 return axis === 'x';
39705 }
39706
39707 var HIGHER_PRIORITY$4 = 1750;
39708
39709
39710 function CreateParticipantBehavior$1(canvas, eventBus, gridSnapping) {
39711 eventBus.on([
39712 'create.start',
39713 'shape.move.start'
39714 ], HIGHER_PRIORITY$4, function(event) {
39715 var context = event.context,
39716 shape = context.shape,
39717 rootElement = canvas.getRootElement();
39718
39719 if (!is$1(shape, 'bpmn:Participant') ||
39720 !is$1(rootElement, 'bpmn:Process') ||
39721 !rootElement.children.length) {
39722 return;
39723 }
39724
39725 var createConstraints = context.createConstraints;
39726
39727 if (!createConstraints) {
39728 return;
39729 }
39730
39731 shape.width = gridSnapping.snapValue(shape.width, { min: shape.width });
39732 shape.height = gridSnapping.snapValue(shape.height, { min: shape.height });
39733 });
39734 }
39735
39736 CreateParticipantBehavior$1.$inject = [
39737 'canvas',
39738 'eventBus',
39739 'gridSnapping'
39740 ];
39741
39742 var HIGH_PRIORITY$e = 3000;
39743
39744
39745 /**
39746 * Snaps connections with Manhattan layout.
39747 */
39748 function LayoutConnectionBehavior(eventBus, gridSnapping, modeling) {
39749 CommandInterceptor.call(this, eventBus);
39750
39751 this._gridSnapping = gridSnapping;
39752
39753 var self = this;
39754
39755 this.postExecuted([
39756 'connection.create',
39757 'connection.layout'
39758 ], HIGH_PRIORITY$e, function(event) {
39759 var context = event.context,
39760 connection = context.connection,
39761 hints = context.hints || {},
39762 waypoints = connection.waypoints;
39763
39764 if (hints.connectionStart || hints.connectionEnd || hints.createElementsBehavior === false) {
39765 return;
39766 }
39767
39768 if (!hasMiddleSegments(waypoints)) {
39769 return;
39770 }
39771
39772 modeling.updateWaypoints(connection, self.snapMiddleSegments(waypoints));
39773 });
39774 }
39775
39776 LayoutConnectionBehavior.$inject = [
39777 'eventBus',
39778 'gridSnapping',
39779 'modeling'
39780 ];
39781
39782 inherits$1(LayoutConnectionBehavior, CommandInterceptor);
39783
39784 /**
39785 * Snap middle segments of a given connection.
39786 *
39787 * @param {Array<Point>} waypoints
39788 *
39789 * @returns {Array<Point>}
39790 */
39791 LayoutConnectionBehavior.prototype.snapMiddleSegments = function(waypoints) {
39792 var gridSnapping = this._gridSnapping,
39793 snapped;
39794
39795 waypoints = waypoints.slice();
39796
39797 for (var i = 1; i < waypoints.length - 2; i++) {
39798
39799 snapped = snapSegment(gridSnapping, waypoints[i], waypoints[i + 1]);
39800
39801 waypoints[i] = snapped[0];
39802 waypoints[i + 1] = snapped[1];
39803 }
39804
39805 return waypoints;
39806 };
39807
39808
39809 // helpers //////////
39810
39811 /**
39812 * Check whether a connection has a middle segments.
39813 *
39814 * @param {Array} waypoints
39815 *
39816 * @returns {boolean}
39817 */
39818 function hasMiddleSegments(waypoints) {
39819 return waypoints.length > 3;
39820 }
39821
39822 /**
39823 * Check whether an alignment is horizontal.
39824 *
39825 * @param {string} aligned
39826 *
39827 * @returns {boolean}
39828 */
39829 function horizontallyAligned(aligned) {
39830 return aligned === 'h';
39831 }
39832
39833 /**
39834 * Check whether an alignment is vertical.
39835 *
39836 * @param {string} aligned
39837 *
39838 * @returns {boolean}
39839 */
39840 function verticallyAligned(aligned) {
39841 return aligned === 'v';
39842 }
39843
39844 /**
39845 * Get middle segments from a given connection.
39846 *
39847 * @param {Array} waypoints
39848 *
39849 * @returns {Array}
39850 */
39851 function snapSegment(gridSnapping, segmentStart, segmentEnd) {
39852
39853 var aligned = pointsAligned(segmentStart, segmentEnd);
39854
39855 var snapped = {};
39856
39857 if (horizontallyAligned(aligned)) {
39858
39859 // snap horizontally
39860 snapped.y = gridSnapping.snapValue(segmentStart.y);
39861 }
39862
39863 if (verticallyAligned(aligned)) {
39864
39865 // snap vertically
39866 snapped.x = gridSnapping.snapValue(segmentStart.x);
39867 }
39868
39869 if ('x' in snapped || 'y' in snapped) {
39870 segmentStart = assign({}, segmentStart, snapped);
39871 segmentEnd = assign({}, segmentEnd, snapped);
39872 }
39873
39874 return [ segmentStart, segmentEnd ];
39875 }
39876
39877 var GridSnappingBehaviorModule = {
39878 __init__: [
39879 'gridSnappingAutoPlaceBehavior',
39880 'gridSnappingCreateParticipantBehavior',
39881 'gridSnappingLayoutConnectionBehavior',
39882 ],
39883 gridSnappingAutoPlaceBehavior: [ 'type', AutoPlaceBehavior ],
39884 gridSnappingCreateParticipantBehavior: [ 'type', CreateParticipantBehavior$1 ],
39885 gridSnappingLayoutConnectionBehavior: [ 'type', LayoutConnectionBehavior ]
39886 };
39887
39888 var GridSnappingModule = {
39889 __depends__: [
39890 GridSnappingModule$1,
39891 GridSnappingBehaviorModule
39892 ],
39893 __init__: [ 'bpmnGridSnapping' ],
39894 bpmnGridSnapping: [ 'type', BpmnGridSnapping ]
39895 };
39896
39897 var LABEL_WIDTH = 30,
39898 LABEL_HEIGHT = 30;
39899
39900
39901 /**
39902 * BPMN-specific hit zones and interaction fixes.
39903 *
39904 * @param {EventBus} eventBus
39905 * @param {InteractionEvents} interactionEvents
39906 */
39907 function BpmnInteractionEvents(eventBus, interactionEvents) {
39908
39909 this._interactionEvents = interactionEvents;
39910
39911 var self = this;
39912
39913 eventBus.on([
39914 'interactionEvents.createHit',
39915 'interactionEvents.updateHit'
39916 ], function(context) {
39917 var element = context.element,
39918 gfx = context.gfx;
39919
39920 if (is$1(element, 'bpmn:Lane')) {
39921 return self.createParticipantHit(element, gfx);
39922 } else
39923
39924 if (is$1(element, 'bpmn:Participant')) {
39925 if (isExpanded(element)) {
39926 return self.createParticipantHit(element, gfx);
39927 } else {
39928 return self.createDefaultHit(element, gfx);
39929 }
39930 } else
39931
39932 if (is$1(element, 'bpmn:SubProcess')) {
39933 if (isExpanded(element)) {
39934 return self.createSubProcessHit(element, gfx);
39935 } else {
39936 return self.createDefaultHit(element, gfx);
39937 }
39938 }
39939 });
39940
39941 }
39942
39943 BpmnInteractionEvents.$inject = [
39944 'eventBus',
39945 'interactionEvents'
39946 ];
39947
39948
39949 BpmnInteractionEvents.prototype.createDefaultHit = function(element, gfx) {
39950 this._interactionEvents.removeHits(gfx);
39951
39952 this._interactionEvents.createDefaultHit(element, gfx);
39953
39954 // indicate that we created a hit
39955 return true;
39956 };
39957
39958 BpmnInteractionEvents.prototype.createParticipantHit = function(element, gfx) {
39959
39960 // remove existing hits
39961 this._interactionEvents.removeHits(gfx);
39962
39963 // add body hit
39964 this._interactionEvents.createBoxHit(gfx, 'no-move', {
39965 width: element.width,
39966 height: element.height
39967 });
39968
39969 // add outline hit
39970 this._interactionEvents.createBoxHit(gfx, 'click-stroke', {
39971 width: element.width,
39972 height: element.height
39973 });
39974
39975 // add label hit
39976 this._interactionEvents.createBoxHit(gfx, 'all', {
39977 width: LABEL_WIDTH,
39978 height: element.height
39979 });
39980
39981 // indicate that we created a hit
39982 return true;
39983 };
39984
39985 BpmnInteractionEvents.prototype.createSubProcessHit = function(element, gfx) {
39986
39987 // remove existing hits
39988 this._interactionEvents.removeHits(gfx);
39989
39990 // add body hit
39991 this._interactionEvents.createBoxHit(gfx, 'no-move', {
39992 width: element.width,
39993 height: element.height
39994 });
39995
39996 // add outline hit
39997 this._interactionEvents.createBoxHit(gfx, 'click-stroke', {
39998 width: element.width,
39999 height: element.height
40000 });
40001
40002 // add label hit
40003 this._interactionEvents.createBoxHit(gfx, 'all', {
40004 width: element.width,
40005 height: LABEL_HEIGHT
40006 });
40007
40008 // indicate that we created a hit
40009 return true;
40010 };
40011
40012 var InteractionEventsModule = {
40013 __init__: [ 'bpmnInteractionEvents' ],
40014 bpmnInteractionEvents: [ 'type', BpmnInteractionEvents ]
40015 };
40016
40017 /**
40018 * BPMN 2.0 specific keyboard bindings.
40019 *
40020 * @param {Injector} injector
40021 */
40022 function BpmnKeyboardBindings(injector) {
40023 injector.invoke(KeyboardBindings, this);
40024 }
40025
40026 inherits$1(BpmnKeyboardBindings, KeyboardBindings);
40027
40028 BpmnKeyboardBindings.$inject = [
40029 'injector'
40030 ];
40031
40032
40033 /**
40034 * Register available keyboard bindings.
40035 *
40036 * @param {Keyboard} keyboard
40037 * @param {EditorActions} editorActions
40038 */
40039 BpmnKeyboardBindings.prototype.registerBindings = function(keyboard, editorActions) {
40040
40041 // inherit default bindings
40042 KeyboardBindings.prototype.registerBindings.call(this, keyboard, editorActions);
40043
40044 /**
40045 * Add keyboard binding if respective editor action
40046 * is registered.
40047 *
40048 * @param {string} action name
40049 * @param {Function} fn that implements the key binding
40050 */
40051 function addListener(action, fn) {
40052
40053 if (editorActions.isRegistered(action)) {
40054 keyboard.addListener(fn);
40055 }
40056 }
40057
40058 // select all elements
40059 // CTRL + A
40060 addListener('selectElements', function(context) {
40061
40062 var event = context.keyEvent;
40063
40064 if (keyboard.isKey(['a', 'A'], event) && keyboard.isCmd(event)) {
40065 editorActions.trigger('selectElements');
40066
40067 return true;
40068 }
40069 });
40070
40071 // search labels
40072 // CTRL + F
40073 addListener('find', function(context) {
40074
40075 var event = context.keyEvent;
40076
40077 if (keyboard.isKey(['f', 'F'], event) && keyboard.isCmd(event)) {
40078 editorActions.trigger('find');
40079
40080 return true;
40081 }
40082 });
40083
40084 // activate space tool
40085 // S
40086 addListener('spaceTool', function(context) {
40087
40088 var event = context.keyEvent;
40089
40090 if (keyboard.hasModifier(event)) {
40091 return;
40092 }
40093
40094 if (keyboard.isKey(['s', 'S'], event)) {
40095 editorActions.trigger('spaceTool');
40096
40097 return true;
40098 }
40099 });
40100
40101 // activate lasso tool
40102 // L
40103 addListener('lassoTool', function(context) {
40104
40105 var event = context.keyEvent;
40106
40107 if (keyboard.hasModifier(event)) {
40108 return;
40109 }
40110
40111 if (keyboard.isKey(['l', 'L'], event)) {
40112 editorActions.trigger('lassoTool');
40113
40114 return true;
40115 }
40116 });
40117
40118 // activate hand tool
40119 // H
40120 addListener('handTool', function(context) {
40121
40122 var event = context.keyEvent;
40123
40124 if (keyboard.hasModifier(event)) {
40125 return;
40126 }
40127
40128 if (keyboard.isKey(['h', 'H'], event)) {
40129 editorActions.trigger('handTool');
40130
40131 return true;
40132 }
40133 });
40134
40135 // activate global connect tool
40136 // C
40137 addListener('globalConnectTool', function(context) {
40138
40139 var event = context.keyEvent;
40140
40141 if (keyboard.hasModifier(event)) {
40142 return;
40143 }
40144
40145 if (keyboard.isKey(['c', 'C'], event)) {
40146 editorActions.trigger('globalConnectTool');
40147
40148 return true;
40149 }
40150 });
40151
40152 // activate direct editing
40153 // E
40154 addListener('directEditing', function(context) {
40155
40156 var event = context.keyEvent;
40157
40158 if (keyboard.hasModifier(event)) {
40159 return;
40160 }
40161
40162 if (keyboard.isKey(['e', 'E'], event)) {
40163 editorActions.trigger('directEditing');
40164
40165 return true;
40166 }
40167 });
40168
40169 };
40170
40171 var KeyboardModule = {
40172 __depends__: [
40173 KeyboardModule$1
40174 ],
40175 __init__: [ 'keyboardBindings' ],
40176 keyboardBindings: [ 'type', BpmnKeyboardBindings ]
40177 };
40178
40179 var DEFAULT_CONFIG = {
40180 moveSpeed: 1,
40181 moveSpeedAccelerated: 10
40182 };
40183
40184 var HIGHER_PRIORITY$3 = 1500;
40185
40186 var LEFT = 'left';
40187 var UP = 'up';
40188 var RIGHT = 'right';
40189 var DOWN = 'down';
40190
40191 var KEY_TO_DIRECTION = {
40192 ArrowLeft: LEFT,
40193 Left: LEFT,
40194 ArrowUp: UP,
40195 Up: UP,
40196 ArrowRight: RIGHT,
40197 Right: RIGHT,
40198 ArrowDown: DOWN,
40199 Down: DOWN
40200 };
40201
40202 var DIRECTIONS_DELTA = {
40203 left: function(speed) {
40204 return {
40205 x: -speed,
40206 y: 0
40207 };
40208 },
40209 up: function(speed) {
40210 return {
40211 x: 0,
40212 y: -speed
40213 };
40214 },
40215 right: function(speed) {
40216 return {
40217 x: speed,
40218 y: 0
40219 };
40220 },
40221 down: function(speed) {
40222 return {
40223 x: 0,
40224 y: speed
40225 };
40226 }
40227 };
40228
40229
40230 /**
40231 * Enables to move selection with keyboard arrows.
40232 * Use with Shift for modified speed (default=1, with Shift=10).
40233 * Pressed Cmd/Ctrl turns the feature off.
40234 *
40235 * @param {Object} config
40236 * @param {number} [config.moveSpeed=1]
40237 * @param {number} [config.moveSpeedAccelerated=10]
40238 * @param {Keyboard} keyboard
40239 * @param {Modeling} modeling
40240 * @param {Selection} selection
40241 */
40242 function KeyboardMoveSelection(
40243 config,
40244 keyboard,
40245 modeling,
40246 rules,
40247 selection
40248 ) {
40249
40250 var self = this;
40251
40252 this._config = assign({}, DEFAULT_CONFIG, config || {});
40253
40254 keyboard.addListener(HIGHER_PRIORITY$3, function(event) {
40255
40256 var keyEvent = event.keyEvent;
40257
40258 var direction = KEY_TO_DIRECTION[keyEvent.key];
40259
40260 if (!direction) {
40261 return;
40262 }
40263
40264 if (keyboard.isCmd(keyEvent)) {
40265 return;
40266 }
40267
40268 var accelerated = keyboard.isShift(keyEvent);
40269
40270 self.moveSelection(direction, accelerated);
40271
40272 return true;
40273 });
40274
40275
40276 /**
40277 * Move selected elements in the given direction,
40278 * optionally specifying accelerated movement.
40279 *
40280 * @param {string} direction
40281 * @param {boolean} [accelerated=false]
40282 */
40283 this.moveSelection = function(direction, accelerated) {
40284
40285 var selectedElements = selection.get();
40286
40287 if (!selectedElements.length) {
40288 return;
40289 }
40290
40291 var speed = this._config[
40292 accelerated ?
40293 'moveSpeedAccelerated' :
40294 'moveSpeed'
40295 ];
40296
40297 var delta = DIRECTIONS_DELTA[direction](speed);
40298
40299 var canMove = rules.allowed('elements.move', {
40300 shapes: selectedElements
40301 });
40302
40303 if (canMove) {
40304 modeling.moveElements(selectedElements, delta);
40305 }
40306 };
40307
40308 }
40309
40310 KeyboardMoveSelection.$inject = [
40311 'config.keyboardMoveSelection',
40312 'keyboard',
40313 'modeling',
40314 'rules',
40315 'selection'
40316 ];
40317
40318 var KeyboardMoveSelectionModule = {
40319 __depends__: [
40320 KeyboardModule$1,
40321 SelectionModule
40322 ],
40323 __init__: [
40324 'keyboardMoveSelection'
40325 ],
40326 keyboardMoveSelection: [ 'type', KeyboardMoveSelection ]
40327 };
40328
40329 var DEFAULT_MIN_WIDTH = 10;
40330
40331
40332 /**
40333 * A component that provides resizing of shapes on the canvas.
40334 *
40335 * The following components are part of shape resize:
40336 *
40337 * * adding resize handles,
40338 * * creating a visual during resize
40339 * * checking resize rules
40340 * * committing a change once finished
40341 *
40342 *
40343 * ## Customizing
40344 *
40345 * It's possible to customize the resizing behaviour by intercepting 'resize.start'
40346 * and providing the following parameters through the 'context':
40347 *
40348 * * minDimensions ({ width, height }): minimum shape dimensions
40349 *
40350 * * childrenBoxPadding ({ left, top, bottom, right } || number):
40351 * gap between the minimum bounding box and the container
40352 *
40353 * f.ex:
40354 *
40355 * ```javascript
40356 * eventBus.on('resize.start', 1500, function(event) {
40357 * var context = event.context,
40358 *
40359 * context.minDimensions = { width: 140, height: 120 };
40360 *
40361 * // Passing general padding
40362 * context.childrenBoxPadding = 30;
40363 *
40364 * // Passing padding to a specific side
40365 * context.childrenBoxPadding.left = 20;
40366 * });
40367 * ```
40368 */
40369 function Resize(eventBus, rules, modeling, dragging) {
40370
40371 this._dragging = dragging;
40372 this._rules = rules;
40373
40374 var self = this;
40375
40376
40377 /**
40378 * Handle resize move by specified delta.
40379 *
40380 * @param {Object} context
40381 * @param {Point} delta
40382 */
40383 function handleMove(context, delta) {
40384
40385 var shape = context.shape,
40386 direction = context.direction,
40387 resizeConstraints = context.resizeConstraints,
40388 newBounds;
40389
40390 context.delta = delta;
40391
40392 newBounds = resizeBounds$1(shape, direction, delta);
40393
40394 // ensure constraints during resize
40395 context.newBounds = ensureConstraints$1(newBounds, resizeConstraints);
40396
40397 // update + cache executable state
40398 context.canExecute = self.canResize(context);
40399 }
40400
40401 /**
40402 * Handle resize start.
40403 *
40404 * @param {Object} context
40405 */
40406 function handleStart(context) {
40407
40408 var resizeConstraints = context.resizeConstraints,
40409
40410 // evaluate minBounds for backwards compatibility
40411 minBounds = context.minBounds;
40412
40413 if (resizeConstraints !== undefined) {
40414 return;
40415 }
40416
40417 if (minBounds === undefined) {
40418 minBounds = self.computeMinResizeBox(context);
40419 }
40420
40421 context.resizeConstraints = {
40422 min: asTRBL(minBounds)
40423 };
40424 }
40425
40426 /**
40427 * Handle resize end.
40428 *
40429 * @param {Object} context
40430 */
40431 function handleEnd(context) {
40432 var shape = context.shape,
40433 canExecute = context.canExecute,
40434 newBounds = context.newBounds;
40435
40436 if (canExecute) {
40437
40438 // ensure we have actual pixel values for new bounds
40439 // (important when zoom level was > 1 during move)
40440 newBounds = roundBounds(newBounds);
40441
40442 if (!boundsChanged(shape, newBounds)) {
40443
40444 // no resize necessary
40445 return;
40446 }
40447
40448 // perform the actual resize
40449 modeling.resizeShape(shape, newBounds);
40450 }
40451 }
40452
40453
40454 eventBus.on('resize.start', function(event) {
40455 handleStart(event.context);
40456 });
40457
40458 eventBus.on('resize.move', function(event) {
40459 var delta = {
40460 x: event.dx,
40461 y: event.dy
40462 };
40463
40464 handleMove(event.context, delta);
40465 });
40466
40467 eventBus.on('resize.end', function(event) {
40468 handleEnd(event.context);
40469 });
40470
40471 }
40472
40473
40474 Resize.prototype.canResize = function(context) {
40475 var rules = this._rules;
40476
40477 var ctx = pick(context, [ 'newBounds', 'shape', 'delta', 'direction' ]);
40478
40479 return rules.allowed('shape.resize', ctx);
40480 };
40481
40482 /**
40483 * Activate a resize operation.
40484 *
40485 * You may specify additional contextual information and must specify a
40486 * resize direction during activation of the resize event.
40487 *
40488 * @param {MouseEvent} event
40489 * @param {djs.model.Shape} shape
40490 * @param {Object|string} contextOrDirection
40491 */
40492 Resize.prototype.activate = function(event, shape, contextOrDirection) {
40493 var dragging = this._dragging,
40494 context,
40495 direction;
40496
40497 if (typeof contextOrDirection === 'string') {
40498 contextOrDirection = {
40499 direction: contextOrDirection
40500 };
40501 }
40502
40503 context = assign({ shape: shape }, contextOrDirection);
40504
40505 direction = context.direction;
40506
40507 if (!direction) {
40508 throw new Error('must provide a direction (n|w|s|e|nw|se|ne|sw)');
40509 }
40510
40511 dragging.init(event, getReferencePoint$1(shape, direction), 'resize', {
40512 autoActivate: true,
40513 cursor: getCursor(direction),
40514 data: {
40515 shape: shape,
40516 context: context
40517 }
40518 });
40519 };
40520
40521 Resize.prototype.computeMinResizeBox = function(context) {
40522 var shape = context.shape,
40523 direction = context.direction,
40524 minDimensions,
40525 childrenBounds;
40526
40527 minDimensions = context.minDimensions || {
40528 width: DEFAULT_MIN_WIDTH,
40529 height: DEFAULT_MIN_WIDTH
40530 };
40531
40532 // get children bounds
40533 childrenBounds = computeChildrenBBox(shape, context.childrenBoxPadding);
40534
40535 // get correct minimum bounds from given resize direction
40536 // basically ensures that the minBounds is max(childrenBounds, minDimensions)
40537 return getMinResizeBounds(direction, shape, minDimensions, childrenBounds);
40538 };
40539
40540
40541 Resize.$inject = [
40542 'eventBus',
40543 'rules',
40544 'modeling',
40545 'dragging'
40546 ];
40547
40548 // helpers //////////
40549
40550 function boundsChanged(shape, newBounds) {
40551 return shape.x !== newBounds.x ||
40552 shape.y !== newBounds.y ||
40553 shape.width !== newBounds.width ||
40554 shape.height !== newBounds.height;
40555 }
40556
40557 function getReferencePoint$1(shape, direction) {
40558 var mid = getMid(shape),
40559 trbl = asTRBL(shape);
40560
40561 var referencePoint = {
40562 x: mid.x,
40563 y: mid.y
40564 };
40565
40566 if (direction.indexOf('n') !== -1) {
40567 referencePoint.y = trbl.top;
40568 } else if (direction.indexOf('s') !== -1) {
40569 referencePoint.y = trbl.bottom;
40570 }
40571
40572 if (direction.indexOf('e') !== -1) {
40573 referencePoint.x = trbl.right;
40574 } else if (direction.indexOf('w') !== -1) {
40575 referencePoint.x = trbl.left;
40576 }
40577
40578 return referencePoint;
40579 }
40580
40581 function getCursor(direction) {
40582 var prefix = 'resize-';
40583
40584 if (direction === 'n' || direction === 's') {
40585 return prefix + 'ns';
40586 } else if (direction === 'e' || direction === 'w') {
40587 return prefix + 'ew';
40588 } else if (direction === 'nw' || direction === 'se') {
40589 return prefix + 'nwse';
40590 } else {
40591 return prefix + 'nesw';
40592 }
40593 }
40594
40595 var MARKER_RESIZING$1 = 'djs-resizing',
40596 MARKER_RESIZE_NOT_OK = 'resize-not-ok';
40597
40598 var LOW_PRIORITY$e = 500;
40599
40600
40601 /**
40602 * Provides previews for resizing shapes when resizing.
40603 *
40604 * @param {EventBus} eventBus
40605 * @param {Canvas} canvas
40606 * @param {PreviewSupport} previewSupport
40607 */
40608 function ResizePreview(eventBus, canvas, previewSupport) {
40609
40610 /**
40611 * Update resizer frame.
40612 *
40613 * @param {Object} context
40614 */
40615 function updateFrame(context) {
40616
40617 var shape = context.shape,
40618 bounds = context.newBounds,
40619 frame = context.frame;
40620
40621 if (!frame) {
40622 frame = context.frame = previewSupport.addFrame(shape, canvas.getActiveLayer());
40623
40624 canvas.addMarker(shape, MARKER_RESIZING$1);
40625 }
40626
40627 if (bounds.width > 5) {
40628 attr$1(frame, { x: bounds.x, width: bounds.width });
40629 }
40630
40631 if (bounds.height > 5) {
40632 attr$1(frame, { y: bounds.y, height: bounds.height });
40633 }
40634
40635 if (context.canExecute) {
40636 classes$1(frame).remove(MARKER_RESIZE_NOT_OK);
40637 } else {
40638 classes$1(frame).add(MARKER_RESIZE_NOT_OK);
40639 }
40640 }
40641
40642 /**
40643 * Remove resizer frame.
40644 *
40645 * @param {Object} context
40646 */
40647 function removeFrame(context) {
40648 var shape = context.shape,
40649 frame = context.frame;
40650
40651 if (frame) {
40652 remove$2(context.frame);
40653 }
40654
40655 canvas.removeMarker(shape, MARKER_RESIZING$1);
40656 }
40657
40658 // add and update previews
40659 eventBus.on('resize.move', LOW_PRIORITY$e, function(event) {
40660 updateFrame(event.context);
40661 });
40662
40663 // remove previews
40664 eventBus.on('resize.cleanup', function(event) {
40665 removeFrame(event.context);
40666 });
40667
40668 }
40669
40670 ResizePreview.$inject = [
40671 'eventBus',
40672 'canvas',
40673 'previewSupport'
40674 ];
40675
40676 var HANDLE_OFFSET = -6,
40677 HANDLE_SIZE = 4,
40678 HANDLE_HIT_SIZE = 20;
40679
40680 var CLS_RESIZER = 'djs-resizer';
40681
40682 var directions = [ 'n', 'w', 's', 'e', 'nw', 'ne', 'se', 'sw' ];
40683
40684
40685 /**
40686 * This component is responsible for adding resize handles.
40687 *
40688 * @param {EventBus} eventBus
40689 * @param {Canvas} canvas
40690 * @param {Selection} selection
40691 * @param {Resize} resize
40692 */
40693 function ResizeHandles(eventBus, canvas, selection, resize) {
40694
40695 this._resize = resize;
40696 this._canvas = canvas;
40697
40698 var self = this;
40699
40700 eventBus.on('selection.changed', function(e) {
40701 var newSelection = e.newSelection;
40702
40703 // remove old selection markers
40704 self.removeResizers();
40705
40706 // add new selection markers ONLY if single selection
40707 if (newSelection.length === 1) {
40708 forEach$2(newSelection, bind(self.addResizer, self));
40709 }
40710 });
40711
40712 eventBus.on('shape.changed', function(e) {
40713 var shape = e.element;
40714
40715 if (selection.isSelected(shape)) {
40716 self.removeResizers();
40717
40718 self.addResizer(shape);
40719 }
40720 });
40721 }
40722
40723
40724 ResizeHandles.prototype.makeDraggable = function(element, gfx, direction) {
40725 var resize = this._resize;
40726
40727 function startResize(event) {
40728
40729 // only trigger on left mouse button
40730 if (isPrimaryButton(event)) {
40731 resize.activate(event, element, direction);
40732 }
40733 }
40734
40735 componentEvent.bind(gfx, 'mousedown', startResize);
40736 componentEvent.bind(gfx, 'touchstart', startResize);
40737 };
40738
40739
40740 ResizeHandles.prototype._createResizer = function(element, x, y, direction) {
40741 var resizersParent = this._getResizersParent();
40742
40743 var offset = getHandleOffset(direction);
40744
40745 var group = create$1('g');
40746
40747 classes$1(group).add(CLS_RESIZER);
40748 classes$1(group).add(CLS_RESIZER + '-' + element.id);
40749 classes$1(group).add(CLS_RESIZER + '-' + direction);
40750
40751 append(resizersParent, group);
40752
40753 var visual = create$1('rect');
40754
40755 attr$1(visual, {
40756 x: -HANDLE_SIZE / 2 + offset.x,
40757 y: -HANDLE_SIZE / 2 + offset.y,
40758 width: HANDLE_SIZE,
40759 height: HANDLE_SIZE
40760 });
40761
40762 classes$1(visual).add(CLS_RESIZER + '-visual');
40763
40764 append(group, visual);
40765
40766 var hit = create$1('rect');
40767
40768 attr$1(hit, {
40769 x: -HANDLE_HIT_SIZE / 2 + offset.x,
40770 y: -HANDLE_HIT_SIZE / 2 + offset.y,
40771 width: HANDLE_HIT_SIZE,
40772 height: HANDLE_HIT_SIZE
40773 });
40774
40775 classes$1(hit).add(CLS_RESIZER + '-hit');
40776
40777 append(group, hit);
40778
40779 transform(group, x, y);
40780
40781 return group;
40782 };
40783
40784 ResizeHandles.prototype.createResizer = function(element, direction) {
40785 var point = getReferencePoint$1(element, direction);
40786
40787 var resizer = this._createResizer(element, point.x, point.y, direction);
40788
40789 this.makeDraggable(element, resizer, direction);
40790 };
40791
40792 // resize handles implementation ///////////////////////////////
40793
40794 /**
40795 * Add resizers for a given element.
40796 *
40797 * @param {djs.model.Shape} shape
40798 */
40799 ResizeHandles.prototype.addResizer = function(shape) {
40800 var self = this;
40801
40802 var resize = this._resize;
40803
40804 if (!resize.canResize({ shape: shape })) {
40805 return;
40806 }
40807
40808 forEach$2(directions, function(direction) {
40809 self.createResizer(shape, direction);
40810 });
40811 };
40812
40813 /**
40814 * Remove all resizers
40815 */
40816 ResizeHandles.prototype.removeResizers = function() {
40817 var resizersParent = this._getResizersParent();
40818
40819 clear$1(resizersParent);
40820 };
40821
40822 ResizeHandles.prototype._getResizersParent = function() {
40823 return this._canvas.getLayer('resizers');
40824 };
40825
40826 ResizeHandles.$inject = [
40827 'eventBus',
40828 'canvas',
40829 'selection',
40830 'resize'
40831 ];
40832
40833 // helpers //////////
40834
40835 function getHandleOffset(direction) {
40836 var offset = {
40837 x: 0,
40838 y: 0
40839 };
40840
40841 if (direction.indexOf('e') !== -1) {
40842 offset.x = -HANDLE_OFFSET;
40843 } else if (direction.indexOf('w') !== -1) {
40844 offset.x = HANDLE_OFFSET;
40845 }
40846
40847 if (direction.indexOf('s') !== -1) {
40848 offset.y = -HANDLE_OFFSET;
40849 } else if (direction.indexOf('n') !== -1) {
40850 offset.y = HANDLE_OFFSET;
40851 }
40852
40853 return offset;
40854 }
40855
40856 var ResizeModule = {
40857 __depends__: [
40858 RulesModule$1,
40859 DraggingModule,
40860 PreviewSupportModule
40861 ],
40862 __init__: [
40863 'resize',
40864 'resizePreview',
40865 'resizeHandles'
40866 ],
40867 resize: [ 'type', Resize ],
40868 resizePreview: [ 'type', ResizePreview ],
40869 resizeHandles: [ 'type', ResizeHandles ]
40870 };
40871
40872 /**
40873 * Creates a new bpmn:CategoryValue inside a new bpmn:Category
40874 *
40875 * @param {ModdleElement} definitions
40876 * @param {BpmnFactory} bpmnFactory
40877 *
40878 * @return {ModdleElement} categoryValue.
40879 */
40880 function createCategoryValue(definitions, bpmnFactory) {
40881 var categoryValue = bpmnFactory.create('bpmn:CategoryValue'),
40882 category = bpmnFactory.create('bpmn:Category', {
40883 categoryValue: [ categoryValue ]
40884 });
40885
40886 // add to correct place
40887 add(definitions.get('rootElements'), category);
40888 getBusinessObject(category).$parent = definitions;
40889 getBusinessObject(categoryValue).$parent = category;
40890
40891 return categoryValue;
40892
40893 }
40894
40895 function LabelEditingProvider(
40896 eventBus, bpmnFactory, canvas, directEditing,
40897 modeling, resizeHandles, textRenderer) {
40898
40899 this._bpmnFactory = bpmnFactory;
40900 this._canvas = canvas;
40901 this._modeling = modeling;
40902 this._textRenderer = textRenderer;
40903
40904 directEditing.registerProvider(this);
40905
40906 // listen to dblclick on non-root elements
40907 eventBus.on('element.dblclick', function(event) {
40908 activateDirectEdit(event.element, true);
40909 });
40910
40911 // complete on followup canvas operation
40912 eventBus.on([
40913 'autoPlace.start',
40914 'canvas.viewbox.changing',
40915 'drag.init',
40916 'element.mousedown',
40917 'popupMenu.open',
40918 'root.set',
40919 'selection.changed'
40920 ], function(event) {
40921
40922 if (directEditing.isActive()) {
40923 directEditing.complete();
40924 }
40925 });
40926
40927 // cancel on command stack changes
40928 eventBus.on([ 'commandStack.changed' ], function(e) {
40929 if (directEditing.isActive()) {
40930 directEditing.cancel();
40931 }
40932 });
40933
40934
40935 eventBus.on('directEditing.activate', function(event) {
40936 resizeHandles.removeResizers();
40937 });
40938
40939 eventBus.on('create.end', 500, function(event) {
40940
40941 var context = event.context,
40942 element = context.shape,
40943 canExecute = event.context.canExecute,
40944 isTouch = event.isTouch;
40945
40946 // TODO(nikku): we need to find a way to support the
40947 // direct editing on mobile devices; right now this will
40948 // break for desworkflowediting on mobile devices
40949 // as it breaks the user interaction workflow
40950
40951 // TODO(nre): we should temporarily focus the edited element
40952 // here and release the focused viewport after the direct edit
40953 // operation is finished
40954 if (isTouch) {
40955 return;
40956 }
40957
40958 if (!canExecute) {
40959 return;
40960 }
40961
40962 if (context.hints && context.hints.createElementsBehavior === false) {
40963 return;
40964 }
40965
40966 activateDirectEdit(element);
40967 });
40968
40969 eventBus.on('autoPlace.end', 500, function(event) {
40970 activateDirectEdit(event.shape);
40971 });
40972
40973
40974 function activateDirectEdit(element, force) {
40975 if (force ||
40976 isAny(element, [ 'bpmn:Task', 'bpmn:TextAnnotation' ]) ||
40977 isCollapsedSubProcess(element)) {
40978
40979 directEditing.activate(element);
40980 }
40981 }
40982
40983 }
40984
40985 LabelEditingProvider.$inject = [
40986 'eventBus',
40987 'bpmnFactory',
40988 'canvas',
40989 'directEditing',
40990 'modeling',
40991 'resizeHandles',
40992 'textRenderer'
40993 ];
40994
40995
40996 /**
40997 * Activate direct editing for activities and text annotations.
40998 *
40999 * @param {djs.model.Base} element
41000 *
41001 * @return {Object} an object with properties bounds (position and size), text and options
41002 */
41003 LabelEditingProvider.prototype.activate = function(element) {
41004
41005 // text
41006 var text = getLabel(element);
41007
41008 if (text === undefined) {
41009 return;
41010 }
41011
41012 var context = {
41013 text: text
41014 };
41015
41016 // bounds
41017 var bounds = this.getEditingBBox(element);
41018
41019 assign(context, bounds);
41020
41021 var options = {};
41022
41023 // tasks
41024 if (
41025 isAny(element, [
41026 'bpmn:Task',
41027 'bpmn:Participant',
41028 'bpmn:Lane',
41029 'bpmn:CallActivity'
41030 ]) ||
41031 isCollapsedSubProcess(element)
41032 ) {
41033 assign(options, {
41034 centerVertically: true
41035 });
41036 }
41037
41038 // external labels
41039 if (isLabelExternal(element)) {
41040 assign(options, {
41041 autoResize: true
41042 });
41043 }
41044
41045 // text annotations
41046 if (is$1(element, 'bpmn:TextAnnotation')) {
41047 assign(options, {
41048 resizable: true,
41049 autoResize: true
41050 });
41051 }
41052
41053 assign(context, {
41054 options: options
41055 });
41056
41057 return context;
41058 };
41059
41060
41061 /**
41062 * Get the editing bounding box based on the element's size and position
41063 *
41064 * @param {djs.model.Base} element
41065 *
41066 * @return {Object} an object containing information about position
41067 * and size (fixed or minimum and/or maximum)
41068 */
41069 LabelEditingProvider.prototype.getEditingBBox = function(element) {
41070 var canvas = this._canvas;
41071
41072 var target = element.label || element;
41073
41074 var bbox = canvas.getAbsoluteBBox(target);
41075
41076 var mid = {
41077 x: bbox.x + bbox.width / 2,
41078 y: bbox.y + bbox.height / 2
41079 };
41080
41081 // default position
41082 var bounds = { x: bbox.x, y: bbox.y };
41083
41084 var zoom = canvas.zoom();
41085
41086 var defaultStyle = this._textRenderer.getDefaultStyle(),
41087 externalStyle = this._textRenderer.getExternalStyle();
41088
41089 // take zoom into account
41090 var externalFontSize = externalStyle.fontSize * zoom,
41091 externalLineHeight = externalStyle.lineHeight,
41092 defaultFontSize = defaultStyle.fontSize * zoom,
41093 defaultLineHeight = defaultStyle.lineHeight;
41094
41095 var style = {
41096 fontFamily: this._textRenderer.getDefaultStyle().fontFamily,
41097 fontWeight: this._textRenderer.getDefaultStyle().fontWeight
41098 };
41099
41100 // adjust for expanded pools AND lanes
41101 if (is$1(element, 'bpmn:Lane') || isExpandedPool(element)) {
41102
41103 assign(bounds, {
41104 width: bbox.height,
41105 height: 30 * zoom,
41106 x: bbox.x - bbox.height / 2 + (15 * zoom),
41107 y: mid.y - (30 * zoom) / 2
41108 });
41109
41110 assign(style, {
41111 fontSize: defaultFontSize + 'px',
41112 lineHeight: defaultLineHeight,
41113 paddingTop: (7 * zoom) + 'px',
41114 paddingBottom: (7 * zoom) + 'px',
41115 paddingLeft: (5 * zoom) + 'px',
41116 paddingRight: (5 * zoom) + 'px',
41117 transform: 'rotate(-90deg)'
41118 });
41119 }
41120
41121
41122 // internal labels for tasks and collapsed call activities,
41123 // sub processes and participants
41124 if (isAny(element, [ 'bpmn:Task', 'bpmn:CallActivity']) ||
41125 isCollapsedPool(element) ||
41126 isCollapsedSubProcess(element)) {
41127
41128 assign(bounds, {
41129 width: bbox.width,
41130 height: bbox.height
41131 });
41132
41133 assign(style, {
41134 fontSize: defaultFontSize + 'px',
41135 lineHeight: defaultLineHeight,
41136 paddingTop: (7 * zoom) + 'px',
41137 paddingBottom: (7 * zoom) + 'px',
41138 paddingLeft: (5 * zoom) + 'px',
41139 paddingRight: (5 * zoom) + 'px'
41140 });
41141 }
41142
41143
41144 // internal labels for expanded sub processes
41145 if (isExpandedSubProcess$1(element)) {
41146 assign(bounds, {
41147 width: bbox.width,
41148 x: bbox.x
41149 });
41150
41151 assign(style, {
41152 fontSize: defaultFontSize + 'px',
41153 lineHeight: defaultLineHeight,
41154 paddingTop: (7 * zoom) + 'px',
41155 paddingBottom: (7 * zoom) + 'px',
41156 paddingLeft: (5 * zoom) + 'px',
41157 paddingRight: (5 * zoom) + 'px'
41158 });
41159 }
41160
41161 var width = 90 * zoom,
41162 paddingTop = 7 * zoom,
41163 paddingBottom = 4 * zoom;
41164
41165 // external labels for events, data elements, gateways, groups and connections
41166 if (target.labelTarget) {
41167 assign(bounds, {
41168 width: width,
41169 height: bbox.height + paddingTop + paddingBottom,
41170 x: mid.x - width / 2,
41171 y: bbox.y - paddingTop
41172 });
41173
41174 assign(style, {
41175 fontSize: externalFontSize + 'px',
41176 lineHeight: externalLineHeight,
41177 paddingTop: paddingTop + 'px',
41178 paddingBottom: paddingBottom + 'px'
41179 });
41180 }
41181
41182 // external label not yet created
41183 if (isLabelExternal(target)
41184 && !hasExternalLabel(target)
41185 && !isLabel$6(target)) {
41186
41187 var externalLabelMid = getExternalLabelMid(element);
41188
41189 var absoluteBBox = canvas.getAbsoluteBBox({
41190 x: externalLabelMid.x,
41191 y: externalLabelMid.y,
41192 width: 0,
41193 height: 0
41194 });
41195
41196 var height = externalFontSize + paddingTop + paddingBottom;
41197
41198 assign(bounds, {
41199 width: width,
41200 height: height,
41201 x: absoluteBBox.x - width / 2,
41202 y: absoluteBBox.y - height / 2
41203 });
41204
41205 assign(style, {
41206 fontSize: externalFontSize + 'px',
41207 lineHeight: externalLineHeight,
41208 paddingTop: paddingTop + 'px',
41209 paddingBottom: paddingBottom + 'px'
41210 });
41211 }
41212
41213 // text annotations
41214 if (is$1(element, 'bpmn:TextAnnotation')) {
41215 assign(bounds, {
41216 width: bbox.width,
41217 height: bbox.height,
41218 minWidth: 30 * zoom,
41219 minHeight: 10 * zoom
41220 });
41221
41222 assign(style, {
41223 textAlign: 'left',
41224 paddingTop: (5 * zoom) + 'px',
41225 paddingBottom: (7 * zoom) + 'px',
41226 paddingLeft: (7 * zoom) + 'px',
41227 paddingRight: (5 * zoom) + 'px',
41228 fontSize: defaultFontSize + 'px',
41229 lineHeight: defaultLineHeight
41230 });
41231 }
41232
41233 return { bounds: bounds, style: style };
41234 };
41235
41236
41237 LabelEditingProvider.prototype.update = function(
41238 element, newLabel,
41239 activeContextText, bounds) {
41240
41241 var newBounds,
41242 bbox;
41243
41244 if (is$1(element, 'bpmn:TextAnnotation')) {
41245
41246 bbox = this._canvas.getAbsoluteBBox(element);
41247
41248 newBounds = {
41249 x: element.x,
41250 y: element.y,
41251 width: element.width / bbox.width * bounds.width,
41252 height: element.height / bbox.height * bounds.height
41253 };
41254 }
41255
41256 if (is$1(element, 'bpmn:Group')) {
41257
41258 var businessObject = getBusinessObject(element);
41259
41260 // initialize categoryValue if not existing
41261 if (!businessObject.categoryValueRef) {
41262
41263 var rootElement = this._canvas.getRootElement(),
41264 definitions = getBusinessObject(rootElement).$parent;
41265
41266 var categoryValue = createCategoryValue(definitions, this._bpmnFactory);
41267
41268 getBusinessObject(element).categoryValueRef = categoryValue;
41269 }
41270
41271 }
41272
41273 if (isEmptyText$1(newLabel)) {
41274 newLabel = null;
41275 }
41276
41277 this._modeling.updateLabel(element, newLabel, newBounds);
41278 };
41279
41280
41281
41282 // helpers //////////////////////
41283
41284 function isCollapsedSubProcess(element) {
41285 return is$1(element, 'bpmn:SubProcess') && !isExpanded(element);
41286 }
41287
41288 function isExpandedSubProcess$1(element) {
41289 return is$1(element, 'bpmn:SubProcess') && isExpanded(element);
41290 }
41291
41292 function isCollapsedPool(element) {
41293 return is$1(element, 'bpmn:Participant') && !isExpanded(element);
41294 }
41295
41296 function isExpandedPool(element) {
41297 return is$1(element, 'bpmn:Participant') && isExpanded(element);
41298 }
41299
41300 function isEmptyText$1(label) {
41301 return !label || !label.trim();
41302 }
41303
41304 var MARKER_HIDDEN = 'djs-element-hidden',
41305 MARKER_LABEL_HIDDEN = 'djs-label-hidden';
41306
41307
41308 function LabelEditingPreview(
41309 eventBus, canvas, elementRegistry,
41310 pathMap) {
41311
41312 var self = this;
41313
41314 var defaultLayer = canvas.getDefaultLayer();
41315
41316 var element, absoluteElementBBox, gfx;
41317
41318 eventBus.on('directEditing.activate', function(context) {
41319 var activeProvider = context.active;
41320
41321 element = activeProvider.element.label || activeProvider.element;
41322
41323 // text annotation
41324 if (is$1(element, 'bpmn:TextAnnotation')) {
41325 absoluteElementBBox = canvas.getAbsoluteBBox(element);
41326
41327 gfx = create$1('g');
41328
41329 var textPathData = pathMap.getScaledPath('TEXT_ANNOTATION', {
41330 xScaleFactor: 1,
41331 yScaleFactor: 1,
41332 containerWidth: element.width,
41333 containerHeight: element.height,
41334 position: {
41335 mx: 0.0,
41336 my: 0.0
41337 }
41338 });
41339
41340 var path = self.path = create$1('path');
41341
41342 attr$1(path, {
41343 d: textPathData,
41344 strokeWidth: 2,
41345 stroke: getStrokeColor(element)
41346 });
41347
41348 append(gfx, path);
41349
41350 append(defaultLayer, gfx);
41351
41352 translate$2(gfx, element.x, element.y);
41353 }
41354
41355 if (is$1(element, 'bpmn:TextAnnotation') ||
41356 element.labelTarget) {
41357 canvas.addMarker(element, MARKER_HIDDEN);
41358 } else if (is$1(element, 'bpmn:Task') ||
41359 is$1(element, 'bpmn:CallActivity') ||
41360 is$1(element, 'bpmn:SubProcess') ||
41361 is$1(element, 'bpmn:Participant')) {
41362 canvas.addMarker(element, MARKER_LABEL_HIDDEN);
41363 }
41364 });
41365
41366 eventBus.on('directEditing.resize', function(context) {
41367
41368 // text annotation
41369 if (is$1(element, 'bpmn:TextAnnotation')) {
41370 var height = context.height,
41371 dy = context.dy;
41372
41373 var newElementHeight = Math.max(element.height / absoluteElementBBox.height * (height + dy), 0);
41374
41375 var textPathData = pathMap.getScaledPath('TEXT_ANNOTATION', {
41376 xScaleFactor: 1,
41377 yScaleFactor: 1,
41378 containerWidth: element.width,
41379 containerHeight: newElementHeight,
41380 position: {
41381 mx: 0.0,
41382 my: 0.0
41383 }
41384 });
41385
41386 attr$1(self.path, {
41387 d: textPathData
41388 });
41389 }
41390 });
41391
41392 eventBus.on([ 'directEditing.complete', 'directEditing.cancel' ], function(context) {
41393 var activeProvider = context.active;
41394
41395 if (activeProvider) {
41396 canvas.removeMarker(activeProvider.element.label || activeProvider.element, MARKER_HIDDEN);
41397 canvas.removeMarker(element, MARKER_LABEL_HIDDEN);
41398 }
41399
41400 element = undefined;
41401 absoluteElementBBox = undefined;
41402
41403 if (gfx) {
41404 remove$2(gfx);
41405
41406 gfx = undefined;
41407 }
41408 });
41409 }
41410
41411 LabelEditingPreview.$inject = [
41412 'eventBus',
41413 'canvas',
41414 'elementRegistry',
41415 'pathMap'
41416 ];
41417
41418
41419 // helpers ///////////////////
41420
41421 function getStrokeColor(element, defaultColor) {
41422 var di = getDi(element);
41423
41424 return di.get('stroke') || defaultColor || 'black';
41425 }
41426
41427 var LabelEditingModule = {
41428 __depends__: [
41429 ChangeSupportModule,
41430 ResizeModule,
41431 DirectEditingModule
41432 ],
41433 __init__: [
41434 'labelEditingProvider',
41435 'labelEditingPreview'
41436 ],
41437 labelEditingProvider: [ 'type', LabelEditingProvider ],
41438 labelEditingPreview: [ 'type', LabelEditingPreview ]
41439 };
41440
41441 var ALIGNMENTS = [
41442 'top',
41443 'bottom',
41444 'left',
41445 'right'
41446 ];
41447
41448 var ELEMENT_LABEL_DISTANCE = 10;
41449
41450 /**
41451 * A component that makes sure that external labels are added
41452 * together with respective elements and properly updated (DI wise)
41453 * during move.
41454 *
41455 * @param {EventBus} eventBus
41456 * @param {Modeling} modeling
41457 */
41458 function AdaptiveLabelPositioningBehavior(eventBus, modeling) {
41459
41460 CommandInterceptor.call(this, eventBus);
41461
41462 this.postExecuted([
41463 'connection.create',
41464 'connection.layout',
41465 'connection.updateWaypoints'
41466 ], function(event) {
41467 var context = event.context,
41468 connection = context.connection,
41469 source = connection.source,
41470 target = connection.target,
41471 hints = context.hints || {};
41472
41473 if (hints.createElementsBehavior !== false) {
41474 checkLabelAdjustment(source);
41475 checkLabelAdjustment(target);
41476 }
41477 });
41478
41479
41480 this.postExecuted([
41481 'label.create'
41482 ], function(event) {
41483 var context = event.context,
41484 shape = context.shape,
41485 hints = context.hints || {};
41486
41487 if (hints.createElementsBehavior !== false) {
41488 checkLabelAdjustment(shape.labelTarget);
41489 }
41490 });
41491
41492
41493 this.postExecuted([
41494 'elements.create'
41495 ], function(event) {
41496 var context = event.context,
41497 elements = context.elements,
41498 hints = context.hints || {};
41499
41500 if (hints.createElementsBehavior !== false) {
41501 elements.forEach(function(element) {
41502 checkLabelAdjustment(element);
41503 });
41504 }
41505 });
41506
41507 function checkLabelAdjustment(element) {
41508
41509 // skip non-existing labels
41510 if (!hasExternalLabel(element)) {
41511 return;
41512 }
41513
41514 var optimalPosition = getOptimalPosition(element);
41515
41516 // no optimal position found
41517 if (!optimalPosition) {
41518 return;
41519 }
41520
41521 adjustLabelPosition(element, optimalPosition);
41522 }
41523
41524 function adjustLabelPosition(element, orientation) {
41525
41526 var elementMid = getMid(element),
41527 label = element.label,
41528 labelMid = getMid(label);
41529
41530 // ignore labels that are being created
41531 if (!label.parent) {
41532 return;
41533 }
41534
41535 var elementTrbl = asTRBL(element);
41536
41537 var newLabelMid;
41538
41539 switch (orientation) {
41540 case 'top':
41541 newLabelMid = {
41542 x: elementMid.x,
41543 y: elementTrbl.top - ELEMENT_LABEL_DISTANCE - label.height / 2
41544 };
41545
41546 break;
41547
41548 case 'left':
41549
41550 newLabelMid = {
41551 x: elementTrbl.left - ELEMENT_LABEL_DISTANCE - label.width / 2,
41552 y: elementMid.y
41553 };
41554
41555 break;
41556
41557 case 'bottom':
41558
41559 newLabelMid = {
41560 x: elementMid.x,
41561 y: elementTrbl.bottom + ELEMENT_LABEL_DISTANCE + label.height / 2
41562 };
41563
41564 break;
41565
41566 case 'right':
41567
41568 newLabelMid = {
41569 x: elementTrbl.right + ELEMENT_LABEL_DISTANCE + label.width / 2,
41570 y: elementMid.y
41571 };
41572
41573 break;
41574 }
41575
41576 var delta$1 = delta(newLabelMid, labelMid);
41577
41578 modeling.moveShape(label, delta$1);
41579 }
41580
41581 }
41582
41583 inherits$1(AdaptiveLabelPositioningBehavior, CommandInterceptor);
41584
41585 AdaptiveLabelPositioningBehavior.$inject = [
41586 'eventBus',
41587 'modeling'
41588 ];
41589
41590
41591 // helpers //////////////////////
41592
41593 /**
41594 * Return alignments which are taken by a boundary's host element
41595 *
41596 * @param {Shape} element
41597 *
41598 * @return {Array<string>}
41599 */
41600 function getTakenHostAlignments(element) {
41601
41602 var hostElement = element.host,
41603 elementMid = getMid(element),
41604 hostOrientation = getOrientation(elementMid, hostElement);
41605
41606 var freeAlignments;
41607
41608 // check whether there is a multi-orientation, e.g. 'top-left'
41609 if (hostOrientation.indexOf('-') >= 0) {
41610 freeAlignments = hostOrientation.split('-');
41611 } else {
41612 freeAlignments = [ hostOrientation ];
41613 }
41614
41615 var takenAlignments = ALIGNMENTS.filter(function(alignment) {
41616
41617 return freeAlignments.indexOf(alignment) === -1;
41618 });
41619
41620 return takenAlignments;
41621
41622 }
41623
41624 /**
41625 * Return alignments which are taken by related connections
41626 *
41627 * @param {Shape} element
41628 *
41629 * @return {Array<string>}
41630 */
41631 function getTakenConnectionAlignments(element) {
41632
41633 var elementMid = getMid(element);
41634
41635 var takenAlignments = [].concat(
41636 element.incoming.map(function(c) {
41637 return c.waypoints[c.waypoints.length - 2 ];
41638 }),
41639 element.outgoing.map(function(c) {
41640 return c.waypoints[1];
41641 })
41642 ).map(function(point) {
41643 return getApproximateOrientation(elementMid, point);
41644 });
41645
41646 return takenAlignments;
41647 }
41648
41649 /**
41650 * Return the optimal label position around an element
41651 * or _undefined_, if none was found.
41652 *
41653 * @param {Shape} element
41654 *
41655 * @return {string} positioning identifier
41656 */
41657 function getOptimalPosition(element) {
41658
41659 var labelMid = getMid(element.label);
41660
41661 var elementMid = getMid(element);
41662
41663 var labelOrientation = getApproximateOrientation(elementMid, labelMid);
41664
41665 if (!isAligned(labelOrientation)) {
41666 return;
41667 }
41668
41669 var takenAlignments = getTakenConnectionAlignments(element);
41670
41671 if (element.host) {
41672 var takenHostAlignments = getTakenHostAlignments(element);
41673
41674 takenAlignments = takenAlignments.concat(takenHostAlignments);
41675 }
41676
41677 var freeAlignments = ALIGNMENTS.filter(function(alignment) {
41678
41679 return takenAlignments.indexOf(alignment) === -1;
41680 });
41681
41682 // NOTHING TO DO; label already aligned a.O.K.
41683 if (freeAlignments.indexOf(labelOrientation) !== -1) {
41684 return;
41685 }
41686
41687 return freeAlignments[0];
41688 }
41689
41690 function getApproximateOrientation(p0, p1) {
41691 return getOrientation(p1, p0, 5);
41692 }
41693
41694 function isAligned(orientation) {
41695 return ALIGNMENTS.indexOf(orientation) !== -1;
41696 }
41697
41698 function AppendBehavior(eventBus, elementFactory, bpmnRules) {
41699
41700 CommandInterceptor.call(this, eventBus);
41701
41702 // assign correct shape position unless already set
41703
41704 this.preExecute('shape.append', function(context) {
41705
41706 var source = context.source,
41707 shape = context.shape;
41708
41709 if (!context.position) {
41710
41711 if (is$1(shape, 'bpmn:TextAnnotation')) {
41712 context.position = {
41713 x: source.x + source.width / 2 + 75,
41714 y: source.y - (50) - shape.height / 2
41715 };
41716 } else {
41717 context.position = {
41718 x: source.x + source.width + 80 + shape.width / 2,
41719 y: source.y + source.height / 2
41720 };
41721 }
41722 }
41723 }, true);
41724 }
41725
41726 inherits$1(AppendBehavior, CommandInterceptor);
41727
41728 AppendBehavior.$inject = [
41729 'eventBus',
41730 'elementFactory',
41731 'bpmnRules'
41732 ];
41733
41734 function AssociationBehavior(injector, modeling) {
41735 injector.invoke(CommandInterceptor, this);
41736
41737 this.postExecute('shape.move', function(context) {
41738 var newParent = context.newParent,
41739 shape = context.shape;
41740
41741 var associations = filter(shape.incoming.concat(shape.outgoing), function(connection) {
41742 return is$1(connection, 'bpmn:Association');
41743 });
41744
41745 forEach$2(associations, function(association) {
41746 modeling.moveConnection(association, { x: 0, y: 0 }, newParent);
41747 });
41748 }, true);
41749 }
41750
41751 inherits$1(AssociationBehavior, CommandInterceptor);
41752
41753 AssociationBehavior.$inject = [
41754 'injector',
41755 'modeling'
41756 ];
41757
41758 var LOW_PRIORITY$d = 500;
41759
41760
41761 /**
41762 * Replace intermediate event with boundary event when creating or moving results in attached event.
41763 */
41764 function AttachEventBehavior(bpmnReplace, injector) {
41765 injector.invoke(CommandInterceptor, this);
41766
41767 this._bpmnReplace = bpmnReplace;
41768
41769 var self = this;
41770
41771 this.postExecuted('elements.create', LOW_PRIORITY$d, function(context) {
41772 var elements = context.elements;
41773
41774 elements = elements.filter(function(shape) {
41775 var host = shape.host;
41776
41777 return shouldReplace$1(shape, host);
41778 });
41779
41780 if (elements.length !== 1) {
41781 return;
41782 }
41783
41784 elements.map(function(element) {
41785 return elements.indexOf(element);
41786 }).forEach(function(index) {
41787 var host = elements[ index ];
41788
41789 context.elements[ index ] = self.replaceShape(elements[ index ], host);
41790 });
41791 }, true);
41792
41793
41794 this.preExecute('elements.move', LOW_PRIORITY$d, function(context) {
41795 var shapes = context.shapes,
41796 host = context.newHost;
41797
41798 if (shapes.length !== 1) {
41799 return;
41800 }
41801
41802 var shape = shapes[0];
41803
41804 if (shouldReplace$1(shape, host)) {
41805 context.shapes = [ self.replaceShape(shape, host) ];
41806 }
41807 }, true);
41808 }
41809
41810 AttachEventBehavior.$inject = [
41811 'bpmnReplace',
41812 'injector'
41813 ];
41814
41815 inherits$1(AttachEventBehavior, CommandInterceptor);
41816
41817 AttachEventBehavior.prototype.replaceShape = function(shape, host) {
41818 var eventDefinition = getEventDefinition$1(shape);
41819
41820 var boundaryEvent = {
41821 type: 'bpmn:BoundaryEvent',
41822 host: host
41823 };
41824
41825 if (eventDefinition) {
41826 boundaryEvent.eventDefinitionType = eventDefinition.$type;
41827 }
41828
41829 return this._bpmnReplace.replaceElement(shape, boundaryEvent, { layoutConnection: false });
41830 };
41831
41832
41833 // helpers //////////
41834
41835 function getEventDefinition$1(element) {
41836 var businessObject = getBusinessObject(element),
41837 eventDefinitions = businessObject.eventDefinitions;
41838
41839 return eventDefinitions && eventDefinitions[0];
41840 }
41841
41842 function shouldReplace$1(shape, host) {
41843 return !isLabel$6(shape) &&
41844 isAny(shape, [ 'bpmn:IntermediateThrowEvent', 'bpmn:IntermediateCatchEvent' ]) && !!host;
41845 }
41846
41847 var HIGH_PRIORITY$d = 2000;
41848
41849
41850 /**
41851 * BPMN specific boundary event behavior
41852 */
41853 function BoundaryEventBehavior(eventBus, moddle, modeling) {
41854
41855 CommandInterceptor.call(this, eventBus);
41856
41857 function getBoundaryEvents(element) {
41858 return filter(element.attachers, function(attacher) {
41859 return is$1(attacher, 'bpmn:BoundaryEvent');
41860 });
41861 }
41862
41863 // remove after connecting to event-based gateway
41864 this.postExecute('connection.create', function(event) {
41865 var source = event.context.source,
41866 target = event.context.target,
41867 boundaryEvents = getBoundaryEvents(target);
41868
41869 if (
41870 is$1(source, 'bpmn:EventBasedGateway') &&
41871 is$1(target, 'bpmn:ReceiveTask') &&
41872 boundaryEvents.length > 0
41873 ) {
41874 modeling.removeElements(boundaryEvents);
41875 }
41876
41877 });
41878
41879 // remove after replacing connected gateway with event-based gateway
41880 this.postExecute('connection.reconnect', function(event) {
41881 var oldSource = event.context.oldSource,
41882 newSource = event.context.newSource;
41883
41884 if (is$1(oldSource, 'bpmn:Gateway') &&
41885 is$1(newSource, 'bpmn:EventBasedGateway')) {
41886 forEach$2(newSource.outgoing, function(connection) {
41887 var target = connection.target,
41888 attachedboundaryEvents = getBoundaryEvents(target);
41889
41890 if (is$1(target, 'bpmn:ReceiveTask') &&
41891 attachedboundaryEvents.length > 0) {
41892 modeling.removeElements(attachedboundaryEvents);
41893 }
41894 });
41895 }
41896 });
41897
41898 // copy reference to root element on replace
41899 eventBus.on('moddleCopy.canCopyProperty', HIGH_PRIORITY$d, function(context) {
41900 var parent = context.parent,
41901 property = context.property,
41902 propertyName = context.propertyName;
41903
41904 var propertyDescriptor = moddle.getPropertyDescriptor(parent, propertyName);
41905
41906 if (propertyDescriptor && propertyDescriptor.isReference && is$1(property, 'bpmn:RootElement')) {
41907 parent.set(propertyName, property);
41908 }
41909 });
41910 }
41911
41912 BoundaryEventBehavior.$inject = [
41913 'eventBus',
41914 'moddle',
41915 'modeling'
41916 ];
41917
41918 inherits$1(BoundaryEventBehavior, CommandInterceptor);
41919
41920 function CreateBehavior(injector) {
41921 injector.invoke(CommandInterceptor, this);
41922
41923 this.preExecute('shape.create', 1500, function(event) {
41924 var context = event.context,
41925 parent = context.parent,
41926 shape = context.shape;
41927
41928 if (is$1(parent, 'bpmn:Lane') && !is$1(shape, 'bpmn:Lane')) {
41929 context.parent = getParent(parent, 'bpmn:Participant');
41930 }
41931 });
41932
41933 }
41934
41935
41936 CreateBehavior.$inject = [ 'injector' ];
41937
41938 inherits$1(CreateBehavior, CommandInterceptor);
41939
41940 /**
41941 * BPMN specific create data object behavior
41942 */
41943 function CreateDataObjectBehavior(eventBus, bpmnFactory, moddle) {
41944
41945 CommandInterceptor.call(this, eventBus);
41946
41947 this.preExecute('shape.create', function(event) {
41948
41949 var context = event.context,
41950 shape = context.shape;
41951
41952 if (is$1(shape, 'bpmn:DataObjectReference') && shape.type !== 'label') {
41953
41954 // create a DataObject every time a DataObjectReference is created
41955 var dataObject = bpmnFactory.create('bpmn:DataObject');
41956
41957 // set the reference to the DataObject
41958 shape.businessObject.dataObjectRef = dataObject;
41959 }
41960 });
41961
41962 }
41963
41964 CreateDataObjectBehavior.$inject = [
41965 'eventBus',
41966 'bpmnFactory',
41967 'moddle'
41968 ];
41969
41970 inherits$1(CreateDataObjectBehavior, CommandInterceptor);
41971
41972 var HORIZONTAL_PARTICIPANT_PADDING = 20,
41973 VERTICAL_PARTICIPANT_PADDING = 20;
41974
41975 var PARTICIPANT_BORDER_WIDTH = 30;
41976
41977 var HIGH_PRIORITY$c = 2000;
41978
41979
41980 /**
41981 * BPMN-specific behavior for creating participants.
41982 */
41983 function CreateParticipantBehavior(canvas, eventBus, modeling) {
41984 CommandInterceptor.call(this, eventBus);
41985
41986 // fit participant
41987 eventBus.on([
41988 'create.start',
41989 'shape.move.start'
41990 ], HIGH_PRIORITY$c, function(event) {
41991 var context = event.context,
41992 shape = context.shape,
41993 rootElement = canvas.getRootElement();
41994
41995 if (!is$1(shape, 'bpmn:Participant') ||
41996 !is$1(rootElement, 'bpmn:Process') ||
41997 !rootElement.children.length) {
41998 return;
41999 }
42000
42001 // ignore connections, groups and labels
42002 var children = rootElement.children.filter(function(element) {
42003 return !is$1(element, 'bpmn:Group') &&
42004 !isLabel$6(element) &&
42005 !isConnection$9(element);
42006 });
42007
42008 // ensure for available children to calculate bounds
42009 if (!children.length) {
42010 return;
42011 }
42012
42013 var childrenBBox = getBBox(children);
42014
42015 var participantBounds = getParticipantBounds(shape, childrenBBox);
42016
42017 // assign width and height
42018 assign(shape, participantBounds);
42019
42020 // assign create constraints
42021 context.createConstraints = getParticipantCreateConstraints(shape, childrenBBox);
42022 });
42023
42024 // force hovering process when creating first participant
42025 eventBus.on('create.start', HIGH_PRIORITY$c, function(event) {
42026 var context = event.context,
42027 shape = context.shape,
42028 rootElement = canvas.getRootElement(),
42029 rootElementGfx = canvas.getGraphics(rootElement);
42030
42031 function ensureHoveringProcess(event) {
42032 event.element = rootElement;
42033 event.gfx = rootElementGfx;
42034 }
42035
42036 if (is$1(shape, 'bpmn:Participant') && is$1(rootElement, 'bpmn:Process')) {
42037 eventBus.on('element.hover', HIGH_PRIORITY$c, ensureHoveringProcess);
42038
42039 eventBus.once('create.cleanup', function() {
42040 eventBus.off('element.hover', ensureHoveringProcess);
42041 });
42042 }
42043 });
42044
42045 // turn process into collaboration when creating first participant
42046 function getOrCreateCollaboration() {
42047 var rootElement = canvas.getRootElement();
42048
42049 if (is$1(rootElement, 'bpmn:Collaboration')) {
42050 return rootElement;
42051 }
42052
42053 return modeling.makeCollaboration();
42054 }
42055
42056 // when creating mutliple elements through `elements.create` parent must be set to collaboration
42057 // and passed to `shape.create` as hint
42058 this.preExecute('elements.create', HIGH_PRIORITY$c, function(context) {
42059 var elements = context.elements,
42060 parent = context.parent,
42061 participant = findParticipant(elements),
42062 hints;
42063
42064 if (participant && is$1(parent, 'bpmn:Process')) {
42065 context.parent = getOrCreateCollaboration();
42066
42067 hints = context.hints = context.hints || {};
42068
42069 hints.participant = participant;
42070 hints.process = parent;
42071 hints.processRef = getBusinessObject(participant).get('processRef');
42072 }
42073 }, true);
42074
42075 // when creating single shape through `shape.create` parent must be set to collaboration
42076 // unless it was already set through `elements.create`
42077 this.preExecute('shape.create', function(context) {
42078 var parent = context.parent,
42079 shape = context.shape;
42080
42081 if (is$1(shape, 'bpmn:Participant') && is$1(parent, 'bpmn:Process')) {
42082 context.parent = getOrCreateCollaboration();
42083
42084 context.process = parent;
42085 context.processRef = getBusinessObject(shape).get('processRef');
42086 }
42087 }, true);
42088
42089 // #execute necessary because #preExecute not called on CommandStack#redo
42090 this.execute('shape.create', function(context) {
42091 var hints = context.hints || {},
42092 process = context.process || hints.process,
42093 shape = context.shape,
42094 participant = hints.participant;
42095
42096 // both shape.create and elements.create must be handled
42097 if (process && (!participant || shape === participant)) {
42098
42099 // monkey-patch process ref
42100 getBusinessObject(shape).set('processRef', getBusinessObject(process));
42101 }
42102 }, true);
42103
42104 this.revert('shape.create', function(context) {
42105 var hints = context.hints || {},
42106 process = context.process || hints.process,
42107 processRef = context.processRef || hints.processRef,
42108 shape = context.shape,
42109 participant = hints.participant;
42110
42111 // both shape.create and elements.create must be handled
42112 if (process && (!participant || shape === participant)) {
42113
42114 // monkey-patch process ref
42115 getBusinessObject(shape).set('processRef', processRef);
42116 }
42117 }, true);
42118
42119 this.postExecute('shape.create', function(context) {
42120 var hints = context.hints || {},
42121 process = context.process || context.hints.process,
42122 shape = context.shape,
42123 participant = hints.participant;
42124
42125 if (process) {
42126 var children = process.children.slice();
42127
42128 // both shape.create and elements.create must be handled
42129 if (!participant) {
42130 modeling.moveElements(children, { x: 0, y: 0 }, shape);
42131 } else if (shape === participant) {
42132 modeling.moveElements(children, { x: 0, y: 0 }, participant);
42133 }
42134 }
42135 }, true);
42136 }
42137
42138 CreateParticipantBehavior.$inject = [
42139 'canvas',
42140 'eventBus',
42141 'modeling'
42142 ];
42143
42144 inherits$1(CreateParticipantBehavior, CommandInterceptor);
42145
42146 // helpers //////////
42147
42148 function getParticipantBounds(shape, childrenBBox) {
42149 childrenBBox = {
42150 width: childrenBBox.width + HORIZONTAL_PARTICIPANT_PADDING * 2 + PARTICIPANT_BORDER_WIDTH,
42151 height: childrenBBox.height + VERTICAL_PARTICIPANT_PADDING * 2
42152 };
42153
42154 var width = Math.max(shape.width, childrenBBox.width),
42155 height = Math.max(shape.height, childrenBBox.height);
42156
42157 return {
42158 x: -width / 2,
42159 y: -height / 2,
42160 width: width,
42161 height: height
42162 };
42163 }
42164
42165 function getParticipantCreateConstraints(shape, childrenBBox) {
42166 childrenBBox = asTRBL(childrenBBox);
42167
42168 return {
42169 bottom: childrenBBox.top + shape.height / 2 - VERTICAL_PARTICIPANT_PADDING,
42170 left: childrenBBox.right - shape.width / 2 + HORIZONTAL_PARTICIPANT_PADDING,
42171 top: childrenBBox.bottom - shape.height / 2 + VERTICAL_PARTICIPANT_PADDING,
42172 right: childrenBBox.left + shape.width / 2 - HORIZONTAL_PARTICIPANT_PADDING - PARTICIPANT_BORDER_WIDTH
42173 };
42174 }
42175
42176 function isConnection$9(element) {
42177 return !!element.waypoints;
42178 }
42179
42180 function findParticipant(elements) {
42181 return find(elements, function(element) {
42182 return is$1(element, 'bpmn:Participant');
42183 });
42184 }
42185
42186 var TARGET_REF_PLACEHOLDER_NAME = '__targetRef_placeholder';
42187
42188
42189 /**
42190 * This behavior makes sure we always set a fake
42191 * DataInputAssociation#targetRef as demanded by the BPMN 2.0
42192 * XSD schema.
42193 *
42194 * The reference is set to a bpmn:Property{ name: '__targetRef_placeholder' }
42195 * which is created on the fly and cleaned up afterwards if not needed
42196 * anymore.
42197 *
42198 * @param {EventBus} eventBus
42199 * @param {BpmnFactory} bpmnFactory
42200 */
42201 function DataInputAssociationBehavior(eventBus, bpmnFactory) {
42202
42203 CommandInterceptor.call(this, eventBus);
42204
42205
42206 this.executed([
42207 'connection.create',
42208 'connection.delete',
42209 'connection.move',
42210 'connection.reconnect'
42211 ], ifDataInputAssociation(fixTargetRef));
42212
42213 this.reverted([
42214 'connection.create',
42215 'connection.delete',
42216 'connection.move',
42217 'connection.reconnect'
42218 ], ifDataInputAssociation(fixTargetRef));
42219
42220
42221 function usesTargetRef(element, targetRef, removedConnection) {
42222
42223 var inputAssociations = element.get('dataInputAssociations');
42224
42225 return find(inputAssociations, function(association) {
42226 return association !== removedConnection &&
42227 association.targetRef === targetRef;
42228 });
42229 }
42230
42231 function getTargetRef(element, create) {
42232
42233 var properties = element.get('properties');
42234
42235 var targetRefProp = find(properties, function(p) {
42236 return p.name === TARGET_REF_PLACEHOLDER_NAME;
42237 });
42238
42239 if (!targetRefProp && create) {
42240 targetRefProp = bpmnFactory.create('bpmn:Property', {
42241 name: TARGET_REF_PLACEHOLDER_NAME
42242 });
42243
42244 add(properties, targetRefProp);
42245 }
42246
42247 return targetRefProp;
42248 }
42249
42250 function cleanupTargetRef(element, connection) {
42251
42252 var targetRefProp = getTargetRef(element);
42253
42254 if (!targetRefProp) {
42255 return;
42256 }
42257
42258 if (!usesTargetRef(element, targetRefProp, connection)) {
42259 remove(element.get('properties'), targetRefProp);
42260 }
42261 }
42262
42263 /**
42264 * Make sure targetRef is set to a valid property or
42265 * `null` if the connection is detached.
42266 *
42267 * @param {Event} event
42268 */
42269 function fixTargetRef(event) {
42270
42271 var context = event.context,
42272 connection = context.connection,
42273 connectionBo = connection.businessObject,
42274 target = connection.target,
42275 targetBo = target && target.businessObject,
42276 newTarget = context.newTarget,
42277 newTargetBo = newTarget && newTarget.businessObject,
42278 oldTarget = context.oldTarget || context.target,
42279 oldTargetBo = oldTarget && oldTarget.businessObject;
42280
42281 var dataAssociation = connection.businessObject,
42282 targetRefProp;
42283
42284 if (oldTargetBo && oldTargetBo !== targetBo) {
42285 cleanupTargetRef(oldTargetBo, connectionBo);
42286 }
42287
42288 if (newTargetBo && newTargetBo !== targetBo) {
42289 cleanupTargetRef(newTargetBo, connectionBo);
42290 }
42291
42292 if (targetBo) {
42293 targetRefProp = getTargetRef(targetBo, true);
42294 dataAssociation.targetRef = targetRefProp;
42295 } else {
42296 dataAssociation.targetRef = null;
42297 }
42298 }
42299 }
42300
42301 DataInputAssociationBehavior.$inject = [
42302 'eventBus',
42303 'bpmnFactory'
42304 ];
42305
42306 inherits$1(DataInputAssociationBehavior, CommandInterceptor);
42307
42308
42309 /**
42310 * Only call the given function when the event
42311 * touches a bpmn:DataInputAssociation.
42312 *
42313 * @param {Function} fn
42314 * @return {Function}
42315 */
42316 function ifDataInputAssociation(fn) {
42317
42318 return function(event) {
42319 var context = event.context,
42320 connection = context.connection;
42321
42322 if (is$1(connection, 'bpmn:DataInputAssociation')) {
42323 return fn(event);
42324 }
42325 };
42326 }
42327
42328 function UpdateSemanticParentHandler(bpmnUpdater) {
42329 this._bpmnUpdater = bpmnUpdater;
42330 }
42331
42332 UpdateSemanticParentHandler.$inject = [ 'bpmnUpdater' ];
42333
42334
42335 UpdateSemanticParentHandler.prototype.execute = function(context) {
42336 var dataStoreBo = context.dataStoreBo,
42337 dataStoreDi = context.dataStoreDi,
42338 newSemanticParent = context.newSemanticParent,
42339 newDiParent = context.newDiParent;
42340
42341 context.oldSemanticParent = dataStoreBo.$parent;
42342 context.oldDiParent = dataStoreDi.$parent;
42343
42344 // update semantic parent
42345 this._bpmnUpdater.updateSemanticParent(dataStoreBo, newSemanticParent);
42346
42347 // update DI parent
42348 this._bpmnUpdater.updateDiParent(dataStoreDi, newDiParent);
42349 };
42350
42351 UpdateSemanticParentHandler.prototype.revert = function(context) {
42352 var dataStoreBo = context.dataStoreBo,
42353 dataStoreDi = context.dataStoreDi,
42354 oldSemanticParent = context.oldSemanticParent,
42355 oldDiParent = context.oldDiParent;
42356
42357 // update semantic parent
42358 this._bpmnUpdater.updateSemanticParent(dataStoreBo, oldSemanticParent);
42359
42360 // update DI parent
42361 this._bpmnUpdater.updateDiParent(dataStoreDi, oldDiParent);
42362 };
42363
42364 /**
42365 * BPMN specific data store behavior
42366 */
42367 function DataStoreBehavior(
42368 canvas, commandStack, elementRegistry,
42369 eventBus) {
42370
42371 CommandInterceptor.call(this, eventBus);
42372
42373 commandStack.registerHandler('dataStore.updateContainment', UpdateSemanticParentHandler);
42374
42375 function getFirstParticipantWithProcessRef() {
42376 return elementRegistry.filter(function(element) {
42377 return is$1(element, 'bpmn:Participant') && getBusinessObject(element).processRef;
42378 })[0];
42379 }
42380
42381 function getDataStores(element) {
42382 return element.children.filter(function(child) {
42383 return is$1(child, 'bpmn:DataStoreReference') && !child.labelTarget;
42384 });
42385 }
42386
42387 function updateDataStoreParent(dataStore, newDataStoreParent) {
42388 var dataStoreBo = dataStore.businessObject || dataStore;
42389
42390 newDataStoreParent = newDataStoreParent || getFirstParticipantWithProcessRef();
42391
42392 if (newDataStoreParent) {
42393 var newDataStoreParentBo = newDataStoreParent.businessObject || newDataStoreParent;
42394
42395 commandStack.execute('dataStore.updateContainment', {
42396 dataStoreBo: dataStoreBo,
42397 dataStoreDi: getDi(dataStore),
42398 newSemanticParent: newDataStoreParentBo.processRef || newDataStoreParentBo,
42399 newDiParent: getDi(newDataStoreParent)
42400 });
42401 }
42402 }
42403
42404
42405 // disable auto-resize for data stores
42406 this.preExecute('shape.create', function(event) {
42407
42408 var context = event.context,
42409 shape = context.shape;
42410
42411 if (is$1(shape, 'bpmn:DataStoreReference') &&
42412 shape.type !== 'label') {
42413
42414 if (!context.hints) {
42415 context.hints = {};
42416 }
42417
42418 // prevent auto resizing
42419 context.hints.autoResize = false;
42420 }
42421 });
42422
42423
42424 // disable auto-resize for data stores
42425 this.preExecute('elements.move', function(event) {
42426 var context = event.context,
42427 shapes = context.shapes;
42428
42429 var dataStoreReferences = shapes.filter(function(shape) {
42430 return is$1(shape, 'bpmn:DataStoreReference');
42431 });
42432
42433 if (dataStoreReferences.length) {
42434 if (!context.hints) {
42435 context.hints = {};
42436 }
42437
42438 // prevent auto resizing for data store references
42439 context.hints.autoResize = shapes.filter(function(shape) {
42440 return !is$1(shape, 'bpmn:DataStoreReference');
42441 });
42442 }
42443 });
42444
42445
42446 // update parent on data store created
42447 this.postExecute('shape.create', function(event) {
42448 var context = event.context,
42449 shape = context.shape,
42450 parent = shape.parent;
42451
42452
42453 if (is$1(shape, 'bpmn:DataStoreReference') &&
42454 shape.type !== 'label' &&
42455 is$1(parent, 'bpmn:Collaboration')) {
42456
42457 updateDataStoreParent(shape);
42458 }
42459 });
42460
42461
42462 // update parent on data store moved
42463 this.postExecute('shape.move', function(event) {
42464 var context = event.context,
42465 shape = context.shape,
42466 oldParent = context.oldParent,
42467 parent = shape.parent;
42468
42469 if (is$1(oldParent, 'bpmn:Collaboration')) {
42470
42471 // do nothing if not necessary
42472 return;
42473 }
42474
42475 if (is$1(shape, 'bpmn:DataStoreReference') &&
42476 shape.type !== 'label' &&
42477 is$1(parent, 'bpmn:Collaboration')) {
42478
42479 var participant = is$1(oldParent, 'bpmn:Participant') ?
42480 oldParent :
42481 getAncestor(oldParent, 'bpmn:Participant');
42482
42483 updateDataStoreParent(shape, participant);
42484 }
42485 });
42486
42487
42488 // update data store parents on participant or subprocess deleted
42489 this.postExecute('shape.delete', function(event) {
42490 var context = event.context,
42491 shape = context.shape,
42492 rootElement = canvas.getRootElement();
42493
42494 if (isAny(shape, [ 'bpmn:Participant', 'bpmn:SubProcess' ])
42495 && is$1(rootElement, 'bpmn:Collaboration')) {
42496 getDataStores(rootElement)
42497 .filter(function(dataStore) {
42498 return isDescendant(dataStore, shape);
42499 })
42500 .forEach(function(dataStore) {
42501 updateDataStoreParent(dataStore);
42502 });
42503 }
42504 });
42505
42506 // update data store parents on collaboration -> process
42507 this.postExecute('canvas.updateRoot', function(event) {
42508 var context = event.context,
42509 oldRoot = context.oldRoot,
42510 newRoot = context.newRoot;
42511
42512 var dataStores = getDataStores(oldRoot);
42513
42514 dataStores.forEach(function(dataStore) {
42515
42516 if (is$1(newRoot, 'bpmn:Process')) {
42517 updateDataStoreParent(dataStore, newRoot);
42518 }
42519
42520 });
42521 });
42522 }
42523
42524 DataStoreBehavior.$inject = [
42525 'canvas',
42526 'commandStack',
42527 'elementRegistry',
42528 'eventBus',
42529 ];
42530
42531 inherits$1(DataStoreBehavior, CommandInterceptor);
42532
42533
42534 // helpers //////////
42535
42536 function isDescendant(descendant, ancestor) {
42537 var descendantBo = descendant.businessObject || descendant,
42538 ancestorBo = ancestor.businessObject || ancestor;
42539
42540 while (descendantBo.$parent) {
42541 if (descendantBo.$parent === ancestorBo.processRef || ancestorBo) {
42542 return true;
42543 }
42544
42545 descendantBo = descendantBo.$parent;
42546 }
42547
42548 return false;
42549 }
42550
42551 function getAncestor(element, type) {
42552
42553 while (element.parent) {
42554 if (is$1(element.parent, type)) {
42555 return element.parent;
42556 }
42557
42558 element = element.parent;
42559 }
42560 }
42561
42562 var LOW_PRIORITY$c = 500;
42563
42564
42565 /**
42566 * BPMN specific delete lane behavior
42567 */
42568 function DeleteLaneBehavior(eventBus, modeling, spaceTool) {
42569
42570 CommandInterceptor.call(this, eventBus);
42571
42572
42573 function compensateLaneDelete(shape, oldParent) {
42574
42575 var siblings = getChildLanes(oldParent);
42576
42577 var topAffected = [];
42578 var bottomAffected = [];
42579
42580 eachElement(siblings, function(element) {
42581
42582 if (element.y > shape.y) {
42583 bottomAffected.push(element);
42584 } else {
42585 topAffected.push(element);
42586 }
42587
42588 return element.children;
42589 });
42590
42591 if (!siblings.length) {
42592 return;
42593 }
42594
42595 var offset;
42596
42597 if (bottomAffected.length && topAffected.length) {
42598 offset = shape.height / 2;
42599 } else {
42600 offset = shape.height;
42601 }
42602
42603 var topAdjustments,
42604 bottomAdjustments;
42605
42606 if (topAffected.length) {
42607 topAdjustments = spaceTool.calculateAdjustments(
42608 topAffected, 'y', offset, shape.y - 10);
42609
42610 spaceTool.makeSpace(
42611 topAdjustments.movingShapes,
42612 topAdjustments.resizingShapes,
42613 { x: 0, y: offset }, 's');
42614 }
42615
42616 if (bottomAffected.length) {
42617 bottomAdjustments = spaceTool.calculateAdjustments(
42618 bottomAffected, 'y', -offset, shape.y + shape.height + 10);
42619
42620 spaceTool.makeSpace(
42621 bottomAdjustments.movingShapes,
42622 bottomAdjustments.resizingShapes,
42623 { x: 0, y: -offset }, 'n');
42624 }
42625 }
42626
42627
42628 /**
42629 * Adjust sizes of other lanes after lane deletion
42630 */
42631 this.postExecuted('shape.delete', LOW_PRIORITY$c, function(event) {
42632
42633 var context = event.context,
42634 hints = context.hints,
42635 shape = context.shape,
42636 oldParent = context.oldParent;
42637
42638 // only compensate lane deletes
42639 if (!is$1(shape, 'bpmn:Lane')) {
42640 return;
42641 }
42642
42643 // compensate root deletes only
42644 if (hints && hints.nested) {
42645 return;
42646 }
42647
42648 compensateLaneDelete(shape, oldParent);
42649 });
42650 }
42651
42652 DeleteLaneBehavior.$inject = [
42653 'eventBus',
42654 'modeling',
42655 'spaceTool'
42656 ];
42657
42658 inherits$1(DeleteLaneBehavior, CommandInterceptor);
42659
42660 var LOW_PRIORITY$b = 500;
42661
42662
42663 /**
42664 * Replace boundary event with intermediate event when creating or moving results in detached event.
42665 */
42666 function DetachEventBehavior(bpmnReplace, injector) {
42667 injector.invoke(CommandInterceptor, this);
42668
42669 this._bpmnReplace = bpmnReplace;
42670
42671 var self = this;
42672
42673 this.postExecuted('elements.create', LOW_PRIORITY$b, function(context) {
42674 var elements = context.elements;
42675
42676 elements.filter(function(shape) {
42677 var host = shape.host;
42678
42679 return shouldReplace(shape, host);
42680 }).map(function(shape) {
42681 return elements.indexOf(shape);
42682 }).forEach(function(index) {
42683 context.elements[ index ] = self.replaceShape(elements[ index ]);
42684 });
42685 }, true);
42686
42687 this.preExecute('elements.move', LOW_PRIORITY$b, function(context) {
42688 var shapes = context.shapes,
42689 newHost = context.newHost;
42690
42691 shapes.forEach(function(shape, index) {
42692 var host = shape.host;
42693
42694 if (shouldReplace(shape, includes$6(shapes, host) ? host : newHost)) {
42695 shapes[ index ] = self.replaceShape(shape);
42696 }
42697 });
42698 }, true);
42699 }
42700
42701 DetachEventBehavior.$inject = [
42702 'bpmnReplace',
42703 'injector'
42704 ];
42705
42706 inherits$1(DetachEventBehavior, CommandInterceptor);
42707
42708 DetachEventBehavior.prototype.replaceShape = function(shape) {
42709 var eventDefinition = getEventDefinition(shape),
42710 intermediateEvent;
42711
42712 if (eventDefinition) {
42713 intermediateEvent = {
42714 type: 'bpmn:IntermediateCatchEvent',
42715 eventDefinitionType: eventDefinition.$type
42716 };
42717 } else {
42718 intermediateEvent = {
42719 type: 'bpmn:IntermediateThrowEvent'
42720 };
42721 }
42722
42723 return this._bpmnReplace.replaceElement(shape, intermediateEvent, { layoutConnection: false });
42724 };
42725
42726
42727 // helpers //////////
42728
42729 function getEventDefinition(element) {
42730 var businessObject = getBusinessObject(element),
42731 eventDefinitions = businessObject.eventDefinitions;
42732
42733 return eventDefinitions && eventDefinitions[0];
42734 }
42735
42736 function shouldReplace(shape, host) {
42737 return !isLabel$6(shape) && is$1(shape, 'bpmn:BoundaryEvent') && !host;
42738 }
42739
42740 function includes$6(array, item) {
42741 return array.indexOf(item) !== -1;
42742 }
42743
42744 function DropOnFlowBehavior(eventBus, bpmnRules, modeling) {
42745
42746 CommandInterceptor.call(this, eventBus);
42747
42748 /**
42749 * Reconnect start / end of a connection after
42750 * dropping an element on a flow.
42751 */
42752
42753 function insertShape(shape, targetFlow, positionOrBounds) {
42754 var waypoints = targetFlow.waypoints,
42755 waypointsBefore,
42756 waypointsAfter,
42757 dockingPoint,
42758 source,
42759 target,
42760 incomingConnection,
42761 outgoingConnection,
42762 oldOutgoing = shape.outgoing.slice(),
42763 oldIncoming = shape.incoming.slice();
42764
42765 var mid;
42766
42767 if (isNumber(positionOrBounds.width)) {
42768 mid = getMid(positionOrBounds);
42769 } else {
42770 mid = positionOrBounds;
42771 }
42772
42773 var intersection = getApproxIntersection(waypoints, mid);
42774
42775 if (intersection) {
42776 waypointsBefore = waypoints.slice(0, intersection.index);
42777 waypointsAfter = waypoints.slice(intersection.index + (intersection.bendpoint ? 1 : 0));
42778
42779 // due to inaccuracy intersection might have been found
42780 if (!waypointsBefore.length || !waypointsAfter.length) {
42781 return;
42782 }
42783
42784 dockingPoint = intersection.bendpoint ? waypoints[intersection.index] : mid;
42785
42786 // if last waypointBefore is inside shape's bounds, ignore docking point
42787 if (waypointsBefore.length === 1 || !isPointInsideBBox(shape, waypointsBefore[waypointsBefore.length-1])) {
42788 waypointsBefore.push(copy(dockingPoint));
42789 }
42790
42791 // if first waypointAfter is inside shape's bounds, ignore docking point
42792 if (waypointsAfter.length === 1 || !isPointInsideBBox(shape, waypointsAfter[0])) {
42793 waypointsAfter.unshift(copy(dockingPoint));
42794 }
42795 }
42796
42797 source = targetFlow.source;
42798 target = targetFlow.target;
42799
42800 if (bpmnRules.canConnect(source, shape, targetFlow)) {
42801
42802 // reconnect source -> inserted shape
42803 modeling.reconnectEnd(targetFlow, shape, waypointsBefore || mid);
42804
42805 incomingConnection = targetFlow;
42806 }
42807
42808 if (bpmnRules.canConnect(shape, target, targetFlow)) {
42809
42810 if (!incomingConnection) {
42811
42812 // reconnect inserted shape -> end
42813 modeling.reconnectStart(targetFlow, shape, waypointsAfter || mid);
42814
42815 outgoingConnection = targetFlow;
42816 } else {
42817 outgoingConnection = modeling.connect(
42818 shape, target, { type: targetFlow.type, waypoints: waypointsAfter }
42819 );
42820 }
42821 }
42822
42823 var duplicateConnections = [].concat(
42824
42825 incomingConnection && filter(oldIncoming, function(connection) {
42826 return connection.source === incomingConnection.source;
42827 }) || [],
42828
42829 outgoingConnection && filter(oldOutgoing, function(connection) {
42830 return connection.target === outgoingConnection.target;
42831 }) || []
42832 );
42833
42834 if (duplicateConnections.length) {
42835 modeling.removeElements(duplicateConnections);
42836 }
42837 }
42838
42839 this.preExecute('elements.move', function(context) {
42840
42841 var newParent = context.newParent,
42842 shapes = context.shapes,
42843 delta = context.delta,
42844 shape = shapes[0];
42845
42846 if (!shape || !newParent) {
42847 return;
42848 }
42849
42850 // if the new parent is a connection,
42851 // change it to the new parent's parent
42852 if (newParent && newParent.waypoints) {
42853 context.newParent = newParent = newParent.parent;
42854 }
42855
42856 var shapeMid = getMid(shape);
42857 var newShapeMid = {
42858 x: shapeMid.x + delta.x,
42859 y: shapeMid.y + delta.y
42860 };
42861
42862 // find a connection which intersects with the
42863 // element's mid point
42864 var connection = find(newParent.children, function(element) {
42865 var canInsert = bpmnRules.canInsert(shapes, element);
42866
42867 return canInsert && getApproxIntersection(element.waypoints, newShapeMid);
42868 });
42869
42870 if (connection) {
42871 context.targetFlow = connection;
42872 context.position = newShapeMid;
42873 }
42874
42875 }, true);
42876
42877 this.postExecuted('elements.move', function(context) {
42878
42879 var shapes = context.shapes,
42880 targetFlow = context.targetFlow,
42881 position = context.position;
42882
42883 if (targetFlow) {
42884 insertShape(shapes[0], targetFlow, position);
42885 }
42886
42887 }, true);
42888
42889 this.preExecute('shape.create', function(context) {
42890
42891 var parent = context.parent,
42892 shape = context.shape;
42893
42894 if (bpmnRules.canInsert(shape, parent)) {
42895 context.targetFlow = parent;
42896 context.parent = parent.parent;
42897 }
42898 }, true);
42899
42900 this.postExecuted('shape.create', function(context) {
42901
42902 var shape = context.shape,
42903 targetFlow = context.targetFlow,
42904 positionOrBounds = context.position;
42905
42906 if (targetFlow) {
42907 insertShape(shape, targetFlow, positionOrBounds);
42908 }
42909 }, true);
42910 }
42911
42912 inherits$1(DropOnFlowBehavior, CommandInterceptor);
42913
42914 DropOnFlowBehavior.$inject = [
42915 'eventBus',
42916 'bpmnRules',
42917 'modeling'
42918 ];
42919
42920
42921 // helpers /////////////////////
42922
42923 function isPointInsideBBox(bbox, point) {
42924 var x = point.x,
42925 y = point.y;
42926
42927 return x >= bbox.x &&
42928 x <= bbox.x + bbox.width &&
42929 y >= bbox.y &&
42930 y <= bbox.y + bbox.height;
42931 }
42932
42933 function copy(obj) {
42934 return assign({}, obj);
42935 }
42936
42937 function EventBasedGatewayBehavior(eventBus, modeling) {
42938
42939 CommandInterceptor.call(this, eventBus);
42940
42941 /**
42942 * Remove existing sequence flows of event-based target before connecting
42943 * from event-based gateway.
42944 */
42945 this.preExecuted('connection.create', function(event) {
42946
42947 var context = event.context,
42948 source = context.source,
42949 target = context.target,
42950 existingIncomingConnections = target.incoming.slice();
42951
42952 if (context.hints && context.hints.createElementsBehavior === false) {
42953 return;
42954 }
42955
42956 if (
42957 is$1(source, 'bpmn:EventBasedGateway') &&
42958 target.incoming.length
42959 ) {
42960
42961 existingIncomingConnections.filter(isSequenceFlow)
42962 .forEach(function(sequenceFlow) {
42963 modeling.removeConnection(sequenceFlow);
42964 });
42965 }
42966 });
42967
42968 /**
42969 * After replacing shape with event-based gateway, remove incoming sequence
42970 * flows of event-based targets which do not belong to event-based gateway
42971 * source.
42972 */
42973 this.preExecuted('shape.replace', function(event) {
42974
42975 var newShape = event.context.newShape,
42976 newShapeTargets,
42977 newShapeTargetsIncomingSequenceFlows;
42978
42979 if (!is$1(newShape, 'bpmn:EventBasedGateway')) {
42980 return;
42981 }
42982
42983 newShapeTargets = newShape.outgoing.filter(isSequenceFlow)
42984 .map(function(sequenceFlow) {
42985 return sequenceFlow.target;
42986 });
42987
42988 newShapeTargetsIncomingSequenceFlows = newShapeTargets.reduce(function(sequenceFlows, target) {
42989 var incomingSequenceFlows = target.incoming.filter(isSequenceFlow);
42990
42991 return sequenceFlows.concat(incomingSequenceFlows);
42992 }, []);
42993
42994 newShapeTargetsIncomingSequenceFlows.forEach(function(sequenceFlow) {
42995 if (sequenceFlow.source !== newShape) {
42996 modeling.removeConnection(sequenceFlow);
42997 }
42998 });
42999 });
43000 }
43001
43002 EventBasedGatewayBehavior.$inject = [
43003 'eventBus',
43004 'modeling'
43005 ];
43006
43007 inherits$1(EventBasedGatewayBehavior, CommandInterceptor);
43008
43009
43010
43011 // helpers //////////////////////
43012
43013 function isSequenceFlow(connection) {
43014 return is$1(connection, 'bpmn:SequenceFlow');
43015 }
43016
43017 var HIGH_PRIORITY$b = 1500;
43018 var HIGHEST_PRIORITY = 2000;
43019
43020
43021 /**
43022 * Correct hover targets in certain situations to improve diagram interaction.
43023 *
43024 * @param {ElementRegistry} elementRegistry
43025 * @param {EventBus} eventBus
43026 * @param {Canvas} canvas
43027 */
43028 function FixHoverBehavior(elementRegistry, eventBus, canvas) {
43029
43030 eventBus.on([
43031 'create.hover',
43032 'create.move',
43033 'create.out',
43034 'create.end',
43035 'shape.move.hover',
43036 'shape.move.move',
43037 'shape.move.out',
43038 'shape.move.end'
43039 ], HIGH_PRIORITY$b, function(event) {
43040 var context = event.context,
43041 shape = context.shape || event.shape,
43042 hover = event.hover;
43043
43044 // ensure elements are not dropped onto a bpmn:Lane but onto
43045 // the underlying bpmn:Participant
43046 if (is$1(hover, 'bpmn:Lane') && !isAny(shape, [ 'bpmn:Lane', 'bpmn:Participant' ])) {
43047 event.hover = getLanesRoot(hover);
43048 event.hoverGfx = elementRegistry.getGraphics(event.hover);
43049 }
43050
43051 var rootElement = canvas.getRootElement();
43052
43053 // ensure bpmn:Group and label elements are dropped
43054 // always onto the root
43055 if (hover !== rootElement && (shape.labelTarget || is$1(shape, 'bpmn:Group'))) {
43056 event.hover = rootElement;
43057 event.hoverGfx = elementRegistry.getGraphics(event.hover);
43058 }
43059 });
43060
43061 eventBus.on([
43062 'connect.hover',
43063 'connect.out',
43064 'connect.end',
43065 'connect.cleanup',
43066 'global-connect.hover',
43067 'global-connect.out',
43068 'global-connect.end',
43069 'global-connect.cleanup'
43070 ], HIGH_PRIORITY$b, function(event) {
43071 var hover = event.hover;
43072
43073 // ensure connections start/end on bpmn:Participant,
43074 // not the underlying bpmn:Lane
43075 if (is$1(hover, 'bpmn:Lane')) {
43076 event.hover = getLanesRoot(hover) || hover;
43077 event.hoverGfx = elementRegistry.getGraphics(event.hover);
43078 }
43079 });
43080
43081
43082 eventBus.on([
43083 'bendpoint.move.hover'
43084 ], HIGH_PRIORITY$b, function(event) {
43085 var context = event.context,
43086 hover = event.hover,
43087 type = context.type;
43088
43089 // ensure reconnect start/end on bpmn:Participant,
43090 // not the underlying bpmn:Lane
43091 if (is$1(hover, 'bpmn:Lane') && /reconnect/.test(type)) {
43092 event.hover = getLanesRoot(hover) || hover;
43093 event.hoverGfx = elementRegistry.getGraphics(event.hover);
43094 }
43095 });
43096
43097
43098 eventBus.on([
43099 'connect.start'
43100 ], HIGH_PRIORITY$b, function(event) {
43101 var context = event.context,
43102 start = context.start;
43103
43104 // ensure connect start on bpmn:Participant,
43105 // not the underlying bpmn:Lane
43106 if (is$1(start, 'bpmn:Lane')) {
43107 context.start = getLanesRoot(start) || start;
43108 }
43109 });
43110
43111
43112 // allow movement of participants from lanes
43113 eventBus.on('shape.move.start', HIGHEST_PRIORITY, function(event) {
43114 var shape = event.shape;
43115
43116 if (is$1(shape, 'bpmn:Lane')) {
43117 event.shape = getLanesRoot(shape) || shape;
43118 }
43119 });
43120
43121 }
43122
43123 FixHoverBehavior.$inject = [
43124 'elementRegistry',
43125 'eventBus',
43126 'canvas'
43127 ];
43128
43129 var HIGH_PRIORITY$a = 2000;
43130
43131
43132 /**
43133 * BPMN specific Group behavior
43134 */
43135 function GroupBehavior(
43136 bpmnFactory,
43137 bpmnjs,
43138 elementRegistry,
43139 eventBus,
43140 injector,
43141 moddleCopy
43142 ) {
43143 injector.invoke(CommandInterceptor, this);
43144
43145 /**
43146 * Removes a referenced category value for a given group shape
43147 *
43148 * @param {djs.model.Shape} shape
43149 */
43150 function removeReferencedCategoryValue(shape) {
43151
43152 var businessObject = getBusinessObject(shape),
43153 categoryValue = businessObject.categoryValueRef;
43154
43155 if (!categoryValue) {
43156 return;
43157 }
43158
43159 var category = categoryValue.$parent;
43160
43161 if (!categoryValue) {
43162 return;
43163 }
43164
43165 remove(category.categoryValue, categoryValue);
43166
43167 // cleanup category if it is empty
43168 if (category && !category.categoryValue.length) {
43169 removeCategory(category);
43170 }
43171 }
43172
43173 /**
43174 * Removes a given category from the definitions
43175 *
43176 * @param {ModdleElement} category
43177 */
43178 function removeCategory(category) {
43179
43180 var definitions = bpmnjs.getDefinitions();
43181
43182 remove(definitions.get('rootElements'), category);
43183 }
43184
43185 /**
43186 * Returns all group element in the current registry
43187 *
43188 * @return {Array<djs.model.shape>} a list of group shapes
43189 */
43190 function getGroupElements() {
43191 return elementRegistry.filter(function(e) {
43192 return is$1(e, 'bpmn:Group');
43193 });
43194 }
43195
43196 /**
43197 * Returns true if given categoryValue is referenced in one of the given elements
43198 *
43199 * @param {Array<djs.model.shape>} elements
43200 * @param {ModdleElement} categoryValue
43201 * @return {boolean}
43202 */
43203 function isReferenced(elements, categoryValue) {
43204 return elements.some(function(e) {
43205
43206 var businessObject = getBusinessObject(e);
43207
43208 return businessObject.categoryValueRef
43209 && businessObject.categoryValueRef === categoryValue;
43210 });
43211 }
43212
43213 /**
43214 * remove referenced category + value when group was deleted
43215 */
43216 this.executed('shape.delete', function(event) {
43217
43218 var context = event.context,
43219 shape = context.shape;
43220
43221 if (is$1(shape, 'bpmn:Group')) {
43222
43223 var businessObject = getBusinessObject(shape),
43224 categoryValueRef = businessObject.categoryValueRef,
43225 groupElements = getGroupElements();
43226
43227 if (!isReferenced(groupElements, categoryValueRef)) {
43228 removeReferencedCategoryValue(shape);
43229 }
43230 }
43231 });
43232
43233 /**
43234 * re-attach removed category
43235 */
43236 this.reverted('shape.delete', function(event) {
43237
43238 var context = event.context,
43239 shape = context.shape;
43240
43241 if (is$1(shape, 'bpmn:Group')) {
43242
43243 var businessObject = getBusinessObject(shape),
43244 categoryValueRef = businessObject.categoryValueRef,
43245 definitions = bpmnjs.getDefinitions(),
43246 category = categoryValueRef ? categoryValueRef.$parent : null;
43247
43248 add(category.get('categoryValue'), categoryValueRef);
43249 add(definitions.get('rootElements'), category);
43250 }
43251 });
43252
43253 /**
43254 * create new category + value when group was created
43255 */
43256 this.execute('shape.create', function(event) {
43257 var context = event.context,
43258 shape = context.shape,
43259 businessObject = getBusinessObject(shape);
43260
43261 if (is$1(businessObject, 'bpmn:Group') && !businessObject.categoryValueRef) {
43262
43263 var definitions = bpmnjs.getDefinitions(),
43264 categoryValue = createCategoryValue(definitions, bpmnFactory);
43265
43266 // link the reference to the Group
43267 businessObject.categoryValueRef = categoryValue;
43268 }
43269 });
43270
43271
43272 this.revert('shape.create', function(event) {
43273
43274 var context = event.context,
43275 shape = context.shape;
43276
43277 if (is$1(shape, 'bpmn:Group')) {
43278 removeReferencedCategoryValue(shape);
43279
43280 delete getBusinessObject(shape).categoryValueRef;
43281
43282 }
43283 });
43284
43285 // copy bpmn:CategoryValue when copying element
43286 eventBus.on('moddleCopy.canCopyProperty', HIGH_PRIORITY$a, function(context) {
43287 var property = context.property,
43288 categoryValue;
43289
43290 if (is$1(property, 'bpmn:CategoryValue')) {
43291 categoryValue = createCategoryValue(bpmnjs.getDefinitions(), bpmnFactory);
43292
43293 // return copy of category
43294 return moddleCopy.copyElement(property, categoryValue);
43295 }
43296 });
43297
43298 }
43299
43300 GroupBehavior.$inject = [
43301 'bpmnFactory',
43302 'bpmnjs',
43303 'elementRegistry',
43304 'eventBus',
43305 'injector',
43306 'moddleCopy'
43307 ];
43308
43309 inherits$1(GroupBehavior, CommandInterceptor);
43310
43311 /**
43312 * Returns the intersection between two line segments a and b.
43313 *
43314 * @param {Point} l1s
43315 * @param {Point} l1e
43316 * @param {Point} l2s
43317 * @param {Point} l2e
43318 *
43319 * @return {Point}
43320 */
43321 function lineIntersect(l1s, l1e, l2s, l2e) {
43322
43323 // if the lines intersect, the result contains the x and y of the
43324 // intersection (treating the lines as infinite) and booleans for
43325 // whether line segment 1 or line segment 2 contain the point
43326 var denominator, a, b, c, numerator;
43327
43328 denominator = ((l2e.y - l2s.y) * (l1e.x - l1s.x)) - ((l2e.x - l2s.x) * (l1e.y - l1s.y));
43329
43330 if (denominator == 0) {
43331 return null;
43332 }
43333
43334 a = l1s.y - l2s.y;
43335 b = l1s.x - l2s.x;
43336 numerator = ((l2e.x - l2s.x) * a) - ((l2e.y - l2s.y) * b);
43337
43338 c = numerator / denominator;
43339
43340 // if we cast these lines infinitely in
43341 // both directions, they intersect here
43342 return {
43343 x: Math.round(l1s.x + (c * (l1e.x - l1s.x))),
43344 y: Math.round(l1s.y + (c * (l1e.y - l1s.y)))
43345 };
43346 }
43347
43348 /**
43349 * Fix broken dockings after DI imports.
43350 *
43351 * @param {EventBus} eventBus
43352 */
43353 function ImportDockingFix(eventBus) {
43354
43355 function adjustDocking(startPoint, nextPoint, elementMid) {
43356
43357 var elementTop = {
43358 x: elementMid.x,
43359 y: elementMid.y - 50
43360 };
43361
43362 var elementLeft = {
43363 x: elementMid.x - 50,
43364 y: elementMid.y
43365 };
43366
43367 var verticalIntersect = lineIntersect(startPoint, nextPoint, elementMid, elementTop),
43368 horizontalIntersect = lineIntersect(startPoint, nextPoint, elementMid, elementLeft);
43369
43370 // original is horizontal or vertical center cross intersection
43371 var centerIntersect;
43372
43373 if (verticalIntersect && horizontalIntersect) {
43374 if (getDistance$1(verticalIntersect, elementMid) > getDistance$1(horizontalIntersect, elementMid)) {
43375 centerIntersect = horizontalIntersect;
43376 } else {
43377 centerIntersect = verticalIntersect;
43378 }
43379 } else {
43380 centerIntersect = verticalIntersect || horizontalIntersect;
43381 }
43382
43383 startPoint.original = centerIntersect;
43384 }
43385
43386 function fixDockings(connection) {
43387 var waypoints = connection.waypoints;
43388
43389 adjustDocking(
43390 waypoints[0],
43391 waypoints[1],
43392 getMid(connection.source)
43393 );
43394
43395 adjustDocking(
43396 waypoints[waypoints.length - 1],
43397 waypoints[waypoints.length - 2],
43398 getMid(connection.target)
43399 );
43400 }
43401
43402 eventBus.on('bpmnElement.added', function(e) {
43403
43404 var element = e.element;
43405
43406 if (element.waypoints) {
43407 fixDockings(element);
43408 }
43409 });
43410 }
43411
43412 ImportDockingFix.$inject = [
43413 'eventBus'
43414 ];
43415
43416
43417 // helpers //////////////////////
43418
43419 function getDistance$1(p1, p2) {
43420 return Math.sqrt(Math.pow(p1.x - p2.x, 2) + Math.pow(p1.y - p2.y, 2));
43421 }
43422
43423 /**
43424 * A component that makes sure that each created or updated
43425 * Pool and Lane is assigned an isHorizontal property set to true.
43426 *
43427 * @param {EventBus} eventBus
43428 */
43429 function IsHorizontalFix(eventBus) {
43430
43431 CommandInterceptor.call(this, eventBus);
43432
43433 var elementTypesToUpdate = [
43434 'bpmn:Participant',
43435 'bpmn:Lane'
43436 ];
43437
43438 this.executed([ 'shape.move', 'shape.create', 'shape.resize' ], function(event) {
43439 var shape = event.context.shape,
43440 bo = getBusinessObject(shape),
43441 di = getDi(shape);
43442
43443 if (isAny(bo, elementTypesToUpdate) && !di.get('isHorizontal')) {
43444
43445 // set attribute directly to avoid modeling#updateProperty side effects
43446 di.set('isHorizontal', true);
43447 }
43448 });
43449
43450 }
43451
43452 IsHorizontalFix.$inject = [ 'eventBus' ];
43453
43454 inherits$1(IsHorizontalFix, CommandInterceptor);
43455
43456 /**
43457 * Returns the length of a vector
43458 *
43459 * @param {Vector}
43460 * @return {Float}
43461 */
43462 function vectorLength(v) {
43463 return Math.sqrt(Math.pow(v.x, 2) + Math.pow(v.y, 2));
43464 }
43465
43466
43467 /**
43468 * Calculates the angle between a line a the yAxis
43469 *
43470 * @param {Array}
43471 * @return {Float}
43472 */
43473 function getAngle(line) {
43474
43475 // return value is between 0, 180 and -180, -0
43476 // @janstuemmel: maybe replace return a/b with b/a
43477 return Math.atan((line[1].y - line[0].y) / (line[1].x - line[0].x));
43478 }
43479
43480
43481 /**
43482 * Rotates a vector by a given angle
43483 *
43484 * @param {Vector}
43485 * @param {Float} Angle in radians
43486 * @return {Vector}
43487 */
43488 function rotateVector(vector, angle) {
43489 return (!angle) ? vector : {
43490 x: Math.cos(angle) * vector.x - Math.sin(angle) * vector.y,
43491 y: Math.sin(angle) * vector.x + Math.cos(angle) * vector.y
43492 };
43493 }
43494
43495
43496 /**
43497 * Solves a 2D equation system
43498 * a + r*b = c, where a,b,c are 2D vectors
43499 *
43500 * @param {Vector}
43501 * @param {Vector}
43502 * @param {Vector}
43503 * @return {Float}
43504 */
43505 function solveLambaSystem(a, b, c) {
43506
43507 // the 2d system
43508 var system = [
43509 { n: a[0] - c[0], lambda: b[0] },
43510 { n: a[1] - c[1], lambda: b[1] }
43511 ];
43512
43513 // solve
43514 var n = system[0].n * b[0] + system[1].n * b[1],
43515 l = system[0].lambda * b[0] + system[1].lambda * b[1];
43516
43517 return -n/l;
43518 }
43519
43520
43521 /**
43522 * Position of perpendicular foot
43523 *
43524 * @param {Point}
43525 * @param [ {Point}, {Point} ] line defined through two points
43526 * @return {Point} the perpendicular foot position
43527 */
43528 function perpendicularFoot(point, line) {
43529
43530 var a = line[0], b = line[1];
43531
43532 // relative position of b from a
43533 var bd = { x: b.x - a.x, y: b.y - a.y };
43534
43535 // solve equation system to the parametrized vectors param real value
43536 var r = solveLambaSystem([ a.x, a.y ], [ bd.x, bd.y ], [ point.x, point.y ]);
43537
43538 return { x: a.x + r*bd.x, y: a.y + r*bd.y };
43539 }
43540
43541
43542 /**
43543 * Calculates the distance between a point and a line
43544 *
43545 * @param {Point}
43546 * @param [ {Point}, {Point} ] line defined through two points
43547 * @return {Float} distance
43548 */
43549 function getDistancePointLine(point, line) {
43550
43551 var pfPoint = perpendicularFoot(point, line);
43552
43553 // distance vector
43554 var connectionVector = {
43555 x: pfPoint.x - point.x,
43556 y: pfPoint.y - point.y
43557 };
43558
43559 return vectorLength(connectionVector);
43560 }
43561
43562
43563 /**
43564 * Calculates the distance between two points
43565 *
43566 * @param {Point}
43567 * @param {Point}
43568 * @return {Float} distance
43569 */
43570 function getDistancePointPoint(point1, point2) {
43571
43572 return vectorLength({
43573 x: point1.x - point2.x,
43574 y: point1.y - point2.y
43575 });
43576 }
43577
43578 var sqrt = Math.sqrt,
43579 min$1 = Math.min,
43580 max$3 = Math.max,
43581 abs$3 = Math.abs;
43582
43583 /**
43584 * Calculate the square (power to two) of a number.
43585 *
43586 * @param {number} n
43587 *
43588 * @return {number}
43589 */
43590 function sq(n) {
43591 return Math.pow(n, 2);
43592 }
43593
43594 /**
43595 * Get distance between two points.
43596 *
43597 * @param {Point} p1
43598 * @param {Point} p2
43599 *
43600 * @return {number}
43601 */
43602 function getDistance(p1, p2) {
43603 return sqrt(sq(p1.x - p2.x) + sq(p1.y - p2.y));
43604 }
43605
43606 /**
43607 * Return the attachment of the given point on the specified line.
43608 *
43609 * The attachment is either a bendpoint (attached to the given point)
43610 * or segment (attached to a location on a line segment) attachment:
43611 *
43612 * ```javascript
43613 * var pointAttachment = {
43614 * type: 'bendpoint',
43615 * bendpointIndex: 3,
43616 * position: { x: 10, y: 10 } // the attach point on the line
43617 * };
43618 *
43619 * var segmentAttachment = {
43620 * type: 'segment',
43621 * segmentIndex: 2,
43622 * relativeLocation: 0.31, // attach point location between 0 (at start) and 1 (at end)
43623 * position: { x: 10, y: 10 } // the attach point on the line
43624 * };
43625 * ```
43626 *
43627 * @param {Point} point
43628 * @param {Array<Point>} line
43629 *
43630 * @return {Object} attachment
43631 */
43632 function getAttachment(point, line) {
43633
43634 var idx = 0,
43635 segmentStart,
43636 segmentEnd,
43637 segmentStartDistance,
43638 segmentEndDistance,
43639 attachmentPosition,
43640 minDistance,
43641 intersections,
43642 attachment,
43643 attachmentDistance,
43644 closestAttachmentDistance,
43645 closestAttachment;
43646
43647 for (idx = 0; idx < line.length - 1; idx++) {
43648
43649 segmentStart = line[idx];
43650 segmentEnd = line[idx + 1];
43651
43652 if (pointsEqual(segmentStart, segmentEnd)) {
43653 intersections = [ segmentStart ];
43654 } else {
43655 segmentStartDistance = getDistance(point, segmentStart);
43656 segmentEndDistance = getDistance(point, segmentEnd);
43657
43658 minDistance = min$1(segmentStartDistance, segmentEndDistance);
43659
43660 intersections = getCircleSegmentIntersections(segmentStart, segmentEnd, point, minDistance);
43661 }
43662
43663 if (intersections.length < 1) {
43664 throw new Error('expected between [1, 2] circle -> line intersections');
43665 }
43666
43667 // one intersection -> bendpoint attachment
43668 if (intersections.length === 1) {
43669 attachment = {
43670 type: 'bendpoint',
43671 position: intersections[0],
43672 segmentIndex: idx,
43673 bendpointIndex: pointsEqual(segmentStart, intersections[0]) ? idx : idx + 1
43674 };
43675 }
43676
43677 // two intersections -> segment attachment
43678 if (intersections.length === 2) {
43679
43680 attachmentPosition = mid$1(intersections[0], intersections[1]);
43681
43682 attachment = {
43683 type: 'segment',
43684 position: attachmentPosition,
43685 segmentIndex: idx,
43686 relativeLocation: getDistance(segmentStart, attachmentPosition) / getDistance(segmentStart, segmentEnd)
43687 };
43688 }
43689
43690 attachmentDistance = getDistance(attachment.position, point);
43691
43692 if (!closestAttachment || closestAttachmentDistance > attachmentDistance) {
43693 closestAttachment = attachment;
43694 closestAttachmentDistance = attachmentDistance;
43695 }
43696 }
43697
43698 return closestAttachment;
43699 }
43700
43701 /**
43702 * Gets the intersection between a circle and a line segment.
43703 *
43704 * @param {Point} s1 segment start
43705 * @param {Point} s2 segment end
43706 * @param {Point} cc circle center
43707 * @param {number} cr circle radius
43708 *
43709 * @return {Array<Point>} intersections
43710 */
43711 function getCircleSegmentIntersections(s1, s2, cc, cr) {
43712
43713 var baX = s2.x - s1.x;
43714 var baY = s2.y - s1.y;
43715 var caX = cc.x - s1.x;
43716 var caY = cc.y - s1.y;
43717
43718 var a = baX * baX + baY * baY;
43719 var bBy2 = baX * caX + baY * caY;
43720 var c = caX * caX + caY * caY - cr * cr;
43721
43722 var pBy2 = bBy2 / a;
43723 var q = c / a;
43724
43725 var disc = pBy2 * pBy2 - q;
43726
43727 // check against negative value to work around
43728 // negative, very close to zero results (-4e-15)
43729 // being produced in some environments
43730 if (disc < 0 && disc > -0.000001) {
43731 disc = 0;
43732 }
43733
43734 if (disc < 0) {
43735 return [];
43736 }
43737
43738 // if disc == 0 ... dealt with later
43739 var tmpSqrt = sqrt(disc);
43740 var abScalingFactor1 = -pBy2 + tmpSqrt;
43741 var abScalingFactor2 = -pBy2 - tmpSqrt;
43742
43743 var i1 = {
43744 x: s1.x - baX * abScalingFactor1,
43745 y: s1.y - baY * abScalingFactor1
43746 };
43747
43748 if (disc === 0) { // abScalingFactor1 == abScalingFactor2
43749 return [ i1 ];
43750 }
43751
43752 var i2 = {
43753 x: s1.x - baX * abScalingFactor2,
43754 y: s1.y - baY * abScalingFactor2
43755 };
43756
43757 // return only points on line segment
43758 return [ i1, i2 ].filter(function(p) {
43759 return isPointInSegment(p, s1, s2);
43760 });
43761 }
43762
43763
43764 function isPointInSegment(p, segmentStart, segmentEnd) {
43765 return (
43766 fenced(p.x, segmentStart.x, segmentEnd.x) &&
43767 fenced(p.y, segmentStart.y, segmentEnd.y)
43768 );
43769 }
43770
43771 function fenced(n, rangeStart, rangeEnd) {
43772
43773 // use matching threshold to work around
43774 // precision errors in intersection computation
43775
43776 return (
43777 n >= min$1(rangeStart, rangeEnd) - EQUAL_THRESHOLD &&
43778 n <= max$3(rangeStart, rangeEnd) + EQUAL_THRESHOLD
43779 );
43780 }
43781
43782 /**
43783 * Calculate mid of two points.
43784 *
43785 * @param {Point} p1
43786 * @param {Point} p2
43787 *
43788 * @return {Point}
43789 */
43790 function mid$1(p1, p2) {
43791
43792 return {
43793 x: (p1.x + p2.x) / 2,
43794 y: (p1.y + p2.y) / 2
43795 };
43796 }
43797
43798 var EQUAL_THRESHOLD = 0.1;
43799
43800 function pointsEqual(p1, p2) {
43801
43802 return (
43803 abs$3(p1.x - p2.x) <= EQUAL_THRESHOLD &&
43804 abs$3(p1.y - p2.y) <= EQUAL_THRESHOLD
43805 );
43806 }
43807
43808 function findNewLabelLineStartIndex(oldWaypoints, newWaypoints, attachment, hints) {
43809
43810 var index = attachment.segmentIndex;
43811
43812 var offset = newWaypoints.length - oldWaypoints.length;
43813
43814 // segmentMove happened
43815 if (hints.segmentMove) {
43816
43817 var oldSegmentStartIndex = hints.segmentMove.segmentStartIndex,
43818 newSegmentStartIndex = hints.segmentMove.newSegmentStartIndex;
43819
43820 // if label was on moved segment return new segment index
43821 if (index === oldSegmentStartIndex) {
43822 return newSegmentStartIndex;
43823 }
43824
43825 // label is after new segment index
43826 if (index >= newSegmentStartIndex) {
43827 return (index+offset < newSegmentStartIndex) ? newSegmentStartIndex : index+offset;
43828 }
43829
43830 // if label is before new segment index
43831 return index;
43832 }
43833
43834 // bendpointMove happened
43835 if (hints.bendpointMove) {
43836
43837 var insert = hints.bendpointMove.insert,
43838 bendpointIndex = hints.bendpointMove.bendpointIndex,
43839 newIndex;
43840
43841 // waypoints length didnt change
43842 if (offset === 0) {
43843 return index;
43844 }
43845
43846 // label behind new/removed bendpoint
43847 if (index >= bendpointIndex) {
43848 newIndex = insert ? index + 1 : index - 1;
43849 }
43850
43851 // label before new/removed bendpoint
43852 if (index < bendpointIndex) {
43853
43854 newIndex = index;
43855
43856 // decide label should take right or left segment
43857 if (insert && attachment.type !== 'bendpoint' && bendpointIndex-1 === index) {
43858
43859 var rel = relativePositionMidWaypoint(newWaypoints, bendpointIndex);
43860
43861 if (rel < attachment.relativeLocation) {
43862 newIndex++;
43863 }
43864 }
43865 }
43866
43867 return newIndex;
43868 }
43869
43870 // start/end changed
43871 if (offset === 0) {
43872 return index;
43873 }
43874
43875 if (hints.connectionStart) {
43876 return (index === 0) ? 0 : null;
43877 }
43878
43879 if (hints.connectionEnd) {
43880 return (index === oldWaypoints.length - 2) ? newWaypoints.length - 2 : null;
43881 }
43882
43883 // if nothing fits, return null
43884 return null;
43885 }
43886
43887
43888 /**
43889 * Calculate the required adjustment (move delta) for the given label
43890 * after the connection waypoints got updated.
43891 *
43892 * @param {djs.model.Label} label
43893 * @param {Array<Point>} newWaypoints
43894 * @param {Array<Point>} oldWaypoints
43895 * @param {Object} hints
43896 *
43897 * @return {Point} delta
43898 */
43899 function getLabelAdjustment(label, newWaypoints, oldWaypoints, hints) {
43900
43901 var x = 0,
43902 y = 0;
43903
43904 var labelPosition = getLabelMid(label);
43905
43906 // get closest attachment
43907 var attachment = getAttachment(labelPosition, oldWaypoints),
43908 oldLabelLineIndex = attachment.segmentIndex,
43909 newLabelLineIndex = findNewLabelLineStartIndex(oldWaypoints, newWaypoints, attachment, hints);
43910
43911 if (newLabelLineIndex === null) {
43912 return { x: x, y: y };
43913 }
43914
43915 // should never happen
43916 // TODO(@janstuemmel): throw an error here when connectionSegmentMove is refactored
43917 if (newLabelLineIndex < 0 ||
43918 newLabelLineIndex > newWaypoints.length - 2) {
43919 return { x: x, y: y };
43920 }
43921
43922 var oldLabelLine = getLine(oldWaypoints, oldLabelLineIndex),
43923 newLabelLine = getLine(newWaypoints, newLabelLineIndex),
43924 oldFoot = attachment.position;
43925
43926 var relativeFootPosition = getRelativeFootPosition(oldLabelLine, oldFoot),
43927 angleDelta = getAngleDelta(oldLabelLine, newLabelLine);
43928
43929 // special rule if label on bendpoint
43930 if (attachment.type === 'bendpoint') {
43931
43932 var offset = newWaypoints.length - oldWaypoints.length,
43933 oldBendpointIndex = attachment.bendpointIndex,
43934 oldBendpoint = oldWaypoints[oldBendpointIndex];
43935
43936 // bendpoint position hasn't changed, return same position
43937 if (newWaypoints.indexOf(oldBendpoint) !== -1) {
43938 return { x: x, y: y };
43939 }
43940
43941 // new bendpoint and old bendpoint have same index, then just return the offset
43942 if (offset === 0) {
43943 var newBendpoint = newWaypoints[oldBendpointIndex];
43944
43945 return {
43946 x: newBendpoint.x - attachment.position.x,
43947 y: newBendpoint.y - attachment.position.y
43948 };
43949 }
43950
43951 // if bendpoints get removed
43952 if (offset < 0 && oldBendpointIndex !== 0 && oldBendpointIndex < oldWaypoints.length - 1) {
43953 relativeFootPosition = relativePositionMidWaypoint(oldWaypoints, oldBendpointIndex);
43954 }
43955 }
43956
43957 var newFoot = {
43958 x: (newLabelLine[1].x - newLabelLine[0].x) * relativeFootPosition + newLabelLine[0].x,
43959 y: (newLabelLine[1].y - newLabelLine[0].y) * relativeFootPosition + newLabelLine[0].y
43960 };
43961
43962 // the rotated vector to label
43963 var newLabelVector = rotateVector({
43964 x: labelPosition.x - oldFoot.x,
43965 y: labelPosition.y - oldFoot.y
43966 }, angleDelta);
43967
43968 // the new relative position
43969 x = newFoot.x + newLabelVector.x - labelPosition.x;
43970 y = newFoot.y + newLabelVector.y - labelPosition.y;
43971
43972 return roundPoint({
43973 x: x,
43974 y: y
43975 });
43976 }
43977
43978
43979 // HELPERS //////////////////////
43980
43981 function relativePositionMidWaypoint(waypoints, idx) {
43982
43983 var distanceSegment1 = getDistancePointPoint(waypoints[idx-1], waypoints[idx]),
43984 distanceSegment2 = getDistancePointPoint(waypoints[idx], waypoints[idx+1]);
43985
43986 var relativePosition = distanceSegment1 / (distanceSegment1 + distanceSegment2);
43987
43988 return relativePosition;
43989 }
43990
43991 function getLabelMid(label) {
43992 return {
43993 x: label.x + label.width / 2,
43994 y: label.y + label.height / 2
43995 };
43996 }
43997
43998 function getAngleDelta(l1, l2) {
43999 var a1 = getAngle(l1),
44000 a2 = getAngle(l2);
44001 return a2 - a1;
44002 }
44003
44004 function getLine(waypoints, idx) {
44005 return [ waypoints[idx], waypoints[idx+1] ];
44006 }
44007
44008 function getRelativeFootPosition(line, foot) {
44009
44010 var length = getDistancePointPoint(line[0], line[1]),
44011 lengthToFoot = getDistancePointPoint(line[0], foot);
44012
44013 return length === 0 ? 0 : lengthToFoot / length;
44014 }
44015
44016 /**
44017 * Calculates the absolute point relative to the new element's position
44018 *
44019 * @param {point} point [absolute]
44020 * @param {bounds} oldBounds
44021 * @param {bounds} newBounds
44022 *
44023 * @return {point} point [absolute]
44024 */
44025 function getNewAttachPoint(point, oldBounds, newBounds) {
44026 var oldCenter = center(oldBounds),
44027 newCenter = center(newBounds),
44028 oldDelta = delta(point, oldCenter);
44029
44030 var newDelta = {
44031 x: oldDelta.x * (newBounds.width / oldBounds.width),
44032 y: oldDelta.y * (newBounds.height / oldBounds.height)
44033 };
44034
44035 return roundPoint({
44036 x: newCenter.x + newDelta.x,
44037 y: newCenter.y + newDelta.y
44038 });
44039 }
44040
44041
44042 /**
44043 * Calculates the shape's delta relative to a new position
44044 * of a certain element's bounds
44045 *
44046 * @param {djs.model.Shape} point [absolute]
44047 * @param {bounds} oldBounds
44048 * @param {bounds} newBounds
44049 *
44050 * @return {delta} delta
44051 */
44052 function getNewAttachShapeDelta(shape, oldBounds, newBounds) {
44053 var shapeCenter = center(shape),
44054 oldCenter = center(oldBounds),
44055 newCenter = center(newBounds),
44056 shapeDelta = delta(shape, shapeCenter),
44057 oldCenterDelta = delta(shapeCenter, oldCenter),
44058 stickyPositionDelta = getStickyPositionDelta(shapeCenter, oldBounds, newBounds);
44059
44060 if (stickyPositionDelta) {
44061 return stickyPositionDelta;
44062 }
44063
44064 var newCenterDelta = {
44065 x: oldCenterDelta.x * (newBounds.width / oldBounds.width),
44066 y: oldCenterDelta.y * (newBounds.height / oldBounds.height)
44067 };
44068
44069 var newShapeCenter = {
44070 x: newCenter.x + newCenterDelta.x,
44071 y: newCenter.y + newCenterDelta.y
44072 };
44073
44074 return roundPoint({
44075 x: newShapeCenter.x + shapeDelta.x - shape.x,
44076 y: newShapeCenter.y + shapeDelta.y - shape.y
44077 });
44078 }
44079
44080 function getStickyPositionDelta(oldShapeCenter, oldBounds, newBounds) {
44081 var oldTRBL = asTRBL(oldBounds),
44082 newTRBL = asTRBL(newBounds);
44083
44084 if (isMoved(oldTRBL, newTRBL)) {
44085 return null;
44086 }
44087
44088 var oldOrientation = getOrientation(oldBounds, oldShapeCenter),
44089 stickyPositionDelta,
44090 newShapeCenter,
44091 newOrientation;
44092
44093 if (oldOrientation === 'top') {
44094 stickyPositionDelta = {
44095 x: 0,
44096 y: newTRBL.bottom - oldTRBL.bottom
44097 };
44098 } else if (oldOrientation === 'bottom') {
44099 stickyPositionDelta = {
44100 x: 0,
44101 y: newTRBL.top - oldTRBL.top
44102 };
44103 } else if (oldOrientation === 'right') {
44104 stickyPositionDelta = {
44105 x: newTRBL.left - oldTRBL.left,
44106 y: 0
44107 };
44108 } else if (oldOrientation === 'left') {
44109 stickyPositionDelta = {
44110 x: newTRBL.right - oldTRBL.right,
44111 y: 0
44112 };
44113 } else {
44114
44115 // fallback to proportional movement for corner-placed attachments
44116 return null;
44117 }
44118
44119 newShapeCenter = {
44120 x: oldShapeCenter.x + stickyPositionDelta.x,
44121 y: oldShapeCenter.y + stickyPositionDelta.y
44122 };
44123
44124 newOrientation = getOrientation(newBounds, newShapeCenter);
44125
44126 if (newOrientation !== oldOrientation) {
44127
44128 // fallback to proportional movement if orientation would otherwise change
44129 return null;
44130 }
44131
44132 return stickyPositionDelta;
44133 }
44134
44135 function isMoved(oldTRBL, newTRBL) {
44136 return isHorizontallyMoved(oldTRBL, newTRBL) || isVerticallyMoved(oldTRBL, newTRBL);
44137 }
44138
44139 function isHorizontallyMoved(oldTRBL, newTRBL) {
44140 return oldTRBL.right !== newTRBL.right && oldTRBL.left !== newTRBL.left;
44141 }
44142
44143 function isVerticallyMoved(oldTRBL, newTRBL) {
44144 return oldTRBL.top !== newTRBL.top && oldTRBL.bottom !== newTRBL.bottom;
44145 }
44146
44147 var DEFAULT_LABEL_DIMENSIONS = {
44148 width: 90,
44149 height: 20
44150 };
44151
44152 var NAME_PROPERTY = 'name';
44153 var TEXT_PROPERTY = 'text';
44154
44155 /**
44156 * A component that makes sure that external labels are added
44157 * together with respective elements and properly updated (DI wise)
44158 * during move.
44159 *
44160 * @param {EventBus} eventBus
44161 * @param {Modeling} modeling
44162 * @param {BpmnFactory} bpmnFactory
44163 * @param {TextRenderer} textRenderer
44164 */
44165 function LabelBehavior(
44166 eventBus, modeling, bpmnFactory,
44167 textRenderer) {
44168
44169 CommandInterceptor.call(this, eventBus);
44170
44171 // update label if name property was updated
44172 this.postExecute('element.updateProperties', function(e) {
44173 var context = e.context,
44174 element = context.element,
44175 properties = context.properties;
44176
44177 if (NAME_PROPERTY in properties) {
44178 modeling.updateLabel(element, properties[NAME_PROPERTY]);
44179 }
44180
44181 if (TEXT_PROPERTY in properties
44182 && is$1(element, 'bpmn:TextAnnotation')) {
44183
44184 var newBounds = textRenderer.getTextAnnotationBounds(
44185 {
44186 x: element.x,
44187 y: element.y,
44188 width: element.width,
44189 height: element.height
44190 },
44191 properties[TEXT_PROPERTY] || ''
44192 );
44193
44194 modeling.updateLabel(element, properties.text, newBounds);
44195 }
44196 });
44197
44198 // create label shape after shape/connection was created
44199 this.postExecute([ 'shape.create', 'connection.create' ], function(e) {
44200 var context = e.context,
44201 hints = context.hints || {};
44202
44203 if (hints.createElementsBehavior === false) {
44204 return;
44205 }
44206
44207 var element = context.shape || context.connection,
44208 businessObject = element.businessObject;
44209
44210 if (isLabel$6(element) || !isLabelExternal(element)) {
44211 return;
44212 }
44213
44214 // only create label if attribute available
44215 if (!getLabel(element)) {
44216 return;
44217 }
44218
44219 var labelCenter = getExternalLabelMid(element);
44220
44221 // we don't care about x and y
44222 var labelDimensions = textRenderer.getExternalLabelBounds(
44223 DEFAULT_LABEL_DIMENSIONS,
44224 getLabel(element)
44225 );
44226
44227 modeling.createLabel(element, labelCenter, {
44228 id: businessObject.id + '_label',
44229 businessObject: businessObject,
44230 width: labelDimensions.width,
44231 height: labelDimensions.height
44232 });
44233 });
44234
44235 // update label after label shape was deleted
44236 this.postExecute('shape.delete', function(event) {
44237 var context = event.context,
44238 labelTarget = context.labelTarget,
44239 hints = context.hints || {};
44240
44241 // check if label
44242 if (labelTarget && hints.unsetLabel !== false) {
44243 modeling.updateLabel(labelTarget, null, null, { removeShape: false });
44244 }
44245 });
44246
44247 // update di information on label creation
44248 this.postExecute([ 'label.create' ], function(event) {
44249
44250 var context = event.context,
44251 element = context.shape,
44252 labelTarget = context.labelTarget,
44253 di;
44254
44255 // we want to trigger on real labels only
44256 if (!labelTarget) {
44257 return;
44258 }
44259
44260 // we want to trigger on BPMN elements only
44261 if (!is$1(labelTarget, 'bpmn:BaseElement')) {
44262 return;
44263 }
44264
44265 di = getDi(labelTarget);
44266
44267 if (!di.label) {
44268 di.label = bpmnFactory.create('bpmndi:BPMNLabel', {
44269 bounds: bpmnFactory.create('dc:Bounds')
44270 });
44271
44272 element.di = di;
44273 }
44274
44275 assign(di.label.bounds, {
44276 x: element.x,
44277 y: element.y,
44278 width: element.width,
44279 height: element.height
44280 });
44281 });
44282
44283 function getVisibleLabelAdjustment(event) {
44284
44285 var context = event.context,
44286 connection = context.connection,
44287 label = connection.label,
44288 hints = assign({}, context.hints),
44289 newWaypoints = context.newWaypoints || connection.waypoints,
44290 oldWaypoints = context.oldWaypoints;
44291
44292
44293 if (typeof hints.startChanged === 'undefined') {
44294 hints.startChanged = !!hints.connectionStart;
44295 }
44296
44297 if (typeof hints.endChanged === 'undefined') {
44298 hints.endChanged = !!hints.connectionEnd;
44299 }
44300
44301 return getLabelAdjustment(label, newWaypoints, oldWaypoints, hints);
44302 }
44303
44304 this.postExecute([
44305 'connection.layout',
44306 'connection.updateWaypoints'
44307 ], function(event) {
44308 var context = event.context,
44309 hints = context.hints || {};
44310
44311 if (hints.labelBehavior === false) {
44312 return;
44313 }
44314
44315 var connection = context.connection,
44316 label = connection.label,
44317 labelAdjustment;
44318
44319 // handle missing label as well as the case
44320 // that the label parent does not exist (yet),
44321 // because it is being pasted / created via multi element create
44322 //
44323 // Cf. https://github.com/bpmn-io/bpmn-js/pull/1227
44324 if (!label || !label.parent) {
44325 return;
44326 }
44327
44328 labelAdjustment = getVisibleLabelAdjustment(event);
44329
44330 modeling.moveShape(label, labelAdjustment);
44331 });
44332
44333
44334 // keep label position on shape replace
44335 this.postExecute([ 'shape.replace' ], function(event) {
44336 var context = event.context,
44337 newShape = context.newShape,
44338 oldShape = context.oldShape;
44339
44340 var businessObject = getBusinessObject(newShape);
44341
44342 if (businessObject
44343 && isLabelExternal(businessObject)
44344 && oldShape.label
44345 && newShape.label) {
44346 newShape.label.x = oldShape.label.x;
44347 newShape.label.y = oldShape.label.y;
44348 }
44349 });
44350
44351
44352 // move external label after resizing
44353 this.postExecute('shape.resize', function(event) {
44354
44355 var context = event.context,
44356 shape = context.shape,
44357 newBounds = context.newBounds,
44358 oldBounds = context.oldBounds;
44359
44360 if (hasExternalLabel(shape)) {
44361
44362 var label = shape.label,
44363 labelMid = getMid(label),
44364 edges = asEdges(oldBounds);
44365
44366 // get nearest border point to label as reference point
44367 var referencePoint = getReferencePoint(labelMid, edges);
44368
44369 var delta = getReferencePointDelta(referencePoint, oldBounds, newBounds);
44370
44371 modeling.moveShape(label, delta);
44372
44373 }
44374
44375 });
44376
44377 }
44378
44379 inherits$1(LabelBehavior, CommandInterceptor);
44380
44381 LabelBehavior.$inject = [
44382 'eventBus',
44383 'modeling',
44384 'bpmnFactory',
44385 'textRenderer'
44386 ];
44387
44388 // helpers //////////////////////
44389
44390 /**
44391 * Calculates a reference point delta relative to a new position
44392 * of a certain element's bounds
44393 *
44394 * @param {Point} point
44395 * @param {Bounds} oldBounds
44396 * @param {Bounds} newBounds
44397 *
44398 * @return {Delta} delta
44399 */
44400 function getReferencePointDelta(referencePoint, oldBounds, newBounds) {
44401
44402 var newReferencePoint = getNewAttachPoint(referencePoint, oldBounds, newBounds);
44403
44404 return roundPoint(delta(newReferencePoint, referencePoint));
44405 }
44406
44407 /**
44408 * Generates the nearest point (reference point) for a given point
44409 * onto given set of lines
44410 *
44411 * @param {Array<Point, Point>} lines
44412 * @param {Point} point
44413 *
44414 * @param {Point}
44415 */
44416 function getReferencePoint(point, lines) {
44417
44418 if (!lines.length) {
44419 return;
44420 }
44421
44422 var nearestLine = getNearestLine(point, lines);
44423
44424 return perpendicularFoot(point, nearestLine);
44425 }
44426
44427 /**
44428 * Convert the given bounds to a lines array containing all edges
44429 *
44430 * @param {Bounds|Point} bounds
44431 *
44432 * @return Array<Point>
44433 */
44434 function asEdges(bounds) {
44435 return [
44436 [ // top
44437 {
44438 x: bounds.x,
44439 y: bounds.y
44440 },
44441 {
44442 x: bounds.x + (bounds.width || 0),
44443 y: bounds.y
44444 }
44445 ],
44446 [ // right
44447 {
44448 x: bounds.x + (bounds.width || 0),
44449 y: bounds.y
44450 },
44451 {
44452 x: bounds.x + (bounds.width || 0),
44453 y: bounds.y + (bounds.height || 0)
44454 }
44455 ],
44456 [ // bottom
44457 {
44458 x: bounds.x,
44459 y: bounds.y + (bounds.height || 0)
44460 },
44461 {
44462 x: bounds.x + (bounds.width || 0),
44463 y: bounds.y + (bounds.height || 0)
44464 }
44465 ],
44466 [ // left
44467 {
44468 x: bounds.x,
44469 y: bounds.y
44470 },
44471 {
44472 x: bounds.x,
44473 y: bounds.y + (bounds.height || 0)
44474 }
44475 ]
44476 ];
44477 }
44478
44479 /**
44480 * Returns the nearest line for a given point by distance
44481 * @param {Point} point
44482 * @param Array<Point> lines
44483 *
44484 * @return Array<Point>
44485 */
44486 function getNearestLine(point, lines) {
44487
44488 var distances = lines.map(function(l) {
44489 return {
44490 line: l,
44491 distance: getDistancePointLine(point, l)
44492 };
44493 });
44494
44495 var sorted = sortBy(distances, 'distance');
44496
44497 return sorted[0].line;
44498 }
44499
44500 function getResizedSourceAnchor(connection, shape, oldBounds) {
44501
44502 var waypoints = safeGetWaypoints(connection),
44503 waypointsInsideNewBounds = getWaypointsInsideBounds(waypoints, shape),
44504 oldAnchor = waypoints[0];
44505
44506 // new anchor is the last waypoint enclosed be resized source
44507 if (waypointsInsideNewBounds.length) {
44508 return waypointsInsideNewBounds[ waypointsInsideNewBounds.length - 1 ];
44509 }
44510
44511 return getNewAttachPoint(oldAnchor.original || oldAnchor, oldBounds, shape);
44512 }
44513
44514
44515 function getResizedTargetAnchor(connection, shape, oldBounds) {
44516
44517 var waypoints = safeGetWaypoints(connection),
44518 waypointsInsideNewBounds = getWaypointsInsideBounds(waypoints, shape),
44519 oldAnchor = waypoints[waypoints.length - 1];
44520
44521 // new anchor is the first waypoint enclosed be resized target
44522 if (waypointsInsideNewBounds.length) {
44523 return waypointsInsideNewBounds[ 0 ];
44524 }
44525
44526 return getNewAttachPoint(oldAnchor.original || oldAnchor, oldBounds, shape);
44527 }
44528
44529
44530 function getMovedSourceAnchor(connection, source, moveDelta) {
44531
44532 var waypoints = safeGetWaypoints(connection),
44533 oldBounds = subtract(source, moveDelta),
44534 oldAnchor = waypoints[ 0 ];
44535
44536 return getNewAttachPoint(oldAnchor.original || oldAnchor, oldBounds, source);
44537 }
44538
44539
44540 function getMovedTargetAnchor(connection, target, moveDelta) {
44541
44542 var waypoints = safeGetWaypoints(connection),
44543 oldBounds = subtract(target, moveDelta),
44544 oldAnchor = waypoints[ waypoints.length - 1 ];
44545
44546 return getNewAttachPoint(oldAnchor.original || oldAnchor, oldBounds, target);
44547 }
44548
44549
44550 // helpers //////////////////////
44551
44552 function subtract(bounds, delta) {
44553 return {
44554 x: bounds.x - delta.x,
44555 y: bounds.y - delta.y,
44556 width: bounds.width,
44557 height: bounds.height
44558 };
44559 }
44560
44561
44562 /**
44563 * Return waypoints of given connection; throw if non exists (should not happen!!).
44564 *
44565 * @param {Connection} connection
44566 *
44567 * @return {Array<Point>}
44568 */
44569 function safeGetWaypoints(connection) {
44570
44571 var waypoints = connection.waypoints;
44572
44573 if (!waypoints.length) {
44574 throw new Error('connection#' + connection.id + ': no waypoints');
44575 }
44576
44577 return waypoints;
44578 }
44579
44580 function getWaypointsInsideBounds(waypoints, bounds) {
44581 var originalWaypoints = map(waypoints, getOriginal);
44582
44583 return filter(originalWaypoints, function(waypoint) {
44584 return isInsideBounds(waypoint, bounds);
44585 });
44586 }
44587
44588 /**
44589 * Checks if point is inside bounds, incl. edges.
44590 *
44591 * @param {Point} point
44592 * @param {Bounds} bounds
44593 */
44594 function isInsideBounds(point, bounds) {
44595 return getOrientation(bounds, point, 1) === 'intersect';
44596 }
44597
44598 function getOriginal(point) {
44599 return point.original || point;
44600 }
44601
44602 /**
44603 * BPMN-specific message flow behavior.
44604 */
44605 function MessageFlowBehavior(eventBus, modeling) {
44606
44607 CommandInterceptor.call(this, eventBus);
44608
44609 this.postExecute('shape.replace', function(context) {
44610 var oldShape = context.oldShape,
44611 newShape = context.newShape;
44612
44613 if (!isParticipantCollapse(oldShape, newShape)) {
44614 return;
44615 }
44616
44617 var messageFlows = getMessageFlows(oldShape);
44618
44619 messageFlows.incoming.forEach(function(incoming) {
44620 var anchor = getResizedTargetAnchor(incoming, newShape, oldShape);
44621
44622 modeling.reconnectEnd(incoming, newShape, anchor);
44623 });
44624
44625 messageFlows.outgoing.forEach(function(outgoing) {
44626 var anchor = getResizedSourceAnchor(outgoing, newShape, oldShape);
44627
44628 modeling.reconnectStart(outgoing, newShape, anchor);
44629 });
44630 }, true);
44631
44632 }
44633
44634 MessageFlowBehavior.$inject = [ 'eventBus', 'modeling' ];
44635
44636 inherits$1(MessageFlowBehavior, CommandInterceptor);
44637
44638 // helpers //////////
44639
44640 function isParticipantCollapse(oldShape, newShape) {
44641 return is$1(oldShape, 'bpmn:Participant')
44642 && isExpanded(oldShape)
44643 && is$1(newShape, 'bpmn:Participant')
44644 && !isExpanded(newShape);
44645 }
44646
44647 function getMessageFlows(parent) {
44648 var elements = selfAndAllChildren([ parent ], false);
44649
44650 var incoming = [],
44651 outgoing = [];
44652
44653 elements.forEach(function(element) {
44654 if (element === parent) {
44655 return;
44656 }
44657
44658 element.incoming.forEach(function(connection) {
44659 if (is$1(connection, 'bpmn:MessageFlow')) {
44660 incoming.push(connection);
44661 }
44662 });
44663
44664 element.outgoing.forEach(function(connection) {
44665 if (is$1(connection, 'bpmn:MessageFlow')) {
44666 outgoing.push(connection);
44667 }
44668 });
44669 }, []);
44670
44671 return {
44672 incoming: incoming,
44673 outgoing: outgoing
44674 };
44675 }
44676
44677 var COLLAB_ERR_MSG = 'flow elements must be children of pools/participants';
44678
44679 function ModelingFeedback(eventBus, tooltips, translate) {
44680
44681 function showError(position, message, timeout) {
44682 tooltips.add({
44683 position: {
44684 x: position.x + 5,
44685 y: position.y + 5
44686 },
44687 type: 'error',
44688 timeout: timeout || 2000,
44689 html: '<div>' + message + '</div>'
44690 });
44691 }
44692
44693 eventBus.on([ 'shape.move.rejected', 'create.rejected' ], function(event) {
44694 var context = event.context,
44695 shape = context.shape,
44696 target = context.target;
44697
44698 if (is$1(target, 'bpmn:Collaboration') && is$1(shape, 'bpmn:FlowNode')) {
44699 showError(event, translate(COLLAB_ERR_MSG));
44700 }
44701 });
44702
44703 }
44704
44705 ModelingFeedback.$inject = [
44706 'eventBus',
44707 'tooltips',
44708 'translate'
44709 ];
44710
44711 /**
44712 * BPMN specific behavior ensuring that bpmndi:Label's dc:Bounds are removed
44713 * when shape is resized.
44714 */
44715 function RemoveEmbeddedLabelBoundsBehavior(eventBus, modeling) {
44716 CommandInterceptor.call(this, eventBus);
44717
44718 this.preExecute('shape.resize', function(context) {
44719 var shape = context.shape;
44720
44721 var di = getDi(shape),
44722 label = di && di.get('label'),
44723 bounds = label && label.get('bounds');
44724
44725 if (bounds) {
44726 modeling.updateModdleProperties(shape, label, {
44727 bounds: undefined
44728 });
44729 }
44730 }, true);
44731 }
44732
44733 inherits$1(RemoveEmbeddedLabelBoundsBehavior, CommandInterceptor);
44734
44735 RemoveEmbeddedLabelBoundsBehavior.$inject = [
44736 'eventBus',
44737 'modeling'
44738 ];
44739
44740 function RemoveElementBehavior(eventBus, bpmnRules, modeling) {
44741
44742 CommandInterceptor.call(this, eventBus);
44743
44744 /**
44745 * Combine sequence flows when deleting an element
44746 * if there is one incoming and one outgoing
44747 * sequence flow
44748 */
44749 this.preExecute('shape.delete', function(e) {
44750
44751 var shape = e.context.shape;
44752
44753 // only handle [a] -> [shape] -> [b] patterns
44754 if (shape.incoming.length !== 1 || shape.outgoing.length !== 1) {
44755 return;
44756 }
44757
44758 var inConnection = shape.incoming[0],
44759 outConnection = shape.outgoing[0];
44760
44761 // only handle sequence flows
44762 if (!is$1(inConnection, 'bpmn:SequenceFlow') || !is$1(outConnection, 'bpmn:SequenceFlow')) {
44763 return;
44764 }
44765
44766 if (bpmnRules.canConnect(inConnection.source, outConnection.target, inConnection)) {
44767
44768 // compute new, combined waypoints
44769 var newWaypoints = getNewWaypoints(inConnection.waypoints, outConnection.waypoints);
44770
44771 modeling.reconnectEnd(inConnection, outConnection.target, newWaypoints);
44772 }
44773 });
44774
44775 }
44776
44777 inherits$1(RemoveElementBehavior, CommandInterceptor);
44778
44779 RemoveElementBehavior.$inject = [
44780 'eventBus',
44781 'bpmnRules',
44782 'modeling'
44783 ];
44784
44785
44786 // helpers //////////////////////
44787
44788 function getDocking$1(point) {
44789 return point.original || point;
44790 }
44791
44792
44793 function getNewWaypoints(inWaypoints, outWaypoints) {
44794
44795 var intersection = lineIntersect(
44796 getDocking$1(inWaypoints[inWaypoints.length - 2]),
44797 getDocking$1(inWaypoints[inWaypoints.length - 1]),
44798 getDocking$1(outWaypoints[1]),
44799 getDocking$1(outWaypoints[0]));
44800
44801 if (intersection) {
44802 return [].concat(
44803 inWaypoints.slice(0, inWaypoints.length - 1),
44804 [ intersection ],
44805 outWaypoints.slice(1));
44806 } else {
44807 return [
44808 getDocking$1(inWaypoints[0]),
44809 getDocking$1(outWaypoints[outWaypoints.length - 1])
44810 ];
44811 }
44812 }
44813
44814 /**
44815 * BPMN specific remove behavior
44816 */
44817 function RemoveParticipantBehavior(eventBus, modeling) {
44818
44819 CommandInterceptor.call(this, eventBus);
44820
44821
44822 /**
44823 * morph collaboration diagram into process diagram
44824 * after the last participant has been removed
44825 */
44826
44827 this.preExecute('shape.delete', function(context) {
44828
44829 var shape = context.shape,
44830 parent = shape.parent;
44831
44832 // activate the behavior if the shape to be removed
44833 // is a participant
44834 if (is$1(shape, 'bpmn:Participant')) {
44835 context.collaborationRoot = parent;
44836 }
44837 }, true);
44838
44839 this.postExecute('shape.delete', function(context) {
44840
44841 var collaborationRoot = context.collaborationRoot;
44842
44843 if (collaborationRoot && !collaborationRoot.businessObject.participants.length) {
44844
44845 // replace empty collaboration with process diagram
44846 modeling.makeProcess();
44847 }
44848 }, true);
44849
44850 }
44851
44852 RemoveParticipantBehavior.$inject = [ 'eventBus', 'modeling' ];
44853
44854 inherits$1(RemoveParticipantBehavior, CommandInterceptor);
44855
44856 function ReplaceConnectionBehavior(eventBus, modeling, bpmnRules, injector) {
44857
44858 CommandInterceptor.call(this, eventBus);
44859
44860 var dragging = injector.get('dragging', false);
44861
44862 function fixConnection(connection) {
44863
44864 var source = connection.source,
44865 target = connection.target,
44866 parent = connection.parent;
44867
44868 // do not do anything if connection
44869 // is already deleted (may happen due to other
44870 // behaviors plugged-in before)
44871 if (!parent) {
44872 return;
44873 }
44874
44875 var replacementType,
44876 remove;
44877
44878 /**
44879 * Check if incoming or outgoing connections
44880 * can stay or could be substituted with an
44881 * appropriate replacement.
44882 *
44883 * This holds true for SequenceFlow <> MessageFlow.
44884 */
44885
44886 if (is$1(connection, 'bpmn:SequenceFlow')) {
44887 if (!bpmnRules.canConnectSequenceFlow(source, target)) {
44888 remove = true;
44889 }
44890
44891 if (bpmnRules.canConnectMessageFlow(source, target)) {
44892 replacementType = 'bpmn:MessageFlow';
44893 }
44894 }
44895
44896 // transform message flows into sequence flows, if possible
44897
44898 if (is$1(connection, 'bpmn:MessageFlow')) {
44899
44900 if (!bpmnRules.canConnectMessageFlow(source, target)) {
44901 remove = true;
44902 }
44903
44904 if (bpmnRules.canConnectSequenceFlow(source, target)) {
44905 replacementType = 'bpmn:SequenceFlow';
44906 }
44907 }
44908
44909 if (is$1(connection, 'bpmn:Association') && !bpmnRules.canConnectAssociation(source, target)) {
44910 remove = true;
44911 }
44912
44913
44914 // remove invalid connection,
44915 // unless it has been removed already
44916 if (remove) {
44917 modeling.removeConnection(connection);
44918 }
44919
44920 // replace SequenceFlow <> MessageFlow
44921
44922 if (replacementType) {
44923 modeling.connect(source, target, {
44924 type: replacementType,
44925 waypoints: connection.waypoints.slice()
44926 });
44927 }
44928 }
44929
44930 function replaceReconnectedConnection(event) {
44931
44932 var context = event.context,
44933 connection = context.connection,
44934 source = context.newSource || connection.source,
44935 target = context.newTarget || connection.target,
44936 allowed,
44937 replacement;
44938
44939 allowed = bpmnRules.canConnect(source, target);
44940
44941 if (!allowed || allowed.type === connection.type) {
44942 return;
44943 }
44944
44945 replacement = modeling.connect(source, target, {
44946 type: allowed.type,
44947 waypoints: connection.waypoints.slice()
44948 });
44949
44950 // remove old connection
44951 modeling.removeConnection(connection);
44952
44953 // replace connection in context to reconnect end/start
44954 context.connection = replacement;
44955
44956 if (dragging) {
44957 cleanDraggingSelection(connection, replacement);
44958 }
44959 }
44960
44961 // monkey-patch selection saved in dragging in order to re-select it when operation is finished
44962 function cleanDraggingSelection(oldConnection, newConnection) {
44963 var context = dragging.context(),
44964 previousSelection = context && context.payload.previousSelection,
44965 index;
44966
44967 // do nothing if not dragging or no selection was present
44968 if (!previousSelection || !previousSelection.length) {
44969 return;
44970 }
44971
44972 index = previousSelection.indexOf(oldConnection);
44973
44974 if (index === -1) {
44975 return;
44976 }
44977
44978 previousSelection.splice(index, 1, newConnection);
44979 }
44980
44981 // lifecycle hooks
44982
44983 this.postExecuted('elements.move', function(context) {
44984
44985 var closure = context.closure,
44986 allConnections = closure.allConnections;
44987
44988 forEach$2(allConnections, fixConnection);
44989 }, true);
44990
44991 this.preExecute('connection.reconnect', replaceReconnectedConnection);
44992
44993 this.postExecuted('element.updateProperties', function(event) {
44994 var context = event.context,
44995 properties = context.properties,
44996 element = context.element,
44997 businessObject = element.businessObject,
44998 connection;
44999
45000 // remove condition on change to default
45001 if (properties.default) {
45002 connection = find(
45003 element.outgoing,
45004 matchPattern({ id: element.businessObject.default.id })
45005 );
45006
45007 if (connection) {
45008 modeling.updateProperties(connection, { conditionExpression: undefined });
45009 }
45010 }
45011
45012 // remove default from source on change to conditional
45013 if (properties.conditionExpression && businessObject.sourceRef.default === businessObject) {
45014 modeling.updateProperties(element.source, { default: undefined });
45015 }
45016 });
45017 }
45018
45019 inherits$1(ReplaceConnectionBehavior, CommandInterceptor);
45020
45021 ReplaceConnectionBehavior.$inject = [
45022 'eventBus',
45023 'modeling',
45024 'bpmnRules',
45025 'injector'
45026 ];
45027
45028 /**
45029 * BPMN-specific replace behavior.
45030 */
45031 function ReplaceElementBehaviour(
45032 bpmnReplace,
45033 bpmnRules,
45034 elementRegistry,
45035 injector,
45036 modeling,
45037 selection
45038 ) {
45039 injector.invoke(CommandInterceptor, this);
45040
45041 this._bpmnReplace = bpmnReplace;
45042 this._elementRegistry = elementRegistry;
45043 this._selection = selection;
45044
45045 // replace elements on create, e.g. during copy-paste
45046 this.postExecuted([ 'elements.create' ], 500, function(event) {
45047 var context = event.context,
45048 target = context.parent,
45049 elements = context.elements;
45050
45051 var canReplace = bpmnRules.canReplace(elements, target);
45052
45053 if (canReplace) {
45054 this.replaceElements(elements, canReplace.replacements);
45055 }
45056 }, this);
45057
45058 // replace elements on move
45059 this.postExecuted([ 'elements.move' ], 500, function(event) {
45060 var context = event.context,
45061 target = context.newParent,
45062 newHost = context.newHost,
45063 elements = [];
45064
45065 forEach$2(context.closure.topLevel, function(topLevelElements) {
45066 if (isEventSubProcess(topLevelElements)) {
45067 elements = elements.concat(topLevelElements.children);
45068 } else {
45069 elements = elements.concat(topLevelElements);
45070 }
45071 });
45072
45073 // set target to host if attaching
45074 if (elements.length === 1 && newHost) {
45075 target = newHost;
45076 }
45077
45078 var canReplace = bpmnRules.canReplace(elements, target);
45079
45080 if (canReplace) {
45081 this.replaceElements(elements, canReplace.replacements, newHost);
45082 }
45083 }, this);
45084
45085 // update attachments on host replace
45086 this.postExecute([ 'shape.replace' ], 1500, function(e) {
45087 var context = e.context,
45088 oldShape = context.oldShape,
45089 newShape = context.newShape,
45090 attachers = oldShape.attachers,
45091 canReplace;
45092
45093 if (attachers && attachers.length) {
45094 canReplace = bpmnRules.canReplace(attachers, newShape);
45095
45096 this.replaceElements(attachers, canReplace.replacements);
45097 }
45098
45099 }, this);
45100
45101 // keep ID on shape replace
45102 this.postExecuted([ 'shape.replace' ], 1500, function(e) {
45103 var context = e.context,
45104 oldShape = context.oldShape,
45105 newShape = context.newShape;
45106
45107 modeling.unclaimId(oldShape.businessObject.id, oldShape.businessObject);
45108 modeling.updateProperties(newShape, { id: oldShape.id });
45109 });
45110 }
45111
45112 inherits$1(ReplaceElementBehaviour, CommandInterceptor);
45113
45114 ReplaceElementBehaviour.prototype.replaceElements = function(elements, newElements) {
45115 var elementRegistry = this._elementRegistry,
45116 bpmnReplace = this._bpmnReplace,
45117 selection = this._selection;
45118
45119 forEach$2(newElements, function(replacement) {
45120 var newElement = {
45121 type: replacement.newElementType
45122 };
45123
45124 var oldElement = elementRegistry.get(replacement.oldElementId);
45125
45126 var idx = elements.indexOf(oldElement);
45127
45128 elements[idx] = bpmnReplace.replaceElement(oldElement, newElement, { select: false });
45129 });
45130
45131 if (newElements) {
45132 selection.select(elements);
45133 }
45134 };
45135
45136 ReplaceElementBehaviour.$inject = [
45137 'bpmnReplace',
45138 'bpmnRules',
45139 'elementRegistry',
45140 'injector',
45141 'modeling',
45142 'selection'
45143 ];
45144
45145 var HIGH_PRIORITY$9 = 1500;
45146
45147 var LANE_MIN_DIMENSIONS = { width: 300, height: 60 };
45148
45149 var PARTICIPANT_MIN_DIMENSIONS = { width: 300, height: 150 };
45150
45151 var SUB_PROCESS_MIN_DIMENSIONS = { width: 140, height: 120 };
45152
45153 var TEXT_ANNOTATION_MIN_DIMENSIONS = { width: 50, height: 30 };
45154
45155 /**
45156 * Set minimum bounds/resize constraints on resize.
45157 *
45158 * @param {EventBus} eventBus
45159 */
45160 function ResizeBehavior(eventBus) {
45161 eventBus.on('resize.start', HIGH_PRIORITY$9, function(event) {
45162 var context = event.context,
45163 shape = context.shape,
45164 direction = context.direction,
45165 balanced = context.balanced;
45166
45167 if (is$1(shape, 'bpmn:Lane') || is$1(shape, 'bpmn:Participant')) {
45168 context.resizeConstraints = getParticipantResizeConstraints(shape, direction, balanced);
45169 }
45170
45171 if (is$1(shape, 'bpmn:Participant')) {
45172 context.minDimensions = PARTICIPANT_MIN_DIMENSIONS;
45173 }
45174
45175 if (is$1(shape, 'bpmn:SubProcess') && isExpanded(shape)) {
45176 context.minDimensions = SUB_PROCESS_MIN_DIMENSIONS;
45177 }
45178
45179 if (is$1(shape, 'bpmn:TextAnnotation')) {
45180 context.minDimensions = TEXT_ANNOTATION_MIN_DIMENSIONS;
45181 }
45182 });
45183 }
45184
45185 ResizeBehavior.$inject = [ 'eventBus' ];
45186
45187
45188 var abs$2 = Math.abs,
45189 min = Math.min,
45190 max$2 = Math.max;
45191
45192
45193 function addToTrbl(trbl, attr, value, choice) {
45194 var current = trbl[attr];
45195
45196 // make sure to set the value if it does not exist
45197 // or apply the correct value by comparing against
45198 // choice(value, currentValue)
45199 trbl[attr] = current === undefined ? value : choice(value, current);
45200 }
45201
45202 function addMin(trbl, attr, value) {
45203 return addToTrbl(trbl, attr, value, min);
45204 }
45205
45206 function addMax(trbl, attr, value) {
45207 return addToTrbl(trbl, attr, value, max$2);
45208 }
45209
45210 var LANE_RIGHT_PADDING = 20,
45211 LANE_LEFT_PADDING = 50,
45212 LANE_TOP_PADDING = 20,
45213 LANE_BOTTOM_PADDING = 20;
45214
45215 function getParticipantResizeConstraints(laneShape, resizeDirection, balanced) {
45216 var lanesRoot = getLanesRoot(laneShape);
45217
45218 var isFirst = true,
45219 isLast = true;
45220
45221 // max top/bottom size for lanes
45222 var allLanes = collectLanes(lanesRoot, [ lanesRoot ]);
45223
45224 var laneTrbl = asTRBL(laneShape);
45225
45226 var maxTrbl = {},
45227 minTrbl = {};
45228
45229 if (/e/.test(resizeDirection)) {
45230 minTrbl.right = laneTrbl.left + LANE_MIN_DIMENSIONS.width;
45231 } else
45232 if (/w/.test(resizeDirection)) {
45233 minTrbl.left = laneTrbl.right - LANE_MIN_DIMENSIONS.width;
45234 }
45235
45236 allLanes.forEach(function(other) {
45237
45238 var otherTrbl = asTRBL(other);
45239
45240 if (/n/.test(resizeDirection)) {
45241
45242 if (otherTrbl.top < (laneTrbl.top - 10)) {
45243 isFirst = false;
45244 }
45245
45246 // max top size (based on next element)
45247 if (balanced && abs$2(laneTrbl.top - otherTrbl.bottom) < 10) {
45248 addMax(maxTrbl, 'top', otherTrbl.top + LANE_MIN_DIMENSIONS.height);
45249 }
45250
45251 // min top size (based on self or nested element)
45252 if (abs$2(laneTrbl.top - otherTrbl.top) < 5) {
45253 addMin(minTrbl, 'top', otherTrbl.bottom - LANE_MIN_DIMENSIONS.height);
45254 }
45255 }
45256
45257 if (/s/.test(resizeDirection)) {
45258
45259 if (otherTrbl.bottom > (laneTrbl.bottom + 10)) {
45260 isLast = false;
45261 }
45262
45263 // max bottom size (based on previous element)
45264 if (balanced && abs$2(laneTrbl.bottom - otherTrbl.top) < 10) {
45265 addMin(maxTrbl, 'bottom', otherTrbl.bottom - LANE_MIN_DIMENSIONS.height);
45266 }
45267
45268 // min bottom size (based on self or nested element)
45269 if (abs$2(laneTrbl.bottom - otherTrbl.bottom) < 5) {
45270 addMax(minTrbl, 'bottom', otherTrbl.top + LANE_MIN_DIMENSIONS.height);
45271 }
45272 }
45273 });
45274
45275 // max top/bottom/left/right size based on flow nodes
45276 var flowElements = lanesRoot.children.filter(function(s) {
45277 return !s.hidden && !s.waypoints && (is$1(s, 'bpmn:FlowElement') || is$1(s, 'bpmn:Artifact'));
45278 });
45279
45280 flowElements.forEach(function(flowElement) {
45281
45282 var flowElementTrbl = asTRBL(flowElement);
45283
45284 if (isFirst && /n/.test(resizeDirection)) {
45285 addMin(minTrbl, 'top', flowElementTrbl.top - LANE_TOP_PADDING);
45286 }
45287
45288 if (/e/.test(resizeDirection)) {
45289 addMax(minTrbl, 'right', flowElementTrbl.right + LANE_RIGHT_PADDING);
45290 }
45291
45292 if (isLast && /s/.test(resizeDirection)) {
45293 addMax(minTrbl, 'bottom', flowElementTrbl.bottom + LANE_BOTTOM_PADDING);
45294 }
45295
45296 if (/w/.test(resizeDirection)) {
45297 addMin(minTrbl, 'left', flowElementTrbl.left - LANE_LEFT_PADDING);
45298 }
45299 });
45300
45301 return {
45302 min: minTrbl,
45303 max: maxTrbl
45304 };
45305 }
45306
45307 var SLIGHTLY_HIGHER_PRIORITY = 1001;
45308
45309
45310 /**
45311 * Invoke {@link Modeling#resizeLane} instead of
45312 * {@link Modeling#resizeShape} when resizing a Lane
45313 * or Participant shape.
45314 */
45315 function ResizeLaneBehavior(eventBus, modeling) {
45316
45317 eventBus.on('resize.start', SLIGHTLY_HIGHER_PRIORITY + 500, function(event) {
45318 var context = event.context,
45319 shape = context.shape;
45320
45321 if (is$1(shape, 'bpmn:Lane') || is$1(shape, 'bpmn:Participant')) {
45322
45323 // should we resize the opposite lane(s) in
45324 // order to compensate for the resize operation?
45325 context.balanced = !hasPrimaryModifier(event);
45326 }
45327 });
45328
45329 /**
45330 * Intercept resize end and call resize lane function instead.
45331 */
45332 eventBus.on('resize.end', SLIGHTLY_HIGHER_PRIORITY, function(event) {
45333 var context = event.context,
45334 shape = context.shape,
45335 canExecute = context.canExecute,
45336 newBounds = context.newBounds;
45337
45338 if (is$1(shape, 'bpmn:Lane') || is$1(shape, 'bpmn:Participant')) {
45339
45340 if (canExecute) {
45341
45342 // ensure we have actual pixel values for new bounds
45343 // (important when zoom level was > 1 during move)
45344 newBounds = roundBounds(newBounds);
45345
45346 // perform the actual resize
45347 modeling.resizeLane(shape, newBounds, context.balanced);
45348 }
45349
45350 // stop propagation
45351 return false;
45352 }
45353 });
45354 }
45355
45356 ResizeLaneBehavior.$inject = [
45357 'eventBus',
45358 'modeling'
45359 ];
45360
45361 var LOW_PRIORITY$a = 500;
45362
45363
45364 /**
45365 * Add referenced root elements (error, escalation, message, signal) if they don't exist.
45366 * Copy referenced root elements on copy & paste.
45367 */
45368 function RootElementReferenceBehavior(
45369 bpmnjs, eventBus, injector, moddleCopy, bpmnFactory
45370 ) {
45371 injector.invoke(CommandInterceptor, this);
45372
45373 function canHaveRootElementReference(element) {
45374 return isAny(element, [ 'bpmn:ReceiveTask', 'bpmn:SendTask' ]) ||
45375 hasAnyEventDefinition(element, [
45376 'bpmn:ErrorEventDefinition',
45377 'bpmn:EscalationEventDefinition',
45378 'bpmn:MessageEventDefinition',
45379 'bpmn:SignalEventDefinition'
45380 ]);
45381 }
45382
45383 function hasRootElement(rootElement) {
45384 var definitions = bpmnjs.getDefinitions(),
45385 rootElements = definitions.get('rootElements');
45386
45387 return !!find(rootElements, matchPattern({ id: rootElement.id }));
45388 }
45389
45390 function getRootElementReferencePropertyName(eventDefinition) {
45391 if (is$1(eventDefinition, 'bpmn:ErrorEventDefinition')) {
45392 return 'errorRef';
45393 } else if (is$1(eventDefinition, 'bpmn:EscalationEventDefinition')) {
45394 return 'escalationRef';
45395 } else if (is$1(eventDefinition, 'bpmn:MessageEventDefinition')) {
45396 return 'messageRef';
45397 } else if (is$1(eventDefinition, 'bpmn:SignalEventDefinition')) {
45398 return 'signalRef';
45399 }
45400 }
45401
45402 function getRootElement(businessObject) {
45403 if (isAny(businessObject, [ 'bpmn:ReceiveTask', 'bpmn:SendTask' ])) {
45404 return businessObject.get('messageRef');
45405 }
45406
45407 var eventDefinitions = businessObject.get('eventDefinitions'),
45408 eventDefinition = eventDefinitions[ 0 ];
45409
45410 return eventDefinition.get(getRootElementReferencePropertyName(eventDefinition));
45411 }
45412
45413 function setRootElement(businessObject, rootElement) {
45414 if (isAny(businessObject, [ 'bpmn:ReceiveTask', 'bpmn:SendTask' ])) {
45415 return businessObject.set('messageRef', rootElement);
45416 }
45417
45418 var eventDefinitions = businessObject.get('eventDefinitions'),
45419 eventDefinition = eventDefinitions[ 0 ];
45420
45421 return eventDefinition.set(getRootElementReferencePropertyName(eventDefinition), rootElement);
45422 }
45423
45424 // create shape
45425 this.executed('shape.create', function(context) {
45426 var shape = context.shape;
45427
45428 if (!canHaveRootElementReference(shape)) {
45429 return;
45430 }
45431
45432 var businessObject = getBusinessObject(shape),
45433 rootElement = getRootElement(businessObject),
45434 rootElements;
45435
45436 if (rootElement && !hasRootElement(rootElement)) {
45437 rootElements = bpmnjs.getDefinitions().get('rootElements');
45438
45439 // add root element
45440 add(rootElements, rootElement);
45441
45442 context.addedRootElement = rootElement;
45443 }
45444 }, true);
45445
45446 this.reverted('shape.create', function(context) {
45447 var addedRootElement = context.addedRootElement;
45448
45449 if (!addedRootElement) {
45450 return;
45451 }
45452
45453 var rootElements = bpmnjs.getDefinitions().get('rootElements');
45454
45455 // remove root element
45456 remove(rootElements, addedRootElement);
45457 }, true);
45458
45459 eventBus.on('copyPaste.copyElement', function(context) {
45460 var descriptor = context.descriptor,
45461 element = context.element;
45462
45463 if (!canHaveRootElementReference(element)) {
45464 return;
45465 }
45466
45467 var businessObject = getBusinessObject(element),
45468 rootElement = getRootElement(businessObject);
45469
45470 if (rootElement) {
45471 descriptor.referencedRootElement = rootElement;
45472 }
45473 });
45474
45475 eventBus.on('copyPaste.pasteElement', LOW_PRIORITY$a, function(context) {
45476 var descriptor = context.descriptor,
45477 businessObject = descriptor.businessObject;
45478
45479 if (!canHaveRootElementReference(businessObject)) {
45480 return;
45481 }
45482
45483 var referencedRootElement = descriptor.referencedRootElement;
45484
45485 if (!referencedRootElement) {
45486 return;
45487 }
45488
45489 if (!hasRootElement(referencedRootElement)) {
45490 referencedRootElement = moddleCopy.copyElement(
45491 referencedRootElement,
45492 bpmnFactory.create(referencedRootElement.$type)
45493 );
45494 }
45495
45496 setRootElement(businessObject, referencedRootElement);
45497 });
45498 }
45499
45500 RootElementReferenceBehavior.$inject = [
45501 'bpmnjs',
45502 'eventBus',
45503 'injector',
45504 'moddleCopy',
45505 'bpmnFactory'
45506 ];
45507
45508 inherits$1(RootElementReferenceBehavior, CommandInterceptor);
45509
45510 // helpers //////////
45511
45512 function hasAnyEventDefinition(element, types) {
45513 if (!isArray$4(types)) {
45514 types = [ types ];
45515 }
45516
45517 return some(types, function(type) {
45518 return hasEventDefinition$2(element, type);
45519 });
45520 }
45521
45522 var max$1 = Math.max;
45523
45524
45525 function SpaceToolBehavior(eventBus) {
45526 eventBus.on('spaceTool.getMinDimensions', function(context) {
45527 var shapes = context.shapes,
45528 axis = context.axis,
45529 start = context.start,
45530 minDimensions = {};
45531
45532 forEach$2(shapes, function(shape) {
45533 var id = shape.id;
45534
45535 if (is$1(shape, 'bpmn:Participant')) {
45536
45537 if (isHorizontal$1(axis)) {
45538 minDimensions[ id ] = PARTICIPANT_MIN_DIMENSIONS;
45539 } else {
45540 minDimensions[ id ] = {
45541 width: PARTICIPANT_MIN_DIMENSIONS.width,
45542 height: getParticipantMinHeight(shape, start)
45543 };
45544 }
45545
45546 }
45547
45548 if (is$1(shape, 'bpmn:SubProcess') && isExpanded(shape)) {
45549 minDimensions[ id ] = SUB_PROCESS_MIN_DIMENSIONS;
45550 }
45551
45552 if (is$1(shape, 'bpmn:TextAnnotation')) {
45553 minDimensions[ id ] = TEXT_ANNOTATION_MIN_DIMENSIONS;
45554 }
45555 });
45556
45557 return minDimensions;
45558 });
45559 }
45560
45561 SpaceToolBehavior.$inject = [ 'eventBus' ];
45562
45563
45564 // helpers //////////
45565 function isHorizontal$1(axis) {
45566 return axis === 'x';
45567 }
45568
45569 /**
45570 * Get minimum height for participant taking lanes into account.
45571 *
45572 * @param {<djs.model.Shape>} participant
45573 * @param {number} start
45574 *
45575 * @returns {Object}
45576 */
45577 function getParticipantMinHeight(participant, start) {
45578 var lanesMinHeight;
45579
45580 if (!hasChildLanes(participant)) {
45581 return PARTICIPANT_MIN_DIMENSIONS.height;
45582 }
45583
45584 lanesMinHeight = getLanesMinHeight(participant, start);
45585
45586 return max$1(PARTICIPANT_MIN_DIMENSIONS.height, lanesMinHeight);
45587 }
45588
45589 function hasChildLanes(element) {
45590 return !!getChildLanes(element).length;
45591 }
45592
45593 function getLanesMinHeight(participant, resizeStart) {
45594 var lanes = getChildLanes(participant),
45595 resizedLane;
45596
45597 // find the nested lane which is currently resized
45598 resizedLane = findResizedLane(lanes, resizeStart);
45599
45600 // resized lane cannot shrink below the minimum height
45601 // but remaining lanes' dimensions are kept intact
45602 return participant.height - resizedLane.height + LANE_MIN_DIMENSIONS.height;
45603 }
45604
45605 /**
45606 * Find nested lane which is currently resized.
45607 *
45608 * @param {Array<djs.model.Shape>} lanes
45609 * @param {number} resizeStart
45610 */
45611 function findResizedLane(lanes, resizeStart) {
45612 var i, lane, childLanes;
45613
45614 for (i = 0; i < lanes.length; i++) {
45615 lane = lanes[i];
45616
45617 // resizing current lane or a lane nested
45618 if (resizeStart >= lane.y && resizeStart <= lane.y + lane.height) {
45619 childLanes = getChildLanes(lane);
45620
45621 // a nested lane is resized
45622 if (childLanes.length) {
45623 return findResizedLane(childLanes, resizeStart);
45624 }
45625
45626 // current lane is the resized one
45627 return lane;
45628 }
45629 }
45630 }
45631
45632 var LOW_PRIORITY$9 = 400;
45633 var HIGH_PRIORITY$8 = 600;
45634
45635 var DEFAULT_POSITION = {
45636 x: 180,
45637 y: 160
45638 };
45639
45640
45641 /**
45642 * Creates bpmndi:BPMNPlane elements and canvas planes when collapsed subprocesses are created.
45643 *
45644 *
45645 * @param {Canvas} canvas
45646 * @param {EventBus} eventBus
45647 * @param {Modeling} modeling
45648 * @param {ElementFactory} elementFactory
45649 * @param {BpmnFactory} bpmnFactory
45650 * @param {Bpmnjs} bpmnjs
45651 * @param {ElementRegistry} elementRegistry
45652 */
45653 function SubProcessPlaneBehavior(
45654 canvas, eventBus, modeling,
45655 elementFactory, bpmnFactory, bpmnjs, elementRegistry) {
45656
45657 CommandInterceptor.call(this, eventBus);
45658
45659 this._canvas = canvas;
45660 this._eventBus = eventBus;
45661 this._modeling = modeling;
45662 this._elementFactory = elementFactory;
45663 this._bpmnFactory = bpmnFactory;
45664 this._bpmnjs = bpmnjs;
45665 this._elementRegistry = elementRegistry;
45666
45667 var self = this;
45668
45669 function isCollapsedSubProcess(element) {
45670 return is$1(element, 'bpmn:SubProcess') && !isExpanded(element);
45671 }
45672
45673 function createRoot(context) {
45674 var shape = context.shape,
45675 rootElement = context.newRootElement;
45676
45677 var businessObject = getBusinessObject(shape);
45678
45679 rootElement = self._addDiagram(rootElement || businessObject);
45680
45681 context.newRootElement = canvas.addRootElement(rootElement);
45682 }
45683
45684 function removeRoot(context) {
45685 var shape = context.shape;
45686
45687 var businessObject = getBusinessObject(shape);
45688 self._removeDiagram(businessObject);
45689
45690 var rootElement = context.newRootElement = elementRegistry.get(getPlaneIdFromShape(businessObject));
45691
45692 canvas.removeRootElement(rootElement);
45693 }
45694
45695 // add plane elements for newly created sub-processes
45696 // this ensures we can actually drill down into the element
45697 this.executed('shape.create', function(context) {
45698 var shape = context.shape;
45699 if (!isCollapsedSubProcess(shape)) {
45700 return;
45701 }
45702
45703 createRoot(context);
45704 }, true);
45705
45706
45707 this.postExecuted('shape.create', function(context) {
45708 var shape = context.shape,
45709 rootElement = context.newRootElement;
45710
45711 if (!rootElement || !shape.children) {
45712 return;
45713 }
45714
45715 self._showRecursively(shape.children);
45716
45717 self._moveChildrenToShape(shape, rootElement);
45718 }, true);
45719
45720
45721 this.reverted('shape.create', function(context) {
45722 var shape = context.shape;
45723 if (!isCollapsedSubProcess(shape)) {
45724 return;
45725 }
45726
45727 removeRoot(context);
45728 }, true);
45729
45730
45731 this.preExecuted('shape.delete', function(context) {
45732 var shape = context.shape;
45733 if (!isCollapsedSubProcess(shape)) {
45734 return;
45735 }
45736
45737 var attachedRoot = elementRegistry.get(getPlaneIdFromShape(shape));
45738
45739 if (!attachedRoot) {
45740 return;
45741 }
45742
45743 modeling.removeElements(attachedRoot.children.slice());
45744 }, true);
45745
45746
45747 this.executed('shape.delete', function(context) {
45748 var shape = context.shape;
45749 if (!isCollapsedSubProcess(shape)) {
45750 return;
45751 }
45752 removeRoot(context);
45753 }, true);
45754
45755
45756 this.reverted('shape.delete', function(context) {
45757 var shape = context.shape;
45758 if (!isCollapsedSubProcess(shape)) {
45759 return;
45760 }
45761
45762 createRoot(context);
45763 }, true);
45764
45765
45766 this.preExecuted('shape.replace', function(context) {
45767 var oldShape = context.oldShape;
45768 var newShape = context.newShape;
45769
45770 if (!isCollapsedSubProcess(oldShape) || !isCollapsedSubProcess(newShape)) {
45771 return;
45772 }
45773
45774 // old plane could have content,
45775 // we remove it so it is not recursively deleted from 'shape.delete'
45776 context.oldRoot = canvas.removeRootElement(getPlaneIdFromShape(oldShape));
45777 }, true);
45778
45779
45780 this.postExecuted('shape.replace', function(context) {
45781 var newShape = context.newShape,
45782 source = context.oldRoot,
45783 target = canvas.findRoot(getPlaneIdFromShape(newShape));
45784
45785 if (!source || !target) {
45786 return;
45787 }
45788 var elements = source.children;
45789
45790 modeling.moveElements(elements, { x: 0, y: 0 }, target);
45791 }, true);
45792
45793
45794 // rename primary elements when the secondary element changes
45795 // this ensures rootElement.id = element.id + '_plane'
45796 this.executed('element.updateProperties', function(context) {
45797 var shape = context.element;
45798
45799 if (!is$1(shape, 'bpmn:SubProcess')) {
45800 return;
45801 }
45802
45803 var properties = context.properties;
45804 var oldProperties = context.oldProperties;
45805
45806 var oldId = oldProperties.id,
45807 newId = properties.id;
45808
45809 if (oldId === newId) {
45810 return;
45811 }
45812
45813 if (isPlane(shape)) {
45814 elementRegistry.updateId(shape, toPlaneId(newId));
45815 elementRegistry.updateId(oldId, newId);
45816
45817 return;
45818 }
45819
45820 var planeElement = elementRegistry.get(toPlaneId(oldId));
45821
45822 if (!planeElement) {
45823 return;
45824 }
45825
45826 elementRegistry.updateId(toPlaneId(oldId), toPlaneId(newId));
45827 }, true);
45828
45829
45830 this.reverted('element.updateProperties', function(context) {
45831 var shape = context.element;
45832
45833 if (!is$1(shape, 'bpmn:SubProcess')) {
45834 return;
45835 }
45836
45837 var properties = context.properties;
45838 var oldProperties = context.oldProperties;
45839
45840 var oldId = oldProperties.id,
45841 newId = properties.id;
45842
45843 if (oldId === newId) {
45844 return;
45845 }
45846
45847 if (isPlane(shape)) {
45848 elementRegistry.updateId(shape, toPlaneId(oldId));
45849 elementRegistry.updateId(newId, oldId);
45850
45851 return;
45852 }
45853
45854 var planeElement = elementRegistry.get(toPlaneId(newId));
45855
45856 if (!planeElement) {
45857 return;
45858 }
45859
45860 elementRegistry.updateId(planeElement, toPlaneId(oldId));
45861 }, true);
45862
45863 // re-throw element.changed to re-render primary shape if associated plane has
45864 // changed (e.g. bpmn:name property has changed)
45865 eventBus.on('element.changed', function(context) {
45866 var element = context.element;
45867
45868 if (!isPlane(element)) {
45869 return;
45870 }
45871
45872 var plane = element;
45873
45874 var primaryShape = elementRegistry.get(getShapeIdFromPlane(plane));
45875
45876 // do not re-throw if no associated primary shape (e.g. bpmn:Process)
45877 if (!primaryShape || primaryShape === plane) {
45878 return;
45879 }
45880
45881 eventBus.fire('element.changed', { element: primaryShape });
45882 });
45883
45884
45885 // create/remove plane for the subprocess
45886 this.executed('shape.toggleCollapse', LOW_PRIORITY$9, function(context) {
45887 var shape = context.shape;
45888
45889 if (!is$1(shape, 'bpmn:SubProcess')) {
45890 return;
45891 }
45892
45893 if (!isExpanded(shape)) {
45894 createRoot(context);
45895 self._showRecursively(shape.children);
45896 } else {
45897 removeRoot(context);
45898 }
45899
45900 }, true);
45901
45902
45903 // create/remove plane for the subprocess
45904 this.reverted('shape.toggleCollapse', LOW_PRIORITY$9, function(context) {
45905 var shape = context.shape;
45906
45907 if (!is$1(shape, 'bpmn:SubProcess')) {
45908 return;
45909 }
45910
45911 if (!isExpanded(shape)) {
45912 createRoot(context);
45913 self._showRecursively(shape.children);
45914 } else {
45915 removeRoot(context);
45916 }
45917
45918 }, true);
45919
45920 // move elements between planes
45921 this.postExecuted('shape.toggleCollapse', HIGH_PRIORITY$8, function(context) {
45922 var shape = context.shape;
45923
45924 if (!is$1(shape, 'bpmn:SubProcess')) {
45925 return;
45926 }
45927
45928 var rootElement = context.newRootElement;
45929
45930 if (!rootElement) {
45931 return;
45932 }
45933
45934 if (!isExpanded(shape)) {
45935
45936 // collapsed
45937 self._moveChildrenToShape(shape, rootElement);
45938
45939 } else {
45940 self._moveChildrenToShape(rootElement, shape);
45941 }
45942 }, true);
45943
45944
45945 // copy-paste ///////////
45946
45947 // add elements in plane to tree
45948 eventBus.on('copyPaste.createTree', function(context) {
45949 var element = context.element,
45950 children = context.children;
45951
45952 if (!isCollapsedSubProcess(element)) {
45953 return;
45954 }
45955
45956 var id = getPlaneIdFromShape(element);
45957 var parent = elementRegistry.get(id);
45958
45959 if (parent) {
45960
45961 // do not copy invisible root element
45962 children.push.apply(children, parent.children);
45963 }
45964 });
45965
45966 // set plane children as direct children of collapsed shape
45967 eventBus.on('copyPaste.copyElement', function(context) {
45968 var descriptor = context.descriptor,
45969 element = context.element,
45970 elements = context.elements;
45971
45972 var parent = element.parent;
45973
45974 var isPlane = is$1(getDi(parent), 'bpmndi:BPMNPlane');
45975 if (!isPlane) {
45976 return;
45977 }
45978
45979 var parentId = getShapeIdFromPlane(parent);
45980
45981 var referencedShape = find(elements, function(element) {
45982 return element.id === parentId;
45983 });
45984
45985 if (!referencedShape) {
45986 return;
45987 }
45988
45989 descriptor.parent = referencedShape.id;
45990 });
45991
45992 // hide children during pasting
45993 eventBus.on('copyPaste.pasteElement', function(context) {
45994 var descriptor = context.descriptor;
45995
45996 if (!descriptor.parent) {
45997 return;
45998 }
45999
46000 if (isCollapsedSubProcess(descriptor.parent) || descriptor.parent.hidden) {
46001 descriptor.hidden = true;
46002 }
46003 });
46004
46005 }
46006
46007 inherits$1(SubProcessPlaneBehavior, CommandInterceptor);
46008
46009 /**
46010 * Moves the child elements from source to target.
46011 *
46012 * If the target is a plane, the children are moved to the top left corner.
46013 * Otherwise, the center of the target is used.
46014 *
46015 * @param {Object|djs.model.Base} source
46016 * @param {Object|djs.model.Base} target
46017 */
46018 SubProcessPlaneBehavior.prototype._moveChildrenToShape = function(source, target) {
46019 var modeling = this._modeling;
46020
46021 var children = source.children;
46022 var offset;
46023
46024 if (!children) {
46025 return;
46026 }
46027
46028 // only change plane if there are no visible children, but don't move them
46029 var visibleChildren = children.filter(function(child) {
46030 return !child.hidden;
46031 });
46032
46033 if (!visibleChildren.length) {
46034 modeling.moveElements(children, { x: 0, y: 0 }, target, { autoResize: false });
46035 return;
46036 }
46037
46038 var childrenBounds = getBBox(visibleChildren);
46039
46040 // target is a plane
46041 if (!target.x) {
46042 offset = {
46043 x: DEFAULT_POSITION.x - childrenBounds.x,
46044 y: DEFAULT_POSITION.y - childrenBounds.y
46045 };
46046 }
46047
46048 // source is a plane
46049 else {
46050
46051 // move relative to the center of the shape
46052 var targetMid = getMid(target);
46053 var childrenMid = getMid(childrenBounds);
46054
46055 offset = {
46056 x: targetMid.x - childrenMid.x,
46057 y: targetMid.y - childrenMid.y
46058 };
46059 }
46060
46061 modeling.moveElements(children, offset, target, { autoResize: false });
46062 };
46063
46064 /**
46065 * Sets `hidden` property on all children of the given shape.
46066 *
46067 * @param {Array} elements
46068 * @param {Boolean} [hidden]
46069 * @returns {Array} all child elements
46070 */
46071 SubProcessPlaneBehavior.prototype._showRecursively = function(elements, hidden) {
46072 var self = this;
46073
46074 var result = [];
46075 elements.forEach(function(element) {
46076 element.hidden = !!hidden;
46077
46078 result = result.concat(element);
46079
46080 if (element.children) {
46081 result = result.concat(
46082 self._showRecursively(element.children, element.collapsed || hidden)
46083 );
46084 }
46085 });
46086
46087 return result;
46088 };
46089
46090 /**
46091 * Adds a given rootElement to the bpmnDi diagrams.
46092 *
46093 * @param {Object} rootElement
46094 * @returns {Object} planeElement
46095 */
46096 SubProcessPlaneBehavior.prototype._addDiagram = function(planeElement) {
46097 var bpmnjs = this._bpmnjs;
46098 var diagrams = bpmnjs.getDefinitions().diagrams;
46099
46100 if (!planeElement.businessObject) {
46101 planeElement = this._createNewDiagram(planeElement);
46102 }
46103
46104 diagrams.push(planeElement.di.$parent);
46105
46106 return planeElement;
46107 };
46108
46109
46110 /**
46111 * Creates a new plane element for the given sub process.
46112 *
46113 * @param {Object} bpmnElement
46114 *
46115 * @return {Object} new diagram element
46116 */
46117 SubProcessPlaneBehavior.prototype._createNewDiagram = function(bpmnElement) {
46118 var bpmnFactory = this._bpmnFactory;
46119 var elementFactory = this._elementFactory;
46120
46121 var diPlane = bpmnFactory.create('bpmndi:BPMNPlane', {
46122 bpmnElement: bpmnElement
46123 });
46124 var diDiagram = bpmnFactory.create('bpmndi:BPMNDiagram', {
46125 plane: diPlane
46126 });
46127 diPlane.$parent = diDiagram;
46128
46129 // add a virtual element (not being drawn),
46130 // a copy cat of our BpmnImporter code
46131 var planeElement = elementFactory.createRoot({
46132 id: getPlaneIdFromShape(bpmnElement),
46133 type: bpmnElement.$type,
46134 di: diPlane,
46135 businessObject: bpmnElement,
46136 collapsed: true
46137 });
46138
46139 return planeElement;
46140 };
46141
46142 /**
46143 * Removes the diagram for a given root element
46144 *
46145 * @param {Object} rootElement
46146 * @returns {Object} removed bpmndi:BPMNDiagram
46147 */
46148 SubProcessPlaneBehavior.prototype._removeDiagram = function(rootElement) {
46149 var bpmnjs = this._bpmnjs;
46150
46151 var diagrams = bpmnjs.getDefinitions().diagrams;
46152
46153 var removedDiagram = find(diagrams, function(diagram) {
46154 return diagram.plane.bpmnElement.id === rootElement.id;
46155 });
46156
46157 diagrams.splice(diagrams.indexOf(removedDiagram), 1);
46158
46159 return removedDiagram;
46160 };
46161
46162
46163 SubProcessPlaneBehavior.$inject = [
46164 'canvas',
46165 'eventBus',
46166 'modeling',
46167 'elementFactory',
46168 'bpmnFactory',
46169 'bpmnjs',
46170 'elementRegistry'
46171 ];
46172
46173 /**
46174 * Add start event replacing element with expanded sub process.
46175 *
46176 * @param {Injector} injector
46177 * @param {Modeling} modeling
46178 */
46179 function SubProcessStartEventBehavior(injector, modeling) {
46180 injector.invoke(CommandInterceptor, this);
46181
46182 this.postExecuted('shape.replace', function(event) {
46183 var oldShape = event.context.oldShape,
46184 newShape = event.context.newShape;
46185
46186 if (
46187 !is$1(newShape, 'bpmn:SubProcess') ||
46188 ! (is$1(oldShape, 'bpmn:Task') || is$1(oldShape, 'bpmn:CallActivity')) ||
46189 !isExpanded(newShape)
46190 ) {
46191 return;
46192 }
46193
46194 var position = getStartEventPosition(newShape);
46195
46196 modeling.createShape({ type: 'bpmn:StartEvent' }, position, newShape);
46197 });
46198 }
46199
46200 SubProcessStartEventBehavior.$inject = [
46201 'injector',
46202 'modeling'
46203 ];
46204
46205 inherits$1(SubProcessStartEventBehavior, CommandInterceptor);
46206
46207 // helpers //////////
46208
46209 function getStartEventPosition(shape) {
46210 return {
46211 x: shape.x + shape.width / 6,
46212 y: shape.y + shape.height / 2
46213 };
46214 }
46215
46216 function ToggleCollapseConnectionBehaviour(
46217 eventBus, modeling
46218 ) {
46219
46220 CommandInterceptor.call(this, eventBus);
46221
46222 this.postExecuted('shape.toggleCollapse', 1500, function(context) {
46223
46224 // var shape = context.shape;
46225 var shape = context.shape;
46226
46227 // only change connections when collapsing
46228 if (isExpanded(shape)) {
46229 return;
46230 }
46231
46232 var allChildren = selfAndAllChildren(shape);
46233
46234 allChildren.forEach(function(child) {
46235
46236 // Ensure that the connection array is not modified during iteration
46237 var incomingConnections = child.incoming.slice(),
46238 outgoingConnections = child.outgoing.slice();
46239
46240 forEach$2(incomingConnections, function(c) {
46241 handleConnection(c, true);
46242 });
46243
46244 forEach$2(outgoingConnections, function(c) {
46245 handleConnection(c, false);
46246 });
46247 });
46248
46249
46250 function handleConnection(c, incoming) {
46251 if (allChildren.indexOf(c.source) !== -1 && allChildren.indexOf(c.target) !== -1) {
46252 return;
46253 }
46254
46255 if (incoming) {
46256 modeling.reconnectEnd(c, shape, getMid(shape));
46257 } else {
46258 modeling.reconnectStart(c, shape, getMid(shape));
46259 }
46260
46261 }
46262
46263 }, true);
46264
46265 }
46266
46267 inherits$1(ToggleCollapseConnectionBehaviour, CommandInterceptor);
46268
46269 ToggleCollapseConnectionBehaviour.$inject = [
46270 'eventBus',
46271 'modeling',
46272 ];
46273
46274 var LOW_PRIORITY$8 = 500;
46275
46276
46277 function ToggleElementCollapseBehaviour(
46278 eventBus, elementFactory, modeling,
46279 resize) {
46280
46281 CommandInterceptor.call(this, eventBus);
46282
46283
46284 function hideEmptyLabels(children) {
46285 if (children.length) {
46286 children.forEach(function(child) {
46287 if (child.type === 'label' && !child.businessObject.name) {
46288 child.hidden = true;
46289 }
46290 });
46291 }
46292 }
46293
46294 function expandedBounds(shape, defaultSize) {
46295 var children = shape.children,
46296 newBounds = defaultSize,
46297 visibleElements,
46298 visibleBBox;
46299
46300 visibleElements = filterVisible(children).concat([ shape ]);
46301
46302 visibleBBox = computeChildrenBBox(visibleElements);
46303
46304 if (visibleBBox) {
46305
46306 // center to visibleBBox with max(defaultSize, childrenBounds)
46307 newBounds.width = Math.max(visibleBBox.width, newBounds.width);
46308 newBounds.height = Math.max(visibleBBox.height, newBounds.height);
46309
46310 newBounds.x = visibleBBox.x + (visibleBBox.width - newBounds.width) / 2;
46311 newBounds.y = visibleBBox.y + (visibleBBox.height - newBounds.height) / 2;
46312 } else {
46313
46314 // center to collapsed shape with defaultSize
46315 newBounds.x = shape.x + (shape.width - newBounds.width) / 2;
46316 newBounds.y = shape.y + (shape.height - newBounds.height) / 2;
46317 }
46318
46319 return newBounds;
46320 }
46321
46322 function collapsedBounds(shape, defaultSize) {
46323
46324 return {
46325 x: shape.x + (shape.width - defaultSize.width) / 2,
46326 y: shape.y + (shape.height - defaultSize.height) / 2,
46327 width: defaultSize.width,
46328 height: defaultSize.height
46329 };
46330 }
46331
46332 this.executed([ 'shape.toggleCollapse' ], LOW_PRIORITY$8, function(e) {
46333
46334 var context = e.context,
46335 shape = context.shape;
46336
46337 if (!is$1(shape, 'bpmn:SubProcess')) {
46338 return;
46339 }
46340
46341 if (!shape.collapsed) {
46342
46343 // all children got made visible through djs, hide empty labels
46344 hideEmptyLabels(shape.children);
46345
46346 // remove collapsed marker
46347 getDi(shape).isExpanded = true;
46348 } else {
46349
46350 // place collapsed marker
46351 getDi(shape).isExpanded = false;
46352 }
46353 });
46354
46355 this.reverted([ 'shape.toggleCollapse' ], LOW_PRIORITY$8, function(e) {
46356
46357 var context = e.context;
46358 var shape = context.shape;
46359
46360
46361 // revert removing/placing collapsed marker
46362 if (!shape.collapsed) {
46363 getDi(shape).isExpanded = true;
46364
46365 } else {
46366 getDi(shape).isExpanded = false;
46367 }
46368 });
46369
46370 this.postExecuted([ 'shape.toggleCollapse' ], LOW_PRIORITY$8, function(e) {
46371 var shape = e.context.shape,
46372 defaultSize = elementFactory.getDefaultSize(shape),
46373 newBounds;
46374
46375 if (shape.collapsed) {
46376
46377 // resize to default size of collapsed shapes
46378 newBounds = collapsedBounds(shape, defaultSize);
46379 } else {
46380
46381 // resize to bounds of max(visible children, defaultSize)
46382 newBounds = expandedBounds(shape, defaultSize);
46383 }
46384
46385 modeling.resizeShape(shape, newBounds, null, {
46386 autoResize: shape.collapsed ? false : 'nwse'
46387 });
46388 });
46389
46390 }
46391
46392
46393 inherits$1(ToggleElementCollapseBehaviour, CommandInterceptor);
46394
46395 ToggleElementCollapseBehaviour.$inject = [
46396 'eventBus',
46397 'elementFactory',
46398 'modeling'
46399 ];
46400
46401
46402 // helpers //////////////////////
46403
46404 function filterVisible(elements) {
46405 return elements.filter(function(e) {
46406 return !e.hidden;
46407 });
46408 }
46409
46410 /**
46411 * Unclaims model IDs on element deletion.
46412 *
46413 * @param {Canvas} canvas
46414 * @param {Injector} injector
46415 * @param {Moddle} moddle
46416 * @param {Modeling} modeling
46417 */
46418 function UnclaimIdBehavior(canvas, injector, moddle, modeling) {
46419 injector.invoke(CommandInterceptor, this);
46420
46421 this.preExecute('shape.delete', function(event) {
46422 var context = event.context,
46423 shape = context.shape,
46424 shapeBo = shape.businessObject;
46425
46426 if (isLabel$6(shape)) {
46427 return;
46428 }
46429
46430 if (is$1(shape, 'bpmn:Participant') && isExpanded(shape)) {
46431 moddle.ids.unclaim(shapeBo.processRef.id);
46432 }
46433
46434 modeling.unclaimId(shapeBo.id, shapeBo);
46435 });
46436
46437
46438 this.preExecute('connection.delete', function(event) {
46439 var context = event.context,
46440 connection = context.connection,
46441 connectionBo = connection.businessObject;
46442
46443 modeling.unclaimId(connectionBo.id, connectionBo);
46444 });
46445
46446 this.preExecute('canvas.updateRoot', function() {
46447 var rootElement = canvas.getRootElement(),
46448 rootElementBo = rootElement.businessObject;
46449
46450 if (is$1(rootElement, 'bpmn:Collaboration')) {
46451 moddle.ids.unclaim(rootElementBo.id);
46452 }
46453 });
46454 }
46455
46456 inherits$1(UnclaimIdBehavior, CommandInterceptor);
46457
46458 UnclaimIdBehavior.$inject = [ 'canvas', 'injector', 'moddle', 'modeling' ];
46459
46460 /**
46461 * A behavior that unsets the Default property of
46462 * sequence flow source on element delete, if the
46463 * removed element is the Gateway or Task's default flow.
46464 *
46465 * @param {EventBus} eventBus
46466 * @param {Modeling} modeling
46467 */
46468 function DeleteSequenceFlowBehavior(eventBus, modeling) {
46469
46470 CommandInterceptor.call(this, eventBus);
46471
46472
46473 this.preExecute('connection.delete', function(event) {
46474 var context = event.context,
46475 connection = context.connection,
46476 source = connection.source;
46477
46478 if (isDefaultFlow(connection, source)) {
46479 modeling.updateProperties(source, {
46480 'default': null
46481 });
46482 }
46483 });
46484 }
46485
46486 inherits$1(DeleteSequenceFlowBehavior, CommandInterceptor);
46487
46488 DeleteSequenceFlowBehavior.$inject = [
46489 'eventBus',
46490 'modeling'
46491 ];
46492
46493
46494 // helpers //////////////////////
46495
46496 function isDefaultFlow(connection, source) {
46497
46498 if (!is$1(connection, 'bpmn:SequenceFlow')) {
46499 return false;
46500 }
46501
46502 var sourceBo = getBusinessObject(source),
46503 sequenceFlow = getBusinessObject(connection);
46504
46505 return sourceBo.get('default') === sequenceFlow;
46506 }
46507
46508 var LOW_PRIORITY$7 = 500,
46509 HIGH_PRIORITY$7 = 5000;
46510
46511
46512 /**
46513 * BPMN specific delete lane behavior
46514 */
46515 function UpdateFlowNodeRefsBehavior(eventBus, modeling, translate) {
46516
46517 CommandInterceptor.call(this, eventBus);
46518
46519 /**
46520 * Ok, this is it:
46521 *
46522 * We have to update the Lane#flowNodeRefs _and_
46523 * FlowNode#lanes with every FlowNode move/resize and
46524 * Lane move/resize.
46525 *
46526 * We want to group that stuff to recompute containments
46527 * as efficient as possible.
46528 *
46529 * Yea!
46530 */
46531
46532 // the update context
46533 var context;
46534
46535
46536 function initContext() {
46537 context = context || new UpdateContext();
46538 context.enter();
46539
46540 return context;
46541 }
46542
46543 function getContext() {
46544 if (!context) {
46545 throw new Error(translate('out of bounds release'));
46546 }
46547
46548 return context;
46549 }
46550
46551 function releaseContext() {
46552
46553 if (!context) {
46554 throw new Error(translate('out of bounds release'));
46555 }
46556
46557 var triggerUpdate = context.leave();
46558
46559 if (triggerUpdate) {
46560 modeling.updateLaneRefs(context.flowNodes, context.lanes);
46561
46562 context = null;
46563 }
46564
46565 return triggerUpdate;
46566 }
46567
46568
46569 var laneRefUpdateEvents = [
46570 'spaceTool',
46571 'lane.add',
46572 'lane.resize',
46573 'lane.split',
46574 'elements.create',
46575 'elements.delete',
46576 'elements.move',
46577 'shape.create',
46578 'shape.delete',
46579 'shape.move',
46580 'shape.resize'
46581 ];
46582
46583
46584 // listen to a lot of stuff to group lane updates
46585
46586 this.preExecute(laneRefUpdateEvents, HIGH_PRIORITY$7, function(event) {
46587 initContext();
46588 });
46589
46590 this.postExecuted(laneRefUpdateEvents, LOW_PRIORITY$7, function(event) {
46591 releaseContext();
46592 });
46593
46594
46595 // Mark flow nodes + lanes that need an update
46596
46597 this.preExecute([
46598 'shape.create',
46599 'shape.move',
46600 'shape.delete',
46601 'shape.resize'
46602 ], function(event) {
46603
46604 var context = event.context,
46605 shape = context.shape;
46606
46607 var updateContext = getContext();
46608
46609 // no need to update labels
46610 if (shape.labelTarget) {
46611 return;
46612 }
46613
46614 if (is$1(shape, 'bpmn:Lane')) {
46615 updateContext.addLane(shape);
46616 }
46617
46618 if (is$1(shape, 'bpmn:FlowNode')) {
46619 updateContext.addFlowNode(shape);
46620 }
46621 });
46622 }
46623
46624 UpdateFlowNodeRefsBehavior.$inject = [
46625 'eventBus',
46626 'modeling' ,
46627 'translate'
46628 ];
46629
46630 inherits$1(UpdateFlowNodeRefsBehavior, CommandInterceptor);
46631
46632
46633 function UpdateContext() {
46634
46635 this.flowNodes = [];
46636 this.lanes = [];
46637
46638 this.counter = 0;
46639
46640 this.addLane = function(lane) {
46641 this.lanes.push(lane);
46642 };
46643
46644 this.addFlowNode = function(flowNode) {
46645 this.flowNodes.push(flowNode);
46646 };
46647
46648 this.enter = function() {
46649 this.counter++;
46650 };
46651
46652 this.leave = function() {
46653 this.counter--;
46654
46655 return !this.counter;
46656 };
46657 }
46658
46659 var BehaviorModule = {
46660 __init__: [
46661 'adaptiveLabelPositioningBehavior',
46662 'appendBehavior',
46663 'associationBehavior',
46664 'attachEventBehavior',
46665 'boundaryEventBehavior',
46666 'createBehavior',
46667 'createDataObjectBehavior',
46668 'createParticipantBehavior',
46669 'dataInputAssociationBehavior',
46670 'dataStoreBehavior',
46671 'deleteLaneBehavior',
46672 'detachEventBehavior',
46673 'dropOnFlowBehavior',
46674 'eventBasedGatewayBehavior',
46675 'fixHoverBehavior',
46676 'groupBehavior',
46677 'importDockingFix',
46678 'isHorizontalFix',
46679 'labelBehavior',
46680 'messageFlowBehavior',
46681 'modelingFeedback',
46682 'removeElementBehavior',
46683 'removeEmbeddedLabelBoundsBehavior',
46684 'removeParticipantBehavior',
46685 'replaceConnectionBehavior',
46686 'replaceElementBehaviour',
46687 'resizeBehavior',
46688 'resizeLaneBehavior',
46689 'rootElementReferenceBehavior',
46690 'spaceToolBehavior',
46691 'subProcessPlaneBehavior',
46692 'subProcessStartEventBehavior',
46693 'toggleCollapseConnectionBehaviour',
46694 'toggleElementCollapseBehaviour',
46695 'unclaimIdBehavior',
46696 'updateFlowNodeRefsBehavior',
46697 'unsetDefaultFlowBehavior'
46698 ],
46699 adaptiveLabelPositioningBehavior: [ 'type', AdaptiveLabelPositioningBehavior ],
46700 appendBehavior: [ 'type', AppendBehavior ],
46701 associationBehavior: [ 'type', AssociationBehavior ],
46702 attachEventBehavior: [ 'type', AttachEventBehavior ],
46703 boundaryEventBehavior: [ 'type', BoundaryEventBehavior ],
46704 createBehavior: [ 'type', CreateBehavior ],
46705 createDataObjectBehavior: [ 'type', CreateDataObjectBehavior ],
46706 createParticipantBehavior: [ 'type', CreateParticipantBehavior ],
46707 dataInputAssociationBehavior: [ 'type', DataInputAssociationBehavior ],
46708 dataStoreBehavior: [ 'type', DataStoreBehavior ],
46709 deleteLaneBehavior: [ 'type', DeleteLaneBehavior ],
46710 detachEventBehavior: [ 'type', DetachEventBehavior ],
46711 dropOnFlowBehavior: [ 'type', DropOnFlowBehavior ],
46712 eventBasedGatewayBehavior: [ 'type', EventBasedGatewayBehavior ],
46713 fixHoverBehavior: [ 'type', FixHoverBehavior ],
46714 groupBehavior: [ 'type', GroupBehavior ],
46715 importDockingFix: [ 'type', ImportDockingFix ],
46716 isHorizontalFix: [ 'type', IsHorizontalFix ],
46717 labelBehavior: [ 'type', LabelBehavior ],
46718 messageFlowBehavior: [ 'type', MessageFlowBehavior ],
46719 modelingFeedback: [ 'type', ModelingFeedback ],
46720 removeElementBehavior: [ 'type', RemoveElementBehavior ],
46721 removeEmbeddedLabelBoundsBehavior: ['type', RemoveEmbeddedLabelBoundsBehavior ],
46722 removeParticipantBehavior: [ 'type', RemoveParticipantBehavior ],
46723 replaceConnectionBehavior: [ 'type', ReplaceConnectionBehavior ],
46724 replaceElementBehaviour: [ 'type', ReplaceElementBehaviour ],
46725 resizeBehavior: [ 'type', ResizeBehavior ],
46726 resizeLaneBehavior: [ 'type', ResizeLaneBehavior ],
46727 rootElementReferenceBehavior: [ 'type', RootElementReferenceBehavior ],
46728 spaceToolBehavior: [ 'type', SpaceToolBehavior ],
46729 subProcessPlaneBehavior: [ 'type', SubProcessPlaneBehavior ],
46730 subProcessStartEventBehavior: [ 'type', SubProcessStartEventBehavior ],
46731 toggleCollapseConnectionBehaviour: [ 'type', ToggleCollapseConnectionBehaviour ],
46732 toggleElementCollapseBehaviour : [ 'type', ToggleElementCollapseBehaviour ],
46733 unclaimIdBehavior: [ 'type', UnclaimIdBehavior ],
46734 unsetDefaultFlowBehavior: [ 'type', DeleteSequenceFlowBehavior ],
46735 updateFlowNodeRefsBehavior: [ 'type', UpdateFlowNodeRefsBehavior ]
46736 };
46737
46738 function getBoundaryAttachment(position, targetBounds) {
46739
46740 var orientation = getOrientation(position, targetBounds, -15);
46741
46742 if (orientation !== 'intersect') {
46743 return orientation;
46744 } else {
46745 return null;
46746 }
46747 }
46748
46749 /**
46750 * BPMN specific modeling rule
46751 */
46752 function BpmnRules(eventBus) {
46753 RuleProvider.call(this, eventBus);
46754 }
46755
46756 inherits$1(BpmnRules, RuleProvider);
46757
46758 BpmnRules.$inject = [ 'eventBus' ];
46759
46760 BpmnRules.prototype.init = function() {
46761
46762 this.addRule('connection.start', function(context) {
46763 var source = context.source;
46764
46765 return canStartConnection(source);
46766 });
46767
46768 this.addRule('connection.create', function(context) {
46769 var source = context.source,
46770 target = context.target,
46771 hints = context.hints || {},
46772 targetParent = hints.targetParent,
46773 targetAttach = hints.targetAttach;
46774
46775 // don't allow incoming connections on
46776 // newly created boundary events
46777 // to boundary events
46778 if (targetAttach) {
46779 return false;
46780 }
46781
46782 // temporarily set target parent for scoping
46783 // checks to work
46784 if (targetParent) {
46785 target.parent = targetParent;
46786 }
46787
46788 try {
46789 return canConnect(source, target);
46790 } finally {
46791
46792 // unset temporary target parent
46793 if (targetParent) {
46794 target.parent = null;
46795 }
46796 }
46797 });
46798
46799 this.addRule('connection.reconnect', function(context) {
46800
46801 var connection = context.connection,
46802 source = context.source,
46803 target = context.target;
46804
46805 return canConnect(source, target, connection);
46806 });
46807
46808 this.addRule('connection.updateWaypoints', function(context) {
46809 return {
46810 type: context.connection.type
46811 };
46812 });
46813
46814 this.addRule('shape.resize', function(context) {
46815
46816 var shape = context.shape,
46817 newBounds = context.newBounds;
46818
46819 return canResize(shape, newBounds);
46820 });
46821
46822 this.addRule('elements.create', function(context) {
46823 var elements = context.elements,
46824 position = context.position,
46825 target = context.target;
46826
46827 if (isConnection$8(target) && !canInsert(elements, target)) {
46828 return false;
46829 }
46830
46831 return every(elements, function(element) {
46832 if (isConnection$8(element)) {
46833 return canConnect(element.source, element.target, element);
46834 }
46835
46836 if (element.host) {
46837 return canAttach(element, element.host, null, position);
46838 }
46839
46840 return canCreate(element, target, null);
46841 });
46842 });
46843
46844 this.addRule('elements.move', function(context) {
46845
46846 var target = context.target,
46847 shapes = context.shapes,
46848 position = context.position;
46849
46850 return canAttach(shapes, target, null, position) ||
46851 canReplace(shapes, target, position) ||
46852 canMove(shapes, target) ||
46853 canInsert(shapes, target);
46854 });
46855
46856 this.addRule('shape.create', function(context) {
46857 return canCreate(
46858 context.shape,
46859 context.target,
46860 context.source,
46861 context.position
46862 );
46863 });
46864
46865 this.addRule('shape.attach', function(context) {
46866
46867 return canAttach(
46868 context.shape,
46869 context.target,
46870 null,
46871 context.position
46872 );
46873 });
46874
46875 this.addRule('element.copy', function(context) {
46876 var element = context.element,
46877 elements = context.elements;
46878
46879 return canCopy(elements, element);
46880 });
46881 };
46882
46883 BpmnRules.prototype.canConnectMessageFlow = canConnectMessageFlow;
46884
46885 BpmnRules.prototype.canConnectSequenceFlow = canConnectSequenceFlow;
46886
46887 BpmnRules.prototype.canConnectDataAssociation = canConnectDataAssociation;
46888
46889 BpmnRules.prototype.canConnectAssociation = canConnectAssociation;
46890
46891 BpmnRules.prototype.canMove = canMove;
46892
46893 BpmnRules.prototype.canAttach = canAttach;
46894
46895 BpmnRules.prototype.canReplace = canReplace;
46896
46897 BpmnRules.prototype.canDrop = canDrop;
46898
46899 BpmnRules.prototype.canInsert = canInsert;
46900
46901 BpmnRules.prototype.canCreate = canCreate;
46902
46903 BpmnRules.prototype.canConnect = canConnect;
46904
46905 BpmnRules.prototype.canResize = canResize;
46906
46907 BpmnRules.prototype.canCopy = canCopy;
46908
46909 /**
46910 * Utility functions for rule checking
46911 */
46912
46913 /**
46914 * Checks if given element can be used for starting connection.
46915 *
46916 * @param {Element} source
46917 * @return {boolean}
46918 */
46919 function canStartConnection(element) {
46920 if (nonExistingOrLabel(element)) {
46921 return null;
46922 }
46923
46924 return isAny(element, [
46925 'bpmn:FlowNode',
46926 'bpmn:InteractionNode',
46927 'bpmn:DataObjectReference',
46928 'bpmn:DataStoreReference',
46929 'bpmn:Group',
46930 'bpmn:TextAnnotation'
46931 ]);
46932 }
46933
46934 function nonExistingOrLabel(element) {
46935 return !element || isLabel$6(element);
46936 }
46937
46938 function isSame$1(a, b) {
46939 return a === b;
46940 }
46941
46942 function getOrganizationalParent(element) {
46943
46944 do {
46945 if (is$1(element, 'bpmn:Process')) {
46946 return getBusinessObject(element);
46947 }
46948
46949 if (is$1(element, 'bpmn:Participant')) {
46950 return (
46951 getBusinessObject(element).processRef ||
46952 getBusinessObject(element)
46953 );
46954 }
46955 } while ((element = element.parent));
46956
46957 }
46958
46959 function isTextAnnotation(element) {
46960 return is$1(element, 'bpmn:TextAnnotation');
46961 }
46962
46963 function isGroup(element) {
46964 return is$1(element, 'bpmn:Group') && !element.labelTarget;
46965 }
46966
46967 function isCompensationBoundary(element) {
46968 return is$1(element, 'bpmn:BoundaryEvent') &&
46969 hasEventDefinition(element, 'bpmn:CompensateEventDefinition');
46970 }
46971
46972 function isForCompensation(e) {
46973 return getBusinessObject(e).isForCompensation;
46974 }
46975
46976 function isSameOrganization(a, b) {
46977 var parentA = getOrganizationalParent(a),
46978 parentB = getOrganizationalParent(b);
46979
46980 return parentA === parentB;
46981 }
46982
46983 function isMessageFlowSource(element) {
46984 return (
46985 is$1(element, 'bpmn:InteractionNode') &&
46986 !is$1(element, 'bpmn:BoundaryEvent') && (
46987 !is$1(element, 'bpmn:Event') || (
46988 is$1(element, 'bpmn:ThrowEvent') &&
46989 hasEventDefinitionOrNone(element, 'bpmn:MessageEventDefinition')
46990 )
46991 )
46992 );
46993 }
46994
46995 function isMessageFlowTarget(element) {
46996 return (
46997 is$1(element, 'bpmn:InteractionNode') &&
46998 !isForCompensation(element) && (
46999 !is$1(element, 'bpmn:Event') || (
47000 is$1(element, 'bpmn:CatchEvent') &&
47001 hasEventDefinitionOrNone(element, 'bpmn:MessageEventDefinition')
47002 )
47003 ) && !(
47004 is$1(element, 'bpmn:BoundaryEvent') &&
47005 !hasEventDefinition(element, 'bpmn:MessageEventDefinition')
47006 )
47007 );
47008 }
47009
47010 function getScopeParent(element) {
47011
47012 var parent = element;
47013
47014 while ((parent = parent.parent)) {
47015
47016 if (is$1(parent, 'bpmn:FlowElementsContainer')) {
47017 return getBusinessObject(parent);
47018 }
47019
47020 if (is$1(parent, 'bpmn:Participant')) {
47021 return getBusinessObject(parent).processRef;
47022 }
47023 }
47024
47025 return null;
47026 }
47027
47028 function isSameScope(a, b) {
47029 var scopeParentA = getScopeParent(a),
47030 scopeParentB = getScopeParent(b);
47031
47032 return scopeParentA === scopeParentB;
47033 }
47034
47035 function hasEventDefinition(element, eventDefinition) {
47036 var bo = getBusinessObject(element);
47037
47038 return !!find(bo.eventDefinitions || [], function(definition) {
47039 return is$1(definition, eventDefinition);
47040 });
47041 }
47042
47043 function hasEventDefinitionOrNone(element, eventDefinition) {
47044 var bo = getBusinessObject(element);
47045
47046 return (bo.eventDefinitions || []).every(function(definition) {
47047 return is$1(definition, eventDefinition);
47048 });
47049 }
47050
47051 function isSequenceFlowSource(element) {
47052 return (
47053 is$1(element, 'bpmn:FlowNode') &&
47054 !is$1(element, 'bpmn:EndEvent') &&
47055 !isEventSubProcess(element) &&
47056 !(is$1(element, 'bpmn:IntermediateThrowEvent') &&
47057 hasEventDefinition(element, 'bpmn:LinkEventDefinition')
47058 ) &&
47059 !isCompensationBoundary(element) &&
47060 !isForCompensation(element)
47061 );
47062 }
47063
47064 function isSequenceFlowTarget(element) {
47065 return (
47066 is$1(element, 'bpmn:FlowNode') &&
47067 !is$1(element, 'bpmn:StartEvent') &&
47068 !is$1(element, 'bpmn:BoundaryEvent') &&
47069 !isEventSubProcess(element) &&
47070 !(is$1(element, 'bpmn:IntermediateCatchEvent') &&
47071 hasEventDefinition(element, 'bpmn:LinkEventDefinition')
47072 ) &&
47073 !isForCompensation(element)
47074 );
47075 }
47076
47077 function isEventBasedTarget(element) {
47078 return (
47079 is$1(element, 'bpmn:ReceiveTask') || (
47080 is$1(element, 'bpmn:IntermediateCatchEvent') && (
47081 hasEventDefinition(element, 'bpmn:MessageEventDefinition') ||
47082 hasEventDefinition(element, 'bpmn:TimerEventDefinition') ||
47083 hasEventDefinition(element, 'bpmn:ConditionalEventDefinition') ||
47084 hasEventDefinition(element, 'bpmn:SignalEventDefinition')
47085 )
47086 )
47087 );
47088 }
47089
47090 function isConnection$8(element) {
47091 return element.waypoints;
47092 }
47093
47094 function getParents(element) {
47095
47096 var parents = [];
47097
47098 while (element) {
47099 element = element.parent;
47100
47101 if (element) {
47102 parents.push(element);
47103 }
47104 }
47105
47106 return parents;
47107 }
47108
47109 function isParent(possibleParent, element) {
47110 var allParents = getParents(element);
47111 return allParents.indexOf(possibleParent) !== -1;
47112 }
47113
47114 function canConnect(source, target, connection) {
47115
47116 if (nonExistingOrLabel(source) || nonExistingOrLabel(target)) {
47117 return null;
47118 }
47119
47120 if (!is$1(connection, 'bpmn:DataAssociation')) {
47121
47122 if (canConnectMessageFlow(source, target)) {
47123 return { type: 'bpmn:MessageFlow' };
47124 }
47125
47126 if (canConnectSequenceFlow(source, target)) {
47127 return { type: 'bpmn:SequenceFlow' };
47128 }
47129 }
47130
47131 var connectDataAssociation = canConnectDataAssociation(source, target);
47132
47133 if (connectDataAssociation) {
47134 return connectDataAssociation;
47135 }
47136
47137 if (isCompensationBoundary(source) && isForCompensation(target)) {
47138 return {
47139 type: 'bpmn:Association',
47140 associationDirection: 'One'
47141 };
47142 }
47143
47144 if (canConnectAssociation(source, target)) {
47145
47146 return {
47147 type: 'bpmn:Association'
47148 };
47149 }
47150
47151 return false;
47152 }
47153
47154 /**
47155 * Can an element be dropped into the target element
47156 *
47157 * @return {boolean}
47158 */
47159 function canDrop(element, target, position) {
47160
47161 // can move labels and groups everywhere
47162 if (isLabel$6(element) || isGroup(element)) {
47163 return true;
47164 }
47165
47166
47167 // disallow to create elements on collapsed pools
47168 if (is$1(target, 'bpmn:Participant') && !isExpanded(target)) {
47169 return false;
47170 }
47171
47172 // allow to create new participants on
47173 // existing collaboration and process diagrams
47174 if (is$1(element, 'bpmn:Participant')) {
47175 return is$1(target, 'bpmn:Process') || is$1(target, 'bpmn:Collaboration');
47176 }
47177
47178 // allow moving DataInput / DataOutput within its original container only
47179 if (isAny(element, [ 'bpmn:DataInput', 'bpmn:DataOutput' ])) {
47180
47181 if (element.parent) {
47182 return target === element.parent;
47183 }
47184 }
47185
47186 // allow creating lanes on participants and other lanes only
47187 if (is$1(element, 'bpmn:Lane')) {
47188 return is$1(target, 'bpmn:Participant') || is$1(target, 'bpmn:Lane');
47189 }
47190
47191 // disallow dropping boundary events which cannot replace with intermediate event
47192 if (is$1(element, 'bpmn:BoundaryEvent') && !isDroppableBoundaryEvent(element)) {
47193 return false;
47194 }
47195
47196 // drop flow elements onto flow element containers
47197 // and participants
47198 if (is$1(element, 'bpmn:FlowElement') && !is$1(element, 'bpmn:DataStoreReference')) {
47199 if (is$1(target, 'bpmn:FlowElementsContainer')) {
47200 return isExpanded(target);
47201 }
47202
47203 return isAny(target, [ 'bpmn:Participant', 'bpmn:Lane' ]);
47204 }
47205
47206 // disallow dropping data store reference if there is no process to append to
47207 if (is$1(element, 'bpmn:DataStoreReference') && is$1(target, 'bpmn:Collaboration')) {
47208 return some(getBusinessObject(target).get('participants'), function(participant) {
47209 return !!participant.get('processRef');
47210 });
47211 }
47212
47213 // account for the fact that data associations are always
47214 // rendered and moved to top (Process or Collaboration level)
47215 //
47216 // artifacts may be placed wherever, too
47217 if (isAny(element, [ 'bpmn:Artifact', 'bpmn:DataAssociation', 'bpmn:DataStoreReference' ])) {
47218 return isAny(target, [
47219 'bpmn:Collaboration',
47220 'bpmn:Lane',
47221 'bpmn:Participant',
47222 'bpmn:Process',
47223 'bpmn:SubProcess' ]);
47224 }
47225
47226 if (is$1(element, 'bpmn:MessageFlow')) {
47227 return is$1(target, 'bpmn:Collaboration')
47228 || element.source.parent == target
47229 || element.target.parent == target;
47230 }
47231
47232 return false;
47233 }
47234
47235 function isDroppableBoundaryEvent(event) {
47236 return getBusinessObject(event).cancelActivity && (
47237 hasNoEventDefinition(event) || hasCommonBoundaryIntermediateEventDefinition(event)
47238 );
47239 }
47240
47241 function isBoundaryEvent(element) {
47242 return !isLabel$6(element) && is$1(element, 'bpmn:BoundaryEvent');
47243 }
47244
47245 function isLane(element) {
47246 return is$1(element, 'bpmn:Lane');
47247 }
47248
47249 /**
47250 * We treat IntermediateThrowEvents as boundary events during create,
47251 * this must be reflected in the rules.
47252 */
47253 function isBoundaryCandidate(element) {
47254 if (isBoundaryEvent(element)) {
47255 return true;
47256 }
47257
47258 if (is$1(element, 'bpmn:IntermediateThrowEvent') && hasNoEventDefinition(element)) {
47259 return true;
47260 }
47261
47262 return (
47263 is$1(element, 'bpmn:IntermediateCatchEvent') &&
47264 hasCommonBoundaryIntermediateEventDefinition(element)
47265 );
47266 }
47267
47268 function hasNoEventDefinition(element) {
47269 var bo = getBusinessObject(element);
47270
47271 return bo && !(bo.eventDefinitions && bo.eventDefinitions.length);
47272 }
47273
47274 function hasCommonBoundaryIntermediateEventDefinition(element) {
47275 return hasOneOfEventDefinitions(element, [
47276 'bpmn:MessageEventDefinition',
47277 'bpmn:TimerEventDefinition',
47278 'bpmn:SignalEventDefinition',
47279 'bpmn:ConditionalEventDefinition'
47280 ]);
47281 }
47282
47283 function hasOneOfEventDefinitions(element, eventDefinitions) {
47284 return eventDefinitions.some(function(definition) {
47285 return hasEventDefinition(element, definition);
47286 });
47287 }
47288
47289 function isReceiveTaskAfterEventBasedGateway(element) {
47290 return (
47291 is$1(element, 'bpmn:ReceiveTask') &&
47292 find(element.incoming, function(incoming) {
47293 return is$1(incoming.source, 'bpmn:EventBasedGateway');
47294 })
47295 );
47296 }
47297
47298
47299 function canAttach(elements, target, source, position) {
47300
47301 if (!Array.isArray(elements)) {
47302 elements = [ elements ];
47303 }
47304
47305 // only (re-)attach one element at a time
47306 if (elements.length !== 1) {
47307 return false;
47308 }
47309
47310 var element = elements[0];
47311
47312 // do not attach labels
47313 if (isLabel$6(element)) {
47314 return false;
47315 }
47316
47317 // only handle boundary events
47318 if (!isBoundaryCandidate(element)) {
47319 return false;
47320 }
47321
47322 // disallow drop on event sub processes
47323 if (isEventSubProcess(target)) {
47324 return false;
47325 }
47326
47327 // only allow drop on non compensation activities
47328 if (!is$1(target, 'bpmn:Activity') || isForCompensation(target)) {
47329 return false;
47330 }
47331
47332 // only attach to subprocess border
47333 if (position && !getBoundaryAttachment(position, target)) {
47334 return false;
47335 }
47336
47337 // do not attach on receive tasks after event based gateways
47338 if (isReceiveTaskAfterEventBasedGateway(target)) {
47339 return false;
47340 }
47341
47342 return 'attach';
47343 }
47344
47345
47346 /**
47347 * Defines how to replace elements for a given target.
47348 *
47349 * Returns an array containing all elements which will be replaced.
47350 *
47351 * @example
47352 *
47353 * [{ id: 'IntermediateEvent_2',
47354 * type: 'bpmn:StartEvent'
47355 * },
47356 * { id: 'IntermediateEvent_5',
47357 * type: 'bpmn:EndEvent'
47358 * }]
47359 *
47360 * @param {Array} elements
47361 * @param {Object} target
47362 *
47363 * @return {Object} an object containing all elements which have to be replaced
47364 */
47365 function canReplace(elements, target, position) {
47366
47367 if (!target) {
47368 return false;
47369 }
47370
47371 var canExecute = {
47372 replacements: []
47373 };
47374
47375 forEach$2(elements, function(element) {
47376
47377 if (!isEventSubProcess(target)) {
47378
47379 if (is$1(element, 'bpmn:StartEvent') &&
47380 element.type !== 'label' &&
47381 canDrop(element, target)) {
47382
47383 // replace a non-interrupting start event by a blank interrupting start event
47384 // when the target is not an event sub process
47385 if (!isInterrupting(element)) {
47386 canExecute.replacements.push({
47387 oldElementId: element.id,
47388 newElementType: 'bpmn:StartEvent'
47389 });
47390 }
47391
47392 // replace an error/escalation/compensate start event by a blank interrupting start event
47393 // when the target is not an event sub process
47394 if (hasErrorEventDefinition(element) ||
47395 hasEscalationEventDefinition(element) ||
47396 hasCompensateEventDefinition(element)) {
47397 canExecute.replacements.push({
47398 oldElementId: element.id,
47399 newElementType: 'bpmn:StartEvent'
47400 });
47401 }
47402
47403 // replace a typed start event by a blank interrupting start event
47404 // when the target is a sub process but not an event sub process
47405 if (hasOneOfEventDefinitions(element,
47406 [
47407 'bpmn:MessageEventDefinition',
47408 'bpmn:TimerEventDefinition',
47409 'bpmn:SignalEventDefinition',
47410 'bpmn:ConditionalEventDefinition'
47411 ]) &&
47412 is$1(target, 'bpmn:SubProcess')) {
47413 canExecute.replacements.push({
47414 oldElementId: element.id,
47415 newElementType: 'bpmn:StartEvent'
47416 });
47417 }
47418 }
47419 }
47420
47421 if (!is$1(target, 'bpmn:Transaction')) {
47422 if (hasEventDefinition(element, 'bpmn:CancelEventDefinition') &&
47423 element.type !== 'label') {
47424
47425 if (is$1(element, 'bpmn:EndEvent') && canDrop(element, target)) {
47426 canExecute.replacements.push({
47427 oldElementId: element.id,
47428 newElementType: 'bpmn:EndEvent'
47429 });
47430 }
47431
47432 if (is$1(element, 'bpmn:BoundaryEvent') && canAttach(element, target, null, position)) {
47433 canExecute.replacements.push({
47434 oldElementId: element.id,
47435 newElementType: 'bpmn:BoundaryEvent'
47436 });
47437 }
47438 }
47439 }
47440 });
47441
47442 return canExecute.replacements.length ? canExecute : false;
47443 }
47444
47445 function canMove(elements, target) {
47446
47447 // do not move selection containing lanes
47448 if (some(elements, isLane)) {
47449 return false;
47450 }
47451
47452 // allow default move check to start move operation
47453 if (!target) {
47454 return true;
47455 }
47456
47457 return elements.every(function(element) {
47458 return canDrop(element, target);
47459 });
47460 }
47461
47462 function canCreate(shape, target, source, position) {
47463
47464 if (!target) {
47465 return false;
47466 }
47467
47468 if (isLabel$6(shape) || isGroup(shape)) {
47469 return true;
47470 }
47471
47472 if (isSame$1(source, target)) {
47473 return false;
47474 }
47475
47476 // ensure we do not drop the element
47477 // into source
47478 if (source && isParent(source, target)) {
47479 return false;
47480 }
47481
47482 return canDrop(shape, target) || canInsert(shape, target);
47483 }
47484
47485 function canResize(shape, newBounds) {
47486 if (is$1(shape, 'bpmn:SubProcess')) {
47487 return (
47488 isExpanded(shape) && (
47489 !newBounds || (newBounds.width >= 100 && newBounds.height >= 80)
47490 )
47491 );
47492 }
47493
47494 if (is$1(shape, 'bpmn:Lane')) {
47495 return !newBounds || (newBounds.width >= 130 && newBounds.height >= 60);
47496 }
47497
47498 if (is$1(shape, 'bpmn:Participant')) {
47499 return !newBounds || (newBounds.width >= 250 && newBounds.height >= 50);
47500 }
47501
47502 if (isTextAnnotation(shape)) {
47503 return true;
47504 }
47505
47506 if (isGroup(shape)) {
47507 return true;
47508 }
47509
47510 return false;
47511 }
47512
47513 /**
47514 * Check, whether one side of the relationship
47515 * is a text annotation.
47516 */
47517 function isOneTextAnnotation(source, target) {
47518
47519 var sourceTextAnnotation = isTextAnnotation(source),
47520 targetTextAnnotation = isTextAnnotation(target);
47521
47522 return (
47523 (sourceTextAnnotation || targetTextAnnotation) &&
47524 (sourceTextAnnotation !== targetTextAnnotation)
47525 );
47526 }
47527
47528
47529 function canConnectAssociation(source, target) {
47530
47531 // do not connect connections
47532 if (isConnection$8(source) || isConnection$8(target)) {
47533 return false;
47534 }
47535
47536 // compensation boundary events are exception
47537 if (isCompensationBoundary(source) && isForCompensation(target)) {
47538 return true;
47539 }
47540
47541 // don't connect parent <-> child
47542 if (isParent(target, source) || isParent(source, target)) {
47543 return false;
47544 }
47545
47546 // allow connection of associations between <!TextAnnotation> and <TextAnnotation>
47547 if (isOneTextAnnotation(source, target)) {
47548 return true;
47549 }
47550
47551 // can connect associations where we can connect
47552 // data associations, too (!)
47553 return !!canConnectDataAssociation(source, target);
47554 }
47555
47556 function canConnectMessageFlow(source, target) {
47557
47558 // during connect user might move mouse out of canvas
47559 // https://github.com/bpmn-io/bpmn-js/issues/1033
47560 if (getRootElement(source) && !getRootElement(target)) {
47561 return false;
47562 }
47563
47564 return (
47565 isMessageFlowSource(source) &&
47566 isMessageFlowTarget(target) &&
47567 !isSameOrganization(source, target)
47568 );
47569 }
47570
47571 function canConnectSequenceFlow(source, target) {
47572
47573 if (
47574 isEventBasedTarget(target) &&
47575 target.incoming.length > 0 &&
47576 areOutgoingEventBasedGatewayConnections(target.incoming) &&
47577 !is$1(source, 'bpmn:EventBasedGateway')
47578 ) {
47579 return false;
47580 }
47581
47582 return isSequenceFlowSource(source) &&
47583 isSequenceFlowTarget(target) &&
47584 isSameScope(source, target) &&
47585 !(is$1(source, 'bpmn:EventBasedGateway') && !isEventBasedTarget(target));
47586 }
47587
47588
47589 function canConnectDataAssociation(source, target) {
47590
47591 if (isAny(source, [ 'bpmn:DataObjectReference', 'bpmn:DataStoreReference' ]) &&
47592 isAny(target, [ 'bpmn:Activity', 'bpmn:ThrowEvent' ])) {
47593 return { type: 'bpmn:DataInputAssociation' };
47594 }
47595
47596 if (isAny(target, [ 'bpmn:DataObjectReference', 'bpmn:DataStoreReference' ]) &&
47597 isAny(source, [ 'bpmn:Activity', 'bpmn:CatchEvent' ])) {
47598 return { type: 'bpmn:DataOutputAssociation' };
47599 }
47600
47601 return false;
47602 }
47603
47604 function canInsert(shape, flow, position) {
47605
47606 if (!flow) {
47607 return false;
47608 }
47609
47610 if (Array.isArray(shape)) {
47611 if (shape.length !== 1) {
47612 return false;
47613 }
47614
47615 shape = shape[0];
47616 }
47617
47618 if (flow.source === shape ||
47619 flow.target === shape) {
47620 return false;
47621 }
47622
47623 // return true if we can drop on the
47624 // underlying flow parent
47625 //
47626 // at this point we are not really able to talk
47627 // about connection rules (yet)
47628
47629 return (
47630 isAny(flow, [ 'bpmn:SequenceFlow', 'bpmn:MessageFlow' ]) &&
47631 !isLabel$6(flow) &&
47632 is$1(shape, 'bpmn:FlowNode') &&
47633 !is$1(shape, 'bpmn:BoundaryEvent') &&
47634 canDrop(shape, flow.parent));
47635 }
47636
47637 function includes$5(elements, element) {
47638 return (elements && element) && elements.indexOf(element) !== -1;
47639 }
47640
47641 function canCopy(elements, element) {
47642 if (isLabel$6(element)) {
47643 return true;
47644 }
47645
47646 if (is$1(element, 'bpmn:Lane') && !includes$5(elements, element.parent)) {
47647 return false;
47648 }
47649
47650 return true;
47651 }
47652
47653 function isOutgoingEventBasedGatewayConnection(connection) {
47654
47655 if (connection && connection.source) {
47656 return is$1(connection.source, 'bpmn:EventBasedGateway');
47657 }
47658 }
47659
47660 function areOutgoingEventBasedGatewayConnections(connections) {
47661 connections = connections || [];
47662
47663 return connections.some(isOutgoingEventBasedGatewayConnection);
47664 }
47665
47666 function getRootElement(element) {
47667 return getParent(element, 'bpmn:Process') || getParent(element, 'bpmn:Collaboration');
47668 }
47669
47670 var RulesModule = {
47671 __depends__: [
47672 RulesModule$1
47673 ],
47674 __init__: [ 'bpmnRules' ],
47675 bpmnRules: [ 'type', BpmnRules ]
47676 };
47677
47678 var HIGH_PRIORITY$6 = 2000;
47679
47680 function BpmnDiOrdering(eventBus, canvas) {
47681
47682 eventBus.on('saveXML.start', HIGH_PRIORITY$6, orderDi);
47683
47684 function orderDi() {
47685 var rootElements = canvas.getRootElements();
47686
47687 forEach$2(rootElements, function(root) {
47688 var rootDi = getDi(root),
47689 elements,
47690 diElements;
47691
47692 elements = selfAndAllChildren([ root ], false);
47693
47694 // only bpmndi:Shape and bpmndi:Edge can be direct children of bpmndi:Plane
47695 elements = filter(elements, function(element) {
47696 return element !== root && !element.labelTarget;
47697 });
47698
47699 diElements = map(elements, getDi);
47700
47701 rootDi.set('planeElement', diElements);
47702 });
47703 }
47704 }
47705
47706 BpmnDiOrdering.$inject = [ 'eventBus', 'canvas' ];
47707
47708 var DiOrderingModule = {
47709 __init__: [
47710 'bpmnDiOrdering'
47711 ],
47712 bpmnDiOrdering: [ 'type', BpmnDiOrdering ]
47713 };
47714
47715 /**
47716 * An abstract provider that allows modelers to implement a custom
47717 * ordering of diagram elements on the canvas.
47718 *
47719 * It makes sure that the order is always preserved during element
47720 * creation and move operations.
47721 *
47722 * In order to use this behavior, inherit from it and override
47723 * the method {@link OrderingProvider#getOrdering}.
47724 *
47725 * @example
47726 *
47727 * ```javascript
47728 * function CustomOrderingProvider(eventBus) {
47729 * OrderingProvider.call(this, eventBus);
47730 *
47731 * this.getOrdering = function(element, newParent) {
47732 * // always insert elements at the front
47733 * // when moving
47734 * return {
47735 * index: 0,
47736 * parent: newParent
47737 * };
47738 * };
47739 * }
47740 * ```
47741 *
47742 * @param {EventBus} eventBus
47743 */
47744 function OrderingProvider(eventBus) {
47745
47746 CommandInterceptor.call(this, eventBus);
47747
47748
47749 var self = this;
47750
47751 this.preExecute([ 'shape.create', 'connection.create' ], function(event) {
47752
47753 var context = event.context,
47754 element = context.shape || context.connection,
47755 parent = context.parent;
47756
47757 var ordering = self.getOrdering(element, parent);
47758
47759 if (ordering) {
47760
47761 if (ordering.parent !== undefined) {
47762 context.parent = ordering.parent;
47763 }
47764
47765 context.parentIndex = ordering.index;
47766 }
47767 });
47768
47769 this.preExecute([ 'shape.move', 'connection.move' ], function(event) {
47770
47771 var context = event.context,
47772 element = context.shape || context.connection,
47773 parent = context.newParent || element.parent;
47774
47775 var ordering = self.getOrdering(element, parent);
47776
47777 if (ordering) {
47778
47779 if (ordering.parent !== undefined) {
47780 context.newParent = ordering.parent;
47781 }
47782
47783 context.newParentIndex = ordering.index;
47784 }
47785 });
47786 }
47787
47788 /**
47789 * Return a custom ordering of the element, both in terms
47790 * of parent element and index in the new parent.
47791 *
47792 * Implementors of this method must return an object with
47793 * `parent` _and_ `index` in it.
47794 *
47795 * @param {djs.model.Base} element
47796 * @param {djs.model.Shape} newParent
47797 *
47798 * @return {Object} ordering descriptor
47799 */
47800 OrderingProvider.prototype.getOrdering = function(element, newParent) {
47801 return null;
47802 };
47803
47804 inherits$1(OrderingProvider, CommandInterceptor);
47805
47806 /**
47807 * a simple ordering provider that makes sure:
47808 *
47809 * (0) labels and groups are rendered always on top
47810 * (1) elements are ordered by a {level} property
47811 */
47812 function BpmnOrderingProvider(eventBus, canvas, translate) {
47813
47814 OrderingProvider.call(this, eventBus);
47815
47816 var orders = [
47817 { type: 'bpmn:SubProcess', order: { level: 6 } },
47818 {
47819 type: 'bpmn:SequenceFlow',
47820 order: {
47821 level: 3,
47822 containers: [
47823 'bpmn:Participant',
47824 'bpmn:FlowElementsContainer'
47825 ]
47826 }
47827 },
47828
47829 // handle DataAssociation(s) like message flows and render them always on top
47830 {
47831 type: 'bpmn:DataAssociation',
47832 order: {
47833 level: 9,
47834 containers: [
47835 'bpmn:Collaboration',
47836 'bpmn:FlowElementsContainer'
47837 ]
47838 }
47839 },
47840 {
47841 type: 'bpmn:MessageFlow', order: {
47842 level: 9,
47843 containers: [ 'bpmn:Collaboration' ]
47844 }
47845 },
47846 {
47847 type: 'bpmn:Association',
47848 order: {
47849 level: 6,
47850 containers: [
47851 'bpmn:Participant',
47852 'bpmn:FlowElementsContainer',
47853 'bpmn:Collaboration'
47854 ]
47855 }
47856 },
47857 { type: 'bpmn:BoundaryEvent', order: { level: 8 } },
47858 {
47859 type: 'bpmn:Group',
47860 order: {
47861 level: 10,
47862 containers: [
47863 'bpmn:Collaboration',
47864 'bpmn:FlowElementsContainer'
47865 ]
47866 }
47867 },
47868 { type: 'bpmn:FlowElement', order: { level: 5 } },
47869 { type: 'bpmn:Participant', order: { level: -2 } },
47870 { type: 'bpmn:Lane', order: { level: -1 } }
47871 ];
47872
47873 function computeOrder(element) {
47874 if (element.labelTarget) {
47875 return { level: 10 };
47876 }
47877
47878 var entry = find(orders, function(o) {
47879 return isAny(element, [ o.type ]);
47880 });
47881
47882 return entry && entry.order || { level: 1 };
47883 }
47884
47885 function getOrder(element) {
47886
47887 var order = element.order;
47888
47889 if (!order) {
47890 element.order = order = computeOrder(element);
47891 }
47892
47893 if (!order) {
47894 throw new Error('no order for <' + element.id + '>');
47895 }
47896
47897 return order;
47898 }
47899
47900 function findActualParent(element, newParent, containers) {
47901
47902 var actualParent = newParent;
47903
47904 while (actualParent) {
47905
47906 if (isAny(actualParent, containers)) {
47907 break;
47908 }
47909
47910 actualParent = actualParent.parent;
47911 }
47912
47913 if (!actualParent) {
47914 throw new Error('no parent for <' + element.id + '> in <' + (newParent && newParent.id) + '>');
47915 }
47916
47917 return actualParent;
47918 }
47919
47920 this.getOrdering = function(element, newParent) {
47921
47922 // render labels always on top
47923 if (element.labelTarget) {
47924 return {
47925 parent: canvas.findRoot(newParent) || canvas.getRootElement(),
47926 index: -1
47927 };
47928 }
47929
47930 var elementOrder = getOrder(element);
47931
47932 if (elementOrder.containers) {
47933 newParent = findActualParent(element, newParent, elementOrder.containers);
47934 }
47935
47936 var currentIndex = newParent.children.indexOf(element);
47937
47938 var insertIndex = findIndex(newParent.children, function(child) {
47939
47940 // do not compare with labels, they are created
47941 // in the wrong order (right after elements) during import and
47942 // mess up the positioning.
47943 if (!element.labelTarget && child.labelTarget) {
47944 return false;
47945 }
47946
47947 return elementOrder.level < getOrder(child).level;
47948 });
47949
47950
47951 // if the element is already in the child list at
47952 // a smaller index, we need to adjust the insert index.
47953 // this takes into account that the element is being removed
47954 // before being re-inserted
47955 if (insertIndex !== -1) {
47956 if (currentIndex !== -1 && currentIndex < insertIndex) {
47957 insertIndex -= 1;
47958 }
47959 }
47960
47961 return {
47962 index: insertIndex,
47963 parent: newParent
47964 };
47965 };
47966 }
47967
47968 BpmnOrderingProvider.$inject = [ 'eventBus', 'canvas', 'translate' ];
47969
47970 inherits$1(BpmnOrderingProvider, OrderingProvider);
47971
47972 var OrderingModule = {
47973 __depends__: [
47974 translate
47975 ],
47976 __init__: [ 'bpmnOrderingProvider' ],
47977 bpmnOrderingProvider: [ 'type', BpmnOrderingProvider ]
47978 };
47979
47980 /**
47981 * A service that offers un- and redoable execution of commands.
47982 *
47983 * The command stack is responsible for executing modeling actions
47984 * in a un- and redoable manner. To do this it delegates the actual
47985 * command execution to {@link CommandHandler}s.
47986 *
47987 * Command handlers provide {@link CommandHandler#execute(ctx)} and
47988 * {@link CommandHandler#revert(ctx)} methods to un- and redo a command
47989 * identified by a command context.
47990 *
47991 *
47992 * ## Life-Cycle events
47993 *
47994 * In the process the command stack fires a number of life-cycle events
47995 * that other components to participate in the command execution.
47996 *
47997 * * preExecute
47998 * * preExecuted
47999 * * execute
48000 * * executed
48001 * * postExecute
48002 * * postExecuted
48003 * * revert
48004 * * reverted
48005 *
48006 * A special event is used for validating, whether a command can be
48007 * performed prior to its execution.
48008 *
48009 * * canExecute
48010 *
48011 * Each of the events is fired as `commandStack.{eventName}` and
48012 * `commandStack.{commandName}.{eventName}`, respectively. This gives
48013 * components fine grained control on where to hook into.
48014 *
48015 * The event object fired transports `command`, the name of the
48016 * command and `context`, the command context.
48017 *
48018 *
48019 * ## Creating Command Handlers
48020 *
48021 * Command handlers should provide the {@link CommandHandler#execute(ctx)}
48022 * and {@link CommandHandler#revert(ctx)} methods to implement
48023 * redoing and undoing of a command.
48024 *
48025 * A command handler _must_ ensure undo is performed properly in order
48026 * not to break the undo chain. It must also return the shapes that
48027 * got changed during the `execute` and `revert` operations.
48028 *
48029 * Command handlers may execute other modeling operations (and thus
48030 * commands) in their `preExecute` and `postExecute` phases. The command
48031 * stack will properly group all commands together into a logical unit
48032 * that may be re- and undone atomically.
48033 *
48034 * Command handlers must not execute other commands from within their
48035 * core implementation (`execute`, `revert`).
48036 *
48037 *
48038 * ## Change Tracking
48039 *
48040 * During the execution of the CommandStack it will keep track of all
48041 * elements that have been touched during the command's execution.
48042 *
48043 * At the end of the CommandStack execution it will notify interested
48044 * components via an 'elements.changed' event with all the dirty
48045 * elements.
48046 *
48047 * The event can be picked up by components that are interested in the fact
48048 * that elements have been changed. One use case for this is updating
48049 * their graphical representation after moving / resizing or deletion.
48050 *
48051 * @see CommandHandler
48052 *
48053 * @param {EventBus} eventBus
48054 * @param {Injector} injector
48055 */
48056 function CommandStack(eventBus, injector) {
48057
48058 /**
48059 * A map of all registered command handlers.
48060 *
48061 * @type {Object}
48062 */
48063 this._handlerMap = {};
48064
48065 /**
48066 * A stack containing all re/undoable actions on the diagram
48067 *
48068 * @type {Array<Object>}
48069 */
48070 this._stack = [];
48071
48072 /**
48073 * The current index on the stack
48074 *
48075 * @type {number}
48076 */
48077 this._stackIdx = -1;
48078
48079 /**
48080 * Current active commandStack execution
48081 *
48082 * @type {Object}
48083 * @property {Object[]} actions
48084 * @property {Object[]} dirty
48085 * @property { 'undo' | 'redo' | 'clear' | 'execute' | null } trigger the cause of the current excecution
48086 */
48087 this._currentExecution = {
48088 actions: [],
48089 dirty: [],
48090 trigger: null
48091 };
48092
48093
48094 this._injector = injector;
48095 this._eventBus = eventBus;
48096
48097 this._uid = 1;
48098
48099 eventBus.on([
48100 'diagram.destroy',
48101 'diagram.clear'
48102 ], function() {
48103 this.clear(false);
48104 }, this);
48105 }
48106
48107 CommandStack.$inject = [ 'eventBus', 'injector' ];
48108
48109
48110 /**
48111 * Execute a command
48112 *
48113 * @param {string} command the command to execute
48114 * @param {Object} context the environment to execute the command in
48115 */
48116 CommandStack.prototype.execute = function(command, context) {
48117 if (!command) {
48118 throw new Error('command required');
48119 }
48120
48121 this._currentExecution.trigger = 'execute';
48122
48123 var action = { command: command, context: context };
48124
48125 this._pushAction(action);
48126 this._internalExecute(action);
48127 this._popAction(action);
48128 };
48129
48130
48131 /**
48132 * Ask whether a given command can be executed.
48133 *
48134 * Implementors may hook into the mechanism on two ways:
48135 *
48136 * * in event listeners:
48137 *
48138 * Users may prevent the execution via an event listener.
48139 * It must prevent the default action for `commandStack.(<command>.)canExecute` events.
48140 *
48141 * * in command handlers:
48142 *
48143 * If the method {@link CommandHandler#canExecute} is implemented in a handler
48144 * it will be called to figure out whether the execution is allowed.
48145 *
48146 * @param {string} command the command to execute
48147 * @param {Object} context the environment to execute the command in
48148 *
48149 * @return {boolean} true if the command can be executed
48150 */
48151 CommandStack.prototype.canExecute = function(command, context) {
48152
48153 var action = { command: command, context: context };
48154
48155 var handler = this._getHandler(command);
48156
48157 var result = this._fire(command, 'canExecute', action);
48158
48159 // handler#canExecute will only be called if no listener
48160 // decided on a result already
48161 if (result === undefined) {
48162 if (!handler) {
48163 return false;
48164 }
48165
48166 if (handler.canExecute) {
48167 result = handler.canExecute(context);
48168 }
48169 }
48170
48171 return result;
48172 };
48173
48174
48175 /**
48176 * Clear the command stack, erasing all undo / redo history
48177 */
48178 CommandStack.prototype.clear = function(emit) {
48179 this._stack.length = 0;
48180 this._stackIdx = -1;
48181
48182 if (emit !== false) {
48183 this._fire('changed', { trigger: 'clear' });
48184 }
48185 };
48186
48187
48188 /**
48189 * Undo last command(s)
48190 */
48191 CommandStack.prototype.undo = function() {
48192 var action = this._getUndoAction(),
48193 next;
48194
48195 if (action) {
48196 this._currentExecution.trigger = 'undo';
48197
48198 this._pushAction(action);
48199
48200 while (action) {
48201 this._internalUndo(action);
48202 next = this._getUndoAction();
48203
48204 if (!next || next.id !== action.id) {
48205 break;
48206 }
48207
48208 action = next;
48209 }
48210
48211 this._popAction();
48212 }
48213 };
48214
48215
48216 /**
48217 * Redo last command(s)
48218 */
48219 CommandStack.prototype.redo = function() {
48220 var action = this._getRedoAction(),
48221 next;
48222
48223 if (action) {
48224 this._currentExecution.trigger = 'redo';
48225
48226 this._pushAction(action);
48227
48228 while (action) {
48229 this._internalExecute(action, true);
48230 next = this._getRedoAction();
48231
48232 if (!next || next.id !== action.id) {
48233 break;
48234 }
48235
48236 action = next;
48237 }
48238
48239 this._popAction();
48240 }
48241 };
48242
48243
48244 /**
48245 * Register a handler instance with the command stack
48246 *
48247 * @param {string} command
48248 * @param {CommandHandler} handler
48249 */
48250 CommandStack.prototype.register = function(command, handler) {
48251 this._setHandler(command, handler);
48252 };
48253
48254
48255 /**
48256 * Register a handler type with the command stack
48257 * by instantiating it and injecting its dependencies.
48258 *
48259 * @param {string} command
48260 * @param {Function} a constructor for a {@link CommandHandler}
48261 */
48262 CommandStack.prototype.registerHandler = function(command, handlerCls) {
48263
48264 if (!command || !handlerCls) {
48265 throw new Error('command and handlerCls must be defined');
48266 }
48267
48268 var handler = this._injector.instantiate(handlerCls);
48269 this.register(command, handler);
48270 };
48271
48272 CommandStack.prototype.canUndo = function() {
48273 return !!this._getUndoAction();
48274 };
48275
48276 CommandStack.prototype.canRedo = function() {
48277 return !!this._getRedoAction();
48278 };
48279
48280 // stack access //////////////////////
48281
48282 CommandStack.prototype._getRedoAction = function() {
48283 return this._stack[this._stackIdx + 1];
48284 };
48285
48286
48287 CommandStack.prototype._getUndoAction = function() {
48288 return this._stack[this._stackIdx];
48289 };
48290
48291
48292 // internal functionality //////////////////////
48293
48294 CommandStack.prototype._internalUndo = function(action) {
48295 var self = this;
48296
48297 var command = action.command,
48298 context = action.context;
48299
48300 var handler = this._getHandler(command);
48301
48302 // guard against illegal nested command stack invocations
48303 this._atomicDo(function() {
48304 self._fire(command, 'revert', action);
48305
48306 if (handler.revert) {
48307 self._markDirty(handler.revert(context));
48308 }
48309
48310 self._revertedAction(action);
48311
48312 self._fire(command, 'reverted', action);
48313 });
48314 };
48315
48316
48317 CommandStack.prototype._fire = function(command, qualifier, event) {
48318 if (arguments.length < 3) {
48319 event = qualifier;
48320 qualifier = null;
48321 }
48322
48323 var names = qualifier ? [ command + '.' + qualifier, qualifier ] : [ command ],
48324 i, name, result;
48325
48326 event = this._eventBus.createEvent(event);
48327
48328 for (i = 0; (name = names[i]); i++) {
48329 result = this._eventBus.fire('commandStack.' + name, event);
48330
48331 if (event.cancelBubble) {
48332 break;
48333 }
48334 }
48335
48336 return result;
48337 };
48338
48339 CommandStack.prototype._createId = function() {
48340 return this._uid++;
48341 };
48342
48343 CommandStack.prototype._atomicDo = function(fn) {
48344
48345 var execution = this._currentExecution;
48346
48347 execution.atomic = true;
48348
48349 try {
48350 fn();
48351 } finally {
48352 execution.atomic = false;
48353 }
48354 };
48355
48356 CommandStack.prototype._internalExecute = function(action, redo) {
48357 var self = this;
48358
48359 var command = action.command,
48360 context = action.context;
48361
48362 var handler = this._getHandler(command);
48363
48364 if (!handler) {
48365 throw new Error('no command handler registered for <' + command + '>');
48366 }
48367
48368 this._pushAction(action);
48369
48370 if (!redo) {
48371 this._fire(command, 'preExecute', action);
48372
48373 if (handler.preExecute) {
48374 handler.preExecute(context);
48375 }
48376
48377 this._fire(command, 'preExecuted', action);
48378 }
48379
48380 // guard against illegal nested command stack invocations
48381 this._atomicDo(function() {
48382
48383 self._fire(command, 'execute', action);
48384
48385 if (handler.execute) {
48386
48387 // actual execute + mark return results as dirty
48388 self._markDirty(handler.execute(context));
48389 }
48390
48391 // log to stack
48392 self._executedAction(action, redo);
48393
48394 self._fire(command, 'executed', action);
48395 });
48396
48397 if (!redo) {
48398 this._fire(command, 'postExecute', action);
48399
48400 if (handler.postExecute) {
48401 handler.postExecute(context);
48402 }
48403
48404 this._fire(command, 'postExecuted', action);
48405 }
48406
48407 this._popAction(action);
48408 };
48409
48410
48411 CommandStack.prototype._pushAction = function(action) {
48412
48413 var execution = this._currentExecution,
48414 actions = execution.actions;
48415
48416 var baseAction = actions[0];
48417
48418 if (execution.atomic) {
48419 throw new Error('illegal invocation in <execute> or <revert> phase (action: ' + action.command + ')');
48420 }
48421
48422 if (!action.id) {
48423 action.id = (baseAction && baseAction.id) || this._createId();
48424 }
48425
48426 actions.push(action);
48427 };
48428
48429
48430 CommandStack.prototype._popAction = function() {
48431 var execution = this._currentExecution,
48432 trigger = execution.trigger,
48433 actions = execution.actions,
48434 dirty = execution.dirty;
48435
48436 actions.pop();
48437
48438 if (!actions.length) {
48439 this._eventBus.fire('elements.changed', { elements: uniqueBy('id', dirty.reverse()) });
48440
48441 dirty.length = 0;
48442
48443 this._fire('changed', { trigger: trigger });
48444
48445 execution.trigger = null;
48446 }
48447 };
48448
48449
48450 CommandStack.prototype._markDirty = function(elements) {
48451 var execution = this._currentExecution;
48452
48453 if (!elements) {
48454 return;
48455 }
48456
48457 elements = isArray$4(elements) ? elements : [ elements ];
48458
48459 execution.dirty = execution.dirty.concat(elements);
48460 };
48461
48462
48463 CommandStack.prototype._executedAction = function(action, redo) {
48464 var stackIdx = ++this._stackIdx;
48465
48466 if (!redo) {
48467 this._stack.splice(stackIdx, this._stack.length, action);
48468 }
48469 };
48470
48471
48472 CommandStack.prototype._revertedAction = function(action) {
48473 this._stackIdx--;
48474 };
48475
48476
48477 CommandStack.prototype._getHandler = function(command) {
48478 return this._handlerMap[command];
48479 };
48480
48481 CommandStack.prototype._setHandler = function(command, handler) {
48482 if (!command || !handler) {
48483 throw new Error('command and handler required');
48484 }
48485
48486 if (this._handlerMap[command]) {
48487 throw new Error('overriding handler for command <' + command + '>');
48488 }
48489
48490 this._handlerMap[command] = handler;
48491 };
48492
48493 var CommandModule = {
48494 commandStack: [ 'type', CommandStack ]
48495 };
48496
48497 // document wide unique tooltip ids
48498 var ids = new IdGenerator('tt');
48499
48500
48501 function createRoot(parentNode) {
48502 var root = domify(
48503 '<div class="djs-tooltip-container" />'
48504 );
48505
48506 assign$1(root, {
48507 position: 'absolute',
48508 width: '0',
48509 height: '0'
48510 });
48511
48512 parentNode.insertBefore(root, parentNode.firstChild);
48513
48514 return root;
48515 }
48516
48517
48518 function setPosition(el, x, y) {
48519 assign$1(el, { left: x + 'px', top: y + 'px' });
48520 }
48521
48522 function setVisible(el, visible) {
48523 el.style.display = visible === false ? 'none' : '';
48524 }
48525
48526
48527 var tooltipClass = 'djs-tooltip',
48528 tooltipSelector = '.' + tooltipClass;
48529
48530 /**
48531 * A service that allows users to render tool tips on the diagram.
48532 *
48533 * The tooltip service will take care of updating the tooltip positioning
48534 * during navigation + zooming.
48535 *
48536 * @example
48537 *
48538 * ```javascript
48539 *
48540 * // add a pink badge on the top left of the shape
48541 * tooltips.add({
48542 * position: {
48543 * x: 50,
48544 * y: 100
48545 * },
48546 * html: '<div style="width: 10px; background: fuchsia; color: white;">0</div>'
48547 * });
48548 *
48549 * // or with optional life span
48550 * tooltips.add({
48551 * position: {
48552 * top: -5,
48553 * left: -5
48554 * },
48555 * html: '<div style="width: 10px; background: fuchsia; color: white;">0</div>',
48556 * ttl: 2000
48557 * });
48558 *
48559 * // remove a tool tip
48560 * var id = tooltips.add(...);
48561 * tooltips.remove(id);
48562 * ```
48563 *
48564 * @param {EventBus} eventBus
48565 * @param {Canvas} canvas
48566 */
48567 function Tooltips(eventBus, canvas) {
48568
48569 this._eventBus = eventBus;
48570 this._canvas = canvas;
48571
48572 this._ids = ids;
48573
48574 this._tooltipDefaults = {
48575 show: {
48576 minZoom: 0.7,
48577 maxZoom: 5.0
48578 }
48579 };
48580
48581 /**
48582 * Mapping tooltipId -> tooltip
48583 */
48584 this._tooltips = {};
48585
48586 // root html element for all tooltips
48587 this._tooltipRoot = createRoot(canvas.getContainer());
48588
48589
48590 var self = this;
48591
48592 delegate.bind(this._tooltipRoot, tooltipSelector, 'mousedown', function(event) {
48593 event.stopPropagation();
48594 });
48595
48596 delegate.bind(this._tooltipRoot, tooltipSelector, 'mouseover', function(event) {
48597 self.trigger('mouseover', event);
48598 });
48599
48600 delegate.bind(this._tooltipRoot, tooltipSelector, 'mouseout', function(event) {
48601 self.trigger('mouseout', event);
48602 });
48603
48604 this._init();
48605 }
48606
48607
48608 Tooltips.$inject = [ 'eventBus', 'canvas' ];
48609
48610
48611 /**
48612 * Adds a HTML tooltip to the diagram
48613 *
48614 * @param {Object} tooltip the tooltip configuration
48615 *
48616 * @param {string|DOMElement} tooltip.html html element to use as an tooltip
48617 * @param {Object} [tooltip.show] show configuration
48618 * @param {number} [tooltip.show.minZoom] minimal zoom level to show the tooltip
48619 * @param {number} [tooltip.show.maxZoom] maximum zoom level to show the tooltip
48620 * @param {Object} tooltip.position where to attach the tooltip
48621 * @param {number} [tooltip.position.left] relative to element bbox left attachment
48622 * @param {number} [tooltip.position.top] relative to element bbox top attachment
48623 * @param {number} [tooltip.position.bottom] relative to element bbox bottom attachment
48624 * @param {number} [tooltip.position.right] relative to element bbox right attachment
48625 * @param {number} [tooltip.timeout=-1]
48626 *
48627 * @return {string} id that may be used to reference the tooltip for update or removal
48628 */
48629 Tooltips.prototype.add = function(tooltip) {
48630
48631 if (!tooltip.position) {
48632 throw new Error('must specifiy tooltip position');
48633 }
48634
48635 if (!tooltip.html) {
48636 throw new Error('must specifiy tooltip html');
48637 }
48638
48639 var id = this._ids.next();
48640
48641 tooltip = assign({}, this._tooltipDefaults, tooltip, {
48642 id: id
48643 });
48644
48645 this._addTooltip(tooltip);
48646
48647 if (tooltip.timeout) {
48648 this.setTimeout(tooltip);
48649 }
48650
48651 return id;
48652 };
48653
48654 Tooltips.prototype.trigger = function(action, event) {
48655
48656 var node = event.delegateTarget || event.target;
48657
48658 var tooltip = this.get(attr(node, 'data-tooltip-id'));
48659
48660 if (!tooltip) {
48661 return;
48662 }
48663
48664 if (action === 'mouseover' && tooltip.timeout) {
48665 this.clearTimeout(tooltip);
48666 }
48667
48668 if (action === 'mouseout' && tooltip.timeout) {
48669
48670 // cut timeout after mouse out
48671 tooltip.timeout = 1000;
48672
48673 this.setTimeout(tooltip);
48674 }
48675 };
48676
48677 /**
48678 * Get a tooltip with the given id
48679 *
48680 * @param {string} id
48681 */
48682 Tooltips.prototype.get = function(id) {
48683
48684 if (typeof id !== 'string') {
48685 id = id.id;
48686 }
48687
48688 return this._tooltips[id];
48689 };
48690
48691 Tooltips.prototype.clearTimeout = function(tooltip) {
48692
48693 tooltip = this.get(tooltip);
48694
48695 if (!tooltip) {
48696 return;
48697 }
48698
48699 var removeTimer = tooltip.removeTimer;
48700
48701 if (removeTimer) {
48702 clearTimeout(removeTimer);
48703 tooltip.removeTimer = null;
48704 }
48705 };
48706
48707 Tooltips.prototype.setTimeout = function(tooltip) {
48708
48709 tooltip = this.get(tooltip);
48710
48711 if (!tooltip) {
48712 return;
48713 }
48714
48715 this.clearTimeout(tooltip);
48716
48717 var self = this;
48718
48719 tooltip.removeTimer = setTimeout(function() {
48720 self.remove(tooltip);
48721 }, tooltip.timeout);
48722 };
48723
48724 /**
48725 * Remove an tooltip with the given id
48726 *
48727 * @param {string} id
48728 */
48729 Tooltips.prototype.remove = function(id) {
48730
48731 var tooltip = this.get(id);
48732
48733 if (tooltip) {
48734 remove$1(tooltip.html);
48735 remove$1(tooltip.htmlContainer);
48736
48737 delete tooltip.htmlContainer;
48738
48739 delete this._tooltips[tooltip.id];
48740 }
48741 };
48742
48743
48744 Tooltips.prototype.show = function() {
48745 setVisible(this._tooltipRoot);
48746 };
48747
48748
48749 Tooltips.prototype.hide = function() {
48750 setVisible(this._tooltipRoot, false);
48751 };
48752
48753
48754 Tooltips.prototype._updateRoot = function(viewbox) {
48755 var a = viewbox.scale || 1;
48756 var d = viewbox.scale || 1;
48757
48758 var matrix = 'matrix(' + a + ',0,0,' + d + ',' + (-1 * viewbox.x * a) + ',' + (-1 * viewbox.y * d) + ')';
48759
48760 this._tooltipRoot.style.transform = matrix;
48761 this._tooltipRoot.style['-ms-transform'] = matrix;
48762 };
48763
48764
48765 Tooltips.prototype._addTooltip = function(tooltip) {
48766
48767 var id = tooltip.id,
48768 html = tooltip.html,
48769 htmlContainer,
48770 tooltipRoot = this._tooltipRoot;
48771
48772 // unwrap jquery (for those who need it)
48773 if (html.get && html.constructor.prototype.jquery) {
48774 html = html.get(0);
48775 }
48776
48777 // create proper html elements from
48778 // tooltip HTML strings
48779 if (isString(html)) {
48780 html = domify(html);
48781 }
48782
48783 htmlContainer = domify('<div data-tooltip-id="' + id + '" class="' + tooltipClass + '">');
48784 assign$1(htmlContainer, { position: 'absolute' });
48785
48786 htmlContainer.appendChild(html);
48787
48788 if (tooltip.type) {
48789 classes(htmlContainer).add('djs-tooltip-' + tooltip.type);
48790 }
48791
48792 if (tooltip.className) {
48793 classes(htmlContainer).add(tooltip.className);
48794 }
48795
48796 tooltip.htmlContainer = htmlContainer;
48797
48798 tooltipRoot.appendChild(htmlContainer);
48799
48800 this._tooltips[id] = tooltip;
48801
48802 this._updateTooltip(tooltip);
48803 };
48804
48805
48806 Tooltips.prototype._updateTooltip = function(tooltip) {
48807
48808 var position = tooltip.position,
48809 htmlContainer = tooltip.htmlContainer;
48810
48811 // update overlay html based on tooltip x, y
48812
48813 setPosition(htmlContainer, position.x, position.y);
48814 };
48815
48816
48817 Tooltips.prototype._updateTooltipVisibilty = function(viewbox) {
48818
48819 forEach$2(this._tooltips, function(tooltip) {
48820 var show = tooltip.show,
48821 htmlContainer = tooltip.htmlContainer,
48822 visible = true;
48823
48824 if (show) {
48825 if (show.minZoom > viewbox.scale ||
48826 show.maxZoom < viewbox.scale) {
48827 visible = false;
48828 }
48829
48830 setVisible(htmlContainer, visible);
48831 }
48832 });
48833 };
48834
48835 Tooltips.prototype._init = function() {
48836
48837 var self = this;
48838
48839 // scroll/zoom integration
48840
48841 function updateViewbox(viewbox) {
48842 self._updateRoot(viewbox);
48843 self._updateTooltipVisibilty(viewbox);
48844
48845 self.show();
48846 }
48847
48848 this._eventBus.on('canvas.viewbox.changing', function(event) {
48849 self.hide();
48850 });
48851
48852 this._eventBus.on('canvas.viewbox.changed', function(event) {
48853 updateViewbox(event.viewbox);
48854 });
48855 };
48856
48857 var TooltipsModule = {
48858 __init__: [ 'tooltips' ],
48859 tooltips: [ 'type', Tooltips ]
48860 };
48861
48862 /**
48863 * Remove from the beginning of a collection until it is empty.
48864 *
48865 * This is a null-safe operation that ensures elements
48866 * are being removed from the given collection until the
48867 * collection is empty.
48868 *
48869 * The implementation deals with the fact that a remove operation
48870 * may touch, i.e. remove multiple elements in the collection
48871 * at a time.
48872 *
48873 * @param {Array<Object>} [collection]
48874 * @param {Function} removeFn
48875 *
48876 * @return {Array<Object>} the cleared collection
48877 */
48878 function saveClear(collection, removeFn) {
48879
48880 if (typeof removeFn !== 'function') {
48881 throw new Error('removeFn iterator must be a function');
48882 }
48883
48884 if (!collection) {
48885 return;
48886 }
48887
48888 var e;
48889
48890 while ((e = collection[0])) {
48891 removeFn(e);
48892 }
48893
48894 return collection;
48895 }
48896
48897 var LOW_PRIORITY$6 = 250,
48898 HIGH_PRIORITY$5 = 1400;
48899
48900
48901 /**
48902 * A handler that makes sure labels are properly moved with
48903 * their label targets.
48904 *
48905 * @param {didi.Injector} injector
48906 * @param {EventBus} eventBus
48907 * @param {Modeling} modeling
48908 */
48909 function LabelSupport(injector, eventBus, modeling) {
48910
48911 CommandInterceptor.call(this, eventBus);
48912
48913 var movePreview = injector.get('movePreview', false);
48914
48915 // remove labels from the collection that are being
48916 // moved with other elements anyway
48917 eventBus.on('shape.move.start', HIGH_PRIORITY$5, function(e) {
48918
48919 var context = e.context,
48920 shapes = context.shapes,
48921 validatedShapes = context.validatedShapes;
48922
48923 context.shapes = removeLabels(shapes);
48924 context.validatedShapes = removeLabels(validatedShapes);
48925 });
48926
48927 // add labels to visual's group
48928 movePreview && eventBus.on('shape.move.start', LOW_PRIORITY$6, function(e) {
48929
48930 var context = e.context,
48931 shapes = context.shapes;
48932
48933 var labels = [];
48934
48935 forEach$2(shapes, function(element) {
48936
48937 forEach$2(element.labels, function(label) {
48938
48939 if (!label.hidden && context.shapes.indexOf(label) === -1) {
48940 labels.push(label);
48941 }
48942
48943 if (element.labelTarget) {
48944 labels.push(element);
48945 }
48946 });
48947 });
48948
48949 forEach$2(labels, function(label) {
48950 movePreview.makeDraggable(context, label, true);
48951 });
48952
48953 });
48954
48955 // add all labels to move closure
48956 this.preExecuted('elements.move', HIGH_PRIORITY$5, function(e) {
48957 var context = e.context,
48958 closure = context.closure,
48959 enclosedElements = closure.enclosedElements;
48960
48961 var enclosedLabels = [];
48962
48963 // find labels that are not part of
48964 // move closure yet and add them
48965 forEach$2(enclosedElements, function(element) {
48966 forEach$2(element.labels, function(label) {
48967
48968 if (!enclosedElements[label.id]) {
48969 enclosedLabels.push(label);
48970 }
48971 });
48972 });
48973
48974 closure.addAll(enclosedLabels);
48975 });
48976
48977
48978 this.preExecute([
48979 'connection.delete',
48980 'shape.delete'
48981 ], function(e) {
48982
48983 var context = e.context,
48984 element = context.connection || context.shape;
48985
48986 saveClear(element.labels, function(label) {
48987 modeling.removeShape(label, { nested: true });
48988 });
48989 });
48990
48991
48992 this.execute('shape.delete', function(e) {
48993
48994 var context = e.context,
48995 shape = context.shape,
48996 labelTarget = shape.labelTarget;
48997
48998 // unset labelTarget
48999 if (labelTarget) {
49000 context.labelTargetIndex = indexOf(labelTarget.labels, shape);
49001 context.labelTarget = labelTarget;
49002
49003 shape.labelTarget = null;
49004 }
49005 });
49006
49007 this.revert('shape.delete', function(e) {
49008
49009 var context = e.context,
49010 shape = context.shape,
49011 labelTarget = context.labelTarget,
49012 labelTargetIndex = context.labelTargetIndex;
49013
49014 // restore labelTarget
49015 if (labelTarget) {
49016 add(labelTarget.labels, shape, labelTargetIndex);
49017
49018 shape.labelTarget = labelTarget;
49019 }
49020 });
49021
49022 }
49023
49024 inherits$1(LabelSupport, CommandInterceptor);
49025
49026 LabelSupport.$inject = [
49027 'injector',
49028 'eventBus',
49029 'modeling'
49030 ];
49031
49032
49033 /**
49034 * Return a filtered list of elements that do not
49035 * contain attached elements with hosts being part
49036 * of the selection.
49037 *
49038 * @param {Array<djs.model.Base>} elements
49039 *
49040 * @return {Array<djs.model.Base>} filtered
49041 */
49042 function removeLabels(elements) {
49043
49044 return filter(elements, function(element) {
49045
49046 // filter out labels that are move together
49047 // with their label targets
49048 return elements.indexOf(element.labelTarget) === -1;
49049 });
49050 }
49051
49052 var LabelSupportModule = {
49053 __init__: [ 'labelSupport' ],
49054 labelSupport: [ 'type', LabelSupport ]
49055 };
49056
49057 var LOW_PRIORITY$5 = 251,
49058 HIGH_PRIORITY$4 = 1401;
49059
49060 var MARKER_ATTACH$1 = 'attach-ok';
49061
49062
49063 /**
49064 * Adds the notion of attached elements to the modeler.
49065 *
49066 * Optionally depends on `diagram-js/lib/features/move` to render
49067 * the attached elements during move preview.
49068 *
49069 * Optionally depends on `diagram-js/lib/features/label-support`
49070 * to render attached labels during move preview.
49071 *
49072 * @param {didi.Injector} injector
49073 * @param {EventBus} eventBus
49074 * @param {Canvas} canvas
49075 * @param {Rules} rules
49076 * @param {Modeling} modeling
49077 */
49078 function AttachSupport(injector, eventBus, canvas, rules, modeling) {
49079
49080 CommandInterceptor.call(this, eventBus);
49081
49082 var movePreview = injector.get('movePreview', false);
49083
49084
49085 // remove all the attached elements from the shapes to be validated
49086 // add all the attached shapes to the overall list of moved shapes
49087 eventBus.on('shape.move.start', HIGH_PRIORITY$4, function(e) {
49088
49089 var context = e.context,
49090 shapes = context.shapes,
49091 validatedShapes = context.validatedShapes;
49092
49093 context.shapes = addAttached(shapes);
49094
49095 context.validatedShapes = removeAttached(validatedShapes);
49096 });
49097
49098 // add attachers to the visual's group
49099 movePreview && eventBus.on('shape.move.start', LOW_PRIORITY$5, function(e) {
49100
49101 var context = e.context,
49102 shapes = context.shapes,
49103 attachers = getAttachers(shapes);
49104
49105 forEach$2(attachers, function(attacher) {
49106 movePreview.makeDraggable(context, attacher, true);
49107
49108 forEach$2(attacher.labels, function(label) {
49109 movePreview.makeDraggable(context, label, true);
49110 });
49111 });
49112 });
49113
49114 // add attach-ok marker to current host
49115 movePreview && eventBus.on('shape.move.start', function(event) {
49116 var context = event.context,
49117 shapes = context.shapes;
49118
49119 if (shapes.length !== 1) {
49120 return;
49121 }
49122
49123 var shape = shapes[0];
49124
49125 var host = shape.host;
49126
49127 if (host) {
49128 canvas.addMarker(host, MARKER_ATTACH$1);
49129
49130 eventBus.once([
49131 'shape.move.out',
49132 'shape.move.cleanup'
49133 ], function() {
49134 canvas.removeMarker(host, MARKER_ATTACH$1);
49135 });
49136 }
49137 });
49138
49139 // add all attachers to move closure
49140 this.preExecuted('elements.move', HIGH_PRIORITY$4, function(e) {
49141 var context = e.context,
49142 closure = context.closure,
49143 shapes = context.shapes,
49144 attachers = getAttachers(shapes);
49145
49146 forEach$2(attachers, function(attacher) {
49147 closure.add(attacher, closure.topLevel[attacher.host.id]);
49148 });
49149 });
49150
49151 // perform the attaching after shapes are done moving
49152 this.postExecuted('elements.move', function(e) {
49153
49154 var context = e.context,
49155 shapes = context.shapes,
49156 newHost = context.newHost,
49157 attachers;
49158
49159 // only single elements can be attached
49160 // multiply elements can be detached
49161 if (newHost && shapes.length !== 1) {
49162 return;
49163 }
49164
49165 if (newHost) {
49166 attachers = shapes;
49167 } else {
49168
49169 // find attachers moved without host
49170 attachers = filter(shapes, function(shape) {
49171 var host = shape.host;
49172
49173 return isAttacher(shape) && !includes$4(shapes, host);
49174 });
49175 }
49176
49177 forEach$2(attachers, function(attacher) {
49178 modeling.updateAttachment(attacher, newHost);
49179 });
49180 });
49181
49182 // ensure invalid attachment connections are removed
49183 this.postExecuted('elements.move', function(e) {
49184
49185 var shapes = e.context.shapes;
49186
49187 forEach$2(shapes, function(shape) {
49188
49189 forEach$2(shape.attachers, function(attacher) {
49190
49191 // remove invalid outgoing connections
49192 forEach$2(attacher.outgoing.slice(), function(connection) {
49193 var allowed = rules.allowed('connection.reconnect', {
49194 connection: connection,
49195 source: connection.source,
49196 target: connection.target
49197 });
49198
49199 if (!allowed) {
49200 modeling.removeConnection(connection);
49201 }
49202 });
49203
49204 // remove invalid incoming connections
49205 forEach$2(attacher.incoming.slice(), function(connection) {
49206 var allowed = rules.allowed('connection.reconnect', {
49207 connection: connection,
49208 source: connection.source,
49209 target: connection.target
49210 });
49211
49212 if (!allowed) {
49213 modeling.removeConnection(connection);
49214 }
49215 });
49216 });
49217 });
49218 });
49219
49220 this.postExecute('shape.create', function(e) {
49221 var context = e.context,
49222 shape = context.shape,
49223 host = context.host;
49224
49225 if (host) {
49226 modeling.updateAttachment(shape, host);
49227 }
49228 });
49229
49230 // update attachments if the host is replaced
49231 this.postExecute('shape.replace', function(e) {
49232
49233 var context = e.context,
49234 oldShape = context.oldShape,
49235 newShape = context.newShape;
49236
49237 // move the attachers to the new host
49238 saveClear(oldShape.attachers, function(attacher) {
49239 var allowed = rules.allowed('elements.move', {
49240 target: newShape,
49241 shapes: [ attacher ]
49242 });
49243
49244 if (allowed === 'attach') {
49245 modeling.updateAttachment(attacher, newShape);
49246 } else {
49247 modeling.removeShape(attacher);
49248 }
49249 });
49250
49251 // move attachers if new host has different size
49252 if (newShape.attachers.length) {
49253
49254 forEach$2(newShape.attachers, function(attacher) {
49255 var delta = getNewAttachShapeDelta(attacher, oldShape, newShape);
49256 modeling.moveShape(attacher, delta, attacher.parent);
49257 });
49258 }
49259
49260 });
49261
49262 // move shape on host resize
49263 this.postExecute('shape.resize', function(event) {
49264 var context = event.context,
49265 shape = context.shape,
49266 oldBounds = context.oldBounds,
49267 newBounds = context.newBounds,
49268 attachers = shape.attachers,
49269 hints = context.hints || {};
49270
49271 if (hints.attachSupport === false) {
49272 return;
49273 }
49274
49275 forEach$2(attachers, function(attacher) {
49276 var delta = getNewAttachShapeDelta(attacher, oldBounds, newBounds);
49277
49278 modeling.moveShape(attacher, delta, attacher.parent);
49279
49280 forEach$2(attacher.labels, function(label) {
49281 modeling.moveShape(label, delta, label.parent);
49282 });
49283 });
49284 });
49285
49286 // remove attachments
49287 this.preExecute('shape.delete', function(event) {
49288
49289 var shape = event.context.shape;
49290
49291 saveClear(shape.attachers, function(attacher) {
49292 modeling.removeShape(attacher);
49293 });
49294
49295 if (shape.host) {
49296 modeling.updateAttachment(shape, null);
49297 }
49298 });
49299 }
49300
49301 inherits$1(AttachSupport, CommandInterceptor);
49302
49303 AttachSupport.$inject = [
49304 'injector',
49305 'eventBus',
49306 'canvas',
49307 'rules',
49308 'modeling'
49309 ];
49310
49311
49312 /**
49313 * Return attachers of the given shapes
49314 *
49315 * @param {Array<djs.model.Base>} shapes
49316 * @return {Array<djs.model.Base>}
49317 */
49318 function getAttachers(shapes) {
49319 return flatten(map(shapes, function(s) {
49320 return s.attachers || [];
49321 }));
49322 }
49323
49324 /**
49325 * Return a combined list of elements and
49326 * attachers.
49327 *
49328 * @param {Array<djs.model.Base>} elements
49329 * @return {Array<djs.model.Base>} filtered
49330 */
49331 function addAttached(elements) {
49332 var attachers = getAttachers(elements);
49333
49334 return unionBy('id', elements, attachers);
49335 }
49336
49337 /**
49338 * Return a filtered list of elements that do not
49339 * contain attached elements with hosts being part
49340 * of the selection.
49341 *
49342 * @param {Array<djs.model.Base>} elements
49343 *
49344 * @return {Array<djs.model.Base>} filtered
49345 */
49346 function removeAttached(elements) {
49347
49348 var ids = groupBy(elements, 'id');
49349
49350 return filter(elements, function(element) {
49351 while (element) {
49352
49353 // host in selection
49354 if (element.host && ids[element.host.id]) {
49355 return false;
49356 }
49357
49358 element = element.parent;
49359 }
49360
49361 return true;
49362 });
49363 }
49364
49365 function isAttacher(shape) {
49366 return !!shape.host;
49367 }
49368
49369 function includes$4(array, item) {
49370 return array.indexOf(item) !== -1;
49371 }
49372
49373 var AttachSupportModule = {
49374 __depends__: [
49375 RulesModule$1
49376 ],
49377 __init__: [ 'attachSupport' ],
49378 attachSupport: [ 'type', AttachSupport ]
49379 };
49380
49381 var LOW_PRIORITY$4 = 250;
49382
49383 /**
49384 * The tool manager acts as middle-man between the available tool's and the Palette,
49385 * it takes care of making sure that the correct active state is set.
49386 *
49387 * @param {Object} eventBus
49388 * @param {Object} dragging
49389 */
49390 function ToolManager(eventBus, dragging) {
49391 this._eventBus = eventBus;
49392 this._dragging = dragging;
49393
49394 this._tools = [];
49395 this._active = null;
49396 }
49397
49398 ToolManager.$inject = [ 'eventBus', 'dragging' ];
49399
49400 ToolManager.prototype.registerTool = function(name, events) {
49401 var tools = this._tools;
49402
49403 if (!events) {
49404 throw new Error('A tool has to be registered with it\'s "events"');
49405 }
49406
49407 tools.push(name);
49408
49409 this.bindEvents(name, events);
49410 };
49411
49412 ToolManager.prototype.isActive = function(tool) {
49413 return tool && this._active === tool;
49414 };
49415
49416 ToolManager.prototype.length = function(tool) {
49417 return this._tools.length;
49418 };
49419
49420 ToolManager.prototype.setActive = function(tool) {
49421 var eventBus = this._eventBus;
49422
49423 if (this._active !== tool) {
49424 this._active = tool;
49425
49426 eventBus.fire('tool-manager.update', { tool: tool });
49427 }
49428 };
49429
49430 ToolManager.prototype.bindEvents = function(name, events) {
49431 var eventBus = this._eventBus,
49432 dragging = this._dragging;
49433
49434 var eventsToRegister = [];
49435
49436 eventBus.on(events.tool + '.init', function(event) {
49437 var context = event.context;
49438
49439 // Active tools that want to reactivate themselves must do this explicitly
49440 if (!context.reactivate && this.isActive(name)) {
49441 this.setActive(null);
49442
49443 dragging.cancel();
49444 return;
49445 }
49446
49447 this.setActive(name);
49448
49449 }, this);
49450
49451 // Todo[ricardo]: add test cases
49452 forEach$2(events, function(event) {
49453 eventsToRegister.push(event + '.ended');
49454 eventsToRegister.push(event + '.canceled');
49455 });
49456
49457 eventBus.on(eventsToRegister, LOW_PRIORITY$4, function(event) {
49458
49459 // We defer the de-activation of the tool to the .activate phase,
49460 // so we're able to check if we want to toggle off the current
49461 // active tool or switch to a new one
49462 if (!this._active) {
49463 return;
49464 }
49465
49466 if (isPaletteClick(event)) {
49467 return;
49468 }
49469
49470 this.setActive(null);
49471 }, this);
49472
49473 };
49474
49475
49476 // helpers ///////////////
49477
49478 /**
49479 * Check if a given event is a palette click event.
49480 *
49481 * @param {EventBus.Event} event
49482 *
49483 * @return {boolean}
49484 */
49485 function isPaletteClick(event) {
49486 var target = event.originalEvent && event.originalEvent.target;
49487
49488 return target && closest(target, '.group[data-group="tools"]');
49489 }
49490
49491 var ToolManagerModule = {
49492 __depends__: [
49493 DraggingModule
49494 ],
49495 __init__: [ 'toolManager' ],
49496 toolManager: [ 'type', ToolManager ]
49497 };
49498
49499 /**
49500 * Return direction given axis and delta.
49501 *
49502 * @param {string} axis
49503 * @param {number} delta
49504 *
49505 * @return {string}
49506 */
49507 function getDirection(axis, delta) {
49508
49509 if (axis === 'x') {
49510 if (delta > 0) {
49511 return 'e';
49512 }
49513
49514 if (delta < 0) {
49515 return 'w';
49516 }
49517 }
49518
49519 if (axis === 'y') {
49520 if (delta > 0) {
49521 return 's';
49522 }
49523
49524 if (delta < 0) {
49525 return 'n';
49526 }
49527 }
49528
49529 return null;
49530 }
49531
49532 /**
49533 * Returns connections whose waypoints are to be updated. Waypoints are to be updated if start
49534 * or end is to be moved or resized.
49535 *
49536 * @param {Array<djs.model.Shape} movingShapes
49537 * @param {Array<djs.model.Shape} resizingShapes
49538 *
49539 * @returns {Array<djs.model.Connection>}
49540 */
49541 function getWaypointsUpdatingConnections(movingShapes, resizingShapes) {
49542 var waypointsUpdatingConnections = [];
49543
49544 forEach$2(movingShapes.concat(resizingShapes), function(shape) {
49545 var incoming = shape.incoming,
49546 outgoing = shape.outgoing;
49547
49548 forEach$2(incoming.concat(outgoing), function(connection) {
49549 var source = connection.source,
49550 target = connection.target;
49551
49552 if (includes$3(movingShapes, source) ||
49553 includes$3(movingShapes, target) ||
49554 includes$3(resizingShapes, source) ||
49555 includes$3(resizingShapes, target)) {
49556
49557 if (!includes$3(waypointsUpdatingConnections, connection)) {
49558 waypointsUpdatingConnections.push(connection);
49559 }
49560 }
49561 });
49562 });
49563
49564 return waypointsUpdatingConnections;
49565 }
49566
49567 function includes$3(array, item) {
49568 return array.indexOf(item) !== -1;
49569 }
49570
49571 /**
49572 * Resize bounds.
49573 *
49574 * @param {Object} bounds
49575 * @param {number} bounds.x
49576 * @param {number} bounds.y
49577 * @param {number} bounds.width
49578 * @param {number} bounds.height
49579 * @param {string} direction
49580 * @param {Object} delta
49581 * @param {number} delta.x
49582 * @param {number} delta.y
49583 *
49584 * @return {Object}
49585 */
49586 function resizeBounds(bounds, direction, delta) {
49587 var x = bounds.x,
49588 y = bounds.y,
49589 width = bounds.width,
49590 height = bounds.height,
49591 dx = delta.x,
49592 dy = delta.y;
49593
49594 switch (direction) {
49595 case 'n':
49596 return {
49597 x: x,
49598 y: y + dy,
49599 width: width,
49600 height: height - dy
49601 };
49602 case 's':
49603 return {
49604 x: x,
49605 y: y,
49606 width: width,
49607 height: height + dy
49608 };
49609 case 'w':
49610 return {
49611 x: x + dx,
49612 y: y,
49613 width: width - dx,
49614 height: height
49615 };
49616 case 'e':
49617 return {
49618 x: x,
49619 y: y,
49620 width: width + dx,
49621 height: height
49622 };
49623 default:
49624 throw new Error('unknown direction: ' + direction);
49625 }
49626 }
49627
49628 var abs$1 = Math.abs,
49629 round$4 = Math.round;
49630
49631 var AXIS_TO_DIMENSION = {
49632 x: 'width',
49633 y: 'height'
49634 };
49635
49636 var CURSOR_CROSSHAIR = 'crosshair';
49637
49638 var DIRECTION_TO_TRBL = {
49639 n: 'top',
49640 w: 'left',
49641 s: 'bottom',
49642 e: 'right'
49643 };
49644
49645 var HIGH_PRIORITY$3 = 1500;
49646
49647 var DIRECTION_TO_OPPOSITE = {
49648 n: 's',
49649 w: 'e',
49650 s: 'n',
49651 e: 'w'
49652 };
49653
49654 var PADDING = 20;
49655
49656
49657 /**
49658 * Add or remove space by moving and resizing elements.
49659 *
49660 * @param {Canvas} canvas
49661 * @param {Dragging} dragging
49662 * @param {EventBus} eventBus
49663 * @param {Modeling} modeling
49664 * @param {Rules} rules
49665 * @param {ToolManager} toolManager
49666 * @param {Mouse} mouse
49667 */
49668 function SpaceTool(
49669 canvas, dragging, eventBus,
49670 modeling, rules, toolManager,
49671 mouse) {
49672
49673 this._canvas = canvas;
49674 this._dragging = dragging;
49675 this._eventBus = eventBus;
49676 this._modeling = modeling;
49677 this._rules = rules;
49678 this._toolManager = toolManager;
49679 this._mouse = mouse;
49680
49681 var self = this;
49682
49683 toolManager.registerTool('space', {
49684 tool: 'spaceTool.selection',
49685 dragging: 'spaceTool'
49686 });
49687
49688 eventBus.on('spaceTool.selection.end', function(event) {
49689 eventBus.once('spaceTool.selection.ended', function() {
49690 self.activateMakeSpace(event.originalEvent);
49691 });
49692 });
49693
49694 eventBus.on('spaceTool.move', HIGH_PRIORITY$3 , function(event) {
49695 var context = event.context,
49696 initialized = context.initialized;
49697
49698 if (!initialized) {
49699 initialized = context.initialized = self.init(event, context);
49700 }
49701
49702 if (initialized) {
49703 ensureConstraints(event);
49704 }
49705 });
49706
49707 eventBus.on('spaceTool.end', function(event) {
49708 var context = event.context,
49709 axis = context.axis,
49710 direction = context.direction,
49711 movingShapes = context.movingShapes,
49712 resizingShapes = context.resizingShapes,
49713 start = context.start;
49714
49715 if (!context.initialized) {
49716 return;
49717 }
49718
49719 ensureConstraints(event);
49720
49721 var delta = {
49722 x: 0,
49723 y: 0
49724 };
49725
49726 delta[ axis ] = round$4(event[ 'd' + axis ]);
49727
49728 self.makeSpace(movingShapes, resizingShapes, delta, direction, start);
49729
49730 eventBus.once('spaceTool.ended', function(event) {
49731
49732 // activate space tool selection after make space
49733 self.activateSelection(event.originalEvent, true, true);
49734 });
49735 });
49736 }
49737
49738 SpaceTool.$inject = [
49739 'canvas',
49740 'dragging',
49741 'eventBus',
49742 'modeling',
49743 'rules',
49744 'toolManager',
49745 'mouse'
49746 ];
49747
49748 /**
49749 * Activate space tool selection.
49750 *
49751 * @param {Object} event
49752 * @param {boolean} autoActivate
49753 */
49754 SpaceTool.prototype.activateSelection = function(event, autoActivate, reactivate) {
49755 this._dragging.init(event, 'spaceTool.selection', {
49756 autoActivate: autoActivate,
49757 cursor: CURSOR_CROSSHAIR,
49758 data: {
49759 context: {
49760 reactivate: reactivate
49761 }
49762 },
49763 trapClick: false
49764 });
49765 };
49766
49767 /**
49768 * Activate space tool make space.
49769 *
49770 * @param {MouseEvent} event
49771 */
49772 SpaceTool.prototype.activateMakeSpace = function(event) {
49773 this._dragging.init(event, 'spaceTool', {
49774 autoActivate: true,
49775 cursor: CURSOR_CROSSHAIR,
49776 data: {
49777 context: {}
49778 }
49779 });
49780 };
49781
49782 /**
49783 * Make space.
49784 *
49785 * @param {Array<djs.model.Shape>} movingShapes
49786 * @param {Array<djs.model.Shape>} resizingShapes
49787 * @param {Object} delta
49788 * @param {number} delta.x
49789 * @param {number} delta.y
49790 * @param {string} direction
49791 * @param {number} start
49792 */
49793 SpaceTool.prototype.makeSpace = function(movingShapes, resizingShapes, delta, direction, start) {
49794 return this._modeling.createSpace(movingShapes, resizingShapes, delta, direction, start);
49795 };
49796
49797 /**
49798 * Initialize make space and return true if that was successful.
49799 *
49800 * @param {Object} event
49801 * @param {Object} context
49802 *
49803 * @return {boolean}
49804 */
49805 SpaceTool.prototype.init = function(event, context) {
49806 var axis = abs$1(event.dx) > abs$1(event.dy) ? 'x' : 'y',
49807 delta = event[ 'd' + axis ],
49808 start = event[ axis ] - delta;
49809
49810 if (abs$1(delta) < 5) {
49811 return false;
49812 }
49813
49814 // invert delta to remove space when moving left
49815 if (delta < 0) {
49816 delta *= -1;
49817 }
49818
49819 // invert delta to add/remove space when removing/adding space if modifier key is pressed
49820 if (hasPrimaryModifier(event)) {
49821 delta *= -1;
49822 }
49823
49824 var direction = getDirection(axis, delta);
49825
49826 var root = this._canvas.getRootElement();
49827
49828 var children = selfAndAllChildren(root, true);
49829
49830 var elements = this.calculateAdjustments(children, axis, delta, start);
49831
49832 var minDimensions = this._eventBus.fire('spaceTool.getMinDimensions', {
49833 axis: axis,
49834 direction: direction,
49835 shapes: elements.resizingShapes,
49836 start: start
49837 });
49838
49839 var spaceToolConstraints = getSpaceToolConstraints(elements, axis, direction, start, minDimensions);
49840
49841 assign(
49842 context,
49843 elements,
49844 {
49845 axis: axis,
49846 direction: direction,
49847 spaceToolConstraints: spaceToolConstraints,
49848 start: start
49849 }
49850 );
49851
49852 set('resize-' + (axis === 'x' ? 'ew' : 'ns'));
49853
49854 return true;
49855 };
49856
49857 /**
49858 * Get elements to be moved and resized.
49859 *
49860 * @param {Array<djs.model.Shape>} elements
49861 * @param {string} axis
49862 * @param {number} delta
49863 * @param {number} start
49864 *
49865 * @return {Object}
49866 */
49867 SpaceTool.prototype.calculateAdjustments = function(elements, axis, delta, start) {
49868 var rules = this._rules;
49869
49870 var movingShapes = [],
49871 resizingShapes = [];
49872
49873 forEach$2(elements, function(element) {
49874 if (!element.parent || isConnection$7(element)) {
49875 return;
49876 }
49877
49878 var shapeStart = element[ axis ],
49879 shapeEnd = shapeStart + element[ AXIS_TO_DIMENSION[ axis ] ];
49880
49881 // shape to be moved
49882 if ((delta > 0 && shapeStart > start) || (delta < 0 && shapeEnd < start)) {
49883 return movingShapes.push(element);
49884 }
49885
49886 // shape to be resized
49887 if (shapeStart < start &&
49888 shapeEnd > start &&
49889 rules.allowed('shape.resize', { shape: element })
49890 ) {
49891
49892 return resizingShapes.push(element);
49893 }
49894 });
49895
49896 return {
49897 movingShapes: movingShapes,
49898 resizingShapes: resizingShapes
49899 };
49900 };
49901
49902 SpaceTool.prototype.toggle = function() {
49903
49904 if (this.isActive()) {
49905 return this._dragging.cancel();
49906 }
49907
49908 var mouseEvent = this._mouse.getLastMoveEvent();
49909
49910 this.activateSelection(mouseEvent, !!mouseEvent);
49911 };
49912
49913 SpaceTool.prototype.isActive = function() {
49914 var context = this._dragging.context();
49915
49916 return context && /^spaceTool/.test(context.prefix);
49917 };
49918
49919 // helpers //////////
49920
49921 function addPadding(trbl) {
49922 return {
49923 top: trbl.top - PADDING,
49924 right: trbl.right + PADDING,
49925 bottom: trbl.bottom + PADDING,
49926 left: trbl.left - PADDING
49927 };
49928 }
49929
49930 function ensureConstraints(event) {
49931 var context = event.context,
49932 spaceToolConstraints = context.spaceToolConstraints;
49933
49934 if (!spaceToolConstraints) {
49935 return;
49936 }
49937
49938 var x, y;
49939
49940 if (isNumber(spaceToolConstraints.left)) {
49941 x = Math.max(event.x, spaceToolConstraints.left);
49942
49943 event.dx = event.dx + x - event.x;
49944 event.x = x;
49945 }
49946
49947 if (isNumber(spaceToolConstraints.right)) {
49948 x = Math.min(event.x, spaceToolConstraints.right);
49949
49950 event.dx = event.dx + x - event.x;
49951 event.x = x;
49952 }
49953
49954 if (isNumber(spaceToolConstraints.top)) {
49955 y = Math.max(event.y, spaceToolConstraints.top);
49956
49957 event.dy = event.dy + y - event.y;
49958 event.y = y;
49959 }
49960
49961 if (isNumber(spaceToolConstraints.bottom)) {
49962 y = Math.min(event.y, spaceToolConstraints.bottom);
49963
49964 event.dy = event.dy + y - event.y;
49965 event.y = y;
49966 }
49967 }
49968
49969 function getSpaceToolConstraints(elements, axis, direction, start, minDimensions) {
49970 var movingShapes = elements.movingShapes,
49971 resizingShapes = elements.resizingShapes;
49972
49973 if (!resizingShapes.length) {
49974 return;
49975 }
49976
49977 var spaceToolConstraints = {},
49978 min,
49979 max;
49980
49981 forEach$2(resizingShapes, function(resizingShape) {
49982 var resizingShapeBBox = asTRBL(resizingShape);
49983
49984 // find children that are not moving or resizing
49985 var nonMovingResizingChildren = filter(resizingShape.children, function(child) {
49986 return !isConnection$7(child) &&
49987 !isLabel$2(child) &&
49988 !includes$2(movingShapes, child) &&
49989 !includes$2(resizingShapes, child);
49990 });
49991
49992 // find children that are moving
49993 var movingChildren = filter(resizingShape.children, function(child) {
49994 return !isConnection$7(child) && !isLabel$2(child) && includes$2(movingShapes, child);
49995 });
49996
49997 var minOrMax,
49998 nonMovingResizingChildrenBBox,
49999 movingChildrenBBox;
50000
50001 if (nonMovingResizingChildren.length) {
50002 nonMovingResizingChildrenBBox = addPadding(asTRBL(getBBox(nonMovingResizingChildren)));
50003
50004 minOrMax = start -
50005 resizingShapeBBox[ DIRECTION_TO_TRBL[ direction ] ] +
50006 nonMovingResizingChildrenBBox[ DIRECTION_TO_TRBL[ direction ] ];
50007
50008 if (direction === 'n') {
50009 spaceToolConstraints.bottom = max = isNumber(max) ? Math.min(max, minOrMax) : minOrMax;
50010 } else if (direction === 'w') {
50011 spaceToolConstraints.right = max = isNumber(max) ? Math.min(max, minOrMax) : minOrMax;
50012 } else if (direction === 's') {
50013 spaceToolConstraints.top = min = isNumber(min) ? Math.max(min, minOrMax) : minOrMax;
50014 } else if (direction === 'e') {
50015 spaceToolConstraints.left = min = isNumber(min) ? Math.max(min, minOrMax) : minOrMax;
50016 }
50017 }
50018
50019 if (movingChildren.length) {
50020 movingChildrenBBox = addPadding(asTRBL(getBBox(movingChildren)));
50021
50022 minOrMax = start -
50023 movingChildrenBBox[ DIRECTION_TO_TRBL[ DIRECTION_TO_OPPOSITE[ direction ] ] ] +
50024 resizingShapeBBox[ DIRECTION_TO_TRBL[ DIRECTION_TO_OPPOSITE[ direction ] ] ];
50025
50026 if (direction === 'n') {
50027 spaceToolConstraints.bottom = max = isNumber(max) ? Math.min(max, minOrMax) : minOrMax;
50028 } else if (direction === 'w') {
50029 spaceToolConstraints.right = max = isNumber(max) ? Math.min(max, minOrMax) : minOrMax;
50030 } else if (direction === 's') {
50031 spaceToolConstraints.top = min = isNumber(min) ? Math.max(min, minOrMax) : minOrMax;
50032 } else if (direction === 'e') {
50033 spaceToolConstraints.left = min = isNumber(min) ? Math.max(min, minOrMax) : minOrMax;
50034 }
50035 }
50036
50037 var resizingShapeMinDimensions = minDimensions && minDimensions[ resizingShape.id ];
50038
50039 if (resizingShapeMinDimensions) {
50040 if (direction === 'n') {
50041 minOrMax = start +
50042 resizingShape[ AXIS_TO_DIMENSION [ axis ] ] -
50043 resizingShapeMinDimensions[ AXIS_TO_DIMENSION[ axis ] ];
50044
50045 spaceToolConstraints.bottom = max = isNumber(max) ? Math.min(max, minOrMax) : minOrMax;
50046 } else if (direction === 'w') {
50047 minOrMax = start +
50048 resizingShape[ AXIS_TO_DIMENSION [ axis ] ] -
50049 resizingShapeMinDimensions[ AXIS_TO_DIMENSION[ axis ] ];
50050
50051 spaceToolConstraints.right = max = isNumber(max) ? Math.min(max, minOrMax) : minOrMax;
50052 } else if (direction === 's') {
50053 minOrMax = start -
50054 resizingShape[ AXIS_TO_DIMENSION [ axis ] ] +
50055 resizingShapeMinDimensions[ AXIS_TO_DIMENSION[ axis ] ];
50056
50057 spaceToolConstraints.top = min = isNumber(min) ? Math.max(min, minOrMax) : minOrMax;
50058 } else if (direction === 'e') {
50059 minOrMax = start -
50060 resizingShape[ AXIS_TO_DIMENSION [ axis ] ] +
50061 resizingShapeMinDimensions[ AXIS_TO_DIMENSION[ axis ] ];
50062
50063 spaceToolConstraints.left = min = isNumber(min) ? Math.max(min, minOrMax) : minOrMax;
50064 }
50065 }
50066 });
50067
50068 return spaceToolConstraints;
50069 }
50070
50071 function includes$2(array, item) {
50072 return array.indexOf(item) !== -1;
50073 }
50074
50075 function isConnection$7(element) {
50076 return !!element.waypoints;
50077 }
50078
50079 function isLabel$2(element) {
50080 return !!element.labelTarget;
50081 }
50082
50083 var MARKER_DRAGGING$1 = 'djs-dragging',
50084 MARKER_RESIZING = 'djs-resizing';
50085
50086 var LOW_PRIORITY$3 = 250;
50087
50088 var max = Math.max;
50089
50090
50091 /**
50092 * Provides previews for selecting/moving/resizing shapes when creating/removing space.
50093 *
50094 * @param {EventBus} eventBus
50095 * @param {ElementRegistry} elementRegistry
50096 * @param {Canvas} canvas
50097 * @param {Styles} styles
50098 */
50099 function SpaceToolPreview(
50100 eventBus, elementRegistry, canvas,
50101 styles, previewSupport) {
50102
50103 function addPreviewGfx(collection, dragGroup) {
50104 forEach$2(collection, function(element) {
50105 previewSupport.addDragger(element, dragGroup);
50106
50107 canvas.addMarker(element, MARKER_DRAGGING$1);
50108 });
50109 }
50110
50111 // add crosshair
50112 eventBus.on('spaceTool.selection.start', function(event) {
50113 var space = canvas.getLayer('space'),
50114 context = event.context;
50115
50116 var orientation = {
50117 x: 'M 0,-10000 L 0,10000',
50118 y: 'M -10000,0 L 10000,0'
50119 };
50120
50121 var crosshairGroup = create$1('g');
50122 attr$1(crosshairGroup, styles.cls('djs-crosshair-group', [ 'no-events' ]));
50123
50124 append(space, crosshairGroup);
50125
50126 // horizontal path
50127 var pathX = create$1('path');
50128 attr$1(pathX, 'd', orientation.x);
50129 classes$1(pathX).add('djs-crosshair');
50130
50131 append(crosshairGroup, pathX);
50132
50133 // vertical path
50134 var pathY = create$1('path');
50135 attr$1(pathY, 'd', orientation.y);
50136 classes$1(pathY).add('djs-crosshair');
50137
50138 append(crosshairGroup, pathY);
50139
50140 context.crosshairGroup = crosshairGroup;
50141 });
50142
50143 // update crosshair
50144 eventBus.on('spaceTool.selection.move', function(event) {
50145 var crosshairGroup = event.context.crosshairGroup;
50146
50147 translate$2(crosshairGroup, event.x, event.y);
50148 });
50149
50150 // remove crosshair
50151 eventBus.on('spaceTool.selection.cleanup', function(event) {
50152 var context = event.context,
50153 crosshairGroup = context.crosshairGroup;
50154
50155 if (crosshairGroup) {
50156 remove$2(crosshairGroup);
50157 }
50158 });
50159
50160 // add and update move/resize previews
50161 eventBus.on('spaceTool.move', LOW_PRIORITY$3, function(event) {
50162
50163 var context = event.context,
50164 line = context.line,
50165 axis = context.axis,
50166 movingShapes = context.movingShapes,
50167 resizingShapes = context.resizingShapes;
50168
50169 if (!context.initialized) {
50170 return;
50171 }
50172
50173 if (!context.dragGroup) {
50174 var spaceLayer = canvas.getLayer('space');
50175
50176 line = create$1('path');
50177 attr$1(line, 'd', 'M0,0 L0,0');
50178 classes$1(line).add('djs-crosshair');
50179
50180 append(spaceLayer, line);
50181
50182 context.line = line;
50183
50184 var dragGroup = create$1('g');
50185 attr$1(dragGroup, styles.cls('djs-drag-group', [ 'no-events' ]));
50186
50187 append(canvas.getActiveLayer(), dragGroup);
50188
50189 // shapes
50190 addPreviewGfx(movingShapes, dragGroup);
50191
50192 // connections
50193 var movingConnections = context.movingConnections = elementRegistry.filter(function(element) {
50194 var sourceIsMoving = false;
50195
50196 forEach$2(movingShapes, function(shape) {
50197 forEach$2(shape.outgoing, function(connection) {
50198 if (element === connection) {
50199 sourceIsMoving = true;
50200 }
50201 });
50202 });
50203
50204 var targetIsMoving = false;
50205
50206 forEach$2(movingShapes, function(shape) {
50207 forEach$2(shape.incoming, function(connection) {
50208 if (element === connection) {
50209 targetIsMoving = true;
50210 }
50211 });
50212 });
50213
50214 var sourceIsResizing = false;
50215
50216 forEach$2(resizingShapes, function(shape) {
50217 forEach$2(shape.outgoing, function(connection) {
50218 if (element === connection) {
50219 sourceIsResizing = true;
50220 }
50221 });
50222 });
50223
50224 var targetIsResizing = false;
50225
50226 forEach$2(resizingShapes, function(shape) {
50227 forEach$2(shape.incoming, function(connection) {
50228 if (element === connection) {
50229 targetIsResizing = true;
50230 }
50231 });
50232 });
50233
50234 return isConnection$6(element)
50235 && (sourceIsMoving || sourceIsResizing)
50236 && (targetIsMoving || targetIsResizing);
50237 });
50238
50239
50240 addPreviewGfx(movingConnections, dragGroup);
50241
50242 context.dragGroup = dragGroup;
50243 }
50244
50245 if (!context.frameGroup) {
50246 var frameGroup = create$1('g');
50247 attr$1(frameGroup, styles.cls('djs-frame-group', [ 'no-events' ]));
50248
50249 append(canvas.getActiveLayer(), frameGroup);
50250
50251 var frames = [];
50252
50253 forEach$2(resizingShapes, function(shape) {
50254 var frame = previewSupport.addFrame(shape, frameGroup);
50255
50256 var initialBounds = frame.getBBox();
50257
50258 frames.push({
50259 element: frame,
50260 initialBounds: initialBounds
50261 });
50262
50263 canvas.addMarker(shape, MARKER_RESIZING);
50264 });
50265
50266 context.frameGroup = frameGroup;
50267 context.frames = frames;
50268 }
50269
50270 var orientation = {
50271 x: 'M' + event.x + ', -10000 L' + event.x + ', 10000',
50272 y: 'M -10000, ' + event.y + ' L 10000, ' + event.y
50273 };
50274
50275 attr$1(line, { d: orientation[ axis ] });
50276
50277 var opposite = { x: 'y', y: 'x' };
50278 var delta = { x: event.dx, y: event.dy };
50279 delta[ opposite[ context.axis ] ] = 0;
50280
50281 // update move previews
50282 translate$2(context.dragGroup, delta.x, delta.y);
50283
50284 // update resize previews
50285 forEach$2(context.frames, function(frame) {
50286 var element = frame.element,
50287 initialBounds = frame.initialBounds,
50288 width,
50289 height;
50290
50291 if (context.direction === 'e') {
50292 attr$1(element, {
50293 width: max(initialBounds.width + delta.x, 5)
50294 });
50295 } else {
50296 width = max(initialBounds.width - delta.x, 5);
50297
50298 attr$1(element, {
50299 width: width,
50300 x: initialBounds.x + initialBounds.width - width
50301 });
50302 }
50303
50304 if (context.direction === 's') {
50305 attr$1(element, {
50306 height: max(initialBounds.height + delta.y, 5)
50307 });
50308 } else {
50309 height = max(initialBounds.height - delta.y, 5);
50310
50311 attr$1(element, {
50312 height: height,
50313 y: initialBounds.y + initialBounds.height - height
50314 });
50315 }
50316 });
50317
50318 });
50319
50320 // remove move/resize previews
50321 eventBus.on('spaceTool.cleanup', function(event) {
50322
50323 var context = event.context,
50324 movingShapes = context.movingShapes,
50325 movingConnections = context.movingConnections,
50326 resizingShapes = context.resizingShapes,
50327 line = context.line,
50328 dragGroup = context.dragGroup,
50329 frameGroup = context.frameGroup;
50330
50331 // moving shapes
50332 forEach$2(movingShapes, function(shape) {
50333 canvas.removeMarker(shape, MARKER_DRAGGING$1);
50334 });
50335
50336 // moving connections
50337 forEach$2(movingConnections, function(connection) {
50338 canvas.removeMarker(connection, MARKER_DRAGGING$1);
50339 });
50340
50341 if (dragGroup) {
50342 remove$2(line);
50343 remove$2(dragGroup);
50344 }
50345
50346 forEach$2(resizingShapes, function(shape) {
50347 canvas.removeMarker(shape, MARKER_RESIZING);
50348 });
50349
50350 if (frameGroup) {
50351 remove$2(frameGroup);
50352 }
50353 });
50354 }
50355
50356 SpaceToolPreview.$inject = [
50357 'eventBus',
50358 'elementRegistry',
50359 'canvas',
50360 'styles',
50361 'previewSupport'
50362 ];
50363
50364
50365 // helpers //////////////////////
50366
50367 /**
50368 * Checks if an element is a connection.
50369 */
50370 function isConnection$6(element) {
50371 return element.waypoints;
50372 }
50373
50374 var SpaceToolModule = {
50375 __init__: [ 'spaceToolPreview' ],
50376 __depends__: [
50377 DraggingModule,
50378 RulesModule$1,
50379 ToolManagerModule,
50380 PreviewSupportModule,
50381 MouseModule
50382 ],
50383 spaceTool: [ 'type', SpaceTool ],
50384 spaceToolPreview: [ 'type', SpaceToolPreview ]
50385 };
50386
50387 function BpmnFactory(moddle) {
50388 this._model = moddle;
50389 }
50390
50391 BpmnFactory.$inject = [ 'moddle' ];
50392
50393
50394 BpmnFactory.prototype._needsId = function(element) {
50395 return isAny(element, [
50396 'bpmn:RootElement',
50397 'bpmn:FlowElement',
50398 'bpmn:MessageFlow',
50399 'bpmn:DataAssociation',
50400 'bpmn:Artifact',
50401 'bpmn:Participant',
50402 'bpmn:Lane',
50403 'bpmn:LaneSet',
50404 'bpmn:Process',
50405 'bpmn:Collaboration',
50406 'bpmndi:BPMNShape',
50407 'bpmndi:BPMNEdge',
50408 'bpmndi:BPMNDiagram',
50409 'bpmndi:BPMNPlane',
50410 'bpmn:Property',
50411 'bpmn:CategoryValue'
50412 ]);
50413 };
50414
50415 BpmnFactory.prototype._ensureId = function(element) {
50416 if (element.id) {
50417 this._model.ids.claim(element.id, element);
50418 return;
50419 }
50420
50421 // generate semantic ids for elements
50422 // bpmn:SequenceFlow -> SequenceFlow_ID
50423 var prefix;
50424
50425 if (is$1(element, 'bpmn:Activity')) {
50426 prefix = 'Activity';
50427 } else if (is$1(element, 'bpmn:Event')) {
50428 prefix = 'Event';
50429 } else if (is$1(element, 'bpmn:Gateway')) {
50430 prefix = 'Gateway';
50431 } else if (isAny(element, [ 'bpmn:SequenceFlow', 'bpmn:MessageFlow' ])) {
50432 prefix = 'Flow';
50433 } else {
50434 prefix = (element.$type || '').replace(/^[^:]*:/g, '');
50435 }
50436
50437 prefix += '_';
50438
50439 if (!element.id && this._needsId(element)) {
50440 element.id = this._model.ids.nextPrefixed(prefix, element);
50441 }
50442 };
50443
50444
50445 BpmnFactory.prototype.create = function(type, attrs) {
50446 var element = this._model.create(type, attrs || {});
50447
50448 this._ensureId(element);
50449
50450 return element;
50451 };
50452
50453
50454 BpmnFactory.prototype.createDiLabel = function() {
50455 return this.create('bpmndi:BPMNLabel', {
50456 bounds: this.createDiBounds()
50457 });
50458 };
50459
50460
50461 BpmnFactory.prototype.createDiShape = function(semantic, attrs) {
50462 return this.create('bpmndi:BPMNShape', assign({
50463 bpmnElement: semantic,
50464 bounds: this.createDiBounds()
50465 }, attrs));
50466 };
50467
50468
50469 BpmnFactory.prototype.createDiBounds = function(bounds) {
50470 return this.create('dc:Bounds', bounds);
50471 };
50472
50473
50474 BpmnFactory.prototype.createDiWaypoints = function(waypoints) {
50475 var self = this;
50476
50477 return map(waypoints, function(pos) {
50478 return self.createDiWaypoint(pos);
50479 });
50480 };
50481
50482 BpmnFactory.prototype.createDiWaypoint = function(point) {
50483 return this.create('dc:Point', pick(point, [ 'x', 'y' ]));
50484 };
50485
50486
50487 BpmnFactory.prototype.createDiEdge = function(semantic, attrs) {
50488 return this.create('bpmndi:BPMNEdge', assign({
50489 bpmnElement: semantic,
50490 waypoint: this.createDiWaypoints([])
50491 }, attrs));
50492 };
50493
50494 BpmnFactory.prototype.createDiPlane = function(semantic, attrs) {
50495 return this.create('bpmndi:BPMNPlane', assign({
50496 bpmnElement: semantic
50497 }, attrs));
50498 };
50499
50500 /**
50501 * A handler responsible for updating the underlying BPMN 2.0 XML + DI
50502 * once changes on the diagram happen
50503 */
50504 function BpmnUpdater(
50505 eventBus, bpmnFactory, connectionDocking,
50506 translate) {
50507
50508 CommandInterceptor.call(this, eventBus);
50509
50510 this._bpmnFactory = bpmnFactory;
50511 this._translate = translate;
50512
50513 var self = this;
50514
50515
50516
50517 // connection cropping //////////////////////
50518
50519 // crop connection ends during create/update
50520 function cropConnection(e) {
50521 var context = e.context,
50522 hints = context.hints || {},
50523 connection;
50524
50525 if (!context.cropped && hints.createElementsBehavior !== false) {
50526 connection = context.connection;
50527 connection.waypoints = connectionDocking.getCroppedWaypoints(connection);
50528 context.cropped = true;
50529 }
50530 }
50531
50532 this.executed([
50533 'connection.layout',
50534 'connection.create'
50535 ], cropConnection);
50536
50537 this.reverted([ 'connection.layout' ], function(e) {
50538 delete e.context.cropped;
50539 });
50540
50541
50542
50543 // BPMN + DI update //////////////////////
50544
50545
50546 // update parent
50547 function updateParent(e) {
50548 var context = e.context;
50549
50550 self.updateParent(context.shape || context.connection, context.oldParent);
50551 }
50552
50553 function reverseUpdateParent(e) {
50554 var context = e.context;
50555
50556 var element = context.shape || context.connection,
50557
50558 // oldParent is the (old) new parent, because we are undoing
50559 oldParent = context.parent || context.newParent;
50560
50561 self.updateParent(element, oldParent);
50562 }
50563
50564 this.executed([
50565 'shape.move',
50566 'shape.create',
50567 'shape.delete',
50568 'connection.create',
50569 'connection.move',
50570 'connection.delete'
50571 ], ifBpmn(updateParent));
50572
50573 this.reverted([
50574 'shape.move',
50575 'shape.create',
50576 'shape.delete',
50577 'connection.create',
50578 'connection.move',
50579 'connection.delete'
50580 ], ifBpmn(reverseUpdateParent));
50581
50582 /*
50583 * ## Updating Parent
50584 *
50585 * When morphing a Process into a Collaboration or vice-versa,
50586 * make sure that both the *semantic* and *di* parent of each element
50587 * is updated.
50588 *
50589 */
50590 function updateRoot(event) {
50591 var context = event.context,
50592 oldRoot = context.oldRoot,
50593 children = oldRoot.children;
50594
50595 forEach$2(children, function(child) {
50596 if (is$1(child, 'bpmn:BaseElement')) {
50597 self.updateParent(child);
50598 }
50599 });
50600 }
50601
50602 this.executed([ 'canvas.updateRoot' ], updateRoot);
50603 this.reverted([ 'canvas.updateRoot' ], updateRoot);
50604
50605
50606 // update bounds
50607 function updateBounds(e) {
50608 var shape = e.context.shape;
50609
50610 if (!is$1(shape, 'bpmn:BaseElement')) {
50611 return;
50612 }
50613
50614 self.updateBounds(shape);
50615 }
50616
50617 this.executed([ 'shape.move', 'shape.create', 'shape.resize' ], ifBpmn(function(event) {
50618
50619 // exclude labels because they're handled separately during shape.changed
50620 if (event.context.shape.type === 'label') {
50621 return;
50622 }
50623
50624 updateBounds(event);
50625 }));
50626
50627 this.reverted([ 'shape.move', 'shape.create', 'shape.resize' ], ifBpmn(function(event) {
50628
50629 // exclude labels because they're handled separately during shape.changed
50630 if (event.context.shape.type === 'label') {
50631 return;
50632 }
50633
50634 updateBounds(event);
50635 }));
50636
50637 // Handle labels separately. This is necessary, because the label bounds have to be updated
50638 // every time its shape changes, not only on move, create and resize.
50639 eventBus.on('shape.changed', function(event) {
50640 if (event.element.type === 'label') {
50641 updateBounds({ context: { shape: event.element } });
50642 }
50643 });
50644
50645 // attach / detach connection
50646 function updateConnection(e) {
50647 self.updateConnection(e.context);
50648 }
50649
50650 this.executed([
50651 'connection.create',
50652 'connection.move',
50653 'connection.delete',
50654 'connection.reconnect'
50655 ], ifBpmn(updateConnection));
50656
50657 this.reverted([
50658 'connection.create',
50659 'connection.move',
50660 'connection.delete',
50661 'connection.reconnect'
50662 ], ifBpmn(updateConnection));
50663
50664
50665 // update waypoints
50666 function updateConnectionWaypoints(e) {
50667 self.updateConnectionWaypoints(e.context.connection);
50668 }
50669
50670 this.executed([
50671 'connection.layout',
50672 'connection.move',
50673 'connection.updateWaypoints',
50674 ], ifBpmn(updateConnectionWaypoints));
50675
50676 this.reverted([
50677 'connection.layout',
50678 'connection.move',
50679 'connection.updateWaypoints',
50680 ], ifBpmn(updateConnectionWaypoints));
50681
50682 // update conditional/default flows
50683 this.executed('connection.reconnect', ifBpmn(function(event) {
50684 var context = event.context,
50685 connection = context.connection,
50686 oldSource = context.oldSource,
50687 newSource = context.newSource,
50688 connectionBo = getBusinessObject(connection),
50689 oldSourceBo = getBusinessObject(oldSource),
50690 newSourceBo = getBusinessObject(newSource);
50691
50692 // remove condition from connection on reconnect to new source
50693 // if new source can NOT have condional sequence flow
50694 if (connectionBo.conditionExpression && !isAny(newSourceBo, [
50695 'bpmn:Activity',
50696 'bpmn:ExclusiveGateway',
50697 'bpmn:InclusiveGateway'
50698 ])) {
50699 context.oldConditionExpression = connectionBo.conditionExpression;
50700
50701 delete connectionBo.conditionExpression;
50702 }
50703
50704 // remove default from old source flow on reconnect to new source
50705 // if source changed
50706 if (oldSource !== newSource && oldSourceBo.default === connectionBo) {
50707 context.oldDefault = oldSourceBo.default;
50708
50709 delete oldSourceBo.default;
50710 }
50711 }));
50712
50713 this.reverted('connection.reconnect', ifBpmn(function(event) {
50714 var context = event.context,
50715 connection = context.connection,
50716 oldSource = context.oldSource,
50717 newSource = context.newSource,
50718 connectionBo = getBusinessObject(connection),
50719 oldSourceBo = getBusinessObject(oldSource),
50720 newSourceBo = getBusinessObject(newSource);
50721
50722 // add condition to connection on revert reconnect to new source
50723 if (context.oldConditionExpression) {
50724 connectionBo.conditionExpression = context.oldConditionExpression;
50725 }
50726
50727 // add default to old source on revert reconnect to new source
50728 if (context.oldDefault) {
50729 oldSourceBo.default = context.oldDefault;
50730
50731 delete newSourceBo.default;
50732 }
50733 }));
50734
50735 // update attachments
50736 function updateAttachment(e) {
50737 self.updateAttachment(e.context);
50738 }
50739
50740 this.executed([ 'element.updateAttachment' ], ifBpmn(updateAttachment));
50741 this.reverted([ 'element.updateAttachment' ], ifBpmn(updateAttachment));
50742 }
50743
50744 inherits$1(BpmnUpdater, CommandInterceptor);
50745
50746 BpmnUpdater.$inject = [
50747 'eventBus',
50748 'bpmnFactory',
50749 'connectionDocking',
50750 'translate'
50751 ];
50752
50753
50754 // implementation //////////////////////
50755
50756 BpmnUpdater.prototype.updateAttachment = function(context) {
50757
50758 var shape = context.shape,
50759 businessObject = shape.businessObject,
50760 host = shape.host;
50761
50762 businessObject.attachedToRef = host && host.businessObject;
50763 };
50764
50765 BpmnUpdater.prototype.updateParent = function(element, oldParent) {
50766
50767 // do not update BPMN 2.0 label parent
50768 if (element instanceof Label) {
50769 return;
50770 }
50771
50772 // data stores in collaborations are handled separately by DataStoreBehavior
50773 if (is$1(element, 'bpmn:DataStoreReference') &&
50774 element.parent &&
50775 is$1(element.parent, 'bpmn:Collaboration')) {
50776 return;
50777 }
50778
50779 var parentShape = element.parent;
50780
50781 var businessObject = element.businessObject,
50782 di = getDi(element),
50783 parentBusinessObject = parentShape && parentShape.businessObject,
50784 parentDi = getDi(parentShape);
50785
50786 if (is$1(element, 'bpmn:FlowNode')) {
50787 this.updateFlowNodeRefs(businessObject, parentBusinessObject, oldParent && oldParent.businessObject);
50788 }
50789
50790 if (is$1(element, 'bpmn:DataOutputAssociation')) {
50791 if (element.source) {
50792 parentBusinessObject = element.source.businessObject;
50793 } else {
50794 parentBusinessObject = null;
50795 }
50796 }
50797
50798 if (is$1(element, 'bpmn:DataInputAssociation')) {
50799 if (element.target) {
50800 parentBusinessObject = element.target.businessObject;
50801 } else {
50802 parentBusinessObject = null;
50803 }
50804 }
50805
50806 this.updateSemanticParent(businessObject, parentBusinessObject);
50807
50808 if (is$1(element, 'bpmn:DataObjectReference') && businessObject.dataObjectRef) {
50809 this.updateSemanticParent(businessObject.dataObjectRef, parentBusinessObject);
50810 }
50811
50812 this.updateDiParent(di, parentDi);
50813 };
50814
50815
50816 BpmnUpdater.prototype.updateBounds = function(shape) {
50817
50818 var di = getDi(shape),
50819 embeddedLabelBounds = getEmbeddedLabelBounds(shape);
50820
50821 // update embedded label bounds if possible
50822 if (embeddedLabelBounds) {
50823 var embeddedLabelBoundsDelta = delta(embeddedLabelBounds, di.get('bounds'));
50824
50825 assign(embeddedLabelBounds, {
50826 x: shape.x + embeddedLabelBoundsDelta.x,
50827 y: shape.y + embeddedLabelBoundsDelta.y
50828 });
50829 }
50830
50831 var target = (shape instanceof Label) ? this._getLabel(di) : di;
50832
50833 var bounds = target.bounds;
50834
50835 if (!bounds) {
50836 bounds = this._bpmnFactory.createDiBounds();
50837 target.set('bounds', bounds);
50838 }
50839
50840 assign(bounds, {
50841 x: shape.x,
50842 y: shape.y,
50843 width: shape.width,
50844 height: shape.height
50845 });
50846 };
50847
50848 BpmnUpdater.prototype.updateFlowNodeRefs = function(businessObject, newContainment, oldContainment) {
50849
50850 if (oldContainment === newContainment) {
50851 return;
50852 }
50853
50854 var oldRefs, newRefs;
50855
50856 if (is$1 (oldContainment, 'bpmn:Lane')) {
50857 oldRefs = oldContainment.get('flowNodeRef');
50858 remove(oldRefs, businessObject);
50859 }
50860
50861 if (is$1(newContainment, 'bpmn:Lane')) {
50862 newRefs = newContainment.get('flowNodeRef');
50863 add(newRefs, businessObject);
50864 }
50865 };
50866
50867
50868 // update existing sourceElement and targetElement di information
50869 BpmnUpdater.prototype.updateDiConnection = function(connection, newSource, newTarget) {
50870 var connectionDi = getDi(connection),
50871 newSourceDi = getDi(newSource),
50872 newTargetDi = getDi(newTarget);
50873
50874 if (connectionDi.sourceElement && connectionDi.sourceElement.bpmnElement !== getBusinessObject(newSource)) {
50875 connectionDi.sourceElement = newSource && newSourceDi;
50876 }
50877
50878 if (connectionDi.targetElement && connectionDi.targetElement.bpmnElement !== getBusinessObject(newTarget)) {
50879 connectionDi.targetElement = newTarget && newTargetDi;
50880 }
50881
50882 };
50883
50884
50885 BpmnUpdater.prototype.updateDiParent = function(di, parentDi) {
50886
50887 if (parentDi && !is$1(parentDi, 'bpmndi:BPMNPlane')) {
50888 parentDi = parentDi.$parent;
50889 }
50890
50891 if (di.$parent === parentDi) {
50892 return;
50893 }
50894
50895 var planeElements = (parentDi || di.$parent).get('planeElement');
50896
50897 if (parentDi) {
50898 planeElements.push(di);
50899 di.$parent = parentDi;
50900 } else {
50901 remove(planeElements, di);
50902 di.$parent = null;
50903 }
50904 };
50905
50906 function getDefinitions(element) {
50907 while (element && !is$1(element, 'bpmn:Definitions')) {
50908 element = element.$parent;
50909 }
50910
50911 return element;
50912 }
50913
50914 BpmnUpdater.prototype.getLaneSet = function(container) {
50915
50916 var laneSet, laneSets;
50917
50918 // bpmn:Lane
50919 if (is$1(container, 'bpmn:Lane')) {
50920 laneSet = container.childLaneSet;
50921
50922 if (!laneSet) {
50923 laneSet = this._bpmnFactory.create('bpmn:LaneSet');
50924 container.childLaneSet = laneSet;
50925 laneSet.$parent = container;
50926 }
50927
50928 return laneSet;
50929 }
50930
50931 // bpmn:Participant
50932 if (is$1(container, 'bpmn:Participant')) {
50933 container = container.processRef;
50934 }
50935
50936 // bpmn:FlowElementsContainer
50937 laneSets = container.get('laneSets');
50938 laneSet = laneSets[0];
50939
50940 if (!laneSet) {
50941 laneSet = this._bpmnFactory.create('bpmn:LaneSet');
50942 laneSet.$parent = container;
50943 laneSets.push(laneSet);
50944 }
50945
50946 return laneSet;
50947 };
50948
50949 BpmnUpdater.prototype.updateSemanticParent = function(businessObject, newParent, visualParent) {
50950
50951 var containment,
50952 translate = this._translate;
50953
50954 if (businessObject.$parent === newParent) {
50955 return;
50956 }
50957
50958 if (is$1(businessObject, 'bpmn:DataInput') || is$1(businessObject, 'bpmn:DataOutput')) {
50959
50960 if (is$1(newParent, 'bpmn:Participant') && 'processRef' in newParent) {
50961 newParent = newParent.processRef;
50962 }
50963
50964 // already in correct ioSpecification
50965 if ('ioSpecification' in newParent && newParent.ioSpecification === businessObject.$parent) {
50966 return;
50967 }
50968 }
50969
50970 if (is$1(businessObject, 'bpmn:Lane')) {
50971
50972 if (newParent) {
50973 newParent = this.getLaneSet(newParent);
50974 }
50975
50976 containment = 'lanes';
50977 } else
50978
50979 if (is$1(businessObject, 'bpmn:FlowElement')) {
50980
50981 if (newParent) {
50982
50983 if (is$1(newParent, 'bpmn:Participant')) {
50984 newParent = newParent.processRef;
50985 } else
50986
50987 if (is$1(newParent, 'bpmn:Lane')) {
50988 do {
50989
50990 // unwrap Lane -> LaneSet -> (Lane | FlowElementsContainer)
50991 newParent = newParent.$parent.$parent;
50992 } while (is$1(newParent, 'bpmn:Lane'));
50993
50994 }
50995 }
50996
50997 containment = 'flowElements';
50998
50999 } else
51000
51001 if (is$1(businessObject, 'bpmn:Artifact')) {
51002
51003 while (newParent &&
51004 !is$1(newParent, 'bpmn:Process') &&
51005 !is$1(newParent, 'bpmn:SubProcess') &&
51006 !is$1(newParent, 'bpmn:Collaboration')) {
51007
51008 if (is$1(newParent, 'bpmn:Participant')) {
51009 newParent = newParent.processRef;
51010 break;
51011 } else {
51012 newParent = newParent.$parent;
51013 }
51014 }
51015
51016 containment = 'artifacts';
51017 } else
51018
51019 if (is$1(businessObject, 'bpmn:MessageFlow')) {
51020 containment = 'messageFlows';
51021
51022 } else
51023
51024 if (is$1(businessObject, 'bpmn:Participant')) {
51025 containment = 'participants';
51026
51027 // make sure the participants process is properly attached / detached
51028 // from the XML document
51029
51030 var process = businessObject.processRef,
51031 definitions;
51032
51033 if (process) {
51034 definitions = getDefinitions(businessObject.$parent || newParent);
51035
51036 if (businessObject.$parent) {
51037 remove(definitions.get('rootElements'), process);
51038 process.$parent = null;
51039 }
51040
51041 if (newParent) {
51042 add(definitions.get('rootElements'), process);
51043 process.$parent = definitions;
51044 }
51045 }
51046 } else
51047
51048 if (is$1(businessObject, 'bpmn:DataOutputAssociation')) {
51049 containment = 'dataOutputAssociations';
51050 } else
51051
51052 if (is$1(businessObject, 'bpmn:DataInputAssociation')) {
51053 containment = 'dataInputAssociations';
51054 }
51055
51056 if (!containment) {
51057 throw new Error(translate(
51058 'no parent for {element} in {parent}',
51059 {
51060 element: businessObject.id,
51061 parent: newParent.id
51062 }
51063 ));
51064 }
51065
51066 var children;
51067
51068 if (businessObject.$parent) {
51069
51070 // remove from old parent
51071 children = businessObject.$parent.get(containment);
51072 remove(children, businessObject);
51073 }
51074
51075 if (!newParent) {
51076 businessObject.$parent = null;
51077 } else {
51078
51079 // add to new parent
51080 children = newParent.get(containment);
51081 children.push(businessObject);
51082 businessObject.$parent = newParent;
51083 }
51084
51085 if (visualParent) {
51086 var diChildren = visualParent.get(containment);
51087
51088 remove(children, businessObject);
51089
51090 if (newParent) {
51091
51092 if (!diChildren) {
51093 diChildren = [];
51094 newParent.set(containment, diChildren);
51095 }
51096
51097 diChildren.push(businessObject);
51098 }
51099 }
51100 };
51101
51102
51103 BpmnUpdater.prototype.updateConnectionWaypoints = function(connection) {
51104 var di = getDi(connection);
51105
51106 di.set('waypoint', this._bpmnFactory.createDiWaypoints(connection.waypoints));
51107 };
51108
51109
51110 BpmnUpdater.prototype.updateConnection = function(context) {
51111 var connection = context.connection,
51112 businessObject = getBusinessObject(connection),
51113 newSource = connection.source,
51114 newSourceBo = getBusinessObject(newSource),
51115 newTarget = connection.target,
51116 newTargetBo = getBusinessObject(connection.target),
51117 visualParent;
51118
51119 if (!is$1(businessObject, 'bpmn:DataAssociation')) {
51120
51121 var inverseSet = is$1(businessObject, 'bpmn:SequenceFlow');
51122
51123 if (businessObject.sourceRef !== newSourceBo) {
51124 if (inverseSet) {
51125 remove(businessObject.sourceRef && businessObject.sourceRef.get('outgoing'), businessObject);
51126
51127 if (newSourceBo && newSourceBo.get('outgoing')) {
51128 newSourceBo.get('outgoing').push(businessObject);
51129 }
51130 }
51131
51132 businessObject.sourceRef = newSourceBo;
51133 }
51134
51135 if (businessObject.targetRef !== newTargetBo) {
51136 if (inverseSet) {
51137 remove(businessObject.targetRef && businessObject.targetRef.get('incoming'), businessObject);
51138
51139 if (newTargetBo && newTargetBo.get('incoming')) {
51140 newTargetBo.get('incoming').push(businessObject);
51141 }
51142 }
51143
51144 businessObject.targetRef = newTargetBo;
51145 }
51146 } else
51147
51148 if (is$1(businessObject, 'bpmn:DataInputAssociation')) {
51149
51150 // handle obnoxious isMsome sourceRef
51151 businessObject.get('sourceRef')[0] = newSourceBo;
51152
51153 visualParent = context.parent || context.newParent || newTargetBo;
51154
51155 this.updateSemanticParent(businessObject, newTargetBo, visualParent);
51156 } else
51157
51158 if (is$1(businessObject, 'bpmn:DataOutputAssociation')) {
51159 visualParent = context.parent || context.newParent || newSourceBo;
51160
51161 this.updateSemanticParent(businessObject, newSourceBo, visualParent);
51162
51163 // targetRef = new target
51164 businessObject.targetRef = newTargetBo;
51165 }
51166
51167 this.updateConnectionWaypoints(connection);
51168
51169 this.updateDiConnection(connection, newSource, newTarget);
51170 };
51171
51172
51173 // helpers //////////////////////
51174
51175 BpmnUpdater.prototype._getLabel = function(di) {
51176 if (!di.label) {
51177 di.label = this._bpmnFactory.createDiLabel();
51178 }
51179
51180 return di.label;
51181 };
51182
51183
51184 /**
51185 * Make sure the event listener is only called
51186 * if the touched element is a BPMN element.
51187 *
51188 * @param {Function} fn
51189 * @return {Function} guarded function
51190 */
51191 function ifBpmn(fn) {
51192
51193 return function(event) {
51194
51195 var context = event.context,
51196 element = context.shape || context.connection;
51197
51198 if (is$1(element, 'bpmn:BaseElement')) {
51199 fn(event);
51200 }
51201 };
51202 }
51203
51204 /**
51205 * Return dc:Bounds of bpmndi:BPMNLabel if exists.
51206 *
51207 * @param {djs.model.shape} shape
51208 *
51209 * @returns {Object|undefined}
51210 */
51211 function getEmbeddedLabelBounds(shape) {
51212 if (!is$1(shape, 'bpmn:Activity')) {
51213 return;
51214 }
51215
51216 var di = getDi(shape);
51217
51218 if (!di) {
51219 return;
51220 }
51221
51222 var label = di.get('label');
51223
51224 if (!label) {
51225 return;
51226 }
51227
51228 return label.get('bounds');
51229 }
51230
51231 /**
51232 * A bpmn-aware factory for diagram-js shapes
51233 */
51234 function ElementFactory(bpmnFactory, moddle, translate) {
51235 ElementFactory$1.call(this);
51236
51237 this._bpmnFactory = bpmnFactory;
51238 this._moddle = moddle;
51239 this._translate = translate;
51240 }
51241
51242 inherits$1(ElementFactory, ElementFactory$1);
51243
51244 ElementFactory.$inject = [
51245 'bpmnFactory',
51246 'moddle',
51247 'translate'
51248 ];
51249
51250 ElementFactory.prototype.baseCreate = ElementFactory$1.prototype.create;
51251
51252 ElementFactory.prototype.create = function(elementType, attrs) {
51253
51254 // no special magic for labels,
51255 // we assume their businessObjects have already been created
51256 // and wired via attrs
51257 if (elementType === 'label') {
51258 var di = attrs.di || this._bpmnFactory.createDiLabel();
51259 return this.baseCreate(elementType, assign({ type: 'label', di: di }, DEFAULT_LABEL_SIZE, attrs));
51260 }
51261
51262 return this.createBpmnElement(elementType, attrs);
51263 };
51264
51265 ElementFactory.prototype.createBpmnElement = function(elementType, attrs) {
51266 var size,
51267 translate = this._translate;
51268
51269 attrs = attrs || {};
51270
51271 var businessObject = attrs.businessObject,
51272 di = attrs.di;
51273
51274 if (!businessObject) {
51275 if (!attrs.type) {
51276 throw new Error(translate('no shape type specified'));
51277 }
51278
51279 businessObject = this._bpmnFactory.create(attrs.type);
51280
51281 ensureCompatDiRef(businessObject);
51282 }
51283
51284 if (!isModdleDi(di)) {
51285 var diAttrs = assign(
51286 di || {},
51287 { id: businessObject.id + '_di' }
51288 );
51289
51290 if (elementType === 'root') {
51291 di = this._bpmnFactory.createDiPlane(businessObject, diAttrs);
51292 } else
51293 if (elementType === 'connection') {
51294 di = this._bpmnFactory.createDiEdge(businessObject, diAttrs);
51295 } else {
51296 di = this._bpmnFactory.createDiShape(businessObject, diAttrs);
51297 }
51298 }
51299
51300 if (is$1(businessObject, 'bpmn:Group')) {
51301 attrs = assign({
51302 isFrame: true
51303 }, attrs);
51304 }
51305
51306 applyAttributes(businessObject, attrs, [
51307 'processRef',
51308 'isInterrupting',
51309 'associationDirection',
51310 'isForCompensation'
51311 ]);
51312
51313 if (attrs.isExpanded) {
51314 applyAttribute(di, attrs, 'isExpanded');
51315 }
51316
51317 if (is$1(businessObject, 'bpmn:SubProcess')) {
51318 attrs.collapsed = !isExpanded(businessObject, di);
51319 }
51320
51321 if (is$1(businessObject, 'bpmn:ExclusiveGateway')) {
51322 di.isMarkerVisible = true;
51323 }
51324
51325 var eventDefinitions,
51326 newEventDefinition;
51327
51328 if (attrs.eventDefinitionType) {
51329 eventDefinitions = businessObject.get('eventDefinitions') || [];
51330 newEventDefinition = this._bpmnFactory.create(attrs.eventDefinitionType, attrs.eventDefinitionAttrs);
51331
51332 if (attrs.eventDefinitionType === 'bpmn:ConditionalEventDefinition') {
51333 newEventDefinition.condition = this._bpmnFactory.create('bpmn:FormalExpression');
51334 }
51335
51336 eventDefinitions.push(newEventDefinition);
51337
51338 newEventDefinition.$parent = businessObject;
51339 businessObject.eventDefinitions = eventDefinitions;
51340
51341 delete attrs.eventDefinitionType;
51342 }
51343
51344 size = this.getDefaultSize(businessObject, di);
51345
51346 attrs = assign({
51347 id: businessObject.id
51348 }, size, attrs, {
51349 businessObject: businessObject,
51350 di: di
51351 });
51352
51353 return this.baseCreate(elementType, attrs);
51354 };
51355
51356
51357 ElementFactory.prototype.getDefaultSize = function(element, di) {
51358
51359 var bo = getBusinessObject(element);
51360 di = di || getDi(element);
51361
51362 if (is$1(bo, 'bpmn:SubProcess')) {
51363 if (isExpanded(bo, di)) {
51364 return { width: 350, height: 200 };
51365 } else {
51366 return { width: 100, height: 80 };
51367 }
51368 }
51369
51370 if (is$1(bo, 'bpmn:Task')) {
51371 return { width: 100, height: 80 };
51372 }
51373
51374 if (is$1(bo, 'bpmn:Gateway')) {
51375 return { width: 50, height: 50 };
51376 }
51377
51378 if (is$1(bo, 'bpmn:Event')) {
51379 return { width: 36, height: 36 };
51380 }
51381
51382 if (is$1(bo, 'bpmn:Participant')) {
51383 if (isExpanded(bo, di)) {
51384 return { width: 600, height: 250 };
51385 } else {
51386 return { width: 400, height: 60 };
51387 }
51388 }
51389
51390 if (is$1(bo, 'bpmn:Lane')) {
51391 return { width: 400, height: 100 };
51392 }
51393
51394 if (is$1(bo, 'bpmn:DataObjectReference')) {
51395 return { width: 36, height: 50 };
51396 }
51397
51398 if (is$1(bo, 'bpmn:DataStoreReference')) {
51399 return { width: 50, height: 50 };
51400 }
51401
51402 if (is$1(bo, 'bpmn:TextAnnotation')) {
51403 return { width: 100, height: 30 };
51404 }
51405
51406 if (is$1(bo, 'bpmn:Group')) {
51407 return { width: 300, height: 300 };
51408 }
51409
51410 return { width: 100, height: 80 };
51411 };
51412
51413
51414 /**
51415 * Create participant.
51416 *
51417 * @param {boolean|Object} [attrs] attrs
51418 *
51419 * @returns {djs.model.Shape}
51420 */
51421 ElementFactory.prototype.createParticipantShape = function(attrs) {
51422
51423 if (!isObject(attrs)) {
51424 attrs = { isExpanded: attrs };
51425 }
51426
51427 attrs = assign({ type: 'bpmn:Participant' }, attrs || {});
51428
51429 // participants are expanded by default
51430 if (attrs.isExpanded !== false) {
51431 attrs.processRef = this._bpmnFactory.create('bpmn:Process');
51432 }
51433
51434 return this.createShape(attrs);
51435 };
51436
51437
51438 // helpers //////////////////////
51439
51440 /**
51441 * Apply attributes from a map to the given element,
51442 * remove attribute from the map on application.
51443 *
51444 * @param {Base} element
51445 * @param {Object} attrs (in/out map of attributes)
51446 * @param {Array<string>} attributeNames name of attributes to apply
51447 */
51448 function applyAttributes(element, attrs, attributeNames) {
51449
51450 forEach$2(attributeNames, function(property) {
51451 if (attrs[property] !== undefined) {
51452 applyAttribute(element, attrs, property);
51453 }
51454 });
51455 }
51456
51457 /**
51458 * Apply named property to element and drain it from the attrs
51459 * collection.
51460 *
51461 * @param {Base} element
51462 * @param {Object} attrs (in/out map of attributes)
51463 * @param {string} attributeName to apply
51464 */
51465 function applyAttribute(element, attrs, attributeName) {
51466 element[attributeName] = attrs[attributeName];
51467
51468 delete attrs[attributeName];
51469 }
51470
51471
51472 function isModdleDi(element) {
51473 return isAny(element, [
51474 'bpmndi:BPMNShape',
51475 'bpmndi:BPMNEdge',
51476 'bpmndi:BPMNDiagram',
51477 'bpmndi:BPMNPlane',
51478 ]);
51479 }
51480
51481 /**
51482 * A handler that align elements in a certain way.
51483 *
51484 */
51485 function AlignElements(modeling, canvas) {
51486 this._modeling = modeling;
51487 this._canvas = canvas;
51488 }
51489
51490 AlignElements.$inject = [ 'modeling', 'canvas' ];
51491
51492
51493 AlignElements.prototype.preExecute = function(context) {
51494 var modeling = this._modeling;
51495
51496 var elements = context.elements,
51497 alignment = context.alignment;
51498
51499
51500 forEach$2(elements, function(element) {
51501 var delta = {
51502 x: 0,
51503 y: 0
51504 };
51505
51506 if (alignment.left) {
51507 delta.x = alignment.left - element.x;
51508
51509 } else if (alignment.right) {
51510 delta.x = (alignment.right - element.width) - element.x;
51511
51512 } else if (alignment.center) {
51513 delta.x = (alignment.center - Math.round(element.width / 2)) - element.x;
51514
51515 } else if (alignment.top) {
51516 delta.y = alignment.top - element.y;
51517
51518 } else if (alignment.bottom) {
51519 delta.y = (alignment.bottom - element.height) - element.y;
51520
51521 } else if (alignment.middle) {
51522 delta.y = (alignment.middle - Math.round(element.height / 2)) - element.y;
51523 }
51524
51525 modeling.moveElements([ element ], delta, element.parent);
51526 });
51527 };
51528
51529 AlignElements.prototype.postExecute = function(context) {
51530
51531 };
51532
51533 /**
51534 * A handler that implements reversible appending of shapes
51535 * to a source shape.
51536 *
51537 * @param {canvas} Canvas
51538 * @param {elementFactory} ElementFactory
51539 * @param {modeling} Modeling
51540 */
51541 function AppendShapeHandler(modeling) {
51542 this._modeling = modeling;
51543 }
51544
51545 AppendShapeHandler.$inject = [ 'modeling' ];
51546
51547
51548 // api //////////////////////
51549
51550
51551 /**
51552 * Creates a new shape
51553 *
51554 * @param {Object} context
51555 * @param {ElementDescriptor} context.shape the new shape
51556 * @param {ElementDescriptor} context.source the source object
51557 * @param {ElementDescriptor} context.parent the parent object
51558 * @param {Point} context.position position of the new element
51559 */
51560 AppendShapeHandler.prototype.preExecute = function(context) {
51561
51562 var source = context.source;
51563
51564 if (!source) {
51565 throw new Error('source required');
51566 }
51567
51568 var target = context.target || source.parent,
51569 shape = context.shape,
51570 hints = context.hints || {};
51571
51572 shape = context.shape =
51573 this._modeling.createShape(
51574 shape,
51575 context.position,
51576 target, { attach: hints.attach });
51577
51578 context.shape = shape;
51579 };
51580
51581 AppendShapeHandler.prototype.postExecute = function(context) {
51582 var hints = context.hints || {};
51583
51584 if (!existsConnection(context.source, context.shape)) {
51585
51586 // create connection
51587 if (hints.connectionTarget === context.source) {
51588 this._modeling.connect(context.shape, context.source, context.connection);
51589 } else {
51590 this._modeling.connect(context.source, context.shape, context.connection);
51591 }
51592 }
51593 };
51594
51595
51596 function existsConnection(source, target) {
51597 return some(source.outgoing, function(c) {
51598 return c.target === target;
51599 });
51600 }
51601
51602 function CreateConnectionHandler(canvas, layouter) {
51603 this._canvas = canvas;
51604 this._layouter = layouter;
51605 }
51606
51607 CreateConnectionHandler.$inject = [ 'canvas', 'layouter' ];
51608
51609
51610 // api //////////////////////
51611
51612
51613 /**
51614 * Appends a shape to a target shape
51615 *
51616 * @param {Object} context
51617 * @param {djs.element.Base} context.source the source object
51618 * @param {djs.element.Base} context.target the parent object
51619 * @param {Point} context.position position of the new element
51620 */
51621 CreateConnectionHandler.prototype.execute = function(context) {
51622
51623 var connection = context.connection,
51624 source = context.source,
51625 target = context.target,
51626 parent = context.parent,
51627 parentIndex = context.parentIndex,
51628 hints = context.hints;
51629
51630 if (!source || !target) {
51631 throw new Error('source and target required');
51632 }
51633
51634 if (!parent) {
51635 throw new Error('parent required');
51636 }
51637
51638 connection.source = source;
51639 connection.target = target;
51640
51641 if (!connection.waypoints) {
51642 connection.waypoints = this._layouter.layoutConnection(connection, hints);
51643 }
51644
51645 // add connection
51646 this._canvas.addConnection(connection, parent, parentIndex);
51647
51648 return connection;
51649 };
51650
51651 CreateConnectionHandler.prototype.revert = function(context) {
51652 var connection = context.connection;
51653
51654 this._canvas.removeConnection(connection);
51655
51656 connection.source = null;
51657 connection.target = null;
51658
51659 return connection;
51660 };
51661
51662 var round$3 = Math.round;
51663
51664 function CreateElementsHandler(modeling) {
51665 this._modeling = modeling;
51666 }
51667
51668 CreateElementsHandler.$inject = [
51669 'modeling'
51670 ];
51671
51672 CreateElementsHandler.prototype.preExecute = function(context) {
51673 var elements = context.elements,
51674 parent = context.parent,
51675 parentIndex = context.parentIndex,
51676 position = context.position,
51677 hints = context.hints;
51678
51679 var modeling = this._modeling;
51680
51681 // make sure each element has x and y
51682 forEach$2(elements, function(element) {
51683 if (!isNumber(element.x)) {
51684 element.x = 0;
51685 }
51686
51687 if (!isNumber(element.y)) {
51688 element.y = 0;
51689 }
51690 });
51691
51692 var visibleElements = filter(elements, function(element) {
51693 return !element.hidden;
51694 });
51695
51696 var bbox = getBBox(visibleElements);
51697
51698 // center elements around position
51699 forEach$2(elements, function(element) {
51700 if (isConnection$5(element)) {
51701 element.waypoints = map(element.waypoints, function(waypoint) {
51702 return {
51703 x: round$3(waypoint.x - bbox.x - bbox.width / 2 + position.x),
51704 y: round$3(waypoint.y - bbox.y - bbox.height / 2 + position.y)
51705 };
51706 });
51707 }
51708
51709 assign(element, {
51710 x: round$3(element.x - bbox.x - bbox.width / 2 + position.x),
51711 y: round$3(element.y - bbox.y - bbox.height / 2 + position.y)
51712 });
51713 });
51714
51715 var parents = getParents$1(elements);
51716
51717 var cache = {};
51718
51719 forEach$2(elements, function(element) {
51720 if (isConnection$5(element)) {
51721 cache[ element.id ] = isNumber(parentIndex) ?
51722 modeling.createConnection(
51723 cache[ element.source.id ],
51724 cache[ element.target.id ],
51725 parentIndex,
51726 element,
51727 element.parent || parent,
51728 hints
51729 ) :
51730 modeling.createConnection(
51731 cache[ element.source.id ],
51732 cache[ element.target.id ],
51733 element,
51734 element.parent || parent,
51735 hints
51736 );
51737
51738 return;
51739 }
51740
51741 var createShapeHints = assign({}, hints);
51742
51743 if (parents.indexOf(element) === -1) {
51744 createShapeHints.autoResize = false;
51745 }
51746
51747 cache[ element.id ] = isNumber(parentIndex) ?
51748 modeling.createShape(
51749 element,
51750 pick(element, [ 'x', 'y', 'width', 'height' ]),
51751 element.parent || parent,
51752 parentIndex,
51753 createShapeHints
51754 ) :
51755 modeling.createShape(
51756 element,
51757 pick(element, [ 'x', 'y', 'width', 'height' ]),
51758 element.parent || parent,
51759 createShapeHints
51760 );
51761 });
51762
51763 context.elements = values(cache);
51764 };
51765
51766 // helpers //////////
51767
51768 function isConnection$5(element) {
51769 return !!element.waypoints;
51770 }
51771
51772 var round$2 = Math.round;
51773
51774
51775 /**
51776 * A handler that implements reversible addition of shapes.
51777 *
51778 * @param {canvas} Canvas
51779 */
51780 function CreateShapeHandler(canvas) {
51781 this._canvas = canvas;
51782 }
51783
51784 CreateShapeHandler.$inject = [ 'canvas' ];
51785
51786
51787 // api //////////////////////
51788
51789
51790 /**
51791 * Appends a shape to a target shape
51792 *
51793 * @param {Object} context
51794 * @param {djs.model.Base} context.parent the parent object
51795 * @param {Point} context.position position of the new element
51796 */
51797 CreateShapeHandler.prototype.execute = function(context) {
51798
51799 var shape = context.shape,
51800 positionOrBounds = context.position,
51801 parent = context.parent,
51802 parentIndex = context.parentIndex;
51803
51804 if (!parent) {
51805 throw new Error('parent required');
51806 }
51807
51808 if (!positionOrBounds) {
51809 throw new Error('position required');
51810 }
51811
51812 // (1) add at event center position _or_ at given bounds
51813 if (positionOrBounds.width !== undefined) {
51814 assign(shape, positionOrBounds);
51815 } else {
51816 assign(shape, {
51817 x: positionOrBounds.x - round$2(shape.width / 2),
51818 y: positionOrBounds.y - round$2(shape.height / 2)
51819 });
51820 }
51821
51822 // (2) add to canvas
51823 this._canvas.addShape(shape, parent, parentIndex);
51824
51825 return shape;
51826 };
51827
51828
51829 /**
51830 * Undo append by removing the shape
51831 */
51832 CreateShapeHandler.prototype.revert = function(context) {
51833
51834 var shape = context.shape;
51835
51836 // (3) remove form canvas
51837 this._canvas.removeShape(shape);
51838
51839 return shape;
51840 };
51841
51842 /**
51843 * A handler that attaches a label to a given target shape.
51844 *
51845 * @param {Canvas} canvas
51846 */
51847 function CreateLabelHandler(canvas) {
51848 CreateShapeHandler.call(this, canvas);
51849 }
51850
51851 inherits$1(CreateLabelHandler, CreateShapeHandler);
51852
51853 CreateLabelHandler.$inject = [ 'canvas' ];
51854
51855
51856 // api //////////////////////
51857
51858
51859 var originalExecute = CreateShapeHandler.prototype.execute;
51860
51861 /**
51862 * Appends a label to a target shape.
51863 *
51864 * @method CreateLabelHandler#execute
51865 *
51866 * @param {Object} context
51867 * @param {ElementDescriptor} context.target the element the label is attached to
51868 * @param {ElementDescriptor} context.parent the parent object
51869 * @param {Point} context.position position of the new element
51870 */
51871 CreateLabelHandler.prototype.execute = function(context) {
51872
51873 var label = context.shape;
51874
51875 ensureValidDimensions(label);
51876
51877 label.labelTarget = context.labelTarget;
51878
51879 return originalExecute.call(this, context);
51880 };
51881
51882 var originalRevert = CreateShapeHandler.prototype.revert;
51883
51884 /**
51885 * Undo append by removing the shape
51886 */
51887 CreateLabelHandler.prototype.revert = function(context) {
51888 context.shape.labelTarget = null;
51889
51890 return originalRevert.call(this, context);
51891 };
51892
51893
51894 // helpers //////////////////////
51895
51896 function ensureValidDimensions(label) {
51897
51898 // make sure a label has valid { width, height } dimensions
51899 [ 'width', 'height' ].forEach(function(prop) {
51900 if (typeof label[prop] === 'undefined') {
51901 label[prop] = 0;
51902 }
51903 });
51904 }
51905
51906 /**
51907 * A handler that implements reversible deletion of Connections.
51908 */
51909 function DeleteConnectionHandler(canvas, modeling) {
51910 this._canvas = canvas;
51911 this._modeling = modeling;
51912 }
51913
51914 DeleteConnectionHandler.$inject = [
51915 'canvas',
51916 'modeling'
51917 ];
51918
51919
51920 DeleteConnectionHandler.prototype.execute = function(context) {
51921
51922 var connection = context.connection,
51923 parent = connection.parent;
51924
51925 context.parent = parent;
51926
51927 // remember containment
51928 context.parentIndex = indexOf(parent.children, connection);
51929
51930 context.source = connection.source;
51931 context.target = connection.target;
51932
51933 this._canvas.removeConnection(connection);
51934
51935 connection.source = null;
51936 connection.target = null;
51937
51938 return connection;
51939 };
51940
51941 /**
51942 * Command revert implementation.
51943 */
51944 DeleteConnectionHandler.prototype.revert = function(context) {
51945
51946 var connection = context.connection,
51947 parent = context.parent,
51948 parentIndex = context.parentIndex;
51949
51950 connection.source = context.source;
51951 connection.target = context.target;
51952
51953 // restore containment
51954 add(parent.children, connection, parentIndex);
51955
51956 this._canvas.addConnection(connection, parent);
51957
51958 return connection;
51959 };
51960
51961 function DeleteElementsHandler(modeling, elementRegistry) {
51962 this._modeling = modeling;
51963 this._elementRegistry = elementRegistry;
51964 }
51965
51966 DeleteElementsHandler.$inject = [
51967 'modeling',
51968 'elementRegistry'
51969 ];
51970
51971
51972 DeleteElementsHandler.prototype.postExecute = function(context) {
51973
51974 var modeling = this._modeling,
51975 elementRegistry = this._elementRegistry,
51976 elements = context.elements;
51977
51978 forEach$2(elements, function(element) {
51979
51980 // element may have been removed with previous
51981 // remove operations already (e.g. in case of nesting)
51982 if (!elementRegistry.get(element.id)) {
51983 return;
51984 }
51985
51986 if (element.waypoints) {
51987 modeling.removeConnection(element);
51988 } else {
51989 modeling.removeShape(element);
51990 }
51991 });
51992 };
51993
51994 /**
51995 * A handler that implements reversible deletion of shapes.
51996 *
51997 */
51998 function DeleteShapeHandler(canvas, modeling) {
51999 this._canvas = canvas;
52000 this._modeling = modeling;
52001 }
52002
52003 DeleteShapeHandler.$inject = [ 'canvas', 'modeling' ];
52004
52005
52006 /**
52007 * - Remove connections
52008 * - Remove all direct children
52009 */
52010 DeleteShapeHandler.prototype.preExecute = function(context) {
52011
52012 var modeling = this._modeling;
52013
52014 var shape = context.shape;
52015
52016 // remove connections
52017 saveClear(shape.incoming, function(connection) {
52018
52019 // To make sure that the connection isn't removed twice
52020 // For example if a container is removed
52021 modeling.removeConnection(connection, { nested: true });
52022 });
52023
52024 saveClear(shape.outgoing, function(connection) {
52025 modeling.removeConnection(connection, { nested: true });
52026 });
52027
52028 // remove child shapes and connections
52029 saveClear(shape.children, function(child) {
52030 if (isConnection$4(child)) {
52031 modeling.removeConnection(child, { nested: true });
52032 } else {
52033 modeling.removeShape(child, { nested: true });
52034 }
52035 });
52036 };
52037
52038 /**
52039 * Remove shape and remember the parent
52040 */
52041 DeleteShapeHandler.prototype.execute = function(context) {
52042 var canvas = this._canvas;
52043
52044 var shape = context.shape,
52045 oldParent = shape.parent;
52046
52047 context.oldParent = oldParent;
52048
52049 // remove containment
52050 context.oldParentIndex = indexOf(oldParent.children, shape);
52051
52052 // remove shape
52053 canvas.removeShape(shape);
52054
52055 return shape;
52056 };
52057
52058
52059 /**
52060 * Command revert implementation
52061 */
52062 DeleteShapeHandler.prototype.revert = function(context) {
52063
52064 var canvas = this._canvas;
52065
52066 var shape = context.shape,
52067 oldParent = context.oldParent,
52068 oldParentIndex = context.oldParentIndex;
52069
52070 // restore containment
52071 add(oldParent.children, shape, oldParentIndex);
52072
52073 canvas.addShape(shape, oldParent);
52074
52075 return shape;
52076 };
52077
52078 function isConnection$4(element) {
52079 return element.waypoints;
52080 }
52081
52082 /**
52083 * A handler that distributes elements evenly.
52084 */
52085 function DistributeElements(modeling) {
52086 this._modeling = modeling;
52087 }
52088
52089 DistributeElements.$inject = [ 'modeling' ];
52090
52091 var OFF_AXIS = {
52092 x: 'y',
52093 y: 'x'
52094 };
52095
52096 DistributeElements.prototype.preExecute = function(context) {
52097 var modeling = this._modeling;
52098
52099 var groups = context.groups,
52100 axis = context.axis,
52101 dimension = context.dimension;
52102
52103 function updateRange(group, element) {
52104 group.range.min = Math.min(element[axis], group.range.min);
52105 group.range.max = Math.max(element[axis] + element[dimension], group.range.max);
52106 }
52107
52108 function center(element) {
52109 return element[axis] + element[dimension] / 2;
52110 }
52111
52112 function lastIdx(arr) {
52113 return arr.length - 1;
52114 }
52115
52116 function rangeDiff(range) {
52117 return range.max - range.min;
52118 }
52119
52120 function centerElement(refCenter, element) {
52121 var delta = { y: 0 };
52122
52123 delta[axis] = refCenter - center(element);
52124
52125 if (delta[axis]) {
52126
52127 delta[OFF_AXIS[axis]] = 0;
52128
52129 modeling.moveElements([ element ], delta, element.parent);
52130 }
52131 }
52132
52133 var firstGroup = groups[0],
52134 lastGroupIdx = lastIdx(groups),
52135 lastGroup = groups[ lastGroupIdx ];
52136
52137 var margin,
52138 spaceInBetween,
52139 groupsSize = 0; // the size of each range
52140
52141 forEach$2(groups, function(group, idx) {
52142 var sortedElements,
52143 refElem,
52144 refCenter;
52145
52146 if (group.elements.length < 2) {
52147 if (idx && idx !== groups.length - 1) {
52148 updateRange(group, group.elements[0]);
52149
52150 groupsSize += rangeDiff(group.range);
52151 }
52152 return;
52153 }
52154
52155 sortedElements = sortBy(group.elements, axis);
52156
52157 refElem = sortedElements[0];
52158
52159 if (idx === lastGroupIdx) {
52160 refElem = sortedElements[lastIdx(sortedElements)];
52161 }
52162
52163 refCenter = center(refElem);
52164
52165 // wanna update the ranges after the shapes have been centered
52166 group.range = null;
52167
52168 forEach$2(sortedElements, function(element) {
52169
52170 centerElement(refCenter, element);
52171
52172 if (group.range === null) {
52173 group.range = {
52174 min: element[axis],
52175 max: element[axis] + element[dimension]
52176 };
52177
52178 return;
52179 }
52180
52181 // update group's range after centering the range elements
52182 updateRange(group, element);
52183 });
52184
52185 if (idx && idx !== groups.length - 1) {
52186 groupsSize += rangeDiff(group.range);
52187 }
52188 });
52189
52190 spaceInBetween = Math.abs(lastGroup.range.min - firstGroup.range.max);
52191
52192 margin = Math.round((spaceInBetween - groupsSize) / (groups.length - 1));
52193
52194 if (margin < groups.length - 1) {
52195 return;
52196 }
52197
52198 forEach$2(groups, function(group, groupIdx) {
52199 var delta = {},
52200 prevGroup;
52201
52202 if (group === firstGroup || group === lastGroup) {
52203 return;
52204 }
52205
52206 prevGroup = groups[groupIdx - 1];
52207
52208 group.range.max = 0;
52209
52210 forEach$2(group.elements, function(element, idx) {
52211 delta[OFF_AXIS[axis]] = 0;
52212 delta[axis] = (prevGroup.range.max - element[axis]) + margin;
52213
52214 if (group.range.min !== element[axis]) {
52215 delta[axis] += element[axis] - group.range.min;
52216 }
52217
52218 if (delta[axis]) {
52219 modeling.moveElements([ element ], delta, element.parent);
52220 }
52221
52222 group.range.max = Math.max(element[axis] + element[dimension], idx ? group.range.max : 0);
52223 });
52224 });
52225 };
52226
52227 DistributeElements.prototype.postExecute = function(context) {
52228
52229 };
52230
52231 /**
52232 * A handler that implements reversible moving of shapes.
52233 */
52234 function LayoutConnectionHandler(layouter, canvas) {
52235 this._layouter = layouter;
52236 this._canvas = canvas;
52237 }
52238
52239 LayoutConnectionHandler.$inject = [ 'layouter', 'canvas' ];
52240
52241 LayoutConnectionHandler.prototype.execute = function(context) {
52242
52243 var connection = context.connection;
52244
52245 var oldWaypoints = connection.waypoints;
52246
52247 assign(context, {
52248 oldWaypoints: oldWaypoints
52249 });
52250
52251 connection.waypoints = this._layouter.layoutConnection(connection, context.hints);
52252
52253 return connection;
52254 };
52255
52256 LayoutConnectionHandler.prototype.revert = function(context) {
52257
52258 var connection = context.connection;
52259
52260 connection.waypoints = context.oldWaypoints;
52261
52262 return connection;
52263 };
52264
52265 /**
52266 * A handler that implements reversible moving of connections.
52267 *
52268 * The handler differs from the layout connection handler in a sense
52269 * that it preserves the connection layout.
52270 */
52271 function MoveConnectionHandler() { }
52272
52273
52274 MoveConnectionHandler.prototype.execute = function(context) {
52275
52276 var connection = context.connection,
52277 delta = context.delta;
52278
52279 var newParent = context.newParent || connection.parent,
52280 newParentIndex = context.newParentIndex,
52281 oldParent = connection.parent;
52282
52283 // save old parent in context
52284 context.oldParent = oldParent;
52285 context.oldParentIndex = remove(oldParent.children, connection);
52286
52287 // add to new parent at position
52288 add(newParent.children, connection, newParentIndex);
52289
52290 // update parent
52291 connection.parent = newParent;
52292
52293 // update waypoint positions
52294 forEach$2(connection.waypoints, function(p) {
52295 p.x += delta.x;
52296 p.y += delta.y;
52297
52298 if (p.original) {
52299 p.original.x += delta.x;
52300 p.original.y += delta.y;
52301 }
52302 });
52303
52304 return connection;
52305 };
52306
52307 MoveConnectionHandler.prototype.revert = function(context) {
52308
52309 var connection = context.connection,
52310 newParent = connection.parent,
52311 oldParent = context.oldParent,
52312 oldParentIndex = context.oldParentIndex,
52313 delta = context.delta;
52314
52315 // remove from newParent
52316 remove(newParent.children, connection);
52317
52318 // restore previous location in old parent
52319 add(oldParent.children, connection, oldParentIndex);
52320
52321 // restore parent
52322 connection.parent = oldParent;
52323
52324 // revert to old waypoint positions
52325 forEach$2(connection.waypoints, function(p) {
52326 p.x -= delta.x;
52327 p.y -= delta.y;
52328
52329 if (p.original) {
52330 p.original.x -= delta.x;
52331 p.original.y -= delta.y;
52332 }
52333 });
52334
52335 return connection;
52336 };
52337
52338 function MoveClosure() {
52339
52340 this.allShapes = {};
52341 this.allConnections = {};
52342
52343 this.enclosedElements = {};
52344 this.enclosedConnections = {};
52345
52346 this.topLevel = {};
52347 }
52348
52349
52350 MoveClosure.prototype.add = function(element, isTopLevel) {
52351 return this.addAll([ element ], isTopLevel);
52352 };
52353
52354
52355 MoveClosure.prototype.addAll = function(elements, isTopLevel) {
52356
52357 var newClosure = getClosure(elements, !!isTopLevel, this);
52358
52359 assign(this, newClosure);
52360
52361 return this;
52362 };
52363
52364 /**
52365 * A helper that is able to carry out serialized move
52366 * operations on multiple elements.
52367 *
52368 * @param {Modeling} modeling
52369 */
52370 function MoveHelper(modeling) {
52371 this._modeling = modeling;
52372 }
52373
52374 /**
52375 * Move the specified elements and all children by the given delta.
52376 *
52377 * This moves all enclosed connections, too and layouts all affected
52378 * external connections.
52379 *
52380 * @param {Array<djs.model.Base>} elements
52381 * @param {Point} delta
52382 * @param {djs.model.Base} newParent applied to the first level of shapes
52383 *
52384 * @return {Array<djs.model.Base>} list of touched elements
52385 */
52386 MoveHelper.prototype.moveRecursive = function(elements, delta, newParent) {
52387 if (!elements) {
52388 return [];
52389 } else {
52390 return this.moveClosure(this.getClosure(elements), delta, newParent);
52391 }
52392 };
52393
52394 /**
52395 * Move the given closure of elmements.
52396 *
52397 * @param {Object} closure
52398 * @param {Point} delta
52399 * @param {djs.model.Base} [newParent]
52400 * @param {djs.model.Base} [newHost]
52401 */
52402 MoveHelper.prototype.moveClosure = function(closure, delta, newParent, newHost, primaryShape) {
52403 var modeling = this._modeling;
52404
52405 var allShapes = closure.allShapes,
52406 allConnections = closure.allConnections,
52407 enclosedConnections = closure.enclosedConnections,
52408 topLevel = closure.topLevel,
52409 keepParent = false;
52410
52411 if (primaryShape && primaryShape.parent === newParent) {
52412 keepParent = true;
52413 }
52414
52415 // move all shapes
52416 forEach$2(allShapes, function(shape) {
52417
52418 // move the element according to the given delta
52419 modeling.moveShape(shape, delta, topLevel[shape.id] && !keepParent && newParent, {
52420 recurse: false,
52421 layout: false
52422 });
52423 });
52424
52425 // move all child connections / layout external connections
52426 forEach$2(allConnections, function(c) {
52427
52428 var sourceMoved = !!allShapes[c.source.id],
52429 targetMoved = !!allShapes[c.target.id];
52430
52431 if (enclosedConnections[c.id] && sourceMoved && targetMoved) {
52432 modeling.moveConnection(c, delta, topLevel[c.id] && !keepParent && newParent);
52433 } else {
52434 modeling.layoutConnection(c, {
52435 connectionStart: sourceMoved && getMovedSourceAnchor(c, c.source, delta),
52436 connectionEnd: targetMoved && getMovedTargetAnchor(c, c.target, delta)
52437 });
52438 }
52439 });
52440 };
52441
52442 /**
52443 * Returns the closure for the selected elements
52444 *
52445 * @param {Array<djs.model.Base>} elements
52446 * @return {MoveClosure} closure
52447 */
52448 MoveHelper.prototype.getClosure = function(elements) {
52449 return new MoveClosure().addAll(elements, true);
52450 };
52451
52452 /**
52453 * A handler that implements reversible moving of shapes.
52454 */
52455 function MoveElementsHandler(modeling) {
52456 this._helper = new MoveHelper(modeling);
52457 }
52458
52459 MoveElementsHandler.$inject = [ 'modeling' ];
52460
52461 MoveElementsHandler.prototype.preExecute = function(context) {
52462 context.closure = this._helper.getClosure(context.shapes);
52463 };
52464
52465 MoveElementsHandler.prototype.postExecute = function(context) {
52466
52467 var hints = context.hints,
52468 primaryShape;
52469
52470 if (hints && hints.primaryShape) {
52471 primaryShape = hints.primaryShape;
52472 hints.oldParent = primaryShape.parent;
52473 }
52474
52475 this._helper.moveClosure(
52476 context.closure,
52477 context.delta,
52478 context.newParent,
52479 context.newHost,
52480 primaryShape
52481 );
52482 };
52483
52484 /**
52485 * A handler that implements reversible moving of shapes.
52486 */
52487 function MoveShapeHandler(modeling) {
52488 this._modeling = modeling;
52489
52490 this._helper = new MoveHelper(modeling);
52491 }
52492
52493 MoveShapeHandler.$inject = [ 'modeling' ];
52494
52495
52496 MoveShapeHandler.prototype.execute = function(context) {
52497
52498 var shape = context.shape,
52499 delta = context.delta,
52500 newParent = context.newParent || shape.parent,
52501 newParentIndex = context.newParentIndex,
52502 oldParent = shape.parent;
52503
52504 context.oldBounds = pick(shape, [ 'x', 'y', 'width', 'height' ]);
52505
52506 // save old parent in context
52507 context.oldParent = oldParent;
52508 context.oldParentIndex = remove(oldParent.children, shape);
52509
52510 // add to new parent at position
52511 add(newParent.children, shape, newParentIndex);
52512
52513 // update shape parent + position
52514 assign(shape, {
52515 parent: newParent,
52516 x: shape.x + delta.x,
52517 y: shape.y + delta.y
52518 });
52519
52520 return shape;
52521 };
52522
52523 MoveShapeHandler.prototype.postExecute = function(context) {
52524
52525 var shape = context.shape,
52526 delta = context.delta,
52527 hints = context.hints;
52528
52529 var modeling = this._modeling;
52530
52531 if (hints.layout !== false) {
52532
52533 forEach$2(shape.incoming, function(c) {
52534 modeling.layoutConnection(c, {
52535 connectionEnd: getMovedTargetAnchor(c, shape, delta)
52536 });
52537 });
52538
52539 forEach$2(shape.outgoing, function(c) {
52540 modeling.layoutConnection(c, {
52541 connectionStart: getMovedSourceAnchor(c, shape, delta)
52542 });
52543 });
52544 }
52545
52546 if (hints.recurse !== false) {
52547 this.moveChildren(context);
52548 }
52549 };
52550
52551 MoveShapeHandler.prototype.revert = function(context) {
52552
52553 var shape = context.shape,
52554 oldParent = context.oldParent,
52555 oldParentIndex = context.oldParentIndex,
52556 delta = context.delta;
52557
52558 // restore previous location in old parent
52559 add(oldParent.children, shape, oldParentIndex);
52560
52561 // revert to old position and parent
52562 assign(shape, {
52563 parent: oldParent,
52564 x: shape.x - delta.x,
52565 y: shape.y - delta.y
52566 });
52567
52568 return shape;
52569 };
52570
52571 MoveShapeHandler.prototype.moveChildren = function(context) {
52572
52573 var delta = context.delta,
52574 shape = context.shape;
52575
52576 this._helper.moveRecursive(shape.children, delta, null);
52577 };
52578
52579 MoveShapeHandler.prototype.getNewParent = function(context) {
52580 return context.newParent || context.shape.parent;
52581 };
52582
52583 /**
52584 * Reconnect connection handler
52585 */
52586 function ReconnectConnectionHandler(modeling) {
52587 this._modeling = modeling;
52588 }
52589
52590 ReconnectConnectionHandler.$inject = [ 'modeling' ];
52591
52592 ReconnectConnectionHandler.prototype.execute = function(context) {
52593 var newSource = context.newSource,
52594 newTarget = context.newTarget,
52595 connection = context.connection,
52596 dockingOrPoints = context.dockingOrPoints;
52597
52598 if (!newSource && !newTarget) {
52599 throw new Error('newSource or newTarget required');
52600 }
52601
52602 if (isArray$4(dockingOrPoints)) {
52603 context.oldWaypoints = connection.waypoints;
52604 connection.waypoints = dockingOrPoints;
52605 }
52606
52607 if (newSource) {
52608 context.oldSource = connection.source;
52609 connection.source = newSource;
52610 }
52611
52612 if (newTarget) {
52613 context.oldTarget = connection.target;
52614 connection.target = newTarget;
52615 }
52616
52617 return connection;
52618 };
52619
52620 ReconnectConnectionHandler.prototype.postExecute = function(context) {
52621 var connection = context.connection,
52622 newSource = context.newSource,
52623 newTarget = context.newTarget,
52624 dockingOrPoints = context.dockingOrPoints,
52625 hints = context.hints || {};
52626
52627 var layoutConnectionHints = {};
52628
52629 if (hints.connectionStart) {
52630 layoutConnectionHints.connectionStart = hints.connectionStart;
52631 }
52632
52633 if (hints.connectionEnd) {
52634 layoutConnectionHints.connectionEnd = hints.connectionEnd;
52635 }
52636
52637 if (hints.layoutConnection === false) {
52638 return;
52639 }
52640
52641 if (newSource && (!newTarget || hints.docking === 'source')) {
52642 layoutConnectionHints.connectionStart = layoutConnectionHints.connectionStart
52643 || getDocking(isArray$4(dockingOrPoints) ? dockingOrPoints[ 0 ] : dockingOrPoints);
52644 }
52645
52646 if (newTarget && (!newSource || hints.docking === 'target')) {
52647 layoutConnectionHints.connectionEnd = layoutConnectionHints.connectionEnd
52648 || getDocking(isArray$4(dockingOrPoints) ? dockingOrPoints[ dockingOrPoints.length - 1 ] : dockingOrPoints);
52649 }
52650
52651 if (hints.newWaypoints) {
52652 layoutConnectionHints.waypoints = hints.newWaypoints;
52653 }
52654
52655 this._modeling.layoutConnection(connection, layoutConnectionHints);
52656 };
52657
52658 ReconnectConnectionHandler.prototype.revert = function(context) {
52659 var oldSource = context.oldSource,
52660 oldTarget = context.oldTarget,
52661 oldWaypoints = context.oldWaypoints,
52662 connection = context.connection;
52663
52664 if (oldSource) {
52665 connection.source = oldSource;
52666 }
52667
52668 if (oldTarget) {
52669 connection.target = oldTarget;
52670 }
52671
52672 if (oldWaypoints) {
52673 connection.waypoints = oldWaypoints;
52674 }
52675
52676 return connection;
52677 };
52678
52679
52680
52681 // helpers //////////
52682
52683 function getDocking(point) {
52684 return point.original || point;
52685 }
52686
52687 /**
52688 * Replace shape by adding new shape and removing old shape. Incoming and outgoing connections will
52689 * be kept if possible.
52690 *
52691 * @class
52692 * @constructor
52693 *
52694 * @param {Modeling} modeling
52695 * @param {Rules} rules
52696 */
52697 function ReplaceShapeHandler(modeling, rules) {
52698 this._modeling = modeling;
52699 this._rules = rules;
52700 }
52701
52702 ReplaceShapeHandler.$inject = [ 'modeling', 'rules' ];
52703
52704
52705 /**
52706 * Add new shape.
52707 *
52708 * @param {Object} context
52709 * @param {djs.model.Shape} context.oldShape
52710 * @param {Object} context.newData
52711 * @param {string} context.newData.type
52712 * @param {number} context.newData.x
52713 * @param {number} context.newData.y
52714 * @param {Object} [hints]
52715 */
52716 ReplaceShapeHandler.prototype.preExecute = function(context) {
52717 var self = this,
52718 modeling = this._modeling,
52719 rules = this._rules;
52720
52721 var oldShape = context.oldShape,
52722 newData = context.newData,
52723 hints = context.hints || {},
52724 newShape;
52725
52726 function canReconnect(source, target, connection) {
52727 return rules.allowed('connection.reconnect', {
52728 connection: connection,
52729 source: source,
52730 target: target
52731 });
52732 }
52733
52734 // (1) add new shape at given position
52735 var position = {
52736 x: newData.x,
52737 y: newData.y
52738 };
52739
52740 var oldBounds = {
52741 x: oldShape.x,
52742 y: oldShape.y,
52743 width: oldShape.width,
52744 height: oldShape.height
52745 };
52746
52747 newShape = context.newShape =
52748 context.newShape ||
52749 self.createShape(newData, position, oldShape.parent, hints);
52750
52751 // (2) update host
52752 if (oldShape.host) {
52753 modeling.updateAttachment(newShape, oldShape.host);
52754 }
52755
52756 // (3) adopt all children from old shape
52757 var children;
52758
52759 if (hints.moveChildren !== false) {
52760 children = oldShape.children.slice();
52761
52762 modeling.moveElements(children, { x: 0, y: 0 }, newShape, hints);
52763 }
52764
52765 // (4) reconnect connections to new shape if possible
52766 var incoming = oldShape.incoming.slice(),
52767 outgoing = oldShape.outgoing.slice();
52768
52769 forEach$2(incoming, function(connection) {
52770 var source = connection.source,
52771 allowed = canReconnect(source, newShape, connection);
52772
52773 if (allowed) {
52774 self.reconnectEnd(
52775 connection, newShape,
52776 getResizedTargetAnchor(connection, newShape, oldBounds),
52777 hints
52778 );
52779 }
52780 });
52781
52782 forEach$2(outgoing, function(connection) {
52783 var target = connection.target,
52784 allowed = canReconnect(newShape, target, connection);
52785
52786 if (allowed) {
52787 self.reconnectStart(
52788 connection, newShape,
52789 getResizedSourceAnchor(connection, newShape, oldBounds),
52790 hints
52791 );
52792 }
52793 });
52794 };
52795
52796
52797 /**
52798 * Remove old shape.
52799 */
52800 ReplaceShapeHandler.prototype.postExecute = function(context) {
52801 var oldShape = context.oldShape;
52802
52803 this._modeling.removeShape(oldShape);
52804 };
52805
52806
52807 ReplaceShapeHandler.prototype.execute = function(context) {};
52808
52809
52810 ReplaceShapeHandler.prototype.revert = function(context) {};
52811
52812
52813 ReplaceShapeHandler.prototype.createShape = function(shape, position, target, hints) {
52814 return this._modeling.createShape(shape, position, target, hints);
52815 };
52816
52817
52818 ReplaceShapeHandler.prototype.reconnectStart = function(connection, newSource, dockingPoint, hints) {
52819 this._modeling.reconnectStart(connection, newSource, dockingPoint, hints);
52820 };
52821
52822
52823 ReplaceShapeHandler.prototype.reconnectEnd = function(connection, newTarget, dockingPoint, hints) {
52824 this._modeling.reconnectEnd(connection, newTarget, dockingPoint, hints);
52825 };
52826
52827 /**
52828 * A handler that implements reversible resizing of shapes.
52829 *
52830 * @param {Modeling} modeling
52831 */
52832 function ResizeShapeHandler(modeling) {
52833 this._modeling = modeling;
52834 }
52835
52836 ResizeShapeHandler.$inject = [ 'modeling' ];
52837
52838 /**
52839 * {
52840 * shape: {....}
52841 * newBounds: {
52842 * width: 20,
52843 * height: 40,
52844 * x: 5,
52845 * y: 10
52846 * }
52847 *
52848 * }
52849 */
52850 ResizeShapeHandler.prototype.execute = function(context) {
52851 var shape = context.shape,
52852 newBounds = context.newBounds,
52853 minBounds = context.minBounds;
52854
52855 if (newBounds.x === undefined || newBounds.y === undefined ||
52856 newBounds.width === undefined || newBounds.height === undefined) {
52857 throw new Error('newBounds must have {x, y, width, height} properties');
52858 }
52859
52860 if (minBounds && (newBounds.width < minBounds.width
52861 || newBounds.height < minBounds.height)) {
52862 throw new Error('width and height cannot be less than minimum height and width');
52863 } else if (!minBounds
52864 && newBounds.width < 10 || newBounds.height < 10) {
52865 throw new Error('width and height cannot be less than 10px');
52866 }
52867
52868 // save old bbox in context
52869 context.oldBounds = {
52870 width: shape.width,
52871 height: shape.height,
52872 x: shape.x,
52873 y: shape.y
52874 };
52875
52876 // update shape
52877 assign(shape, {
52878 width: newBounds.width,
52879 height: newBounds.height,
52880 x: newBounds.x,
52881 y: newBounds.y
52882 });
52883
52884 return shape;
52885 };
52886
52887 ResizeShapeHandler.prototype.postExecute = function(context) {
52888 var modeling = this._modeling;
52889
52890 var shape = context.shape,
52891 oldBounds = context.oldBounds,
52892 hints = context.hints || {};
52893
52894 if (hints.layout === false) {
52895 return;
52896 }
52897
52898 forEach$2(shape.incoming, function(c) {
52899 modeling.layoutConnection(c, {
52900 connectionEnd: getResizedTargetAnchor(c, shape, oldBounds)
52901 });
52902 });
52903
52904 forEach$2(shape.outgoing, function(c) {
52905 modeling.layoutConnection(c, {
52906 connectionStart: getResizedSourceAnchor(c, shape, oldBounds)
52907 });
52908 });
52909
52910 };
52911
52912 ResizeShapeHandler.prototype.revert = function(context) {
52913
52914 var shape = context.shape,
52915 oldBounds = context.oldBounds;
52916
52917 // restore previous bbox
52918 assign(shape, {
52919 width: oldBounds.width,
52920 height: oldBounds.height,
52921 x: oldBounds.x,
52922 y: oldBounds.y
52923 });
52924
52925 return shape;
52926 };
52927
52928 /**
52929 * Add or remove space by moving and resizing shapes and updating connection waypoints.
52930 */
52931 function SpaceToolHandler(modeling) {
52932 this._modeling = modeling;
52933 }
52934
52935 SpaceToolHandler.$inject = [ 'modeling' ];
52936
52937 SpaceToolHandler.prototype.preExecute = function(context) {
52938 var delta = context.delta,
52939 direction = context.direction,
52940 movingShapes = context.movingShapes,
52941 resizingShapes = context.resizingShapes,
52942 start = context.start,
52943 oldBounds = {};
52944
52945 // (1) move shapes
52946 this.moveShapes(movingShapes, delta);
52947
52948 // (2a) save old bounds of resized shapes
52949 forEach$2(resizingShapes, function(shape) {
52950 oldBounds[shape.id] = getBounds(shape);
52951 });
52952
52953 // (2b) resize shapes
52954 this.resizeShapes(resizingShapes, delta, direction);
52955
52956 // (3) update connection waypoints
52957 this.updateConnectionWaypoints(
52958 getWaypointsUpdatingConnections(movingShapes, resizingShapes),
52959 delta,
52960 direction,
52961 start,
52962 movingShapes,
52963 resizingShapes,
52964 oldBounds
52965 );
52966 };
52967
52968 SpaceToolHandler.prototype.execute = function() {};
52969 SpaceToolHandler.prototype.revert = function() {};
52970
52971 SpaceToolHandler.prototype.moveShapes = function(shapes, delta) {
52972 var self = this;
52973
52974 forEach$2(shapes, function(element) {
52975 self._modeling.moveShape(element, delta, null, {
52976 autoResize: false,
52977 layout: false,
52978 recurse: false
52979 });
52980 });
52981 };
52982
52983 SpaceToolHandler.prototype.resizeShapes = function(shapes, delta, direction) {
52984 var self = this;
52985
52986 forEach$2(shapes, function(shape) {
52987 var newBounds = resizeBounds(shape, direction, delta);
52988
52989 self._modeling.resizeShape(shape, newBounds, null, {
52990 attachSupport: false,
52991 autoResize: false,
52992 layout: false
52993 });
52994 });
52995 };
52996
52997 /**
52998 * Update connections waypoints according to the rules:
52999 * 1. Both source and target are moved/resized => move waypoints by the delta
53000 * 2. Only one of source and target is moved/resized => re-layout connection with moved start/end
53001 */
53002 SpaceToolHandler.prototype.updateConnectionWaypoints = function(
53003 connections,
53004 delta,
53005 direction,
53006 start,
53007 movingShapes,
53008 resizingShapes,
53009 oldBounds
53010 ) {
53011 var self = this,
53012 affectedShapes = movingShapes.concat(resizingShapes);
53013
53014 forEach$2(connections, function(connection) {
53015 var source = connection.source,
53016 target = connection.target,
53017 waypoints = copyWaypoints(connection),
53018 axis = getAxisFromDirection(direction),
53019 layoutHints = {
53020 labelBehavior: false
53021 };
53022
53023 if (includes$1(affectedShapes, source) && includes$1(affectedShapes, target)) {
53024
53025 // move waypoints
53026 waypoints = map(waypoints, function(waypoint) {
53027 if (shouldMoveWaypoint(waypoint, start, direction)) {
53028
53029 // move waypoint
53030 waypoint[ axis ] = waypoint[ axis ] + delta[ axis ];
53031 }
53032
53033 if (waypoint.original && shouldMoveWaypoint(waypoint.original, start, direction)) {
53034
53035 // move waypoint original
53036 waypoint.original[ axis ] = waypoint.original[ axis ] + delta[ axis ];
53037 }
53038
53039 return waypoint;
53040 });
53041
53042 self._modeling.updateWaypoints(connection, waypoints, {
53043 labelBehavior: false
53044 });
53045 } else if (includes$1(affectedShapes, source) || includes$1(affectedShapes, target)) {
53046
53047 // re-layout connection with moved start/end
53048 if (includes$1(movingShapes, source)) {
53049 layoutHints.connectionStart = getMovedSourceAnchor(connection, source, delta);
53050 } else if (includes$1(movingShapes, target)) {
53051 layoutHints.connectionEnd = getMovedTargetAnchor(connection, target, delta);
53052 } else if (includes$1(resizingShapes, source)) {
53053 layoutHints.connectionStart = getResizedSourceAnchor(
53054 connection, source, oldBounds[source.id]
53055 );
53056 } else if (includes$1(resizingShapes, target)) {
53057 layoutHints.connectionEnd = getResizedTargetAnchor(
53058 connection, target, oldBounds[target.id]
53059 );
53060 }
53061
53062 self._modeling.layoutConnection(connection, layoutHints);
53063 }
53064 });
53065 };
53066
53067
53068 // helpers //////////
53069
53070 function copyWaypoint(waypoint) {
53071 return assign({}, waypoint);
53072 }
53073
53074 function copyWaypoints(connection) {
53075 return map(connection.waypoints, function(waypoint) {
53076
53077 waypoint = copyWaypoint(waypoint);
53078
53079 if (waypoint.original) {
53080 waypoint.original = copyWaypoint(waypoint.original);
53081 }
53082
53083 return waypoint;
53084 });
53085 }
53086
53087 function getAxisFromDirection(direction) {
53088 switch (direction) {
53089 case 'n':
53090 return 'y';
53091 case 'w':
53092 return 'x';
53093 case 's':
53094 return 'y';
53095 case 'e':
53096 return 'x';
53097 }
53098 }
53099
53100 function shouldMoveWaypoint(waypoint, start, direction) {
53101 var relevantAxis = getAxisFromDirection(direction);
53102
53103 if (/e|s/.test(direction)) {
53104 return waypoint[ relevantAxis ] > start;
53105 } else if (/n|w/.test(direction)) {
53106 return waypoint[ relevantAxis ] < start;
53107 }
53108 }
53109
53110 function includes$1(array, item) {
53111 return array.indexOf(item) !== -1;
53112 }
53113
53114 function getBounds(shape) {
53115 return {
53116 x: shape.x,
53117 y: shape.y,
53118 height: shape.height,
53119 width: shape.width
53120 };
53121 }
53122
53123 /**
53124 * A handler that toggles the collapsed state of an element
53125 * and the visibility of all its children.
53126 *
53127 * @param {Modeling} modeling
53128 */
53129 function ToggleShapeCollapseHandler(modeling) {
53130 this._modeling = modeling;
53131 }
53132
53133 ToggleShapeCollapseHandler.$inject = [ 'modeling' ];
53134
53135
53136 ToggleShapeCollapseHandler.prototype.execute = function(context) {
53137
53138 var shape = context.shape,
53139 children = shape.children;
53140
53141 // recursively remember previous visibility of children
53142 context.oldChildrenVisibility = getElementsVisibilityRecursive(children);
53143
53144 // toggle state
53145 shape.collapsed = !shape.collapsed;
53146
53147 // recursively hide/show children
53148 var result = setHiddenRecursive(children, shape.collapsed);
53149
53150 return [ shape ].concat(result);
53151 };
53152
53153
53154 ToggleShapeCollapseHandler.prototype.revert = function(context) {
53155
53156 var shape = context.shape,
53157 oldChildrenVisibility = context.oldChildrenVisibility;
53158
53159 var children = shape.children;
53160
53161 // recursively set old visability of children
53162 var result = restoreVisibilityRecursive(children, oldChildrenVisibility);
53163
53164 // retoggle state
53165 shape.collapsed = !shape.collapsed;
53166
53167 return [ shape ].concat(result);
53168 };
53169
53170
53171 // helpers //////////////////////
53172
53173 /**
53174 * Return a map { elementId -> hiddenState}.
53175 *
53176 * @param {Array<djs.model.Shape>} elements
53177 *
53178 * @return {Object}
53179 */
53180 function getElementsVisibilityRecursive(elements) {
53181
53182 var result = {};
53183
53184 forEach$2(elements, function(element) {
53185 result[element.id] = element.hidden;
53186
53187 if (element.children) {
53188 result = assign({}, result, getElementsVisibilityRecursive(element.children));
53189 }
53190 });
53191
53192 return result;
53193 }
53194
53195
53196 function setHiddenRecursive(elements, newHidden) {
53197 var result = [];
53198 forEach$2(elements, function(element) {
53199 element.hidden = newHidden;
53200
53201 result = result.concat(element);
53202
53203 if (element.children) {
53204 result = result.concat(setHiddenRecursive(element.children, element.collapsed || newHidden));
53205 }
53206 });
53207
53208 return result;
53209 }
53210
53211 function restoreVisibilityRecursive(elements, lastState) {
53212 var result = [];
53213 forEach$2(elements, function(element) {
53214 element.hidden = lastState[element.id];
53215
53216 result = result.concat(element);
53217
53218 if (element.children) {
53219 result = result.concat(restoreVisibilityRecursive(element.children, lastState));
53220 }
53221 });
53222
53223 return result;
53224 }
53225
53226 /**
53227 * A handler that implements reversible attaching/detaching of shapes.
53228 */
53229 function UpdateAttachmentHandler(modeling) {
53230 this._modeling = modeling;
53231 }
53232
53233 UpdateAttachmentHandler.$inject = [ 'modeling' ];
53234
53235
53236 UpdateAttachmentHandler.prototype.execute = function(context) {
53237 var shape = context.shape,
53238 newHost = context.newHost,
53239 oldHost = shape.host;
53240
53241 // (0) detach from old host
53242 context.oldHost = oldHost;
53243 context.attacherIdx = removeAttacher(oldHost, shape);
53244
53245 // (1) attach to new host
53246 addAttacher(newHost, shape);
53247
53248 // (2) update host
53249 shape.host = newHost;
53250
53251 return shape;
53252 };
53253
53254 UpdateAttachmentHandler.prototype.revert = function(context) {
53255 var shape = context.shape,
53256 newHost = context.newHost,
53257 oldHost = context.oldHost,
53258 attacherIdx = context.attacherIdx;
53259
53260 // (2) update host
53261 shape.host = oldHost;
53262
53263 // (1) attach to new host
53264 removeAttacher(newHost, shape);
53265
53266 // (0) detach from old host
53267 addAttacher(oldHost, shape, attacherIdx);
53268
53269 return shape;
53270 };
53271
53272
53273 function removeAttacher(host, attacher) {
53274
53275 // remove attacher from host
53276 return remove(host && host.attachers, attacher);
53277 }
53278
53279 function addAttacher(host, attacher, idx) {
53280
53281 if (!host) {
53282 return;
53283 }
53284
53285 var attachers = host.attachers;
53286
53287 if (!attachers) {
53288 host.attachers = attachers = [];
53289 }
53290
53291 add(attachers, attacher, idx);
53292 }
53293
53294 function UpdateWaypointsHandler() { }
53295
53296 UpdateWaypointsHandler.prototype.execute = function(context) {
53297
53298 var connection = context.connection,
53299 newWaypoints = context.newWaypoints;
53300
53301 context.oldWaypoints = connection.waypoints;
53302
53303 connection.waypoints = newWaypoints;
53304
53305 return connection;
53306 };
53307
53308 UpdateWaypointsHandler.prototype.revert = function(context) {
53309
53310 var connection = context.connection,
53311 oldWaypoints = context.oldWaypoints;
53312
53313 connection.waypoints = oldWaypoints;
53314
53315 return connection;
53316 };
53317
53318 /**
53319 * The basic modeling entry point.
53320 *
53321 * @param {EventBus} eventBus
53322 * @param {ElementFactory} elementFactory
53323 * @param {CommandStack} commandStack
53324 */
53325 function Modeling$1(eventBus, elementFactory, commandStack) {
53326 this._eventBus = eventBus;
53327 this._elementFactory = elementFactory;
53328 this._commandStack = commandStack;
53329
53330 var self = this;
53331
53332 eventBus.on('diagram.init', function() {
53333
53334 // register modeling handlers
53335 self.registerHandlers(commandStack);
53336 });
53337 }
53338
53339 Modeling$1.$inject = [ 'eventBus', 'elementFactory', 'commandStack' ];
53340
53341
53342 Modeling$1.prototype.getHandlers = function() {
53343 return {
53344 'shape.append': AppendShapeHandler,
53345 'shape.create': CreateShapeHandler,
53346 'shape.delete': DeleteShapeHandler,
53347 'shape.move': MoveShapeHandler,
53348 'shape.resize': ResizeShapeHandler,
53349 'shape.replace': ReplaceShapeHandler,
53350 'shape.toggleCollapse': ToggleShapeCollapseHandler,
53351
53352 'spaceTool': SpaceToolHandler,
53353
53354 'label.create': CreateLabelHandler,
53355
53356 'connection.create': CreateConnectionHandler,
53357 'connection.delete': DeleteConnectionHandler,
53358 'connection.move': MoveConnectionHandler,
53359 'connection.layout': LayoutConnectionHandler,
53360
53361 'connection.updateWaypoints': UpdateWaypointsHandler,
53362
53363 'connection.reconnect': ReconnectConnectionHandler,
53364
53365 'elements.create': CreateElementsHandler,
53366 'elements.move': MoveElementsHandler,
53367 'elements.delete': DeleteElementsHandler,
53368
53369 'elements.distribute': DistributeElements,
53370 'elements.align': AlignElements,
53371
53372 'element.updateAttachment': UpdateAttachmentHandler
53373 };
53374 };
53375
53376 /**
53377 * Register handlers with the command stack
53378 *
53379 * @param {CommandStack} commandStack
53380 */
53381 Modeling$1.prototype.registerHandlers = function(commandStack) {
53382 forEach$2(this.getHandlers(), function(handler, id) {
53383 commandStack.registerHandler(id, handler);
53384 });
53385 };
53386
53387
53388 // modeling helpers //////////////////////
53389
53390 Modeling$1.prototype.moveShape = function(shape, delta, newParent, newParentIndex, hints) {
53391
53392 if (typeof newParentIndex === 'object') {
53393 hints = newParentIndex;
53394 newParentIndex = null;
53395 }
53396
53397 var context = {
53398 shape: shape,
53399 delta: delta,
53400 newParent: newParent,
53401 newParentIndex: newParentIndex,
53402 hints: hints || {}
53403 };
53404
53405 this._commandStack.execute('shape.move', context);
53406 };
53407
53408
53409 /**
53410 * Update the attachment of the given shape.
53411 *
53412 * @param {djs.mode.Base} shape
53413 * @param {djs.model.Base} [newHost]
53414 */
53415 Modeling$1.prototype.updateAttachment = function(shape, newHost) {
53416 var context = {
53417 shape: shape,
53418 newHost: newHost
53419 };
53420
53421 this._commandStack.execute('element.updateAttachment', context);
53422 };
53423
53424
53425 /**
53426 * Move a number of shapes to a new target, either setting it as
53427 * the new parent or attaching it.
53428 *
53429 * @param {Array<djs.mode.Base>} shapes
53430 * @param {Point} delta
53431 * @param {djs.model.Base} [target]
53432 * @param {Object} [hints]
53433 * @param {boolean} [hints.attach=false]
53434 */
53435 Modeling$1.prototype.moveElements = function(shapes, delta, target, hints) {
53436
53437 hints = hints || {};
53438
53439 var attach = hints.attach;
53440
53441 var newParent = target,
53442 newHost;
53443
53444 if (attach === true) {
53445 newHost = target;
53446 newParent = target.parent;
53447 } else
53448
53449 if (attach === false) {
53450 newHost = null;
53451 }
53452
53453 var context = {
53454 shapes: shapes,
53455 delta: delta,
53456 newParent: newParent,
53457 newHost: newHost,
53458 hints: hints
53459 };
53460
53461 this._commandStack.execute('elements.move', context);
53462 };
53463
53464
53465 Modeling$1.prototype.moveConnection = function(connection, delta, newParent, newParentIndex, hints) {
53466
53467 if (typeof newParentIndex === 'object') {
53468 hints = newParentIndex;
53469 newParentIndex = undefined;
53470 }
53471
53472 var context = {
53473 connection: connection,
53474 delta: delta,
53475 newParent: newParent,
53476 newParentIndex: newParentIndex,
53477 hints: hints || {}
53478 };
53479
53480 this._commandStack.execute('connection.move', context);
53481 };
53482
53483
53484 Modeling$1.prototype.layoutConnection = function(connection, hints) {
53485 var context = {
53486 connection: connection,
53487 hints: hints || {}
53488 };
53489
53490 this._commandStack.execute('connection.layout', context);
53491 };
53492
53493
53494 /**
53495 * Create connection.
53496 *
53497 * @param {djs.model.Base} source
53498 * @param {djs.model.Base} target
53499 * @param {number} [parentIndex]
53500 * @param {Object|djs.model.Connection} connection
53501 * @param {djs.model.Base} parent
53502 * @param {Object} hints
53503 *
53504 * @return {djs.model.Connection} the created connection.
53505 */
53506 Modeling$1.prototype.createConnection = function(source, target, parentIndex, connection, parent, hints) {
53507
53508 if (typeof parentIndex === 'object') {
53509 hints = parent;
53510 parent = connection;
53511 connection = parentIndex;
53512 parentIndex = undefined;
53513 }
53514
53515 connection = this._create('connection', connection);
53516
53517 var context = {
53518 source: source,
53519 target: target,
53520 parent: parent,
53521 parentIndex: parentIndex,
53522 connection: connection,
53523 hints: hints
53524 };
53525
53526 this._commandStack.execute('connection.create', context);
53527
53528 return context.connection;
53529 };
53530
53531
53532 /**
53533 * Create a shape at the specified position.
53534 *
53535 * @param {djs.model.Shape|Object} shape
53536 * @param {Point} position
53537 * @param {djs.model.Shape|djs.model.Root} target
53538 * @param {number} [parentIndex] position in parents children list
53539 * @param {Object} [hints]
53540 * @param {boolean} [hints.attach] whether to attach to target or become a child
53541 *
53542 * @return {djs.model.Shape} the created shape
53543 */
53544 Modeling$1.prototype.createShape = function(shape, position, target, parentIndex, hints) {
53545
53546 if (typeof parentIndex !== 'number') {
53547 hints = parentIndex;
53548 parentIndex = undefined;
53549 }
53550
53551 hints = hints || {};
53552
53553 var attach = hints.attach,
53554 parent,
53555 host;
53556
53557 shape = this._create('shape', shape);
53558
53559 if (attach) {
53560 parent = target.parent;
53561 host = target;
53562 } else {
53563 parent = target;
53564 }
53565
53566 var context = {
53567 position: position,
53568 shape: shape,
53569 parent: parent,
53570 parentIndex: parentIndex,
53571 host: host,
53572 hints: hints
53573 };
53574
53575 this._commandStack.execute('shape.create', context);
53576
53577 return context.shape;
53578 };
53579
53580
53581 Modeling$1.prototype.createElements = function(elements, position, parent, parentIndex, hints) {
53582 if (!isArray$4(elements)) {
53583 elements = [ elements ];
53584 }
53585
53586 if (typeof parentIndex !== 'number') {
53587 hints = parentIndex;
53588 parentIndex = undefined;
53589 }
53590
53591 hints = hints || {};
53592
53593 var context = {
53594 position: position,
53595 elements: elements,
53596 parent: parent,
53597 parentIndex: parentIndex,
53598 hints: hints
53599 };
53600
53601 this._commandStack.execute('elements.create', context);
53602
53603 return context.elements;
53604 };
53605
53606
53607 Modeling$1.prototype.createLabel = function(labelTarget, position, label, parent) {
53608
53609 label = this._create('label', label);
53610
53611 var context = {
53612 labelTarget: labelTarget,
53613 position: position,
53614 parent: parent || labelTarget.parent,
53615 shape: label
53616 };
53617
53618 this._commandStack.execute('label.create', context);
53619
53620 return context.shape;
53621 };
53622
53623
53624 /**
53625 * Append shape to given source, drawing a connection
53626 * between source and the newly created shape.
53627 *
53628 * @param {djs.model.Shape} source
53629 * @param {djs.model.Shape|Object} shape
53630 * @param {Point} position
53631 * @param {djs.model.Shape} target
53632 * @param {Object} [hints]
53633 * @param {boolean} [hints.attach]
53634 * @param {djs.model.Connection|Object} [hints.connection]
53635 * @param {djs.model.Base} [hints.connectionParent]
53636 *
53637 * @return {djs.model.Shape} the newly created shape
53638 */
53639 Modeling$1.prototype.appendShape = function(source, shape, position, target, hints) {
53640
53641 hints = hints || {};
53642
53643 shape = this._create('shape', shape);
53644
53645 var context = {
53646 source: source,
53647 position: position,
53648 target: target,
53649 shape: shape,
53650 connection: hints.connection,
53651 connectionParent: hints.connectionParent,
53652 hints: hints
53653 };
53654
53655 this._commandStack.execute('shape.append', context);
53656
53657 return context.shape;
53658 };
53659
53660
53661 Modeling$1.prototype.removeElements = function(elements) {
53662 var context = {
53663 elements: elements
53664 };
53665
53666 this._commandStack.execute('elements.delete', context);
53667 };
53668
53669
53670 Modeling$1.prototype.distributeElements = function(groups, axis, dimension) {
53671 var context = {
53672 groups: groups,
53673 axis: axis,
53674 dimension: dimension
53675 };
53676
53677 this._commandStack.execute('elements.distribute', context);
53678 };
53679
53680
53681 Modeling$1.prototype.removeShape = function(shape, hints) {
53682 var context = {
53683 shape: shape,
53684 hints: hints || {}
53685 };
53686
53687 this._commandStack.execute('shape.delete', context);
53688 };
53689
53690
53691 Modeling$1.prototype.removeConnection = function(connection, hints) {
53692 var context = {
53693 connection: connection,
53694 hints: hints || {}
53695 };
53696
53697 this._commandStack.execute('connection.delete', context);
53698 };
53699
53700 Modeling$1.prototype.replaceShape = function(oldShape, newShape, hints) {
53701 var context = {
53702 oldShape: oldShape,
53703 newData: newShape,
53704 hints: hints || {}
53705 };
53706
53707 this._commandStack.execute('shape.replace', context);
53708
53709 return context.newShape;
53710 };
53711
53712 Modeling$1.prototype.alignElements = function(elements, alignment) {
53713 var context = {
53714 elements: elements,
53715 alignment: alignment
53716 };
53717
53718 this._commandStack.execute('elements.align', context);
53719 };
53720
53721 Modeling$1.prototype.resizeShape = function(shape, newBounds, minBounds, hints) {
53722 var context = {
53723 shape: shape,
53724 newBounds: newBounds,
53725 minBounds: minBounds,
53726 hints: hints
53727 };
53728
53729 this._commandStack.execute('shape.resize', context);
53730 };
53731
53732 Modeling$1.prototype.createSpace = function(movingShapes, resizingShapes, delta, direction, start) {
53733 var context = {
53734 delta: delta,
53735 direction: direction,
53736 movingShapes: movingShapes,
53737 resizingShapes: resizingShapes,
53738 start: start
53739 };
53740
53741 this._commandStack.execute('spaceTool', context);
53742 };
53743
53744 Modeling$1.prototype.updateWaypoints = function(connection, newWaypoints, hints) {
53745 var context = {
53746 connection: connection,
53747 newWaypoints: newWaypoints,
53748 hints: hints || {}
53749 };
53750
53751 this._commandStack.execute('connection.updateWaypoints', context);
53752 };
53753
53754 Modeling$1.prototype.reconnect = function(connection, source, target, dockingOrPoints, hints) {
53755 var context = {
53756 connection: connection,
53757 newSource: source,
53758 newTarget: target,
53759 dockingOrPoints: dockingOrPoints,
53760 hints: hints || {}
53761 };
53762
53763 this._commandStack.execute('connection.reconnect', context);
53764 };
53765
53766 Modeling$1.prototype.reconnectStart = function(connection, newSource, dockingOrPoints, hints) {
53767 if (!hints) {
53768 hints = {};
53769 }
53770
53771 this.reconnect(connection, newSource, connection.target, dockingOrPoints, assign(hints, {
53772 docking: 'source'
53773 }));
53774 };
53775
53776 Modeling$1.prototype.reconnectEnd = function(connection, newTarget, dockingOrPoints, hints) {
53777 if (!hints) {
53778 hints = {};
53779 }
53780
53781 this.reconnect(connection, connection.source, newTarget, dockingOrPoints, assign(hints, {
53782 docking: 'target'
53783 }));
53784 };
53785
53786 Modeling$1.prototype.connect = function(source, target, attrs, hints) {
53787 return this.createConnection(source, target, attrs || {}, source.parent, hints);
53788 };
53789
53790 Modeling$1.prototype._create = function(type, attrs) {
53791 if (attrs instanceof Base$1) {
53792 return attrs;
53793 } else {
53794 return this._elementFactory.create(type, attrs);
53795 }
53796 };
53797
53798 Modeling$1.prototype.toggleCollapse = function(shape, hints) {
53799 var context = {
53800 shape: shape,
53801 hints: hints || {}
53802 };
53803
53804 this._commandStack.execute('shape.toggleCollapse', context);
53805 };
53806
53807 function UpdateModdlePropertiesHandler(elementRegistry) {
53808 this._elementRegistry = elementRegistry;
53809 }
53810
53811 UpdateModdlePropertiesHandler.$inject = ['elementRegistry'];
53812
53813 UpdateModdlePropertiesHandler.prototype.execute = function(context) {
53814
53815 var element = context.element,
53816 moddleElement = context.moddleElement,
53817 properties = context.properties;
53818
53819 if (!moddleElement) {
53820 throw new Error('<moddleElement> required');
53821 }
53822
53823 var changed = context.changed || this.getVisualReferences(moddleElement).concat(element);
53824 var oldProperties = context.oldProperties || getModdleProperties(moddleElement, keys(properties));
53825
53826 setModdleProperties(moddleElement, properties);
53827
53828 context.oldProperties = oldProperties;
53829 context.changed = changed;
53830
53831 return changed;
53832 };
53833
53834 UpdateModdlePropertiesHandler.prototype.revert = function(context) {
53835 var oldProperties = context.oldProperties,
53836 moddleElement = context.moddleElement,
53837 changed = context.changed;
53838
53839 setModdleProperties(moddleElement, oldProperties);
53840
53841 return changed;
53842 };
53843
53844 /**
53845 * Return visual references of given moddle element within the diagram.
53846 *
53847 * @param {ModdleElement} moddleElement
53848 *
53849 * @return {Array<djs.model.Element>}
53850 */
53851 UpdateModdlePropertiesHandler.prototype.getVisualReferences = function(moddleElement) {
53852
53853 var elementRegistry = this._elementRegistry;
53854
53855 if (is$1(moddleElement, 'bpmn:DataObject')) {
53856 return getAllDataObjectReferences(moddleElement, elementRegistry);
53857 }
53858
53859 return [];
53860 };
53861
53862
53863 // helpers /////////////////
53864
53865 function getModdleProperties(moddleElement, propertyNames) {
53866 return reduce(propertyNames, function(result, key) {
53867 result[key] = moddleElement.get(key);
53868 return result;
53869 }, {});
53870 }
53871
53872 function setModdleProperties(moddleElement, properties) {
53873 forEach$2(properties, function(value, key) {
53874 moddleElement.set(key, value);
53875 });
53876 }
53877
53878 function getAllDataObjectReferences(dataObject, elementRegistry) {
53879 return elementRegistry.filter(function(element) {
53880 return (
53881 is$1(element, 'bpmn:DataObjectReference') &&
53882 getBusinessObject(element).dataObjectRef === dataObject
53883 );
53884 });
53885 }
53886
53887 var DEFAULT_FLOW = 'default',
53888 ID = 'id',
53889 DI = 'di';
53890
53891 var NULL_DIMENSIONS$1 = {
53892 width: 0,
53893 height: 0
53894 };
53895
53896 /**
53897 * A handler that implements a BPMN 2.0 property update.
53898 *
53899 * This should be used to set simple properties on elements with
53900 * an underlying BPMN business object.
53901 *
53902 * Use respective diagram-js provided handlers if you would
53903 * like to perform automated modeling.
53904 */
53905 function UpdatePropertiesHandler(
53906 elementRegistry, moddle, translate,
53907 modeling, textRenderer) {
53908
53909 this._elementRegistry = elementRegistry;
53910 this._moddle = moddle;
53911 this._translate = translate;
53912 this._modeling = modeling;
53913 this._textRenderer = textRenderer;
53914 }
53915
53916 UpdatePropertiesHandler.$inject = [
53917 'elementRegistry',
53918 'moddle',
53919 'translate',
53920 'modeling',
53921 'textRenderer'
53922 ];
53923
53924
53925 // api //////////////////////
53926
53927 /**
53928 * Updates a BPMN element with a list of new properties
53929 *
53930 * @param {Object} context
53931 * @param {djs.model.Base} context.element the element to update
53932 * @param {Object} context.properties a list of properties to set on the element's
53933 * businessObject (the BPMN model element)
53934 *
53935 * @return {Array<djs.model.Base>} the updated element
53936 */
53937 UpdatePropertiesHandler.prototype.execute = function(context) {
53938
53939 var element = context.element,
53940 changed = [ element ],
53941 translate = this._translate;
53942
53943 if (!element) {
53944 throw new Error(translate('element required'));
53945 }
53946
53947 var elementRegistry = this._elementRegistry,
53948 ids = this._moddle.ids;
53949
53950 var businessObject = element.businessObject,
53951 properties = unwrapBusinessObjects(context.properties),
53952 oldProperties = context.oldProperties || getProperties(element, properties);
53953
53954 if (isIdChange(properties, businessObject)) {
53955 ids.unclaim(businessObject[ID]);
53956
53957 elementRegistry.updateId(element, properties[ID]);
53958
53959 ids.claim(properties[ID], businessObject);
53960 }
53961
53962 // correctly indicate visual changes on default flow updates
53963 if (DEFAULT_FLOW in properties) {
53964
53965 if (properties[DEFAULT_FLOW]) {
53966 changed.push(elementRegistry.get(properties[DEFAULT_FLOW].id));
53967 }
53968
53969 if (businessObject[DEFAULT_FLOW]) {
53970 changed.push(elementRegistry.get(businessObject[DEFAULT_FLOW].id));
53971 }
53972 }
53973
53974 // update properties
53975 setProperties(element, properties);
53976
53977 // store old values
53978 context.oldProperties = oldProperties;
53979 context.changed = changed;
53980
53981 // indicate changed on objects affected by the update
53982 return changed;
53983 };
53984
53985
53986 UpdatePropertiesHandler.prototype.postExecute = function(context) {
53987 var element = context.element,
53988 label = element.label;
53989
53990 var text = label && getBusinessObject(label).name;
53991
53992 if (!text) {
53993 return;
53994 }
53995
53996 // get layouted text bounds and resize external
53997 // external label accordingly
53998 var newLabelBounds = this._textRenderer.getExternalLabelBounds(label, text);
53999
54000 this._modeling.resizeShape(label, newLabelBounds, NULL_DIMENSIONS$1);
54001 };
54002
54003 /**
54004 * Reverts the update on a BPMN elements properties.
54005 *
54006 * @param {Object} context
54007 *
54008 * @return {djs.model.Base} the updated element
54009 */
54010 UpdatePropertiesHandler.prototype.revert = function(context) {
54011
54012 var element = context.element,
54013 properties = context.properties,
54014 oldProperties = context.oldProperties,
54015 businessObject = element.businessObject,
54016 elementRegistry = this._elementRegistry,
54017 ids = this._moddle.ids;
54018
54019 // update properties
54020 setProperties(element, oldProperties);
54021
54022 if (isIdChange(properties, businessObject)) {
54023 ids.unclaim(properties[ID]);
54024
54025 elementRegistry.updateId(element, oldProperties[ID]);
54026
54027 ids.claim(oldProperties[ID], businessObject);
54028 }
54029
54030 return context.changed;
54031 };
54032
54033
54034 function isIdChange(properties, businessObject) {
54035 return ID in properties && properties[ID] !== businessObject[ID];
54036 }
54037
54038
54039 function getProperties(element, properties) {
54040 var propertyNames = keys(properties),
54041 businessObject = element.businessObject,
54042 di = getDi(element);
54043
54044 return reduce(propertyNames, function(result, key) {
54045
54046 // handle DI separately
54047 if (key !== DI) {
54048 result[key] = businessObject.get(key);
54049
54050 } else {
54051 result[key] = getDiProperties(di, keys(properties.di));
54052 }
54053
54054 return result;
54055 }, {});
54056 }
54057
54058
54059 function getDiProperties(di, propertyNames) {
54060 return reduce(propertyNames, function(result, key) {
54061 result[key] = di && di.get(key);
54062
54063 return result;
54064 }, {});
54065 }
54066
54067
54068 function setProperties(element, properties) {
54069 var businessObject = element.businessObject,
54070 di = getDi(element);
54071
54072 forEach$2(properties, function(value, key) {
54073
54074 if (key !== DI) {
54075 businessObject.set(key, value);
54076 } else {
54077
54078 // only update, if di exists
54079 if (di) {
54080 setDiProperties(di, value);
54081 }
54082 }
54083 });
54084 }
54085
54086
54087 function setDiProperties(di, properties) {
54088 forEach$2(properties, function(value, key) {
54089 di.set(key, value);
54090 });
54091 }
54092
54093
54094 var referencePropertyNames = [ 'default' ];
54095
54096 /**
54097 * Make sure we unwrap the actual business object
54098 * behind diagram element that may have been
54099 * passed as arguments.
54100 *
54101 * @param {Object} properties
54102 *
54103 * @return {Object} unwrappedProps
54104 */
54105 function unwrapBusinessObjects(properties) {
54106
54107 var unwrappedProps = assign({}, properties);
54108
54109 referencePropertyNames.forEach(function(name) {
54110 if (name in properties) {
54111 unwrappedProps[name] = getBusinessObject(unwrappedProps[name]);
54112 }
54113 });
54114
54115 return unwrappedProps;
54116 }
54117
54118 function UpdateCanvasRootHandler(canvas, modeling) {
54119 this._canvas = canvas;
54120 this._modeling = modeling;
54121 }
54122
54123 UpdateCanvasRootHandler.$inject = [
54124 'canvas',
54125 'modeling'
54126 ];
54127
54128
54129 UpdateCanvasRootHandler.prototype.execute = function(context) {
54130
54131 var canvas = this._canvas;
54132
54133 var newRoot = context.newRoot,
54134 newRootBusinessObject = newRoot.businessObject,
54135 oldRoot = canvas.getRootElement(),
54136 oldRootBusinessObject = oldRoot.businessObject,
54137 bpmnDefinitions = oldRootBusinessObject.$parent,
54138 diPlane = getDi(oldRoot);
54139
54140 // (1) replace process old <> new root
54141 canvas.setRootElement(newRoot);
54142 canvas.removeRootElement(oldRoot);
54143
54144 // (2) update root elements
54145 add(bpmnDefinitions.rootElements, newRootBusinessObject);
54146 newRootBusinessObject.$parent = bpmnDefinitions;
54147
54148 remove(bpmnDefinitions.rootElements, oldRootBusinessObject);
54149 oldRootBusinessObject.$parent = null;
54150
54151 // (3) wire di
54152 oldRoot.di = null;
54153
54154 diPlane.bpmnElement = newRootBusinessObject;
54155 newRoot.di = diPlane;
54156
54157 context.oldRoot = oldRoot;
54158
54159 // TODO(nikku): return changed elements?
54160 // return [ newRoot, oldRoot ];
54161 };
54162
54163
54164 UpdateCanvasRootHandler.prototype.revert = function(context) {
54165
54166 var canvas = this._canvas;
54167
54168 var newRoot = context.newRoot,
54169 newRootBusinessObject = newRoot.businessObject,
54170 oldRoot = context.oldRoot,
54171 oldRootBusinessObject = oldRoot.businessObject,
54172 bpmnDefinitions = newRootBusinessObject.$parent,
54173 diPlane = getDi(newRoot);
54174
54175 // (1) replace process old <> new root
54176 canvas.setRootElement(oldRoot);
54177 canvas.removeRootElement(newRoot);
54178
54179 // (2) update root elements
54180 remove(bpmnDefinitions.rootElements, newRootBusinessObject);
54181 newRootBusinessObject.$parent = null;
54182
54183 add(bpmnDefinitions.rootElements, oldRootBusinessObject);
54184 oldRootBusinessObject.$parent = bpmnDefinitions;
54185
54186 // (3) wire di
54187 newRoot.di = null;
54188
54189 diPlane.bpmnElement = oldRootBusinessObject;
54190 oldRoot.di = diPlane;
54191
54192 // TODO(nikku): return changed elements?
54193 // return [ newRoot, oldRoot ];
54194 };
54195
54196 /**
54197 * A handler that allows us to add a new lane
54198 * above or below an existing one.
54199 *
54200 * @param {Modeling} modeling
54201 * @param {SpaceTool} spaceTool
54202 */
54203 function AddLaneHandler(modeling, spaceTool) {
54204 this._modeling = modeling;
54205 this._spaceTool = spaceTool;
54206 }
54207
54208 AddLaneHandler.$inject = [
54209 'modeling',
54210 'spaceTool'
54211 ];
54212
54213
54214 AddLaneHandler.prototype.preExecute = function(context) {
54215
54216 var spaceTool = this._spaceTool,
54217 modeling = this._modeling;
54218
54219 var shape = context.shape,
54220 location = context.location;
54221
54222 var lanesRoot = getLanesRoot(shape);
54223
54224 var isRoot = lanesRoot === shape,
54225 laneParent = isRoot ? shape : shape.parent;
54226
54227 var existingChildLanes = getChildLanes(laneParent);
54228
54229 // (0) add a lane if we currently got none and are adding to root
54230 if (!existingChildLanes.length) {
54231 modeling.createShape({ type: 'bpmn:Lane' }, {
54232 x: shape.x + LANE_INDENTATION,
54233 y: shape.y,
54234 width: shape.width - LANE_INDENTATION,
54235 height: shape.height
54236 }, laneParent);
54237 }
54238
54239 // (1) collect affected elements to create necessary space
54240 var allAffected = [];
54241
54242 eachElement(lanesRoot, function(element) {
54243 allAffected.push(element);
54244
54245 // handle element labels in the diagram root
54246 if (element.label) {
54247 allAffected.push(element.label);
54248 }
54249
54250 if (element === shape) {
54251 return [];
54252 }
54253
54254 return filter(element.children, function(c) {
54255 return c !== shape;
54256 });
54257 });
54258
54259 var offset = location === 'top' ? -120 : 120,
54260 lanePosition = location === 'top' ? shape.y : shape.y + shape.height,
54261 spacePos = lanePosition + (location === 'top' ? 10 : -10),
54262 direction = location === 'top' ? 'n' : 's';
54263
54264 var adjustments = spaceTool.calculateAdjustments(allAffected, 'y', offset, spacePos);
54265
54266 spaceTool.makeSpace(
54267 adjustments.movingShapes,
54268 adjustments.resizingShapes,
54269 { x: 0, y: offset },
54270 direction,
54271 spacePos
54272 );
54273
54274 // (2) create new lane at open space
54275 context.newLane = modeling.createShape({ type: 'bpmn:Lane' }, {
54276 x: shape.x + (isRoot ? LANE_INDENTATION : 0),
54277 y: lanePosition - (location === 'top' ? 120 : 0),
54278 width: shape.width - (isRoot ? LANE_INDENTATION : 0),
54279 height: 120
54280 }, laneParent);
54281 };
54282
54283 /**
54284 * A handler that splits a lane into a number of sub-lanes,
54285 * creating new sub lanes, if necessary.
54286 *
54287 * @param {Modeling} modeling
54288 */
54289 function SplitLaneHandler(modeling, translate) {
54290 this._modeling = modeling;
54291 this._translate = translate;
54292 }
54293
54294 SplitLaneHandler.$inject = [
54295 'modeling',
54296 'translate'
54297 ];
54298
54299
54300 SplitLaneHandler.prototype.preExecute = function(context) {
54301
54302 var modeling = this._modeling,
54303 translate = this._translate;
54304
54305 var shape = context.shape,
54306 newLanesCount = context.count;
54307
54308 var childLanes = getChildLanes(shape),
54309 existingLanesCount = childLanes.length;
54310
54311 if (existingLanesCount > newLanesCount) {
54312 throw new Error(translate('more than {count} child lanes', { count: newLanesCount }));
54313 }
54314
54315 var newLanesHeight = Math.round(shape.height / newLanesCount);
54316
54317 // Iterate from top to bottom in child lane order,
54318 // resizing existing lanes and creating new ones
54319 // so that they split the parent proportionally.
54320 //
54321 // Due to rounding related errors, the bottom lane
54322 // needs to take up all the remaining space.
54323 var laneY,
54324 laneHeight,
54325 laneBounds,
54326 newLaneAttrs,
54327 idx;
54328
54329 for (idx = 0; idx < newLanesCount; idx++) {
54330
54331 laneY = shape.y + idx * newLanesHeight;
54332
54333 // if bottom lane
54334 if (idx === newLanesCount - 1) {
54335 laneHeight = shape.height - (newLanesHeight * idx);
54336 } else {
54337 laneHeight = newLanesHeight;
54338 }
54339
54340 laneBounds = {
54341 x: shape.x + LANE_INDENTATION,
54342 y: laneY,
54343 width: shape.width - LANE_INDENTATION,
54344 height: laneHeight
54345 };
54346
54347 if (idx < existingLanesCount) {
54348
54349 // resize existing lane
54350 modeling.resizeShape(childLanes[idx], laneBounds);
54351 } else {
54352
54353 // create a new lane at position
54354 newLaneAttrs = {
54355 type: 'bpmn:Lane'
54356 };
54357
54358 modeling.createShape(newLaneAttrs, laneBounds, shape);
54359 }
54360 }
54361 };
54362
54363 /**
54364 * A handler that resizes a lane.
54365 *
54366 * @param {Modeling} modeling
54367 */
54368 function ResizeLaneHandler(modeling, spaceTool) {
54369 this._modeling = modeling;
54370 this._spaceTool = spaceTool;
54371 }
54372
54373 ResizeLaneHandler.$inject = [
54374 'modeling',
54375 'spaceTool'
54376 ];
54377
54378
54379 ResizeLaneHandler.prototype.preExecute = function(context) {
54380
54381 var shape = context.shape,
54382 newBounds = context.newBounds,
54383 balanced = context.balanced;
54384
54385 if (balanced !== false) {
54386 this.resizeBalanced(shape, newBounds);
54387 } else {
54388 this.resizeSpace(shape, newBounds);
54389 }
54390 };
54391
54392
54393 /**
54394 * Resize balanced, adjusting next / previous lane sizes.
54395 *
54396 * @param {djs.model.Shape} shape
54397 * @param {Bounds} newBounds
54398 */
54399 ResizeLaneHandler.prototype.resizeBalanced = function(shape, newBounds) {
54400
54401 var modeling = this._modeling;
54402
54403 var resizeNeeded = computeLanesResize(shape, newBounds);
54404
54405 // resize the lane
54406 modeling.resizeShape(shape, newBounds);
54407
54408 // resize other lanes as needed
54409 resizeNeeded.forEach(function(r) {
54410 modeling.resizeShape(r.shape, r.newBounds);
54411 });
54412 };
54413
54414
54415 /**
54416 * Resize, making actual space and moving below / above elements.
54417 *
54418 * @param {djs.model.Shape} shape
54419 * @param {Bounds} newBounds
54420 */
54421 ResizeLaneHandler.prototype.resizeSpace = function(shape, newBounds) {
54422 var spaceTool = this._spaceTool;
54423
54424 var shapeTrbl = asTRBL(shape),
54425 newTrbl = asTRBL(newBounds);
54426
54427 var trblDiff = substractTRBL(newTrbl, shapeTrbl);
54428
54429 var lanesRoot = getLanesRoot(shape);
54430
54431 var allAffected = [],
54432 allLanes = [];
54433
54434 eachElement(lanesRoot, function(element) {
54435 allAffected.push(element);
54436
54437 if (is$1(element, 'bpmn:Lane') || is$1(element, 'bpmn:Participant')) {
54438 allLanes.push(element);
54439 }
54440
54441 return element.children;
54442 });
54443
54444 var change,
54445 spacePos,
54446 direction,
54447 offset,
54448 adjustments;
54449
54450 if (trblDiff.bottom || trblDiff.top) {
54451
54452 change = trblDiff.bottom || trblDiff.top;
54453 spacePos = shape.y + (trblDiff.bottom ? shape.height : 0) + (trblDiff.bottom ? -10 : 10);
54454 direction = trblDiff.bottom ? 's' : 'n';
54455
54456 offset = trblDiff.top > 0 || trblDiff.bottom < 0 ? -change : change;
54457
54458 adjustments = spaceTool.calculateAdjustments(allAffected, 'y', offset, spacePos);
54459
54460 spaceTool.makeSpace(adjustments.movingShapes, adjustments.resizingShapes, { x: 0, y: change }, direction);
54461 }
54462
54463
54464 if (trblDiff.left || trblDiff.right) {
54465
54466 change = trblDiff.right || trblDiff.left;
54467 spacePos = shape.x + (trblDiff.right ? shape.width : 0) + (trblDiff.right ? -10 : 100);
54468 direction = trblDiff.right ? 'e' : 'w';
54469
54470 offset = trblDiff.left > 0 || trblDiff.right < 0 ? -change : change;
54471
54472 adjustments = spaceTool.calculateAdjustments(allLanes, 'x', offset, spacePos);
54473
54474 spaceTool.makeSpace(adjustments.movingShapes, adjustments.resizingShapes, { x: change, y: 0 }, direction);
54475 }
54476 };
54477
54478 var FLOW_NODE_REFS_ATTR = 'flowNodeRef',
54479 LANES_ATTR = 'lanes';
54480
54481
54482 /**
54483 * A handler that updates lane refs on changed elements
54484 */
54485 function UpdateFlowNodeRefsHandler(elementRegistry) {
54486 this._elementRegistry = elementRegistry;
54487 }
54488
54489 UpdateFlowNodeRefsHandler.$inject = [
54490 'elementRegistry'
54491 ];
54492
54493
54494 UpdateFlowNodeRefsHandler.prototype.computeUpdates = function(flowNodeShapes, laneShapes) {
54495
54496 var handledNodes = [];
54497
54498 var updates = [];
54499
54500 var participantCache = {};
54501
54502 var allFlowNodeShapes = [];
54503
54504 function isInLaneShape(element, laneShape) {
54505
54506 var laneTrbl = asTRBL(laneShape);
54507
54508 var elementMid = {
54509 x: element.x + element.width / 2,
54510 y: element.y + element.height / 2
54511 };
54512
54513 return elementMid.x > laneTrbl.left &&
54514 elementMid.x < laneTrbl.right &&
54515 elementMid.y > laneTrbl.top &&
54516 elementMid.y < laneTrbl.bottom;
54517 }
54518
54519 function addFlowNodeShape(flowNodeShape) {
54520 if (handledNodes.indexOf(flowNodeShape) === -1) {
54521 allFlowNodeShapes.push(flowNodeShape);
54522 handledNodes.push(flowNodeShape);
54523 }
54524 }
54525
54526 function getAllLaneShapes(flowNodeShape) {
54527
54528 var root = getLanesRoot(flowNodeShape);
54529
54530 if (!participantCache[root.id]) {
54531 participantCache[root.id] = collectLanes(root);
54532 }
54533
54534 return participantCache[root.id];
54535 }
54536
54537 function getNewLanes(flowNodeShape) {
54538 if (!flowNodeShape.parent) {
54539 return [];
54540 }
54541
54542 var allLaneShapes = getAllLaneShapes(flowNodeShape);
54543
54544 return allLaneShapes.filter(function(l) {
54545 return isInLaneShape(flowNodeShape, l);
54546 }).map(function(shape) {
54547 return shape.businessObject;
54548 });
54549 }
54550
54551 laneShapes.forEach(function(laneShape) {
54552 var root = getLanesRoot(laneShape);
54553
54554 if (!root || handledNodes.indexOf(root) !== -1) {
54555 return;
54556 }
54557
54558 var children = root.children.filter(function(c) {
54559 return is$1(c, 'bpmn:FlowNode');
54560 });
54561
54562 children.forEach(addFlowNodeShape);
54563
54564 handledNodes.push(root);
54565 });
54566
54567 flowNodeShapes.forEach(addFlowNodeShape);
54568
54569
54570 allFlowNodeShapes.forEach(function(flowNodeShape) {
54571
54572 var flowNode = flowNodeShape.businessObject;
54573
54574 var lanes = flowNode.get(LANES_ATTR),
54575 remove = lanes.slice(),
54576 add = getNewLanes(flowNodeShape);
54577
54578 updates.push({ flowNode: flowNode, remove: remove, add: add });
54579 });
54580
54581 laneShapes.forEach(function(laneShape) {
54582
54583 var lane = laneShape.businessObject;
54584
54585 // lane got removed XX-)
54586 if (!laneShape.parent) {
54587 lane.get(FLOW_NODE_REFS_ATTR).forEach(function(flowNode) {
54588 updates.push({ flowNode: flowNode, remove: [ lane ], add: [] });
54589 });
54590 }
54591 });
54592
54593 return updates;
54594 };
54595
54596 UpdateFlowNodeRefsHandler.prototype.execute = function(context) {
54597
54598 var updates = context.updates;
54599
54600 if (!updates) {
54601 updates = context.updates = this.computeUpdates(context.flowNodeShapes, context.laneShapes);
54602 }
54603
54604
54605 updates.forEach(function(update) {
54606
54607 var flowNode = update.flowNode,
54608 lanes = flowNode.get(LANES_ATTR);
54609
54610 // unwire old
54611 update.remove.forEach(function(oldLane) {
54612 remove(lanes, oldLane);
54613 remove(oldLane.get(FLOW_NODE_REFS_ATTR), flowNode);
54614 });
54615
54616 // wire new
54617 update.add.forEach(function(newLane) {
54618 add(lanes, newLane);
54619 add(newLane.get(FLOW_NODE_REFS_ATTR), flowNode);
54620 });
54621 });
54622
54623 // TODO(nikku): return changed elements
54624 // return [ ... ];
54625 };
54626
54627
54628 UpdateFlowNodeRefsHandler.prototype.revert = function(context) {
54629
54630 var updates = context.updates;
54631
54632 updates.forEach(function(update) {
54633
54634 var flowNode = update.flowNode,
54635 lanes = flowNode.get(LANES_ATTR);
54636
54637 // unwire new
54638 update.add.forEach(function(newLane) {
54639 remove(lanes, newLane);
54640 remove(newLane.get(FLOW_NODE_REFS_ATTR), flowNode);
54641 });
54642
54643 // wire old
54644 update.remove.forEach(function(oldLane) {
54645 add(lanes, oldLane);
54646 add(oldLane.get(FLOW_NODE_REFS_ATTR), flowNode);
54647 });
54648 });
54649
54650 // TODO(nikku): return changed elements
54651 // return [ ... ];
54652 };
54653
54654 function IdClaimHandler(moddle) {
54655 this._moddle = moddle;
54656 }
54657
54658 IdClaimHandler.$inject = [ 'moddle' ];
54659
54660
54661 IdClaimHandler.prototype.execute = function(context) {
54662 var ids = this._moddle.ids,
54663 id = context.id,
54664 element = context.element,
54665 claiming = context.claiming;
54666
54667 if (claiming) {
54668 ids.claim(id, element);
54669 } else {
54670 ids.unclaim(id);
54671 }
54672 };
54673
54674 /**
54675 * Command revert implementation.
54676 */
54677 IdClaimHandler.prototype.revert = function(context) {
54678 var ids = this._moddle.ids,
54679 id = context.id,
54680 element = context.element,
54681 claiming = context.claiming;
54682
54683 if (claiming) {
54684 ids.unclaim(id);
54685 } else {
54686 ids.claim(id, element);
54687 }
54688 };
54689
54690 var DEFAULT_COLORS = {
54691 fill: undefined,
54692 stroke: undefined
54693 };
54694
54695
54696 function SetColorHandler(commandStack) {
54697 this._commandStack = commandStack;
54698
54699 this._normalizeColor = function(color) {
54700
54701 // Remove color for falsy values.
54702 if (!color) {
54703 return undefined;
54704 }
54705
54706 if (isString(color)) {
54707 var hexColor = colorToHex(color);
54708
54709 if (hexColor) {
54710 return hexColor;
54711 }
54712 }
54713
54714 throw new Error('invalid color value: ' + color);
54715 };
54716 }
54717
54718 SetColorHandler.$inject = [
54719 'commandStack'
54720 ];
54721
54722
54723 SetColorHandler.prototype.postExecute = function(context) {
54724 var elements = context.elements,
54725 colors = context.colors || DEFAULT_COLORS;
54726
54727 var self = this;
54728
54729 var di = {};
54730
54731 if ('fill' in colors) {
54732 assign(di, {
54733 'background-color': this._normalizeColor(colors.fill) });
54734 }
54735
54736 if ('stroke' in colors) {
54737 assign(di, {
54738 'border-color': this._normalizeColor(colors.stroke) });
54739 }
54740
54741 forEach$2(elements, function(element) {
54742 var assignedDi = isConnection$3(element) ? pick(di, [ 'border-color' ]) : di;
54743
54744 // TODO @barmac: remove once we drop bpmn.io properties
54745 ensureLegacySupport(assignedDi);
54746
54747 if (element.labelTarget) {
54748
54749 // set label colors as bpmndi:BPMNLabel#color
54750 self._commandStack.execute('element.updateModdleProperties', {
54751 element: element,
54752 moddleElement: getDi(element).label,
54753 properties: {
54754 color: di['background-color']
54755 }
54756 });
54757 } else {
54758
54759 // set colors bpmndi:BPMNEdge or bpmndi:BPMNShape
54760 self._commandStack.execute('element.updateProperties', {
54761 element: element,
54762 properties: {
54763 di: assignedDi
54764 }
54765 });
54766 }
54767 });
54768
54769 };
54770
54771 /**
54772 * Convert color from rgb(a)/hsl to hex. Returns `null` for unknown color names and for colors
54773 * with alpha less than 1.0. This depends on `<canvas>` serialization of the `context.fillStyle`.
54774 * Cf. https://html.spec.whatwg.org/multipage/canvas.html#dom-context-2d-fillstyle
54775 *
54776 * @example
54777 * ```js
54778 * var color = 'fuchsia';
54779 * console.log(colorToHex(color));
54780 * // "#ff00ff"
54781 * color = 'rgba(1,2,3,0.4)';
54782 * console.log(colorToHex(color));
54783 * // null
54784 * ```
54785 *
54786 * @param {string} color
54787 * @returns {string|null}
54788 */
54789 function colorToHex(color) {
54790 var context = document.createElement('canvas').getContext('2d');
54791
54792 // (0) Start with transparent to account for browser default values.
54793 context.fillStyle = 'transparent';
54794
54795 // (1) Assign color so that it's serialized.
54796 context.fillStyle = color;
54797
54798 // (2) Return null for non-hex serialization result.
54799 return /^#[0-9a-fA-F]{6}$/.test(context.fillStyle) ? context.fillStyle : null;
54800 }
54801
54802 function isConnection$3(element) {
54803 return !!element.waypoints;
54804 }
54805
54806 /**
54807 * Add legacy properties if required.
54808 * @param {{ 'border-color': string?, 'background-color': string? }} di
54809 */
54810 function ensureLegacySupport(di) {
54811 if ('border-color' in di) {
54812 di.stroke = di['border-color'];
54813 }
54814
54815 if ('background-color' in di) {
54816 di.fill = di['background-color'];
54817 }
54818 }
54819
54820 var NULL_DIMENSIONS = {
54821 width: 0,
54822 height: 0
54823 };
54824
54825
54826 /**
54827 * A handler that updates the text of a BPMN element.
54828 */
54829 function UpdateLabelHandler(modeling, textRenderer, bpmnFactory) {
54830
54831 /**
54832 * Creates an empty `diLabel` attribute for embedded labels.
54833 *
54834 * @param {djs.model.Base} element
54835 * @param {string} text
54836 */
54837 function ensureInternalLabelDi(element, text) {
54838 if (isLabelExternal(element)) {
54839 return;
54840 }
54841
54842 var di = getDi(element);
54843
54844 if (text && !di.label) {
54845 di.label = bpmnFactory.create('bpmndi:BPMNLabel');
54846 }
54847
54848 if (!text && di.label) {
54849 delete di.label;
54850 }
54851 }
54852
54853
54854 /**
54855 * Set the label and return the changed elements.
54856 *
54857 * Element parameter can be label itself or connection (i.e. sequence flow).
54858 *
54859 * @param {djs.model.Base} element
54860 * @param {string} text
54861 */
54862 function setText(element, text) {
54863
54864 // external label if present
54865 var label = element.label || element;
54866
54867 var labelTarget = element.labelTarget || element;
54868
54869 setLabel(label, text);
54870
54871 ensureInternalLabelDi(element, text);
54872
54873 return [ label, labelTarget ];
54874 }
54875
54876 function preExecute(ctx) {
54877 var element = ctx.element,
54878 businessObject = element.businessObject,
54879 newLabel = ctx.newLabel;
54880
54881 if (!isLabel$6(element)
54882 && isLabelExternal(element)
54883 && !hasExternalLabel(element)
54884 && !isEmptyText(newLabel)) {
54885
54886 // create label
54887 var paddingTop = 7;
54888
54889 var labelCenter = getExternalLabelMid(element);
54890
54891 labelCenter = {
54892 x: labelCenter.x,
54893 y: labelCenter.y + paddingTop
54894 };
54895
54896 modeling.createLabel(element, labelCenter, {
54897 id: businessObject.id + '_label',
54898 businessObject: businessObject,
54899 di: element.di
54900 });
54901 }
54902 }
54903
54904 function execute(ctx) {
54905 ctx.oldLabel = getLabel(ctx.element);
54906 return setText(ctx.element, ctx.newLabel);
54907 }
54908
54909 function revert(ctx) {
54910 return setText(ctx.element, ctx.oldLabel);
54911 }
54912
54913 function postExecute(ctx) {
54914 var element = ctx.element,
54915 label = element.label || element,
54916 newLabel = ctx.newLabel,
54917 newBounds = ctx.newBounds,
54918 hints = ctx.hints || {};
54919
54920 // ignore internal labels for elements except text annotations
54921 if (!isLabel$6(label) && !is$1(label, 'bpmn:TextAnnotation')) {
54922 return;
54923 }
54924
54925 if (isLabel$6(label) && isEmptyText(newLabel)) {
54926
54927 if (hints.removeShape !== false) {
54928 modeling.removeShape(label, { unsetLabel: false });
54929 }
54930
54931 return;
54932 }
54933
54934 var text = getLabel(label);
54935
54936 // resize element based on label _or_ pre-defined bounds
54937 if (typeof newBounds === 'undefined') {
54938 newBounds = textRenderer.getExternalLabelBounds(label, text);
54939 }
54940
54941 // setting newBounds to false or _null_ will
54942 // disable the postExecute resize operation
54943 if (newBounds) {
54944 modeling.resizeShape(label, newBounds, NULL_DIMENSIONS);
54945 }
54946 }
54947
54948 // API
54949
54950 this.preExecute = preExecute;
54951 this.execute = execute;
54952 this.revert = revert;
54953 this.postExecute = postExecute;
54954 }
54955
54956 UpdateLabelHandler.$inject = [
54957 'modeling',
54958 'textRenderer',
54959 'bpmnFactory'
54960 ];
54961
54962
54963 // helpers ///////////////////////
54964
54965 function isEmptyText(label) {
54966 return !label || !label.trim();
54967 }
54968
54969 /**
54970 * BPMN 2.0 modeling features activator
54971 *
54972 * @param {EventBus} eventBus
54973 * @param {ElementFactory} elementFactory
54974 * @param {CommandStack} commandStack
54975 * @param {BpmnRules} bpmnRules
54976 */
54977 function Modeling(
54978 eventBus, elementFactory, commandStack,
54979 bpmnRules) {
54980
54981 Modeling$1.call(this, eventBus, elementFactory, commandStack);
54982
54983 this._bpmnRules = bpmnRules;
54984 }
54985
54986 inherits$1(Modeling, Modeling$1);
54987
54988 Modeling.$inject = [
54989 'eventBus',
54990 'elementFactory',
54991 'commandStack',
54992 'bpmnRules'
54993 ];
54994
54995
54996 Modeling.prototype.getHandlers = function() {
54997 var handlers = Modeling$1.prototype.getHandlers.call(this);
54998
54999 handlers['element.updateModdleProperties'] = UpdateModdlePropertiesHandler;
55000 handlers['element.updateProperties'] = UpdatePropertiesHandler;
55001 handlers['canvas.updateRoot'] = UpdateCanvasRootHandler;
55002 handlers['lane.add'] = AddLaneHandler;
55003 handlers['lane.resize'] = ResizeLaneHandler;
55004 handlers['lane.split'] = SplitLaneHandler;
55005 handlers['lane.updateRefs'] = UpdateFlowNodeRefsHandler;
55006 handlers['id.updateClaim'] = IdClaimHandler;
55007 handlers['element.setColor'] = SetColorHandler;
55008 handlers['element.updateLabel'] = UpdateLabelHandler;
55009
55010 return handlers;
55011 };
55012
55013
55014 Modeling.prototype.updateLabel = function(element, newLabel, newBounds, hints) {
55015 this._commandStack.execute('element.updateLabel', {
55016 element: element,
55017 newLabel: newLabel,
55018 newBounds: newBounds,
55019 hints: hints || {}
55020 });
55021 };
55022
55023
55024 Modeling.prototype.connect = function(source, target, attrs, hints) {
55025
55026 var bpmnRules = this._bpmnRules;
55027
55028 if (!attrs) {
55029 attrs = bpmnRules.canConnect(source, target);
55030 }
55031
55032 if (!attrs) {
55033 return;
55034 }
55035
55036 return this.createConnection(source, target, attrs, source.parent, hints);
55037 };
55038
55039
55040 Modeling.prototype.updateModdleProperties = function(element, moddleElement, properties) {
55041 this._commandStack.execute('element.updateModdleProperties', {
55042 element: element,
55043 moddleElement: moddleElement,
55044 properties: properties
55045 });
55046 };
55047
55048 Modeling.prototype.updateProperties = function(element, properties) {
55049 this._commandStack.execute('element.updateProperties', {
55050 element: element,
55051 properties: properties
55052 });
55053 };
55054
55055 Modeling.prototype.resizeLane = function(laneShape, newBounds, balanced) {
55056 this._commandStack.execute('lane.resize', {
55057 shape: laneShape,
55058 newBounds: newBounds,
55059 balanced: balanced
55060 });
55061 };
55062
55063 Modeling.prototype.addLane = function(targetLaneShape, location) {
55064 var context = {
55065 shape: targetLaneShape,
55066 location: location
55067 };
55068
55069 this._commandStack.execute('lane.add', context);
55070
55071 return context.newLane;
55072 };
55073
55074 Modeling.prototype.splitLane = function(targetLane, count) {
55075 this._commandStack.execute('lane.split', {
55076 shape: targetLane,
55077 count: count
55078 });
55079 };
55080
55081 /**
55082 * Transform the current diagram into a collaboration.
55083 *
55084 * @return {djs.model.Root} the new root element
55085 */
55086 Modeling.prototype.makeCollaboration = function() {
55087
55088 var collaborationElement = this._create('root', {
55089 type: 'bpmn:Collaboration'
55090 });
55091
55092 var context = {
55093 newRoot: collaborationElement
55094 };
55095
55096 this._commandStack.execute('canvas.updateRoot', context);
55097
55098 return collaborationElement;
55099 };
55100
55101 Modeling.prototype.updateLaneRefs = function(flowNodeShapes, laneShapes) {
55102
55103 this._commandStack.execute('lane.updateRefs', {
55104 flowNodeShapes: flowNodeShapes,
55105 laneShapes: laneShapes
55106 });
55107 };
55108
55109 /**
55110 * Transform the current diagram into a process.
55111 *
55112 * @return {djs.model.Root} the new root element
55113 */
55114 Modeling.prototype.makeProcess = function() {
55115
55116 var processElement = this._create('root', {
55117 type: 'bpmn:Process'
55118 });
55119
55120 var context = {
55121 newRoot: processElement
55122 };
55123
55124 this._commandStack.execute('canvas.updateRoot', context);
55125 };
55126
55127
55128 Modeling.prototype.claimId = function(id, moddleElement) {
55129 this._commandStack.execute('id.updateClaim', {
55130 id: id,
55131 element: moddleElement,
55132 claiming: true
55133 });
55134 };
55135
55136
55137 Modeling.prototype.unclaimId = function(id, moddleElement) {
55138 this._commandStack.execute('id.updateClaim', {
55139 id: id,
55140 element: moddleElement
55141 });
55142 };
55143
55144 Modeling.prototype.setColor = function(elements, colors) {
55145 if (!elements.length) {
55146 elements = [ elements ];
55147 }
55148
55149 this._commandStack.execute('element.setColor', {
55150 elements: elements,
55151 colors: colors
55152 });
55153 };
55154
55155 /**
55156 * A base connection layouter implementation
55157 * that layouts the connection by directly connecting
55158 * mid(source) + mid(target).
55159 */
55160 function BaseLayouter() {}
55161
55162
55163 /**
55164 * Return the new layouted waypoints for the given connection.
55165 *
55166 * The connection passed is still unchanged; you may figure out about
55167 * the new connection start / end via the layout hints provided.
55168 *
55169 * @param {djs.model.Connection} connection
55170 * @param {Object} [hints]
55171 * @param {Point} [hints.connectionStart]
55172 * @param {Point} [hints.connectionEnd]
55173 * @param {Point} [hints.source]
55174 * @param {Point} [hints.target]
55175 *
55176 * @return {Array<Point>} the layouted connection waypoints
55177 */
55178 BaseLayouter.prototype.layoutConnection = function(connection, hints) {
55179
55180 hints = hints || {};
55181
55182 return [
55183 hints.connectionStart || getMid(hints.source || connection.source),
55184 hints.connectionEnd || getMid(hints.target || connection.target)
55185 ];
55186 };
55187
55188 var MIN_SEGMENT_LENGTH = 20,
55189 POINT_ORIENTATION_PADDING = 5;
55190
55191 var round$1 = Math.round;
55192
55193 var INTERSECTION_THRESHOLD = 20,
55194 ORIENTATION_THRESHOLD = {
55195 'h:h': 20,
55196 'v:v': 20,
55197 'h:v': -10,
55198 'v:h': -10
55199 };
55200
55201 function needsTurn(orientation, startDirection) {
55202 return !{
55203 t: /top/,
55204 r: /right/,
55205 b: /bottom/,
55206 l: /left/,
55207 h: /./,
55208 v: /./
55209 }[startDirection].test(orientation);
55210 }
55211
55212 function canLayoutStraight(direction, targetOrientation) {
55213 return {
55214 t: /top/,
55215 r: /right/,
55216 b: /bottom/,
55217 l: /left/,
55218 h: /left|right/,
55219 v: /top|bottom/
55220 }[direction].test(targetOrientation);
55221 }
55222
55223 function getSegmentBendpoints(a, b, directions) {
55224 var orientation = getOrientation(b, a, POINT_ORIENTATION_PADDING);
55225
55226 var startDirection = directions.split(':')[0];
55227
55228 var xmid = round$1((b.x - a.x) / 2 + a.x),
55229 ymid = round$1((b.y - a.y) / 2 + a.y);
55230
55231 var segmentEnd, segmentDirections;
55232
55233 var layoutStraight = canLayoutStraight(startDirection, orientation),
55234 layoutHorizontal = /h|r|l/.test(startDirection),
55235 layoutTurn = false;
55236
55237 var turnNextDirections = false;
55238
55239 if (layoutStraight) {
55240 segmentEnd = layoutHorizontal ? { x: xmid, y: a.y } : { x: a.x, y: ymid };
55241
55242 segmentDirections = layoutHorizontal ? 'h:h' : 'v:v';
55243 } else {
55244 layoutTurn = needsTurn(orientation, startDirection);
55245
55246 segmentDirections = layoutHorizontal ? 'h:v' : 'v:h';
55247
55248 if (layoutTurn) {
55249
55250 if (layoutHorizontal) {
55251 turnNextDirections = ymid === a.y;
55252
55253 segmentEnd = {
55254 x: a.x + MIN_SEGMENT_LENGTH * (/l/.test(startDirection) ? -1 : 1),
55255 y: turnNextDirections ? ymid + MIN_SEGMENT_LENGTH : ymid
55256 };
55257 } else {
55258 turnNextDirections = xmid === a.x;
55259
55260 segmentEnd = {
55261 x: turnNextDirections ? xmid + MIN_SEGMENT_LENGTH : xmid,
55262 y: a.y + MIN_SEGMENT_LENGTH * (/t/.test(startDirection) ? -1 : 1)
55263 };
55264 }
55265
55266 } else {
55267 segmentEnd = {
55268 x: xmid,
55269 y: ymid
55270 };
55271 }
55272 }
55273
55274 return {
55275 waypoints: getBendpoints(a, segmentEnd, segmentDirections).concat(segmentEnd),
55276 directions: segmentDirections,
55277 turnNextDirections: turnNextDirections
55278 };
55279 }
55280
55281 function getStartSegment(a, b, directions) {
55282 return getSegmentBendpoints(a, b, directions);
55283 }
55284
55285 function getEndSegment(a, b, directions) {
55286 var invertedSegment = getSegmentBendpoints(b, a, invertDirections(directions));
55287
55288 return {
55289 waypoints: invertedSegment.waypoints.slice().reverse(),
55290 directions: invertDirections(invertedSegment.directions),
55291 turnNextDirections: invertedSegment.turnNextDirections
55292 };
55293 }
55294
55295 function getMidSegment(startSegment, endSegment) {
55296
55297 var startDirection = startSegment.directions.split(':')[1],
55298 endDirection = endSegment.directions.split(':')[0];
55299
55300 if (startSegment.turnNextDirections) {
55301 startDirection = startDirection == 'h' ? 'v' : 'h';
55302 }
55303
55304 if (endSegment.turnNextDirections) {
55305 endDirection = endDirection == 'h' ? 'v' : 'h';
55306 }
55307
55308 var directions = startDirection + ':' + endDirection;
55309
55310 var bendpoints = getBendpoints(
55311 startSegment.waypoints[startSegment.waypoints.length - 1],
55312 endSegment.waypoints[0],
55313 directions
55314 );
55315
55316 return {
55317 waypoints: bendpoints,
55318 directions: directions
55319 };
55320 }
55321
55322 function invertDirections(directions) {
55323 return directions.split(':').reverse().join(':');
55324 }
55325
55326 /**
55327 * Handle simple layouts with maximum two bendpoints.
55328 */
55329 function getSimpleBendpoints(a, b, directions) {
55330
55331 var xmid = round$1((b.x - a.x) / 2 + a.x),
55332 ymid = round$1((b.y - a.y) / 2 + a.y);
55333
55334 // one point, right or left from a
55335 if (directions === 'h:v') {
55336 return [ { x: b.x, y: a.y } ];
55337 }
55338
55339 // one point, above or below a
55340 if (directions === 'v:h') {
55341 return [ { x: a.x, y: b.y } ];
55342 }
55343
55344 // vertical segment between a and b
55345 if (directions === 'h:h') {
55346 return [
55347 { x: xmid, y: a.y },
55348 { x: xmid, y: b.y }
55349 ];
55350 }
55351
55352 // horizontal segment between a and b
55353 if (directions === 'v:v') {
55354 return [
55355 { x: a.x, y: ymid },
55356 { x: b.x, y: ymid }
55357 ];
55358 }
55359
55360 throw new Error('invalid directions: can only handle varians of [hv]:[hv]');
55361 }
55362
55363
55364 /**
55365 * Returns the mid points for a manhattan connection between two points.
55366 *
55367 * @example h:h (horizontal:horizontal)
55368 *
55369 * [a]----[x]
55370 * |
55371 * [x]----[b]
55372 *
55373 * @example h:v (horizontal:vertical)
55374 *
55375 * [a]----[x]
55376 * |
55377 * [b]
55378 *
55379 * @example h:r (horizontal:right)
55380 *
55381 * [a]----[x]
55382 * |
55383 * [b]-[x]
55384 *
55385 * @param {Point} a
55386 * @param {Point} b
55387 * @param {string} directions
55388 *
55389 * @return {Array<Point>}
55390 */
55391 function getBendpoints(a, b, directions) {
55392 directions = directions || 'h:h';
55393
55394 if (!isValidDirections(directions)) {
55395 throw new Error(
55396 'unknown directions: <' + directions + '>: ' +
55397 'must be specified as <start>:<end> ' +
55398 'with start/end in { h,v,t,r,b,l }'
55399 );
55400 }
55401
55402 // compute explicit directions, involving trbl dockings
55403 // using a three segmented layouting algorithm
55404 if (isExplicitDirections(directions)) {
55405 var startSegment = getStartSegment(a, b, directions),
55406 endSegment = getEndSegment(a, b, directions),
55407 midSegment = getMidSegment(startSegment, endSegment);
55408
55409 return [].concat(
55410 startSegment.waypoints,
55411 midSegment.waypoints,
55412 endSegment.waypoints
55413 );
55414 }
55415
55416 // handle simple [hv]:[hv] cases that can be easily computed
55417 return getSimpleBendpoints(a, b, directions);
55418 }
55419
55420 /**
55421 * Create a connection between the two points according
55422 * to the manhattan layout (only horizontal and vertical) edges.
55423 *
55424 * @param {Point} a
55425 * @param {Point} b
55426 *
55427 * @param {string} [directions='h:h'] specifies manhattan directions for each point as {adirection}:{bdirection}.
55428 A directionfor a point is either `h` (horizontal) or `v` (vertical)
55429 *
55430 * @return {Array<Point>}
55431 */
55432 function connectPoints(a, b, directions) {
55433
55434 var points = getBendpoints(a, b, directions);
55435
55436 points.unshift(a);
55437 points.push(b);
55438
55439 return withoutRedundantPoints(points);
55440 }
55441
55442
55443 /**
55444 * Connect two rectangles using a manhattan layouted connection.
55445 *
55446 * @param {Bounds} source source rectangle
55447 * @param {Bounds} target target rectangle
55448 * @param {Point} [start] source docking
55449 * @param {Point} [end] target docking
55450 *
55451 * @param {Object} [hints]
55452 * @param {string} [hints.preserveDocking=source] preserve docking on selected side
55453 * @param {Array<string>} [hints.preferredLayouts]
55454 * @param {Point|boolean} [hints.connectionStart] whether the start changed
55455 * @param {Point|boolean} [hints.connectionEnd] whether the end changed
55456 *
55457 * @return {Array<Point>} connection points
55458 */
55459 function connectRectangles(source, target, start, end, hints) {
55460
55461 var preferredLayouts = hints && hints.preferredLayouts || [];
55462
55463 var preferredLayout = without(preferredLayouts, 'straight')[0] || 'h:h';
55464
55465 var threshold = ORIENTATION_THRESHOLD[preferredLayout] || 0;
55466
55467 var orientation = getOrientation(source, target, threshold);
55468
55469 var directions = getDirections(orientation, preferredLayout);
55470
55471 start = start || getMid(source);
55472 end = end || getMid(target);
55473
55474 var directionSplit = directions.split(':');
55475
55476 // compute actual docking points for start / end
55477 // this ensures we properly layout only parts of the
55478 // connection that lies in between the two rectangles
55479 var startDocking = getDockingPoint(start, source, directionSplit[0], invertOrientation(orientation)),
55480 endDocking = getDockingPoint(end, target, directionSplit[1], orientation);
55481
55482 return connectPoints(startDocking, endDocking, directions);
55483 }
55484
55485
55486 /**
55487 * Repair the connection between two rectangles, of which one has been updated.
55488 *
55489 * @param {Bounds} source
55490 * @param {Bounds} target
55491 * @param {Point} [start]
55492 * @param {Point} [end]
55493 * @param {Array<Point>} [waypoints]
55494 * @param {Object} [hints]
55495 * @param {Array<string>} [hints.preferredLayouts] list of preferred layouts
55496 * @param {boolean} [hints.connectionStart]
55497 * @param {boolean} [hints.connectionEnd]
55498 *
55499 * @return {Array<Point>} repaired waypoints
55500 */
55501 function repairConnection(source, target, start, end, waypoints, hints) {
55502
55503 if (isArray$4(start)) {
55504 waypoints = start;
55505 hints = end;
55506
55507 start = getMid(source);
55508 end = getMid(target);
55509 }
55510
55511 hints = assign({ preferredLayouts: [] }, hints);
55512 waypoints = waypoints || [];
55513
55514 var preferredLayouts = hints.preferredLayouts,
55515 preferStraight = preferredLayouts.indexOf('straight') !== -1,
55516 repairedWaypoints;
55517
55518 // just layout non-existing or simple connections
55519 // attempt to render straight lines, if required
55520
55521 // attempt to layout a straight line
55522 repairedWaypoints = preferStraight && tryLayoutStraight(source, target, start, end, hints);
55523
55524 if (repairedWaypoints) {
55525 return repairedWaypoints;
55526 }
55527
55528 // try to layout from end
55529 repairedWaypoints = hints.connectionEnd && tryRepairConnectionEnd(target, source, end, waypoints);
55530
55531 if (repairedWaypoints) {
55532 return repairedWaypoints;
55533 }
55534
55535 // try to layout from start
55536 repairedWaypoints = hints.connectionStart && tryRepairConnectionStart(source, target, start, waypoints);
55537
55538 if (repairedWaypoints) {
55539 return repairedWaypoints;
55540 }
55541
55542 // or whether nothing seems to have changed
55543 if (!hints.connectionStart && !hints.connectionEnd && waypoints && waypoints.length) {
55544 return waypoints;
55545 }
55546
55547 // simply reconnect if nothing else worked
55548 return connectRectangles(source, target, start, end, hints);
55549 }
55550
55551
55552 function inRange(a, start, end) {
55553 return a >= start && a <= end;
55554 }
55555
55556 function isInRange(axis, a, b) {
55557 var size = {
55558 x: 'width',
55559 y: 'height'
55560 };
55561
55562 return inRange(a[axis], b[axis], b[axis] + b[size[axis]]);
55563 }
55564
55565 /**
55566 * Layout a straight connection
55567 *
55568 * @param {Bounds} source
55569 * @param {Bounds} target
55570 * @param {Point} start
55571 * @param {Point} end
55572 * @param {Object} [hints]
55573 *
55574 * @return {Array<Point>|null} waypoints if straight layout worked
55575 */
55576 function tryLayoutStraight(source, target, start, end, hints) {
55577 var axis = {},
55578 primaryAxis,
55579 orientation;
55580
55581 orientation = getOrientation(source, target);
55582
55583 // only layout a straight connection if shapes are
55584 // horizontally or vertically aligned
55585 if (!/^(top|bottom|left|right)$/.test(orientation)) {
55586 return null;
55587 }
55588
55589 if (/top|bottom/.test(orientation)) {
55590 primaryAxis = 'x';
55591 }
55592
55593 if (/left|right/.test(orientation)) {
55594 primaryAxis = 'y';
55595 }
55596
55597 if (hints.preserveDocking === 'target') {
55598
55599 if (!isInRange(primaryAxis, end, source)) {
55600 return null;
55601 }
55602
55603 axis[primaryAxis] = end[primaryAxis];
55604
55605 return [
55606 {
55607 x: axis.x !== undefined ? axis.x : start.x,
55608 y: axis.y !== undefined ? axis.y : start.y,
55609 original: {
55610 x: axis.x !== undefined ? axis.x : start.x,
55611 y: axis.y !== undefined ? axis.y : start.y
55612 }
55613 },
55614 {
55615 x: end.x,
55616 y: end.y
55617 }
55618 ];
55619
55620 } else {
55621
55622 if (!isInRange(primaryAxis, start, target)) {
55623 return null;
55624 }
55625
55626 axis[primaryAxis] = start[primaryAxis];
55627
55628 return [
55629 {
55630 x: start.x,
55631 y: start.y
55632 },
55633 {
55634 x: axis.x !== undefined ? axis.x : end.x,
55635 y: axis.y !== undefined ? axis.y : end.y,
55636 original: {
55637 x: axis.x !== undefined ? axis.x : end.x,
55638 y: axis.y !== undefined ? axis.y : end.y
55639 }
55640 }
55641 ];
55642 }
55643
55644 }
55645
55646 /**
55647 * Repair a connection from start.
55648 *
55649 * @param {Bounds} moved
55650 * @param {Bounds} other
55651 * @param {Point} newDocking
55652 * @param {Array<Point>} points originalPoints from moved to other
55653 *
55654 * @return {Array<Point>|null} the repaired points between the two rectangles
55655 */
55656 function tryRepairConnectionStart(moved, other, newDocking, points) {
55657 return _tryRepairConnectionSide(moved, other, newDocking, points);
55658 }
55659
55660 /**
55661 * Repair a connection from end.
55662 *
55663 * @param {Bounds} moved
55664 * @param {Bounds} other
55665 * @param {Point} newDocking
55666 * @param {Array<Point>} points originalPoints from moved to other
55667 *
55668 * @return {Array<Point>|null} the repaired points between the two rectangles
55669 */
55670 function tryRepairConnectionEnd(moved, other, newDocking, points) {
55671 var waypoints = points.slice().reverse();
55672
55673 waypoints = _tryRepairConnectionSide(moved, other, newDocking, waypoints);
55674
55675 return waypoints ? waypoints.reverse() : null;
55676 }
55677
55678 /**
55679 * Repair a connection from one side that moved.
55680 *
55681 * @param {Bounds} moved
55682 * @param {Bounds} other
55683 * @param {Point} newDocking
55684 * @param {Array<Point>} points originalPoints from moved to other
55685 *
55686 * @return {Array<Point>} the repaired points between the two rectangles
55687 */
55688 function _tryRepairConnectionSide(moved, other, newDocking, points) {
55689
55690 function needsRelayout(points) {
55691 if (points.length < 3) {
55692 return true;
55693 }
55694
55695 if (points.length > 4) {
55696 return false;
55697 }
55698
55699 // relayout if two points overlap
55700 // this is most likely due to
55701 return !!find(points, function(p, idx) {
55702 var q = points[idx - 1];
55703
55704 return q && pointDistance(p, q) < 3;
55705 });
55706 }
55707
55708 function repairBendpoint(candidate, oldPeer, newPeer) {
55709
55710 var alignment = pointsAligned(oldPeer, candidate);
55711
55712 switch (alignment) {
55713 case 'v':
55714
55715 // repair horizontal alignment
55716 return { x: newPeer.x, y: candidate.y };
55717 case 'h':
55718
55719 // repair vertical alignment
55720 return { x: candidate.x, y: newPeer.y };
55721 }
55722
55723 return { x: candidate.x, y: candidate. y };
55724 }
55725
55726 function removeOverlapping(points, a, b) {
55727 var i;
55728
55729 for (i = points.length - 2; i !== 0; i--) {
55730
55731 // intersects (?) break, remove all bendpoints up to this one and relayout
55732 if (pointInRect(points[i], a, INTERSECTION_THRESHOLD) ||
55733 pointInRect(points[i], b, INTERSECTION_THRESHOLD)) {
55734
55735 // return sliced old connection
55736 return points.slice(i);
55737 }
55738 }
55739
55740 return points;
55741 }
55742
55743 // (0) only repair what has layoutable bendpoints
55744
55745 // (1) if only one bendpoint and on shape moved onto other shapes axis
55746 // (horizontally / vertically), relayout
55747
55748 if (needsRelayout(points)) {
55749 return null;
55750 }
55751
55752 var oldDocking = points[0],
55753 newPoints = points.slice(),
55754 slicedPoints;
55755
55756 // (2) repair only last line segment and only if it was layouted before
55757
55758 newPoints[0] = newDocking;
55759 newPoints[1] = repairBendpoint(newPoints[1], oldDocking, newDocking);
55760
55761
55762 // (3) if shape intersects with any bendpoint after repair,
55763 // remove all segments up to this bendpoint and repair from there
55764 slicedPoints = removeOverlapping(newPoints, moved, other);
55765
55766 if (slicedPoints !== newPoints) {
55767 newPoints = _tryRepairConnectionSide(moved, other, newDocking, slicedPoints);
55768 }
55769
55770 // (4) do NOT repair if repaired bendpoints are aligned
55771 if (newPoints && pointsAligned(newPoints)) {
55772 return null;
55773 }
55774
55775 return newPoints;
55776 }
55777
55778
55779 /**
55780 * Returns the manhattan directions connecting two rectangles
55781 * with the given orientation.
55782 *
55783 * Will always return the default layout, if it is specific
55784 * regarding sides already (trbl).
55785 *
55786 * @example
55787 *
55788 * getDirections('top'); // -> 'v:v'
55789 * getDirections('intersect'); // -> 't:t'
55790 *
55791 * getDirections('top-right', 'v:h'); // -> 'v:h'
55792 * getDirections('top-right', 'h:h'); // -> 'h:h'
55793 *
55794 *
55795 * @param {string} orientation
55796 * @param {string} defaultLayout
55797 *
55798 * @return {string}
55799 */
55800 function getDirections(orientation, defaultLayout) {
55801
55802 // don't override specific trbl directions
55803 if (isExplicitDirections(defaultLayout)) {
55804 return defaultLayout;
55805 }
55806
55807 switch (orientation) {
55808 case 'intersect':
55809 return 't:t';
55810
55811 case 'top':
55812 case 'bottom':
55813 return 'v:v';
55814
55815 case 'left':
55816 case 'right':
55817 return 'h:h';
55818
55819 // 'top-left'
55820 // 'top-right'
55821 // 'bottom-left'
55822 // 'bottom-right'
55823 default:
55824 return defaultLayout;
55825 }
55826 }
55827
55828 function isValidDirections(directions) {
55829 return directions && /^h|v|t|r|b|l:h|v|t|r|b|l$/.test(directions);
55830 }
55831
55832 function isExplicitDirections(directions) {
55833 return directions && /t|r|b|l/.test(directions);
55834 }
55835
55836 function invertOrientation(orientation) {
55837 return {
55838 'top': 'bottom',
55839 'bottom': 'top',
55840 'left': 'right',
55841 'right': 'left',
55842 'top-left': 'bottom-right',
55843 'bottom-right': 'top-left',
55844 'top-right': 'bottom-left',
55845 'bottom-left': 'top-right',
55846 }[orientation];
55847 }
55848
55849 function getDockingPoint(point, rectangle, dockingDirection, targetOrientation) {
55850
55851 // ensure we end up with a specific docking direction
55852 // based on the targetOrientation, if <h|v> is being passed
55853
55854 if (dockingDirection === 'h') {
55855 dockingDirection = /left/.test(targetOrientation) ? 'l' : 'r';
55856 }
55857
55858 if (dockingDirection === 'v') {
55859 dockingDirection = /top/.test(targetOrientation) ? 't' : 'b';
55860 }
55861
55862 if (dockingDirection === 't') {
55863 return { original: point, x: point.x, y: rectangle.y };
55864 }
55865
55866 if (dockingDirection === 'r') {
55867 return { original: point, x: rectangle.x + rectangle.width, y: point.y };
55868 }
55869
55870 if (dockingDirection === 'b') {
55871 return { original: point, x: point.x, y: rectangle.y + rectangle.height };
55872 }
55873
55874 if (dockingDirection === 'l') {
55875 return { original: point, x: rectangle.x, y: point.y };
55876 }
55877
55878 throw new Error('unexpected dockingDirection: <' + dockingDirection + '>');
55879 }
55880
55881
55882 /**
55883 * Return list of waypoints with redundant ones filtered out.
55884 *
55885 * @example
55886 *
55887 * Original points:
55888 *
55889 * [x] ----- [x] ------ [x]
55890 * |
55891 * [x] ----- [x] - [x]
55892 *
55893 * Filtered:
55894 *
55895 * [x] ---------------- [x]
55896 * |
55897 * [x] ----------- [x]
55898 *
55899 * @param {Array<Point>} waypoints
55900 *
55901 * @return {Array<Point>}
55902 */
55903 function withoutRedundantPoints(waypoints) {
55904 return waypoints.reduce(function(points, p, idx) {
55905
55906 var previous = points[points.length - 1],
55907 next = waypoints[idx + 1];
55908
55909 if (!pointsOnLine(previous, next, p, 0)) {
55910 points.push(p);
55911 }
55912
55913 return points;
55914 }, []);
55915 }
55916
55917 var ATTACH_ORIENTATION_PADDING = -10,
55918 BOUNDARY_TO_HOST_THRESHOLD$1 = 40;
55919
55920 var oppositeOrientationMapping = {
55921 'top': 'bottom',
55922 'top-right': 'bottom-left',
55923 'top-left': 'bottom-right',
55924 'right': 'left',
55925 'bottom': 'top',
55926 'bottom-right': 'top-left',
55927 'bottom-left': 'top-right',
55928 'left': 'right'
55929 };
55930
55931 var orientationDirectionMapping = {
55932 top: 't',
55933 right: 'r',
55934 bottom: 'b',
55935 left: 'l'
55936 };
55937
55938
55939 function BpmnLayouter() {}
55940
55941 inherits$1(BpmnLayouter, BaseLayouter);
55942
55943
55944 BpmnLayouter.prototype.layoutConnection = function(connection, hints) {
55945 if (!hints) {
55946 hints = {};
55947 }
55948
55949 var source = hints.source || connection.source,
55950 target = hints.target || connection.target,
55951 waypoints = hints.waypoints || connection.waypoints,
55952 connectionStart = hints.connectionStart,
55953 connectionEnd = hints.connectionEnd;
55954
55955 var manhattanOptions,
55956 updatedWaypoints;
55957
55958 if (!connectionStart) {
55959 connectionStart = getConnectionDocking(waypoints && waypoints[ 0 ], source);
55960 }
55961
55962 if (!connectionEnd) {
55963 connectionEnd = getConnectionDocking(waypoints && waypoints[ waypoints.length - 1 ], target);
55964 }
55965
55966 // TODO(nikku): support vertical modeling
55967 // and invert preferredLayouts accordingly
55968
55969 if (is$1(connection, 'bpmn:Association') ||
55970 is$1(connection, 'bpmn:DataAssociation')) {
55971
55972 if (waypoints && !isCompensationAssociation(source, target)) {
55973 return [].concat([ connectionStart ], waypoints.slice(1, -1), [ connectionEnd ]);
55974 }
55975 }
55976
55977 if (is$1(connection, 'bpmn:MessageFlow')) {
55978 manhattanOptions = getMessageFlowManhattanOptions(source, target);
55979 } else if (is$1(connection, 'bpmn:SequenceFlow') || isCompensationAssociation(source, target)) {
55980
55981 // layout all connection between flow elements h:h, except for
55982 // (1) outgoing of boundary events -> layout based on attach orientation and target orientation
55983 // (2) incoming/outgoing of gateways -> v:h for outgoing, h:v for incoming
55984 // (3) loops
55985 if (source === target) {
55986 manhattanOptions = {
55987 preferredLayouts: getLoopPreferredLayout(source, connection)
55988 };
55989 } else if (is$1(source, 'bpmn:BoundaryEvent')) {
55990 manhattanOptions = {
55991 preferredLayouts: getBoundaryEventPreferredLayouts(source, target, connectionEnd)
55992 };
55993 } else if (isExpandedSubProcess(source) || isExpandedSubProcess(target)) {
55994 manhattanOptions = getSubProcessManhattanOptions(source);
55995 } else if (is$1(source, 'bpmn:Gateway')) {
55996 manhattanOptions = {
55997 preferredLayouts: [ 'v:h' ]
55998 };
55999 } else if (is$1(target, 'bpmn:Gateway')) {
56000 manhattanOptions = {
56001 preferredLayouts: [ 'h:v' ]
56002 };
56003 } else {
56004 manhattanOptions = {
56005 preferredLayouts: [ 'h:h' ]
56006 };
56007 }
56008 }
56009
56010 if (manhattanOptions) {
56011 manhattanOptions = assign(manhattanOptions, hints);
56012
56013 updatedWaypoints = withoutRedundantPoints(repairConnection(
56014 source,
56015 target,
56016 connectionStart,
56017 connectionEnd,
56018 waypoints,
56019 manhattanOptions
56020 ));
56021 }
56022
56023 return updatedWaypoints || [ connectionStart, connectionEnd ];
56024 };
56025
56026
56027 // helpers //////////
56028
56029 function getAttachOrientation(attachedElement) {
56030 var hostElement = attachedElement.host;
56031
56032 return getOrientation(getMid(attachedElement), hostElement, ATTACH_ORIENTATION_PADDING);
56033 }
56034
56035 function getMessageFlowManhattanOptions(source, target) {
56036 return {
56037 preferredLayouts: [ 'straight', 'v:v' ],
56038 preserveDocking: getMessageFlowPreserveDocking(source, target)
56039 };
56040 }
56041
56042 function getMessageFlowPreserveDocking(source, target) {
56043
56044 // (1) docking element connected to participant has precedence
56045 if (is$1(target, 'bpmn:Participant')) {
56046 return 'source';
56047 }
56048
56049 if (is$1(source, 'bpmn:Participant')) {
56050 return 'target';
56051 }
56052
56053 // (2) docking element connected to expanded sub-process has precedence
56054 if (isExpandedSubProcess(target)) {
56055 return 'source';
56056 }
56057
56058 if (isExpandedSubProcess(source)) {
56059 return 'target';
56060 }
56061
56062 // (3) docking event has precedence
56063 if (is$1(target, 'bpmn:Event')) {
56064 return 'target';
56065 }
56066
56067 if (is$1(source, 'bpmn:Event')) {
56068 return 'source';
56069 }
56070
56071 return null;
56072 }
56073
56074 function getSubProcessManhattanOptions(source) {
56075 return {
56076 preferredLayouts: [ 'straight', 'h:h' ],
56077 preserveDocking: getSubProcessPreserveDocking(source)
56078 };
56079 }
56080
56081 function getSubProcessPreserveDocking(source) {
56082 return isExpandedSubProcess(source) ? 'target' : 'source';
56083 }
56084
56085 function getConnectionDocking(point, shape) {
56086 return point ? (point.original || point) : getMid(shape);
56087 }
56088
56089 function isCompensationAssociation(source, target) {
56090 return is$1(target, 'bpmn:Activity') &&
56091 is$1(source, 'bpmn:BoundaryEvent') &&
56092 target.businessObject.isForCompensation;
56093 }
56094
56095 function isExpandedSubProcess(element) {
56096 return is$1(element, 'bpmn:SubProcess') && isExpanded(element);
56097 }
56098
56099 function isSame(a, b) {
56100 return a === b;
56101 }
56102
56103 function isAnyOrientation(orientation, orientations) {
56104 return orientations.indexOf(orientation) !== -1;
56105 }
56106
56107 function getHorizontalOrientation(orientation) {
56108 var matches = /right|left/.exec(orientation);
56109
56110 return matches && matches[0];
56111 }
56112
56113 function getVerticalOrientation(orientation) {
56114 var matches = /top|bottom/.exec(orientation);
56115
56116 return matches && matches[0];
56117 }
56118
56119 function isOppositeOrientation(a, b) {
56120 return oppositeOrientationMapping[a] === b;
56121 }
56122
56123 function isOppositeHorizontalOrientation(a, b) {
56124 var horizontalOrientation = getHorizontalOrientation(a);
56125
56126 var oppositeHorizontalOrientation = oppositeOrientationMapping[horizontalOrientation];
56127
56128 return b.indexOf(oppositeHorizontalOrientation) !== -1;
56129 }
56130
56131 function isOppositeVerticalOrientation(a, b) {
56132 var verticalOrientation = getVerticalOrientation(a);
56133
56134 var oppositeVerticalOrientation = oppositeOrientationMapping[verticalOrientation];
56135
56136 return b.indexOf(oppositeVerticalOrientation) !== -1;
56137 }
56138
56139 function isHorizontalOrientation(orientation) {
56140 return orientation === 'right' || orientation === 'left';
56141 }
56142
56143 function getLoopPreferredLayout(source, connection) {
56144 var waypoints = connection.waypoints;
56145
56146 var orientation = waypoints && waypoints.length && getOrientation(waypoints[0], source);
56147
56148 if (orientation === 'top') {
56149 return [ 't:r' ];
56150 } else if (orientation === 'right') {
56151 return [ 'r:b' ];
56152 } else if (orientation === 'left') {
56153 return [ 'l:t' ];
56154 }
56155
56156 return [ 'b:l' ];
56157 }
56158
56159 function getBoundaryEventPreferredLayouts(source, target, end) {
56160 var sourceMid = getMid(source),
56161 targetMid = getMid(target),
56162 attachOrientation = getAttachOrientation(source),
56163 sourceLayout,
56164 targetLayout;
56165
56166 var isLoop = isSame(source.host, target);
56167
56168 var attachedToSide = isAnyOrientation(attachOrientation, [ 'top', 'right', 'bottom', 'left' ]);
56169
56170 var targetOrientation = getOrientation(targetMid, sourceMid, {
56171 x: source.width / 2 + target.width / 2,
56172 y: source.height / 2 + target.height / 2
56173 });
56174
56175 if (isLoop) {
56176 return getBoundaryEventLoopLayout(attachOrientation, attachedToSide, source, target, end);
56177 }
56178
56179 // source layout
56180 sourceLayout = getBoundaryEventSourceLayout(attachOrientation, targetOrientation, attachedToSide);
56181
56182 // target layout
56183 targetLayout = getBoundaryEventTargetLayout(attachOrientation, targetOrientation, attachedToSide);
56184
56185 return [ sourceLayout + ':' + targetLayout ];
56186 }
56187
56188 function getBoundaryEventLoopLayout(attachOrientation, attachedToSide, source, target, end) {
56189 var orientation = attachedToSide ? attachOrientation : getVerticalOrientation(attachOrientation),
56190 sourceLayout = orientationDirectionMapping[ orientation ],
56191 targetLayout;
56192
56193 if (attachedToSide) {
56194 if (isHorizontalOrientation(attachOrientation)) {
56195 targetLayout = shouldConnectToSameSide('y', source, target, end) ? 'h' : 'b';
56196 } else {
56197 targetLayout = shouldConnectToSameSide('x', source, target, end) ? 'v' : 'l';
56198 }
56199 } else {
56200 targetLayout = 'v';
56201 }
56202
56203 return [ sourceLayout + ':' + targetLayout ];
56204 }
56205
56206 function shouldConnectToSameSide(axis, source, target, end) {
56207 var threshold = BOUNDARY_TO_HOST_THRESHOLD$1;
56208
56209 return !(
56210 areCloseOnAxis(axis, end, target, threshold) ||
56211 areCloseOnAxis(axis, end, {
56212 x: target.x + target.width,
56213 y: target.y + target.height
56214 }, threshold) ||
56215 areCloseOnAxis(axis, end, getMid(source), threshold)
56216 );
56217 }
56218
56219 function areCloseOnAxis(axis, a, b, threshold) {
56220 return Math.abs(a[ axis ] - b[ axis ]) < threshold;
56221 }
56222
56223 function getBoundaryEventSourceLayout(attachOrientation, targetOrientation, attachedToSide) {
56224
56225 // attached to either top, right, bottom or left side
56226 if (attachedToSide) {
56227 return orientationDirectionMapping[ attachOrientation ];
56228 }
56229
56230 // attached to either top-right, top-left, bottom-right or bottom-left corner
56231
56232 // same vertical or opposite horizontal orientation
56233 if (isSame(
56234 getVerticalOrientation(attachOrientation), getVerticalOrientation(targetOrientation)
56235 ) || isOppositeOrientation(
56236 getHorizontalOrientation(attachOrientation), getHorizontalOrientation(targetOrientation)
56237 )) {
56238 return orientationDirectionMapping[ getVerticalOrientation(attachOrientation) ];
56239 }
56240
56241 // fallback
56242 return orientationDirectionMapping[ getHorizontalOrientation(attachOrientation) ];
56243 }
56244
56245 function getBoundaryEventTargetLayout(attachOrientation, targetOrientation, attachedToSide) {
56246
56247 // attached to either top, right, bottom or left side
56248 if (attachedToSide) {
56249 if (isHorizontalOrientation(attachOrientation)) {
56250
56251 // orientation is right or left
56252
56253 // opposite horizontal orientation or same orientation
56254 if (
56255 isOppositeHorizontalOrientation(attachOrientation, targetOrientation) ||
56256 isSame(attachOrientation, targetOrientation)
56257 ) {
56258 return 'h';
56259 }
56260
56261 // fallback
56262 return 'v';
56263 } else {
56264
56265 // orientation is top or bottom
56266
56267 // opposite vertical orientation or same orientation
56268 if (
56269 isOppositeVerticalOrientation(attachOrientation, targetOrientation) ||
56270 isSame(attachOrientation, targetOrientation)
56271 ) {
56272 return 'v';
56273 }
56274
56275 // fallback
56276 return 'h';
56277 }
56278 }
56279
56280 // attached to either top-right, top-left, bottom-right or bottom-left corner
56281
56282 // orientation is right, left
56283 // or same vertical orientation but also right or left
56284 if (isHorizontalOrientation(targetOrientation) ||
56285 (isSame(getVerticalOrientation(attachOrientation), getVerticalOrientation(targetOrientation)) &&
56286 getHorizontalOrientation(targetOrientation))) {
56287 return 'h';
56288 } else {
56289 return 'v';
56290 }
56291 }
56292
56293 function dockingToPoint(docking) {
56294
56295 // use the dockings actual point and
56296 // retain the original docking
56297 return assign({ original: docking.point.original || docking.point }, docking.actual);
56298 }
56299
56300
56301 /**
56302 * A {@link ConnectionDocking} that crops connection waypoints based on
56303 * the path(s) of the connection source and target.
56304 *
56305 * @param {djs.core.ElementRegistry} elementRegistry
56306 */
56307 function CroppingConnectionDocking(elementRegistry, graphicsFactory) {
56308 this._elementRegistry = elementRegistry;
56309 this._graphicsFactory = graphicsFactory;
56310 }
56311
56312 CroppingConnectionDocking.$inject = [ 'elementRegistry', 'graphicsFactory' ];
56313
56314
56315 /**
56316 * @inheritDoc ConnectionDocking#getCroppedWaypoints
56317 */
56318 CroppingConnectionDocking.prototype.getCroppedWaypoints = function(connection, source, target) {
56319
56320 source = source || connection.source;
56321 target = target || connection.target;
56322
56323 var sourceDocking = this.getDockingPoint(connection, source, true),
56324 targetDocking = this.getDockingPoint(connection, target);
56325
56326 var croppedWaypoints = connection.waypoints.slice(sourceDocking.idx + 1, targetDocking.idx);
56327
56328 croppedWaypoints.unshift(dockingToPoint(sourceDocking));
56329 croppedWaypoints.push(dockingToPoint(targetDocking));
56330
56331 return croppedWaypoints;
56332 };
56333
56334 /**
56335 * Return the connection docking point on the specified shape
56336 *
56337 * @inheritDoc ConnectionDocking#getDockingPoint
56338 */
56339 CroppingConnectionDocking.prototype.getDockingPoint = function(connection, shape, dockStart) {
56340
56341 var waypoints = connection.waypoints,
56342 dockingIdx,
56343 dockingPoint,
56344 croppedPoint;
56345
56346 dockingIdx = dockStart ? 0 : waypoints.length - 1;
56347 dockingPoint = waypoints[dockingIdx];
56348
56349 croppedPoint = this._getIntersection(shape, connection, dockStart);
56350
56351 return {
56352 point: dockingPoint,
56353 actual: croppedPoint || dockingPoint,
56354 idx: dockingIdx
56355 };
56356 };
56357
56358
56359 // helpers //////////////////////
56360
56361 CroppingConnectionDocking.prototype._getIntersection = function(shape, connection, takeFirst) {
56362
56363 var shapePath = this._getShapePath(shape),
56364 connectionPath = this._getConnectionPath(connection);
56365
56366 return getElementLineIntersection(shapePath, connectionPath, takeFirst);
56367 };
56368
56369 CroppingConnectionDocking.prototype._getConnectionPath = function(connection) {
56370 return this._graphicsFactory.getConnectionPath(connection);
56371 };
56372
56373 CroppingConnectionDocking.prototype._getShapePath = function(shape) {
56374 return this._graphicsFactory.getShapePath(shape);
56375 };
56376
56377 CroppingConnectionDocking.prototype._getGfx = function(element) {
56378 return this._elementRegistry.getGraphics(element);
56379 };
56380
56381 var ModelingModule = {
56382 __init__: [
56383 'modeling',
56384 'bpmnUpdater'
56385 ],
56386 __depends__: [
56387 BehaviorModule,
56388 RulesModule,
56389 DiOrderingModule,
56390 OrderingModule,
56391 ReplaceModule,
56392 CommandModule,
56393 TooltipsModule,
56394 LabelSupportModule,
56395 AttachSupportModule,
56396 SelectionModule,
56397 ChangeSupportModule,
56398 SpaceToolModule
56399 ],
56400 bpmnFactory: [ 'type', BpmnFactory ],
56401 bpmnUpdater: [ 'type', BpmnUpdater ],
56402 elementFactory: [ 'type', ElementFactory ],
56403 modeling: [ 'type', Modeling ],
56404 layouter: [ 'type', BpmnLayouter ],
56405 connectionDocking: [ 'type', CroppingConnectionDocking ]
56406 };
56407
56408 var LOW_PRIORITY$2 = 500,
56409 MEDIUM_PRIORITY = 1250,
56410 HIGH_PRIORITY$2 = 1500;
56411
56412 var round = Math.round;
56413
56414 function mid(element) {
56415 return {
56416 x: element.x + round(element.width / 2),
56417 y: element.y + round(element.height / 2)
56418 };
56419 }
56420
56421 /**
56422 * A plugin that makes shapes draggable / droppable.
56423 *
56424 * @param {EventBus} eventBus
56425 * @param {Dragging} dragging
56426 * @param {Modeling} modeling
56427 * @param {Selection} selection
56428 * @param {Rules} rules
56429 */
56430 function MoveEvents(
56431 eventBus, dragging, modeling,
56432 selection, rules) {
56433
56434 // rules
56435
56436 function canMove(shapes, delta, position, target) {
56437
56438 return rules.allowed('elements.move', {
56439 shapes: shapes,
56440 delta: delta,
56441 position: position,
56442 target: target
56443 });
56444 }
56445
56446
56447 // move events
56448
56449 // assign a high priority to this handler to setup the environment
56450 // others may hook up later, e.g. at default priority and modify
56451 // the move environment.
56452 //
56453 // This sets up the context with
56454 //
56455 // * shape: the primary shape being moved
56456 // * shapes: a list of shapes to be moved
56457 // * validatedShapes: a list of shapes that are being checked
56458 // against the rules before and during move
56459 //
56460 eventBus.on('shape.move.start', HIGH_PRIORITY$2, function(event) {
56461
56462 var context = event.context,
56463 shape = event.shape,
56464 shapes = selection.get().slice();
56465
56466 // move only single shape if the dragged element
56467 // is not part of the current selection
56468 if (shapes.indexOf(shape) === -1) {
56469 shapes = [ shape ];
56470 }
56471
56472 // ensure we remove nested elements in the collection
56473 // and add attachers for a proper dragger
56474 shapes = removeNested(shapes);
56475
56476 // attach shapes to drag context
56477 assign(context, {
56478 shapes: shapes,
56479 validatedShapes: shapes,
56480 shape: shape
56481 });
56482 });
56483
56484
56485 // assign a high priority to this handler to setup the environment
56486 // others may hook up later, e.g. at default priority and modify
56487 // the move environment
56488 //
56489 eventBus.on('shape.move.start', MEDIUM_PRIORITY, function(event) {
56490
56491 var context = event.context,
56492 validatedShapes = context.validatedShapes,
56493 canExecute;
56494
56495 canExecute = context.canExecute = canMove(validatedShapes);
56496
56497 // check if we can move the elements
56498 if (!canExecute) {
56499 return false;
56500 }
56501 });
56502
56503 // assign a low priority to this handler
56504 // to let others modify the move event before we update
56505 // the context
56506 //
56507 eventBus.on('shape.move.move', LOW_PRIORITY$2, function(event) {
56508
56509 var context = event.context,
56510 validatedShapes = context.validatedShapes,
56511 hover = event.hover,
56512 delta = { x: event.dx, y: event.dy },
56513 position = { x: event.x, y: event.y },
56514 canExecute;
56515
56516 // check if we can move the elements
56517 canExecute = canMove(validatedShapes, delta, position, hover);
56518
56519 context.delta = delta;
56520 context.canExecute = canExecute;
56521
56522 // simply ignore move over
56523 if (canExecute === null) {
56524 context.target = null;
56525
56526 return;
56527 }
56528
56529 context.target = hover;
56530 });
56531
56532 eventBus.on('shape.move.end', function(event) {
56533
56534 var context = event.context;
56535
56536 var delta = context.delta,
56537 canExecute = context.canExecute,
56538 isAttach = canExecute === 'attach',
56539 shapes = context.shapes;
56540
56541 if (canExecute === false) {
56542 return false;
56543 }
56544
56545 // ensure we have actual pixel values deltas
56546 // (important when zoom level was > 1 during move)
56547 delta.x = round(delta.x);
56548 delta.y = round(delta.y);
56549
56550 if (delta.x === 0 && delta.y === 0) {
56551
56552 // didn't move
56553 return;
56554 }
56555
56556 modeling.moveElements(shapes, delta, context.target, {
56557 primaryShape: context.shape,
56558 attach: isAttach
56559 });
56560 });
56561
56562
56563 // move activation
56564
56565 eventBus.on('element.mousedown', function(event) {
56566
56567 if (!isPrimaryButton(event)) {
56568 return;
56569 }
56570
56571 var originalEvent = getOriginal$1(event);
56572
56573 if (!originalEvent) {
56574 throw new Error('must supply DOM mousedown event');
56575 }
56576
56577 return start(originalEvent, event.element);
56578 });
56579
56580 /**
56581 * Start move.
56582 *
56583 * @param {MouseEvent} event
56584 * @param {djs.model.Shape} shape
56585 * @param {boolean} [activate]
56586 * @param {Object} [context]
56587 */
56588 function start(event, element, activate, context) {
56589 if (isObject(activate)) {
56590 context = activate;
56591 activate = false;
56592 }
56593
56594 // do not move connections or the root element
56595 if (element.waypoints || !element.parent) {
56596 return;
56597 }
56598
56599 // ignore non-draggable hits
56600 if (classes$1(event.target).has('djs-hit-no-move')) {
56601 return;
56602 }
56603
56604 var referencePoint = mid(element);
56605
56606 dragging.init(event, referencePoint, 'shape.move', {
56607 cursor: 'grabbing',
56608 autoActivate: activate,
56609 data: {
56610 shape: element,
56611 context: context || {}
56612 }
56613 });
56614
56615 // we've handled the event
56616 return true;
56617 }
56618
56619 // API
56620
56621 this.start = start;
56622 }
56623
56624 MoveEvents.$inject = [
56625 'eventBus',
56626 'dragging',
56627 'modeling',
56628 'selection',
56629 'rules'
56630 ];
56631
56632
56633 /**
56634 * Return a filtered list of elements that do not contain
56635 * those nested into others.
56636 *
56637 * @param {Array<djs.model.Base>} elements
56638 *
56639 * @return {Array<djs.model.Base>} filtered
56640 */
56641 function removeNested(elements) {
56642
56643 var ids = groupBy(elements, 'id');
56644
56645 return filter(elements, function(element) {
56646 while ((element = element.parent)) {
56647
56648 // parent in selection
56649 if (ids[element.id]) {
56650 return false;
56651 }
56652 }
56653
56654 return true;
56655 });
56656 }
56657
56658 var LOW_PRIORITY$1 = 499;
56659
56660 var MARKER_DRAGGING = 'djs-dragging',
56661 MARKER_OK$1 = 'drop-ok',
56662 MARKER_NOT_OK$1 = 'drop-not-ok',
56663 MARKER_NEW_PARENT = 'new-parent',
56664 MARKER_ATTACH = 'attach-ok';
56665
56666
56667 /**
56668 * Provides previews for moving shapes when moving.
56669 *
56670 * @param {EventBus} eventBus
56671 * @param {ElementRegistry} elementRegistry
56672 * @param {Canvas} canvas
56673 * @param {Styles} styles
56674 */
56675 function MovePreview(
56676 eventBus, canvas, styles, previewSupport) {
56677
56678 function getVisualDragShapes(shapes) {
56679 var elements = getAllDraggedElements(shapes);
56680
56681 var filteredElements = removeEdges(elements);
56682
56683 return filteredElements;
56684 }
56685
56686 function getAllDraggedElements(shapes) {
56687 var allShapes = selfAndAllChildren(shapes, true);
56688
56689 var allConnections = map(allShapes, function(shape) {
56690 return (shape.incoming || []).concat(shape.outgoing || []);
56691 });
56692
56693 return flatten(allShapes.concat(allConnections));
56694 }
56695
56696 /**
56697 * Sets drop marker on an element.
56698 */
56699 function setMarker(element, marker) {
56700
56701 [ MARKER_ATTACH, MARKER_OK$1, MARKER_NOT_OK$1, MARKER_NEW_PARENT ].forEach(function(m) {
56702
56703 if (m === marker) {
56704 canvas.addMarker(element, m);
56705 } else {
56706 canvas.removeMarker(element, m);
56707 }
56708 });
56709 }
56710
56711 /**
56712 * Make an element draggable.
56713 *
56714 * @param {Object} context
56715 * @param {djs.model.Base} element
56716 * @param {boolean} addMarker
56717 */
56718 function makeDraggable(context, element, addMarker) {
56719
56720 previewSupport.addDragger(element, context.dragGroup);
56721
56722 if (addMarker) {
56723 canvas.addMarker(element, MARKER_DRAGGING);
56724 }
56725
56726 if (context.allDraggedElements) {
56727 context.allDraggedElements.push(element);
56728 } else {
56729 context.allDraggedElements = [ element ];
56730 }
56731 }
56732
56733 // assign a low priority to this handler
56734 // to let others modify the move context before
56735 // we draw things
56736 eventBus.on('shape.move.start', LOW_PRIORITY$1, function(event) {
56737 var context = event.context,
56738 dragShapes = context.shapes,
56739 allDraggedElements = context.allDraggedElements;
56740
56741 var visuallyDraggedShapes = getVisualDragShapes(dragShapes);
56742
56743 if (!context.dragGroup) {
56744 var dragGroup = create$1('g');
56745
56746 attr$1(dragGroup, styles.cls('djs-drag-group', [ 'no-events' ]));
56747
56748 var activeLayer = canvas.getActiveLayer();
56749
56750 append(activeLayer, dragGroup);
56751
56752 context.dragGroup = dragGroup;
56753 }
56754
56755 // add previews
56756 visuallyDraggedShapes.forEach(function(shape) {
56757 previewSupport.addDragger(shape, context.dragGroup);
56758 });
56759
56760 // cache all dragged elements / gfx
56761 // so that we can quickly undo their state changes later
56762 if (!allDraggedElements) {
56763 allDraggedElements = getAllDraggedElements(dragShapes);
56764 } else {
56765 allDraggedElements = flatten([
56766 allDraggedElements,
56767 getAllDraggedElements(dragShapes)
56768 ]);
56769 }
56770
56771 // add dragging marker
56772 forEach$2(allDraggedElements, function(e) {
56773 canvas.addMarker(e, MARKER_DRAGGING);
56774 });
56775
56776 context.allDraggedElements = allDraggedElements;
56777
56778 // determine, if any of the dragged elements have different parents
56779 context.differentParents = haveDifferentParents(dragShapes);
56780 });
56781
56782 // update previews
56783 eventBus.on('shape.move.move', LOW_PRIORITY$1, function(event) {
56784
56785 var context = event.context,
56786 dragGroup = context.dragGroup,
56787 target = context.target,
56788 parent = context.shape.parent,
56789 canExecute = context.canExecute;
56790
56791 if (target) {
56792 if (canExecute === 'attach') {
56793 setMarker(target, MARKER_ATTACH);
56794 } else if (context.canExecute && target && target.id !== parent.id) {
56795 setMarker(target, MARKER_NEW_PARENT);
56796 } else {
56797 setMarker(target, context.canExecute ? MARKER_OK$1 : MARKER_NOT_OK$1);
56798 }
56799 }
56800
56801 translate$2(dragGroup, event.dx, event.dy);
56802 });
56803
56804 eventBus.on([ 'shape.move.out', 'shape.move.cleanup' ], function(event) {
56805 var context = event.context,
56806 target = context.target;
56807
56808 if (target) {
56809 setMarker(target, null);
56810 }
56811 });
56812
56813 // remove previews
56814 eventBus.on('shape.move.cleanup', function(event) {
56815
56816 var context = event.context,
56817 allDraggedElements = context.allDraggedElements,
56818 dragGroup = context.dragGroup;
56819
56820
56821 // remove dragging marker
56822 forEach$2(allDraggedElements, function(e) {
56823 canvas.removeMarker(e, MARKER_DRAGGING);
56824 });
56825
56826 if (dragGroup) {
56827 remove$2(dragGroup);
56828 }
56829 });
56830
56831
56832 // API //////////////////////
56833
56834 /**
56835 * Make an element draggable.
56836 *
56837 * @param {Object} context
56838 * @param {djs.model.Base} element
56839 * @param {boolean} addMarker
56840 */
56841 this.makeDraggable = makeDraggable;
56842 }
56843
56844 MovePreview.$inject = [
56845 'eventBus',
56846 'canvas',
56847 'styles',
56848 'previewSupport'
56849 ];
56850
56851
56852 // helpers //////////////////////
56853
56854 /**
56855 * returns elements minus all connections
56856 * where source or target is not elements
56857 */
56858 function removeEdges(elements) {
56859
56860 var filteredElements = filter(elements, function(element) {
56861
56862 if (!isConnection$2(element)) {
56863 return true;
56864 } else {
56865
56866 return (
56867 find(elements, matchPattern({ id: element.source.id })) &&
56868 find(elements, matchPattern({ id: element.target.id }))
56869 );
56870 }
56871 });
56872
56873 return filteredElements;
56874 }
56875
56876 function haveDifferentParents(elements) {
56877 return size(groupBy(elements, function(e) { return e.parent && e.parent.id; })) !== 1;
56878 }
56879
56880 /**
56881 * Checks if an element is a connection.
56882 */
56883 function isConnection$2(element) {
56884 return element.waypoints;
56885 }
56886
56887 var MoveModule = {
56888 __depends__: [
56889 InteractionEventsModule$1,
56890 SelectionModule,
56891 OutlineModule,
56892 RulesModule$1,
56893 DraggingModule,
56894 PreviewSupportModule
56895 ],
56896 __init__: [
56897 'move',
56898 'movePreview'
56899 ],
56900 move: [ 'type', MoveEvents ],
56901 movePreview: [ 'type', MovePreview ]
56902 };
56903
56904 var TOGGLE_SELECTOR = '.djs-palette-toggle',
56905 ENTRY_SELECTOR = '.entry',
56906 ELEMENT_SELECTOR = TOGGLE_SELECTOR + ', ' + ENTRY_SELECTOR;
56907
56908 var PALETTE_PREFIX = 'djs-palette-',
56909 PALETTE_SHOWN_CLS = 'shown',
56910 PALETTE_OPEN_CLS = 'open',
56911 PALETTE_TWO_COLUMN_CLS = 'two-column';
56912
56913 var DEFAULT_PRIORITY = 1000;
56914
56915
56916 /**
56917 * A palette containing modeling elements.
56918 */
56919 function Palette(eventBus, canvas) {
56920
56921 this._eventBus = eventBus;
56922 this._canvas = canvas;
56923
56924 var self = this;
56925
56926 eventBus.on('tool-manager.update', function(event) {
56927 var tool = event.tool;
56928
56929 self.updateToolHighlight(tool);
56930 });
56931
56932 eventBus.on('i18n.changed', function() {
56933 self._update();
56934 });
56935
56936 eventBus.on('diagram.init', function() {
56937
56938 self._diagramInitialized = true;
56939
56940 self._rebuild();
56941 });
56942 }
56943
56944 Palette.$inject = [ 'eventBus', 'canvas' ];
56945
56946
56947 /**
56948 * Register a provider with the palette
56949 *
56950 * @param {number} [priority=1000]
56951 * @param {PaletteProvider} provider
56952 *
56953 * @example
56954 * const paletteProvider = {
56955 * getPaletteEntries: function() {
56956 * return function(entries) {
56957 * return {
56958 * ...entries,
56959 * 'entry-1': {
56960 * label: 'My Entry',
56961 * action: function() { alert("I have been clicked!"); }
56962 * }
56963 * };
56964 * }
56965 * }
56966 * };
56967 *
56968 * palette.registerProvider(800, paletteProvider);
56969 */
56970 Palette.prototype.registerProvider = function(priority, provider) {
56971 if (!provider) {
56972 provider = priority;
56973 priority = DEFAULT_PRIORITY;
56974 }
56975
56976 this._eventBus.on('palette.getProviders', priority, function(event) {
56977 event.providers.push(provider);
56978 });
56979
56980 this._rebuild();
56981 };
56982
56983
56984 /**
56985 * Returns the palette entries
56986 *
56987 * @return {Object<string, PaletteEntryDescriptor>} map of entries
56988 */
56989 Palette.prototype.getEntries = function() {
56990 var providers = this._getProviders();
56991
56992 return providers.reduce(addPaletteEntries, {});
56993 };
56994
56995 Palette.prototype._rebuild = function() {
56996
56997 if (!this._diagramInitialized) {
56998 return;
56999 }
57000
57001 var providers = this._getProviders();
57002
57003 if (!providers.length) {
57004 return;
57005 }
57006
57007 if (!this._container) {
57008 this._init();
57009 }
57010
57011 this._update();
57012 };
57013
57014 /**
57015 * Initialize
57016 */
57017 Palette.prototype._init = function() {
57018
57019 var self = this;
57020
57021 var eventBus = this._eventBus;
57022
57023 var parentContainer = this._getParentContainer();
57024
57025 var container = this._container = domify(Palette.HTML_MARKUP);
57026
57027 parentContainer.appendChild(container);
57028 classes(parentContainer).add(PALETTE_PREFIX + PALETTE_SHOWN_CLS);
57029
57030 delegate.bind(container, ELEMENT_SELECTOR, 'click', function(event) {
57031
57032 var target = event.delegateTarget;
57033
57034 if (matchesSelector(target, TOGGLE_SELECTOR)) {
57035 return self.toggle();
57036 }
57037
57038 self.trigger('click', event);
57039 });
57040
57041 // prevent drag propagation
57042 componentEvent.bind(container, 'mousedown', function(event) {
57043 event.stopPropagation();
57044 });
57045
57046 // prevent drag propagation
57047 delegate.bind(container, ENTRY_SELECTOR, 'dragstart', function(event) {
57048 self.trigger('dragstart', event);
57049 });
57050
57051 eventBus.on('canvas.resized', this._layoutChanged, this);
57052
57053 eventBus.fire('palette.create', {
57054 container: container
57055 });
57056 };
57057
57058 Palette.prototype._getProviders = function(id) {
57059
57060 var event = this._eventBus.createEvent({
57061 type: 'palette.getProviders',
57062 providers: []
57063 });
57064
57065 this._eventBus.fire(event);
57066
57067 return event.providers;
57068 };
57069
57070 /**
57071 * Update palette state.
57072 *
57073 * @param {Object} [state] { open, twoColumn }
57074 */
57075 Palette.prototype._toggleState = function(state) {
57076
57077 state = state || {};
57078
57079 var parent = this._getParentContainer(),
57080 container = this._container;
57081
57082 var eventBus = this._eventBus;
57083
57084 var twoColumn;
57085
57086 var cls = classes(container),
57087 parentCls = classes(parent);
57088
57089 if ('twoColumn' in state) {
57090 twoColumn = state.twoColumn;
57091 } else {
57092 twoColumn = this._needsCollapse(parent.clientHeight, this._entries || {});
57093 }
57094
57095 // always update two column
57096 cls.toggle(PALETTE_TWO_COLUMN_CLS, twoColumn);
57097 parentCls.toggle(PALETTE_PREFIX + PALETTE_TWO_COLUMN_CLS, twoColumn);
57098
57099 if ('open' in state) {
57100 cls.toggle(PALETTE_OPEN_CLS, state.open);
57101 parentCls.toggle(PALETTE_PREFIX + PALETTE_OPEN_CLS, state.open);
57102 }
57103
57104 eventBus.fire('palette.changed', {
57105 twoColumn: twoColumn,
57106 open: this.isOpen()
57107 });
57108 };
57109
57110 Palette.prototype._update = function() {
57111
57112 var entriesContainer = query('.djs-palette-entries', this._container),
57113 entries = this._entries = this.getEntries();
57114
57115 clear(entriesContainer);
57116
57117 forEach$2(entries, function(entry, id) {
57118
57119 var grouping = entry.group || 'default';
57120
57121 var container = query('[data-group=' + grouping + ']', entriesContainer);
57122 if (!container) {
57123 container = domify('<div class="group" data-group="' + grouping + '"></div>');
57124 entriesContainer.appendChild(container);
57125 }
57126
57127 var html = entry.html || (
57128 entry.separator ?
57129 '<hr class="separator" />' :
57130 '<div class="entry" draggable="true"></div>');
57131
57132
57133 var control = domify(html);
57134 container.appendChild(control);
57135
57136 if (!entry.separator) {
57137 attr(control, 'data-action', id);
57138
57139 if (entry.title) {
57140 attr(control, 'title', entry.title);
57141 }
57142
57143 if (entry.className) {
57144 addClasses(control, entry.className);
57145 }
57146
57147 if (entry.imageUrl) {
57148 control.appendChild(domify('<img src="' + entry.imageUrl + '">'));
57149 }
57150 }
57151 });
57152
57153 // open after update
57154 this.open();
57155 };
57156
57157
57158 /**
57159 * Trigger an action available on the palette
57160 *
57161 * @param {string} action
57162 * @param {Event} event
57163 */
57164 Palette.prototype.trigger = function(action, event, autoActivate) {
57165 var entries = this._entries,
57166 entry,
57167 handler,
57168 originalEvent,
57169 button = event.delegateTarget || event.target;
57170
57171 if (!button) {
57172 return event.preventDefault();
57173 }
57174
57175 entry = entries[attr(button, 'data-action')];
57176
57177 // when user clicks on the palette and not on an action
57178 if (!entry) {
57179 return;
57180 }
57181
57182 handler = entry.action;
57183
57184 originalEvent = event.originalEvent || event;
57185
57186 // simple action (via callback function)
57187 if (isFunction(handler)) {
57188 if (action === 'click') {
57189 handler(originalEvent, autoActivate);
57190 }
57191 } else {
57192 if (handler[action]) {
57193 handler[action](originalEvent, autoActivate);
57194 }
57195 }
57196
57197 // silence other actions
57198 event.preventDefault();
57199 };
57200
57201 Palette.prototype._layoutChanged = function() {
57202 this._toggleState({});
57203 };
57204
57205 /**
57206 * Do we need to collapse to two columns?
57207 *
57208 * @param {number} availableHeight
57209 * @param {Object} entries
57210 *
57211 * @return {boolean}
57212 */
57213 Palette.prototype._needsCollapse = function(availableHeight, entries) {
57214
57215 // top margin + bottom toggle + bottom margin
57216 // implementors must override this method if they
57217 // change the palette styles
57218 var margin = 20 + 10 + 20;
57219
57220 var entriesHeight = Object.keys(entries).length * 46;
57221
57222 return availableHeight < entriesHeight + margin;
57223 };
57224
57225 /**
57226 * Close the palette
57227 */
57228 Palette.prototype.close = function() {
57229
57230 this._toggleState({
57231 open: false,
57232 twoColumn: false
57233 });
57234 };
57235
57236
57237 /**
57238 * Open the palette
57239 */
57240 Palette.prototype.open = function() {
57241 this._toggleState({ open: true });
57242 };
57243
57244
57245 Palette.prototype.toggle = function(open) {
57246 if (this.isOpen()) {
57247 this.close();
57248 } else {
57249 this.open();
57250 }
57251 };
57252
57253 Palette.prototype.isActiveTool = function(tool) {
57254 return tool && this._activeTool === tool;
57255 };
57256
57257 Palette.prototype.updateToolHighlight = function(name) {
57258 var entriesContainer,
57259 toolsContainer;
57260
57261 if (!this._toolsContainer) {
57262 entriesContainer = query('.djs-palette-entries', this._container);
57263
57264 this._toolsContainer = query('[data-group=tools]', entriesContainer);
57265 }
57266
57267 toolsContainer = this._toolsContainer;
57268
57269 forEach$2(toolsContainer.children, function(tool) {
57270 var actionName = tool.getAttribute('data-action');
57271
57272 if (!actionName) {
57273 return;
57274 }
57275
57276 var toolClasses = classes(tool);
57277
57278 actionName = actionName.replace('-tool', '');
57279
57280 if (toolClasses.contains('entry') && actionName === name) {
57281 toolClasses.add('highlighted-entry');
57282 } else {
57283 toolClasses.remove('highlighted-entry');
57284 }
57285 });
57286 };
57287
57288
57289 /**
57290 * Return true if the palette is opened.
57291 *
57292 * @example
57293 *
57294 * palette.open();
57295 *
57296 * if (palette.isOpen()) {
57297 * // yes, we are open
57298 * }
57299 *
57300 * @return {boolean} true if palette is opened
57301 */
57302 Palette.prototype.isOpen = function() {
57303 return classes(this._container).has(PALETTE_OPEN_CLS);
57304 };
57305
57306 /**
57307 * Get container the palette lives in.
57308 *
57309 * @return {Element}
57310 */
57311 Palette.prototype._getParentContainer = function() {
57312 return this._canvas.getContainer();
57313 };
57314
57315
57316 /* markup definition */
57317
57318 Palette.HTML_MARKUP =
57319 '<div class="djs-palette">' +
57320 '<div class="djs-palette-entries"></div>' +
57321 '<div class="djs-palette-toggle"></div>' +
57322 '</div>';
57323
57324
57325 // helpers //////////////////////
57326
57327 function addClasses(element, classNames) {
57328
57329 var classes$1 = classes(element);
57330
57331 var actualClassNames = isArray$4(classNames) ? classNames : classNames.split(/\s+/g);
57332 actualClassNames.forEach(function(cls) {
57333 classes$1.add(cls);
57334 });
57335 }
57336
57337 function addPaletteEntries(entries, provider) {
57338
57339 var entriesOrUpdater = provider.getPaletteEntries();
57340
57341 if (isFunction(entriesOrUpdater)) {
57342 return entriesOrUpdater(entries);
57343 }
57344
57345 forEach$2(entriesOrUpdater, function(entry, id) {
57346 entries[id] = entry;
57347 });
57348
57349 return entries;
57350 }
57351
57352 var PaletteModule$1 = {
57353 __init__: [ 'palette' ],
57354 palette: [ 'type', Palette ]
57355 };
57356
57357 var LASSO_TOOL_CURSOR = 'crosshair';
57358
57359
57360 function LassoTool(
57361 eventBus, canvas, dragging,
57362 elementRegistry, selection, toolManager,
57363 mouse) {
57364
57365 this._selection = selection;
57366 this._dragging = dragging;
57367 this._mouse = mouse;
57368
57369 var self = this;
57370
57371 // lasso visuals implementation
57372
57373 /**
57374 * A helper that realizes the selection box visual
57375 */
57376 var visuals = {
57377
57378 create: function(context) {
57379 var container = canvas.getActiveLayer(),
57380 frame;
57381
57382 frame = context.frame = create$1('rect');
57383 attr$1(frame, {
57384 class: 'djs-lasso-overlay',
57385 width: 1,
57386 height: 1,
57387 x: 0,
57388 y: 0
57389 });
57390
57391 append(container, frame);
57392 },
57393
57394 update: function(context) {
57395 var frame = context.frame,
57396 bbox = context.bbox;
57397
57398 attr$1(frame, {
57399 x: bbox.x,
57400 y: bbox.y,
57401 width: bbox.width,
57402 height: bbox.height
57403 });
57404 },
57405
57406 remove: function(context) {
57407
57408 if (context.frame) {
57409 remove$2(context.frame);
57410 }
57411 }
57412 };
57413
57414 toolManager.registerTool('lasso', {
57415 tool: 'lasso.selection',
57416 dragging: 'lasso'
57417 });
57418
57419 eventBus.on('lasso.selection.end', function(event) {
57420 var target = event.originalEvent.target;
57421
57422 // only reactive on diagram click
57423 // on some occasions, event.hover is not set and we have to check if the target is an svg
57424 if (!event.hover && !(target instanceof SVGElement)) {
57425 return;
57426 }
57427
57428 eventBus.once('lasso.selection.ended', function() {
57429 self.activateLasso(event.originalEvent, true);
57430 });
57431 });
57432
57433 // lasso interaction implementation
57434
57435 eventBus.on('lasso.end', function(event) {
57436
57437 var bbox = toBBox(event);
57438
57439 var elements = elementRegistry.filter(function(element) {
57440 return element;
57441 });
57442
57443 self.select(elements, bbox);
57444 });
57445
57446 eventBus.on('lasso.start', function(event) {
57447
57448 var context = event.context;
57449
57450 context.bbox = toBBox(event);
57451 visuals.create(context);
57452 });
57453
57454 eventBus.on('lasso.move', function(event) {
57455
57456 var context = event.context;
57457
57458 context.bbox = toBBox(event);
57459 visuals.update(context);
57460 });
57461
57462 eventBus.on('lasso.cleanup', function(event) {
57463
57464 var context = event.context;
57465
57466 visuals.remove(context);
57467 });
57468
57469
57470 // event integration
57471
57472 eventBus.on('element.mousedown', 1500, function(event) {
57473
57474 if (!hasSecondaryModifier(event)) {
57475 return;
57476 }
57477
57478 self.activateLasso(event.originalEvent);
57479
57480 // we've handled the event
57481 return true;
57482 });
57483 }
57484
57485 LassoTool.$inject = [
57486 'eventBus',
57487 'canvas',
57488 'dragging',
57489 'elementRegistry',
57490 'selection',
57491 'toolManager',
57492 'mouse'
57493 ];
57494
57495
57496 LassoTool.prototype.activateLasso = function(event, autoActivate) {
57497
57498 this._dragging.init(event, 'lasso', {
57499 autoActivate: autoActivate,
57500 cursor: LASSO_TOOL_CURSOR,
57501 data: {
57502 context: {}
57503 }
57504 });
57505 };
57506
57507 LassoTool.prototype.activateSelection = function(event, autoActivate) {
57508
57509 this._dragging.init(event, 'lasso.selection', {
57510 trapClick: false,
57511 autoActivate: autoActivate,
57512 cursor: LASSO_TOOL_CURSOR,
57513 data: {
57514 context: {}
57515 }
57516 });
57517 };
57518
57519 LassoTool.prototype.select = function(elements, bbox) {
57520 var selectedElements = getEnclosedElements(elements, bbox);
57521
57522 this._selection.select(values(selectedElements));
57523 };
57524
57525 LassoTool.prototype.toggle = function() {
57526 if (this.isActive()) {
57527 return this._dragging.cancel();
57528 }
57529
57530 var mouseEvent = this._mouse.getLastMoveEvent();
57531
57532 this.activateSelection(mouseEvent, !!mouseEvent);
57533 };
57534
57535 LassoTool.prototype.isActive = function() {
57536 var context = this._dragging.context();
57537
57538 return context && /^lasso/.test(context.prefix);
57539 };
57540
57541
57542
57543 function toBBox(event) {
57544
57545 var start = {
57546
57547 x: event.x - event.dx,
57548 y: event.y - event.dy
57549 };
57550
57551 var end = {
57552 x: event.x,
57553 y: event.y
57554 };
57555
57556 var bbox;
57557
57558 if ((start.x <= end.x && start.y < end.y) ||
57559 (start.x < end.x && start.y <= end.y)) {
57560
57561 bbox = {
57562 x: start.x,
57563 y: start.y,
57564 width: end.x - start.x,
57565 height: end.y - start.y
57566 };
57567 } else if ((start.x >= end.x && start.y < end.y) ||
57568 (start.x > end.x && start.y <= end.y)) {
57569
57570 bbox = {
57571 x: end.x,
57572 y: start.y,
57573 width: start.x - end.x,
57574 height: end.y - start.y
57575 };
57576 } else if ((start.x <= end.x && start.y > end.y) ||
57577 (start.x < end.x && start.y >= end.y)) {
57578
57579 bbox = {
57580 x: start.x,
57581 y: end.y,
57582 width: end.x - start.x,
57583 height: start.y - end.y
57584 };
57585 } else if ((start.x >= end.x && start.y > end.y) ||
57586 (start.x > end.x && start.y >= end.y)) {
57587
57588 bbox = {
57589 x: end.x,
57590 y: end.y,
57591 width: start.x - end.x,
57592 height: start.y - end.y
57593 };
57594 } else {
57595
57596 bbox = {
57597 x: end.x,
57598 y: end.y,
57599 width: 0,
57600 height: 0
57601 };
57602 }
57603 return bbox;
57604 }
57605
57606 var LassoToolModule = {
57607 __depends__: [
57608 ToolManagerModule,
57609 MouseModule
57610 ],
57611 __init__: [ 'lassoTool' ],
57612 lassoTool: [ 'type', LassoTool ]
57613 };
57614
57615 var HIGH_PRIORITY$1 = 1500;
57616 var HAND_CURSOR = 'grab';
57617
57618
57619 function HandTool(
57620 eventBus, canvas, dragging,
57621 injector, toolManager, mouse) {
57622
57623 this._dragging = dragging;
57624 this._mouse = mouse;
57625
57626 var self = this,
57627 keyboard = injector.get('keyboard', false);
57628
57629 toolManager.registerTool('hand', {
57630 tool: 'hand',
57631 dragging: 'hand.move'
57632 });
57633
57634 eventBus.on('element.mousedown', HIGH_PRIORITY$1, function(event) {
57635
57636 if (!hasPrimaryModifier(event)) {
57637 return;
57638 }
57639
57640 self.activateMove(event.originalEvent, true);
57641
57642 return false;
57643 });
57644
57645 keyboard && keyboard.addListener(HIGH_PRIORITY$1, function(e) {
57646 if (!isSpace(e.keyEvent) || self.isActive()) {
57647 return;
57648 }
57649
57650 var mouseEvent = self._mouse.getLastMoveEvent();
57651
57652 self.activateMove(mouseEvent, !!mouseEvent);
57653 }, 'keyboard.keydown');
57654
57655 keyboard && keyboard.addListener(HIGH_PRIORITY$1, function(e) {
57656 if (!isSpace(e.keyEvent) || !self.isActive()) {
57657 return;
57658 }
57659
57660 self.toggle();
57661 }, 'keyboard.keyup');
57662
57663 eventBus.on('hand.end', function(event) {
57664 var target = event.originalEvent.target;
57665
57666 // only reactive on diagram click
57667 // on some occasions, event.hover is not set and we have to check if the target is an svg
57668 if (!event.hover && !(target instanceof SVGElement)) {
57669 return false;
57670 }
57671
57672 eventBus.once('hand.ended', function() {
57673 self.activateMove(event.originalEvent, { reactivate: true });
57674 });
57675
57676 });
57677
57678 eventBus.on('hand.move.move', function(event) {
57679 var scale = canvas.viewbox().scale;
57680
57681 canvas.scroll({
57682 dx: event.dx * scale,
57683 dy: event.dy * scale
57684 });
57685 });
57686
57687 eventBus.on('hand.move.end', function(event) {
57688 var context = event.context,
57689 reactivate = context.reactivate;
57690
57691 // Don't reactivate if the user is using the keyboard keybinding
57692 if (!hasPrimaryModifier(event) && reactivate) {
57693
57694 eventBus.once('hand.move.ended', function(event) {
57695 self.activateHand(event.originalEvent, true, true);
57696 });
57697
57698 }
57699
57700 return false;
57701 });
57702
57703 }
57704
57705 HandTool.$inject = [
57706 'eventBus',
57707 'canvas',
57708 'dragging',
57709 'injector',
57710 'toolManager',
57711 'mouse'
57712 ];
57713
57714
57715 HandTool.prototype.activateMove = function(event, autoActivate, context) {
57716 if (typeof autoActivate === 'object') {
57717 context = autoActivate;
57718 autoActivate = false;
57719 }
57720
57721 this._dragging.init(event, 'hand.move', {
57722 autoActivate: autoActivate,
57723 cursor: HAND_CURSOR,
57724 data: {
57725 context: context || {}
57726 }
57727 });
57728 };
57729
57730 HandTool.prototype.activateHand = function(event, autoActivate, reactivate) {
57731 this._dragging.init(event, 'hand', {
57732 trapClick: false,
57733 autoActivate: autoActivate,
57734 cursor: HAND_CURSOR,
57735 data: {
57736 context: {
57737 reactivate: reactivate
57738 }
57739 }
57740 });
57741 };
57742
57743 HandTool.prototype.toggle = function() {
57744 if (this.isActive()) {
57745 return this._dragging.cancel();
57746 }
57747
57748 var mouseEvent = this._mouse.getLastMoveEvent();
57749
57750 this.activateHand(mouseEvent, !!mouseEvent);
57751 };
57752
57753 HandTool.prototype.isActive = function() {
57754 var context = this._dragging.context();
57755
57756 if (context) {
57757 return /^(hand|hand\.move)$/.test(context.prefix);
57758 }
57759
57760 return false;
57761 };
57762
57763 // helpers //////////
57764
57765 function isSpace(keyEvent) {
57766 return isKey(' ', keyEvent);
57767 }
57768
57769 var HandToolModule = {
57770 __depends__: [
57771 ToolManagerModule,
57772 MouseModule
57773 ],
57774 __init__: [ 'handTool' ],
57775 handTool: [ 'type', HandTool ]
57776 };
57777
57778 var MARKER_OK = 'connect-ok',
57779 MARKER_NOT_OK = 'connect-not-ok';
57780
57781 /**
57782 * @class
57783 * @constructor
57784 *
57785 * @param {EventBus} eventBus
57786 * @param {Dragging} dragging
57787 * @param {Connect} connect
57788 * @param {Canvas} canvas
57789 * @param {ToolManager} toolManager
57790 * @param {Rules} rules
57791 * @param {Mouse} mouse
57792 */
57793 function GlobalConnect(
57794 eventBus, dragging, connect,
57795 canvas, toolManager, rules,
57796 mouse) {
57797
57798 var self = this;
57799
57800 this._dragging = dragging;
57801 this._rules = rules;
57802 this._mouse = mouse;
57803
57804 toolManager.registerTool('global-connect', {
57805 tool: 'global-connect',
57806 dragging: 'global-connect.drag'
57807 });
57808
57809 eventBus.on('global-connect.hover', function(event) {
57810 var context = event.context,
57811 startTarget = event.hover;
57812
57813 var canStartConnect = context.canStartConnect = self.canStartConnect(startTarget);
57814
57815 // simply ignore hover
57816 if (canStartConnect === null) {
57817 return;
57818 }
57819
57820 context.startTarget = startTarget;
57821
57822 canvas.addMarker(startTarget, canStartConnect ? MARKER_OK : MARKER_NOT_OK);
57823 });
57824
57825
57826 eventBus.on([ 'global-connect.out', 'global-connect.cleanup' ], function(event) {
57827 var startTarget = event.context.startTarget,
57828 canStartConnect = event.context.canStartConnect;
57829
57830 if (startTarget) {
57831 canvas.removeMarker(startTarget, canStartConnect ? MARKER_OK : MARKER_NOT_OK);
57832 }
57833 });
57834
57835
57836 eventBus.on([ 'global-connect.ended' ], function(event) {
57837 var context = event.context,
57838 startTarget = context.startTarget,
57839 startPosition = {
57840 x: event.x,
57841 y: event.y
57842 };
57843
57844 var canStartConnect = self.canStartConnect(startTarget);
57845
57846 if (!canStartConnect) {
57847 return;
57848 }
57849
57850 eventBus.once('element.out', function() {
57851 eventBus.once([ 'connect.ended', 'connect.canceled' ], function() {
57852 eventBus.fire('global-connect.drag.ended');
57853 });
57854
57855 connect.start(null, startTarget, startPosition);
57856 });
57857
57858 return false;
57859 });
57860 }
57861
57862 GlobalConnect.$inject = [
57863 'eventBus',
57864 'dragging',
57865 'connect',
57866 'canvas',
57867 'toolManager',
57868 'rules',
57869 'mouse'
57870 ];
57871
57872 /**
57873 * Initiates tool activity.
57874 */
57875 GlobalConnect.prototype.start = function(event, autoActivate) {
57876 this._dragging.init(event, 'global-connect', {
57877 autoActivate: autoActivate,
57878 trapClick: false,
57879 data: {
57880 context: {}
57881 }
57882 });
57883 };
57884
57885 GlobalConnect.prototype.toggle = function() {
57886
57887 if (this.isActive()) {
57888 return this._dragging.cancel();
57889 }
57890
57891 var mouseEvent = this._mouse.getLastMoveEvent();
57892
57893 return this.start(mouseEvent, !!mouseEvent);
57894 };
57895
57896 GlobalConnect.prototype.isActive = function() {
57897 var context = this._dragging.context();
57898
57899 return context && /^global-connect/.test(context.prefix);
57900 };
57901
57902 /**
57903 * Check if source shape can initiate connection.
57904 *
57905 * @param {Shape} startTarget
57906 * @return {boolean}
57907 */
57908 GlobalConnect.prototype.canStartConnect = function(startTarget) {
57909 return this._rules.allowed('connection.start', { source: startTarget });
57910 };
57911
57912 var GlobalConnectModule = {
57913 __depends__: [
57914 ConnectModule,
57915 RulesModule$1,
57916 DraggingModule,
57917 ToolManagerModule,
57918 MouseModule
57919 ],
57920 globalConnect: [ 'type', GlobalConnect ]
57921 };
57922
57923 /**
57924 * A palette provider for BPMN 2.0 elements.
57925 */
57926 function PaletteProvider(
57927 palette, create, elementFactory,
57928 spaceTool, lassoTool, handTool,
57929 globalConnect, translate) {
57930
57931 this._palette = palette;
57932 this._create = create;
57933 this._elementFactory = elementFactory;
57934 this._spaceTool = spaceTool;
57935 this._lassoTool = lassoTool;
57936 this._handTool = handTool;
57937 this._globalConnect = globalConnect;
57938 this._translate = translate;
57939
57940 palette.registerProvider(this);
57941 }
57942
57943 PaletteProvider.$inject = [
57944 'palette',
57945 'create',
57946 'elementFactory',
57947 'spaceTool',
57948 'lassoTool',
57949 'handTool',
57950 'globalConnect',
57951 'translate'
57952 ];
57953
57954
57955 PaletteProvider.prototype.getPaletteEntries = function(element) {
57956
57957 var actions = {},
57958 create = this._create,
57959 elementFactory = this._elementFactory,
57960 spaceTool = this._spaceTool,
57961 lassoTool = this._lassoTool,
57962 handTool = this._handTool,
57963 globalConnect = this._globalConnect,
57964 translate = this._translate;
57965
57966 function createAction(type, group, className, title, options) {
57967
57968 function createListener(event) {
57969 var shape = elementFactory.createShape(assign({ type: type }, options));
57970
57971 if (options) {
57972 var di = getDi(shape);
57973 di.isExpanded = options.isExpanded;
57974 }
57975
57976 create.start(event, shape);
57977 }
57978
57979 var shortType = type.replace(/^bpmn:/, '');
57980
57981 return {
57982 group: group,
57983 className: className,
57984 title: title || translate('Create {type}', { type: shortType }),
57985 action: {
57986 dragstart: createListener,
57987 click: createListener
57988 }
57989 };
57990 }
57991
57992 function createSubprocess(event) {
57993 var subProcess = elementFactory.createShape({
57994 type: 'bpmn:SubProcess',
57995 x: 0,
57996 y: 0,
57997 isExpanded: true
57998 });
57999
58000 var startEvent = elementFactory.createShape({
58001 type: 'bpmn:StartEvent',
58002 x: 40,
58003 y: 82,
58004 parent: subProcess
58005 });
58006
58007 create.start(event, [ subProcess, startEvent ], {
58008 hints: {
58009 autoSelect: [ subProcess ]
58010 }
58011 });
58012 }
58013
58014 function createParticipant(event) {
58015 create.start(event, elementFactory.createParticipantShape());
58016 }
58017
58018 assign(actions, {
58019 'hand-tool': {
58020 group: 'tools',
58021 className: 'bpmn-icon-hand-tool',
58022 title: translate('Activate the hand tool'),
58023 action: {
58024 click: function(event) {
58025 handTool.activateHand(event);
58026 }
58027 }
58028 },
58029 'lasso-tool': {
58030 group: 'tools',
58031 className: 'bpmn-icon-lasso-tool',
58032 title: translate('Activate the lasso tool'),
58033 action: {
58034 click: function(event) {
58035 lassoTool.activateSelection(event);
58036 }
58037 }
58038 },
58039 'space-tool': {
58040 group: 'tools',
58041 className: 'bpmn-icon-space-tool',
58042 title: translate('Activate the create/remove space tool'),
58043 action: {
58044 click: function(event) {
58045 spaceTool.activateSelection(event);
58046 }
58047 }
58048 },
58049 'global-connect-tool': {
58050 group: 'tools',
58051 className: 'bpmn-icon-connection-multi',
58052 title: translate('Activate the global connect tool'),
58053 action: {
58054 click: function(event) {
58055 globalConnect.start(event);
58056 }
58057 }
58058 },
58059 'tool-separator': {
58060 group: 'tools',
58061 separator: true
58062 },
58063 'create.start-event': createAction(
58064 'bpmn:StartEvent', 'event', 'bpmn-icon-start-event-none',
58065 translate('Create StartEvent')
58066 ),
58067 'create.intermediate-event': createAction(
58068 'bpmn:IntermediateThrowEvent', 'event', 'bpmn-icon-intermediate-event-none',
58069 translate('Create Intermediate/Boundary Event')
58070 ),
58071 'create.end-event': createAction(
58072 'bpmn:EndEvent', 'event', 'bpmn-icon-end-event-none',
58073 translate('Create EndEvent')
58074 ),
58075 'create.exclusive-gateway': createAction(
58076 'bpmn:ExclusiveGateway', 'gateway', 'bpmn-icon-gateway-none',
58077 translate('Create Gateway')
58078 ),
58079 'create.task': createAction(
58080 'bpmn:Task', 'activity', 'bpmn-icon-task',
58081 translate('Create Task')
58082 ),
58083 'create.data-object': createAction(
58084 'bpmn:DataObjectReference', 'data-object', 'bpmn-icon-data-object',
58085 translate('Create DataObjectReference')
58086 ),
58087 'create.data-store': createAction(
58088 'bpmn:DataStoreReference', 'data-store', 'bpmn-icon-data-store',
58089 translate('Create DataStoreReference')
58090 ),
58091 'create.subprocess-expanded': {
58092 group: 'activity',
58093 className: 'bpmn-icon-subprocess-expanded',
58094 title: translate('Create expanded SubProcess'),
58095 action: {
58096 dragstart: createSubprocess,
58097 click: createSubprocess
58098 }
58099 },
58100 'create.participant-expanded': {
58101 group: 'collaboration',
58102 className: 'bpmn-icon-participant',
58103 title: translate('Create Pool/Participant'),
58104 action: {
58105 dragstart: createParticipant,
58106 click: createParticipant
58107 }
58108 },
58109 'create.group': createAction(
58110 'bpmn:Group', 'artifact', 'bpmn-icon-group',
58111 translate('Create Group')
58112 ),
58113 });
58114
58115 return actions;
58116 };
58117
58118 var PaletteModule = {
58119 __depends__: [
58120 PaletteModule$1,
58121 CreateModule,
58122 SpaceToolModule,
58123 LassoToolModule,
58124 HandToolModule,
58125 GlobalConnectModule,
58126 translate
58127 ],
58128 __init__: [ 'paletteProvider' ],
58129 paletteProvider: [ 'type', PaletteProvider ]
58130 };
58131
58132 var LOW_PRIORITY = 250;
58133
58134
58135 function BpmnReplacePreview(
58136 eventBus, elementRegistry, elementFactory,
58137 canvas, previewSupport) {
58138
58139 CommandInterceptor.call(this, eventBus);
58140
58141 /**
58142 * Replace the visuals of all elements in the context which can be replaced
58143 *
58144 * @param {Object} context
58145 */
58146 function replaceVisual(context) {
58147
58148 var replacements = context.canExecute.replacements;
58149
58150 forEach$2(replacements, function(replacement) {
58151
58152 var id = replacement.oldElementId;
58153
58154 var newElement = {
58155 type: replacement.newElementType
58156 };
58157
58158 // if the visual of the element is already replaced
58159 if (context.visualReplacements[id]) {
58160 return;
58161 }
58162
58163 var element = elementRegistry.get(id);
58164
58165 assign(newElement, { x: element.x, y: element.y });
58166
58167 // create a temporary shape
58168 var tempShape = elementFactory.createShape(newElement);
58169
58170 canvas.addShape(tempShape, element.parent);
58171
58172 // select the original SVG element related to the element and hide it
58173 var gfx = query$1('[data-element-id="' + cssEscape(element.id) + '"]', context.dragGroup);
58174
58175 if (gfx) {
58176 attr$1(gfx, { display: 'none' });
58177 }
58178
58179 // clone the gfx of the temporary shape and add it to the drag group
58180 var dragger = previewSupport.addDragger(tempShape, context.dragGroup);
58181
58182 context.visualReplacements[id] = dragger;
58183
58184 canvas.removeShape(tempShape);
58185 });
58186 }
58187
58188 /**
58189 * Restore the original visuals of the previously replaced elements
58190 *
58191 * @param {Object} context
58192 */
58193 function restoreVisual(context) {
58194
58195 var visualReplacements = context.visualReplacements;
58196
58197 forEach$2(visualReplacements, function(dragger, id) {
58198
58199 var originalGfx = query$1('[data-element-id="' + cssEscape(id) + '"]', context.dragGroup);
58200
58201 if (originalGfx) {
58202 attr$1(originalGfx, { display: 'inline' });
58203 }
58204
58205 dragger.remove();
58206
58207 if (visualReplacements[id]) {
58208 delete visualReplacements[id];
58209 }
58210 });
58211 }
58212
58213 eventBus.on('shape.move.move', LOW_PRIORITY, function(event) {
58214
58215 var context = event.context,
58216 canExecute = context.canExecute;
58217
58218 if (!context.visualReplacements) {
58219 context.visualReplacements = {};
58220 }
58221
58222 if (canExecute && canExecute.replacements) {
58223 replaceVisual(context);
58224 } else {
58225 restoreVisual(context);
58226 }
58227 });
58228 }
58229
58230 BpmnReplacePreview.$inject = [
58231 'eventBus',
58232 'elementRegistry',
58233 'elementFactory',
58234 'canvas',
58235 'previewSupport'
58236 ];
58237
58238 inherits$1(BpmnReplacePreview, CommandInterceptor);
58239
58240 var ReplacePreviewModule = {
58241 __depends__: [
58242 PreviewSupportModule
58243 ],
58244 __init__: [ 'bpmnReplacePreview' ],
58245 bpmnReplacePreview: [ 'type', BpmnReplacePreview ]
58246 };
58247
58248 var HIGHER_PRIORITY$2 = 1250;
58249
58250 var BOUNDARY_TO_HOST_THRESHOLD = 40;
58251
58252 var TARGET_BOUNDS_PADDING = 20,
58253 TASK_BOUNDS_PADDING = 10;
58254
58255 var TARGET_CENTER_PADDING = 20;
58256
58257 var AXES = [ 'x', 'y' ];
58258
58259 var abs = Math.abs;
58260
58261 /**
58262 * Snap during connect.
58263 *
58264 * @param {EventBus} eventBus
58265 */
58266 function BpmnConnectSnapping(eventBus) {
58267 eventBus.on([
58268 'connect.hover',
58269 'connect.move',
58270 'connect.end',
58271 ], HIGHER_PRIORITY$2, function(event) {
58272 var context = event.context,
58273 canExecute = context.canExecute,
58274 start = context.start,
58275 hover = context.hover,
58276 source = context.source,
58277 target = context.target;
58278
58279 // do NOT snap on CMD
58280 if (event.originalEvent && isCmd(event.originalEvent)) {
58281 return;
58282 }
58283
58284 if (!context.initialConnectionStart) {
58285 context.initialConnectionStart = context.connectionStart;
58286 }
58287
58288 // snap hover
58289 if (canExecute && hover) {
58290 snapToShape(event, hover, getTargetBoundsPadding(hover));
58291 }
58292
58293 if (hover && isAnyType(canExecute, [
58294 'bpmn:Association',
58295 'bpmn:DataInputAssociation',
58296 'bpmn:DataOutputAssociation',
58297 'bpmn:SequenceFlow'
58298 ])) {
58299 context.connectionStart = mid$2(start);
58300
58301 // snap hover
58302 if (isAny(hover, [ 'bpmn:Event', 'bpmn:Gateway' ])) {
58303 snapToPosition(event, mid$2(hover));
58304 }
58305
58306 // snap hover
58307 if (isAny(hover, [ 'bpmn:Task', 'bpmn:SubProcess' ])) {
58308 snapToTargetMid(event, hover);
58309 }
58310
58311 // snap source and target
58312 if (is$1(source, 'bpmn:BoundaryEvent') && target === source.host) {
58313 snapBoundaryEventLoop(event);
58314 }
58315
58316 } else if (isType(canExecute, 'bpmn:MessageFlow')) {
58317
58318 if (is$1(start, 'bpmn:Event')) {
58319
58320 // snap start
58321 context.connectionStart = mid$2(start);
58322 }
58323
58324 if (is$1(hover, 'bpmn:Event')) {
58325
58326 // snap hover
58327 snapToPosition(event, mid$2(hover));
58328 }
58329
58330 } else {
58331
58332 // un-snap source
58333 context.connectionStart = context.initialConnectionStart;
58334 }
58335 });
58336 }
58337
58338 BpmnConnectSnapping.$inject = [ 'eventBus' ];
58339
58340
58341 // helpers //////////
58342
58343 // snap to target if event in target
58344 function snapToShape(event, target, padding) {
58345 AXES.forEach(function(axis) {
58346 var dimensionForAxis = getDimensionForAxis(axis, target);
58347
58348 if (event[ axis ] < target[ axis ] + padding) {
58349 setSnapped(event, axis, target[ axis ] + padding);
58350 } else if (event[ axis ] > target[ axis ] + dimensionForAxis - padding) {
58351 setSnapped(event, axis, target[ axis ] + dimensionForAxis - padding);
58352 }
58353 });
58354 }
58355
58356 // snap to target mid if event in target mid
58357 function snapToTargetMid(event, target) {
58358 var targetMid = mid$2(target);
58359
58360 AXES.forEach(function(axis) {
58361 if (isMid(event, target, axis)) {
58362 setSnapped(event, axis, targetMid[ axis ]);
58363 }
58364 });
58365 }
58366
58367 // snap to prevent loop overlapping boundary event
58368 function snapBoundaryEventLoop(event) {
58369 var context = event.context,
58370 source = context.source,
58371 target = context.target;
58372
58373 if (isReverse(context)) {
58374 return;
58375 }
58376
58377 var sourceMid = mid$2(source),
58378 orientation = getOrientation(sourceMid, target, -10),
58379 axes = [];
58380
58381 if (/top|bottom/.test(orientation)) {
58382 axes.push('x');
58383 }
58384
58385 if (/left|right/.test(orientation)) {
58386 axes.push('y');
58387 }
58388
58389 axes.forEach(function(axis) {
58390 var coordinate = event[ axis ], newCoordinate;
58391
58392 if (abs(coordinate - sourceMid[ axis ]) < BOUNDARY_TO_HOST_THRESHOLD) {
58393 if (coordinate > sourceMid[ axis ]) {
58394 newCoordinate = sourceMid[ axis ] + BOUNDARY_TO_HOST_THRESHOLD;
58395 }
58396 else {
58397 newCoordinate = sourceMid[ axis ] - BOUNDARY_TO_HOST_THRESHOLD;
58398 }
58399
58400 setSnapped(event, axis, newCoordinate);
58401 }
58402 });
58403 }
58404
58405 function snapToPosition(event, position) {
58406 setSnapped(event, 'x', position.x);
58407 setSnapped(event, 'y', position.y);
58408 }
58409
58410 function isType(attrs, type) {
58411 return attrs && attrs.type === type;
58412 }
58413
58414 function isAnyType(attrs, types) {
58415 return some(types, function(type) {
58416 return isType(attrs, type);
58417 });
58418 }
58419
58420 function getDimensionForAxis(axis, element) {
58421 return axis === 'x' ? element.width : element.height;
58422 }
58423
58424 function getTargetBoundsPadding(target) {
58425 if (is$1(target, 'bpmn:Task')) {
58426 return TASK_BOUNDS_PADDING;
58427 } else {
58428 return TARGET_BOUNDS_PADDING;
58429 }
58430 }
58431
58432 function isMid(event, target, axis) {
58433 return event[ axis ] > target[ axis ] + TARGET_CENTER_PADDING
58434 && event[ axis ] < target[ axis ] + getDimensionForAxis(axis, target) - TARGET_CENTER_PADDING;
58435 }
58436
58437 function isReverse(context) {
58438 var hover = context.hover,
58439 source = context.source;
58440
58441 return hover && source && hover === source;
58442 }
58443
58444 /**
58445 * A snap context, containing the (possibly incomplete)
58446 * mappings of drop targets (to identify the snapping)
58447 * to computed snap points.
58448 */
58449 function SnapContext() {
58450
58451 /**
58452 * Map<String, SnapPoints> mapping drop targets to
58453 * a list of possible snappings.
58454 *
58455 * @type {Object}
58456 */
58457 this._targets = {};
58458
58459 /**
58460 * Map<String, Point> initial positioning of element
58461 * regarding various snap directions.
58462 *
58463 * @type {Object}
58464 */
58465 this._snapOrigins = {};
58466
58467 /**
58468 * List of snap locations
58469 *
58470 * @type {Array<string>}
58471 */
58472 this._snapLocations = [];
58473
58474 /**
58475 * Map<String, Array<Point>> of default snapping locations
58476 *
58477 * @type {Object}
58478 */
58479 this._defaultSnaps = {};
58480 }
58481
58482
58483 SnapContext.prototype.getSnapOrigin = function(snapLocation) {
58484 return this._snapOrigins[snapLocation];
58485 };
58486
58487
58488 SnapContext.prototype.setSnapOrigin = function(snapLocation, initialValue) {
58489 this._snapOrigins[snapLocation] = initialValue;
58490
58491 if (this._snapLocations.indexOf(snapLocation) === -1) {
58492 this._snapLocations.push(snapLocation);
58493 }
58494 };
58495
58496
58497 SnapContext.prototype.addDefaultSnap = function(type, point) {
58498
58499 var snapValues = this._defaultSnaps[type];
58500
58501 if (!snapValues) {
58502 snapValues = this._defaultSnaps[type] = [];
58503 }
58504
58505 snapValues.push(point);
58506 };
58507
58508 /**
58509 * Return a number of initialized snaps, i.e. snap locations such as
58510 * top-left, mid, bottom-right and so forth.
58511 *
58512 * @return {Array<string>} snapLocations
58513 */
58514 SnapContext.prototype.getSnapLocations = function() {
58515 return this._snapLocations;
58516 };
58517
58518 /**
58519 * Set the snap locations for this context.
58520 *
58521 * The order of locations determines precedence.
58522 *
58523 * @param {Array<string>} snapLocations
58524 */
58525 SnapContext.prototype.setSnapLocations = function(snapLocations) {
58526 this._snapLocations = snapLocations;
58527 };
58528
58529 /**
58530 * Get snap points for a given target
58531 *
58532 * @param {Element|string} target
58533 */
58534 SnapContext.prototype.pointsForTarget = function(target) {
58535
58536 var targetId = target.id || target;
58537
58538 var snapPoints = this._targets[targetId];
58539
58540 if (!snapPoints) {
58541 snapPoints = this._targets[targetId] = new SnapPoints();
58542 snapPoints.initDefaults(this._defaultSnaps);
58543 }
58544
58545 return snapPoints;
58546 };
58547
58548
58549 /**
58550 * Creates the snap points and initializes them with the
58551 * given default values.
58552 *
58553 * @param {Object<string, Array<Point>>} [defaultPoints]
58554 */
58555 function SnapPoints(defaultSnaps) {
58556
58557 /**
58558 * Map<String, Map<(x|y), Array<number>>> mapping snap locations,
58559 * i.e. top-left, bottom-right, center to actual snap values.
58560 *
58561 * @type {Object}
58562 */
58563 this._snapValues = {};
58564 }
58565
58566 SnapPoints.prototype.add = function(snapLocation, point) {
58567
58568 var snapValues = this._snapValues[snapLocation];
58569
58570 if (!snapValues) {
58571 snapValues = this._snapValues[snapLocation] = { x: [], y: [] };
58572 }
58573
58574 if (snapValues.x.indexOf(point.x) === -1) {
58575 snapValues.x.push(point.x);
58576 }
58577
58578 if (snapValues.y.indexOf(point.y) === -1) {
58579 snapValues.y.push(point.y);
58580 }
58581 };
58582
58583
58584 SnapPoints.prototype.snap = function(point, snapLocation, axis, tolerance) {
58585 var snappingValues = this._snapValues[snapLocation];
58586
58587 return snappingValues && snapTo(point[axis], snappingValues[axis], tolerance);
58588 };
58589
58590 /**
58591 * Initialize a number of default snapping points.
58592 *
58593 * @param {Object} defaultSnaps
58594 */
58595 SnapPoints.prototype.initDefaults = function(defaultSnaps) {
58596
58597 var self = this;
58598
58599 forEach$2(defaultSnaps || {}, function(snapPoints, snapLocation) {
58600 forEach$2(snapPoints, function(point) {
58601 self.add(snapLocation, point);
58602 });
58603 });
58604 };
58605
58606 var HIGHER_PRIORITY$1 = 1250;
58607
58608
58609 /**
58610 * Snap during create and move.
58611 *
58612 * @param {EventBus} elementRegistry
58613 * @param {EventBus} eventBus
58614 * @param {Snapping} snapping
58615 */
58616 function CreateMoveSnapping(elementRegistry, eventBus, snapping) {
58617 var self = this;
58618
58619 this._elementRegistry = elementRegistry;
58620
58621 eventBus.on([
58622 'create.start',
58623 'shape.move.start'
58624 ], function(event) {
58625 self.initSnap(event);
58626 });
58627
58628 eventBus.on([
58629 'create.move',
58630 'create.end',
58631 'shape.move.move',
58632 'shape.move.end'
58633 ], HIGHER_PRIORITY$1, function(event) {
58634 var context = event.context,
58635 shape = context.shape,
58636 snapContext = context.snapContext,
58637 target = context.target;
58638
58639 if (event.originalEvent && isCmd(event.originalEvent)) {
58640 return;
58641 }
58642
58643 if (isSnapped(event) || !target) {
58644 return;
58645 }
58646
58647 var snapPoints = snapContext.pointsForTarget(target);
58648
58649 if (!snapPoints.initialized) {
58650 snapPoints = self.addSnapTargetPoints(snapPoints, shape, target);
58651
58652 snapPoints.initialized = true;
58653 }
58654
58655 snapping.snap(event, snapPoints);
58656 });
58657
58658 eventBus.on([
58659 'create.cleanup',
58660 'shape.move.cleanup'
58661 ], function() {
58662 snapping.hide();
58663 });
58664 }
58665
58666 CreateMoveSnapping.$inject = [
58667 'elementRegistry',
58668 'eventBus',
58669 'snapping'
58670 ];
58671
58672 CreateMoveSnapping.prototype.initSnap = function(event) {
58673 var elementRegistry = this._elementRegistry;
58674
58675 var context = event.context,
58676 shape = context.shape,
58677 snapContext = context.snapContext;
58678
58679 if (!snapContext) {
58680 snapContext = context.snapContext = new SnapContext();
58681 }
58682
58683 var shapeMid;
58684
58685 if (elementRegistry.get(shape.id)) {
58686
58687 // move
58688 shapeMid = mid$2(shape, event);
58689 } else {
58690
58691 // create
58692 shapeMid = {
58693 x: event.x + mid$2(shape).x,
58694 y: event.y + mid$2(shape).y
58695 };
58696 }
58697
58698 var shapeTopLeft = {
58699 x: shapeMid.x - shape.width / 2,
58700 y: shapeMid.y - shape.height / 2
58701 },
58702 shapeBottomRight = {
58703 x: shapeMid.x + shape.width / 2,
58704 y: shapeMid.y + shape.height / 2
58705 };
58706
58707 snapContext.setSnapOrigin('mid', {
58708 x: shapeMid.x - event.x,
58709 y: shapeMid.y - event.y
58710 });
58711
58712 // snap labels to mid only
58713 if (isLabel$1(shape)) {
58714 return snapContext;
58715 }
58716
58717 snapContext.setSnapOrigin('top-left', {
58718 x: shapeTopLeft.x - event.x,
58719 y: shapeTopLeft.y - event.y
58720 });
58721
58722 snapContext.setSnapOrigin('bottom-right', {
58723 x: shapeBottomRight.x - event.x,
58724 y: shapeBottomRight.y - event.y
58725 });
58726
58727 return snapContext;
58728 };
58729
58730 CreateMoveSnapping.prototype.addSnapTargetPoints = function(snapPoints, shape, target) {
58731 var snapTargets = this.getSnapTargets(shape, target);
58732
58733 forEach$2(snapTargets, function(snapTarget) {
58734
58735 // handle labels
58736 if (isLabel$1(snapTarget)) {
58737
58738 if (isLabel$1(shape)) {
58739 snapPoints.add('mid', mid$2(snapTarget));
58740 }
58741
58742 return;
58743 }
58744
58745 // handle connections
58746 if (isConnection$1(snapTarget)) {
58747
58748 // ignore single segment connections
58749 if (snapTarget.waypoints.length < 3) {
58750 return;
58751 }
58752
58753 // ignore first and last waypoint
58754 var waypoints = snapTarget.waypoints.slice(1, -1);
58755
58756 forEach$2(waypoints, function(waypoint) {
58757 snapPoints.add('mid', waypoint);
58758 });
58759
58760 return;
58761 }
58762
58763 // handle shapes
58764 snapPoints.add('mid', mid$2(snapTarget));
58765 });
58766
58767 if (!isNumber(shape.x) || !isNumber(shape.y)) {
58768 return snapPoints;
58769 }
58770
58771 // snap to original position when moving
58772 if (this._elementRegistry.get(shape.id)) {
58773 snapPoints.add('mid', mid$2(shape));
58774 }
58775
58776 return snapPoints;
58777 };
58778
58779 CreateMoveSnapping.prototype.getSnapTargets = function(shape, target) {
58780 return getChildren(target).filter(function(child) {
58781 return !isHidden$1(child);
58782 });
58783 };
58784
58785 // helpers //////////
58786
58787 function isConnection$1(element) {
58788 return !!element.waypoints;
58789 }
58790
58791 function isHidden$1(element) {
58792 return !!element.hidden;
58793 }
58794
58795 function isLabel$1(element) {
58796 return !!element.labelTarget;
58797 }
58798
58799 var HIGH_PRIORITY = 1500;
58800
58801
58802 /**
58803 * Snap during create and move.
58804 *
58805 * @param {EventBus} eventBus
58806 * @param {Injector} injector
58807 */
58808 function BpmnCreateMoveSnapping(eventBus, injector) {
58809 injector.invoke(CreateMoveSnapping, this);
58810
58811 // creating first participant
58812 eventBus.on([ 'create.move', 'create.end' ], HIGH_PRIORITY, setSnappedIfConstrained);
58813
58814 // snap boundary events
58815 eventBus.on([
58816 'create.move',
58817 'create.end',
58818 'shape.move.move',
58819 'shape.move.end'
58820 ], HIGH_PRIORITY, function(event) {
58821 var context = event.context,
58822 canExecute = context.canExecute,
58823 target = context.target;
58824
58825 var canAttach = canExecute && (canExecute === 'attach' || canExecute.attach);
58826
58827 if (canAttach && !isSnapped(event)) {
58828 snapBoundaryEvent(event, target);
58829 }
58830 });
58831 }
58832
58833 inherits$1(BpmnCreateMoveSnapping, CreateMoveSnapping);
58834
58835 BpmnCreateMoveSnapping.$inject = [
58836 'eventBus',
58837 'injector'
58838 ];
58839
58840 BpmnCreateMoveSnapping.prototype.initSnap = function(event) {
58841 var snapContext = CreateMoveSnapping.prototype.initSnap.call(this, event);
58842
58843 var shape = event.shape;
58844
58845 var isMove = !!this._elementRegistry.get(shape.id);
58846
58847 // snap to docking points
58848 forEach$2(shape.outgoing, function(connection) {
58849 var docking = connection.waypoints[0];
58850
58851 docking = docking.original || docking;
58852
58853 snapContext.setSnapOrigin(connection.id + '-docking', getDockingSnapOrigin(docking, isMove, event));
58854 });
58855
58856 forEach$2(shape.incoming, function(connection) {
58857 var docking = connection.waypoints[connection.waypoints.length - 1];
58858
58859 docking = docking.original || docking;
58860
58861 snapContext.setSnapOrigin(connection.id + '-docking', getDockingSnapOrigin(docking, isMove, event));
58862 });
58863
58864 if (is$1(shape, 'bpmn:Participant')) {
58865
58866 // snap to borders with higher priority
58867 snapContext.setSnapLocations([ 'top-left', 'bottom-right', 'mid' ]);
58868 }
58869
58870 return snapContext;
58871 };
58872
58873 BpmnCreateMoveSnapping.prototype.addSnapTargetPoints = function(snapPoints, shape, target) {
58874 CreateMoveSnapping.prototype.addSnapTargetPoints.call(this, snapPoints, shape, target);
58875
58876 var snapTargets = this.getSnapTargets(shape, target);
58877
58878 forEach$2(snapTargets, function(snapTarget) {
58879
58880 // handle TRBL alignment
58881 //
58882 // * with container elements
58883 // * with text annotations
58884 if (isContainer(snapTarget) || areAll([ shape, snapTarget ], 'bpmn:TextAnnotation')) {
58885 snapPoints.add('top-left', topLeft(snapTarget));
58886 snapPoints.add('bottom-right', bottomRight(snapTarget));
58887 }
58888 });
58889
58890 var elementRegistry = this._elementRegistry;
58891
58892 // snap to docking points if not create mode
58893 forEach$2(shape.incoming, function(connection) {
58894 if (elementRegistry.get(shape.id)) {
58895
58896 if (!includes(snapTargets, connection.source)) {
58897 snapPoints.add('mid', getMid(connection.source));
58898 }
58899
58900 var docking = connection.waypoints[0];
58901 snapPoints.add(connection.id + '-docking', docking.original || docking);
58902 }
58903 });
58904
58905 forEach$2(shape.outgoing, function(connection) {
58906 if (elementRegistry.get(shape.id)) {
58907
58908 if (!includes(snapTargets, connection.target)) {
58909 snapPoints.add('mid', getMid(connection.target));
58910 }
58911
58912 var docking = connection.waypoints[ connection.waypoints.length - 1 ];
58913
58914 snapPoints.add(connection.id + '-docking', docking.original || docking);
58915 }
58916 });
58917
58918 // add sequence flow parents as snap targets
58919 if (is$1(target, 'bpmn:SequenceFlow')) {
58920 snapPoints = this.addSnapTargetPoints(snapPoints, shape, target.parent);
58921 }
58922
58923 return snapPoints;
58924 };
58925
58926 BpmnCreateMoveSnapping.prototype.getSnapTargets = function(shape, target) {
58927 return CreateMoveSnapping.prototype.getSnapTargets.call(this, shape, target)
58928 .filter(function(snapTarget) {
58929
58930 // do not snap to lanes
58931 return !is$1(snapTarget, 'bpmn:Lane');
58932 });
58933 };
58934
58935 // helpers //////////
58936
58937 function snapBoundaryEvent(event, target) {
58938 var targetTRBL = asTRBL(target);
58939
58940 var direction = getBoundaryAttachment(event, target);
58941
58942 var context = event.context,
58943 shape = context.shape;
58944
58945 var offset;
58946
58947 if (shape.parent) {
58948 offset = { x: 0, y: 0 };
58949 } else {
58950 offset = getMid(shape);
58951 }
58952
58953 if (/top/.test(direction)) {
58954 setSnapped(event, 'y', targetTRBL.top - offset.y);
58955 } else if (/bottom/.test(direction)) {
58956 setSnapped(event, 'y', targetTRBL.bottom - offset.y);
58957 }
58958
58959 if (/left/.test(direction)) {
58960 setSnapped(event, 'x', targetTRBL.left - offset.x);
58961 } else if (/right/.test(direction)) {
58962 setSnapped(event, 'x', targetTRBL.right - offset.x);
58963 }
58964 }
58965
58966 function areAll(elements, type) {
58967 return elements.every(function(el) {
58968 return is$1(el, type);
58969 });
58970 }
58971
58972 function isContainer(element) {
58973 if (is$1(element, 'bpmn:SubProcess') && isExpanded(element)) {
58974 return true;
58975 }
58976
58977 return is$1(element, 'bpmn:Participant');
58978 }
58979
58980
58981 function setSnappedIfConstrained(event) {
58982 var context = event.context,
58983 createConstraints = context.createConstraints;
58984
58985 if (!createConstraints) {
58986 return;
58987 }
58988
58989 var top = createConstraints.top,
58990 right = createConstraints.right,
58991 bottom = createConstraints.bottom,
58992 left = createConstraints.left;
58993
58994 if ((left && left >= event.x) || (right && right <= event.x)) {
58995 setSnapped(event, 'x', event.x);
58996 }
58997
58998 if ((top && top >= event.y) || (bottom && bottom <= event.y)) {
58999 setSnapped(event, 'y', event.y);
59000 }
59001 }
59002
59003 function includes(array, value) {
59004 return array.indexOf(value) !== -1;
59005 }
59006
59007 function getDockingSnapOrigin(docking, isMove, event) {
59008 return isMove ? (
59009 {
59010 x: docking.x - event.x,
59011 y: docking.y - event.y
59012 }
59013 ) : {
59014 x: docking.x,
59015 y: docking.y
59016 };
59017 }
59018
59019 var HIGHER_PRIORITY = 1250;
59020
59021
59022 /**
59023 * Snap during resize.
59024 *
59025 * @param {EventBus} eventBus
59026 * @param {Snapping} snapping
59027 */
59028 function ResizeSnapping(eventBus, snapping) {
59029 var self = this;
59030
59031 eventBus.on([ 'resize.start' ], function(event) {
59032 self.initSnap(event);
59033 });
59034
59035 eventBus.on([
59036 'resize.move',
59037 'resize.end',
59038 ], HIGHER_PRIORITY, function(event) {
59039 var context = event.context,
59040 shape = context.shape,
59041 parent = shape.parent,
59042 direction = context.direction,
59043 snapContext = context.snapContext;
59044
59045 if (event.originalEvent && isCmd(event.originalEvent)) {
59046 return;
59047 }
59048
59049 if (isSnapped(event)) {
59050 return;
59051 }
59052
59053 var snapPoints = snapContext.pointsForTarget(parent);
59054
59055 if (!snapPoints.initialized) {
59056 snapPoints = self.addSnapTargetPoints(snapPoints, shape, parent, direction);
59057
59058 snapPoints.initialized = true;
59059 }
59060
59061 if (isHorizontal(direction)) {
59062 setSnapped(event, 'x', event.x);
59063 }
59064
59065 if (isVertical(direction)) {
59066 setSnapped(event, 'y', event.y);
59067 }
59068
59069 snapping.snap(event, snapPoints);
59070 });
59071
59072 eventBus.on([ 'resize.cleanup' ], function() {
59073 snapping.hide();
59074 });
59075 }
59076
59077 ResizeSnapping.prototype.initSnap = function(event) {
59078 var context = event.context,
59079 shape = context.shape,
59080 direction = context.direction,
59081 snapContext = context.snapContext;
59082
59083 if (!snapContext) {
59084 snapContext = context.snapContext = new SnapContext();
59085 }
59086
59087 var snapOrigin = getSnapOrigin(shape, direction);
59088
59089 snapContext.setSnapOrigin('corner', {
59090 x: snapOrigin.x - event.x,
59091 y: snapOrigin.y - event.y
59092 });
59093
59094 return snapContext;
59095 };
59096
59097 ResizeSnapping.prototype.addSnapTargetPoints = function(snapPoints, shape, target, direction) {
59098 var snapTargets = this.getSnapTargets(shape, target);
59099
59100 forEach$2(snapTargets, function(snapTarget) {
59101 snapPoints.add('corner', bottomRight(snapTarget));
59102 snapPoints.add('corner', topLeft(snapTarget));
59103 });
59104
59105 snapPoints.add('corner', getSnapOrigin(shape, direction));
59106
59107 return snapPoints;
59108 };
59109
59110 ResizeSnapping.$inject = [
59111 'eventBus',
59112 'snapping'
59113 ];
59114
59115 ResizeSnapping.prototype.getSnapTargets = function(shape, target) {
59116 return getChildren(target).filter(function(child) {
59117 return !isAttached(child, shape)
59118 && !isConnection(child)
59119 && !isHidden(child)
59120 && !isLabel(child);
59121 });
59122 };
59123
59124 // helpers //////////
59125
59126 function getSnapOrigin(shape, direction) {
59127 var mid = getMid(shape),
59128 trbl = asTRBL(shape);
59129
59130 var snapOrigin = {
59131 x: mid.x,
59132 y: mid.y
59133 };
59134
59135 if (direction.indexOf('n') !== -1) {
59136 snapOrigin.y = trbl.top;
59137 } else if (direction.indexOf('s') !== -1) {
59138 snapOrigin.y = trbl.bottom;
59139 }
59140
59141 if (direction.indexOf('e') !== -1) {
59142 snapOrigin.x = trbl.right;
59143 } else if (direction.indexOf('w') !== -1) {
59144 snapOrigin.x = trbl.left;
59145 }
59146
59147 return snapOrigin;
59148 }
59149
59150 function isAttached(element, host) {
59151 return element.host === host;
59152 }
59153
59154 function isConnection(element) {
59155 return !!element.waypoints;
59156 }
59157
59158 function isHidden(element) {
59159 return !!element.hidden;
59160 }
59161
59162 function isLabel(element) {
59163 return !!element.labelTarget;
59164 }
59165
59166 function isHorizontal(direction) {
59167 return direction === 'n' || direction === 's';
59168 }
59169
59170 function isVertical(direction) {
59171 return direction === 'e' || direction === 'w';
59172 }
59173
59174 var SNAP_TOLERANCE = 7;
59175
59176 var SNAP_LINE_HIDE_DELAY = 1000;
59177
59178
59179 /**
59180 * Generic snapping feature.
59181 *
59182 * @param {EventBus} eventBus
59183 * @param {Canvas} canvas
59184 */
59185 function Snapping(canvas) {
59186 this._canvas = canvas;
59187
59188 // delay hide by 1000 seconds since last snap
59189 this._asyncHide = debounce(bind(this.hide, this), SNAP_LINE_HIDE_DELAY);
59190 }
59191
59192 Snapping.$inject = [ 'canvas' ];
59193
59194 /**
59195 * Snap an event to given snap points.
59196 *
59197 * @param {Event} event
59198 * @param {SnapPoints} snapPoints
59199 */
59200 Snapping.prototype.snap = function(event, snapPoints) {
59201 var context = event.context,
59202 snapContext = context.snapContext,
59203 snapLocations = snapContext.getSnapLocations();
59204
59205 var snapping = {
59206 x: isSnapped(event, 'x'),
59207 y: isSnapped(event, 'y')
59208 };
59209
59210 forEach$2(snapLocations, function(location) {
59211 var snapOrigin = snapContext.getSnapOrigin(location);
59212
59213 var snapCurrent = {
59214 x: event.x + snapOrigin.x,
59215 y: event.y + snapOrigin.y
59216 };
59217
59218 // snap both axis if not snapped already
59219 forEach$2([ 'x', 'y' ], function(axis) {
59220 var locationSnapping;
59221
59222 if (!snapping[axis]) {
59223 locationSnapping = snapPoints.snap(snapCurrent, location, axis, SNAP_TOLERANCE);
59224
59225 if (locationSnapping !== undefined) {
59226 snapping[axis] = {
59227 value: locationSnapping,
59228 originValue: locationSnapping - snapOrigin[axis]
59229 };
59230 }
59231 }
59232 });
59233
59234 // no need to continue snapping
59235 if (snapping.x && snapping.y) {
59236 return false;
59237 }
59238 });
59239
59240 // show snap lines
59241 this.showSnapLine('vertical', snapping.x && snapping.x.value);
59242 this.showSnapLine('horizontal', snapping.y && snapping.y.value);
59243
59244 // snap event
59245 forEach$2([ 'x', 'y' ], function(axis) {
59246 var axisSnapping = snapping[axis];
59247
59248 if (isObject(axisSnapping)) {
59249 setSnapped(event, axis, axisSnapping.originValue);
59250 }
59251 });
59252 };
59253
59254 Snapping.prototype._createLine = function(orientation) {
59255 var root = this._canvas.getLayer('snap');
59256
59257 var line = create$1('path');
59258
59259 attr$1(line, { d: 'M0,0 L0,0' });
59260
59261 classes$1(line).add('djs-snap-line');
59262
59263 append(root, line);
59264
59265 return {
59266 update: function(position) {
59267
59268 if (!isNumber(position)) {
59269 attr$1(line, { display: 'none' });
59270 } else {
59271 if (orientation === 'horizontal') {
59272 attr$1(line, {
59273 d: 'M-100000,' + position + ' L+100000,' + position,
59274 display: ''
59275 });
59276 } else {
59277 attr$1(line, {
59278 d: 'M ' + position + ',-100000 L ' + position + ', +100000',
59279 display: ''
59280 });
59281 }
59282 }
59283 }
59284 };
59285 };
59286
59287 Snapping.prototype._createSnapLines = function() {
59288 this._snapLines = {
59289 horizontal: this._createLine('horizontal'),
59290 vertical: this._createLine('vertical')
59291 };
59292 };
59293
59294 Snapping.prototype.showSnapLine = function(orientation, position) {
59295
59296 var line = this.getSnapLine(orientation);
59297
59298 if (line) {
59299 line.update(position);
59300 }
59301
59302 this._asyncHide();
59303 };
59304
59305 Snapping.prototype.getSnapLine = function(orientation) {
59306 if (!this._snapLines) {
59307 this._createSnapLines();
59308 }
59309
59310 return this._snapLines[orientation];
59311 };
59312
59313 Snapping.prototype.hide = function() {
59314 forEach$2(this._snapLines, function(snapLine) {
59315 snapLine.update();
59316 });
59317 };
59318
59319 var SnappingModule$1 = {
59320 __init__: [
59321 'createMoveSnapping',
59322 'resizeSnapping',
59323 'snapping'
59324 ],
59325 createMoveSnapping: [ 'type', CreateMoveSnapping ],
59326 resizeSnapping: [ 'type', ResizeSnapping ],
59327 snapping: [ 'type', Snapping ]
59328 };
59329
59330 var SnappingModule = {
59331 __depends__: [ SnappingModule$1 ],
59332 __init__: [
59333 'connectSnapping',
59334 'createMoveSnapping'
59335 ],
59336 connectSnapping: [ 'type', BpmnConnectSnapping ],
59337 createMoveSnapping: [ 'type', BpmnCreateMoveSnapping ]
59338 };
59339
59340 /**
59341 * Provides searching infrastructure
59342 */
59343 function SearchPad(canvas, eventBus, overlays, selection) {
59344 this._open = false;
59345 this._results = [];
59346 this._eventMaps = [];
59347
59348 this._canvas = canvas;
59349 this._eventBus = eventBus;
59350 this._overlays = overlays;
59351 this._selection = selection;
59352
59353 // setup elements
59354 this._container = domify(SearchPad.BOX_HTML);
59355 this._searchInput = query(SearchPad.INPUT_SELECTOR, this._container);
59356 this._resultsContainer = query(SearchPad.RESULTS_CONTAINER_SELECTOR, this._container);
59357
59358 // attach search pad
59359 this._canvas.getContainer().appendChild(this._container);
59360
59361 // cleanup on destroy
59362 eventBus.on([ 'canvas.destroy', 'diagram.destroy' ], this.close, this);
59363 }
59364
59365
59366 SearchPad.$inject = [
59367 'canvas',
59368 'eventBus',
59369 'overlays',
59370 'selection'
59371 ];
59372
59373
59374 /**
59375 * Binds and keeps track of all event listereners
59376 */
59377 SearchPad.prototype._bindEvents = function() {
59378 var self = this;
59379
59380 function listen(el, selector, type, fn) {
59381 self._eventMaps.push({
59382 el: el,
59383 type: type,
59384 listener: delegate.bind(el, selector, type, fn)
59385 });
59386 }
59387
59388 // close search on clicking anywhere outside
59389 listen(document, 'html', 'click', function(e) {
59390 self.close();
59391 });
59392
59393 // stop event from propagating and closing search
59394 // focus on input
59395 listen(this._container, SearchPad.INPUT_SELECTOR, 'click', function(e) {
59396 e.stopPropagation();
59397 e.delegateTarget.focus();
59398 });
59399
59400 // preselect result on hover
59401 listen(this._container, SearchPad.RESULT_SELECTOR, 'mouseover', function(e) {
59402 e.stopPropagation();
59403 self._scrollToNode(e.delegateTarget);
59404 self._preselect(e.delegateTarget);
59405 });
59406
59407 // selects desired result on mouse click
59408 listen(this._container, SearchPad.RESULT_SELECTOR, 'click', function(e) {
59409 e.stopPropagation();
59410 self._select(e.delegateTarget);
59411 });
59412
59413 // prevent cursor in input from going left and right when using up/down to
59414 // navigate results
59415 listen(this._container, SearchPad.INPUT_SELECTOR, 'keydown', function(e) {
59416
59417 // up
59418 if (e.keyCode === 38) {
59419 e.preventDefault();
59420 }
59421
59422 // down
59423 if (e.keyCode === 40) {
59424 e.preventDefault();
59425 }
59426 });
59427
59428 // handle keyboard input
59429 listen(this._container, SearchPad.INPUT_SELECTOR, 'keyup', function(e) {
59430
59431 // escape
59432 if (e.keyCode === 27) {
59433 return self.close();
59434 }
59435
59436 // enter
59437 if (e.keyCode === 13) {
59438 var selected = self._getCurrentResult();
59439
59440 return selected ? self._select(selected) : self.close();
59441 }
59442
59443 // up
59444 if (e.keyCode === 38) {
59445 return self._scrollToDirection(true);
59446 }
59447
59448 // down
59449 if (e.keyCode === 40) {
59450 return self._scrollToDirection();
59451 }
59452
59453 // left && right
59454 // do not search while navigating text input
59455 if (e.keyCode === 37 || e.keyCode === 39) {
59456 return;
59457 }
59458
59459 // anything else
59460 self._search(e.delegateTarget.value);
59461 });
59462 };
59463
59464
59465 /**
59466 * Unbinds all previously established listeners
59467 */
59468 SearchPad.prototype._unbindEvents = function() {
59469 this._eventMaps.forEach(function(m) {
59470 delegate.unbind(m.el, m.type, m.listener);
59471 });
59472 };
59473
59474
59475 /**
59476 * Performs a search for the given pattern.
59477 *
59478 * @param {string} pattern
59479 */
59480 SearchPad.prototype._search = function(pattern) {
59481 var self = this;
59482
59483 this._clearResults();
59484
59485 // do not search on empty query
59486 if (!pattern || pattern === '') {
59487 return;
59488 }
59489
59490 var searchResults = this._searchProvider.find(pattern);
59491
59492 if (!searchResults.length) {
59493 return;
59494 }
59495
59496 // append new results
59497 searchResults.forEach(function(result) {
59498 var id = result.element.id;
59499 var node = self._createResultNode(result, id);
59500 self._results[id] = {
59501 element: result.element,
59502 node: node
59503 };
59504 });
59505
59506 // preselect first result
59507 var node = query(SearchPad.RESULT_SELECTOR, this._resultsContainer);
59508 this._scrollToNode(node);
59509 this._preselect(node);
59510 };
59511
59512
59513 /**
59514 * Navigate to the previous/next result. Defaults to next result.
59515 * @param {boolean} previous
59516 */
59517 SearchPad.prototype._scrollToDirection = function(previous) {
59518 var selected = this._getCurrentResult();
59519 if (!selected) {
59520 return;
59521 }
59522
59523 var node = previous ? selected.previousElementSibling : selected.nextElementSibling;
59524 if (node) {
59525 this._scrollToNode(node);
59526 this._preselect(node);
59527 }
59528 };
59529
59530
59531 /**
59532 * Scroll to the node if it is not visible.
59533 *
59534 * @param {Element} node
59535 */
59536 SearchPad.prototype._scrollToNode = function(node) {
59537 if (!node || node === this._getCurrentResult()) {
59538 return;
59539 }
59540
59541 var nodeOffset = node.offsetTop;
59542 var containerScroll = this._resultsContainer.scrollTop;
59543
59544 var bottomScroll = nodeOffset - this._resultsContainer.clientHeight + node.clientHeight;
59545
59546 if (nodeOffset < containerScroll) {
59547 this._resultsContainer.scrollTop = nodeOffset;
59548 } else if (containerScroll < bottomScroll) {
59549 this._resultsContainer.scrollTop = bottomScroll;
59550 }
59551 };
59552
59553
59554 /**
59555 * Clears all results data.
59556 */
59557 SearchPad.prototype._clearResults = function() {
59558 clear(this._resultsContainer);
59559
59560 this._results = [];
59561
59562 this._resetOverlay();
59563
59564 this._eventBus.fire('searchPad.cleared');
59565 };
59566
59567
59568 /**
59569 * Get currently selected result.
59570 *
59571 * @return {Element}
59572 */
59573 SearchPad.prototype._getCurrentResult = function() {
59574 return query(SearchPad.RESULT_SELECTED_SELECTOR, this._resultsContainer);
59575 };
59576
59577
59578 /**
59579 * Create result DOM element within results container
59580 * that corresponds to a search result.
59581 *
59582 * 'result' : one of the elements returned by SearchProvider
59583 * 'id' : id attribute value to assign to the new DOM node
59584 * return : created DOM element
59585 *
59586 * @param {SearchResult} result
59587 * @param {string} id
59588 * @return {Element}
59589 */
59590 SearchPad.prototype._createResultNode = function(result, id) {
59591 var node = domify(SearchPad.RESULT_HTML);
59592
59593 // create only if available
59594 if (result.primaryTokens.length > 0) {
59595 createInnerTextNode(node, result.primaryTokens, SearchPad.RESULT_PRIMARY_HTML);
59596 }
59597
59598 // secondary tokens (represent element ID) are allways available
59599 createInnerTextNode(node, result.secondaryTokens, SearchPad.RESULT_SECONDARY_HTML);
59600
59601 attr(node, SearchPad.RESULT_ID_ATTRIBUTE, id);
59602
59603 this._resultsContainer.appendChild(node);
59604
59605 return node;
59606 };
59607
59608
59609 /**
59610 * Register search element provider.
59611 *
59612 * SearchProvider.find - provides search function over own elements
59613 * (pattern) => [{ text: <String>, element: <Element>}, ...]
59614 *
59615 * @param {SearchProvider} provider
59616 */
59617 SearchPad.prototype.registerProvider = function(provider) {
59618 this._searchProvider = provider;
59619 };
59620
59621
59622 /**
59623 * Open search pad.
59624 */
59625 SearchPad.prototype.open = function() {
59626 if (!this._searchProvider) {
59627 throw new Error('no search provider registered');
59628 }
59629
59630 if (this.isOpen()) {
59631 return;
59632 }
59633
59634 this._bindEvents();
59635
59636 this._open = true;
59637
59638 classes(this._container).add('open');
59639
59640 this._searchInput.focus();
59641
59642 this._eventBus.fire('searchPad.opened');
59643 };
59644
59645
59646 /**
59647 * Close search pad.
59648 */
59649 SearchPad.prototype.close = function() {
59650 if (!this.isOpen()) {
59651 return;
59652 }
59653
59654 this._unbindEvents();
59655
59656 this._open = false;
59657
59658 classes(this._container).remove('open');
59659
59660 this._clearResults();
59661
59662 this._searchInput.value = '';
59663 this._searchInput.blur();
59664
59665 this._resetOverlay();
59666
59667 this._eventBus.fire('searchPad.closed');
59668 };
59669
59670
59671 /**
59672 * Toggles search pad on/off.
59673 */
59674 SearchPad.prototype.toggle = function() {
59675 this.isOpen() ? this.close() : this.open();
59676 };
59677
59678
59679 /**
59680 * Report state of search pad.
59681 */
59682 SearchPad.prototype.isOpen = function() {
59683 return this._open;
59684 };
59685
59686
59687 /**
59688 * Preselect result entry.
59689 *
59690 * @param {Element} element
59691 */
59692 SearchPad.prototype._preselect = function(node) {
59693 var selectedNode = this._getCurrentResult();
59694
59695 // already selected
59696 if (node === selectedNode) {
59697 return;
59698 }
59699
59700 // removing preselection from current node
59701 if (selectedNode) {
59702 classes(selectedNode).remove(SearchPad.RESULT_SELECTED_CLASS);
59703 }
59704
59705 var id = attr(node, SearchPad.RESULT_ID_ATTRIBUTE);
59706 var element = this._results[id].element;
59707
59708 classes(node).add(SearchPad.RESULT_SELECTED_CLASS);
59709
59710 this._resetOverlay(element);
59711
59712 this._canvas.scrollToElement(element, { top: 400 });
59713
59714 this._selection.select(element);
59715
59716 this._eventBus.fire('searchPad.preselected', element);
59717 };
59718
59719
59720 /**
59721 * Select result node.
59722 *
59723 * @param {Element} element
59724 */
59725 SearchPad.prototype._select = function(node) {
59726 var id = attr(node, SearchPad.RESULT_ID_ATTRIBUTE);
59727 var element = this._results[id].element;
59728
59729 this.close();
59730
59731 this._resetOverlay();
59732
59733 this._canvas.scrollToElement(element, { top: 400 });
59734
59735 this._selection.select(element);
59736
59737 this._eventBus.fire('searchPad.selected', element);
59738 };
59739
59740
59741 /**
59742 * Reset overlay removes and, optionally, set
59743 * overlay to a new element.
59744 *
59745 * @param {Element} element
59746 */
59747 SearchPad.prototype._resetOverlay = function(element) {
59748 if (this._overlayId) {
59749 this._overlays.remove(this._overlayId);
59750 }
59751
59752 if (element) {
59753 var box = getBBox(element);
59754 var overlay = constructOverlay(box);
59755 this._overlayId = this._overlays.add(element, overlay);
59756 }
59757 };
59758
59759
59760 /**
59761 * Construct overlay object for the given bounding box.
59762 *
59763 * @param {BoundingBox} box
59764 * @return {Object}
59765 */
59766 function constructOverlay(box) {
59767
59768 var offset = 6;
59769 var w = box.width + offset * 2;
59770 var h = box.height + offset * 2;
59771
59772 var styles = {
59773 width: w + 'px',
59774 height: h + 'px'
59775 };
59776
59777 var html = domify('<div class="' + SearchPad.OVERLAY_CLASS + '"></div>');
59778
59779 assign$1(html, styles);
59780
59781 return {
59782 position: {
59783 bottom: h - offset,
59784 right: w - offset
59785 },
59786 show: true,
59787 html: html
59788 };
59789 }
59790
59791
59792 /**
59793 * Creates and appends child node from result tokens and HTML template.
59794 *
59795 * @param {Element} node
59796 * @param {Array<Object>} tokens
59797 * @param {string} template
59798 */
59799 function createInnerTextNode(parentNode, tokens, template) {
59800 var text = createHtmlText(tokens);
59801 var childNode = domify(template);
59802 childNode.innerHTML = text;
59803 parentNode.appendChild(childNode);
59804 }
59805
59806 /**
59807 * Create internal HTML markup from result tokens.
59808 * Caters for highlighting pattern matched tokens.
59809 *
59810 * @param {Array<Object>} tokens
59811 * @return {string}
59812 */
59813 function createHtmlText(tokens) {
59814 var htmlText = '';
59815
59816 tokens.forEach(function(t) {
59817 if (t.matched) {
59818 htmlText += '<strong class="' + SearchPad.RESULT_HIGHLIGHT_CLASS + '">' + escapeHTML(t.matched) + '</strong>';
59819 } else {
59820 htmlText += escapeHTML(t.normal);
59821 }
59822 });
59823
59824 return htmlText !== '' ? htmlText : null;
59825 }
59826
59827
59828 /**
59829 * CONSTANTS
59830 */
59831 SearchPad.CONTAINER_SELECTOR = '.djs-search-container';
59832 SearchPad.INPUT_SELECTOR = '.djs-search-input input';
59833 SearchPad.RESULTS_CONTAINER_SELECTOR = '.djs-search-results';
59834 SearchPad.RESULT_SELECTOR = '.djs-search-result';
59835 SearchPad.RESULT_SELECTED_CLASS = 'djs-search-result-selected';
59836 SearchPad.RESULT_SELECTED_SELECTOR = '.' + SearchPad.RESULT_SELECTED_CLASS;
59837 SearchPad.RESULT_ID_ATTRIBUTE = 'data-result-id';
59838 SearchPad.RESULT_HIGHLIGHT_CLASS = 'djs-search-highlight';
59839 SearchPad.OVERLAY_CLASS = 'djs-search-overlay';
59840
59841 SearchPad.BOX_HTML =
59842 '<div class="djs-search-container djs-draggable djs-scrollable">' +
59843 '<div class="djs-search-input">' +
59844 '<input type="text"/>' +
59845 '</div>' +
59846 '<div class="djs-search-results"></div>' +
59847 '</div>';
59848
59849 SearchPad.RESULT_HTML =
59850 '<div class="djs-search-result"></div>';
59851
59852 SearchPad.RESULT_PRIMARY_HTML =
59853 '<div class="djs-search-result-primary"></div>';
59854
59855 SearchPad.RESULT_SECONDARY_HTML =
59856 '<p class="djs-search-result-secondary"></p>';
59857
59858 var SearchPadModule = {
59859 __depends__: [
59860 OverlaysModule,
59861 SelectionModule
59862 ],
59863 searchPad: [ 'type', SearchPad ]
59864 };
59865
59866 /**
59867 * Provides ability to search through BPMN elements
59868 */
59869 function BpmnSearchProvider(elementRegistry, searchPad, canvas) {
59870
59871 this._elementRegistry = elementRegistry;
59872 this._canvas = canvas;
59873
59874 searchPad.registerProvider(this);
59875 }
59876
59877 BpmnSearchProvider.$inject = [
59878 'elementRegistry',
59879 'searchPad',
59880 'canvas'
59881 ];
59882
59883
59884 /**
59885 * Finds all elements that match given pattern
59886 *
59887 * <Result> :
59888 * {
59889 * primaryTokens: <Array<Token>>,
59890 * secondaryTokens: <Array<Token>>,
59891 * element: <Element>
59892 * }
59893 *
59894 * <Token> :
59895 * {
59896 * normal|matched: <string>
59897 * }
59898 *
59899 * @param {string} pattern
59900 * @return {Array<Result>}
59901 */
59902 BpmnSearchProvider.prototype.find = function(pattern) {
59903 var rootElement = this._canvas.getRootElement();
59904
59905 var elements = this._elementRegistry.filter(function(element) {
59906 if (element.labelTarget) {
59907 return false;
59908 }
59909 return true;
59910 });
59911
59912 // do not include root element
59913 elements = filter(elements, function(element) {
59914 return element !== rootElement;
59915 });
59916
59917 elements = map(elements, function(element) {
59918 return {
59919 primaryTokens: matchAndSplit(getLabel(element), pattern),
59920 secondaryTokens: matchAndSplit(element.id, pattern),
59921 element: element
59922 };
59923 });
59924
59925 // exclude non-matched elements
59926 elements = filter(elements, function(element) {
59927 return hasMatched(element.primaryTokens) || hasMatched(element.secondaryTokens);
59928 });
59929
59930 elements = sortBy(elements, function(element) {
59931 return getLabel(element.element) + element.element.id;
59932 });
59933
59934 return elements;
59935 };
59936
59937
59938 function hasMatched(tokens) {
59939 var matched = filter(tokens, function(t) {
59940 return !!t.matched;
59941 });
59942
59943 return matched.length > 0;
59944 }
59945
59946
59947 function matchAndSplit(text, pattern) {
59948 var tokens = [],
59949 originalText = text;
59950
59951 if (!text) {
59952 return tokens;
59953 }
59954
59955 text = text.toLowerCase();
59956 pattern = pattern.toLowerCase();
59957
59958 var i = text.indexOf(pattern);
59959
59960 if (i > -1) {
59961 if (i !== 0) {
59962 tokens.push({
59963 normal: originalText.substr(0, i)
59964 });
59965 }
59966
59967 tokens.push({
59968 matched: originalText.substr(i, pattern.length)
59969 });
59970
59971 if (pattern.length + i < text.length) {
59972 tokens.push({
59973 normal: originalText.substr(pattern.length + i, text.length)
59974 });
59975 }
59976 } else {
59977 tokens.push({
59978 normal: originalText
59979 });
59980 }
59981
59982 return tokens;
59983 }
59984
59985 var SearchModule = {
59986 __depends__: [
59987 SearchPadModule
59988 ],
59989 __init__: [ 'bpmnSearch'],
59990 bpmnSearch: [ 'type', BpmnSearchProvider ]
59991 };
59992
59993 var initialDiagram =
59994 '<?xml version="1.0" encoding="UTF-8"?>' +
59995 '<bpmn:definitions xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" ' +
59996 'xmlns:bpmn="http://www.omg.org/spec/BPMN/20100524/MODEL" ' +
59997 'xmlns:bpmndi="http://www.omg.org/spec/BPMN/20100524/DI" ' +
59998 'xmlns:dc="http://www.omg.org/spec/DD/20100524/DC" ' +
59999 'targetNamespace="http://bpmn.io/schema/bpmn" ' +
60000 'id="Definitions_1">' +
60001 '<bpmn:process id="Process_1" isExecutable="false">' +
60002 '<bpmn:startEvent id="StartEvent_1"/>' +
60003 '</bpmn:process>' +
60004 '<bpmndi:BPMNDiagram id="BPMNDiagram_1">' +
60005 '<bpmndi:BPMNPlane id="BPMNPlane_1" bpmnElement="Process_1">' +
60006 '<bpmndi:BPMNShape id="_BPMNShape_StartEvent_2" bpmnElement="StartEvent_1">' +
60007 '<dc:Bounds height="36.0" width="36.0" x="173.0" y="102.0"/>' +
60008 '</bpmndi:BPMNShape>' +
60009 '</bpmndi:BPMNPlane>' +
60010 '</bpmndi:BPMNDiagram>' +
60011 '</bpmn:definitions>';
60012
60013
60014 /**
60015 * A modeler for BPMN 2.0 diagrams.
60016 *
60017 *
60018 * ## Extending the Modeler
60019 *
60020 * In order to extend the viewer pass extension modules to bootstrap via the
60021 * `additionalModules` option. An extension module is an object that exposes
60022 * named services.
60023 *
60024 * The following example depicts the integration of a simple
60025 * logging component that integrates with interaction events:
60026 *
60027 *
60028 * ```javascript
60029 *
60030 * // logging component
60031 * function InteractionLogger(eventBus) {
60032 * eventBus.on('element.hover', function(event) {
60033 * console.log()
60034 * })
60035 * }
60036 *
60037 * InteractionLogger.$inject = [ 'eventBus' ]; // minification save
60038 *
60039 * // extension module
60040 * var extensionModule = {
60041 * __init__: [ 'interactionLogger' ],
60042 * interactionLogger: [ 'type', InteractionLogger ]
60043 * };
60044 *
60045 * // extend the viewer
60046 * var bpmnModeler = new Modeler({ additionalModules: [ extensionModule ] });
60047 * bpmnModeler.importXML(...);
60048 * ```
60049 *
60050 *
60051 * ## Customizing / Replacing Components
60052 *
60053 * You can replace individual diagram components by redefining them in override modules.
60054 * This works for all components, including those defined in the core.
60055 *
60056 * Pass in override modules via the `options.additionalModules` flag like this:
60057 *
60058 * ```javascript
60059 * function CustomContextPadProvider(contextPad) {
60060 *
60061 * contextPad.registerProvider(this);
60062 *
60063 * this.getContextPadEntries = function(element) {
60064 * // no entries, effectively disable the context pad
60065 * return {};
60066 * };
60067 * }
60068 *
60069 * CustomContextPadProvider.$inject = [ 'contextPad' ];
60070 *
60071 * var overrideModule = {
60072 * contextPadProvider: [ 'type', CustomContextPadProvider ]
60073 * };
60074 *
60075 * var bpmnModeler = new Modeler({ additionalModules: [ overrideModule ]});
60076 * ```
60077 *
60078 * @param {Object} [options] configuration options to pass to the viewer
60079 * @param {DOMElement} [options.container] the container to render the viewer in, defaults to body.
60080 * @param {string|number} [options.width] the width of the viewer
60081 * @param {string|number} [options.height] the height of the viewer
60082 * @param {Object} [options.moddleExtensions] extension packages to provide
60083 * @param {Array<didi.Module>} [options.modules] a list of modules to override the default modules
60084 * @param {Array<didi.Module>} [options.additionalModules] a list of modules to use with the default modules
60085 */
60086 function Modeler(options) {
60087 BaseModeler.call(this, options);
60088 }
60089
60090 inherits$1(Modeler, BaseModeler);
60091
60092
60093 Modeler.Viewer = Viewer;
60094 Modeler.NavigatedViewer = NavigatedViewer;
60095
60096 /**
60097 * The createDiagram result.
60098 *
60099 * @typedef {Object} CreateDiagramResult
60100 *
60101 * @property {Array<string>} warnings
60102 */
60103
60104 /**
60105 * The createDiagram error.
60106 *
60107 * @typedef {Error} CreateDiagramError
60108 *
60109 * @property {Array<string>} warnings
60110 */
60111
60112 /**
60113 * Create a new diagram to start modeling.
60114 *
60115 * Returns {Promise<CreateDiagramResult, CreateDiagramError>}
60116 */
60117 Modeler.prototype.createDiagram = wrapForCompatibility(function createDiagram() {
60118 return this.importXML(initialDiagram);
60119 });
60120
60121
60122 Modeler.prototype._interactionModules = [
60123
60124 // non-modeling components
60125 KeyboardMoveModule,
60126 MoveCanvasModule,
60127 TouchModule,
60128 ZoomScrollModule
60129 ];
60130
60131 Modeler.prototype._modelingModules = [
60132
60133 // modeling components
60134 AlignElementsModule,
60135 AutoPlaceModule,
60136 AutoScrollModule,
60137 AutoResizeModule,
60138 BendpointsModule,
60139 ConnectModule,
60140 ConnectionPreviewModule,
60141 ContextPadModule,
60142 CopyPasteModule,
60143 CreateModule,
60144 DistributeElementsModule,
60145 EditorActionsModule,
60146 GridSnappingModule,
60147 InteractionEventsModule,
60148 KeyboardModule,
60149 KeyboardMoveSelectionModule,
60150 LabelEditingModule,
60151 ModelingModule,
60152 MoveModule,
60153 PaletteModule,
60154 ReplacePreviewModule,
60155 ResizeModule,
60156 SnappingModule,
60157 SearchModule
60158 ];
60159
60160
60161 // modules the modeler is composed of
60162 //
60163 // - viewer modules
60164 // - interaction modules
60165 // - modeling modules
60166
60167 Modeler.prototype._modules = [].concat(
60168 Viewer.prototype._modules,
60169 Modeler.prototype._interactionModules,
60170 Modeler.prototype._modelingModules
60171 );
60172
60173 return Modeler;
60174
60175})));