懒羊羊
2023-11-14 8286c62256f23bc2367a6729c0f46f84215e380b
提交 | 用户 | 时间
8286c6 1 /**
2  * Represents a canvas on which BPMN 2.0 constructs can be drawn.
3  * 
4  * Some of the icons used are licenced under a Creative Commons Attribution 2.5
5  * License, see http://www.famfamfam.com/lab/icons/silk/
6  * 
7  * @see ProcessDiagramGenerator
8  * @author (Java) Joram Barrez
9  * @author (Javascript) Dmitry Farafonov
10  */
11  
12 //Color.Cornsilk
13
14 var ARROW_HEAD_SIMPLE = "simple";
15 var ARROW_HEAD_EMPTY = "empty";
16 var ARROW_HEAD_FILL = "FILL";
17 var MULTILINE_VERTICAL_ALIGN_TOP = "top";
18 var MULTILINE_VERTICAL_ALIGN_MIDDLE = "middle";
19 var MULTILINE_VERTICAL_ALIGN_BOTTOM = "bottom";
20 var MULTILINE_HORIZONTAL_ALIGN_LEFT = "start";
21 var MULTILINE_HORIZONTAL_ALIGN_MIDDLE = "middle";
22 var MULTILINE_HORIZONTAL_ALIGN_RIGHT = "end";
23
24 // Predefined sized
25 var TEXT_PADDING = 3;
26 var ARROW_WIDTH = 4;
27 var CONDITIONAL_INDICATOR_WIDTH = 16;
28 var MARKER_WIDTH = 12;
29 var ANNOTATION_TEXT_PADDING = 7;
30
31 // Colors
32 var TASK_COLOR = Color.OldLace; // original: Color.get(255, 255, 204);
33 var TASK_STROKE_COLOR = Color.black; /*Color.SlateGrey; */
34 //var EXPANDED_SUBPROCESS_ATTRS = Color.black; /*Color.SlateGrey; */
35 var BOUNDARY_EVENT_COLOR = Color.white;
36 var CONDITIONAL_INDICATOR_COLOR = Color.get(255, 255, 255);
37 var HIGHLIGHT_COLOR = Color.Firebrick1;
38 //var SEQUENCEFLOW_COLOR = Color.DimGrey;
39 var SEQUENCEFLOW_COLOR = Color.black;
40
41 var CATCHING_EVENT_COLOR = Color.black; /* Color.SlateGrey; */
42 var START_EVENT_COLOR = Color.get(251,251,251);
43 var START_EVENT_STROKE_COLOR = Color.black; /* Color.SlateGrey; */
44 var END_EVENT_COLOR = Color.get(251,251,251);
45 //var END_EVENT_STROKE_COLOR = Color.black;
46 var NONE_END_EVENT_COLOR = Color.Firebrick4;
47 var NONE_END_EVENT_STROKE_COLOR = Color.Firebrick4;
48 var ERROR_END_EVENT_COLOR = Color.Firebrick;
49 var ERROR_END_EVENT_STROKE_COLOR = Color.Firebrick;
50 //var LABEL_COLOR = Color.get(112, 146, 190);
51 var LABEL_COLOR = Color.get(72, 106, 150);
52
53 // Fonts
54 var NORMAL_FONT = {font: "10px Arial", opacity: 1, fill: Color.black};
55 var LABEL_FONT = {font: "11px Arial", "font-style":"italic", opacity: 1, "fill": LABEL_COLOR};
56 var LABEL_FONT_SMOOTH = {font: "10px Arial", "font-style":"italic", opacity: 1, "fill": LABEL_COLOR, stroke: LABEL_COLOR, "stroke-width":.4};
57 var TASK_FONT = {font: "11px Arial", opacity: 1, fill: Color.black};
58 var TASK_FONT_SMOOTH = {font: "11px Arial", opacity: 1, fill: Color.black, stroke: LABEL_COLOR, "stroke-width":.4};
59 var POOL_LANE_FONT = {font: "11px Arial", opacity: 1, fill: Color.black};
60 var EXPANDED_SUBPROCESS_FONT = {font: "11px Arial", opacity: 1, fill: Color.black};
61
62 // Strokes
63 var NORMAL_STROKE = 1;
64 var SEQUENCEFLOW_STROKE = 1.5;
65 var SEQUENCEFLOW_HIGHLIGHT_STROKE = 2;
66 var THICK_TASK_BORDER_STROKE = 2.5;
67 var GATEWAY_TYPE_STROKE = 3.2;
68 var END_EVENT_STROKE = NORMAL_STROKE+2;
69 var MULTI_INSTANCE_STROKE = 1.3;
70 var EVENT_SUBPROCESS_ATTRS =     {"stroke": Color.black, "stroke-width": NORMAL_STROKE, "stroke-dasharray": ". "};
71 //var EXPANDED_SUBPROCESS_ATTRS = {"stroke": Color.black, "stroke-width": NORMAL_STROKE, "fill": Color.FloralWhite};
72 var EXPANDED_SUBPROCESS_ATTRS = {"stroke": Color.black, "stroke-width": NORMAL_STROKE, "fill": Color.WhiteSmoke};
73 var NON_INTERRUPTING_EVENT_STROKE = "- ";
74
75 var TASK_CORNER_ROUND = 10;
76 var EXPANDED_SUBPROCESS_CORNER_ROUND = 10;
77
78 // icons
79 var ICON_SIZE = 16;
80 var ICON_PADDING = 4;
81 var USERTASK_IMAGE =         "images/deployer/user.png";
82 var SCRIPTTASK_IMAGE =         "images/deployer/script.png";
83 var SERVICETASK_IMAGE =     "images/deployer/service.png";
84 var RECEIVETASK_IMAGE =     "images/deployer/receive.png";
85 var SENDTASK_IMAGE =         "images/deployer/send.png";
86 var MANUALTASK_IMAGE =         "images/deployer/manual.png";
87 var BUSINESS_RULE_TASK_IMAGE = "images/deployer/business_rule.png";
88 var TIMER_IMAGE =             "images/deployer/timer.png";
89 var MESSAGE_CATCH_IMAGE =     "images/deployer/message_catch.png";
90 var MESSAGE_THROW_IMAGE =     "images/deployer/message_throw.png";
91 var ERROR_THROW_IMAGE =     "images/deployer/error_throw.png";
92 var ERROR_CATCH_IMAGE =     "images/deployer/error_catch.png";
93 var SIGNAL_CATCH_IMAGE =     "images/deployer/signal_catch.png";
94 var SIGNAL_THROW_IMAGE =     "images/deployer/signal_throw.png";
95 var MULTIPLE_CATCH_IMAGE =     "images/deployer/multiple_catch.png";
96
97
98 var ObjectType = {
99     ELLIPSE: "ellipse",
100     FLOW: "flow",
101     RECT: "rect",
102     RHOMBUS: "rhombus"
103 };
104
105 function OBJ(type){
106     this.c = null;
107     this.type = type;
108     this.nestedElements = [];
109 };
110 OBJ.prototype = {
111     
112 };
113
114 var CONNECTION_TYPE = {
115     SEQUENCE_FLOW: "sequence_flow",
116     MESSAGE_FLOW: "message_flow",
117     ASSOCIATION: "association"
118 };
119
120 var ProcessDiagramCanvas = function(){
121 };
122 ProcessDiagramCanvas.prototype = {
123 // var DefaultProcessDiagramCanvas = {
124     canvasHolder: "holder",
125     canvasWidth: 0, 
126     canvasHeight: 0,
127     paint: Color.black,
128     strokeWidth: 0,
129     font: null,
130     fontSmoothing: null,
131     
132     g: null,
133     ninjaPaper: null,
134     
135     objects: [],
136     
137     processDefinitionId: null,
138     activity: null,
139     
140     frame: null,
141     
142     
143     debug: false,
144     
145     /**
146     * Creates an empty canvas with given width and height.
147     */
148     init: function(width, height, processDefinitionId){
149         this.canvasWidth = width;
150         this.canvasHeight = height;
151         
152         // TODO: name it as 'canvasName'
153         if (!processDefinitionId)
154             processDefinitionId = "holder";
155         
156         this.processDefinitionId = processDefinitionId;
157         this.canvasHolder = this.processDefinitionId;
158
159         var h = document.getElementById(this.canvasHolder);
160         if (!h) return;
161         
162         h.style.width = this.canvasWidth;
163         h.style.height = this.canvasHeight;
164         
165         this.g = Raphael(this.canvasHolder);
166         this.g.clear();
167     
168         //this.setPaint(Color.DimGrey);
169         this.setPaint(Color.black);
170         //this.setPaint(Color.white);
171         this.setStroke(NORMAL_STROKE);
172         
173         //this.setFont("Arial", 11);
174         this.setFont(NORMAL_FONT);
175         //this.font = this.g.getFont("Arial");
176         
177         this.fontSmoothing = true;
178         
179         // ninja!
180         var RaphaelOriginal = Raphael;
181         this.ninjaPaper =(function (local_raphael) {
182             var paper = local_raphael(1, 1, 1, 1, processDefinitionId);
183             return paper;
184         })(Raphael.ninja());
185         Raphael = RaphaelOriginal;
186     },
187     setPaint: function(color){
188         this.paint = color;
189     },
190     getPaint: function(){
191         return this.paint;
192     },
193     setStroke: function(strokeWidth){
194         this.strokeWidth = strokeWidth;
195     },
196     getStroke: function(){
197         return this.strokeWidth;
198     },
199     /*
200     setFont: function(family, weight, style, stretch){
201         this.font = this.g.getFont(family, weight);
202     },
203     */
204     setFont: function(font){
205         this.font = font;
206     },
207     getFont: function(){
208         return this.font;
209     },
210     drawShaddow: function(object){
211         var border = object.clone();
212         border.attr({"stroke-width": this.strokeWidth + 6, 
213                     "stroke": Color.white,
214                     "fill": Color.white,
215                     "opacity": 1,
216                     "stroke-dasharray":null});
217         //border.toBack();
218         object.toFront();
219         
220         return border;
221     },
222     
223     setConextObject: function(obj){
224         this.contextObject = obj;
225     },
226     getConextObject: function(){
227         return this.contextObject;
228     },
229     setContextToElement: function(object){
230         var contextObject = this.getConextObject();
231         object.id = contextObject.id;
232         object.data("contextObject", contextObject);
233     },
234     onClick: function(event, instance, element){
235       var overlay = element;
236       var set = overlay.data("set");
237       var contextObject = overlay.data("contextObject");
238       //console.log("["+contextObject.getProperty("type")+"], activityId: " + contextObject.getId());
239       if (ProcessDiagramGenerator.options && ProcessDiagramGenerator.options.on && ProcessDiagramGenerator.options.on.click) {
240         var args = [instance, element, contextObject];
241         ProcessDiagramGenerator.options.on.click.apply(event, args);
242       }
243     },
244     onRightClick: function(event, instance, element){
245       var overlay = element;
246       var set = overlay.data("set");
247       var contextObject = overlay.data("contextObject");
248       //console.log("[%s], activityId: %s (RIGHTCLICK)", contextObject.getProperty("type"), contextObject.getId());
249
250       if (ProcessDiagramGenerator.options && ProcessDiagramGenerator.options.on && ProcessDiagramGenerator.options.on.rightClick) {
251         var args = [instance, element, contextObject];
252         ProcessDiagramGenerator.options.on.rightClick.apply(event, args);
253       }
254     },
255     onHoverIn: function(event, instance, element){
256       var overlay = element;
257       var set = overlay.data("set");
258       var contextObject = overlay.data("contextObject");
259
260       var border = instance.g.getById(contextObject.id + "_border");
261       border.attr("opacity", 0.3);
262
263       // provide callback
264       if (ProcessDiagramGenerator.options && ProcessDiagramGenerator.options.on && ProcessDiagramGenerator.options.on.over) {
265         var args = [instance, element, contextObject];
266         ProcessDiagramGenerator.options.on.over.apply(event, args);
267       }
268      },
269      onHoverOut: function(event, instance, element){
270        var overlay = element;
271        var set = overlay.data("set");
272        var contextObject = overlay.data("contextObject");
273
274        var border = instance.g.getById(contextObject.id + "_border");
275        border.attr("opacity", 0.0);
276        // provide callback
277        if (ProcessDiagramGenerator.options && ProcessDiagramGenerator.options.on && ProcessDiagramGenerator.options.on.out) {
278          var args = [instance, element, contextObject];
279          ProcessDiagramGenerator.options.on.out.apply(event, args);
280        }
281      },
282      addHandlers: function(set, x, y, width, height, type){
283        var contextObject = this.getConextObject();
284
285        var cx = x+width/2, cy = y+height/2;
286        if (type == "event") {
287          var border = this.g.ellipse(cx, cy, width/2+4, height/2+4);
288          var overlay = this.g.ellipse(cx, cy, width/2, height/2);
289        } else if (type == "gateway") {
290          // rhombus
291          var border = this.g.path( "M" + (x - 4) + " " + (y + (height / 2)) +
292              "L" + (x + (width / 2)) + " " + (y + height + 4) +
293              "L" + (x + width + 4) + " " + (y + (height / 2)) +
294              "L" + (x + (width / 2)) + " " + (y - 4) +
295              "z" );
296          var overlay = this.g.path(  "M" + x + " " + (y + (height / 2)) +
297              "L" + (x + (width / 2)) + " " + (y + height) +
298              "L" + (x + width) + " " + (y + (height / 2)) +
299              "L" + (x + (width / 2)) + " " + y +
300              "z" );
301        } else if (type == "task") {
302          var border = this.g.rect(x - 4, y - 4, width+9, height+9, TASK_CORNER_ROUND+4);
303          var overlay = this.g.rect(x, y, width, height, TASK_CORNER_ROUND);
304        }
305
306        border.attr({stroke: Color.get(132,112,255)/*Color.Tan1*/,"stroke-width": 4, opacity: 0.0});
307        border.id = contextObject.id + "_border";
308
309        set.push(border);
310
311        overlay.attr({stroke: Color.Orange,"stroke-width": 3, fill: Color.get(0,0,0), opacity: 0.0, cursor: "hand"});
312        overlay.data("set",set);
313        overlay.id = contextObject.id;
314        overlay.data("contextObject",contextObject);
315
316        var instance = this;
317        overlay.mousedown(function(event){if (event.button == 2) instance.onRightClick(event, instance, this);});
318        overlay.click(function(event){instance.onClick(event, instance, this);});
319        overlay.hover(function(event){instance.onHoverIn(event, instance, this);}, function(event){instance.onHoverOut(event, instance, this);});
320      },
321     
322     /*
323      * Start Events:
324      * 
325      *    drawNoneStartEvent
326      *    drawTimerStartEvent
327      *    drawMessageStartEvent
328      *    drawErrorStartEvent
329      *    drawSignalStartEvent
330      *    _drawStartEventImage
331      *    _drawStartEvent
332      */
333      
334     drawNoneStartEvent: function(x, y, width, height) {
335       this.g.setStart();
336       
337         var isInterrupting = undefined;
338         this._drawStartEvent(x, y, width, height, isInterrupting, null);
339         
340         var set = this.g.setFinish();
341         this.addHandlers(set, x, y, width, height, "event");
342     },
343     
344     drawTimerStartEvent: function(x, y, width, height, isInterrupting, name) {
345       this.g.setStart();
346       
347         this._drawStartEvent(x, y, width, height, isInterrupting, null);
348         
349         var cx = x + width/2 - this.getStroke()/4;
350         var cy = y + height/2 - this.getStroke()/4;
351         
352         var w = width*.9;// - this.getStroke()*2;
353         var h = height*.9;// - this.getStroke()*2;
354         
355         this._drawClock(cx, cy, w, h);
356         
357         if (this.gebug)
358             var center = this.g.ellipse(cx, cy, 3, 3).attr({stroke:"none", fill: Color.green});
359         
360         var set = this.g.setFinish();
361         this.addHandlers(set, x, y, width, height, "event");
362     },
363     
364     drawMessageStartEvent: function(x, y, width, height, isInterrupting, name) {
365       this.g.setStart();
366       
367         this._drawStartEvent(x, y, width, height, isInterrupting, null);
368         
369         this._drawStartEventImage(x, y, width, height, MESSAGE_CATCH_IMAGE);
370         
371         var set = this.g.setFinish();
372     this.addHandlers(set, x, y, width, height, "event");
373     },
374     
375     drawErrorStartEvent: function(x, y, width, height, name) {
376       this.g.setStart();
377         var isInterrupting = undefined;
378         this._drawStartEvent(x, y, width, height, isInterrupting);
379
380         this._drawStartEventImage(x, y, width, height, ERROR_CATCH_IMAGE);
381         
382         var set = this.g.setFinish();
383     this.addHandlers(set, x, y, width, height, "event");
384     },
385     
386     drawSignalStartEvent: function(x, y, width, height, isInterrupting, name) {
387       this.g.setStart();
388         this._drawStartEvent(x, y, width, height, isInterrupting, null);
389         
390         this._drawStartEventImage(x, y, width, height, SIGNAL_CATCH_IMAGE);
391         
392         var set = this.g.setFinish();
393     this.addHandlers(set, x, y, width, height, "event");
394     },
395     
396     drawMultipleStartEvent: function(x, y, width, height, isInterrupting, name) {
397       this.g.setStart();
398         
399       this._drawStartEvent(x, y, width, height, isInterrupting, null);
400         
401         var cx = x + width/2 - this.getStroke()/4;
402         var cy = y + height/2 - this.getStroke()/4;
403         
404         var w = width*1;
405         var h = height*1;
406         
407         this._drawPentagon(cx, cy, w, h);
408         
409         var set = this.g.setFinish();
410     this.addHandlers(set, x, y, width, height, "event");
411     },
412     
413     _drawStartEventImage: function(x, y, width, height, image){
414         var cx = x + width/2 - this.getStroke()/2;
415         var cy = y + height/2 - this.getStroke()/2;
416         
417         var w = width*.65;// - this.getStroke()*2;
418         var h = height*.65;// - this.getStroke()*2;
419         
420         var img = this.g.image(image, cx-w/2, cy-h/2, w, h);
421     },
422     _drawStartEvent: function(x, y, width, height, isInterrupting){
423         var originalPaint = this.getPaint();
424         if (typeof(START_EVENT_STROKE_COLOR) != "undefined")
425             this.setPaint(START_EVENT_STROKE_COLOR);
426         
427         
428         width -= this.strokeWidth / 2;
429         height -= this.strokeWidth / 2;
430         
431         x = x + width/2;
432         y = y + height/2;
433         
434         var circle = this.g.ellipse(x, y, width/2, height/2);
435         
436         circle.attr({"stroke-width": this.strokeWidth, 
437                 "stroke": this.paint, 
438                 //"stroke": START_EVENT_STROKE_COLOR,
439                 "fill": START_EVENT_COLOR});
440                 
441         // white shaddow
442         this.drawShaddow(circle);
443         
444         if (isInterrupting!=null && isInterrupting!=undefined && !isInterrupting) 
445             circle.attr({"stroke-dasharray": NON_INTERRUPTING_EVENT_STROKE});
446
447         this.setContextToElement(circle);
448         
449         
450         this.setPaint(originalPaint);
451     },
452     
453     /*
454      * End Events:
455      * 
456      *    drawNoneEndEvent
457      *    drawErrorEndEvent
458      *    drawMessageEndEvent
459      *    drawSignalEndEvent
460      *    drawMultipleEndEvent
461      *  _drawEndEventImage
462      *    _drawNoneEndEvent
463      */
464      
465     drawNoneEndEvent: function(x, y, width, height) {
466       this.g.setStart();
467       
468         this._drawNoneEndEvent(x, y, width, height, null, "noneEndEvent");
469         
470         var set = this.g.setFinish();
471     this.addHandlers(set, x, y, width, height, "event");
472     },
473     
474     drawErrorEndEvent: function(x, y, width, height) {
475       this.g.setStart();
476         var type = "errorEndEvent";
477         this._drawNoneEndEvent(x, y, width, height, null, type);
478         
479         this._drawEndEventImage(x, y, width, height, ERROR_THROW_IMAGE);
480         
481         var set = this.g.setFinish();
482     this.addHandlers(set, x, y, width, height, "event");
483     },
484     
485     drawMessageEndEvent: function(x, y, width, height, name) {
486       this.g.setStart();
487         var type = "errorEndEvent";
488         this._drawNoneEndEvent(x, y, width, height, null, type);
489         
490         this._drawEndEventImage(x, y, width, height, MESSAGE_THROW_IMAGE);
491         
492         var set = this.g.setFinish();
493     this.addHandlers(set, x, y, width, height, "event");
494     },
495     
496     drawSignalEndEvent: function(x, y, width, height, name) {
497       this.g.setStart();
498         var type = "errorEndEvent";
499         this._drawNoneEndEvent(x, y, width, height, null, type);
500         
501         this._drawEndEventImage(x, y, width, height, SIGNAL_THROW_IMAGE);
502         
503         var set = this.g.setFinish();
504     this.addHandlers(set, x, y, width, height, "event");
505     },
506     
507     drawMultipleEndEvent: function(x, y, width, height, name) {
508       this.g.setStart();
509         var type = "errorEndEvent";
510         this._drawNoneEndEvent(x, y, width, height, null, type);
511         
512         var cx = x + width/2;// - this.getStroke();
513         var cy = y + height/2;// - this.getStroke();
514         
515         var w = width*1;
516         var h = height*1;
517         
518         var filled = true;
519         this._drawPentagon(cx, cy, w, h, filled);
520         
521         var set = this.g.setFinish();
522     this.addHandlers(set, x, y, width, height, "event");
523     },
524     
525     drawTerminateEndEvent: function(x, y, width, height) {
526       this.g.setStart();
527         var type = "errorEndEvent";
528         this._drawNoneEndEvent(x, y, width, height, null, type);
529         
530         var cx = x + width/2;// - this.getStroke()/2;
531         var cy = y + height/2;// - this.getStroke()/2;
532         
533         var w = width/2*.6;
534         var h = height/2*.6;
535         
536         var circle = this.g.ellipse(cx, cy, w, h).attr({fill: Color.black});
537         
538         var set = this.g.setFinish();
539     this.addHandlers(set, x, y, width, height, "event");
540     },
541     
542     _drawEndEventImage: function(x, y, width, height, image){
543         var cx = x + width/2 - this.getStroke()/2;
544         var cy = y + height/2 - this.getStroke()/2;
545         
546         var w = width*.65;
547         var h = height*.65;
548         
549         var img = this.g.image(image, cx-w/2, cy-h/2, w, h);
550     },
551     
552     _drawNoneEndEvent: function(x, y, width, height, image, type) {
553         var originalPaint = this.getPaint();
554         if (typeof(CATCHING_EVENT_COLOR) != "undefined")
555             this.setPaint(CATCHING_EVENT_COLOR);
556             
557         var strokeColor = this.getPaint();
558         var fillColor = this.getPaint();
559         
560         if (type == "errorEndEvent") {
561             strokeColor = ERROR_END_EVENT_STROKE_COLOR;
562             fillColor = ERROR_END_EVENT_COLOR;
563         } else if (type == "noneEndEvent") {
564             strokeColor = NONE_END_EVENT_STROKE_COLOR;
565             fillColor = NONE_END_EVENT_COLOR;
566         } else 
567             
568         // event circles
569         width -= this.strokeWidth / 2;
570         height -= this.strokeWidth / 2;
571         
572         x = x + width/2;// + this.strokeWidth/2;
573         y = y + width/2;// + this.strokeWidth/2;
574         
575         // outerCircle
576         var outerCircle = this.g.ellipse(x, y, width/2, height/2);
577         
578         // white shaddow
579         var shaddow = this.drawShaddow(outerCircle);
580         
581         outerCircle.attr({"stroke-width": this.strokeWidth,
582                         "stroke": strokeColor,
583                         "fill": fillColor});
584         
585         var innerCircleX = x;
586         var innerCircleY = y;
587         var innerCircleWidth = width/2 - 2;
588         var innerCircleHeight = height/2 - 2;
589         var innerCircle = this.g.ellipse(innerCircleX, innerCircleY, innerCircleWidth, innerCircleHeight);
590         innerCircle.attr({"stroke-width": this.strokeWidth,
591                 "stroke": strokeColor,
592                 "fill": Color.white});
593
594         // TODO: implement it
595         //var originalPaint = this.getPaint();
596         //this.g.setPaint(BOUNDARY_EVENT_COLOR);
597         
598         this.setPaint(originalPaint);
599     },
600     
601     /*
602      * Catching Events:
603      * 
604      *    drawCatchingTimerEvent
605      *    drawCatchingErrorEvent
606      *    drawCatchingSignalEvent
607      *  drawCatchingMessageEvent
608      *    drawCatchingMultipleEvent
609      *    _drawCatchingEventImage
610      *    _drawCatchingEvent
611      */
612      
613     
614     drawCatchingTimerEvent: function(x, y, width, height, isInterrupting, name) {
615       this.g.setStart();
616         this._drawCatchingEvent(x, y, width, height, isInterrupting, null);
617         
618         var innerCircleWidth = width - 4;
619         var innerCircleHeight = height - 4;
620         
621         var cx = x + width/2 - this.getStroke()/4;
622         var cy = y + height/2 - this.getStroke()/4;
623         
624         var w = innerCircleWidth*.9;// - this.getStroke()*2;
625         var h = innerCircleHeight*.9;// - this.getStroke()*2;
626         
627         this._drawClock(cx, cy, w, h);
628         
629         var set = this.g.setFinish();
630         this.addHandlers(set, x, y, width, height, "event");
631     },
632
633     drawCatchingErrorEvent: function(x, y, width, height, isInterrupting, name) {
634       this.g.setStart();
635         this._drawCatchingEvent(x, y, width, height, isInterrupting, null);
636         
637         this._drawCatchingEventImage(x, y, width, height, ERROR_CATCH_IMAGE);
638         
639         var set = this.g.setFinish();
640     this.addHandlers(set, x, y, width, height, "event");
641     },
642     
643     drawCatchingSignalEvent: function(x, y, width, height, isInterrupting, name) {
644       this.g.setStart();
645         this._drawCatchingEvent(x, y, width, height, isInterrupting, null);
646         
647         this._drawCatchingEventImage(x, y, width, height, SIGNAL_CATCH_IMAGE);
648         
649         var set = this.g.setFinish();
650     this.addHandlers(set, x, y, width, height, "event");
651     },
652     
653     drawCatchingMessageEvent: function(x, y, width, height, isInterrupting, name) {
654       this.g.setStart();
655         this._drawCatchingEvent(x, y, width, height, isInterrupting, null);
656         
657         this._drawCatchingEventImage(x, y, width, height, MESSAGE_CATCH_IMAGE);
658         
659         var set = this.g.setFinish();
660     this.addHandlers(set, x, y, width, height, "event");
661     },
662     
663     drawCatchingMultipleEvent: function(x, y, width, height, isInterrupting, name) {
664       this.g.setStart();
665         this._drawCatchingEvent(x, y, width, height, isInterrupting, null);
666         
667         var cx = x + width/2 - this.getStroke();
668         var cy = y + height/2 - this.getStroke();
669         
670         var w = width*.9;
671         var h = height*.9;
672         
673         this._drawPentagon(cx, cy, w, h);
674         
675         var set = this.g.setFinish();
676     this.addHandlers(set, x, y, width, height, "event");
677     },
678     
679     _drawCatchingEventImage: function(x, y, width, height, image){
680         var innerCircleWidth = width - 4;
681         var innerCircleHeight = height - 4;
682         
683         var cx = x + width/2 - this.getStroke()/2;
684         var cy = y + height/2 - this.getStroke()/2;
685         
686         var w = innerCircleWidth*.6;// - this.getStroke()*2;
687         var h = innerCircleHeight*.6;// - this.getStroke()*2;
688         
689         var img = this.g.image(image, cx-w/2, cy-h/2, w, h);
690     },
691     
692     _drawCatchingEvent: function(x, y, width, height, isInterrupting, image) {
693         var originalPaint = this.getPaint();
694         if (typeof(CATCHING_EVENT_COLOR) != "undefined")
695             this.setPaint(CATCHING_EVENT_COLOR);
696             
697         // event circles
698         width -= this.strokeWidth / 2;
699         height -= this.strokeWidth / 2;
700         
701         x = x + width/2;// + this.strokeWidth/2;
702         y = y + width/2;// + this.strokeWidth/2;
703         
704         // outerCircle
705         var outerCircle = this.g.ellipse(x, y, width/2, height/2);
706         
707         // white shaddow
708         var shaddow = this.drawShaddow(outerCircle);
709         
710         //console.log("isInterrupting: " + isInterrupting, "x:" , x, "y:",y);
711         if (isInterrupting!=null && isInterrupting!=undefined && !isInterrupting) 
712             outerCircle.attr({"stroke-dasharray": NON_INTERRUPTING_EVENT_STROKE});
713         
714         outerCircle.attr({"stroke-width": this.strokeWidth,
715                         "stroke": this.getPaint(),
716                         "fill": BOUNDARY_EVENT_COLOR});
717         
718         var innerCircleX = x;
719         var innerCircleY = y;
720         var innerCircleRadiusX = width/2 - 4;
721         var innerCircleRadiusY = height/2 - 4;
722         var innerCircle = this.g.ellipse(innerCircleX, innerCircleY, innerCircleRadiusX, innerCircleRadiusY);
723         innerCircle.attr({"stroke-width": this.strokeWidth,
724                 "stroke": this.getPaint()});
725
726         if (image) {
727             var imageWidth = imageHeight = innerCircleRadiusX*1.2 + this.getStroke()*2;
728             var imageX = innerCircleX-imageWidth/2 - this.strokeWidth/2;
729             var imageY = innerCircleY-imageWidth/2 - this.strokeWidth/2;
730             var img = this.g.image(image, imageX, imageY, imageWidth, imageHeight);
731         }
732         
733         this.setPaint(originalPaint);
734         
735         var set = this.g.set();
736         set.push(outerCircle, innerCircle, shaddow);
737         this.setContextToElement(outerCircle);
738         
739         // TODO: add shapes to set
740         
741         /*
742         var st = this.g.set();
743         st.push(
744             this.g.ellipse(innerCircleX, innerCircleY, 2, 2),
745             this.g.ellipse(imageX, imageY, 2, 2)
746         );
747         st.attr({fill: "red", "stroke-width":0});
748         */
749     },
750     
751     /*
752      * Catching Events:
753      * 
754      *    drawThrowingNoneEvent
755      *    drawThrowingSignalEvent
756      *    drawThrowingMessageEvent
757      *    drawThrowingMultipleEvent
758      */
759     
760     drawThrowingNoneEvent: function(x, y, width, height, name) {
761       this.g.setStart();
762         this._drawCatchingEvent(x, y, width, height, null, null);
763         
764         var set = this.g.setFinish();
765     this.addHandlers(set, x, y, width, height, "event");
766     },
767     
768     drawThrowingSignalEvent: function(x, y, width, height, name) {
769       this.g.setStart();
770         this._drawCatchingEvent(x, y, width, height, null, null);
771         
772         this._drawCatchingEventImage(x, y, width, height, SIGNAL_THROW_IMAGE);
773         
774         var set = this.g.setFinish();
775     this.addHandlers(set, x, y, width, height, "event");
776     },
777     
778     drawThrowingMessageEvent: function(x, y, width, height, name) {
779       this.g.setStart();
780         this._drawCatchingEvent(x, y, width, height, null, null);
781         
782         this._drawCatchingEventImage(x, y, width, height, MESSAGE_THROW_IMAGE);
783         
784         var set = this.g.setFinish();
785     this.addHandlers(set, x, y, width, height, "event");
786     },
787     
788     drawThrowingMultipleEvent: function(x, y, width, height, name) {
789       this.g.setStart();
790         this._drawCatchingEvent(x, y, width, height, null, null);
791         
792         var cx = x + width/2 - this.getStroke();
793         var cy = y + height/2 - this.getStroke();
794         
795         var w = width*.9;
796         var h = height*.9;
797         
798         var filled = true;
799         this._drawPentagon(cx, cy, w, h, filled);
800         
801         var set = this.g.setFinish();
802     this.addHandlers(set, x, y, width, height, "event");
803     },
804     
805     /*
806      * Draw flows:
807      * 
808      *  _connectFlowToActivity
809      *    _drawFlow
810      *    _drawDefaultSequenceFlowIndicator
811      *    drawSequenceflow
812      *    drawMessageflow
813      *    drawAssociation
814      *    _drawCircleTail
815      *    _drawArrowHead
816      *    _drawConditionalSequenceFlowIndicator
817      *    drawSequenceflowWithoutArrow
818      */
819      
820     _connectFlowToActivity: function(sourceActivityId, destinationActivityId, waypoints){
821         var sourceActivity = this.g.getById(sourceActivityId);
822         var destinationActivity = this.g.getById(destinationActivityId);
823         if (sourceActivity == null || destinationActivity == null) {
824             if (sourceActivity == null)
825                 console.error("source activity["+sourceActivityId+"] not found");
826             else
827                 console.error("destination activity["+destinationActivityId+"] not found");
828             return null;
829         }
830             var bbSourceActivity = sourceActivity.getBBox()
831             var bbDestinationActivity = destinationActivity.getBBox()
832             
833             var path = [];
834             var newWaypoints = [];
835             for(var i = 0; i < waypoints.length; i++){
836                 var pathType = ""
837                 if (i==0)
838                     pathType = "M";
839                 else 
840                     pathType = "L";
841                     
842                 path.push([pathType, waypoints[i].x, waypoints[i].y]);
843                 newWaypoints.push({x:waypoints[i].x, y:waypoints[i].y});
844             }
845
846             var ninjaPathSourceActivity = this.ninjaPaper.path(sourceActivity.realPath);
847             var ninjaPathDestinationActivity = this.ninjaPaper.path(destinationActivity.realPath);
848             var ninjaBBSourceActivity = ninjaPathSourceActivity.getBBox();
849             var ninjaBBDestinationActivity = ninjaPathDestinationActivity.getBBox();
850             
851             // set target of the flow to the center of the taskObject
852             var newPath = path;
853             var originalSource = {x: newPath[0][1], y: newPath[0][2]};
854             var originalTarget = {x: newPath[newPath.length-1][1], y: newPath[newPath.length-1][2]};
855             newPath[0][1] = ninjaBBSourceActivity.x + (ninjaBBSourceActivity.x2 - ninjaBBSourceActivity.x ) / 2;
856             newPath[0][2] = ninjaBBSourceActivity.y + (ninjaBBSourceActivity.y2 - ninjaBBSourceActivity.y ) / 2;
857             newPath[newPath.length-1][1] = ninjaBBDestinationActivity.x + (ninjaBBDestinationActivity.x2 - ninjaBBDestinationActivity.x ) / 2;
858             newPath[newPath.length-1][2] = ninjaBBDestinationActivity.y + (ninjaBBDestinationActivity.y2 - ninjaBBDestinationActivity.y ) / 2;
859             
860             var ninjaPathFlowObject = this.ninjaPaper.path(newPath);
861             var ninjaBBFlowObject = ninjaPathFlowObject.getBBox();
862             
863             var intersectionsSource = Raphael.pathIntersection(ninjaPathSourceActivity.realPath, ninjaPathFlowObject.realPath);
864             var intersectionsDestination = Raphael.pathIntersection(ninjaPathDestinationActivity.realPath, ninjaPathFlowObject.realPath);
865             var intersectionSource = intersectionsSource.pop();
866             var intersectionDestination = intersectionsDestination.pop();
867             
868             if (intersectionSource != undefined) {
869                 if (this.gebug) {
870                     var diameter = 5;
871                     var dotOriginal = this.g.ellipse(originalSource.x, originalSource.y, diameter, diameter).attr({"fill": Color.white, "stroke": Color.Pink});
872                     var dot = this.g.ellipse(intersectionSource.x, intersectionSource.y, diameter, diameter).attr({"fill": Color.white, "stroke": Color.Green});
873                 }
874                 
875                 newWaypoints[0].x = intersectionSource.x;
876                 newWaypoints[0].y = intersectionSource.y;
877             }
878             if (intersectionDestination != undefined) {
879                 if (this.gebug) {
880                     var diameter = 5;
881                     var dotOriginal = this.g.ellipse(originalTarget.x, originalTarget.y, diameter, diameter).attr({"fill": Color.white, "stroke": Color.Red});
882                     var dot = this.g.ellipse(intersectionDestination.x, intersectionDestination.y, diameter, diameter).attr({"fill": Color.white, "stroke": Color.Blue});
883                 }
884                 
885                 newWaypoints[newWaypoints.length-1].x = intersectionDestination.x;
886                 newWaypoints[newWaypoints.length-1].y = intersectionDestination.y;
887             }
888             
889             this.ninjaPaper.clear();
890         return newWaypoints;
891     },
892      
893     _drawFlow: function(waypoints, conditional, isDefault, highLighted, withArrowHead, connectionType){
894         var originalPaint = this.getPaint();
895         var originalStroke = this.getStroke();
896         
897         this.setPaint(SEQUENCEFLOW_COLOR);
898         this.setStroke(SEQUENCEFLOW_STROKE);
899         
900         if (highLighted) {
901             this.setPaint(HIGHLIGHT_COLOR);
902             this.setStroke(SEQUENCEFLOW_HIGHLIGHT_STROKE);
903         }
904
905 // TODO: generate polylineId or do something!!
906         var uuid = Raphael.createUUID();
907         
908         var contextObject = this.getConextObject();
909         var newWaypoints = waypoints;
910         if (contextObject) {
911             var newWaypoints = this._connectFlowToActivity(contextObject.sourceActivityId, contextObject.destinationActivityId, waypoints);
912             
913             if (!newWaypoints) {
914                 console.error("Error draw flow from '"+contextObject.sourceActivityId+"' to '"+contextObject.destinationActivityId+"' ");
915                 return;
916             }
917         }
918         var polyline = new Polyline(uuid, newWaypoints, this.getStroke());
919         //var polyline = new Polyline(waypoints, 3);
920         
921         polyline.element = this.g.path(polyline.path);
922         polyline.element.attr("stroke-width", this.getStroke());
923         polyline.element.attr("stroke", this.getPaint());
924             
925         if (contextObject) {
926             polyline.element.id = contextObject.id;
927             polyline.element.data("contextObject", contextObject);
928         } else {
929             polyline.element.id = uuid;
930         }
931         
932         
933         /*
934         polyline.element.mouseover(function(){
935             this.attr({"stroke-width": NORMAL_STROKE + 2});
936         }).mouseout(function(){
937             this.attr({"stroke-width": NORMAL_STROKE});
938         });
939         */
940         
941         var last = polyline.getAnchorsCount()-1;
942         var x = polyline.getAnchor(last).x;
943         var y = polyline.getAnchor(last).y;
944         //var c = this.g.ellipse(x, y, 5, 5);
945         
946         var lastLineIndex = polyline.getLinesCount()-1;
947         var line = polyline.getLine(lastLineIndex);
948         var firstLine = polyline.getLine(0);
949         
950         var arrowHead = null,
951             circleTail = null,
952             defaultSequenceFlowIndicator = null,
953             conditionalSequenceFlowIndicator = null;
954
955         if (connectionType == CONNECTION_TYPE.MESSAGE_FLOW) {
956             circleTail = this._drawCircleTail(firstLine, connectionType);
957         }
958         if(withArrowHead)
959             arrowHead = this._drawArrowHead(line, connectionType);
960         
961         //console.log("isDefault: ", isDefault, ", isDefaultConditionAvailable: ", polyline.isDefaultConditionAvailable);
962         if (isDefault && polyline.isDefaultConditionAvailable) {
963             //var angle = polyline.getLineAngle(0);
964             //console.log("firstLine", firstLine);
965             defaultSequenceFlowIndicator = this._drawDefaultSequenceFlowIndicator(firstLine);
966         }
967         
968         if (conditional) {
969             conditionalSequenceFlowIndicator = this._drawConditionalSequenceFlowIndicator(firstLine);
970         }
971
972         // draw flow name
973         var flowName = contextObject.name;
974         if (flowName) {
975             var xPointArray = contextObject.xPointArray;
976             var yPointArray = contextObject.yPointArray;
977             var textX = xPointArray[0] < xPointArray[1] ? xPointArray[0] : xPointArray[1];
978             var textY = yPointArray[0] < yPointArray[1] ? yPointArray[1] : yPointArray[0];
979             // fix xy
980             textX += 20;
981             textY -= 10;
982             this.g.text(textX, textY, flowName).attr(LABEL_FONT);
983         }
984         
985         var st = this.g.set();
986         st.push(polyline.element, arrowHead, circleTail, conditionalSequenceFlowIndicator);
987         polyline.element.data("set", st);
988         polyline.element.data("withArrowHead", withArrowHead);
989         
990         var polyCloneAttrNormal = {"stroke-width": this.getStroke() + 5, stroke: Color.get(132,112,255), opacity: 0.0, cursor: "hand"};
991         var polyClone = st.clone().attr(polyCloneAttrNormal).hover(function () {
992                 //if (polyLine.data("isSelected")) return;
993                 polyClone.attr({opacity: 0.2});
994             }, function () {
995                 //if (polyLine.data("isSelected")) return;
996                 polyClone.attr({opacity: 0.0});
997             });
998         polyClone.data("objectId", polyline.element.id);
999         polyClone.click(function(){
1000             var instance = this;
1001             var objectId = instance.data("objectId");
1002             var object = this.paper.getById(objectId);
1003             var contextObject = object.data("contextObject");
1004             if (contextObject) {
1005                 console.log("[flow], objectId: " + object.id +", flow: " + contextObject.flow);
1006                 ProcessDiagramGenerator.showFlowInfo(contextObject);
1007             }
1008         }).dblclick(function(){
1009             console.log("!!! DOUBLE CLICK !!!");
1010         }).hover(function (mouseEvent) {
1011             var instance = this;
1012             var objectId = instance.data("objectId");
1013             var object = this.paper.getById(objectId);
1014             var contextObject = object.data("contextObject");
1015             if (contextObject)
1016                 ProcessDiagramGenerator.showFlowInfo(contextObject);
1017         });
1018         polyClone.data("parentId", uuid);
1019         
1020         if (!connectionType || connectionType == CONNECTION_TYPE.SEQUENCE_FLOW)
1021             polyline.element.attr("stroke-width", this.getStroke());
1022         else if (connectionType == CONNECTION_TYPE.MESSAGE_FLOW)
1023             polyline.element.attr({"stroke-dasharray": "--"});
1024         else if (connectionType == CONNECTION_TYPE.ASSOCIATION)
1025             polyline.element.attr({"stroke-dasharray": ". "});
1026         
1027         this.setPaint(originalPaint);
1028         this.setStroke(originalStroke);
1029     },
1030     
1031     _drawDefaultSequenceFlowIndicator: function(line) {
1032         //console.log("line: ", line);
1033         
1034         var len = 10; c = len/2, f = 8;
1035         var defaultIndicator = this.g.path("M" + (-c) + " " + 0 + "L" + (c) + " " + 0);
1036         defaultIndicator.attr("stroke-width", this.getStroke()+0);
1037         defaultIndicator.attr("stroke", this.getPaint());
1038         
1039         
1040         var cosAngle = Math.cos((line.angle));
1041         var sinAngle = Math.sin((line.angle));
1042         
1043         var dx = f * cosAngle;
1044         var dy = f * sinAngle;
1045         
1046         var x1 = line.x1 + dx + 0*c*cosAngle;
1047         var y1 = line.y1 + dy + 0*c*sinAngle;
1048         
1049         defaultIndicator.transform("t" + (x1) + "," + (y1) + "");
1050         defaultIndicator.transform("...r" + Raphael.deg(line.angle - 3*Math.PI / 4) + " " + 0 + " " + 0);
1051         /*
1052         var c0 = this.g.ellipse(0, 0, 1, 1).attr({stroke: Color.Blue});
1053         c0.transform("t" + (line.x1) + "," + (line.y1) + "");
1054         var center = this.g.ellipse(0, 0, 1, 1).attr({stroke: Color.Red});
1055         center.transform("t" + (line.x1+dx) + "," + (line.y1+dy) + "");
1056         */
1057         
1058         return defaultIndicator;
1059     },
1060     
1061     drawSequenceflow: function(waypoints, conditional, isDefault, highLighted) {
1062         var withArrowHead = true;
1063         this._drawFlow(waypoints, conditional, isDefault, highLighted, withArrowHead, CONNECTION_TYPE.SEQUENCE_FLOW);
1064     },
1065     
1066     drawMessageflow: function(waypoints, highLighted) {
1067         var withArrowHead = true;
1068         var conditional=isDefault=false;
1069         this._drawFlow(waypoints, conditional, isDefault, highLighted, withArrowHead, CONNECTION_TYPE.MESSAGE_FLOW);
1070     },
1071     
1072     drawAssociation: function(waypoints, withArrowHead, highLighted) {
1073         var withArrowHead = withArrowHead;
1074         var conditional=isDefault=false;
1075         this._drawFlow(waypoints, conditional, isDefault, highLighted, withArrowHead, CONNECTION_TYPE.ASSOCIATION);
1076     },
1077   
1078     _drawCircleTail: function(line, connectionType){
1079         var diameter = ARROW_WIDTH/2*1.5;
1080         
1081         // anti smoothing
1082         if (this.strokeWidth%2 == 1)
1083             line.x1 += .5, line.y1 += .5;
1084         
1085         var circleTail = this.g.ellipse(line.x1, line.y1, diameter, diameter);
1086         circleTail.attr("fill", Color.white);
1087         circleTail.attr("stroke", this.getPaint());
1088         
1089         return circleTail;
1090     },
1091     
1092     _drawArrowHead: function(line, connectionType){
1093         var doubleArrowWidth = 2 * ARROW_WIDTH;
1094         
1095         if (connectionType == CONNECTION_TYPE.ASSOCIATION)
1096             var arrowHead = this.g.path("M-" + (ARROW_WIDTH/2+.5) + " -" + doubleArrowWidth + "L 0 0 L" + (ARROW_WIDTH/2+.5) + " -" + doubleArrowWidth);
1097         else
1098             var arrowHead = this.g.path("M0 0L-" + (ARROW_WIDTH/2+.5) + " -" + doubleArrowWidth + "L" + (ARROW_WIDTH/2+.5) + " -" + doubleArrowWidth + "z");
1099         
1100         //arrowHead.transform("t" + 0 + ",-" + this.getStroke() + "");
1101         
1102         // anti smoothing
1103         if (this.strokeWidth%2 == 1)
1104             line.x2 += .5, line.y2 += .5;
1105         
1106         arrowHead.transform("t" + line.x2 + "," + line.y2 + "");
1107         arrowHead.transform("...r" + Raphael.deg(line.angle - Math.PI / 2) + " " + 0 + " " + 0);
1108         
1109         if (!connectionType || connectionType == CONNECTION_TYPE.SEQUENCE_FLOW)
1110             arrowHead.attr("fill", this.getPaint());
1111         else if (connectionType == CONNECTION_TYPE.MESSAGE_FLOW)
1112             arrowHead.attr("fill", Color.white);
1113             
1114         arrowHead.attr("stroke-width", this.getStroke());
1115         arrowHead.attr("stroke", this.getPaint());
1116         
1117         return arrowHead;
1118     },
1119     
1120     /*
1121     drawArrowHead2: function(srcX, srcY, targetX, targetY) {
1122         var doubleArrowWidth = 2 * ARROW_WIDTH;
1123         
1124         //var arrowHead = this.g.path("M-" + ARROW_WIDTH/2 + " -" + doubleArrowWidth + "L0 0" + "L" + ARROW_WIDTH/2 + " -" + doubleArrowWidth + "z");
1125         
1126         var arrowHead = this.g.path("M0 0L-" + ARROW_WIDTH/1.5 + " -" + doubleArrowWidth + "L" + ARROW_WIDTH/1.5 + " -" + doubleArrowWidth + "z");
1127         //var c = DefaultProcessDiagramCanvas.g.ellipse(0, 0, 3, 3);
1128         //c.transform("t"+targetX+","+targetY+"");
1129         
1130         var angle = Math.atan2(targetY - srcY, targetX - srcX);
1131         
1132         arrowHead.transform("t"+targetX+","+targetY+"");
1133         arrowHead.transform("...r" + Raphael.deg(angle - Math.PI / 2) + " "+0+" "+0);
1134         
1135         //console.log(arrowHead.transform());
1136         //console.log("--> " + Raphael.deg(angle - Math.PI / 2));
1137         
1138         arrowHead.attr("fill", this.getPaint());
1139         arrowHead.attr("stroke", this.getPaint());
1140         
1141         / *
1142         // shaddow
1143         var c0 = arrowHead.clone();
1144         c0.transform("...t-1 1");
1145         c0.attr("stroke-width", this.strokeWidth);
1146         c0.attr("stroke", Color.black);
1147         c0.attr("opacity", 0.15);
1148         c0.toBack();
1149         * /
1150     },
1151     */
1152     
1153     _drawConditionalSequenceFlowIndicator: function(line){
1154         var horizontal = (CONDITIONAL_INDICATOR_WIDTH * 0.7);
1155         var halfOfHorizontal = horizontal / 2;
1156         var halfOfVertical = CONDITIONAL_INDICATOR_WIDTH / 2;
1157
1158         var uuid = null;
1159         var waypoints = [{x: 0, y: 0},
1160                         {x: -halfOfHorizontal, y: halfOfVertical},
1161                         {x: 0, y: CONDITIONAL_INDICATOR_WIDTH},
1162                         {x: halfOfHorizontal, y: halfOfVertical}];
1163         /*
1164         var polyline = new Polyline(uuid, waypoints, this.getStroke());
1165         polyline.element = this.g.path(polyline.path);
1166         polyline.element.attr("stroke-width", this.getStroke());
1167         polyline.element.attr("stroke", this.getPaint());
1168         polyline.element.id = uuid;
1169         */
1170         var polygone = new Polygone(waypoints, this.getStroke());
1171         polygone.element = this.g.path(polygone.path);
1172         polygone.element.attr("fill", Color.white);
1173         
1174         polygone.transform("t" + line.x1 + "," + line.y1 + "");
1175         polygone.transform("...r" + Raphael.deg(line.angle - Math.PI / 2) + " " + 0 + " " + 0);
1176         
1177         
1178         var cosAngle = Math.cos((line.angle));
1179         var sinAngle = Math.sin((line.angle));
1180         
1181         //polygone.element.attr("stroke-width", this.getStroke());
1182         //polygone.element.attr("stroke", this.getPaint());
1183         
1184         polygone.attr({"stroke-width": this.getStroke(), "stroke": this.getPaint()});
1185         
1186         return polygone.element;
1187     },
1188   
1189     drawSequenceflowWithoutArrow: function(waypoints, conditional, isDefault, highLighted) {
1190         var withArrowHead = false;
1191         this._drawFlow(waypoints, conditional, isDefault, highLighted, withArrowHead, CONNECTION_TYPE.SEQUENCE_FLOW);
1192     },
1193     
1194     /*
1195      * Draw artifacts
1196      */
1197     
1198     drawPoolOrLane: function(x, y, width, height, name){
1199         // anti smoothing
1200         if (this.strokeWidth%2 == 1)
1201             x = Math.round(x) + .5, y = Math.round(y) + .5;
1202         
1203         // shape
1204         var rect = this.g.rect(x, y, width, height);
1205         var attr = {"stroke-width": NORMAL_STROKE, stroke: TASK_STROKE_COLOR};
1206         rect.attr(attr);
1207         
1208         // Add the name as text, vertical
1209         if(name != null && name.length > 0) {
1210             var attr = POOL_LANE_FONT;
1211             
1212             // Include some padding
1213             var availableTextSpace = height - 6;
1214             
1215             // Create rotation for derived font
1216             var truncated = this.fitTextToWidth(name, availableTextSpace);
1217             var realWidth = this.getStringWidth(truncated, attr);
1218             var realHeight = this.getStringHeight(truncated, attr);
1219             
1220             //console.log("truncated:", truncated, ", height:", height, ", realHeight:", realHeight, ", availableTextSpace:", availableTextSpace, ", realWidth:", realWidth);
1221             var newX = x + 2 + realHeight*1 - realHeight/2;
1222             var newY = 3 + y + availableTextSpace - (availableTextSpace - realWidth) / 2 - realWidth/2;
1223             var textElement = this.g.text(newX, newY, truncated).attr(attr);
1224             //console.log(".getBBox(): ", t.getBBox());
1225             textElement.transform("r" + Raphael.deg(270 * Math.PI/180) + " " + newX + " " + newY);
1226         }
1227         
1228         // TODO: add to set
1229     },
1230     
1231     _drawTask: function(name, x, y, width, height, thickBorder) {
1232         var originalPaint = this.getPaint();
1233         this.setPaint(TASK_COLOR);
1234         
1235         // anti smoothing
1236         if (this.strokeWidth%2 == 1)
1237             x = Math.round(x) + .5, y = Math.round(y) + .5;
1238         
1239         // shape
1240         var shape = this.g.rect(x, y, width, height, TASK_CORNER_ROUND);
1241         var attr = {"stroke-width": this.strokeWidth, stroke: TASK_STROKE_COLOR, fill: this.getPaint()};
1242         shape.attr(attr);
1243         //shape.attr({fill: "90-"+this.getPaint()+"-" + Color.get(250, 250, 244)});
1244         
1245         var contextObject = this.getConextObject();
1246         if (contextObject) {
1247             shape.id = contextObject.id;
1248             shape.data("contextObject", contextObject);
1249         }
1250         
1251         //var activity = this.getConextObject();
1252         //console.log("activity: " + activity.getId(), activity);
1253         //Object.clone(activity);
1254         
1255         /*
1256         c.mouseover(function(){
1257             this.attr({"stroke-width": NORMAL_STROKE + 2});
1258         }).mouseout(function(){
1259             this.attr({"stroke-width": NORMAL_STROKE});
1260         });
1261         */
1262         
1263         this.setPaint(originalPaint);
1264
1265         // white shaddow
1266         this.drawShaddow(shape);
1267         
1268         
1269         if (thickBorder) {
1270             shape.attr({"stroke-width": THICK_TASK_BORDER_STROKE});
1271         } else {
1272             //g.draw(rect);
1273         }
1274         
1275         // text
1276         if (name) {
1277             var fontAttr = TASK_FONT;
1278             
1279             // Include some padding
1280             var paddingX = 5;
1281             var paddingY = 5;
1282             var availableTextSpace = width - paddingX*2;
1283             
1284             // TODO: this.setFont
1285             // var originalFont = this.getFont();
1286             // this.setFont(TASK_FONT)
1287             /*
1288             var truncated = this.fitTextToWidth(name, availableTextSpace);
1289             var realWidth = this.getStringWidth(truncated, fontAttr);
1290             var realHeight = this.getStringHeight(truncated, fontAttr);
1291             
1292             //var t = this.g.text(x + width/2 + realWidth*0/2 + paddingX*0, y + height/2, truncated).attr(fontAttr);
1293             */
1294             //console.log("draw task name: " + name);
1295             var boxWidth = width - (2 * TEXT_PADDING);
1296             var boxHeight = height - ICON_SIZE - ICON_PADDING - ICON_PADDING - MARKER_WIDTH - 2 - 2;
1297             var boxX = x + width/2 - boxWidth/2;
1298             var boxY = y + height/2 - boxHeight/2 + ICON_PADDING + ICON_PADDING - 2 - 2;
1299             /*
1300             var boxWidth = width - (2 * ANNOTATION_TEXT_PADDING);
1301             var boxHeight = height - (2 * ANNOTATION_TEXT_PADDING);
1302             var boxX = x + width/2 - boxWidth/2;
1303             var boxY = y + height/2 - boxHeight/2;
1304             */
1305             
1306             this.drawTaskLabel(name, boxX, boxY, boxWidth, boxHeight);
1307         }
1308     },
1309     
1310     drawTaskLabel: function(text, x, y, boxWidth, boxHeight){
1311         var originalFont = this.getFont();
1312         this.setFont(TASK_FONT);
1313             
1314         this._drawMultilineText(text, x, y, boxWidth, boxHeight, MULTILINE_VERTICAL_ALIGN_MIDDLE, MULTILINE_HORIZONTAL_ALIGN_MIDDLE);
1315         
1316         this.setFont(originalFont);
1317     },
1318     
1319     drawAnnotationText: function(text, x, y, width, height){
1320         //this._drawMultilineText(text, x, y, width, height, "start");
1321         
1322         var originalPaint = this.getPaint();
1323         var originalFont = this.getFont();
1324         
1325         this.setPaint(Color.black);
1326         this.setFont(TASK_FONT);
1327             
1328         this._drawMultilineText(text, x, y, width, height, MULTILINE_VERTICAL_ALIGN_TOP, MULTILINE_HORIZONTAL_ALIGN_LEFT);
1329         
1330         this.setPaint(originalPaint);
1331         this.setFont(originalFont);
1332     },
1333     
1334     drawLabel: function(text, x, y, width, height){
1335         //this._drawMultilineText(text, x, y, width, height, "start");
1336         
1337         var originalPaint = this.getPaint();
1338         var originalFont = this.getFont();
1339         
1340         this.setPaint(LABEL_COLOR);
1341         //this.setFont(LABEL_FONT);
1342         this.setFont(LABEL_FONT_SMOOTH);
1343         
1344         // predefined box width for labels
1345         // TODO: use label width as is, but not height (for stretching)
1346         if (!width || !height) {
1347           width = 100;
1348           height = 0;
1349         }
1350         
1351         // TODO: remove it. It is debug
1352         x = x - width/2;
1353       
1354         this._drawMultilineText(text, x, y, width, height, MULTILINE_VERTICAL_ALIGN_TOP, MULTILINE_HORIZONTAL_ALIGN_MIDDLE);
1355         
1356         this.setPaint(originalPaint);
1357         this.setFont(originalFont);
1358     },
1359     
1360     /*
1361     drawMultilineLabel: function(text, x, y){
1362         var originalFont = this.getFont();
1363         this.setFont(LABEL_FONT_SMOOTH);
1364         
1365         var boxWidth = 80;
1366         x = x - boxWidth/2
1367         
1368         this._drawMultilineText(text, x, y, boxWidth, null, "middle");
1369         this.setFont(originalFont);
1370     },
1371     */
1372     
1373     getStringWidth: function(text, fontAttrs){
1374         var textElement = this.g.text(0, 0, text).attr(fontAttrs).hide();
1375         var bb = textElement.getBBox();
1376         
1377         //console.log("string width: ", t.getBBox().width);
1378         return textElement.getBBox().width;
1379     },
1380     getStringHeight: function(text, fontAttrs){
1381         var textElement = this.g.text(0, 0, text).attr(fontAttrs).hide();
1382         var bb = textElement.getBBox();
1383         
1384         //console.log("string height: ", t.getBBox().height);
1385         return textElement.getBBox().height;
1386     },
1387     fitTextToWidth: function(original, width) {
1388         var text = original;
1389
1390         // TODO: move attr on parameters
1391         var attr = {font: "11px Arial", opacity: 0};
1392         
1393         // remove length for "..."
1394         var dots = this.g.text(0, 0, "...").attr(attr).hide();
1395         var dotsBB = dots.getBBox();
1396         
1397         var maxWidth = width - dotsBB.width;
1398         
1399         var textElement = this.g.text(0, 0, text).attr(attr).hide();
1400         var bb = textElement.getBBox();
1401         
1402         // it's a little bit incorrect with "..."
1403         while (bb.width > maxWidth && text.length > 0) {
1404             text = text.substring(0, text.length - 1);
1405             textElement.attr({"text": text});
1406             bb = textElement.getBBox();
1407         }
1408
1409         // remove element from paper
1410         textElement.remove();
1411         
1412         if (text != original) {
1413             text = text + "...";
1414         }
1415
1416         return text;
1417     },
1418     wrapTextToWidth: function(original, width){
1419     
1420         //return original;
1421         
1422         var text = original;
1423         var wrappedText = "\n";
1424         
1425         // TODO: move attr on parameters
1426         var attr = {font: "11px Arial", opacity: 0};
1427         
1428         var textElement = this.g.text(0, 0, wrappedText).attr(attr).hide();
1429         var bb = textElement.getBBox();
1430         
1431         var resultText = "";
1432         var i = 0, j = 0;
1433         while (text.length > 0) {
1434             while (bb.width < width && text.length>0) {
1435                 // remove "\n"
1436                 wrappedText = wrappedText.substring(0,wrappedText.length-1);
1437                 // add new char, add "\n"
1438                 wrappedText = wrappedText + text.substring(0,1) + "\n";
1439                 text = text.substring(1);
1440                 
1441                 textElement.attr({"text": wrappedText});
1442                 bb = textElement.getBBox();
1443                 i++;
1444                 if (i>200) break;
1445             }
1446             // remove "\n"
1447             wrappedText = wrappedText.substring(0, wrappedText.length - 1);
1448             
1449             if (text.length == 0) {
1450                 resultText += wrappedText;
1451                 break;
1452             }
1453             
1454             // return last char to text
1455             text = wrappedText.substring(wrappedText.length-1) + text;
1456             // remove last char from wrappedText
1457             wrappedText = wrappedText.substring(0, wrappedText.length-1) + "\n";
1458             
1459             textElement.attr({"text": wrappedText});
1460             bb = textElement.getBBox();
1461             
1462             //console.log(">> ", wrappedText, ", ", text);
1463             resultText += wrappedText;
1464             wrappedText = "\n";
1465             
1466             j++;
1467             if (j>20) break;
1468         }
1469         // remove element from paper
1470         textElement.remove();
1471         
1472         return resultText;
1473     },
1474     
1475     wrapTextToWidth2: function(original, width){
1476         var text = original;
1477         var wrappedText = "\n";
1478         
1479         // TODO: move attr on parameters
1480         var attr = {font: "11px Arial", opacity: 0};
1481         
1482         var textElement = this.g.text(0, 0, wrappedText).attr(attr).hide();
1483         var bb = textElement.getBBox();
1484         
1485         var resultText = "";
1486         var i = 0, j = 0;
1487         while (text.length > 0) {
1488             while (bb.width < width && text.length>0) {
1489                 // remove "\n"
1490                 wrappedText = wrappedText.substring(0,wrappedText.length-1);
1491                 // add new char, add "\n"
1492                 wrappedText = wrappedText + text.substring(0,1) + "\n";
1493                 text = text.substring(1);
1494                 
1495                 textElement.attr({"text": wrappedText});
1496                 bb = textElement.getBBox();
1497                 i++;
1498                 if (i>200) break;
1499             }
1500             // remove "\n"
1501             wrappedText = wrappedText.substring(0, wrappedText.length - 1);
1502             
1503             if (text.length == 0) {
1504                 resultText += wrappedText;
1505                 break;
1506             }
1507             
1508             // return last char to text
1509             text = wrappedText.substring(wrappedText.length-1) + text;
1510             // remove last char from wrappedText
1511             wrappedText = wrappedText.substring(0, wrappedText.length-1) + "\n";
1512             
1513             textElement.attr({"text": wrappedText});
1514             bb = textElement.getBBox();
1515             
1516             //console.log(">> ", wrappedText, ", ", text);
1517             resultText += wrappedText;
1518             wrappedText = "\n";
1519             
1520             j++;
1521             if (j>20) break;
1522         }
1523         // remove element from paper
1524         textElement.remove();
1525         
1526         return resultText;
1527     },
1528     
1529     drawUserTask: function(name, x, y, width, height) {
1530       this.g.setStart();
1531         this._drawTask(name, x, y, width, height);
1532         var img = this.g.image(USERTASK_IMAGE, x + ICON_PADDING, y + ICON_PADDING, ICON_SIZE, ICON_SIZE);
1533         var set = this.g.setFinish();
1534         this.addHandlers(set, x, y, width, height, "task");
1535     },
1536     
1537     drawScriptTask: function(name, x, y, width, height) {
1538       this.g.setStart();
1539         this._drawTask(name, x, y, width, height);
1540         var img = this.g.image(SCRIPTTASK_IMAGE, x + ICON_PADDING, y + ICON_PADDING, ICON_SIZE, ICON_SIZE);
1541         var set = this.g.setFinish();
1542     this.addHandlers(set, x, y, width, height, "task");
1543     },
1544     
1545     drawServiceTask: function(name, x, y, width, height) {
1546       this.g.setStart();
1547         this._drawTask(name, x, y, width, height);
1548         var img = this.g.image(SERVICETASK_IMAGE, x + ICON_PADDING, y + ICON_PADDING, ICON_SIZE, ICON_SIZE);
1549         var set = this.g.setFinish();
1550     this.addHandlers(set, x, y, width, height, "task");
1551     },
1552     
1553     drawReceiveTask: function(name, x, y, width, height) {
1554       this.g.setStart();
1555         this._drawTask(name, x, y, width, height);
1556         var img = this.g.image(RECEIVETASK_IMAGE, x + 7, y + 7, ICON_SIZE, ICON_SIZE);
1557         var set = this.g.setFinish();
1558     this.addHandlers(set, x, y, width, height, "task");
1559     },
1560     
1561     drawSendTask: function(name, x, y, width, height) {
1562       this.g.setStart();
1563         this._drawTask(name, x, y, width, height);
1564         var img = this.g.image(SENDTASK_IMAGE, x + 7, y + 7, ICON_SIZE, ICON_SIZE);
1565         var set = this.g.setFinish();
1566     this.addHandlers(set, x, y, width, height, "task");
1567     },
1568     
1569     drawManualTask: function(name, x, y, width, height) {
1570       this.g.setStart();
1571         this._drawTask(name, x, y, width, height);
1572         var img = this.g.image(MANUALTASK_IMAGE, x + 7, y + 7, ICON_SIZE, ICON_SIZE);
1573         var set = this.g.setFinish();
1574     this.addHandlers(set, x, y, width, height, "task");
1575     },
1576     
1577     drawBusinessRuleTask: function(name, x, y, width, height) {
1578       this.g.setStart();
1579         this._drawTask(name, x, y, width, height);
1580         var img = this.g.image(BUSINESS_RULE_TASK_IMAGE, x + 7, y + 7, ICON_SIZE, ICON_SIZE);
1581         var set = this.g.setFinish();
1582     this.addHandlers(set, x, y, width, height, "task");
1583     },
1584     
1585     drawExpandedSubProcess: function(name, x, y, width, height, isTriggeredByEvent){
1586       this.g.setStart();
1587         // anti smoothing
1588         if (this.strokeWidth%2 == 1)
1589             x = Math.round(x) + .5, y = Math.round(y) + .5;
1590         
1591         // shape
1592         var rect = this.g.rect(x, y, width, height, EXPANDED_SUBPROCESS_CORNER_ROUND);
1593         
1594         // Use different stroke (dashed)
1595         if(isTriggeredByEvent) {
1596             rect.attr(EVENT_SUBPROCESS_ATTRS);
1597         } else {
1598             rect.attr(EXPANDED_SUBPROCESS_ATTRS);
1599         }
1600         
1601         this.setContextToElement(rect);
1602         
1603         var fontAttr = EXPANDED_SUBPROCESS_FONT;
1604         
1605         // Include some padding
1606         var paddingX = 10;
1607         var paddingY = 5;
1608         var availableTextSpace = width - paddingX*2;
1609         
1610         var truncated = this.fitTextToWidth(name, availableTextSpace);
1611         var realWidth = this.getStringWidth(truncated, fontAttr);
1612         var realHeight = this.getStringHeight(truncated, fontAttr);
1613         
1614         var textElement = this.g.text(x + width/2 - realWidth*0/2 + 0*paddingX, y + realHeight/2 + paddingY, truncated).attr(fontAttr);
1615         
1616         var set = this.g.setFinish();
1617         // TODO: Expanded Sub Process may has specific handlers
1618         //this.addHandlers(set, x, y, width, height, "task");
1619     },
1620     
1621     drawCollapsedSubProcess: function(name, x, y, width, height, isTriggeredByEvent) {
1622       this.g.setStart();
1623       this._drawCollapsedTask(name, x, y, width, height, false);
1624       var set = this.g.setFinish();
1625     this.addHandlers(set, x, y, width, height, "task");
1626     },
1627     
1628     drawCollapsedCallActivity: function(name, x, y, width, height) {
1629       this.g.setStart();
1630         this._drawCollapsedTask(name, x, y, width, height, true);
1631         var set = this.g.setFinish();
1632     this.addHandlers(set, x, y, width, height, "task");
1633     },
1634
1635     _drawCollapsedTask: function(name, x, y, width, height, thickBorder) {
1636         // The collapsed marker is now visualized separately
1637         this._drawTask(name, x, y, width, height, thickBorder);
1638     },
1639     
1640     drawCollapsedMarker: function(x, y, width, height){
1641         // rectangle
1642         var rectangleWidth = MARKER_WIDTH;
1643         var rectangleHeight = MARKER_WIDTH;
1644         
1645         // anti smoothing
1646         if (this.strokeWidth%2 == 1)
1647             y += .5;
1648         
1649         var rect = this.g.rect(x + (width - rectangleWidth) / 2, y + height - rectangleHeight - 3, rectangleWidth, rectangleHeight);
1650         
1651         // plus inside rectangle
1652         var cx = rect.attr("x") + rect.attr("width")/2;
1653         var cy = rect.attr("y") + rect.attr("height")/2;
1654         
1655         var line = this.g.path(
1656             "M" + cx + " " + (cy+2) + "L" + cx + " " + (cy-2) + 
1657             "M" + (cx-2) + " " + cy + "L" + (cx+2) + " " + cy
1658         ).attr({"stroke-width": this.strokeWidth});
1659         
1660     },
1661     
1662     drawActivityMarkers: function(x, y, width, height, multiInstanceSequential, multiInstanceParallel, collapsed){
1663         if (collapsed) {
1664             if (!multiInstanceSequential && !multiInstanceParallel) {
1665                 this.drawCollapsedMarker(x, y, width, height);
1666             } else {
1667                 this.drawCollapsedMarker(x - MARKER_WIDTH / 2 - 2, y, width, height);
1668                 if (multiInstanceSequential) {
1669                     console.log("is collapsed and multiInstanceSequential");
1670                     this.drawMultiInstanceMarker(true, x + MARKER_WIDTH / 2 + 2, y, width, height);
1671                 } else if (multiInstanceParallel) {
1672                     console.log("is collapsed and multiInstanceParallel");
1673                     this.drawMultiInstanceMarker(false, x + MARKER_WIDTH / 2 + 2, y, width, height);
1674                 }
1675             }
1676         } else {
1677             if (multiInstanceSequential) {
1678                 console.log("is multiInstanceSequential");
1679                 this.drawMultiInstanceMarker(true, x, y, width, height);
1680             } else if (multiInstanceParallel) {
1681                 console.log("is multiInstanceParallel");
1682                 this.drawMultiInstanceMarker(false, x, y, width, height);
1683             }
1684         }
1685     },
1686     
1687     drawGateway: function(x, y, width, height) {
1688         
1689         var rhombus = this.g.path(    "M" + x + " " + (y + (height / 2)) + 
1690                                     "L" + (x + (width / 2)) + " " + (y + height) + 
1691                                     "L" + (x + width) + " " + (y + (height / 2)) +
1692                                     "L" + (x + (width / 2)) + " " + y +
1693                                     "z"
1694                                 );
1695         
1696         // white shaddow
1697         this.drawShaddow(rhombus);
1698         
1699         rhombus.attr("stroke-width", this.strokeWidth);
1700         rhombus.attr("stroke", Color.SlateGrey);
1701         rhombus.attr({fill: Color.white});
1702         
1703         this.setContextToElement(rhombus);
1704         
1705         return rhombus;
1706     },
1707     
1708     drawParallelGateway: function(x, y, width, height) {
1709       this.g.setStart();
1710       
1711         // rhombus
1712         this.drawGateway(x, y, width, height);
1713
1714         // plus inside rhombus
1715         var originalStroke = this.getStroke();
1716         this.setStroke(GATEWAY_TYPE_STROKE);
1717         
1718         var plus = this.g.path(
1719             "M" + (x + 10) + " " + (y + height / 2) + "L" + (x + width - 10) + " " + (y + height / 2) +    // horizontal
1720             "M" + (x + width / 2) + " " + (y + height - 10) + "L" + (x + width / 2) + " " + (y + 10)    // vertical
1721         );
1722         plus.attr({"stroke-width": this.getStroke(), "stroke": this.getPaint()});
1723         
1724         this.setStroke(originalStroke);
1725         
1726         var set = this.g.setFinish();
1727         this.addHandlers(set, x, y, width, height, "gateway");
1728     },
1729     
1730     drawExclusiveGateway: function(x, y, width, height) {
1731       this.g.setStart();
1732       
1733         // rhombus
1734         var rhombus = this.drawGateway(x, y, width, height);
1735
1736         var quarterWidth = width / 4;
1737         var quarterHeight = height / 4;
1738         
1739         // X inside rhombus
1740         var originalStroke = this.getStroke();
1741         this.setStroke(GATEWAY_TYPE_STROKE);
1742         
1743         var iks = this.g.path(
1744             "M" + (x + quarterWidth + 3) + " " + (y + quarterHeight + 3) + "L" + (x + 3 * quarterWidth - 3) + " " + (y + 3 * quarterHeight - 3) + 
1745             "M" + (x + quarterWidth + 3) + " " + (y + 3 * quarterHeight - 3) + "L" + (x + 3 * quarterWidth - 3) + " " + (y + quarterHeight + 3)
1746         );
1747         iks.attr({"stroke-width": this.getStroke(), "stroke": this.getPaint()});
1748         
1749         this.setStroke(originalStroke);
1750         
1751         var set = this.g.setFinish();
1752     this.addHandlers(set, x, y, width, height, "gateway");
1753     },
1754     
1755     drawInclusiveGateway: function(x, y, width, height){
1756       this.g.setStart();
1757       
1758         // rhombus
1759         this.drawGateway(x, y, width, height);
1760         
1761         var diameter = width / 4;
1762         
1763         // circle inside rhombus
1764         var originalStroke = this.getStroke();
1765         this.setStroke(GATEWAY_TYPE_STROKE);
1766         var circle = this.g.ellipse(width/2 + x, height/2 + y, diameter, diameter);
1767         circle.attr({"stroke-width": this.getStroke(), "stroke": this.getPaint()});
1768         
1769         this.setStroke(originalStroke);
1770         
1771         var set = this.g.setFinish();
1772     this.addHandlers(set, x, y, width, height, "gateway");
1773     },
1774     
1775     drawEventBasedGateway: function(x, y, width, height){
1776       this.g.setStart();
1777       
1778         // rhombus
1779         this.drawGateway(x, y, width, height);
1780         
1781         var diameter = width / 2;
1782
1783         // rombus inside rhombus
1784         var originalStroke = this.getStroke();
1785         this.setStroke(GATEWAY_TYPE_STROKE);
1786         
1787         
1788         // draw GeneralPath (polygon)
1789         var n=5;
1790         var angle = 2*Math.PI/n;
1791         var x1Points = [];
1792         var y1Points = [];
1793         
1794         for ( var index = 0; index < n; index++ ) {
1795             var v = index*angle - Math.PI/2;
1796             x1Points[index] = x + parseInt(Math.round(width/2)) + parseInt(Math.round((width/4)*Math.cos(v)));
1797             y1Points[index] = y + parseInt(Math.round(height/2)) + parseInt(Math.round((height/4)*Math.sin(v)));
1798         }
1799         //g.drawPolygon(x1Points, y1Points, n);
1800         
1801         var path = "";
1802         for ( var index = 0; index < n; index++ ) {
1803             if (index == 0) 
1804                 path += "M";
1805             else 
1806                 path += "L";
1807             path += x1Points[index] + "," + y1Points[index];
1808         }
1809         path += "z";
1810         var polygone = this.g.path(path);
1811         polygone.attr("stroke-width", this.strokeWidth);
1812         polygone.attr("stroke", this.getPaint());
1813         
1814         this.setStroke(originalStroke);
1815         
1816         var set = this.g.setFinish();
1817     this.addHandlers(set, x, y, width, height, "gateway");
1818     },
1819     
1820     /*
1821     *  drawMultiInstanceMarker
1822     *  drawHighLight
1823     *  highLightFlow
1824     */
1825     
1826     drawMultiInstanceMarker: function(sequential, x, y, width, height) {
1827         var rectangleWidth = MARKER_WIDTH;
1828         var rectangleHeight = MARKER_WIDTH;
1829         
1830         // anti smoothing
1831         if (this.strokeWidth%2 == 1)
1832             x += .5;//, y += .5;
1833             
1834         var lineX = x + (width - rectangleWidth) / 2;
1835         var lineY = y + height - rectangleHeight - 3;
1836         
1837         var originalStroke = this.getStroke();
1838         this.setStroke(MULTI_INSTANCE_STROKE);
1839         
1840         if (sequential) {
1841             var line = this.g.path(
1842                 "M" + lineX + " " + lineY +                             "L" + (lineX + rectangleWidth) + " " + lineY + 
1843                 "M" + lineX + " " + (lineY + rectangleHeight / 2) +     "L" + (lineX + rectangleWidth) + " " + (lineY + rectangleHeight / 2) + 
1844                 "M" + lineX + " " + (lineY + rectangleHeight) +         "L" + (lineX + rectangleWidth) + " " + (lineY + rectangleHeight)
1845             ).attr({"stroke-width": this.strokeWidth});
1846         } else {
1847             var line = this.g.path(
1848                 "M" + lineX + " " +                             lineY + "L" + lineX + " " +                     (lineY + rectangleHeight) +
1849                 "M" + (lineX + rectangleWidth / 2) + " " +     lineY + "L" + (lineX + rectangleWidth / 2) + " " +     (lineY + rectangleHeight) + 
1850                 "M" + (lineX + rectangleWidth) + " " +         lineY + "L" + (lineX + rectangleWidth) + " " +         (lineY + rectangleHeight)
1851             ).attr({"stroke-width": this.strokeWidth});
1852         }
1853         
1854         this.setStroke(originalStroke);
1855     },
1856     
1857     drawHighLight: function(x, y, width, height){
1858         var originalPaint = this.getPaint();
1859         var originalStroke = this.getStroke();
1860         
1861         this.setPaint(HIGHLIGHT_COLOR);
1862         this.setStroke(THICK_TASK_BORDER_STROKE);
1863
1864         //var c = this.g.rect(x - width/2 - THICK_TASK_BORDER_STROKE, y - height/2 - THICK_TASK_BORDER_STROKE, width + THICK_TASK_BORDER_STROKE*2, height + THICK_TASK_BORDER_STROKE*2, 5);
1865         var rect = this.g.rect(x - THICK_TASK_BORDER_STROKE, y - THICK_TASK_BORDER_STROKE, width + THICK_TASK_BORDER_STROKE*2, height + THICK_TASK_BORDER_STROKE*2, TASK_CORNER_ROUND);
1866         rect.attr("stroke-width", this.strokeWidth);
1867         rect.attr("stroke", this.getPaint());
1868         
1869         this.setPaint(originalPaint);
1870         this.setStroke(originalStroke);
1871     },
1872     
1873     highLightActivity: function(activityId){
1874         var shape = this.g.getById(activityId);
1875         if (!shape) {
1876             console.error("Activity " + activityId + " not found");
1877             return;
1878         }
1879         
1880         var contextObject = shape.data("contextObject");
1881         if (contextObject)
1882             console.log("--> highLightActivity: ["+contextObject.getProperty("type")+"], activityId: " + contextObject.getId());
1883         else
1884             console.log("--> highLightActivity: ", shape, shape.data("contextObject"));
1885         
1886         shape.attr("stroke-width", THICK_TASK_BORDER_STROKE);
1887         shape.attr("stroke", HIGHLIGHT_COLOR);
1888     },
1889     
1890     highLightFlow: function(flowId){
1891         var shapeFlow = this.g.getById(flowId);
1892         if (!shapeFlow) {
1893             console.error("Flow " + flowId + " not found");
1894             return;
1895         }
1896         
1897         var contextObject = shapeFlow.data("contextObject");
1898         if (contextObject)
1899             console.log("--> highLightFlow: ["+contextObject.id+"] " + contextObject.flow);
1900         //console.log("--> highLightFlow: ", flow.flow, flow.data("set"));
1901         
1902         var st = shapeFlow.data("set");
1903         
1904         st.attr("stroke-width", SEQUENCEFLOW_HIGHLIGHT_STROKE);
1905         st.attr("stroke", HIGHLIGHT_COLOR);
1906         var withArrowHead = shapeFlow.data("withArrowHead");
1907         if (withArrowHead)
1908             st[1].attr("fill", HIGHLIGHT_COLOR);
1909         
1910         st.forEach(function(el){
1911             //console.log("---->", el);
1912             //el.attr("")
1913         });
1914     },
1915     
1916
1917     _drawClock: function(cx, cy, width, height){
1918         
1919         var circle = this.g.ellipse(cx, cy, 1, 1).attr({stroke:"none", fill: Color.get(232, 239, 241)});
1920         //var c = this.g.ellipse(cx, cy, width, height).attr({stroke:"none", fill: Color.red});
1921         //x = cx - width/2;
1922         //y = cy - height/2;
1923     
1924         var clock = this.g.path(
1925         /* outer circle */ "M15.5,2.374        C8.251,2.375,2.376,8.251,2.374,15.5        C2.376,22.748,8.251,28.623,15.5,28.627c7.249-0.004,13.124-5.879,13.125-13.127C28.624,8.251,22.749,2.375,15.5,2.374z" +
1926         /* inner circle */ "M15.5,26.623    C8.909,26.615,4.385,22.09,4.375,15.5    C4.385,8.909,8.909,4.384,15.5,4.374c4.59,0.01,11.115,3.535,11.124,11.125C26.615,22.09,22.091,26.615,15.5,26.623z" +
1927         /*  9 */ "M8.625,15.5c-0.001-0.552-0.448-0.999-1.001-1c-0.553,0-1,0.448-1,1c0,0.553,0.449,1,1,1C8.176,16.5,8.624,16.053,8.625,15.5z" +
1928         /*  8 */ "M8.179,18.572c-0.478,0.277-0.642,0.889-0.365,1.367c0.275,0.479,0.889,0.641,1.365,0.365c0.479-0.275,0.643-0.887,0.367-1.367C9.27,18.461,8.658,18.297,8.179,18.572z" +
1929         /* 10 */ "M9.18,10.696c-0.479-0.276-1.09-0.112-1.366,0.366s-0.111,1.09,0.365,1.366c0.479,0.276,1.09,0.113,1.367-0.366C9.821,11.584,9.657,10.973,9.18,10.696z" +
1930         /*  2 */ "M22.822,12.428c0.478-0.275,0.643-0.888,0.366-1.366c-0.275-0.478-0.89-0.642-1.366-0.366c-0.479,0.278-0.642,0.89-0.366,1.367C21.732,12.54,22.344,12.705,22.822,12.428z" +
1931         /*  7 */ "M12.062,21.455c-0.478-0.275-1.089-0.111-1.366,0.367c-0.275,0.479-0.111,1.09,0.366,1.365c0.478,0.277,1.091,0.111,1.365-0.365C12.704,22.344,12.54,21.732,12.062,21.455z" +
1932         /* 11 */ "M12.062,9.545c0.479-0.276,0.642-0.888,0.366-1.366c-0.276-0.478-0.888-0.642-1.366-0.366s-0.642,0.888-0.366,1.366C10.973,9.658,11.584,9.822,12.062,9.545z" +
1933         /*  4 */ "M22.823,18.572c-0.48-0.275-1.092-0.111-1.367,0.365c-0.275,0.479-0.112,1.092,0.367,1.367c0.477,0.275,1.089,0.113,1.365-0.365C23.464,19.461,23.3,18.848,22.823,18.572z" +
1934         /*  2 */ "M19.938,7.813c-0.477-0.276-1.091-0.111-1.365,0.366c-0.275,0.48-0.111,1.091,0.366,1.367s1.089,0.112,1.366-0.366C20.581,8.702,20.418,8.089,19.938,7.813z" +
1935         /*  3 */ "M23.378,14.5c-0.554,0.002-1.001,0.45-1.001,1c0.001,0.552,0.448,1,1.001,1c0.551,0,1-0.447,1-1C24.378,14.949,23.929,14.5,23.378,14.5z" +
1936         /* arrows */ "M15.501,6.624c-0.552,0-1,0.448-1,1l-0.466,7.343l-3.004,1.96c-0.478,0.277-0.642,0.889-0.365,1.365c0.275,0.479,0.889,0.643,1.365,0.367l3.305-1.676C15.39,16.99,15.444,17,15.501,17c0.828,0,1.5-0.671,1.5-1.5l-0.5-7.876C16.501,7.072,16.053,6.624,15.501,6.624z" +
1937         /*  9 */ "M15.501,22.377c-0.552,0-1,0.447-1,1s0.448,1,1,1s1-0.447,1-1S16.053,22.377,15.501,22.377z" +
1938         /*  8 */ "M18.939,21.455c-0.479,0.277-0.643,0.889-0.366,1.367c0.275,0.477,0.888,0.643,1.366,0.365c0.478-0.275,0.642-0.889,0.366-1.365C20.028,21.344,19.417,21.18,18.939,21.455z" +
1939         "");
1940         clock.attr({fill: Color.black, stroke: "none"});
1941         //clock.transform("t " + (cx-29.75/2) + " " + (cy-29.75/2));
1942         //clock.transform("...s 0.85");
1943         
1944         //clock.transform("...s " + .85 + " " + .85);
1945         clock.transform("t " + (-2.374) + " " + (-2.374)    );
1946         clock.transform("...t -" + (15.5-2.374) + " -" + (15.5-2.374)    );
1947         clock.transform("...s " + 1*(width/35) + " " + 1*(height/35));
1948         clock.transform("...T " + cx + " " + cy);
1949         //clock.transform("t " + (cx-width/2) + " " + (cy-height/2));
1950         
1951         //console.log(".getBBox(): ", clock.getBBox());
1952         //console.log(".attr(): ", c.attrs);
1953         circle.attr("rx", clock.getBBox().width/2);
1954         circle.attr("ry", clock.getBBox().height/2);
1955         
1956         //return circle
1957     },
1958     
1959     _drawPentagon: function(cx, cy, width, height, filled){
1960         // draw GeneralPath (polygon)
1961         var n=5;
1962         var angle = 2*Math.PI/n;
1963         var waypoints = [];
1964         
1965         for ( var index = 0; index < n; index++ ) {
1966             var v = index*angle - Math.PI/2;
1967             var point = {};
1968             point.x = -width*1.2/2 + parseInt(Math.round(width*1.2/2)) + parseInt(Math.round((width*1.2/4)*Math.cos(v)));
1969             point.y = -height*1.2/2 + parseInt(Math.round(height*1.2/2)) + parseInt(Math.round((height*1.2/4)*Math.sin(v)));
1970             waypoints[index] = point;
1971         }
1972         
1973         var polygone = new Polygone(waypoints, this.getStroke());
1974         polygone.element = this.g.path(polygone.path);
1975         if (filled)
1976             polygone.element.attr("fill", Color.black);
1977         else
1978             polygone.element.attr("fill", Color.white);
1979         
1980         polygone.element.transform("s " + 1*(width/35) + " " + 1*(height/35));
1981         polygone.element.transform("...T " + cx + " " + cy);
1982     },
1983     
1984     //_drawMultilineText: function(text, x, y, boxWidth, boxHeight, textAnchor) {
1985     _drawMultilineText: function(text, x, y, boxWidth, boxHeight, verticalAlign, horizontalAlign) {
1986         if (!text || text == "") 
1987             return;
1988             
1989         // Autostretch boxHeight if boxHeight is 0
1990         if (boxHeight == 0)
1991           verticalAlign = MULTILINE_VERTICAL_ALIGN_TOP;      
1992     
1993         //var TEXT_PADDING = 3;
1994         var width = boxWidth;
1995         if (boxHeight)
1996             var height = boxHeight;
1997     
1998         var layouts = [];
1999         
2000         //var font = {font: "11px Arial", opacity: 1, "fill": LABEL_COLOR};
2001         var font = this.getFont();
2002         var measurer = new LineBreakMeasurer(this.g, x, y, text, font);
2003         var lineHeight = measurer.rafaelTextObject.getBBox().height;
2004         //console.log("text: ", text.replace(/\n/g, "?"));
2005         
2006         if (height) {
2007             var availableLinesCount = parseInt(height/lineHeight);
2008             //console.log("availableLinesCount: " + availableLinesCount);
2009         }
2010         
2011         var i = 1;
2012         while (measurer.getPosition() < measurer.text.getEndIndex()) {
2013             var layout = measurer.nextLayout(width);
2014             //console.log("LAYOUT: " + layout + ", getPosition: " + measurer.getPosition());
2015             
2016             if (layout != null) {
2017                 // TODO: and check if measurer has next layout. If no then don't draw  dots
2018                 if (!availableLinesCount || i < availableLinesCount) {
2019                     layouts.push(layout);
2020                 } else {
2021                     layouts.push(this.fitTextToWidth(layout + "...", boxWidth));
2022                     break;
2023                 }
2024             }
2025             i++;
2026         };
2027         //console.log(layouts);
2028         
2029         measurer.rafaelTextObject.attr({"text": layouts.join("\n")});
2030         
2031         if (horizontalAlign)
2032             measurer.rafaelTextObject.attr({"text-anchor": horizontalAlign}); // end, middle, start
2033             
2034         var bb = measurer.rafaelTextObject.getBBox();
2035         // TODO: there is somethin wrong with wertical align. May be: measurer.rafaelTextObject.attr({"y": y + height/2 - bb.height/2})
2036         measurer.rafaelTextObject.attr({"y": y + bb.height/2});
2037         //var bb = measurer.rafaelTextObject.getBBox();
2038         
2039         if (measurer.rafaelTextObject.attr("text-anchor") == MULTILINE_HORIZONTAL_ALIGN_MIDDLE )
2040             measurer.rafaelTextObject.attr("x",  x +  boxWidth/2);
2041         else if (measurer.rafaelTextObject.attr("text-anchor") == MULTILINE_HORIZONTAL_ALIGN_RIGHT )
2042             measurer.rafaelTextObject.attr("x",  x +  boxWidth);
2043         
2044         var boxStyle = {stroke: Color.LightSteelBlue2, "stroke-width": 1.0, "stroke-dasharray": "- "};
2045         //var box = this.g.rect(x+.5, y + .5, width, height).attr(boxStyle);
2046         var textAreaCX = x + boxWidth/2;
2047                 var height = boxHeight;
2048                 if (!height) height = bb.height;
2049                 var textAreaCY = y + height/2;
2050                 var dotLeftTop = this.g.ellipse(x, y, 3, 3).attr({"stroke-width": 0, fill: Color.LightSteelBlue, stroke: "none"}).hide();
2051                 var dotCenter = this.g.ellipse(textAreaCX, textAreaCY, 3, 3).attr({fill: Color.LightSteelBlue2, stroke: "none"}).hide();
2052
2053                 /*
2054                 // real bbox
2055                 var bb = measurer.rafaelTextObject.getBBox();
2056                 var rect = paper.rect(bb.x+.5, bb.y + .5, bb.width, bb.height).attr({"stroke-width": 1});
2057                 */
2058                 var rect = this.g.rect(x, y, boxWidth, height).attr({"stroke-width": 1}).attr(boxStyle).hide();
2059                 var debugSet = this.g.set();
2060                 debugSet.push(dotLeftTop, dotCenter, rect);
2061                 //debugSet.show();
2062     },
2063     
2064     drawTextAnnotation: function(text, x, y, width, height){
2065         var lineLength = 18;
2066         var path = [];
2067           path.push(["M", x + lineLength, y]);
2068           path.push(["L", x, y]);
2069           path.push(["L", x, y + height]);
2070           path.push(["L", x + lineLength, y + height]);
2071           
2072           path.push(["L", x + lineLength, y + height -1]);
2073           path.push(["L", x + 1, y + height -1]);
2074           path.push(["L", x + 1, y + 1]);
2075           path.push(["L", x + lineLength, y + 1]);
2076           path.push(["z"]);
2077     
2078         var textAreaLines = this.g.path(path);
2079         
2080       var boxWidth = width - (2 * ANNOTATION_TEXT_PADDING);
2081       var boxHeight = height - (2 * ANNOTATION_TEXT_PADDING);
2082       var boxX = x + width/2 - boxWidth/2;
2083       var boxY = y + height/2 - boxHeight/2;
2084       
2085       // for debug
2086           var rectStyle = {stroke: Color(112, 146, 190), "stroke-width": 1.0, "stroke-dasharray": "- "};
2087           var r = this.g.rect(boxX, boxY, boxWidth, boxHeight).attr(rectStyle);
2088       //
2089       
2090       this.drawAnnotationText(text, boxX, boxY, boxWidth, boxHeight);
2091     },
2092     
2093     drawLabel111111111: function(text, x, y, width, height, labelAttrs){
2094         var  debug = false;
2095         
2096         // text
2097         if (text != null && text != undefined && text != "") {
2098             var attr = LABEL_FONT;
2099             
2100             //console.log("x", x, "y", y, "width", width, "height", height );
2101             
2102             wrappedText = text;
2103             if (labelAttrs && labelAttrs.wrapWidth) {
2104                 wrappedText = this.wrapTextToWidth(wrappedText, labelAttrs.wrapWidth);
2105             }
2106             var realWidth = this.getStringWidth(wrappedText, attr);
2107             var realHeight = this.getStringHeight(wrappedText, attr);
2108             
2109             var textAreaCX = x + width/2;
2110             var textAreaCY = y + 3 + height + this.getStringHeight(wrappedText, attr)/2;
2111             
2112             var textX = textAreaCX;
2113             var textY = textAreaCY;
2114
2115             var textAttrs = {};
2116             if (labelAttrs && labelAttrs.align) {
2117                 switch (labelAttrs.align) {
2118                     case "left": 
2119                         textAttrs["text-anchor"] = "start"; 
2120                         textX = textX - realWidth/2;
2121                     break;
2122                     case "center": 
2123                         textAttrs["text-anchor"] = "middle"; 
2124                     break;
2125                     case "right": 
2126                         textAttrs["text-anchor"] = "end"; 
2127                         textX = textX + realWidth/2;
2128                     break;
2129                 }
2130             }
2131             if (labelAttrs && labelAttrs.wrapWidth) {
2132                 if (true) {
2133                     // Draw frameborder
2134                     var textAreaStyle = {stroke: Color.LightSteelBlue2, "stroke-width": 1.0, "stroke-dasharray": "- "};
2135                     var textAreaX = textAreaCX - realWidth/2;
2136                     var textAreaY = textAreaCY+.5 - realHeight/2;
2137                     var textArea = this.g.rect(textAreaX, textAreaY, realWidth, realHeight).attr(textAreaStyle);
2138                     
2139                     var textAreaLines = this.g.path("M" + textAreaX + " " + textAreaY + "L" + (textAreaX+realWidth) + " " + (textAreaY+realHeight) + "M" +  + (textAreaX+realWidth) + " " + textAreaY + "L" + textAreaX + " " + (textAreaY+realHeight));
2140                     textAreaLines.attr(textAreaStyle);
2141                     
2142                     this.g.ellipse(textAreaCX, textAreaCY, 3, 3).attr({fill: Color.LightSteelBlue2, stroke: "none"});
2143                 }
2144             }
2145             
2146             var label = this.g.text(textX, textY, wrappedText).attr(attr).attr(textAttrs);
2147             //label.id = Raphael.createUUID();
2148             //console.log("label ", label.id, ", ", wrappedText);
2149             
2150             if (this.fontSmoothing) {
2151                 label.attr({stroke: LABEL_COLOR, "stroke-width":.4});
2152             }
2153             
2154             // debug
2155             if (debug) {
2156                 var imageAreaStyle = {stroke: Color.grey61, "stroke-width": 1.0, "stroke-dasharray": "- "};
2157                 var imageArea = this.g.rect(x+.5, y+.5, width, height).attr(imageAreaStyle);
2158                 var imageAreaLines = this.g.path("M" + x + " " + y + "L" + (x+width) + " " + (y+height) + "M" +  + (x+width) + " " + y + "L" + x + " " + (y+height));
2159                 imageAreaLines.attr(imageAreaStyle);
2160                 var dotStyle = {fill: Color.Coral, stroke: "none"};
2161                 this.g.ellipse(x, y, 3, 3).attr(dotStyle);
2162                 this.g.ellipse(x+width, y, 2, 2).attr(dotStyle);
2163                 this.g.ellipse(x+width, y+height, 2, 2).attr(dotStyle);
2164                 this.g.ellipse(x, y+height, 2, 2).attr(dotStyle);
2165             }
2166             
2167             return label;
2168         }
2169     },
2170     
2171     vvoid: function(){}
2172 };