懒羊羊
2023-12-04 b3e9a47f4c17c0d708398698432a109420e7d9e1
提交 | 用户 | 时间
1ac2bc 1 /*
2  * Activiti Modeler component part of the Activiti project
3  * Copyright 2005-2014 Alfresco Software, Ltd. All rights reserved.
4  * 
5  * This library is free software; you can redistribute it and/or
6  * modify it under the terms of the GNU Lesser General Public
7  * License as published by the Free Software Foundation; either
8  * version 2.1 of the License, or (at your option) any later version.
9  *
10  * This library is distributed in the hope that it will be useful,
11  * but WITHOUT ANY WARRANTY; without even the implied warranty of
12  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
13  * Lesser General Public License for more details.
14
15  * You should have received a copy of the GNU Lesser General Public
16  * License along with this library; if not, write to the Free Software
17  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
18  */
19 'use strict';
20
21 angular.module('activitiModeler')
22     .controller('StencilController', ['$rootScope', '$scope', '$http', '$modal', '$timeout', function ($rootScope, $scope, $http, $modal, $timeout) {
23
24         // Property window toggle state
25         $scope.propertyWindowState = {'collapsed': false};
26
27         // Add reference to global header-config
28         $scope.headerConfig = KISBPM.HEADER_CONFIG;
29
30         $scope.propertyWindowState.toggle = function () {
31             $scope.propertyWindowState.collapsed = !$scope.propertyWindowState.collapsed;
32             $timeout(function () {
33                 jQuery(window).trigger('resize');
34             });
35         };
36         
37         // Code that is dependent on an initialised Editor is wrapped in a promise for the editor
38         $scope.editorFactory.promise.then(function () {
39             
40             /* Build stencil item list */
41
42             // Build simple json representation of stencil set
43             var stencilItemGroups = [];
44
45             // Helper method: find a group in an array
46             var findGroup = function (name, groupArray) {
47                 for (var index = 0; index < groupArray.length; index++) {
48                     if (groupArray[index].name === name) {
49                         return groupArray[index];
50                     }
51                 }
52                 return null;
53             };
54
55             // Helper method: add a new group to an array of groups
56             var addGroup = function (groupName, groupArray) {
57                 var group = { name: groupName, items: [], paletteItems: [], groups: [], visible: true };
58                 groupArray.push(group);
59                 return group;
60             };
61
62             /*
63              StencilSet items
64              */
65             $http({method: 'GET', url: KISBPM.URL.getStencilSet()}).success(function (data, status, headers, config) {
66
67                 var quickMenuDefinition = ['UserTask', 'EndNoneEvent', 'ExclusiveGateway', 
68                                            'CatchTimerEvent', 'ThrowNoneEvent', 'TextAnnotation',
69                                            'SequenceFlow', 'Association'];
70                 var ignoreForPaletteDefinition = ['SequenceFlow', 'MessageFlow', 'Association', 'DataAssociation', 'DataStore', 'SendTask'];
71                 var quickMenuItems = [];
72                 
73                 var morphRoles = [];
74                 for (var i = 0; i < data.rules.morphingRules.length; i++) 
75                 {
76                     var role = data.rules.morphingRules[i].role;
77                     var roleItem = {'role': role, 'morphOptions': []};
78                     morphRoles.push(roleItem);
79                 }
80                 
81                 // Check all received items
82                 for (var stencilIndex = 0; stencilIndex < data.stencils.length; stencilIndex++) 
83                 {
84                     // Check if the root group is the 'diagram' group. If so, this item should not be shown.
85                     var currentGroupName = data.stencils[stencilIndex].groups[0];
86                     if (currentGroupName === 'Diagram' || currentGroupName === 'Form') {
87                         continue;  // go to next item
88                     }
89                     
90                     var removed = false;
91                     if (data.stencils[stencilIndex].removed) {
92                         removed = true;
93                     }
94
95                     var currentGroup = undefined;
96                     if (!removed) {
97                         // Check if this group already exists. If not, we create a new one
98
99                         if (currentGroupName !== null && currentGroupName !== undefined && currentGroupName.length > 0) {
100
101                             currentGroup = findGroup(currentGroupName, stencilItemGroups); // Find group in root groups array
102                             if (currentGroup === null) {
103                                 currentGroup = addGroup(currentGroupName, stencilItemGroups);
104                             }
105
106                             // Add all child groups (if any)
107                             for (var groupIndex = 1; groupIndex < data.stencils[stencilIndex].groups.length; groupIndex++) {
108                                 var childGroupName = data.stencils[stencilIndex].groups[groupIndex];
109                                 var childGroup = findGroup(childGroupName, currentGroup.groups);
110                                 if (childGroup === null) {
111                                     childGroup = addGroup(childGroupName, currentGroup.groups);
112                                 }
113
114                                 // The current group variable holds the parent of the next group (if any),
115                                 // and is basically the last element in the array of groups defined in the stencil item
116                                 currentGroup = childGroup;
117
118                             }
119
120                         }
121                     }
122                     
123                     // Construct the stencil item
124                     var stencilItem = {'id': data.stencils[stencilIndex].id,
125                         'name': data.stencils[stencilIndex].title,
126                         'description': data.stencils[stencilIndex].description,
127                         'icon': data.stencils[stencilIndex].icon,
128                         'type': data.stencils[stencilIndex].type,
129                         'roles': data.stencils[stencilIndex].roles,
130                         'removed': removed,
131                         'customIcon': false,
132                         'canConnect': false,
133                         'canConnectTo': false,
134                         'canConnectAssociation': false};
135                     
136                     if (data.stencils[stencilIndex].customIconId && data.stencils[stencilIndex].customIconId > 0) {
137                         stencilItem.customIcon = true;
138                         stencilItem.icon = data.stencils[stencilIndex].customIconId;
139                     }
140                     
141                     if (!removed) {
142                         if (quickMenuDefinition.indexOf(stencilItem.id) >= 0) {
143                             quickMenuItems[quickMenuDefinition.indexOf(stencilItem.id)] = stencilItem;
144                         }
145                     }
146                     
147                     if (stencilItem.id === 'TextAnnotation' || stencilItem.id === 'BoundaryCompensationEvent') {
148                         stencilItem.canConnectAssociation = true;
149                     }
150                     
151                     for (var i = 0; i < data.stencils[stencilIndex].roles.length; i++) {
152                         var stencilRole = data.stencils[stencilIndex].roles[i];
153                         if (stencilRole === 'sequence_start') {
154                             stencilItem.canConnect = true;
155                         } else if (stencilRole === 'sequence_end') {
156                             stencilItem.canConnectTo = true;
157                         }
158                         
159                         for (var j = 0; j < morphRoles.length; j++) {
160                             if (stencilRole === morphRoles[j].role) {
161                                 if (!removed) {
162                                      morphRoles[j].morphOptions.push(stencilItem);
163                                 }
164                                 stencilItem.morphRole = morphRoles[j].role;
165                                 break;
166                             }
167                         }
168                     }
169
170                     if (currentGroup) {
171                         // Add the stencil item to the correct group
172                         currentGroup.items.push(stencilItem);
173                         if (ignoreForPaletteDefinition.indexOf(stencilItem.id) < 0) {
174                             currentGroup.paletteItems.push(stencilItem);
175                         }
176
177                     } else {
178                         // It's a root stencil element
179                         if (!removed) {
180                             stencilItemGroups.push(stencilItem);
181                         }
182                     }
183                 }
184                 
185                 for (var i = 0; i < stencilItemGroups.length; i++) 
186                 {
187                     if (stencilItemGroups[i].paletteItems && stencilItemGroups[i].paletteItems.length == 0)
188                     {
189                         stencilItemGroups[i].visible = false;
190                     }
191                 }
192                 
193                 $scope.stencilItemGroups = stencilItemGroups;
194
195                 var containmentRules = [];
196                 for (var i = 0; i < data.rules.containmentRules.length; i++) 
197                 {
198                     var rule = data.rules.containmentRules[i];
199                     containmentRules.push(rule);
200                 }
201                 $scope.containmentRules = containmentRules;
202                 
203                 // remove quick menu items which are not available anymore due to custom pallette
204                 var availableQuickMenuItems = [];
205                 for (var i = 0; i < quickMenuItems.length; i++) 
206                 {
207                     if (quickMenuItems[i]) {
208                         availableQuickMenuItems[availableQuickMenuItems.length] = quickMenuItems[i];
209                     }
210                 }
211                 
212                 $scope.quickMenuItems = availableQuickMenuItems;
213                 $scope.morphRoles = morphRoles;
214             }).
215             
216             error(function (data, status, headers, config) {
217                 console.log('Something went wrong when fetching stencil items:' + JSON.stringify(data));
218             });
219
220             /*
221              * Listen to selection change events: show properties
222              */
223             $scope.editor.registerOnEvent(ORYX.CONFIG.EVENT_SELECTION_CHANGED, function (event) {
224                 var shapes = event.elements;
225                 var canvasSelected = false;
226                 if (shapes && shapes.length == 0) {
227                     shapes = [$scope.editor.getCanvas()];
228                     canvasSelected = true;
229                 }
230                 if (shapes && shapes.length > 0) {
231
232                     var selectedShape = shapes.first();
233                     var stencil = selectedShape.getStencil();
234                     
235                     if ($rootScope.selectedElementBeforeScrolling && stencil.id().indexOf('BPMNDiagram') !== -1)
236                     {
237                         // ignore canvas event because of empty selection when scrolling stops
238                         return;
239                     }
240                     
241                     if ($rootScope.selectedElementBeforeScrolling && $rootScope.selectedElementBeforeScrolling.getId() === selectedShape.getId())
242                     {
243                         $rootScope.selectedElementBeforeScrolling = null;
244                         return;
245                     }
246
247                     // Store previous selection
248                     $scope.previousSelectedShape = $scope.selectedShape;
249                     
250                     // Only do something if another element is selected (Oryx fires this event multiple times)
251                     if ($scope.selectedShape !== undefined && $scope.selectedShape.getId() === selectedShape.getId()) {
252                         if ($rootScope.forceSelectionRefresh) {
253                             // Switch the flag again, this run will force refresh
254                             $rootScope.forceSelectionRefresh = false;
255                         } else {
256                             // Selected the same element again, no need to update anything
257                             return;
258                         }
259                     }
260
261                     var selectedItem = {'title': '', 'properties': []};
262
263                     if (canvasSelected) {
264                         selectedItem.auditData = {
265                             'author': $scope.modelData.createdByUser,
266                             'createDate': $scope.modelData.createDate
267                         };
268                     }
269
270                     // Gather properties of selected item
271                     var properties = stencil.properties();
272                     for (var i = 0; i < properties.length; i++) {
273                         var property = properties[i];
274                         if (property.popular() == false) continue;
275                         var key = property.prefix() + "-" + property.id();
276
277                         if (key === 'oryx-name') {
278                             selectedItem.title = selectedShape.properties[key];
279                         }
280
281                         // First we check if there is a config for 'key-type' and then for 'type' alone
282                         var propertyConfig = KISBPM.PROPERTY_CONFIG[key + '-' + property.type()];
283                         if (propertyConfig === undefined || propertyConfig === null) {
284                             propertyConfig = KISBPM.PROPERTY_CONFIG[property.type()];
285                         }
286
287                         if (propertyConfig === undefined || propertyConfig === null) {
288                             console.log('WARNING: no property configuration defined for ' + key + ' of type ' + property.type());
289                         } else {
290
291                             if (selectedShape.properties[key] === 'true') {
292                                 selectedShape.properties[key] = true;
293                             }
294                             
295                             if (KISBPM.CONFIG.showRemovedProperties == false && property.isHidden())
296                             {
297                                 continue;
298                             }
299
300                             var currentProperty = {
301                                 'key': key,
302                                 'title': property.title(),
303                                 'type': property.type(),
304                                 'mode': 'read',
305                                 'hidden': property.isHidden(),
306                                 'value': selectedShape.properties[key]
307                             };
308                             
309                             if ((currentProperty.type === 'complex' || currentProperty.type === 'multiplecomplex') && currentProperty.value && currentProperty.value.length > 0) {
310                                 try {
311                                     currentProperty.value = JSON.parse(currentProperty.value);
312                                 } catch (err) {
313                                     // ignore
314                                 }
315                             }
316
317                             if (propertyConfig.readModeTemplateUrl !== undefined && propertyConfig.readModeTemplateUrl !== null) {
318                                 currentProperty.readModeTemplateUrl = propertyConfig.readModeTemplateUrl + '?version=' + $rootScope.staticIncludeVersion;
319                             }
320                             if (propertyConfig.writeModeTemplateUrl !== null && propertyConfig.writeModeTemplateUrl !== null) {
321                                 currentProperty.writeModeTemplateUrl = propertyConfig.writeModeTemplateUrl + '?version=' + $rootScope.staticIncludeVersion;
322                             }
323
324                             if (propertyConfig.templateUrl !== undefined && propertyConfig.templateUrl !== null) {
325                                 currentProperty.templateUrl = propertyConfig.templateUrl + '?version=' + $rootScope.staticIncludeVersion;
326                                 currentProperty.hasReadWriteMode = false;
327                             }
328                             else {
329                                 currentProperty.hasReadWriteMode = true;
330                             }
331
332                             if (currentProperty.value === undefined
333                                 || currentProperty.value === null
334                                 || currentProperty.value.length == 0) {
335                                 currentProperty.noValue = true;
336                             }
337
338                             selectedItem.properties.push(currentProperty);
339                         }
340                     }
341
342                     // Need to wrap this in an $apply block, see http://jimhoskins.com/2012/12/17/angularjs-and-apply.html
343                     $scope.safeApply(function () {
344                         $scope.selectedItem = selectedItem;
345                         $scope.selectedShape = selectedShape;
346                     });
347
348                 } else {
349                     $scope.safeApply(function () {
350                         $scope.selectedItem = {};
351                         $scope.selectedShape = null;
352                     });
353                 }
354             });
355             
356             $scope.editor.registerOnEvent(ORYX.CONFIG.EVENT_SELECTION_CHANGED, function (event) {
357                 
358                 KISBPM.eventBus.dispatch(KISBPM.eventBus.EVENT_TYPE_HIDE_SHAPE_BUTTONS);
359                 var shapes = event.elements;
360                 
361                 if (shapes && shapes.length == 1) {
362
363                     var selectedShape = shapes.first();
364                 
365                     var a = $scope.editor.getCanvas().node.getScreenCTM();
366                     
367                     var absoluteXY = selectedShape.absoluteXY();
368                     
369                     absoluteXY.x *= a.a;
370                     absoluteXY.y *= a.d;
371                     
372                     var additionalIEZoom = 1;
373                     if (!isNaN(screen.logicalXDPI) && !isNaN(screen.systemXDPI)) {
374                         var ua = navigator.userAgent;
375                         if (ua.indexOf('MSIE') >= 0) {
376                             //IE 10 and below
377                             var zoom = Math.round((screen.deviceXDPI / screen.logicalXDPI) * 100);
378                             if (zoom !== 100) {
379                                 additionalIEZoom = zoom / 100
380                             }
381                         }
382                     }
383                     
384                     if (additionalIEZoom === 1) {
385                          absoluteXY.y = absoluteXY.y - jQuery("#canvasSection").offset().top + 5;
386                          absoluteXY.x = absoluteXY.x - jQuery("#canvasSection").offset().left;
387                     
388                     } else {
389                          var canvasOffsetLeft = jQuery("#canvasSection").offset().left;
390                          var canvasScrollLeft = jQuery("#canvasSection").scrollLeft();
391                          var canvasScrollTop = jQuery("#canvasSection").scrollTop();
392                          
393                          var offset = a.e - (canvasOffsetLeft * additionalIEZoom);
394                          var additionaloffset = 0;
395                          if (offset > 10) {
396                              additionaloffset = (offset / additionalIEZoom) - offset;
397                          }
398                          absoluteXY.y = absoluteXY.y - (jQuery("#canvasSection").offset().top * additionalIEZoom) + 5 + ((canvasScrollTop * additionalIEZoom) - canvasScrollTop);
399                          absoluteXY.x = absoluteXY.x - (canvasOffsetLeft * additionalIEZoom) + additionaloffset + ((canvasScrollLeft * additionalIEZoom) - canvasScrollLeft);
400                     }
401                     
402                     var bounds = new ORYX.Core.Bounds(a.e + absoluteXY.x, a.f + absoluteXY.y, a.e + absoluteXY.x + a.a*selectedShape.bounds.width(), a.f + absoluteXY.y + a.d*selectedShape.bounds.height());
403                     var shapeXY = bounds.upperLeft();
404                     
405                     var stencilItem = $scope.getStencilItemById(selectedShape.getStencil().idWithoutNs());
406                     var morphShapes = [];
407                     if (stencilItem && stencilItem.morphRole)
408                     {
409                         for (var i = 0; i < $scope.morphRoles.length; i++)
410                         {
411                             if ($scope.morphRoles[i].role === stencilItem.morphRole)
412                             {
413                                 morphShapes = $scope.morphRoles[i].morphOptions;
414                             }
415                         }
416                     }
417                     
418                     var x = shapeXY.x;
419                     if (bounds.width() < 48) {
420                         x -= 24;
421                     }
422                     
423                     if (morphShapes && morphShapes.length > 0) {
424                         // In case the element is not wide enough, start the 2 bottom-buttons more to the left
425                         // to prevent overflow in the right-menu
426                         var morphButton = document.getElementById('morph-button');
427                         morphButton.style.display = "block";
428                         morphButton.style.left = x + 24 +'px';
429                         morphButton.style.top = (shapeXY.y+bounds.height() + 2) + 'px';
430                     }
431                     
432                     var deleteButton = document.getElementById('delete-button');
433                     deleteButton.style.display = "block";
434                     deleteButton.style.left = x + 'px';
435                     deleteButton.style.top = (shapeXY.y+bounds.height() + 2) + 'px';
436                     
437                     if (stencilItem && (stencilItem.canConnect || stencilItem.canConnectAssociation)) {
438                         var quickButtonCounter = 0;
439                         var quickButtonX = shapeXY.x+bounds.width() + 5;
440                         var quickButtonY = shapeXY.y;
441                         jQuery('.Oryx_button').each(function(i, obj) {
442                             if (obj.id !== 'morph-button' && obj.id != 'delete-button') {
443                                 quickButtonCounter++;
444                                 if (quickButtonCounter > 3) {
445                                     quickButtonX = shapeXY.x+bounds.width() + 5;
446                                     quickButtonY += 24;
447                                     quickButtonCounter = 1;
448                                     
449                                 } else if (quickButtonCounter > 1) {
450                                     quickButtonX += 24;
451                                 }
452                                 obj.style.display = "block";
453                                 obj.style.left = quickButtonX + 'px';
454                                 obj.style.top = quickButtonY + 'px';
455                             }
456                         });
457                     }
458                 }
459             });
460             
461             if (!$rootScope.stencilInitialized) {
462                 KISBPM.eventBus.addListener(KISBPM.eventBus.EVENT_TYPE_HIDE_SHAPE_BUTTONS, function (event) {
463                     jQuery('.Oryx_button').each(function(i, obj) {
464                         obj.style.display = "none";
465                       });
466                 });
467
468                 /*
469                  * Listen to property updates and act upon them
470                  */
471                 KISBPM.eventBus.addListener(KISBPM.eventBus.EVENT_TYPE_PROPERTY_VALUE_CHANGED, function (event) {
472                     if (event.property && event.property.key) {
473                         // If the name property is been updated, we also need to change the title of the currently selected item
474                         if (event.property.key === 'oryx-name' && $scope.selectedItem !== undefined && $scope.selectedItem !== null) {
475                             $scope.selectedItem.title = event.newValue;
476                         }
477
478                         // Update "no value" flag
479                         event.property.noValue = (event.property.value === undefined
480                             || event.property.value === null
481                             || event.property.value.length == 0);
482                     }
483                 });
484                 
485                 $rootScope.stencilInitialized = true;
486             }
487             
488             $scope.morphShape = function() {
489                 $scope.safeApply(function () {
490                     
491                     var shapes = $rootScope.editor.getSelection();
492                     if (shapes && shapes.length == 1)
493                     {
494                         $rootScope.currentSelectedShape = shapes.first();
495                         var stencilItem = $scope.getStencilItemById($rootScope.currentSelectedShape.getStencil().idWithoutNs());
496                         var morphShapes = [];
497                         for (var i = 0; i < $scope.morphRoles.length; i++)
498                         {
499                             if ($scope.morphRoles[i].role === stencilItem.morphRole)
500                             {
501                                 morphShapes = $scope.morphRoles[i].morphOptions.slice();
502                             }
503                         }
504
505                         // Method to open shape select dialog (used later on)
506                         var showSelectShapeDialog = function()
507                         {
508                             $rootScope.morphShapes = morphShapes;
509                             $modal({
510                                 backdrop: false,
511                                 keyboard: true,
512                                 template: 'editor-app/popups/select-shape.html?version=' + Date.now()
513                             });
514                         };
515
516                         showSelectShapeDialog();
517                     }
518                 });
519             };
520             
521             $scope.deleteShape = function() {
522               KISBPM.TOOLBAR.ACTIONS.deleteItem({'$scope': $scope});
523             };
524             
525             $scope.quickAddItem = function(newItemId) {
526                 $scope.safeApply(function () {
527                     
528                     var shapes = $rootScope.editor.getSelection();
529                     if (shapes && shapes.length == 1)
530                     {
531                         $rootScope.currentSelectedShape = shapes.first();
532                         
533                         var containedStencil = undefined;
534                         var stencilSets = $scope.editor.getStencilSets().values();
535                         for (var i = 0; i < stencilSets.length; i++)
536                         {
537                             var stencilSet = stencilSets[i];
538                             var nodes = stencilSet.nodes();
539                             for (var j = 0; j < nodes.length; j++)
540                             {
541                                 if (nodes[j].idWithoutNs() === newItemId)
542                                 {
543                                     containedStencil = nodes[j];
544                                     break;
545                                 }
546                             }
547                         }
548                         
549                         if (!containedStencil) return;
550                         
551                         var option = {type: $scope.currentSelectedShape.getStencil().namespace() + newItemId, namespace: $scope.currentSelectedShape.getStencil().namespace()};
552                         option['connectedShape'] = $rootScope.currentSelectedShape;
553                         option['parent'] = $rootScope.currentSelectedShape.parent;
554                         option['containedStencil'] = containedStencil;
555                     
556                         var args = { sourceShape: $rootScope.currentSelectedShape, targetStencil: containedStencil };
557                         var targetStencil = $scope.editor.getRules().connectMorph(args);
558                         if (!targetStencil){ return; }// Check if there can be a target shape
559                         option['connectingType'] = targetStencil.id();
560
561                         var command = new KISBPM.CreateCommand(option, undefined, undefined, $scope.editor);
562                     
563                         $scope.editor.executeCommands([command]);
564                     }
565                 });
566             };
567
568         }); // end of $scope.editorFactory.promise block
569
570         /* Click handler for clicking a property */
571         $scope.propertyClicked = function (index) {
572             if (!$scope.selectedItem.properties[index].hidden) {
573                 $scope.selectedItem.properties[index].mode = "write";
574             }
575         };
576
577         /* Helper method to retrieve the template url for a property */
578         $scope.getPropertyTemplateUrl = function (index) {
579             return $scope.selectedItem.properties[index].templateUrl;
580         };
581         $scope.getPropertyReadModeTemplateUrl = function (index) {
582             return $scope.selectedItem.properties[index].readModeTemplateUrl;
583         };
584         $scope.getPropertyWriteModeTemplateUrl = function (index) {
585             return $scope.selectedItem.properties[index].writeModeTemplateUrl;
586         };
587
588         /* Method available to all sub controllers (for property controllers) to update the internal Oryx model */
589         $scope.updatePropertyInModel = function (property, shapeId) {
590
591             var shape = $scope.selectedShape;
592             // Some updates may happen when selected shape is already changed, so when an additional
593             // shapeId is supplied, we need to make sure the correct shape is updated (current or previous)
594             if (shapeId) {
595                 if (shape.id != shapeId && $scope.previousSelectedShape && $scope.previousSelectedShape.id == shapeId) {
596                     shape = $scope.previousSelectedShape;
597                 } else {
598                     shape = null;
599                 }
600             }
601
602             if (!shape) {
603                 // When no shape is selected, or no shape is found for the alternative
604                 // shape ID, do nothing
605                 return;
606             }
607             var key = property.key;
608             var newValue = property.value;
609             var oldValue = shape.properties[key];
610
611             if (newValue != oldValue) {
612                 var commandClass = ORYX.Core.Command.extend({
613                     construct: function () {
614                         this.key = key;
615                         this.oldValue = oldValue;
616                         this.newValue = newValue;
617                         this.shape = shape;
618                         this.facade = $scope.editor;
619                     },
620                     execute: function () {
621                         this.shape.setProperty(this.key, this.newValue);
622                         this.facade.getCanvas().update();
623                         this.facade.updateSelection();
624                     },
625                     rollback: function () {
626                         this.shape.setProperty(this.key, this.oldValue);
627                         this.facade.getCanvas().update();
628                         this.facade.updateSelection();
629                     }
630                 });
631                 // Instantiate the class
632                 var command = new commandClass();
633
634                 // Execute the command
635                 $scope.editor.executeCommands([command]);
636                 $scope.editor.handleEvents({
637                     type: ORYX.CONFIG.EVENT_PROPWINDOW_PROP_CHANGED,
638                     elements: [shape],
639                     key: key
640                 });
641
642                 // Switch the property back to read mode, now the update is done
643                 property.mode = 'read';
644
645                 // Fire event to all who is interested
646                 // Fire event to all who want to know about this
647                 var event = {
648                     type: KISBPM.eventBus.EVENT_TYPE_PROPERTY_VALUE_CHANGED,
649                     property: property,
650                     oldValue: oldValue,
651                     newValue: newValue
652                 };
653                 KISBPM.eventBus.dispatch(event.type, event);
654             } else {
655                 // Switch the property back to read mode, no update was needed
656                 property.mode = 'read';
657             }
658
659         };
660
661         /**
662          * Helper method that searches a group for an item with the given id.
663          * If not found, will return undefined.
664          */
665         $scope.findStencilItemInGroup = function (stencilItemId, group) {
666
667             var item;
668
669             // Check all items directly in this group
670             for (var j = 0; j < group.items.length; j++) {
671                 item = group.items[j];
672                 if (item.id === stencilItemId) {
673                     return item;
674                 }
675             }
676
677             // Check the child groups
678             if (group.groups && group.groups.length > 0) {
679                 for (var k = 0; k < group.groups.length; k++) {
680                     item = $scope.findStencilItemInGroup(stencilItemId, group.groups[k]);
681                     if (item) {
682                         return item;
683                     }
684                 }
685             }
686
687             return undefined;
688         };
689
690         /**
691          * Helper method to find a stencil item.
692          */
693         $scope.getStencilItemById = function (stencilItemId) {
694             for (var i = 0; i < $scope.stencilItemGroups.length; i++) {
695                 var element = $scope.stencilItemGroups[i];
696
697                 // Real group
698                 if (element.items !== null && element.items !== undefined) {
699                     var item = $scope.findStencilItemInGroup(stencilItemId, element);
700                     if (item) {
701                         return item;
702                     }
703                 } else { // Root stencil item
704                     if (element.id === stencilItemId) {
705                         return element;
706                     }
707                 }
708             }
709             return undefined;
710         };
711
712         /*
713          * DRAG AND DROP FUNCTIONALITY
714          */
715
716         $scope.dropCallback = function (event, ui) {
717             
718             $scope.editor.handleEvents({
719                 type: ORYX.CONFIG.EVENT_HIGHLIGHT_HIDE,
720                 highlightId: "shapeRepo.attached"
721             });
722             $scope.editor.handleEvents({
723                 type: ORYX.CONFIG.EVENT_HIGHLIGHT_HIDE,
724                 highlightId: "shapeRepo.added"
725             });
726             
727             $scope.editor.handleEvents({
728                 type: ORYX.CONFIG.EVENT_HIGHLIGHT_HIDE,
729                 highlightId: "shapeMenu"
730             });
731             
732             KISBPM.eventBus.dispatch(KISBPM.eventBus.EVENT_TYPE_HIDE_SHAPE_BUTTONS);
733
734             if ($scope.dragCanContain) {
735
736                 var item = $scope.getStencilItemById(ui.draggable[0].id);
737                 
738                 var pos = {x: event.pageX, y: event.pageY};
739                 
740                 var additionalIEZoom = 1;
741                 if (!isNaN(screen.logicalXDPI) && !isNaN(screen.systemXDPI)) {
742                     var ua = navigator.userAgent;
743                     if (ua.indexOf('MSIE') >= 0) {
744                         //IE 10 and below
745                         var zoom = Math.round((screen.deviceXDPI / screen.logicalXDPI) * 100);
746                         if (zoom !== 100) {
747                             additionalIEZoom = zoom / 100;
748                         }
749                     }
750                 }
751                 
752                 var screenCTM = $scope.editor.getCanvas().node.getScreenCTM();
753
754                 // Correcting the UpperLeft-Offset
755                 pos.x -= (screenCTM.e / additionalIEZoom);
756                 pos.y -= (screenCTM.f / additionalIEZoom);
757                 // Correcting the Zoom-Factor
758                 pos.x /= screenCTM.a;
759                 pos.y /= screenCTM.d;
760                 
761                 // Correcting the ScrollOffset
762                 pos.x -= document.documentElement.scrollLeft;
763                 pos.y -= document.documentElement.scrollTop;
764                 
765                 var parentAbs = $scope.dragCurrentParent.absoluteXY();
766                 pos.x -= parentAbs.x;
767                 pos.y -= parentAbs.y;
768
769                 var containedStencil = undefined;
770                 var stencilSets = $scope.editor.getStencilSets().values();
771                 for (var i = 0; i < stencilSets.length; i++)
772                 {
773                     var stencilSet = stencilSets[i];
774                     var nodes = stencilSet.nodes();
775                     for (var j = 0; j < nodes.length; j++)
776                     {
777                         if (nodes[j].idWithoutNs() === ui.draggable[0].id)
778                         {
779                             containedStencil = nodes[j];
780                             break;
781                         }
782                     }
783
784                     if (!containedStencil)
785                     {
786                         var edges = stencilSet.edges();
787                         for (var j = 0; j < edges.length; j++)
788                         {
789                             if (edges[j].idWithoutNs() === ui.draggable[0].id)
790                             {
791                                 containedStencil = edges[j];
792                                 break;
793                             }
794                         }
795                     }
796                 }
797
798                 if (!containedStencil) return;
799
800                 if ($scope.quickMenu)
801                 {
802                     var shapes = $scope.editor.getSelection();
803                     if (shapes && shapes.length == 1)
804                     {
805                         var currentSelectedShape = shapes.first();
806
807                         var option = {};
808                         option.type = currentSelectedShape.getStencil().namespace() + ui.draggable[0].id;
809                         option.namespace = currentSelectedShape.getStencil().namespace();
810                         option.connectedShape = currentSelectedShape;
811                         option.parent = $scope.dragCurrentParent;
812                         option.containedStencil = containedStencil;
813                         
814                         // If the ctrl key is not pressed, 
815                         // snapp the new shape to the center 
816                         // if it is near to the center of the other shape
817                         if (!event.ctrlKey){
818                             // Get the center of the shape
819                             var cShape = currentSelectedShape.bounds.center();
820                             // Snapp +-20 Pixel horizontal to the center 
821                             if (20 > Math.abs(cShape.x - pos.x)){
822                                 pos.x = cShape.x;
823                             }
824                             // Snapp +-20 Pixel vertical to the center 
825                             if (20 > Math.abs(cShape.y - pos.y)){
826                                 pos.y = cShape.y;
827                             }
828                         }
829                         
830                         option.position = pos;
831                     
832                         if (containedStencil.idWithoutNs() !== 'SequenceFlow' && containedStencil.idWithoutNs() !== 'Association' && 
833                                 containedStencil.idWithoutNs() !== 'MessageFlow' && containedStencil.idWithoutNs() !== 'DataAssociation')
834                         {
835                             var args = { sourceShape: currentSelectedShape, targetStencil: containedStencil };
836                             var targetStencil = $scope.editor.getRules().connectMorph(args);
837                             if (!targetStencil){ return; }// Check if there can be a target shape
838                             option.connectingType = targetStencil.id();
839                         }
840     
841                         var command = new KISBPM.CreateCommand(option, $scope.dropTargetElement, pos, $scope.editor);
842                     
843                         $scope.editor.executeCommands([command]);
844                     }
845                 }
846                 else
847                 {
848                     var canAttach = false;
849                     if (containedStencil.idWithoutNs() === 'BoundaryErrorEvent' || containedStencil.idWithoutNs() === 'BoundaryTimerEvent' ||
850                         containedStencil.idWithoutNs() === 'BoundarySignalEvent' || containedStencil.idWithoutNs() === 'BoundaryMessageEvent' ||
851                         containedStencil.idWithoutNs() === 'BoundaryCancelEvent' || containedStencil.idWithoutNs() === 'BoundaryCompensationEvent') {
852                         // Modify position, otherwise boundary event will get position related to left corner of the canvas instead of the container
853                         pos = $scope.editor.eventCoordinates( event );
854                         canAttach = true;
855                     }
856
857                     var option = {};
858                     option['type'] = $scope.modelData.model.stencilset.namespace + item.id;
859                     option['namespace'] = $scope.modelData.model.stencilset.namespace;
860                     option['position'] = pos;
861                     option['parent'] = $scope.dragCurrentParent;
862
863                     var commandClass = ORYX.Core.Command.extend({
864                         construct: function(option, dockedShape, canAttach, position, facade){
865                             this.option = option;
866                             this.docker = null;
867                             this.dockedShape = dockedShape;
868                             this.dockedShapeParent = dockedShape.parent || facade.getCanvas();
869                             this.position = position;
870                             this.facade    = facade;
871                             this.selection = this.facade.getSelection();
872                             this.shape = null;
873                             this.parent = null;
874                             this.canAttach = canAttach;
875                         },
876                         execute: function(){
877                             if (!this.shape) {
878                                 this.shape = this.facade.createShape(option);
879                                 this.parent = this.shape.parent;
880                             } else if (this.parent) {
881                                 this.parent.add(this.shape);
882                             }
883
884                             if (this.canAttach && this.shape.dockers && this.shape.dockers.length) {
885                                 this.docker = this.shape.dockers[0];
886
887                                 this.dockedShapeParent.add(this.docker.parent);
888
889                                 // Set the Docker to the new Shape
890                                 this.docker.setDockedShape(undefined);
891                                 this.docker.bounds.centerMoveTo(this.position);
892                                 if (this.dockedShape !== this.facade.getCanvas()) {
893                                     this.docker.setDockedShape(this.dockedShape);
894                                 }
895                                 this.facade.setSelection( [this.docker.parent] );
896                             }
897
898                             this.facade.getCanvas().update();
899                             this.facade.updateSelection();
900
901                         },
902                         rollback: function(){
903                             if (this.shape) {
904                                 this.facade.setSelection(this.selection.without(this.shape));
905                                 this.facade.deleteShape(this.shape);
906                             }
907                             if (this.canAttach && this.docker) {
908                                 this.docker.setDockedShape(undefined);
909                             }
910                             this.facade.getCanvas().update();
911                             this.facade.updateSelection();
912
913                         }
914                     });
915
916                     // Update canvas
917                     var command = new commandClass(option, $scope.dragCurrentParent, canAttach, pos, $scope.editor);
918                     $scope.editor.executeCommands([command]);
919
920                     // Fire event to all who want to know about this
921                     var dropEvent = {
922                         type: KISBPM.eventBus.EVENT_TYPE_ITEM_DROPPED,
923                         droppedItem: item,
924                         position: pos
925                     };
926                     KISBPM.eventBus.dispatch(dropEvent.type, dropEvent);
927                 }
928             }
929
930             $scope.dragCurrentParent = undefined;
931             $scope.dragCurrentParentId = undefined;
932             $scope.dragCurrentParentStencil = undefined;
933             $scope.dragCanContain = undefined;
934             $scope.quickMenu = undefined;
935             $scope.dropTargetElement = undefined;
936         };
937
938
939         $scope.overCallback = function (event, ui) {
940             $scope.dragModeOver = true;
941         };
942
943         $scope.outCallback = function (event, ui) {
944             $scope.dragModeOver = false;
945         };
946
947         $scope.startDragCallback = function (event, ui) {
948             $scope.dragModeOver = false;
949             $scope.quickMenu = false;
950             if (!ui.helper.hasClass('stencil-item-dragged')) {
951                 ui.helper.addClass('stencil-item-dragged');
952             }
953         };
954         
955         $scope.startDragCallbackQuickMenu = function (event, ui) {
956             $scope.dragModeOver = false;
957             $scope.quickMenu = true;
958         };
959         
960         $scope.dragCallback = function (event, ui) {
961             
962             if ($scope.dragModeOver != false) {
963                 
964                 var coord = $scope.editor.eventCoordinatesXY(event.pageX, event.pageY);
965                 
966                 var additionalIEZoom = 1;
967                 if (!isNaN(screen.logicalXDPI) && !isNaN(screen.systemXDPI)) {
968                     var ua = navigator.userAgent;
969                     if (ua.indexOf('MSIE') >= 0) {
970                         //IE 10 and below
971                         var zoom = Math.round((screen.deviceXDPI / screen.logicalXDPI) * 100);
972                         if (zoom !== 100) {
973                             additionalIEZoom = zoom / 100
974                         }
975                     }
976                 }
977                 
978                 if (additionalIEZoom !== 1) {
979                      coord.x = coord.x / additionalIEZoom;
980                      coord.y = coord.y / additionalIEZoom;
981                 }
982                 
983                 var aShapes = $scope.editor.getCanvas().getAbstractShapesAtPosition(coord);
984                 
985                 if (aShapes.length <= 0) {
986                     if (event.helper) {
987                         $scope.dragCanContain = false;
988                         return false;
989                     }
990                 }
991
992                 if (aShapes[0] instanceof ORYX.Core.Canvas) {
993                     $scope.editor.getCanvas().setHightlightStateBasedOnX(coord.x);
994                 }
995
996                 if (aShapes.length == 1 && aShapes[0] instanceof ORYX.Core.Canvas)
997                 {
998                     var parentCandidate = aShapes[0];
999
1000                     $scope.dragCanContain = true;
1001                     $scope.dragCurrentParent = parentCandidate;
1002                     $scope.dragCurrentParentId = parentCandidate.id;
1003
1004                     $scope.editor.handleEvents({
1005                         type: ORYX.CONFIG.EVENT_HIGHLIGHT_HIDE,
1006                         highlightId: "shapeRepo.attached"
1007                     });
1008                     $scope.editor.handleEvents({
1009                         type: ORYX.CONFIG.EVENT_HIGHLIGHT_HIDE,
1010                         highlightId: "shapeRepo.added"
1011                     });
1012                     return false;
1013                 }
1014                 else 
1015                 {
1016                     var item = $scope.getStencilItemById(event.target.id);
1017                     
1018                     var parentCandidate = aShapes.reverse().find(function (candidate) {
1019                         return (candidate instanceof ORYX.Core.Canvas
1020                             || candidate instanceof ORYX.Core.Node
1021                             || candidate instanceof ORYX.Core.Edge);
1022                     });
1023                     
1024                     if (!parentCandidate) {
1025                         $scope.dragCanContain = false;
1026                         return false;
1027                     }
1028                     
1029                     if (item.type === "node") {
1030                         
1031                         // check if the draggable is a boundary event and the parent an Activity
1032                         var _canContain = false;
1033                         var parentStencilId = parentCandidate.getStencil().id();
1034
1035                         if ($scope.dragCurrentParentId && $scope.dragCurrentParentId === parentCandidate.id) {
1036                             return false;
1037                         }
1038
1039                         var parentItem = $scope.getStencilItemById(parentCandidate.getStencil().idWithoutNs());
1040                         if (parentItem.roles.indexOf("Activity") > -1) {
1041                             if (item.roles.indexOf("IntermediateEventOnActivityBoundary") > -1) {
1042                                 _canContain = true;
1043                             }
1044                         }
1045                         else if (parentCandidate.getStencil().idWithoutNs() === 'Pool')
1046                         {
1047                             if (item.id === 'Lane')
1048                             {
1049                                 _canContain = true;
1050                             }
1051                         }
1052                         
1053                         if (_canContain)
1054                         {
1055                             $scope.editor.handleEvents({
1056                                 type: ORYX.CONFIG.EVENT_HIGHLIGHT_SHOW,
1057                                 highlightId: "shapeRepo.attached",
1058                                 elements: [parentCandidate],
1059                                 style: ORYX.CONFIG.SELECTION_HIGHLIGHT_STYLE_RECTANGLE,
1060                                 color: ORYX.CONFIG.SELECTION_VALID_COLOR
1061                             });
1062
1063                             $scope.editor.handleEvents({
1064                                 type: ORYX.CONFIG.EVENT_HIGHLIGHT_HIDE,
1065                                 highlightId: "shapeRepo.added"
1066                             });
1067                         }
1068                         else
1069                         {
1070                             for (var i = 0; i < $scope.containmentRules.length; i++) {
1071                                 var rule = $scope.containmentRules[i];
1072                                 if (rule.role === parentItem.id) {
1073                                     for (var j = 0; j < rule.contains.length; j++) {
1074                                         if (item.roles.indexOf(rule.contains[j]) > -1) {
1075                                             _canContain = true;
1076                                             break;
1077                                         }
1078                                     }
1079
1080                                     if (_canContain) {
1081                                         break;
1082                                     }
1083                                 }
1084                             }
1085
1086                             // Show Highlight
1087                             $scope.editor.handleEvents({
1088                                 type: ORYX.CONFIG.EVENT_HIGHLIGHT_SHOW,
1089                                 highlightId: 'shapeRepo.added',
1090                                 elements: [parentCandidate],
1091                                 color: _canContain ? ORYX.CONFIG.SELECTION_VALID_COLOR : ORYX.CONFIG.SELECTION_INVALID_COLOR
1092                             });
1093
1094                             $scope.editor.handleEvents({
1095                                 type: ORYX.CONFIG.EVENT_HIGHLIGHT_HIDE,
1096                                 highlightId: "shapeRepo.attached"
1097                             });
1098                         }
1099
1100                         $scope.dragCurrentParent = parentCandidate;
1101                         $scope.dragCurrentParentId = parentCandidate.id;
1102                         $scope.dragCurrentParentStencil = parentStencilId;
1103                         $scope.dragCanContain = _canContain;
1104                         
1105                     } else  { 
1106                         var canvasCandidate = $scope.editor.getCanvas();
1107                         var canConnect = false;
1108                         
1109                         var targetStencil = $scope.getStencilItemById(parentCandidate.getStencil().idWithoutNs());
1110                         if (targetStencil) {
1111                             var associationConnect = false;
1112                             if (stencil.idWithoutNs() === 'Association' && (curCan.getStencil().idWithoutNs() === 'TextAnnotation' || curCan.getStencil().idWithoutNs() === 'BoundaryCompensationEvent')) {
1113                                 associationConnect = true;
1114                             } else if (stencil.idWithoutNs() === 'DataAssociation' && curCan.getStencil().idWithoutNs() === 'DataStore') {
1115                                 associationConnect = true;
1116                             }
1117                             
1118                             if (targetStencil.canConnectTo || associationConnect) {
1119                                 canConnect = true;
1120                             }
1121                         }
1122                         
1123                         //Edge
1124                         $scope.dragCurrentParent = canvasCandidate;
1125                         $scope.dragCurrentParentId = canvasCandidate.id;
1126                         $scope.dragCurrentParentStencil = canvasCandidate.getStencil().id();
1127                         $scope.dragCanContain = canConnect;
1128                         
1129                         // Show Highlight
1130                         $scope.editor.handleEvents({
1131                             type: ORYX.CONFIG.EVENT_HIGHLIGHT_SHOW,
1132                             highlightId: 'shapeRepo.added',
1133                             elements: [canvasCandidate],
1134                             color: ORYX.CONFIG.SELECTION_VALID_COLOR
1135                         });
1136
1137                         $scope.editor.handleEvents({
1138                             type: ORYX.CONFIG.EVENT_HIGHLIGHT_HIDE,
1139                             highlightId: "shapeRepo.attached"
1140                         });
1141                     }
1142                 }
1143             }
1144         };
1145         
1146         $scope.dragCallbackQuickMenu = function (event, ui) {
1147             
1148             if ($scope.dragModeOver != false) {
1149                 var coord = $scope.editor.eventCoordinatesXY(event.pageX, event.pageY);
1150                 
1151                 var additionalIEZoom = 1;
1152                 if (!isNaN(screen.logicalXDPI) && !isNaN(screen.systemXDPI)) {
1153                     var ua = navigator.userAgent;
1154                     if (ua.indexOf('MSIE') >= 0) {
1155                         //IE 10 and below
1156                         var zoom = Math.round((screen.deviceXDPI / screen.logicalXDPI) * 100);
1157                         if (zoom !== 100) {
1158                             additionalIEZoom = zoom / 100
1159                         }
1160                     }
1161                 }
1162                 
1163                 if (additionalIEZoom !== 1) {
1164                      coord.x = coord.x / additionalIEZoom;
1165                      coord.y = coord.y / additionalIEZoom;
1166                 }
1167                 
1168                 var aShapes = $scope.editor.getCanvas().getAbstractShapesAtPosition(coord);
1169                
1170                 if (aShapes.length <= 0) {
1171                     if (event.helper) {
1172                         $scope.dragCanContain = false;
1173                         return false;
1174                     }
1175                 }
1176
1177                 if (aShapes[0] instanceof ORYX.Core.Canvas) {
1178                     $scope.editor.getCanvas().setHightlightStateBasedOnX(coord.x);
1179                 }
1180                 
1181                 var stencil = undefined;
1182                 var stencilSets = $scope.editor.getStencilSets().values();
1183                 for (var i = 0; i < stencilSets.length; i++)
1184                 {
1185                     var stencilSet = stencilSets[i];
1186                     var nodes = stencilSet.nodes();
1187                     for (var j = 0; j < nodes.length; j++)
1188                     {
1189                         if (nodes[j].idWithoutNs() === event.target.id)
1190                         {
1191                             stencil = nodes[j];
1192                             break;
1193                         }
1194                     }
1195                     
1196                     if (!stencil)
1197                     {
1198                         var edges = stencilSet.edges();
1199                         for (var j = 0; j < edges.length; j++)
1200                         {
1201                             if (edges[j].idWithoutNs() === event.target.id)
1202                             {
1203                                 stencil = edges[j];
1204                                 break;
1205                             }
1206                         }
1207                     }
1208                 }
1209                 
1210                 var candidate = aShapes.last();
1211                 
1212                 var isValid = false;
1213                 if (stencil.type() === "node") 
1214                 {
1215                     //check containment rules
1216                     var canContain = $scope.editor.getRules().canContain({containingShape:candidate, containedStencil:stencil});
1217                     
1218                     var parentCandidate = aShapes.reverse().find(function (candidate) {
1219                         return (candidate instanceof ORYX.Core.Canvas
1220                             || candidate instanceof ORYX.Core.Node
1221                             || candidate instanceof ORYX.Core.Edge);
1222                     });
1223
1224                     if (!parentCandidate) {
1225                         $scope.dragCanContain = false;
1226                         return false;
1227                     }
1228                     
1229                     $scope.dragCurrentParent = parentCandidate;
1230                     $scope.dragCurrentParentId = parentCandidate.id;
1231                     $scope.dragCurrentParentStencil = parentCandidate.getStencil().id();
1232                     $scope.dragCanContain = canContain;
1233                     $scope.dropTargetElement = parentCandidate;
1234                     isValid = canContain;
1235         
1236                 } else { //Edge
1237                 
1238                     var shapes = $scope.editor.getSelection();
1239                     if (shapes && shapes.length == 1)
1240                     {
1241                         var currentSelectedShape = shapes.first();
1242                         var curCan = candidate;
1243                         var canConnect = false;
1244                         
1245                         var targetStencil = $scope.getStencilItemById(curCan.getStencil().idWithoutNs());
1246                         if (targetStencil)
1247                         {
1248                             var associationConnect = false;
1249                             if (stencil.idWithoutNs() === 'Association' && (curCan.getStencil().idWithoutNs() === 'TextAnnotation' || curCan.getStencil().idWithoutNs() === 'BoundaryCompensationEvent'))  
1250                             {
1251                                 associationConnect = true;
1252                             }
1253                             else if (stencil.idWithoutNs() === 'DataAssociation' && curCan.getStencil().idWithoutNs() === 'DataStore')
1254                             {
1255                                 associationConnect = true;
1256                             }
1257                             
1258                             if (targetStencil.canConnectTo || associationConnect)
1259                             {
1260                                 while (!canConnect && curCan && !(curCan instanceof ORYX.Core.Canvas))
1261                                 {
1262                                     candidate = curCan;
1263                                     //check connection rules
1264                                     canConnect = $scope.editor.getRules().canConnect({
1265                                                             sourceShape: currentSelectedShape, 
1266                                                             edgeStencil: stencil, 
1267                                                             targetShape: curCan
1268                                                             });    
1269                                     curCan = curCan.parent;
1270                                 }
1271                             }
1272                         }
1273                         var parentCandidate = $scope.editor.getCanvas();
1274                         
1275                         isValid = canConnect;
1276                         $scope.dragCurrentParent = parentCandidate;
1277                         $scope.dragCurrentParentId = parentCandidate.id;
1278                         $scope.dragCurrentParentStencil = parentCandidate.getStencil().id();
1279                         $scope.dragCanContain = canConnect;
1280                         $scope.dropTargetElement = candidate;
1281                     }        
1282                     
1283                 }    
1284
1285                 $scope.editor.handleEvents({
1286                     type:        ORYX.CONFIG.EVENT_HIGHLIGHT_SHOW, 
1287                     highlightId:'shapeMenu',
1288                     elements:    [candidate],
1289                     color:        isValid ? ORYX.CONFIG.SELECTION_VALID_COLOR : ORYX.CONFIG.SELECTION_INVALID_COLOR
1290                 });
1291             }
1292         };
1293
1294     }]);
1295
1296 var KISBPM = KISBPM || {};
1297 //create command for undo/redo
1298 KISBPM.CreateCommand = ORYX.Core.Command.extend({
1299     construct: function(option, currentReference, position, facade){
1300         this.option = option;
1301         this.currentReference = currentReference;
1302         this.position = position;
1303         this.facade = facade;
1304         this.shape;
1305         this.edge;
1306         this.targetRefPos;
1307         this.sourceRefPos;
1308         /*
1309          * clone options parameters
1310          */
1311         this.connectedShape = option.connectedShape;
1312         this.connectingType = option.connectingType;
1313         this.namespace = option.namespace;
1314         this.type = option.type;
1315         this.containedStencil = option.containedStencil;
1316         this.parent = option.parent;
1317         this.currentReference = currentReference;
1318         this.shapeOptions = option.shapeOptions;
1319     },            
1320     execute: function(){
1321         
1322         if (this.shape) {
1323             if (this.shape instanceof ORYX.Core.Node) {
1324                 this.parent.add(this.shape);
1325                 if (this.edge) {
1326                     this.facade.getCanvas().add(this.edge);
1327                     this.edge.dockers.first().setDockedShape(this.connectedShape);
1328                     this.edge.dockers.first().setReferencePoint(this.sourceRefPos);
1329                     this.edge.dockers.last().setDockedShape(this.shape);
1330                     this.edge.dockers.last().setReferencePoint(this.targetRefPos);
1331                 }
1332                 
1333                 this.facade.setSelection([this.shape]);
1334                 
1335             } else if (this.shape instanceof ORYX.Core.Edge) {
1336                 this.facade.getCanvas().add(this.shape);
1337                 this.shape.dockers.first().setDockedShape(this.connectedShape);
1338                 this.shape.dockers.first().setReferencePoint(this.sourceRefPos);
1339             }
1340         }
1341         else {
1342             this.shape = this.facade.createShape(this.option);
1343             this.edge = (!(this.shape instanceof ORYX.Core.Edge)) ? this.shape.getIncomingShapes().first() : undefined;
1344         }
1345         
1346         if (this.currentReference && this.position) {
1347             
1348             if (this.shape instanceof ORYX.Core.Edge) {
1349             
1350                 if (!(this.currentReference instanceof ORYX.Core.Canvas)) {
1351                     this.shape.dockers.last().setDockedShape(this.currentReference);
1352                     
1353                     if (this.currentReference.getStencil().idWithoutNs() === 'TextAnnotation')
1354                     {
1355                         var midpoint = {};
1356                         midpoint.x = 0;
1357                         midpoint.y = this.currentReference.bounds.height() / 2;
1358                         this.shape.dockers.last().setReferencePoint(midpoint);
1359                     }
1360                     else
1361                     {
1362                         this.shape.dockers.last().setReferencePoint(this.currentReference.bounds.midPoint());
1363                     }
1364                 }
1365                 else {
1366                     this.shape.dockers.last().bounds.centerMoveTo(this.position);
1367                 }
1368                 this.sourceRefPos = this.shape.dockers.first().referencePoint;
1369                 this.targetRefPos = this.shape.dockers.last().referencePoint;
1370                 
1371             } else if (this.edge){
1372                 this.sourceRefPos = this.edge.dockers.first().referencePoint;
1373                 this.targetRefPos = this.edge.dockers.last().referencePoint;
1374             }
1375         } else {
1376             var containedStencil = this.containedStencil;
1377             var connectedShape = this.connectedShape;
1378             var bc = connectedShape.bounds;
1379             var bs = this.shape.bounds;
1380             
1381             var pos = bc.center();
1382             if(containedStencil.defaultAlign()==="north") {
1383                 pos.y -= (bc.height() / 2) + ORYX.CONFIG.SHAPEMENU_CREATE_OFFSET + (bs.height()/2);
1384             } else if(containedStencil.defaultAlign()==="northeast") {
1385                 pos.x += (bc.width() / 2) + ORYX.CONFIG.SHAPEMENU_CREATE_OFFSET_CORNER + (bs.width()/2);
1386                 pos.y -= (bc.height() / 2) + ORYX.CONFIG.SHAPEMENU_CREATE_OFFSET_CORNER + (bs.height()/2);
1387             } else if(containedStencil.defaultAlign()==="southeast") {
1388                 pos.x += (bc.width() / 2) + ORYX.CONFIG.SHAPEMENU_CREATE_OFFSET_CORNER + (bs.width()/2);
1389                 pos.y += (bc.height() / 2) + ORYX.CONFIG.SHAPEMENU_CREATE_OFFSET_CORNER + (bs.height()/2);
1390             } else if(containedStencil.defaultAlign()==="south") {
1391                 pos.y += (bc.height() / 2) + ORYX.CONFIG.SHAPEMENU_CREATE_OFFSET + (bs.height()/2);
1392             } else if(containedStencil.defaultAlign()==="southwest") {
1393                 pos.x -= (bc.width() / 2) + ORYX.CONFIG.SHAPEMENU_CREATE_OFFSET_CORNER + (bs.width()/2);
1394                 pos.y += (bc.height() / 2) + ORYX.CONFIG.SHAPEMENU_CREATE_OFFSET_CORNER + (bs.height()/2);
1395             } else if(containedStencil.defaultAlign()==="west") {
1396                 pos.x -= (bc.width() / 2) + ORYX.CONFIG.SHAPEMENU_CREATE_OFFSET + (bs.width()/2);
1397             } else if(containedStencil.defaultAlign()==="northwest") {
1398                 pos.x -= (bc.width() / 2) + ORYX.CONFIG.SHAPEMENU_CREATE_OFFSET_CORNER + (bs.width()/2);
1399                 pos.y -= (bc.height() / 2) + ORYX.CONFIG.SHAPEMENU_CREATE_OFFSET_CORNER + (bs.height()/2);
1400             } else {
1401                 pos.x += (bc.width() / 2) + ORYX.CONFIG.SHAPEMENU_CREATE_OFFSET + (bs.width()/2);
1402             }
1403             
1404             // Move shape to the new position
1405             this.shape.bounds.centerMoveTo(pos);
1406             
1407             // Move all dockers of a node to the position
1408             if (this.shape instanceof ORYX.Core.Node){
1409                 (this.shape.dockers||[]).each(function(docker){
1410                     docker.bounds.centerMoveTo(pos);
1411                 });
1412             }
1413             
1414             //this.shape.update();
1415             this.position = pos;
1416             
1417             if (this.edge){
1418                 this.sourceRefPos = this.edge.dockers.first().referencePoint;
1419                 this.targetRefPos = this.edge.dockers.last().referencePoint;
1420             }
1421         }
1422         
1423         this.facade.getCanvas().update();
1424         this.facade.updateSelection();
1425
1426     },
1427     rollback: function(){
1428         this.facade.deleteShape(this.shape);
1429         if(this.edge) {
1430             this.facade.deleteShape(this.edge);
1431         }
1432         //this.currentParent.update();
1433         this.facade.setSelection(this.facade.getSelection().without(this.shape, this.edge));
1434     }
1435 });