UNPKG

62.1 kBJavaScriptView Raw
1import inherits from 'inherits-browser';
2
3import {
4 assign,
5 forEach,
6 isObject
7} from 'min-dash';
8
9import BaseRenderer from 'diagram-js/lib/draw/BaseRenderer';
10
11import {
12 isExpanded,
13 isHorizontal,
14 isEventSubProcess
15} from '../util/DiUtil';
16
17import {
18 getLabel
19} from '../util/LabelUtil';
20
21import {
22 is
23} from '../util/ModelUtil';
24
25import {
26 createLine
27} from 'diagram-js/lib/util/RenderUtil';
28
29import {
30 isTypedEvent,
31 isThrowEvent,
32 isCollection,
33 getBounds,
34 getDi,
35 getSemantic,
36 getCirclePath,
37 getRoundRectPath,
38 getDiamondPath,
39 getRectPath,
40 getFillColor,
41 getStrokeColor,
42 getLabelColor,
43 getHeight,
44 getWidth
45} from './BpmnRenderUtil';
46
47import {
48 query as domQuery
49} from 'min-dom';
50
51import {
52 append as svgAppend,
53 attr as svgAttr,
54 create as svgCreate,
55 classes as svgClasses
56} from 'tiny-svg';
57
58import {
59 rotate,
60 transform,
61 translate
62} from 'diagram-js/lib/util/SvgTransformUtil';
63
64import Ids from 'ids';
65
66import { black } from './BpmnRenderUtil';
67
68var rendererIds = new Ids();
69
70var ELEMENT_LABEL_DISTANCE = 10,
71 INNER_OUTER_DIST = 3,
72 PARTICIPANT_STROKE_WIDTH = 1.5,
73 TASK_BORDER_RADIUS = 10;
74
75var DEFAULT_OPACITY = 0.95,
76 FULL_OPACITY = 1,
77 LOW_OPACITY = 0.25;
78
79/**
80 * @typedef { Partial<{
81 * defaultFillColor: string,
82 * defaultStrokeColor: string,
83 * defaultLabelColor: string
84 * }> } BpmnRendererConfig
85 *
86 * @typedef { Partial<{
87 * fill: string,
88 * stroke: string,
89 * width: string,
90 * height: string
91 * }> } Attrs
92 */
93
94/**
95 * @typedef { import('../model/Types').Element } Element
96 */
97
98/**
99 * A renderer for BPMN elements
100 *
101 * @param {BpmnRendererConfig} config
102 * @param {import('diagram-js/lib/core/EventBus').default} eventBus
103 * @param {import('diagram-js/lib/draw/Styles').default} styles
104 * @param {import('./PathMap').default} pathMap
105 * @param {import('diagram-js/lib/core/Canvas').default} canvas
106 * @param {import('./TextRenderer').default} textRenderer
107 * @param {number} [priority]
108 */
109export default function BpmnRenderer(
110 config, eventBus, styles, pathMap,
111 canvas, textRenderer, priority) {
112
113 BaseRenderer.call(this, eventBus, priority);
114
115 var defaultFillColor = config && config.defaultFillColor,
116 defaultStrokeColor = config && config.defaultStrokeColor,
117 defaultLabelColor = config && config.defaultLabelColor;
118
119 var rendererId = rendererIds.next();
120
121 var markers = {};
122
123 function shapeStyle(attrs) {
124 return styles.computeStyle(attrs, {
125 strokeLinecap: 'round',
126 strokeLinejoin: 'round',
127 stroke: black,
128 strokeWidth: 2,
129 fill: 'white'
130 });
131 }
132
133 function lineStyle(attrs) {
134 return styles.computeStyle(attrs, [ 'no-fill' ], {
135 strokeLinecap: 'round',
136 strokeLinejoin: 'round',
137 stroke: black,
138 strokeWidth: 2
139 });
140 }
141
142 function addMarker(id, options) {
143 var {
144 ref = { x: 0, y: 0 },
145 scale = 1,
146 element
147 } = options;
148
149 var marker = svgCreate('marker', {
150 id: id,
151 viewBox: '0 0 20 20',
152 refX: ref.x,
153 refY: ref.y,
154 markerWidth: 20 * scale,
155 markerHeight: 20 * scale,
156 orient: 'auto'
157 });
158
159 svgAppend(marker, element);
160
161 var defs = domQuery('defs', canvas._svg);
162
163 if (!defs) {
164 defs = svgCreate('defs');
165
166 svgAppend(canvas._svg, defs);
167 }
168
169 svgAppend(defs, marker);
170
171 markers[id] = marker;
172 }
173
174 function colorEscape(str) {
175
176 // only allow characters and numbers
177 return str.replace(/[^0-9a-zA-Z]+/g, '_');
178 }
179
180 function marker(type, fill, stroke) {
181 var id = type + '-' + colorEscape(fill) + '-' + colorEscape(stroke) + '-' + rendererId;
182
183 if (!markers[id]) {
184 createMarker(id, type, fill, stroke);
185 }
186
187 return 'url(#' + id + ')';
188 }
189
190 function createMarker(id, type, fill, stroke) {
191
192 if (type === 'sequenceflow-end') {
193 var sequenceflowEnd = svgCreate('path', {
194 d: 'M 1 5 L 11 10 L 1 15 Z',
195 ...shapeStyle({
196 fill: stroke,
197 stroke: stroke,
198 strokeWidth: 1
199 })
200 });
201
202 addMarker(id, {
203 element: sequenceflowEnd,
204 ref: { x: 11, y: 10 },
205 scale: 0.5
206 });
207 }
208
209 if (type === 'messageflow-start') {
210 var messageflowStart = svgCreate('circle', {
211 cx: 6,
212 cy: 6,
213 r: 3.5,
214 ...shapeStyle({
215 fill,
216 stroke: stroke,
217 strokeWidth: 1,
218
219 // fix for safari / chrome / firefox bug not correctly
220 // resetting stroke dash array
221 strokeDasharray: [ 10000, 1 ]
222 })
223 });
224
225 addMarker(id, {
226 element: messageflowStart,
227 ref: { x: 6, y: 6 }
228 });
229 }
230
231 if (type === 'messageflow-end') {
232 var messageflowEnd = svgCreate('path', {
233 d: 'm 1 5 l 0 -3 l 7 3 l -7 3 z',
234 ...shapeStyle({
235 fill,
236 stroke: stroke,
237 strokeWidth: 1,
238
239 // fix for safari / chrome / firefox bug not correctly
240 // resetting stroke dash array
241 strokeDasharray: [ 10000, 1 ]
242 })
243 });
244
245 addMarker(id, {
246 element: messageflowEnd,
247 ref: { x: 8.5, y: 5 }
248 });
249 }
250
251 if (type === 'association-start') {
252 var associationStart = svgCreate('path', {
253 d: 'M 11 5 L 1 10 L 11 15',
254 ...lineStyle({
255 fill: 'none',
256 stroke,
257 strokeWidth: 1.5,
258
259 // fix for safari / chrome / firefox bug not correctly
260 // resetting stroke dash array
261 strokeDasharray: [ 10000, 1 ]
262 })
263 });
264
265 addMarker(id, {
266 element: associationStart,
267 ref: { x: 1, y: 10 },
268 scale: 0.5
269 });
270 }
271
272 if (type === 'association-end') {
273 var associationEnd = svgCreate('path', {
274 d: 'M 1 5 L 11 10 L 1 15',
275 ...lineStyle({
276 fill: 'none',
277 stroke,
278 strokeWidth: 1.5,
279
280 // fix for safari / chrome / firefox bug not correctly
281 // resetting stroke dash array
282 strokeDasharray: [ 10000, 1 ]
283 })
284 });
285
286 addMarker(id, {
287 element: associationEnd,
288 ref: { x: 11, y: 10 },
289 scale: 0.5
290 });
291 }
292
293 if (type === 'conditional-flow-marker') {
294 var conditionalFlowMarker = svgCreate('path', {
295 d: 'M 0 10 L 8 6 L 16 10 L 8 14 Z',
296 ...shapeStyle({
297 fill,
298 stroke: stroke
299 })
300 });
301
302 addMarker(id, {
303 element: conditionalFlowMarker,
304 ref: { x: -1, y: 10 },
305 scale: 0.5
306 });
307 }
308
309 if (type === 'conditional-default-flow-marker') {
310 var defaultFlowMarker = svgCreate('path', {
311 d: 'M 6 4 L 10 16',
312 ...shapeStyle({
313 stroke: stroke
314 })
315 });
316
317 addMarker(id, {
318 element: defaultFlowMarker,
319 ref: { x: 0, y: 10 },
320 scale: 0.5
321 });
322 }
323 }
324
325 function drawCircle(parentGfx, width, height, offset, attrs = {}) {
326
327 if (isObject(offset)) {
328 attrs = offset;
329 offset = 0;
330 }
331
332 offset = offset || 0;
333
334 attrs = shapeStyle(attrs);
335
336 var cx = width / 2,
337 cy = height / 2;
338
339 var circle = svgCreate('circle', {
340 cx: cx,
341 cy: cy,
342 r: Math.round((width + height) / 4 - offset),
343 ...attrs
344 });
345
346 svgAppend(parentGfx, circle);
347
348 return circle;
349 }
350
351 function drawRect(parentGfx, width, height, r, offset, attrs) {
352
353 if (isObject(offset)) {
354 attrs = offset;
355 offset = 0;
356 }
357
358 offset = offset || 0;
359
360 attrs = shapeStyle(attrs);
361
362 var rect = svgCreate('rect', {
363 x: offset,
364 y: offset,
365 width: width - offset * 2,
366 height: height - offset * 2,
367 rx: r,
368 ry: r,
369 ...attrs
370 });
371
372 svgAppend(parentGfx, rect);
373
374 return rect;
375 }
376
377 function drawDiamond(parentGfx, width, height, attrs) {
378
379 var x_2 = width / 2;
380 var y_2 = height / 2;
381
382 var points = [
383 { x: x_2, y: 0 },
384 { x: width, y: y_2 },
385 { x: x_2, y: height },
386 { x: 0, y: y_2 }
387 ];
388
389 var pointsString = points.map(function(point) {
390 return point.x + ',' + point.y;
391 }).join(' ');
392
393 attrs = shapeStyle(attrs);
394
395 var polygon = svgCreate('polygon', {
396 ...attrs,
397 points: pointsString
398 });
399
400 svgAppend(parentGfx, polygon);
401
402 return polygon;
403 }
404
405 /**
406 * @param {SVGElement} parentGfx
407 * @param {Point[]} waypoints
408 * @param {any} attrs
409 * @param {number} [radius]
410 *
411 * @return {SVGElement}
412 */
413 function drawLine(parentGfx, waypoints, attrs, radius) {
414 attrs = lineStyle(attrs);
415
416 var line = createLine(waypoints, attrs, radius);
417
418 svgAppend(parentGfx, line);
419
420 return line;
421 }
422
423 /**
424 * @param {SVGElement} parentGfx
425 * @param {Point[]} waypoints
426 * @param {any} attrs
427 *
428 * @return {SVGElement}
429 */
430 function drawConnectionSegments(parentGfx, waypoints, attrs) {
431 return drawLine(parentGfx, waypoints, attrs, 5);
432 }
433
434 function drawPath(parentGfx, d, attrs) {
435 attrs = lineStyle(attrs);
436
437 var path = svgCreate('path', {
438 ...attrs,
439 d
440 });
441
442 svgAppend(parentGfx, path);
443
444 return path;
445 }
446
447 function drawMarker(type, parentGfx, path, attrs) {
448 return drawPath(parentGfx, path, assign({ 'data-marker': type }, attrs));
449 }
450
451 function renderer(type) {
452 return handlers[type];
453 }
454
455 function as(type) {
456 return function(parentGfx, element, attrs) {
457 return renderer(type)(parentGfx, element, attrs);
458 };
459 }
460
461 var eventIconRenderers = {
462 'bpmn:MessageEventDefinition': function(parentGfx, element, attrs = {}, isThrowing) {
463 var pathData = pathMap.getScaledPath('EVENT_MESSAGE', {
464 xScaleFactor: 0.9,
465 yScaleFactor: 0.9,
466 containerWidth: element.width,
467 containerHeight: element.height,
468 position: {
469 mx: 0.235,
470 my: 0.315
471 }
472 });
473
474 var fill = isThrowing
475 ? getStrokeColor(element, defaultStrokeColor, attrs.stroke)
476 : getFillColor(element, defaultFillColor, attrs.fill);
477
478 var stroke = isThrowing
479 ? getFillColor(element, defaultFillColor, attrs.fill)
480 : getStrokeColor(element, defaultStrokeColor, attrs.stroke);
481
482 var messagePath = drawPath(parentGfx, pathData, {
483 fill,
484 stroke,
485 strokeWidth: 1
486 });
487
488 return messagePath;
489 },
490 'bpmn:TimerEventDefinition': function(parentGfx, element, attrs = {}) {
491 var circle = drawCircle(parentGfx, element.width, element.height, 0.2 * element.height, {
492 fill: getFillColor(element, defaultFillColor, attrs.fill),
493 stroke: getStrokeColor(element, defaultStrokeColor, attrs.stroke),
494 strokeWidth: 2
495 });
496
497 var pathData = pathMap.getScaledPath('EVENT_TIMER_WH', {
498 xScaleFactor: 0.75,
499 yScaleFactor: 0.75,
500 containerWidth: element.width,
501 containerHeight: element.height,
502 position: {
503 mx: 0.5,
504 my: 0.5
505 }
506 });
507
508 drawPath(parentGfx, pathData, {
509 stroke: getStrokeColor(element, defaultStrokeColor, attrs.stroke),
510 strokeWidth: 2
511 });
512
513 for (var i = 0; i < 12; i++) {
514 var linePathData = pathMap.getScaledPath('EVENT_TIMER_LINE', {
515 xScaleFactor: 0.75,
516 yScaleFactor: 0.75,
517 containerWidth: element.width,
518 containerHeight: element.height,
519 position: {
520 mx: 0.5,
521 my: 0.5
522 }
523 });
524
525 var width = element.width / 2,
526 height = element.height / 2;
527
528 drawPath(parentGfx, linePathData, {
529 strokeWidth: 1,
530 stroke: getStrokeColor(element, defaultStrokeColor, attrs.stroke),
531 transform: 'rotate(' + (i * 30) + ',' + height + ',' + width + ')'
532 });
533 }
534
535 return circle;
536 },
537 'bpmn:EscalationEventDefinition': function(parentGfx, event, attrs = {}, isThrowing) {
538 var pathData = pathMap.getScaledPath('EVENT_ESCALATION', {
539 xScaleFactor: 1,
540 yScaleFactor: 1,
541 containerWidth: event.width,
542 containerHeight: event.height,
543 position: {
544 mx: 0.5,
545 my: 0.2
546 }
547 });
548
549 var fill = isThrowing
550 ? getStrokeColor(event, defaultStrokeColor, attrs.stroke)
551 : getFillColor(event, defaultFillColor, attrs.fill);
552
553 return drawPath(parentGfx, pathData, {
554 fill,
555 stroke: getStrokeColor(event, defaultStrokeColor, attrs.stroke),
556 strokeWidth: 1
557 });
558 },
559 'bpmn:ConditionalEventDefinition': function(parentGfx, event, attrs = {}) {
560 var pathData = pathMap.getScaledPath('EVENT_CONDITIONAL', {
561 xScaleFactor: 1,
562 yScaleFactor: 1,
563 containerWidth: event.width,
564 containerHeight: event.height,
565 position: {
566 mx: 0.5,
567 my: 0.222
568 }
569 });
570
571 return drawPath(parentGfx, pathData, {
572 fill: getFillColor(event, defaultFillColor, attrs.fill),
573 stroke: getStrokeColor(event, defaultStrokeColor, attrs.stroke),
574 strokeWidth: 1
575 });
576 },
577 'bpmn:LinkEventDefinition': function(parentGfx, event, attrs = {}, isThrowing) {
578 var pathData = pathMap.getScaledPath('EVENT_LINK', {
579 xScaleFactor: 1,
580 yScaleFactor: 1,
581 containerWidth: event.width,
582 containerHeight: event.height,
583 position: {
584 mx: 0.57,
585 my: 0.263
586 }
587 });
588
589 var fill = isThrowing
590 ? getStrokeColor(event, defaultStrokeColor, attrs.stroke)
591 : getFillColor(event, defaultFillColor, attrs.fill);
592
593 return drawPath(parentGfx, pathData, {
594 fill,
595 stroke: getStrokeColor(event, defaultStrokeColor, attrs.stroke),
596 strokeWidth: 1
597 });
598 },
599 'bpmn:ErrorEventDefinition': function(parentGfx, event, attrs = {}, isThrowing) {
600 var pathData = pathMap.getScaledPath('EVENT_ERROR', {
601 xScaleFactor: 1.1,
602 yScaleFactor: 1.1,
603 containerWidth: event.width,
604 containerHeight: event.height,
605 position: {
606 mx: 0.2,
607 my: 0.722
608 }
609 });
610
611 var fill = isThrowing
612 ? getStrokeColor(event, defaultStrokeColor, attrs.stroke)
613 : getFillColor(event, defaultFillColor, attrs.fill);
614
615 return drawPath(parentGfx, pathData, {
616 fill,
617 stroke: getStrokeColor(event, defaultStrokeColor, attrs.stroke),
618 strokeWidth: 1
619 });
620 },
621 'bpmn:CancelEventDefinition': function(parentGfx, event, attrs = {}, isThrowing) {
622 var pathData = pathMap.getScaledPath('EVENT_CANCEL_45', {
623 xScaleFactor: 1.0,
624 yScaleFactor: 1.0,
625 containerWidth: event.width,
626 containerHeight: event.height,
627 position: {
628 mx: 0.638,
629 my: -0.055
630 }
631 });
632
633 var fill = isThrowing ? getStrokeColor(event, defaultStrokeColor, attrs.stroke) : 'none';
634
635 var path = drawPath(parentGfx, pathData, {
636 fill,
637 stroke: getStrokeColor(event, defaultStrokeColor, attrs.stroke),
638 strokeWidth: 1
639 });
640
641 rotate(path, 45);
642
643 return path;
644 },
645 'bpmn:CompensateEventDefinition': function(parentGfx, event, attrs = {}, isThrowing) {
646 var pathData = pathMap.getScaledPath('EVENT_COMPENSATION', {
647 xScaleFactor: 1,
648 yScaleFactor: 1,
649 containerWidth: event.width,
650 containerHeight: event.height,
651 position: {
652 mx: 0.22,
653 my: 0.5
654 }
655 });
656
657 var fill = isThrowing
658 ? getStrokeColor(event, defaultStrokeColor, attrs.stroke)
659 : getFillColor(event, defaultFillColor, attrs.fill);
660
661 return drawPath(parentGfx, pathData, {
662 fill,
663 stroke: getStrokeColor(event, defaultStrokeColor, attrs.stroke),
664 strokeWidth: 1
665 });
666 },
667 'bpmn:SignalEventDefinition': function(parentGfx, event, attrs = {}, isThrowing) {
668 var pathData = pathMap.getScaledPath('EVENT_SIGNAL', {
669 xScaleFactor: 0.9,
670 yScaleFactor: 0.9,
671 containerWidth: event.width,
672 containerHeight: event.height,
673 position: {
674 mx: 0.5,
675 my: 0.2
676 }
677 });
678
679 var fill = isThrowing
680 ? getStrokeColor(event, defaultStrokeColor, attrs.stroke)
681 : getFillColor(event, defaultFillColor, attrs.fill);
682
683 return drawPath(parentGfx, pathData, {
684 strokeWidth: 1,
685 fill,
686 stroke: getStrokeColor(event, defaultStrokeColor, attrs.stroke)
687 });
688 },
689 'bpmn:MultipleEventDefinition': function(parentGfx, event, attrs = {}, isThrowing) {
690 var pathData = pathMap.getScaledPath('EVENT_MULTIPLE', {
691 xScaleFactor: 1.1,
692 yScaleFactor: 1.1,
693 containerWidth: event.width,
694 containerHeight: event.height,
695 position: {
696 mx: 0.222,
697 my: 0.36
698 }
699 });
700
701 var fill = isThrowing
702 ? getStrokeColor(event, defaultStrokeColor, attrs.stroke)
703 : getFillColor(event, defaultFillColor, attrs.fill);
704
705 return drawPath(parentGfx, pathData, {
706 fill,
707 strokeWidth: 1
708 });
709 },
710 'bpmn:ParallelMultipleEventDefinition': function(parentGfx, event, attrs = {}) {
711 var pathData = pathMap.getScaledPath('EVENT_PARALLEL_MULTIPLE', {
712 xScaleFactor: 1.2,
713 yScaleFactor: 1.2,
714 containerWidth: event.width,
715 containerHeight: event.height,
716 position: {
717 mx: 0.458,
718 my: 0.194
719 }
720 });
721
722 return drawPath(parentGfx, pathData, {
723 fill: getFillColor(event, defaultFillColor, attrs.fill),
724 stroke: getStrokeColor(event, defaultStrokeColor, attrs.stroke),
725 strokeWidth: 1
726 });
727 },
728 'bpmn:TerminateEventDefinition': function(parentGfx, element, attrs = {}) {
729 var circle = drawCircle(parentGfx, element.width, element.height, 8, {
730 fill: getStrokeColor(element, defaultStrokeColor, attrs.stroke),
731 stroke: getStrokeColor(element, defaultStrokeColor, attrs.stroke),
732 strokeWidth: 4
733 });
734
735 return circle;
736 }
737 };
738
739 function renderEventIcon(element, parentGfx, attrs = {}) {
740 var semantic = getSemantic(element),
741 isThrowing = isThrowEvent(semantic);
742
743 if (semantic.get('eventDefinitions') && semantic.get('eventDefinitions').length > 1) {
744 if (semantic.get('parallelMultiple')) {
745 return eventIconRenderers[ 'bpmn:ParallelMultipleEventDefinition' ](parentGfx, element, attrs, isThrowing);
746 }
747 else {
748 return eventIconRenderers[ 'bpmn:MultipleEventDefinition' ](parentGfx, element, attrs, isThrowing);
749 }
750 }
751
752 if (isTypedEvent(semantic, 'bpmn:MessageEventDefinition')) {
753 return eventIconRenderers[ 'bpmn:MessageEventDefinition' ](parentGfx, element, attrs, isThrowing);
754 }
755
756 if (isTypedEvent(semantic, 'bpmn:TimerEventDefinition')) {
757 return eventIconRenderers[ 'bpmn:TimerEventDefinition' ](parentGfx, element, attrs, isThrowing);
758 }
759
760 if (isTypedEvent(semantic, 'bpmn:ConditionalEventDefinition')) {
761 return eventIconRenderers[ 'bpmn:ConditionalEventDefinition' ](parentGfx, element, attrs, isThrowing);
762 }
763
764 if (isTypedEvent(semantic, 'bpmn:SignalEventDefinition')) {
765 return eventIconRenderers[ 'bpmn:SignalEventDefinition' ](parentGfx, element, attrs, isThrowing);
766 }
767
768 if (isTypedEvent(semantic, 'bpmn:EscalationEventDefinition')) {
769 return eventIconRenderers[ 'bpmn:EscalationEventDefinition' ](parentGfx, element, attrs, isThrowing);
770 }
771
772 if (isTypedEvent(semantic, 'bpmn:LinkEventDefinition')) {
773 return eventIconRenderers[ 'bpmn:LinkEventDefinition' ](parentGfx, element, attrs, isThrowing);
774 }
775
776 if (isTypedEvent(semantic, 'bpmn:ErrorEventDefinition')) {
777 return eventIconRenderers[ 'bpmn:ErrorEventDefinition' ](parentGfx, element, attrs, isThrowing);
778 }
779
780 if (isTypedEvent(semantic, 'bpmn:CancelEventDefinition')) {
781 return eventIconRenderers[ 'bpmn:CancelEventDefinition' ](parentGfx, element, attrs, isThrowing);
782 }
783
784 if (isTypedEvent(semantic, 'bpmn:CompensateEventDefinition')) {
785 return eventIconRenderers[ 'bpmn:CompensateEventDefinition' ](parentGfx, element, attrs, isThrowing);
786 }
787
788 if (isTypedEvent(semantic, 'bpmn:TerminateEventDefinition')) {
789 return eventIconRenderers[ 'bpmn:TerminateEventDefinition' ](parentGfx, element, attrs, isThrowing);
790 }
791
792 return null;
793 }
794
795 var taskMarkerRenderers = {
796 'ParticipantMultiplicityMarker': function(parentGfx, element, attrs = {}) {
797 var width = getWidth(element, attrs),
798 height = getHeight(element, attrs);
799
800 var markerPath = pathMap.getScaledPath('MARKER_PARALLEL', {
801 xScaleFactor: 1,
802 yScaleFactor: 1,
803 containerWidth: width,
804 containerHeight: height,
805 position: {
806 mx: ((width / 2 - 6) / width),
807 my: (height - 15) / height
808 }
809 });
810
811 drawMarker('participant-multiplicity', parentGfx, markerPath, {
812 strokeWidth: 2,
813 fill: getFillColor(element, defaultFillColor, attrs.fill),
814 stroke: getStrokeColor(element, defaultStrokeColor, attrs.stroke)
815 });
816 },
817 'SubProcessMarker': function(parentGfx, element, attrs = {}) {
818 var markerRect = drawRect(parentGfx, 14, 14, 0, {
819 strokeWidth: 1,
820 fill: getFillColor(element, defaultFillColor, attrs.fill),
821 stroke: getStrokeColor(element, defaultStrokeColor, attrs.stroke)
822 });
823
824 translate(markerRect, element.width / 2 - 7.5, element.height - 20);
825
826 var markerPath = pathMap.getScaledPath('MARKER_SUB_PROCESS', {
827 xScaleFactor: 1.5,
828 yScaleFactor: 1.5,
829 containerWidth: element.width,
830 containerHeight: element.height,
831 position: {
832 mx: (element.width / 2 - 7.5) / element.width,
833 my: (element.height - 20) / element.height
834 }
835 });
836
837 drawMarker('sub-process', parentGfx, markerPath, {
838 fill: getFillColor(element, defaultFillColor, attrs.fill),
839 stroke: getStrokeColor(element, defaultStrokeColor, attrs.stroke)
840 });
841 },
842 'ParallelMarker': function(parentGfx, element, attrs) {
843 var width = getWidth(element, attrs),
844 height = getHeight(element, attrs);
845
846 var markerPath = pathMap.getScaledPath('MARKER_PARALLEL', {
847 xScaleFactor: 1,
848 yScaleFactor: 1,
849 containerWidth: width,
850 containerHeight: height,
851 position: {
852 mx: ((width / 2 + attrs.parallel) / width),
853 my: (height - 20) / height
854 }
855 });
856
857 drawMarker('parallel', parentGfx, markerPath, {
858 fill: getFillColor(element, defaultFillColor, attrs.fill),
859 stroke: getStrokeColor(element, defaultStrokeColor, attrs.stroke)
860 });
861 },
862 'SequentialMarker': function(parentGfx, element, attrs) {
863 var markerPath = pathMap.getScaledPath('MARKER_SEQUENTIAL', {
864 xScaleFactor: 1,
865 yScaleFactor: 1,
866 containerWidth: element.width,
867 containerHeight: element.height,
868 position: {
869 mx: ((element.width / 2 + attrs.seq) / element.width),
870 my: (element.height - 19) / element.height
871 }
872 });
873
874 drawMarker('sequential', parentGfx, markerPath, {
875 fill: getFillColor(element, defaultFillColor, attrs.fill),
876 stroke: getStrokeColor(element, defaultStrokeColor, attrs.stroke)
877 });
878 },
879 'CompensationMarker': function(parentGfx, element, attrs) {
880 var markerMath = pathMap.getScaledPath('MARKER_COMPENSATION', {
881 xScaleFactor: 1,
882 yScaleFactor: 1,
883 containerWidth: element.width,
884 containerHeight: element.height,
885 position: {
886 mx: ((element.width / 2 + attrs.compensation) / element.width),
887 my: (element.height - 13) / element.height
888 }
889 });
890
891 drawMarker('compensation', parentGfx, markerMath, {
892 strokeWidth: 1,
893 fill: getFillColor(element, defaultFillColor, attrs.fill),
894 stroke: getStrokeColor(element, defaultStrokeColor, attrs.stroke)
895 });
896 },
897 'LoopMarker': function(parentGfx, element, attrs) {
898 var width = getWidth(element, attrs),
899 height = getHeight(element, attrs);
900
901 var markerPath = pathMap.getScaledPath('MARKER_LOOP', {
902 xScaleFactor: 1,
903 yScaleFactor: 1,
904 containerWidth: width,
905 containerHeight: height,
906 position: {
907 mx: ((width / 2 + attrs.loop) / width),
908 my: (height - 7) / height
909 }
910 });
911
912 drawMarker('loop', parentGfx, markerPath, {
913 strokeWidth: 1.5,
914 fill: 'none',
915 stroke: getStrokeColor(element, defaultStrokeColor, attrs.stroke),
916 strokeMiterlimit: 0.5
917 });
918 },
919 'AdhocMarker': function(parentGfx, element, attrs) {
920 var width = getWidth(element, attrs),
921 height = getHeight(element, attrs);
922
923 var markerPath = pathMap.getScaledPath('MARKER_ADHOC', {
924 xScaleFactor: 1,
925 yScaleFactor: 1,
926 containerWidth: width,
927 containerHeight: height,
928 position: {
929 mx: ((width / 2 + attrs.adhoc) / width),
930 my: (height - 15) / height
931 }
932 });
933
934 drawMarker('adhoc', parentGfx, markerPath, {
935 strokeWidth: 1,
936 fill: getStrokeColor(element, defaultStrokeColor, attrs.stroke),
937 stroke: getStrokeColor(element, defaultStrokeColor, attrs.stroke)
938 });
939 }
940 };
941
942 function renderTaskMarker(type, parentGfx, element, attrs) {
943 taskMarkerRenderers[ type ](parentGfx, element, attrs);
944 }
945
946 function renderTaskMarkers(parentGfx, element, taskMarkers, attrs = {}) {
947 attrs = {
948 fill: attrs.fill,
949 stroke: attrs.stroke,
950 width: getWidth(element, attrs),
951 height: getHeight(element, attrs)
952 };
953
954 var semantic = getSemantic(element);
955
956 var subprocess = taskMarkers && taskMarkers.includes('SubProcessMarker');
957
958 if (subprocess) {
959 attrs = {
960 ...attrs,
961 seq: -21,
962 parallel: -22,
963 compensation: -42,
964 loop: -18,
965 adhoc: 10
966 };
967 } else {
968 attrs = {
969 ...attrs,
970 seq: -5,
971 parallel: -6,
972 compensation: -27,
973 loop: 0,
974 adhoc: 10
975 };
976 }
977
978 forEach(taskMarkers, function(marker) {
979 renderTaskMarker(marker, parentGfx, element, attrs);
980 });
981
982 if (semantic.get('isForCompensation')) {
983 renderTaskMarker('CompensationMarker', parentGfx, element, attrs);
984 }
985
986 if (is(semantic, 'bpmn:AdHocSubProcess')) {
987 renderTaskMarker('AdhocMarker', parentGfx, element, attrs);
988 }
989
990 var loopCharacteristics = semantic.get('loopCharacteristics'),
991 isSequential = loopCharacteristics && loopCharacteristics.get('isSequential');
992
993 if (loopCharacteristics) {
994
995 if (isSequential === undefined) {
996 renderTaskMarker('LoopMarker', parentGfx, element, attrs);
997 }
998
999 if (isSequential === false) {
1000 renderTaskMarker('ParallelMarker', parentGfx, element, attrs);
1001 }
1002
1003 if (isSequential === true) {
1004 renderTaskMarker('SequentialMarker', parentGfx, element, attrs);
1005 }
1006 }
1007 }
1008
1009 function renderLabel(parentGfx, label, attrs = {}) {
1010 attrs = assign({
1011 size: {
1012 width: 100
1013 }
1014 }, attrs);
1015
1016 var text = textRenderer.createText(label || '', attrs);
1017
1018 svgClasses(text).add('djs-label');
1019
1020 svgAppend(parentGfx, text);
1021
1022 return text;
1023 }
1024
1025 function renderEmbeddedLabel(parentGfx, element, align, attrs = {}) {
1026 var semantic = getSemantic(element);
1027
1028 var box = getBounds({
1029 x: element.x,
1030 y: element.y,
1031 width: element.width,
1032 height: element.height
1033 }, attrs);
1034
1035 return renderLabel(parentGfx, semantic.name, {
1036 align,
1037 box,
1038 padding: 7,
1039 style: {
1040 fill: getLabelColor(element, defaultLabelColor, defaultStrokeColor, attrs.stroke)
1041 }
1042 });
1043 }
1044
1045 function renderExternalLabel(parentGfx, element, attrs = {}) {
1046 var box = {
1047 width: 90,
1048 height: 30,
1049 x: element.width / 2 + element.x,
1050 y: element.height / 2 + element.y
1051 };
1052
1053 return renderLabel(parentGfx, getLabel(element), {
1054 box: box,
1055 fitBox: true,
1056 style: assign(
1057 {},
1058 textRenderer.getExternalStyle(),
1059 {
1060 fill: getLabelColor(element, defaultLabelColor, defaultStrokeColor, attrs.stroke)
1061 }
1062 )
1063 });
1064 }
1065
1066 function renderLaneLabel(parentGfx, text, element, attrs = {}) {
1067 var isHorizontalLane = isHorizontal(element);
1068
1069 var textBox = renderLabel(parentGfx, text, {
1070 box: {
1071 height: 30,
1072 width: isHorizontalLane ? getHeight(element, attrs) : getWidth(element, attrs),
1073 },
1074 align: 'center-middle',
1075 style: {
1076 fill: getLabelColor(element, defaultLabelColor, defaultStrokeColor, attrs.stroke)
1077 }
1078 });
1079
1080 if (isHorizontalLane) {
1081 var top = -1 * getHeight(element, attrs);
1082 transform(textBox, 0, -top, 270);
1083 }
1084 }
1085
1086 function renderActivity(parentGfx, element, attrs = {}) {
1087 var {
1088 width,
1089 height
1090 } = getBounds(element, attrs);
1091
1092 return drawRect(parentGfx, width, height, TASK_BORDER_RADIUS, {
1093 ...attrs,
1094 fill: getFillColor(element, defaultFillColor, attrs.fill),
1095 fillOpacity: DEFAULT_OPACITY,
1096 stroke: getStrokeColor(element, defaultStrokeColor, attrs.stroke)
1097 });
1098 }
1099
1100 function renderAssociation(parentGfx, element, attrs = {}) {
1101 var semantic = getSemantic(element);
1102
1103 var fill = getFillColor(element, defaultFillColor, attrs.fill),
1104 stroke = getStrokeColor(element, defaultStrokeColor, attrs.stroke);
1105
1106 if (semantic.get('associationDirection') === 'One' ||
1107 semantic.get('associationDirection') === 'Both') {
1108 attrs.markerEnd = marker('association-end', fill, stroke);
1109 }
1110
1111 if (semantic.get('associationDirection') === 'Both') {
1112 attrs.markerStart = marker('association-start', fill, stroke);
1113 }
1114
1115 attrs = pickAttrs(attrs, [
1116 'markerStart',
1117 'markerEnd'
1118 ]);
1119
1120 return drawConnectionSegments(parentGfx, element.waypoints, {
1121 ...attrs,
1122 stroke,
1123 strokeDasharray: '0, 5'
1124 });
1125 }
1126
1127 function renderDataObject(parentGfx, element, attrs = {}) {
1128 var fill = getFillColor(element, defaultFillColor, attrs.fill),
1129 stroke = getStrokeColor(element, defaultStrokeColor, attrs.stroke);
1130
1131 var pathData = pathMap.getScaledPath('DATA_OBJECT_PATH', {
1132 xScaleFactor: 1,
1133 yScaleFactor: 1,
1134 containerWidth: element.width,
1135 containerHeight: element.height,
1136 position: {
1137 mx: 0.474,
1138 my: 0.296
1139 }
1140 });
1141
1142 var dataObject = drawPath(parentGfx, pathData, {
1143 fill,
1144 fillOpacity: DEFAULT_OPACITY,
1145 stroke
1146 });
1147
1148 var semantic = getSemantic(element);
1149
1150 if (isCollection(semantic)) {
1151 var collectionPathData = pathMap.getScaledPath('DATA_OBJECT_COLLECTION_PATH', {
1152 xScaleFactor: 1,
1153 yScaleFactor: 1,
1154 containerWidth: element.width,
1155 containerHeight: element.height,
1156 position: {
1157 mx: 0.33,
1158 my: (element.height - 18) / element.height
1159 }
1160 });
1161
1162 drawPath(parentGfx, collectionPathData, {
1163 strokeWidth: 2,
1164 fill,
1165 stroke
1166 });
1167 }
1168
1169 return dataObject;
1170 }
1171
1172 function renderEvent(parentGfx, element, attrs = {}) {
1173 return drawCircle(parentGfx, element.width, element.height, {
1174 fillOpacity: DEFAULT_OPACITY,
1175 ...attrs,
1176 fill: getFillColor(element, defaultFillColor, attrs.fill),
1177 stroke: getStrokeColor(element, defaultStrokeColor, attrs.stroke)
1178 });
1179 }
1180
1181 function renderGateway(parentGfx, element, attrs = {}) {
1182 return drawDiamond(parentGfx, element.width, element.height, {
1183 fill: getFillColor(element, defaultFillColor, attrs.fill),
1184 fillOpacity: DEFAULT_OPACITY,
1185 stroke: getStrokeColor(element, defaultStrokeColor, attrs.stroke)
1186 });
1187 }
1188
1189 function renderLane(parentGfx, element, attrs = {}) {
1190 var lane = drawRect(parentGfx, getWidth(element, attrs), getHeight(element, attrs), 0, {
1191 fill: getFillColor(element, defaultFillColor, attrs.fill),
1192 fillOpacity: attrs.fillOpacity || DEFAULT_OPACITY,
1193 stroke: getStrokeColor(element, defaultStrokeColor, attrs.stroke),
1194 strokeWidth: 1.5
1195 });
1196
1197 var semantic = getSemantic(element);
1198
1199 if (is(semantic, 'bpmn:Lane')) {
1200 var text = semantic.get('name');
1201
1202 renderLaneLabel(parentGfx, text, element, attrs);
1203 }
1204
1205 return lane;
1206 }
1207
1208 function renderSubProcess(parentGfx, element, attrs = {}) {
1209 var activity = renderActivity(parentGfx, element, attrs);
1210
1211 if (isEventSubProcess(element)) {
1212 svgAttr(activity, {
1213 strokeDasharray: '0, 5.5',
1214 strokeWidth: 2.5
1215 });
1216 }
1217
1218 var expanded = isExpanded(element);
1219
1220 renderEmbeddedLabel(parentGfx, element, expanded ? 'center-top' : 'center-middle', attrs);
1221
1222 if (expanded) {
1223 renderTaskMarkers(parentGfx, element, undefined, attrs);
1224 } else {
1225 renderTaskMarkers(parentGfx, element, [ 'SubProcessMarker' ], attrs);
1226 }
1227
1228 return activity;
1229 }
1230
1231 function renderTask(parentGfx, element, attrs = {}) {
1232 var activity = renderActivity(parentGfx, element, attrs);
1233
1234 renderEmbeddedLabel(parentGfx, element, 'center-middle', attrs);
1235
1236 renderTaskMarkers(parentGfx, element, undefined, attrs);
1237
1238 return activity;
1239 }
1240
1241 var handlers = this.handlers = {
1242 'bpmn:AdHocSubProcess': function(parentGfx, element, attrs = {}) {
1243 if (isExpanded(element)) {
1244 attrs = pickAttrs(attrs, [
1245 'fill',
1246 'stroke',
1247 'width',
1248 'height'
1249 ]);
1250 } else {
1251 attrs = pickAttrs(attrs, [
1252 'fill',
1253 'stroke'
1254 ]);
1255 }
1256
1257 return renderSubProcess(parentGfx, element, attrs);
1258 },
1259 'bpmn:Association': function(parentGfx, element, attrs = {}) {
1260 attrs = pickAttrs(attrs, [
1261 'fill',
1262 'stroke'
1263 ]);
1264
1265 return renderAssociation(parentGfx, element, attrs);
1266 },
1267 'bpmn:BoundaryEvent': function(parentGfx, element, attrs = {}) {
1268 var { renderIcon = true } = attrs;
1269
1270 attrs = pickAttrs(attrs, [
1271 'fill',
1272 'stroke'
1273 ]);
1274
1275 var semantic = getSemantic(element),
1276 cancelActivity = semantic.get('cancelActivity');
1277
1278 attrs = {
1279 strokeWidth: 1.5,
1280 fill: getFillColor(element, defaultFillColor, attrs.fill),
1281 fillOpacity: FULL_OPACITY,
1282 stroke: getStrokeColor(element, defaultStrokeColor, attrs.stroke)
1283 };
1284
1285 if (!cancelActivity) {
1286 attrs.strokeDasharray = '6';
1287 }
1288
1289 var event = renderEvent(parentGfx, element, attrs);
1290
1291 drawCircle(parentGfx, element.width, element.height, INNER_OUTER_DIST, {
1292 ...attrs,
1293 fill: 'none'
1294 });
1295
1296 if (renderIcon) {
1297 renderEventIcon(element, parentGfx, attrs);
1298 }
1299
1300 return event;
1301 },
1302 'bpmn:BusinessRuleTask': function(parentGfx, element, attrs = {}) {
1303 attrs = pickAttrs(attrs, [
1304 'fill',
1305 'stroke'
1306 ]);
1307
1308 var task = renderTask(parentGfx, element, attrs);
1309
1310 var headerData = pathMap.getScaledPath('TASK_TYPE_BUSINESS_RULE_MAIN', {
1311 abspos: {
1312 x: 8,
1313 y: 8
1314 }
1315 });
1316
1317 var businessPath = drawPath(parentGfx, headerData);
1318
1319 svgAttr(businessPath, {
1320 fill: getFillColor(element, defaultFillColor, attrs.fill),
1321 stroke: getStrokeColor(element, defaultStrokeColor, attrs.stroke),
1322 strokeWidth: 1
1323 });
1324
1325 var headerPathData = pathMap.getScaledPath('TASK_TYPE_BUSINESS_RULE_HEADER', {
1326 abspos: {
1327 x: 8,
1328 y: 8
1329 }
1330 });
1331
1332 var businessHeaderPath = drawPath(parentGfx, headerPathData);
1333
1334 svgAttr(businessHeaderPath, {
1335 fill: getStrokeColor(element, defaultStrokeColor, attrs.stroke),
1336 stroke: getStrokeColor(element, defaultStrokeColor, attrs.stroke),
1337 strokeWidth: 1
1338 });
1339
1340 return task;
1341 },
1342 'bpmn:CallActivity': function(parentGfx, element, attrs = {}) {
1343 attrs = pickAttrs(attrs, [
1344 'fill',
1345 'stroke'
1346 ]);
1347
1348 return renderSubProcess(parentGfx, element, {
1349 strokeWidth: 5,
1350 ...attrs
1351 });
1352 },
1353 'bpmn:ComplexGateway': function(parentGfx, element, attrs = {}) {
1354 attrs = pickAttrs(attrs, [
1355 'fill',
1356 'stroke'
1357 ]);
1358
1359 var gateway = renderGateway(parentGfx, element, attrs);
1360
1361 var pathData = pathMap.getScaledPath('GATEWAY_COMPLEX', {
1362 xScaleFactor: 0.5,
1363 yScaleFactor:0.5,
1364 containerWidth: element.width,
1365 containerHeight: element.height,
1366 position: {
1367 mx: 0.46,
1368 my: 0.26
1369 }
1370 });
1371
1372 drawPath(parentGfx, pathData, {
1373 fill: getStrokeColor(element, defaultStrokeColor, attrs.stroke),
1374 stroke: getStrokeColor(element, defaultStrokeColor, attrs.stroke),
1375 strokeWidth: 1
1376 });
1377
1378 return gateway;
1379 },
1380 'bpmn:DataInput': function(parentGfx, element, attrs = {}) {
1381 attrs = pickAttrs(attrs, [
1382 'fill',
1383 'stroke'
1384 ]);
1385
1386 var arrowPathData = pathMap.getRawPath('DATA_ARROW');
1387
1388 var dataObject = renderDataObject(parentGfx, element, attrs);
1389
1390 drawPath(parentGfx, arrowPathData, {
1391 fill: 'none',
1392 stroke: getStrokeColor(element, defaultStrokeColor, attrs.stroke),
1393 strokeWidth: 1
1394 });
1395
1396 return dataObject;
1397 },
1398 'bpmn:DataInputAssociation': function(parentGfx, element, attrs = {}) {
1399 attrs = pickAttrs(attrs, [
1400 'fill',
1401 'stroke'
1402 ]);
1403
1404 return renderAssociation(parentGfx, element, {
1405 ...attrs,
1406 markerEnd: marker('association-end', getFillColor(element, defaultFillColor, attrs.fill), getStrokeColor(element, defaultStrokeColor, attrs.stroke))
1407 });
1408 },
1409 'bpmn:DataObject': function(parentGfx, element, attrs = {}) {
1410 attrs = pickAttrs(attrs, [
1411 'fill',
1412 'stroke'
1413 ]);
1414
1415 return renderDataObject(parentGfx, element, attrs);
1416 },
1417 'bpmn:DataObjectReference': as('bpmn:DataObject'),
1418 'bpmn:DataOutput': function(parentGfx, element, attrs = {}) {
1419 attrs = pickAttrs(attrs, [
1420 'fill',
1421 'stroke'
1422 ]);
1423
1424 var arrowPathData = pathMap.getRawPath('DATA_ARROW');
1425
1426 var dataObject = renderDataObject(parentGfx, element, attrs);
1427
1428 drawPath(parentGfx, arrowPathData, {
1429 strokeWidth: 1,
1430 fill: getFillColor(element, defaultFillColor, attrs.fill),
1431 stroke: getStrokeColor(element, defaultStrokeColor, attrs.stroke)
1432 });
1433
1434 return dataObject;
1435 },
1436 'bpmn:DataOutputAssociation': function(parentGfx, element, attrs = {}) {
1437 attrs = pickAttrs(attrs, [
1438 'fill',
1439 'stroke'
1440 ]);
1441
1442 return renderAssociation(parentGfx, element, {
1443 ...attrs,
1444 markerEnd: marker('association-end', getFillColor(element, defaultFillColor, attrs.fill), getStrokeColor(element, defaultStrokeColor, attrs.stroke))
1445 });
1446 },
1447 'bpmn:DataStoreReference': function(parentGfx, element, attrs = {}) {
1448 attrs = pickAttrs(attrs, [
1449 'fill',
1450 'stroke'
1451 ]);
1452
1453 var dataStorePath = pathMap.getScaledPath('DATA_STORE', {
1454 xScaleFactor: 1,
1455 yScaleFactor: 1,
1456 containerWidth: element.width,
1457 containerHeight: element.height,
1458 position: {
1459 mx: 0,
1460 my: 0.133
1461 }
1462 });
1463
1464 return drawPath(parentGfx, dataStorePath, {
1465 fill: getFillColor(element, defaultFillColor, attrs.fill),
1466 fillOpacity: DEFAULT_OPACITY,
1467 stroke: getStrokeColor(element, defaultStrokeColor, attrs.stroke),
1468 strokeWidth: 2
1469 });
1470 },
1471 'bpmn:EndEvent': function(parentGfx, element, attrs = {}) {
1472 var { renderIcon = true } = attrs;
1473
1474 attrs = pickAttrs(attrs, [
1475 'fill',
1476 'stroke'
1477 ]);
1478
1479 var event = renderEvent(parentGfx, element, {
1480 ...attrs,
1481 strokeWidth: 4
1482 });
1483
1484 if (renderIcon) {
1485 renderEventIcon(element, parentGfx, attrs);
1486 }
1487
1488 return event;
1489 },
1490 'bpmn:EventBasedGateway': function(parentGfx, element, attrs = {}) {
1491 attrs = pickAttrs(attrs, [
1492 'fill',
1493 'stroke'
1494 ]);
1495
1496 var semantic = getSemantic(element);
1497
1498 var diamond = renderGateway(parentGfx, element, attrs);
1499
1500 drawCircle(parentGfx, element.width, element.height, element.height * 0.20, {
1501 fill: getFillColor(element, 'none', attrs.fill),
1502 stroke: getStrokeColor(element, defaultStrokeColor, attrs.stroke),
1503 strokeWidth: 1
1504 });
1505
1506 var type = semantic.get('eventGatewayType'),
1507 instantiate = !!semantic.get('instantiate');
1508
1509 function drawEvent() {
1510
1511 var pathData = pathMap.getScaledPath('GATEWAY_EVENT_BASED', {
1512 xScaleFactor: 0.18,
1513 yScaleFactor: 0.18,
1514 containerWidth: element.width,
1515 containerHeight: element.height,
1516 position: {
1517 mx: 0.36,
1518 my: 0.44
1519 }
1520 });
1521
1522 drawPath(parentGfx, pathData, {
1523 fill: 'none',
1524 stroke: getStrokeColor(element, defaultStrokeColor, attrs.stroke),
1525 strokeWidth: 2
1526 });
1527 }
1528
1529 if (type === 'Parallel') {
1530 var pathData = pathMap.getScaledPath('GATEWAY_PARALLEL', {
1531 xScaleFactor: 0.4,
1532 yScaleFactor: 0.4,
1533 containerWidth: element.width,
1534 containerHeight: element.height,
1535 position: {
1536 mx: 0.474,
1537 my: 0.296
1538 }
1539 });
1540
1541 drawPath(parentGfx, pathData, {
1542 fill: 'none',
1543 stroke: getStrokeColor(element, defaultStrokeColor, attrs.stroke),
1544 strokeWidth: 1
1545 });
1546 } else if (type === 'Exclusive') {
1547 if (!instantiate) {
1548 drawCircle(parentGfx, element.width, element.height, element.height * 0.26, {
1549 fill: 'none',
1550 stroke: getStrokeColor(element, defaultStrokeColor, attrs.stroke),
1551 strokeWidth: 1
1552 });
1553 }
1554
1555 drawEvent();
1556 }
1557
1558
1559 return diamond;
1560 },
1561 'bpmn:ExclusiveGateway': function(parentGfx, element, attrs = {}) {
1562 attrs = pickAttrs(attrs, [
1563 'fill',
1564 'stroke'
1565 ]);
1566
1567 var gateway = renderGateway(parentGfx, element, attrs);
1568
1569 var pathData = pathMap.getScaledPath('GATEWAY_EXCLUSIVE', {
1570 xScaleFactor: 0.4,
1571 yScaleFactor: 0.4,
1572 containerWidth: element.width,
1573 containerHeight: element.height,
1574 position: {
1575 mx: 0.32,
1576 my: 0.3
1577 }
1578 });
1579
1580 var di = getDi(element);
1581
1582 if (di.get('isMarkerVisible')) {
1583 drawPath(parentGfx, pathData, {
1584 fill: getStrokeColor(element, defaultStrokeColor, attrs.stroke),
1585 stroke: getStrokeColor(element, defaultStrokeColor, attrs.stroke),
1586 strokeWidth: 1
1587 });
1588 }
1589
1590 return gateway;
1591 },
1592 'bpmn:Gateway': function(parentGfx, element, attrs = {}) {
1593 attrs = pickAttrs(attrs, [
1594 'fill',
1595 'stroke'
1596 ]);
1597
1598 return renderGateway(parentGfx, element, attrs);
1599 },
1600 'bpmn:Group': function(parentGfx, element, attrs = {}) {
1601 attrs = pickAttrs(attrs, [
1602 'fill',
1603 'stroke',
1604 'width',
1605 'height'
1606 ]);
1607
1608 return drawRect(parentGfx, element.width, element.height, TASK_BORDER_RADIUS, {
1609 stroke: getStrokeColor(element, defaultStrokeColor, attrs.stroke),
1610 strokeWidth: 1.5,
1611 strokeDasharray: '10, 6, 0, 6',
1612 fill: 'none',
1613 pointerEvents: 'none',
1614 width: getWidth(element, attrs),
1615 height: getHeight(element, attrs)
1616 });
1617 },
1618 'bpmn:InclusiveGateway': function(parentGfx, element, attrs = {}) {
1619 attrs = pickAttrs(attrs, [
1620 'fill',
1621 'stroke'
1622 ]);
1623
1624 var gateway = renderGateway(parentGfx, element, attrs);
1625
1626 drawCircle(parentGfx, element.width, element.height, element.height * 0.24, {
1627 fill: getFillColor(element, defaultFillColor, attrs.fill),
1628 stroke: getStrokeColor(element, defaultStrokeColor, attrs.stroke),
1629 strokeWidth: 2.5
1630 });
1631
1632 return gateway;
1633 },
1634 'bpmn:IntermediateEvent': function(parentGfx, element, attrs = {}) {
1635 var { renderIcon = true } = attrs;
1636
1637 attrs = pickAttrs(attrs, [
1638 'fill',
1639 'stroke'
1640 ]);
1641
1642 var outer = renderEvent(parentGfx, element, {
1643 ...attrs,
1644 strokeWidth: 1.5
1645 });
1646
1647 drawCircle(parentGfx, element.width, element.height, INNER_OUTER_DIST, {
1648 fill: 'none',
1649 stroke: getStrokeColor(element, defaultStrokeColor, attrs.stroke),
1650 strokeWidth: 1.5
1651 });
1652
1653 if (renderIcon) {
1654 renderEventIcon(element, parentGfx, attrs);
1655 }
1656
1657 return outer;
1658 },
1659 'bpmn:IntermediateCatchEvent': as('bpmn:IntermediateEvent'),
1660 'bpmn:IntermediateThrowEvent': as('bpmn:IntermediateEvent'),
1661 'bpmn:Lane': function(parentGfx, element, attrs = {}) {
1662 attrs = pickAttrs(attrs, [
1663 'fill',
1664 'stroke',
1665 'width',
1666 'height'
1667 ]);
1668
1669 return renderLane(parentGfx, element, {
1670 ...attrs,
1671 fillOpacity: LOW_OPACITY
1672 });
1673 },
1674 'bpmn:ManualTask': function(parentGfx, element, attrs = {}) {
1675 attrs = pickAttrs(attrs, [
1676 'fill',
1677 'stroke'
1678 ]);
1679
1680 var task = renderTask(parentGfx, element, attrs);
1681
1682 var pathData = pathMap.getScaledPath('TASK_TYPE_MANUAL', {
1683 abspos: {
1684 x: 17,
1685 y: 15
1686 }
1687 });
1688
1689 drawPath(parentGfx, pathData, {
1690 fill: getFillColor(element, defaultFillColor, attrs.fill),
1691 stroke: getStrokeColor(element, defaultStrokeColor, attrs.stroke),
1692 strokeWidth: 0.5
1693 });
1694
1695 return task;
1696 },
1697 'bpmn:MessageFlow': function(parentGfx, element, attrs = {}) {
1698 attrs = pickAttrs(attrs, [
1699 'fill',
1700 'stroke'
1701 ]);
1702
1703 var semantic = getSemantic(element),
1704 di = getDi(element);
1705
1706 var fill = getFillColor(element, defaultFillColor, attrs.fill),
1707 stroke = getStrokeColor(element, defaultStrokeColor, attrs.stroke);
1708
1709 var path = drawConnectionSegments(parentGfx, element.waypoints, {
1710 markerEnd: marker('messageflow-end', fill, stroke),
1711 markerStart: marker('messageflow-start', fill, stroke),
1712 stroke,
1713 strokeDasharray: '10, 11',
1714 strokeWidth: 1.5
1715 });
1716
1717 if (semantic.get('messageRef')) {
1718 var midPoint = path.getPointAtLength(path.getTotalLength() / 2);
1719
1720 var markerPathData = pathMap.getScaledPath('MESSAGE_FLOW_MARKER', {
1721 abspos: {
1722 x: midPoint.x,
1723 y: midPoint.y
1724 }
1725 });
1726
1727 var messageAttrs = {
1728 strokeWidth: 1
1729 };
1730
1731 if (di.get('messageVisibleKind') === 'initiating') {
1732 messageAttrs.fill = fill;
1733 messageAttrs.stroke = stroke;
1734 } else {
1735 messageAttrs.fill = stroke;
1736 messageAttrs.stroke = fill;
1737 }
1738
1739 var message = drawPath(parentGfx, markerPathData, messageAttrs);
1740
1741 var messageRef = semantic.get('messageRef'),
1742 name = messageRef.get('name');
1743
1744 var label = renderLabel(parentGfx, name, {
1745 align: 'center-top',
1746 fitBox: true,
1747 style: {
1748 fill: stroke
1749 }
1750 });
1751
1752 var messageBounds = message.getBBox(),
1753 labelBounds = label.getBBox();
1754
1755 var translateX = midPoint.x - labelBounds.width / 2,
1756 translateY = midPoint.y + messageBounds.height / 2 + ELEMENT_LABEL_DISTANCE;
1757
1758 transform(label, translateX, translateY, 0);
1759 }
1760
1761 return path;
1762 },
1763 'bpmn:ParallelGateway': function(parentGfx, element, attrs = {}) {
1764 attrs = pickAttrs(attrs, [
1765 'fill',
1766 'stroke'
1767 ]);
1768
1769 var diamond = renderGateway(parentGfx, element, attrs);
1770
1771 var pathData = pathMap.getScaledPath('GATEWAY_PARALLEL', {
1772 xScaleFactor: 0.6,
1773 yScaleFactor: 0.6,
1774 containerWidth: element.width,
1775 containerHeight: element.height,
1776 position: {
1777 mx: 0.46,
1778 my: 0.2
1779 }
1780 });
1781
1782 drawPath(parentGfx, pathData, {
1783 fill: getStrokeColor(element, defaultStrokeColor, attrs.stroke),
1784 stroke: getStrokeColor(element, defaultStrokeColor, attrs.stroke),
1785 strokeWidth: 1
1786 });
1787
1788 return diamond;
1789 },
1790 'bpmn:Participant': function(parentGfx, element, attrs = {}) {
1791 attrs = pickAttrs(attrs, [
1792 'fill',
1793 'stroke',
1794 'width',
1795 'height'
1796 ]);
1797
1798 var participant = renderLane(parentGfx, element, attrs);
1799
1800 var expandedParticipant = isExpanded(element);
1801 var horizontalParticipant = isHorizontal(element);
1802
1803 var semantic = getSemantic(element),
1804 name = semantic.get('name');
1805
1806 if (expandedParticipant) {
1807 var waypoints = horizontalParticipant ? [
1808 {
1809 x: 30,
1810 y: 0
1811 },
1812 {
1813 x: 30,
1814 y: getHeight(element, attrs)
1815 }
1816 ] : [
1817 {
1818 x: 0,
1819 y: 30
1820 },
1821 {
1822 x: getWidth(element, attrs),
1823 y: 30
1824 }
1825 ];
1826
1827 drawLine(parentGfx, waypoints, {
1828 stroke: getStrokeColor(element, defaultStrokeColor, attrs.stroke),
1829 strokeWidth: PARTICIPANT_STROKE_WIDTH
1830 });
1831
1832 renderLaneLabel(parentGfx, name, element, attrs);
1833 } else {
1834 var bounds = getBounds(element, attrs);
1835
1836 if (!horizontalParticipant) {
1837 bounds.height = getWidth(element, attrs);
1838 bounds.width = getHeight(element, attrs);
1839 }
1840
1841 var textBox = renderLabel(parentGfx, name, {
1842 box: bounds,
1843 align: 'center-middle',
1844 style: {
1845 fill: getLabelColor(element, defaultLabelColor, defaultStrokeColor, attrs.stroke)
1846 }
1847 });
1848
1849 if (!horizontalParticipant) {
1850 var top = -1 * getHeight(element, attrs);
1851 transform(textBox, 0, -top, 270);
1852 }
1853 }
1854
1855 if (semantic.get('participantMultiplicity')) {
1856 renderTaskMarker('ParticipantMultiplicityMarker', parentGfx, element, attrs);
1857 }
1858
1859 return participant;
1860 },
1861 'bpmn:ReceiveTask' : function(parentGfx, element, attrs = {}) {
1862 attrs = pickAttrs(attrs, [
1863 'fill',
1864 'stroke'
1865 ]);
1866
1867 var semantic = getSemantic(element);
1868
1869 var task = renderTask(parentGfx, element, attrs);
1870
1871 var pathData;
1872
1873 if (semantic.get('instantiate')) {
1874 drawCircle(parentGfx, 28, 28, 20 * 0.22, {
1875 fill: getFillColor(element, defaultFillColor, attrs.fill),
1876 stroke: getStrokeColor(element, defaultStrokeColor, attrs.stroke),
1877 strokeWidth: 1
1878 });
1879
1880 pathData = pathMap.getScaledPath('TASK_TYPE_INSTANTIATING_SEND', {
1881 abspos: {
1882 x: 7.77,
1883 y: 9.52
1884 }
1885 });
1886 } else {
1887 pathData = pathMap.getScaledPath('TASK_TYPE_SEND', {
1888 xScaleFactor: 0.9,
1889 yScaleFactor: 0.9,
1890 containerWidth: 21,
1891 containerHeight: 14,
1892 position: {
1893 mx: 0.3,
1894 my: 0.4
1895 }
1896 });
1897 }
1898
1899 drawPath(parentGfx, pathData, {
1900 fill: getFillColor(element, defaultFillColor, attrs.fill),
1901 stroke: getStrokeColor(element, defaultStrokeColor, attrs.stroke),
1902 strokeWidth: 1
1903 });
1904
1905 return task;
1906 },
1907 'bpmn:ScriptTask': function(parentGfx, element, attrs = {}) {
1908 attrs = pickAttrs(attrs, [
1909 'fill',
1910 'stroke'
1911 ]);
1912
1913 var task = renderTask(parentGfx, element, attrs);
1914
1915 var pathData = pathMap.getScaledPath('TASK_TYPE_SCRIPT', {
1916 abspos: {
1917 x: 15,
1918 y: 20
1919 }
1920 });
1921
1922 drawPath(parentGfx, pathData, {
1923 fill: getFillColor(element, defaultFillColor, attrs.fill),
1924 stroke: getStrokeColor(element, defaultStrokeColor, attrs.stroke),
1925 strokeWidth: 1
1926 });
1927
1928 return task;
1929 },
1930 'bpmn:SendTask': function(parentGfx, element, attrs = {}) {
1931 attrs = pickAttrs(attrs, [
1932 'fill',
1933 'stroke'
1934 ]);
1935
1936 var task = renderTask(parentGfx, element, attrs);
1937
1938 var pathData = pathMap.getScaledPath('TASK_TYPE_SEND', {
1939 xScaleFactor: 1,
1940 yScaleFactor: 1,
1941 containerWidth: 21,
1942 containerHeight: 14,
1943 position: {
1944 mx: 0.285,
1945 my: 0.357
1946 }
1947 });
1948
1949 drawPath(parentGfx, pathData, {
1950 fill: getStrokeColor(element, defaultStrokeColor, attrs.stroke),
1951 stroke: getFillColor(element, defaultFillColor, attrs.fill),
1952 strokeWidth: 1
1953 });
1954
1955 return task;
1956 },
1957 'bpmn:SequenceFlow': function(parentGfx, element, attrs = {}) {
1958 attrs = pickAttrs(attrs, [
1959 'fill',
1960 'stroke'
1961 ]);
1962
1963 var fill = getFillColor(element, defaultFillColor, attrs.fill),
1964 stroke = getStrokeColor(element, defaultStrokeColor, attrs.stroke);
1965
1966 var connection = drawConnectionSegments(parentGfx, element.waypoints, {
1967 markerEnd: marker('sequenceflow-end', fill, stroke),
1968 stroke
1969 });
1970
1971 var semantic = getSemantic(element);
1972
1973 var { source } = element;
1974
1975 if (source) {
1976 var sourceSemantic = getSemantic(source);
1977
1978 // conditional flow marker
1979 if (semantic.get('conditionExpression') && is(sourceSemantic, 'bpmn:Activity')) {
1980 svgAttr(connection, {
1981 markerStart: marker('conditional-flow-marker', fill, stroke)
1982 });
1983 }
1984
1985 // default marker
1986 if (sourceSemantic.get('default') && (is(sourceSemantic, 'bpmn:Gateway') || is(sourceSemantic, 'bpmn:Activity')) &&
1987 sourceSemantic.get('default') === semantic) {
1988 svgAttr(connection, {
1989 markerStart: marker('conditional-default-flow-marker', fill, stroke)
1990 });
1991 }
1992 }
1993
1994 return connection;
1995 },
1996 'bpmn:ServiceTask': function(parentGfx, element, attrs = {}) {
1997 attrs = pickAttrs(attrs, [
1998 'fill',
1999 'stroke'
2000 ]);
2001
2002 var task = renderTask(parentGfx, element, attrs);
2003
2004 drawCircle(parentGfx, 10, 10, {
2005 fill: getFillColor(element, defaultFillColor, attrs.fill),
2006 stroke: 'none',
2007 transform: 'translate(6, 6)'
2008 });
2009
2010 var pathDataService1 = pathMap.getScaledPath('TASK_TYPE_SERVICE', {
2011 abspos: {
2012 x: 12,
2013 y: 18
2014 }
2015 });
2016
2017 drawPath(parentGfx, pathDataService1, {
2018 fill: getFillColor(element, defaultFillColor, attrs.fill),
2019 stroke: getStrokeColor(element, defaultStrokeColor, attrs.stroke),
2020 strokeWidth: 1
2021 });
2022
2023 drawCircle(parentGfx, 10, 10, {
2024 fill: getFillColor(element, defaultFillColor, attrs.fill),
2025 stroke: 'none',
2026 transform: 'translate(11, 10)'
2027 });
2028
2029 var pathDataService2 = pathMap.getScaledPath('TASK_TYPE_SERVICE', {
2030 abspos: {
2031 x: 17,
2032 y: 22
2033 }
2034 });
2035
2036 drawPath(parentGfx, pathDataService2, {
2037 fill: getFillColor(element, defaultFillColor, attrs.fill),
2038 stroke: getStrokeColor(element, defaultStrokeColor, attrs.stroke),
2039 strokeWidth: 1
2040 });
2041
2042 return task;
2043 },
2044 'bpmn:StartEvent': function(parentGfx, element, attrs = {}) {
2045 var { renderIcon = true } = attrs;
2046
2047 attrs = pickAttrs(attrs, [
2048 'fill',
2049 'stroke'
2050 ]);
2051
2052 var semantic = getSemantic(element);
2053
2054 if (!semantic.get('isInterrupting')) {
2055 attrs = {
2056 ...attrs,
2057 strokeDasharray: '6'
2058 };
2059 }
2060
2061 var event = renderEvent(parentGfx, element, attrs);
2062
2063 if (renderIcon) {
2064 renderEventIcon(element, parentGfx, attrs);
2065 }
2066
2067 return event;
2068 },
2069 'bpmn:SubProcess': function(parentGfx, element, attrs = {}) {
2070 if (isExpanded(element)) {
2071 attrs = pickAttrs(attrs, [
2072 'fill',
2073 'stroke',
2074 'width',
2075 'height'
2076 ]);
2077 } else {
2078 attrs = pickAttrs(attrs, [
2079 'fill',
2080 'stroke'
2081 ]);
2082 }
2083
2084 return renderSubProcess(parentGfx, element, attrs);
2085 },
2086 'bpmn:Task': function(parentGfx, element, attrs = {}) {
2087 attrs = pickAttrs(attrs, [
2088 'fill',
2089 'stroke'
2090 ]);
2091
2092 return renderTask(parentGfx, element, attrs);
2093 },
2094 'bpmn:TextAnnotation': function(parentGfx, element, attrs = {}) {
2095 attrs = pickAttrs(attrs, [
2096 'fill',
2097 'stroke',
2098 'width',
2099 'height'
2100 ]);
2101
2102 var {
2103 width,
2104 height
2105 } = getBounds(element, attrs);
2106
2107 var textElement = drawRect(parentGfx, width, height, 0, 0, {
2108 fill: 'none',
2109 stroke: 'none'
2110 });
2111
2112 var textPathData = pathMap.getScaledPath('TEXT_ANNOTATION', {
2113 xScaleFactor: 1,
2114 yScaleFactor: 1,
2115 containerWidth: width,
2116 containerHeight: height,
2117 position: {
2118 mx: 0.0,
2119 my: 0.0
2120 }
2121 });
2122
2123 drawPath(parentGfx, textPathData, {
2124 stroke: getStrokeColor(element, defaultStrokeColor, attrs.stroke)
2125 });
2126
2127 var semantic = getSemantic(element),
2128 text = semantic.get('text') || '';
2129
2130 renderLabel(parentGfx, text, {
2131 align: 'left-top',
2132 box: getBounds(element, attrs),
2133 padding: 7,
2134 style: {
2135 fill: getLabelColor(element, defaultLabelColor, defaultStrokeColor, attrs.stroke)
2136 }
2137 });
2138
2139 return textElement;
2140 },
2141 'bpmn:Transaction': function(parentGfx, element, attrs = {}) {
2142 if (isExpanded(element)) {
2143 attrs = pickAttrs(attrs, [
2144 'fill',
2145 'stroke',
2146 'width',
2147 'height'
2148 ]);
2149 } else {
2150 attrs = pickAttrs(attrs, [
2151 'fill',
2152 'stroke'
2153 ]);
2154 }
2155
2156 var outer = renderSubProcess(parentGfx, element, {
2157 strokeWidth: 1.5,
2158 ...attrs
2159 });
2160
2161 var innerAttrs = styles.style([ 'no-fill', 'no-events' ], {
2162 stroke: getStrokeColor(element, defaultStrokeColor, attrs.stroke),
2163 strokeWidth: 1.5
2164 });
2165
2166 var expanded = isExpanded(element);
2167
2168 if (!expanded) {
2169 attrs = {};
2170 }
2171
2172 drawRect(
2173 parentGfx,
2174 getWidth(element, attrs),
2175 getHeight(element, attrs),
2176 TASK_BORDER_RADIUS - INNER_OUTER_DIST,
2177 INNER_OUTER_DIST,
2178 innerAttrs
2179 );
2180
2181 return outer;
2182 },
2183 'bpmn:UserTask': function(parentGfx, element, attrs = {}) {
2184 attrs = pickAttrs(attrs, [
2185 'fill',
2186 'stroke'
2187 ]);
2188
2189 var task = renderTask(parentGfx, element, attrs);
2190
2191 var x = 15;
2192 var y = 12;
2193
2194 var pathDataUser1 = pathMap.getScaledPath('TASK_TYPE_USER_1', {
2195 abspos: {
2196 x: x,
2197 y: y
2198 }
2199 });
2200
2201 drawPath(parentGfx, pathDataUser1, {
2202 fill: getFillColor(element, defaultFillColor, attrs.fill),
2203 stroke: getStrokeColor(element, defaultStrokeColor, attrs.stroke),
2204 strokeWidth: 0.5
2205 });
2206
2207 var pathDataUser2 = pathMap.getScaledPath('TASK_TYPE_USER_2', {
2208 abspos: {
2209 x: x,
2210 y: y
2211 }
2212 });
2213
2214 drawPath(parentGfx, pathDataUser2, {
2215 fill: getFillColor(element, defaultFillColor, attrs.fill),
2216 stroke: getStrokeColor(element, defaultStrokeColor, attrs.stroke),
2217 strokeWidth: 0.5
2218 });
2219
2220 var pathDataUser3 = pathMap.getScaledPath('TASK_TYPE_USER_3', {
2221 abspos: {
2222 x: x,
2223 y: y
2224 }
2225 });
2226
2227 drawPath(parentGfx, pathDataUser3, {
2228 fill: getStrokeColor(element, defaultStrokeColor, attrs.stroke),
2229 stroke: getStrokeColor(element, defaultStrokeColor, attrs.stroke),
2230 strokeWidth: 0.5
2231 });
2232
2233 return task;
2234 },
2235 'label': function(parentGfx, element, attrs = {}) {
2236 return renderExternalLabel(parentGfx, element, attrs);
2237 }
2238 };
2239
2240 // extension API, use at your own risk
2241 this._drawPath = drawPath;
2242
2243 this._renderer = renderer;
2244}
2245
2246
2247inherits(BpmnRenderer, BaseRenderer);
2248
2249BpmnRenderer.$inject = [
2250 'config.bpmnRenderer',
2251 'eventBus',
2252 'styles',
2253 'pathMap',
2254 'canvas',
2255 'textRenderer'
2256];
2257
2258
2259/**
2260 * @param {Element} element
2261 *
2262 * @return {boolean}
2263 */
2264BpmnRenderer.prototype.canRender = function(element) {
2265 return is(element, 'bpmn:BaseElement');
2266};
2267
2268/**
2269 * Draw shape into parentGfx.
2270 *
2271 * @param {SVGElement} parentGfx
2272 * @param {Element} element
2273 * @param {Attrs} [attrs]
2274 *
2275 * @return {SVGElement} mainGfx
2276 */
2277BpmnRenderer.prototype.drawShape = function(parentGfx, element, attrs = {}) {
2278 var { type } = element;
2279
2280 var handler = this._renderer(type);
2281
2282 return handler(parentGfx, element, attrs);
2283};
2284
2285/**
2286 * Draw connection into parentGfx.
2287 *
2288 * @param {SVGElement} parentGfx
2289 * @param {Element} element
2290 * @param {Attrs} [attrs]
2291 *
2292 * @return {SVGElement} mainGfx
2293 */
2294BpmnRenderer.prototype.drawConnection = function(parentGfx, element, attrs = {}) {
2295 var { type } = element;
2296
2297 var handler = this._renderer(type);
2298
2299 return handler(parentGfx, element, attrs);
2300};
2301
2302/**
2303 * Get shape path.
2304 *
2305 * @param {Element} element
2306 *
2307 * @return {string} path
2308 */
2309BpmnRenderer.prototype.getShapePath = function(element) {
2310 if (is(element, 'bpmn:Event')) {
2311 return getCirclePath(element);
2312 }
2313
2314 if (is(element, 'bpmn:Activity')) {
2315 return getRoundRectPath(element, TASK_BORDER_RADIUS);
2316 }
2317
2318 if (is(element, 'bpmn:Gateway')) {
2319 return getDiamondPath(element);
2320 }
2321
2322 return getRectPath(element);
2323};
2324
2325/**
2326 * Pick attributes if they exist.
2327 *
2328 * @param {Object} attrs
2329 * @param {string[]} keys
2330 *
2331 * @returns {Object}
2332 */
2333function pickAttrs(attrs, keys = []) {
2334 return keys.reduce((pickedAttrs, key) => {
2335 if (attrs[ key ]) {
2336 pickedAttrs[ key ] = attrs[ key ];
2337 }
2338
2339 return pickedAttrs;
2340 }, {});
2341}
\No newline at end of file