You can not select more than 25 topics
			Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
		
		
		
		
		
			
		
			
				
					
					
						
							1507 lines
						
					
					
						
							68 KiB
						
					
					
				
			
		
		
		
			
			
			
				
					
				
				
					
				
			
		
		
	
	
							1507 lines
						
					
					
						
							68 KiB
						
					
					
				| /* Licensed under the Apache License, Version 2.0 (the "License"); | |
|  * you may not use this file except in compliance with the License. | |
|  * You may obtain a copy of the License at | |
|  *  | |
|  *      http://www.apache.org/licenses/LICENSE-2.0 | |
|  *  | |
|  * Unless required by applicable law or agreed to in writing, software | |
|  * distributed under the License is distributed on an "AS IS" BASIS, | |
|  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | |
|  * See the License for the specific language governing permissions and | |
|  * limitations under the License. | |
|  */ | |
| 
 | |
| 'use strict'; | |
| 
 | |
| angular.module('flowableModeler') | |
|     .controller('StencilController', ['$rootScope', '$scope', '$http', '$modal', '$timeout', '$window', 'editorManager', | |
|                                       function ($rootScope, $scope, $http, $modal, $timeout, $window, editorManager) { | |
| 
 | |
|         // Property window toggle state | |
|         $scope.propertyWindowState = {'collapsed': false}; | |
| 
 | |
|         // Add reference to global header-config | |
|         $scope.headerConfig = FLOWABLE.HEADER_CONFIG; | |
| 
 | |
|         $scope.propertyWindowState.toggle = function () { | |
|             $scope.propertyWindowState.collapsed = !$scope.propertyWindowState.collapsed; | |
|             $timeout(function () { | |
|                 $window.dispatchEvent(new Event("resize")); | |
|             }, 100); | |
|         }; | |
| 
 | |
|         // Code that is dependent on an initialised Editor is wrapped in a promise for the editor | |
|         $scope.editorFactory.promise.then(function () { | |
|          | |
|         	/* Build stencil item list */ | |
| 
 | |
|             // Build simple json representation of stencil set | |
|             var stencilItemGroups = []; | |
| 
 | |
|             // Helper method: find a group in an array | |
|             var findGroup = function (name, groupArray) { | |
|                 for (var index = 0; index < groupArray.length; index++) { | |
|                     if (groupArray[index].name === name) { | |
|                         return groupArray[index]; | |
|                     } | |
|                 } | |
|                 return null; | |
|             }; | |
| 
 | |
|             // Helper method: add a new group to an array of groups | |
|             var addGroup = function (groupName, groupArray) { | |
|                 var group = { name: groupName, items: [], paletteItems: [], groups: [], visible: true }; | |
|                 groupArray.push(group); | |
|                 return group; | |
|             }; | |
| 
 | |
|             /* | |
|              StencilSet items | |
|              */ | |
|             var data = editorManager.getStencilData(); | |
| 
 | |
|             var quickMenuDefinition = undefined; | |
|             var ignoreForPaletteDefinition = undefined; | |
|              | |
|             if (data.namespace == 'http://b3mn.org/stencilset/cmmn1.1#') { | |
|                 quickMenuDefinition = ['HumanTask', 'Association']; | |
|                 ignoreForPaletteDefinition = ['CasePlanModel']; | |
|                  | |
|             } else if (data.namespace == 'http://b3mn.org/stencilset/dmn1.2#') { | |
|                 quickMenuDefinition = ['DecisionTableDecision', 'InformationRequirement', 'KnowledgeRequirement']; | |
|                 ignoreForPaletteDefinition = []; | |
|             } else { | |
|                 quickMenuDefinition = ['UserTask', 'EndNoneEvent', 'ExclusiveGateway',  | |
|                                          'CatchTimerEvent', 'ThrowNoneEvent', 'TextAnnotation', | |
|                                          'SequenceFlow', 'Association']; | |
|                                           | |
|                 ignoreForPaletteDefinition = ['SequenceFlow', 'MessageFlow', 'Association', 'DataAssociation', 'DataStore', 'SendTask']; | |
|             } | |
|               | |
|             var quickMenuItems = []; | |
|                | |
|             var morphRoles = []; | |
|             for (var i = 0; i < data.rules.morphingRules.length; i++)  | |
|             { | |
|                 var role = data.rules.morphingRules[i].role; | |
|                 var roleItem = {'role': role, 'morphOptions': []}; | |
|                 morphRoles.push(roleItem); | |
|             } | |
|            | |
|             // Check all received items | |
|             for (var stencilIndex = 0; stencilIndex < data.stencils.length; stencilIndex++) { | |
|              | |
|                 // Check if the root group is the 'diagram' group. If so, this item should not be shown. | |
|                 var currentGroupName = data.stencils[stencilIndex].groups[0]; | |
|                 if (currentGroupName === 'Diagram' || currentGroupName === 'BPMN.STENCILS.GROUPS.DIAGRAM' ||  | |
|                         currentGroupName === 'CMMN.STENCILS.GROUPS.DIAGRAM' || | |
|                         currentGroupName === 'DMN.STENCILS.GROUPS.DIAGRAM') { | |
|                          | |
|                     continue;  // go to next item | |
|                 } | |
|                  | |
|                 var removed = false; | |
|                 if (data.stencils[stencilIndex].removed) { | |
|                     removed = true; | |
|                 } | |
| 
 | |
|                 var currentGroup = undefined; | |
|                 if (!removed) { | |
|                     // Check if this group already exists. If not, we create a new one | |
|  | |
|                     if (currentGroupName !== null && currentGroupName !== undefined && currentGroupName.length > 0) { | |
| 
 | |
|                         currentGroup = findGroup(currentGroupName, stencilItemGroups); // Find group in root groups array | |
|                         if (currentGroup === null) { | |
|                             currentGroup = addGroup(currentGroupName, stencilItemGroups); | |
|                         } | |
| 
 | |
|                         // Add all child groups (if any) | |
|                         for (var groupIndex = 1; groupIndex < data.stencils[stencilIndex].groups.length; groupIndex++) { | |
|                             var childGroupName = data.stencils[stencilIndex].groups[groupIndex]; | |
|                             var childGroup = findGroup(childGroupName, currentGroup.groups); | |
|                             if (childGroup === null) { | |
|                                 childGroup = addGroup(childGroupName, currentGroup.groups); | |
|                             } | |
| 
 | |
|                             // The current group variable holds the parent of the next group (if any), | |
|                             // and is basically the last element in the array of groups defined in the stencil item | |
|                             currentGroup = childGroup; | |
| 
 | |
|                         } | |
| 
 | |
|                     } | |
|                 } | |
|                  | |
|                 // Construct the stencil item | |
|                 var stencilItem = {'id': data.stencils[stencilIndex].id, | |
|                     'name': data.stencils[stencilIndex].title, | |
|                     'description': data.stencils[stencilIndex].description, | |
|                     'icon': data.stencils[stencilIndex].icon, | |
|                     'type': data.stencils[stencilIndex].type, | |
|                     'roles': data.stencils[stencilIndex].roles, | |
|                     'removed': removed, | |
|                     'customIcon': false, | |
|                     'canConnect': false, | |
|                     'canConnectTo': false, | |
|                     'canConnectAssociation': false}; | |
|                  | |
|                 if (data.stencils[stencilIndex].customIconId && data.stencils[stencilIndex].customIconId > 0) { | |
|                     stencilItem.customIcon = true; | |
|                     stencilItem.icon = data.stencils[stencilIndex].customIconId; | |
|                 } | |
|                  | |
|                 if (!removed) { | |
|                     if (quickMenuDefinition.indexOf(stencilItem.id) >= 0) { | |
|                       quickMenuItems[quickMenuDefinition.indexOf(stencilItem.id)] = stencilItem; | |
|                     } | |
|                 } | |
|                  | |
|                 if (stencilItem.id === 'TextAnnotation' || stencilItem.id === 'BoundaryCompensationEvent') { | |
|                   stencilItem.canConnectAssociation = true; | |
|                 } | |
|                  | |
|                 for (var i = 0; i < data.stencils[stencilIndex].roles.length; i++) { | |
|                   var stencilRole = data.stencils[stencilIndex].roles[i]; | |
|                   if (data.namespace == 'http://b3mn.org/stencilset/cmmn1.1#') { | |
|                       if (stencilRole === 'association_start') { | |
|                         stencilItem.canConnect = true; | |
|                       } else if (stencilRole === 'association_end') { | |
|                         stencilItem.canConnectTo = true; | |
|                       } | |
|                   } else if (data.namespace == 'http://b3mn.org/stencilset/dmn1.2#') { | |
|                       if (stencilRole === 'information_requirement_start') { | |
|                           stencilItem.canConnect = true; | |
|                       } else if (stencilRole === 'information_requirement_end') { | |
|                           stencilItem.canConnectTo = true; | |
|                       } | |
|                   } else { | |
|                       if (stencilRole === 'sequence_start') { | |
|                         stencilItem.canConnect = true; | |
|                       } else if (stencilRole === 'sequence_end') { | |
|                         stencilItem.canConnectTo = true; | |
|                       } | |
|                   } | |
|                    | |
|                   for (var j = 0; j < morphRoles.length; j++) { | |
|                     if (stencilRole === morphRoles[j].role) { | |
|                       if (!removed) { | |
|                         morphRoles[j].morphOptions.push(stencilItem); | |
|                       } | |
|                       stencilItem.morphRole = morphRoles[j].role; | |
|                       break; | |
|                     } | |
|                   } | |
|                 } | |
| 
 | |
|                 if (currentGroup) { | |
|                   // Add the stencil item to the correct group | |
|                   currentGroup.items.push(stencilItem); | |
|                   if (ignoreForPaletteDefinition.indexOf(stencilItem.id) < 0) { | |
|                     currentGroup.paletteItems.push(stencilItem); | |
|                   } | |
| 
 | |
|                 } else { | |
|                     // It's a root stencil element | |
|                     if (!removed) { | |
|                         stencilItemGroups.push(stencilItem); | |
|                     } | |
|                 } | |
|             } | |
|              | |
|             for (var i = 0; i < stencilItemGroups.length; i++)  { | |
|               if (stencilItemGroups[i].paletteItems && stencilItemGroups[i].paletteItems.length == 0) { | |
|                 stencilItemGroups[i].visible = false; | |
|               } | |
|             } | |
|              | |
|             $scope.stencilItemGroups = stencilItemGroups; | |
| 
 | |
|             var containmentRules = []; | |
|             for (var i = 0; i < data.rules.containmentRules.length; i++) { | |
|                 var rule = data.rules.containmentRules[i]; | |
|                 containmentRules.push(rule); | |
|             } | |
|             $scope.containmentRules = containmentRules; | |
|              | |
|             // remove quick menu items which are not available anymore due to custom pallette | |
|             var availableQuickMenuItems = []; | |
|             for (var i = 0; i < quickMenuItems.length; i++) { | |
|                 if (quickMenuItems[i]) { | |
|                     availableQuickMenuItems[availableQuickMenuItems.length] = quickMenuItems[i]; | |
|                 } | |
|             } | |
|              | |
|             $scope.quickMenuItems = availableQuickMenuItems; | |
|             $scope.morphRoles = morphRoles; | |
| 
 | |
|             /* | |
|              * Listen to selection change events: show properties | |
|              */ | |
|             editorManager.registerOnEvent(ORYX.CONFIG.EVENT_SELECTION_CHANGED, function (event) { | |
|                 var shapes = event.elements; | |
|                 var canvasSelected = false; | |
|                 if (shapes && shapes.length == 0) { | |
|                     shapes = [editorManager.getCanvas()]; | |
|                     canvasSelected = true; | |
|                 } | |
|                 if (shapes && shapes.length > 0) { | |
| 
 | |
|                     var selectedShape = shapes.first(); | |
|                     var stencil = selectedShape.getStencil(); | |
|                      | |
|                     if ($rootScope.selectedElementBeforeScrolling && stencil.id().indexOf('BPMNDiagram') !== -1 && stencil.id().indexOf('CMMNDiagram') !== -1 && | |
|                         stencil.id().indexOf('DMNDiagram') !== -1) { | |
|                       // ignore canvas event because of empty selection when scrolling stops | |
|                       return; | |
|                     } | |
|                      | |
|                     if ($rootScope.selectedElementBeforeScrolling && $rootScope.selectedElementBeforeScrolling.getId() === selectedShape.getId()) { | |
|                       $rootScope.selectedElementBeforeScrolling = null; | |
|                       return; | |
|                     } | |
| 
 | |
|                     // Store previous selection | |
|                     $scope.previousSelectedShape = $scope.selectedShape; | |
|                      | |
|                     // Only do something if another element is selected (Oryx fires this event multiple times) | |
|                     if ($scope.selectedShape !== undefined && $scope.selectedShape.getId() === selectedShape.getId()) { | |
|                         if ($rootScope.forceSelectionRefresh) { | |
|                             // Switch the flag again, this run will force refresh | |
|                             $rootScope.forceSelectionRefresh = false; | |
|                         } else { | |
|                             // Selected the same element again, no need to update anything | |
|                             return; | |
|                         } | |
|                     } | |
| 
 | |
|                     var selectedItem = {'title': '', 'properties': []}; | |
| 
 | |
|                     if (canvasSelected) { | |
|                         selectedItem.auditData = { | |
|                             'author': $scope.modelData.createdByUser, | |
|                             'createDate': $scope.modelData.createDate | |
|                         }; | |
|                     } | |
| 
 | |
|                     // Gather properties of selected item | |
|                     var properties = stencil.properties(); | |
|                     for (var i = 0; i < properties.length; i++) { | |
|                         var property = properties[i]; | |
|                         if (property.popular() == false) continue; | |
|                         var key = property.prefix() + "-" + property.id(); | |
| 
 | |
|                         if (key === 'oryx-name') { | |
|                             selectedItem.title = selectedShape.properties.get(key); | |
|                         } | |
| 
 | |
|                         // First we check if there is a config for 'key-type' and then for 'type' alone | |
|                         var propertyConfig = FLOWABLE.PROPERTY_CONFIG[key + '-' + property.type()]; | |
|                         if (propertyConfig === undefined || propertyConfig === null) { | |
|                             propertyConfig = FLOWABLE.PROPERTY_CONFIG[property.type()]; | |
|                         } | |
| 
 | |
|                         if (propertyConfig === undefined || propertyConfig === null) { | |
|                             console.log('WARNING: no property configuration defined for ' + key + ' of type ' + property.type()); | |
|                         } else { | |
| 
 | |
|                             if (selectedShape.properties.get(key) === 'true') { | |
|                                 selectedShape.properties.set(key, true); | |
|                             } | |
|                              | |
|                             if (FLOWABLE.UI_CONFIG.showRemovedProperties == false && property.isHidden()) | |
|                             { | |
|                               continue; | |
|                             } | |
| 
 | |
|                             var currentProperty = { | |
|                                 'key': key, | |
|                                 'title': property.title(), | |
|                                 'description': property.description(), | |
|                                 'type': property.type(), | |
|                                 'mode': 'read', | |
|                                 'readonly': property.readonly(), | |
|                                 'hidden': property.isHidden(), | |
|                                 'value': selectedShape.properties.get(key) | |
|                             }; | |
|                              | |
|                             if ((currentProperty.type === 'complex' || currentProperty.type === 'multiplecomplex') && currentProperty.value && currentProperty.value.length > 0) { | |
|                                 try { | |
|                                     currentProperty.value = JSON.parse(currentProperty.value); | |
|                                 } catch (err) { | |
|                                     // ignore | |
|                                 } | |
|                             } | |
| 
 | |
|                             if (propertyConfig.readModeTemplateUrl !== undefined && propertyConfig.readModeTemplateUrl !== null) { | |
|                                 currentProperty.readModeTemplateUrl = propertyConfig.readModeTemplateUrl + '?version=' + $rootScope.staticIncludeVersion; | |
|                             } | |
|                             if (propertyConfig.writeModeTemplateUrl !== null && propertyConfig.writeModeTemplateUrl !== null) { | |
|                               currentProperty.writeModeTemplateUrl = propertyConfig.writeModeTemplateUrl + '?version=' + $rootScope.staticIncludeVersion; | |
|                             } | |
| 
 | |
|                             if ((currentProperty.readonly && propertyConfig.templateUrl !== undefined && propertyConfig.templateUrl !== null) || | |
|                                 (currentProperty.readonly === undefined && propertyConfig.templateUrl !== undefined && propertyConfig.templateUrl !== null)) { | |
|                                 currentProperty.templateUrl = propertyConfig.templateUrl + '?version=' + $rootScope.staticIncludeVersion; | |
|                                 currentProperty.hasReadWriteMode = false; | |
|                             } | |
|                             else { | |
|                                 currentProperty.hasReadWriteMode = true; | |
|                             } | |
| 
 | |
|                             if (currentProperty.value === undefined | |
|                                 || currentProperty.value === null | |
|                                 || currentProperty.value.length == 0) { | |
|                                 currentProperty.noValue = true; | |
|                             } | |
| 
 | |
|                             selectedItem.properties.push(currentProperty); | |
|                         } | |
|                     } | |
| 
 | |
|                     // Need to wrap this in an $apply block, see http://jimhoskins.com/2012/12/17/angularjs-and-apply.html | |
|                     $scope.safeApply(function () { | |
|                         $scope.selectedItem = selectedItem; | |
|                         $scope.selectedShape = selectedShape; | |
|                     }); | |
| 
 | |
|                 } else { | |
|                     $scope.safeApply(function () { | |
|                         $scope.selectedItem = {}; | |
|                         $scope.selectedShape = null; | |
|                     }); | |
|                 } | |
|             }); | |
|              | |
|             editorManager.registerOnEvent(ORYX.CONFIG.EVENT_SELECTION_CHANGED, function (event) { | |
|                | |
|               FLOWABLE.eventBus.dispatch(FLOWABLE.eventBus.EVENT_TYPE_HIDE_SHAPE_BUTTONS); | |
|               var shapes = event.elements; | |
|                  | |
|               if (shapes && shapes.length == 1) { | |
| 
 | |
|               	  var selectedShape = shapes.first(); | |
|                | |
|                   var a = editorManager.getCanvas().node.getScreenCTM(); | |
|                | |
| 	              var absoluteXY = selectedShape.absoluteXY(); | |
| 	               | |
| 	              absoluteXY.x *= a.a; | |
| 	              absoluteXY.y *= a.d; | |
| 	               | |
| 	              var additionalIEZoom = 1; | |
| 	              if (!isNaN(screen.logicalXDPI) && !isNaN(screen.systemXDPI)) { | |
|                         var ua = navigator.userAgent; | |
|                         if (ua.indexOf('MSIE') >= 0) { | |
|                             //IE 10 and below | |
|                             var zoom = Math.round((screen.deviceXDPI / screen.logicalXDPI) * 100); | |
|                             if (zoom !== 100) { | |
|                                 additionalIEZoom = zoom / 100 | |
|                             } | |
|                         } | |
|                   } | |
| 	                     | |
| 	              if (additionalIEZoom === 1) { | |
| 	                   absoluteXY.y = absoluteXY.y - jQuery("#canvasSection").offset().top + 5; | |
| 	                         absoluteXY.x = absoluteXY.x - jQuery("#canvasSection").offset().left; | |
| 	               | |
| 	              } else { | |
| 	                   var canvasOffsetLeft = jQuery("#canvasSection").offset().left; | |
| 	                   var canvasScrollLeft = jQuery("#canvasSection").scrollLeft(); | |
| 	                   var canvasScrollTop = jQuery("#canvasSection").scrollTop(); | |
| 	                    | |
| 	                   var offset = a.e - (canvasOffsetLeft * additionalIEZoom); | |
| 	                   var additionaloffset = 0; | |
| 	                   if (offset > 10) { | |
| 	                       additionaloffset = (offset / additionalIEZoom) - offset; | |
| 	                   } | |
| 	                   absoluteXY.y = absoluteXY.y - (jQuery("#canvasSection").offset().top * additionalIEZoom) + 5 + ((canvasScrollTop * additionalIEZoom) - canvasScrollTop); | |
| 	                         absoluteXY.x = absoluteXY.x - (canvasOffsetLeft * additionalIEZoom) + additionaloffset + ((canvasScrollLeft * additionalIEZoom) - canvasScrollLeft); | |
|                   } | |
| 	               | |
| 	              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()); | |
| 	              var shapeXY = bounds.upperLeft(); | |
| 	               | |
| 	              var stencilItem = $scope.getStencilItemById(selectedShape.getStencil().idWithoutNs()); | |
| 	              var morphShapes = []; | |
| 	              if (stencilItem && stencilItem.morphRole) { | |
| 	                  for (var i = 0; i < $scope.morphRoles.length; i++) { | |
| 						  if ($scope.morphRoles[i].role === stencilItem.morphRole) { | |
| 	                    	  morphShapes = $scope.morphRoles[i].morphOptions; | |
| 	                      } | |
| 	                  } | |
| 	              } | |
|                | |
|               	  var x = shapeXY.x; | |
|             	  if (bounds.width() < 48) { | |
|               		  x -= 24; | |
|             	  } | |
|                | |
|               	  if (morphShapes && morphShapes.length > 0) { | |
|                 	// In case the element is not wide enough, start the 2 bottom-buttons more to the left | |
|                 	// to prevent overflow in the right-menu | |
|                  | |
|                 	var morphButton = document.getElementById('morph-button'); | |
|                 	morphButton.style.display = "block"; | |
|                 	morphButton.style.left = x + 24 +'px'; | |
|                 	morphButton.style.top = (shapeXY.y+bounds.height() + 2) + 'px'; | |
|               	  } | |
|                | |
|               	  var deleteButton = document.getElementById('delete-button'); | |
|               	  deleteButton.style.display = "block"; | |
|               	  deleteButton.style.left = x + 'px'; | |
|               	  deleteButton.style.top = (shapeXY.y+bounds.height() + 2) + 'px'; | |
|               	   | |
|               	  var editable = selectedShape._stencil._jsonStencil.id.endsWith('CollapsedSubProcess') ; | |
| 				  var editButton = document.getElementById('edit-button'); | |
| 				  if (editable) { | |
| 				      editButton.style.display = "block"; | |
|  					  if (morphShapes && morphShapes.length > 0) { | |
| 						editButton.style.left = x + 24 + 24 + 'px'; | |
| 				      } else { | |
| 					 	editButton.style.left = x + 24 +'px'; | |
| 					  } | |
| 					  editButton.style.top = (shapeXY.y+bounds.height() + 2) + 'px'; | |
| 					   | |
| 				  } else { | |
| 					  editButton.style.display = "none"; | |
| 				  } | |
|                | |
|               	  if (stencilItem && (stencilItem.canConnect || stencilItem.canConnectAssociation)) { | |
| 	                var quickButtonCounter = 0; | |
| 	                var quickButtonX = shapeXY.x+bounds.width() + 5; | |
| 	                var quickButtonY = shapeXY.y; | |
| 	                jQuery('.Oryx_button').each(function(i, obj) { | |
| 	                  if (obj.id !== 'morph-button' && obj.id != 'delete-button' && obj.id !== 'edit-button') { | |
| 	                    quickButtonCounter++; | |
| 	                    if (quickButtonCounter > 3) { | |
| 	                      quickButtonX = shapeXY.x+bounds.width() + 5; | |
| 	                      quickButtonY += 24; | |
| 	                      quickButtonCounter = 1; | |
| 	                       | |
| 	                    } else if (quickButtonCounter > 1) { | |
| 	                      quickButtonX += 24; | |
| 	                    } | |
| 	                     | |
| 	                    obj.style.display = "block"; | |
| 	                    obj.style.left = quickButtonX + 'px'; | |
| 	                    obj.style.top = quickButtonY + 'px'; | |
| 	                  } | |
| 	                }); | |
|                   } | |
|                } | |
|             }); | |
|              | |
|             if (!$rootScope.stencilInitialized) { | |
|               FLOWABLE.eventBus.addListener(FLOWABLE.eventBus.EVENT_TYPE_HIDE_SHAPE_BUTTONS, function (event) { | |
|                 jQuery('.Oryx_button').each(function(i, obj) { | |
|                   obj.style.display = "none"; | |
|               }); | |
|               }); | |
| 
 | |
|               /* | |
|                * Listen to property updates and act upon them | |
|                */ | |
|               FLOWABLE.eventBus.addListener(FLOWABLE.eventBus.EVENT_TYPE_PROPERTY_VALUE_CHANGED, function (event) { | |
|                   if (event.property && event.property.key) { | |
|                       // If the name property is been updated, we also need to change the title of the currently selected item | |
|                       if (event.property.key === 'oryx-name' && $scope.selectedItem !== undefined && $scope.selectedItem !== null) { | |
|                           $scope.selectedItem.title = event.newValue; | |
|                       } | |
| 
 | |
|                       // Update "no value" flag | |
|                       event.property.noValue = (event.property.value === undefined | |
|                           || event.property.value === null | |
|                           || event.property.value.length == 0); | |
|                   } | |
|               }); | |
|                | |
|               FLOWABLE.eventBus.addListener(FLOWABLE.eventBus.EVENT_TYPE_SHOW_VALIDATION_POPUP, function (event) { | |
|                   // Method to open validation dialog | |
|                   var showValidationDialog = function() { | |
|                       $rootScope.currentValidationId = event.validationId; | |
|                       $rootScope.isOnProcessLevel = event.onProcessLevel; | |
| 
 | |
|                       _internalCreateModal({template: 'editor-app/popups/validation-errors.html?version=' + Date.now()},  $modal, $rootScope); | |
|                   }; | |
| 
 | |
|                   showValidationDialog(); | |
|               }); | |
|                | |
|               FLOWABLE.eventBus.addListener(FLOWABLE.eventBus.EVENT_TYPE_NAVIGATE_TO_PROCESS, function (event) { | |
|                   var modelMetaData = editorManager.getBaseModelData(); | |
|                   $rootScope.editorHistory.push({ | |
|                         id: modelMetaData.modelId,  | |
|                         name: modelMetaData.name,  | |
|                         type: 'bpmnmodel' | |
|                   }); | |
|                    | |
|               	  $window.location.href = "../editor/#/editor/" + event.processId; | |
|               }); | |
|                | |
|               $rootScope.stencilInitialized = true; | |
|             } | |
|              | |
|             $scope.morphShape = function() { | |
|               $scope.safeApply(function () { | |
|                  | |
|                 var shapes = editorManager.getSelection(); | |
|                 if (shapes && shapes.length == 1) { | |
|                   $rootScope.currentSelectedShape = shapes.first(); | |
|                   var stencilItem = $scope.getStencilItemById($rootScope.currentSelectedShape.getStencil().idWithoutNs()); | |
|                   var morphShapes = []; | |
|                   for (var i = 0; i < $scope.morphRoles.length; i++) { | |
|                     if ($scope.morphRoles[i].role === stencilItem.morphRole) { | |
|                     	morphShapes = $scope.morphRoles[i].morphOptions.slice(); | |
|                     } | |
|                   } | |
| 
 | |
|                   // Method to open shape select dialog (used later on) | |
|                         var showSelectShapeDialog = function() | |
|                         { | |
|                             $rootScope.morphShapes = morphShapes; | |
|                             _internalCreateModal({ | |
|                                 backdrop: false, | |
|                                 keyboard: true, | |
|                                 template: 'editor-app/popups/select-shape.html?version=' + Date.now() | |
|                             },  $modal, $rootScope); | |
|                         }; | |
| 
 | |
|                         showSelectShapeDialog(); | |
|                 } | |
|               }); | |
|             }; | |
|              | |
|             $scope.deleteShape = function() { | |
|               FLOWABLE.TOOLBAR.ACTIONS.deleteItem({'$scope': $scope, 'editorManager': editorManager}); | |
|             }; | |
|              | |
|             $scope.quickAddItem = function(newItemId) { | |
|               $scope.safeApply(function () { | |
|                  | |
|                 var shapes = editorManager.getSelection(); | |
|                 if (shapes && shapes.length == 1) { | |
|                   $rootScope.currentSelectedShape = shapes.first(); | |
|                    | |
|                   var containedStencil = undefined; | |
|                   var stencilSets = editorManager.getStencilSets().values(); | |
|                   for (var i = 0; i < stencilSets.length; i++) { | |
|                     var stencilSet = stencilSets[i]; | |
|                   	var nodes = stencilSet.nodes(); | |
|                   	for (var j = 0; j < nodes.length; j++) { | |
|                     	if (nodes[j].idWithoutNs() === newItemId) { | |
|                       	    containedStencil = nodes[j]; | |
|                       		break; | |
|                     	} | |
|                     } | |
|                   } | |
|                    | |
|                   if (!containedStencil) return; | |
|                    | |
|                   var option = {type: $scope.currentSelectedShape.getStencil().namespace() + newItemId, namespace: $scope.currentSelectedShape.getStencil().namespace()}; | |
|                   option['connectedShape'] = $rootScope.currentSelectedShape; | |
|                   option['parent'] = $rootScope.currentSelectedShape.parent; | |
|                   option['containedStencil'] = containedStencil; | |
|                  | |
|                   var args = { sourceShape: $rootScope.currentSelectedShape, targetStencil: containedStencil }; | |
|                   var targetStencil = editorManager.getRules().connectMorph(args); | |
|                    | |
|                   // Check if there can be a target shape | |
|                   if (!targetStencil) {  | |
|                   	return;  | |
|                   } | |
|                    | |
|                   option['connectingType'] = targetStencil.id(); | |
| 
 | |
|                   var command = new FLOWABLE.CreateCommand(option, undefined, undefined, editorManager.getEditor()); | |
|                  | |
|                   editorManager.executeCommands([command]); | |
|                 } | |
|               }); | |
|             }; | |
|              | |
|             $scope.editShape = function(){ | |
| 				editorManager.edit($scope.selectedShape.resourceId); | |
| 			}; | |
| 
 | |
|         }); // end of $scope.editorFactory.promise block | |
|  | |
|         /* Click handler for clicking a property */ | |
|         $scope.propertyClicked = function (index) { | |
|             if (!$scope.selectedItem.properties[index].hidden) { | |
|                 $scope.selectedItem.properties[index].mode = "write"; | |
|             } | |
|         }; | |
| 
 | |
|         /* Helper method to retrieve the template url for a property */ | |
|         $scope.getPropertyTemplateUrl = function (index) { | |
|             return $scope.selectedItem.properties[index].templateUrl; | |
|         }; | |
|         $scope.getPropertyReadModeTemplateUrl = function (index) { | |
|             return $scope.selectedItem.properties[index].readModeTemplateUrl; | |
|         }; | |
|         $scope.getPropertyWriteModeTemplateUrl = function (index) { | |
|             return $scope.selectedItem.properties[index].writeModeTemplateUrl; | |
|         }; | |
| 
 | |
|         /* Method available to all sub controllers (for property controllers) to update the internal Oryx model */ | |
|         $scope.updatePropertyInModel = function (property, shapeId) { | |
| 
 | |
|             var shape = $scope.selectedShape; | |
|             // Some updates may happen when selected shape is already changed, so when an additional | |
|             // shapeId is supplied, we need to make sure the correct shape is updated (current or previous) | |
|             if (shapeId) { | |
|                 if (shape.id != shapeId && $scope.previousSelectedShape && $scope.previousSelectedShape.id == shapeId) { | |
|                     shape = $scope.previousSelectedShape; | |
|                 } else { | |
|                     shape = null; | |
|                 } | |
|             } | |
| 
 | |
|             if (!shape) { | |
|                 // When no shape is selected, or no shape is found for the alternative | |
|                 // shape ID, do nothing | |
|                 return; | |
|             } | |
|             var key = property.key; | |
|             var newValue = property.value; | |
|             var oldValue = shape.properties.get(key); | |
| 
 | |
|             if (newValue != oldValue) { | |
|                 var commandClass = ORYX.Core.Command.extend({ | |
|                     construct: function () { | |
|                         this.key = key; | |
|                         this.oldValue = oldValue; | |
|                         this.newValue = newValue; | |
|                         this.shape = shape; | |
|                         this.facade = editorManager.getEditor(); | |
|                     }, | |
|                     execute: function () { | |
|                         this.shape.setProperty(this.key, this.newValue); | |
|                         this.facade.getCanvas().update(); | |
|                         this.facade.updateSelection(); | |
|                     }, | |
|                     rollback: function () { | |
|                         this.shape.setProperty(this.key, this.oldValue); | |
|                         this.facade.getCanvas().update(); | |
|                         this.facade.updateSelection(); | |
|                     } | |
|                 }); | |
|                 // Instantiate the class | |
|                 var command = new commandClass(); | |
| 
 | |
|                 // Execute the command | |
|                 editorManager.executeCommands([command]); | |
|                 editorManager.handleEvents({ | |
|                     type: ORYX.CONFIG.EVENT_PROPWINDOW_PROP_CHANGED, | |
|                     elements: [shape], | |
|                     key: key | |
|                 }); | |
| 
 | |
|                 // Switch the property back to read mode, now the update is done | |
|                 property.mode = 'read'; | |
| 
 | |
|                 // Fire event to all who is interested | |
|                 // Fire event to all who want to know about this | |
|                 var event = { | |
|                     type: FLOWABLE.eventBus.EVENT_TYPE_PROPERTY_VALUE_CHANGED, | |
|                     property: property, | |
|                     oldValue: oldValue, | |
|                     newValue: newValue | |
|                 }; | |
|                 FLOWABLE.eventBus.dispatch(event.type, event); | |
|             } else { | |
|                 // Switch the property back to read mode, no update was needed | |
|                 property.mode = 'read'; | |
|             } | |
| 
 | |
|         }; | |
| 
 | |
|         /** | |
|          * Helper method that searches a group for an item with the given id. | |
|          * If not found, will return undefined. | |
|          */ | |
|         $scope.findStencilItemInGroup = function (stencilItemId, group) { | |
| 
 | |
|             var item; | |
| 
 | |
|             // Check all items directly in this group | |
|             for (var j = 0; j < group.items.length; j++) { | |
|                 item = group.items[j]; | |
|                 if (item.id === stencilItemId) { | |
|                     return item; | |
|                 } | |
|             } | |
| 
 | |
|             // Check the child groups | |
|             if (group.groups && group.groups.length > 0) { | |
|                 for (var k = 0; k < group.groups.length; k++) { | |
|                     item = $scope.findStencilItemInGroup(stencilItemId, group.groups[k]); | |
|                     if (item) { | |
|                         return item; | |
|                     } | |
|                 } | |
|             } | |
| 
 | |
|             return undefined; | |
|         }; | |
| 
 | |
|         /** | |
|          * Helper method to find a stencil item. | |
|          */ | |
|         $scope.getStencilItemById = function (stencilItemId) { | |
|             for (var i = 0; i < $scope.stencilItemGroups.length; i++) { | |
|                 var element = $scope.stencilItemGroups[i]; | |
| 
 | |
|                 // Real group | |
|                 if (element.items !== null && element.items !== undefined) { | |
|                     var item = $scope.findStencilItemInGroup(stencilItemId, element); | |
|                     if (item) { | |
|                         return item; | |
|                     } | |
|                 } else { // Root stencil item | |
|                     if (element.id === stencilItemId) { | |
|                         return element; | |
|                     } | |
|                 } | |
|             } | |
|             return undefined; | |
|         }; | |
| 
 | |
|         /* | |
|          * DRAG AND DROP FUNCTIONALITY | |
|          */ | |
| 
 | |
|         $scope.dropCallback = function (event, ui) { | |
|            | |
|             editorManager.handleEvents({ | |
|                 type: ORYX.CONFIG.EVENT_HIGHLIGHT_HIDE, | |
|                 highlightId: "shapeRepo.attached" | |
|             }); | |
|             editorManager.handleEvents({ | |
|                 type: ORYX.CONFIG.EVENT_HIGHLIGHT_HIDE, | |
|                 highlightId: "shapeRepo.added" | |
|             }); | |
|              | |
|             editorManager.handleEvents({ | |
|                 type: ORYX.CONFIG.EVENT_HIGHLIGHT_HIDE, | |
|                 highlightId: "shapeMenu" | |
|             }); | |
|              | |
|             FLOWABLE.eventBus.dispatch(FLOWABLE.eventBus.EVENT_TYPE_HIDE_SHAPE_BUTTONS); | |
| 
 | |
|             if ($scope.dragCanContain) { | |
| 
 | |
|               var item = $scope.getStencilItemById(ui.draggable[0].id); | |
|                | |
|               var pos = {x: event.pageX, y: event.pageY}; | |
|                | |
|               var additionalIEZoom = 1; | |
|               if (!isNaN(screen.logicalXDPI) && !isNaN(screen.systemXDPI)) { | |
|                 var ua = navigator.userAgent; | |
|                 if (ua.indexOf('MSIE') >= 0) { | |
|                   //IE 10 and below | |
|                   var zoom = Math.round((screen.deviceXDPI / screen.logicalXDPI) * 100); | |
|                   if (zoom !== 100) { | |
|                     additionalIEZoom = zoom / 100; | |
|                   } | |
|                 } | |
|               } | |
|                | |
|               var screenCTM = editorManager.getCanvas().node.getScreenCTM(); | |
| 
 | |
|               // Correcting the UpperLeft-Offset | |
|               pos.x -= (screenCTM.e / additionalIEZoom); | |
|               pos.y -= (screenCTM.f / additionalIEZoom); | |
|               // Correcting the Zoom-Factor | |
|               pos.x /= screenCTM.a; | |
|               pos.y /= screenCTM.d; | |
|                  | |
|               // Correcting the ScrollOffset | |
|               pos.x -= document.documentElement.scrollLeft; | |
|               pos.y -= document.documentElement.scrollTop; | |
|                  | |
|               var parentAbs = $scope.dragCurrentParent.absoluteXY(); | |
|               pos.x -= parentAbs.x; | |
|               pos.y -= parentAbs.y; | |
| 
 | |
|               var containedStencil = undefined; | |
|               var stencilSets = editorManager.getStencilSets().values(); | |
|               for (var i = 0; i < stencilSets.length; i++) { | |
|                 var stencilSet = stencilSets[i]; | |
|                 var nodes = stencilSet.nodes(); | |
|                 for (var j = 0; j < nodes.length; j++) { | |
|                   if (nodes[j].idWithoutNs() === ui.draggable[0].id) { | |
|                     containedStencil = nodes[j]; | |
|                     break; | |
|                   } | |
|                 } | |
| 
 | |
|                 if (!containedStencil) { | |
|                   var edges = stencilSet.edges(); | |
|                   for (var j = 0; j < edges.length; j++) { | |
|                   if (edges[j].idWithoutNs() === ui.draggable[0].id) { | |
|                     containedStencil = edges[j]; | |
|                     break; | |
|                   } | |
|                 } | |
|               } | |
|             } | |
| 
 | |
|             if (!containedStencil) return; | |
| 
 | |
|             if ($scope.quickMenu) { | |
|               var shapes = editorManager.getSelection(); | |
|               if (shapes && shapes.length == 1) { | |
|                 var currentSelectedShape = shapes.first(); | |
| 
 | |
|                 var option = {}; | |
|                 option.type = currentSelectedShape.getStencil().namespace() + ui.draggable[0].id; | |
|                 option.namespace = currentSelectedShape.getStencil().namespace(); | |
|                 option.connectedShape = currentSelectedShape; | |
|                 option.parent = $scope.dragCurrentParent; | |
|                 option.containedStencil = containedStencil; | |
| 	                 | |
|                 // If the ctrl key is not pressed,  | |
|                 // snapp the new shape to the center  | |
|                 // if it is near to the center of the other shape | |
|                 if (!event.ctrlKey) { | |
|                   // Get the center of the shape | |
|                   var cShape = currentSelectedShape.bounds.center(); | |
|                   // Snapp +-20 Pixel horizontal to the center  | |
|                   if (20 > Math.abs(cShape.x - pos.x)) { | |
|                     pos.x = cShape.x; | |
|                   } | |
|                   // Snapp +-20 Pixel vertical to the center  | |
|                   if (20 > Math.abs(cShape.y - pos.y)) { | |
|                     pos.y = cShape.y; | |
|                   } | |
|                 } | |
| 	                 | |
|                 option.position = pos; | |
| 	               | |
|                 if (containedStencil.idWithoutNs() !== 'SequenceFlow' && containedStencil.idWithoutNs() !== 'Association' &&  | |
|                         containedStencil.idWithoutNs() !== 'MessageFlow' && containedStencil.idWithoutNs() !== 'DataAssociation' && | |
|                         containedStencil.idWithoutNs() !== 'InformationRequirement' && containedStencil.idWithoutNs() !== 'KnowledgeRequirement') { | |
| 	                         | |
|                   var args = { sourceShape: currentSelectedShape, targetStencil: containedStencil }; | |
|                   var targetStencil = editorManager.getRules().connectMorph(args); | |
|                   if (!targetStencil) { // Check if there can be a target shape | |
|                   	return;  | |
|                   } | |
|                   option.connectingType = targetStencil.id(); | |
|                 } | |
| 
 | |
|                 var command = new FLOWABLE.CreateCommand(option, $scope.dropTargetElement, pos, editorManager.getEditor()); | |
| 	               | |
|                 editorManager.executeCommands([command]); | |
|               } | |
| 	                 | |
|             } else { | |
|               var canAttach = false; | |
|               if (containedStencil.idWithoutNs() === 'BoundaryErrorEvent' || containedStencil.idWithoutNs() === 'BoundaryTimerEvent' || | |
|                     containedStencil.idWithoutNs() === 'BoundarySignalEvent' || containedStencil.idWithoutNs() === 'BoundaryMessageEvent' || | |
|                     containedStencil.idWithoutNs() === 'BoundaryCancelEvent' || containedStencil.idWithoutNs() === 'BoundaryCompensationEvent') { | |
|                      | |
|                 // Modify position, otherwise boundary event will get position related to left corner of the canvas instead of the container | |
|                 pos = editorManager.eventCoordinates( event ); | |
|                 canAttach = true; | |
|               } | |
| 
 | |
|               var option = {}; | |
|               option['type'] = $scope.modelData.model.stencilset.namespace + item.id; | |
|               option['namespace'] = $scope.modelData.model.stencilset.namespace; | |
|               option['position'] = pos; | |
|               option['parent'] = $scope.dragCurrentParent; | |
| 
 | |
|               var commandClass = ORYX.Core.Command.extend({ | |
|                     construct: function(option, dockedShape, canAttach, position, facade){ | |
|                         this.option = option; | |
|                         this.docker = null; | |
|                         this.dockedShape = dockedShape; | |
|                         this.dockedShapeParent = dockedShape.parent || facade.getCanvas(); | |
|                         this.position = position; | |
|                         this.facade = facade; | |
|                         this.selection = this.facade.getSelection(); | |
|                         this.shape = null; | |
|                         this.parent = null; | |
|                         this.canAttach = canAttach; | |
|                     }, | |
|                     execute: function(){ | |
|                         if (!this.shape) { | |
|                             this.shape = this.facade.createShape(option); | |
|                             this.parent = this.shape.parent; | |
|                         } else if (this.parent) { | |
|                             this.parent.add(this.shape); | |
|                         } | |
| 
 | |
|                         if (this.canAttach && this.shape.dockers && this.shape.dockers.length) { | |
|                             this.docker = this.shape.dockers[0]; | |
| 
 | |
|                             this.dockedShapeParent.add(this.docker.parent); | |
| 
 | |
|                             // Set the Docker to the new Shape | |
|                             this.docker.setDockedShape(undefined); | |
|                             this.docker.bounds.centerMoveTo(this.position); | |
|                             if (this.dockedShape !== this.facade.getCanvas()) { | |
|                                 this.docker.setDockedShape(this.dockedShape); | |
|                             } | |
|                             this.facade.setSelection( [this.docker.parent] ); | |
|                         } | |
| 
 | |
|                         this.facade.getCanvas().update(); | |
|                         this.facade.updateSelection(); | |
| 
 | |
|                     }, | |
|                     rollback: function(){ | |
|                         if (this.shape) { | |
|                             this.facade.setSelection(this.selection.without(this.shape)); | |
|                             this.facade.deleteShape(this.shape); | |
|                         } | |
|                         if (this.canAttach && this.docker) { | |
|                             this.docker.setDockedShape(undefined); | |
|                         } | |
|                         this.facade.getCanvas().update(); | |
|                         this.facade.updateSelection(); | |
| 
 | |
|                     } | |
|               }); | |
| 
 | |
|               // Update canvas | |
|               var command = new commandClass(option, $scope.dragCurrentParent, canAttach, pos, editorManager.getEditor()); | |
|               editorManager.executeCommands([command]); | |
| 
 | |
|               // Fire event to all who want to know about this | |
|               var dropEvent = { | |
|                   type: FLOWABLE.eventBus.EVENT_TYPE_ITEM_DROPPED, | |
|                   droppedItem: item, | |
|                   position: pos | |
|               }; | |
|               FLOWABLE.eventBus.dispatch(dropEvent.type, dropEvent); | |
|             } | |
|           } | |
| 
 | |
|           $scope.dragCurrentParent = undefined; | |
|           $scope.dragCurrentParentId = undefined; | |
|           $scope.dragCurrentParentStencil = undefined; | |
|           $scope.dragCanContain = undefined; | |
|           $scope.quickMenu = undefined; | |
|           $scope.dropTargetElement = undefined; | |
|         }; | |
| 
 | |
| 
 | |
|         $scope.overCallback = function (event, ui) { | |
|             $scope.dragModeOver = true; | |
|         }; | |
| 
 | |
|         $scope.outCallback = function (event, ui) { | |
|             $scope.dragModeOver = false; | |
|         }; | |
| 
 | |
|         $scope.startDragCallback = function (event, ui) { | |
|             $scope.dragModeOver = false; | |
|             $scope.quickMenu = false; | |
|             if (!ui.helper.hasClass('stencil-item-dragged')) { | |
|                 ui.helper.addClass('stencil-item-dragged'); | |
|             } | |
|         }; | |
|          | |
|         $scope.startDragCallbackQuickMenu = function (event, ui) { | |
|             $scope.dragModeOver = false; | |
|             $scope.quickMenu = true; | |
|         }; | |
|          | |
|         $scope.dragCallback = function (event, ui) { | |
|            | |
|             if ($scope.dragModeOver != false) { | |
|                | |
|                 var coord = editorManager.eventCoordinatesXY(event.pageX, event.pageY); | |
|                  | |
|                 var additionalIEZoom = 1; | |
|                 if (!isNaN(screen.logicalXDPI) && !isNaN(screen.systemXDPI)) { | |
|                     var ua = navigator.userAgent; | |
|                     if (ua.indexOf('MSIE') >= 0) { | |
|                         //IE 10 and below | |
|                         var zoom = Math.round((screen.deviceXDPI / screen.logicalXDPI) * 100); | |
|                         if (zoom !== 100) { | |
|                             additionalIEZoom = zoom / 100 | |
|                         } | |
|                     } | |
|                 } | |
|                  | |
|                 if (additionalIEZoom !== 1) { | |
|                      coord.x = coord.x / additionalIEZoom; | |
|                      coord.y = coord.y / additionalIEZoom; | |
|                 } | |
|                  | |
|                 var aShapes = editorManager.getCanvas().getAbstractShapesAtPosition(coord); | |
|                  | |
|                 if (aShapes.length <= 0) { | |
|                     if (event.helper) { | |
|                         $scope.dragCanContain = false; | |
|                         return false; | |
|                     } | |
|                 } | |
| 
 | |
|                 if (aShapes[0] instanceof ORYX.Core.Canvas) { | |
|                     editorManager.getCanvas().setHightlightStateBasedOnX(coord.x); | |
|                 } | |
| 
 | |
|                 if (aShapes.length == 1 && aShapes[0] instanceof ORYX.Core.Canvas) { | |
|                   	var item = $scope.getStencilItemById(event.target.id); | |
|                     var parentCandidate = aShapes[0]; | |
| 
 | |
|                     if (item.id === 'Lane' || item.id === 'BoundaryErrorEvent' || item.id === 'BoundaryMessageEvent' ||  | |
|                             item.id === 'BoundarySignalEvent' || item.id === 'BoundaryTimerEvent' || | |
|                             item.id === 'BoundaryCancelEvent' || item.id === 'BoundaryCompensationEvent' ||  | |
|                             item.id === 'EntryCriterion') { | |
|                          | |
|                         $scope.dragCanContain = false; | |
|                          | |
|                         // Show Highlight | |
|                         editorManager.handleEvents({ | |
|                             type: ORYX.CONFIG.EVENT_HIGHLIGHT_SHOW, | |
|                             highlightId: 'shapeRepo.added', | |
|                             elements: [parentCandidate], | |
|                             style: ORYX.CONFIG.SELECTION_HIGHLIGHT_STYLE_RECTANGLE, | |
|                             color: ORYX.CONFIG.SELECTION_INVALID_COLOR | |
|                         }); | |
|                          | |
|                     } else { | |
|                         $scope.dragCanContain = true; | |
|                         $scope.dragCurrentParent = parentCandidate; | |
|                         $scope.dragCurrentParentId = parentCandidate.id; | |
|                          | |
|                         editorManager.handleEvents({ | |
|                             type: ORYX.CONFIG.EVENT_HIGHLIGHT_HIDE, | |
|                             highlightId: "shapeRepo.added" | |
|                         }); | |
|                     } | |
| 
 | |
|                     editorManager.handleEvents({ | |
|                         type: ORYX.CONFIG.EVENT_HIGHLIGHT_HIDE, | |
|                         highlightId: "shapeRepo.attached" | |
|                     }); | |
|                      | |
|                     return false; | |
|                      | |
|                 } else  { | |
|                     var item = $scope.getStencilItemById(event.target.id); | |
|                      | |
|                     var parentCandidate = aShapes.reverse().find(function (candidate) { | |
|                         return (candidate instanceof ORYX.Core.Canvas | |
|                             || candidate instanceof ORYX.Core.Node | |
|                             || candidate instanceof ORYX.Core.Edge); | |
|                     }); | |
|                      | |
|                     if (!parentCandidate) { | |
|                         $scope.dragCanContain = false; | |
|                         return false; | |
|                     } | |
|                      | |
|                     if (item.type === "node") { | |
|                          | |
|                         // check if the draggable is a boundary event and the parent an Activity | |
|                         var _canContain = false; | |
|                         var parentStencilId = parentCandidate.getStencil().id(); | |
| 
 | |
|                         if ($scope.dragCurrentParentId && $scope.dragCurrentParentId === parentCandidate.id) { | |
|                             return false; | |
|                         } | |
| 
 | |
|                         var parentItem = $scope.getStencilItemById(parentCandidate.getStencil().idWithoutNs()); | |
|                         if (parentCandidate.getStencil().id().endsWith("DecisionServiceSection")) { | |
|                             if (item.id === 'Decision') { | |
|                                 _canContain = true; | |
|                             } | |
|                         } else if (parentItem.roles.indexOf('Activity') > -1) { | |
|                             if (item.roles.indexOf('IntermediateEventOnActivityBoundary') > -1  | |
|                                 || item.roles.indexOf('EntryCriterionOnItemBoundary') > -1 | |
|                                 || item.roles.indexOf('ExitCriterionOnItemBoundary') > -1) { | |
|                                 _canContain = true; | |
|                             } | |
|                              | |
|                         } else if(parentItem.roles.indexOf('StageActivity') > -1) { | |
|                             if (item.roles.indexOf('EntryCriterionOnItemBoundary') > -1 | |
|                                 || item.roles.indexOf('ExitCriterionOnItemBoundary') > -1) { | |
|                                 _canContain = true; | |
|                             } | |
|                          | |
|                         } else if(parentItem.roles.indexOf('StageModelActivity') > -1) {  | |
|                             if (item.roles.indexOf('ExitCriterionOnItemBoundary') > -1) { | |
|                                 _canContain = true; | |
|                             } | |
|                          | |
|                         } else if (parentCandidate.getStencil().idWithoutNs() === 'Pool') { | |
|                           	if (item.id === 'Lane') { | |
|                             	_canContain = true; | |
|                           	} | |
|                         } | |
|                          | |
|                         if (_canContain) { | |
|                             editorManager.handleEvents({ | |
|                                 type: ORYX.CONFIG.EVENT_HIGHLIGHT_SHOW, | |
|                                 highlightId: "shapeRepo.attached", | |
|                                 elements: [parentCandidate], | |
|                                 style: ORYX.CONFIG.SELECTION_HIGHLIGHT_STYLE_RECTANGLE, | |
|                                 color: ORYX.CONFIG.SELECTION_VALID_COLOR | |
|                             }); | |
| 
 | |
|                             editorManager.handleEvents({ | |
|                                 type: ORYX.CONFIG.EVENT_HIGHLIGHT_HIDE, | |
|                                 highlightId: "shapeRepo.added" | |
|                             }); | |
|                              | |
|                         } else { | |
|                             for (var i = 0; i < $scope.containmentRules.length; i++) { | |
|                                 var rule = $scope.containmentRules[i]; | |
|                                 if (rule.role === parentItem.id) { | |
|                                     for (var j = 0; j < rule.contains.length; j++) { | |
|                                         if (item.roles.indexOf(rule.contains[j]) > -1) { | |
|                                             _canContain = true; | |
|                                             break; | |
|                                         } | |
|                                     } | |
| 
 | |
|                                     if (_canContain) { | |
|                                         break; | |
|                                     } | |
|                                 } | |
|                             } | |
|                              | |
|                             // Show Highlight | |
|                             editorManager.handleEvents({ | |
|                                 type: ORYX.CONFIG.EVENT_HIGHLIGHT_SHOW, | |
|                                 highlightId: 'shapeRepo.added', | |
|                                 elements: [parentCandidate], | |
|                                 color: _canContain ? ORYX.CONFIG.SELECTION_VALID_COLOR : ORYX.CONFIG.SELECTION_INVALID_COLOR | |
|                             }); | |
| 
 | |
|                             editorManager.handleEvents({ | |
|                                 type: ORYX.CONFIG.EVENT_HIGHLIGHT_HIDE, | |
|                                 highlightId: "shapeRepo.attached" | |
|                             }); | |
|                         } | |
| 
 | |
|                         $scope.dragCurrentParent = parentCandidate; | |
|                         $scope.dragCurrentParentId = parentCandidate.id; | |
|                         $scope.dragCurrentParentStencil = parentStencilId; | |
|                         $scope.dragCanContain = _canContain; | |
|                          | |
|                     } else  {  | |
|                       var canvasCandidate = editorManager.getCanvas(); | |
|                       var canConnect = false; | |
|                        | |
|                       var targetStencil = $scope.getStencilItemById(parentCandidate.getStencil().idWithoutNs()); | |
| 	                  if (targetStencil) { | |
| 	                    var associationConnect = false; | |
| 	                    if (stencil.idWithoutNs() === 'Association' && (curCan.getStencil().idWithoutNs() === 'TextAnnotation' || curCan.getStencil().idWithoutNs() === 'BoundaryCompensationEvent')) { | |
| 	                        associationConnect = true; | |
| 	                    } else if (stencil.idWithoutNs() === 'DataAssociation' && curCan.getStencil().idWithoutNs() === 'DataStore') { | |
|                             associationConnect = true; | |
|                         } | |
| 	                     | |
| 	                    if (targetStencil.canConnectTo || associationConnect) { | |
| 	                      canConnect = true; | |
| 	                    } | |
| 	                  } | |
|                        | |
|                       //Edge | |
|                       $scope.dragCurrentParent = canvasCandidate; | |
|                       $scope.dragCurrentParentId = canvasCandidate.id; | |
|                       $scope.dragCurrentParentStencil = canvasCandidate.getStencil().id(); | |
|                       $scope.dragCanContain = canConnect; | |
|                          | |
|                       // Show Highlight | |
|                       editorManager.handleEvents({ | |
|                             type: ORYX.CONFIG.EVENT_HIGHLIGHT_SHOW, | |
|                             highlightId: 'shapeRepo.added', | |
|                             elements: [canvasCandidate], | |
|                             color: ORYX.CONFIG.SELECTION_VALID_COLOR | |
|                       }); | |
| 
 | |
|                       editorManager.handleEvents({ | |
|                             type: ORYX.CONFIG.EVENT_HIGHLIGHT_HIDE, | |
|                             highlightId: "shapeRepo.attached" | |
|                       }); | |
|               		} | |
|                 } | |
|             } | |
|         }; | |
|          | |
|         $scope.dragCallbackQuickMenu = function (event, ui) { | |
|            | |
|             if ($scope.dragModeOver != false) { | |
|                 var coord = editorManager.eventCoordinatesXY(event.pageX, event.pageY); | |
|                  | |
|                 var additionalIEZoom = 1; | |
|                 if (!isNaN(screen.logicalXDPI) && !isNaN(screen.systemXDPI)) { | |
|                     var ua = navigator.userAgent; | |
|                     if (ua.indexOf('MSIE') >= 0) { | |
|                         //IE 10 and below | |
|                         var zoom = Math.round((screen.deviceXDPI / screen.logicalXDPI) * 100); | |
|                         if (zoom !== 100) { | |
|                             additionalIEZoom = zoom / 100 | |
|                         } | |
|                     } | |
|                 } | |
|                  | |
|                 if (additionalIEZoom !== 1) { | |
|                      coord.x = coord.x / additionalIEZoom; | |
|                      coord.y = coord.y / additionalIEZoom; | |
|                 } | |
|                  | |
|                 var aShapes = editorManager.getCanvas().getAbstractShapesAtPosition(coord); | |
|                 | |
|                 if (aShapes.length <= 0) { | |
|                     if (event.helper) { | |
|                         $scope.dragCanContain = false; | |
|                         return false; | |
|                     } | |
|                 } | |
| 
 | |
|                 if (aShapes[0] instanceof ORYX.Core.Canvas) { | |
|                     editorManager.getCanvas().setHightlightStateBasedOnX(coord.x); | |
|                 } | |
|                  | |
|                 var stencil = undefined; | |
|                 var stencilSets = editorManager.getStencilSets().values(); | |
|                 for (var i = 0; i < stencilSets.length; i++) { | |
|                 	var stencilSet = stencilSets[i]; | |
|               		var nodes = stencilSet.nodes(); | |
|               		for (var j = 0; j < nodes.length; j++) { | |
|                 		if (nodes[j].idWithoutNs() === event.target.id) { | |
|                   			stencil = nodes[j]; | |
|                   			break; | |
|                 		} | |
|                     } | |
|                | |
|               		if (!stencil) { | |
|                 		var edges = stencilSet.edges(); | |
|                   		for (var j = 0; j < edges.length; j++) { | |
|                     		if (edges[j].idWithoutNs() === event.target.id) { | |
|                       			stencil = edges[j]; | |
|                       			break; | |
|                     		} | |
|                       	} | |
|               		} | |
|               	} | |
|              | |
|                 var candidate = aShapes.last(); | |
|                  | |
|                 var isValid = false; | |
|                 if (stencil.type() === "node")  { | |
|             		//check containment rules | |
|             		var canContain = editorManager.getRules().canContain({containingShape:candidate, containedStencil:stencil}); | |
|              | |
|             		var parentCandidate = aShapes.reverse().find(function (candidate) { | |
|                         return (candidate instanceof ORYX.Core.Canvas | |
|                             || candidate instanceof ORYX.Core.Node | |
|                             || candidate instanceof ORYX.Core.Edge); | |
|                     }); | |
| 
 | |
|                     if (!parentCandidate) { | |
|                         $scope.dragCanContain = false; | |
|                         return false; | |
|                     } | |
|              | |
|             		$scope.dragCurrentParent = parentCandidate; | |
|                     $scope.dragCurrentParentId = parentCandidate.id; | |
|                     $scope.dragCurrentParentStencil = parentCandidate.getStencil().id(); | |
|                     $scope.dragCanContain = canContain; | |
|                     $scope.dropTargetElement = parentCandidate; | |
|                     isValid = canContain; | |
|        | |
|           		} else { //Edge | |
|            | |
|             		var shapes = editorManager.getSelection(); | |
|                 	if (shapes && shapes.length == 1) { | |
|                   		var currentSelectedShape = shapes.first(); | |
|                   		var curCan = candidate; | |
|                   		var canConnect = false; | |
|                    | |
|                   		var targetStencil = $scope.getStencilItemById(curCan.getStencil().idWithoutNs()); | |
|                   		if (targetStencil) { | |
|                     		var associationConnect = false; | |
|                     		if (stencil.idWithoutNs() === 'Association' && (curCan.getStencil().idWithoutNs() === 'TextAnnotation' || curCan.getStencil().idWithoutNs() === 'BoundaryCompensationEvent')) { | |
|                       			associationConnect = true; | |
|                     		} else if (stencil.idWithoutNs() === 'DataAssociation' && curCan.getStencil().idWithoutNs() === 'DataStore') { | |
|                         		associationConnect = true; | |
|                     		} | |
|                      | |
|                     		if (targetStencil.canConnectTo || associationConnect) { | |
|                     			while (!canConnect && curCan && !(curCan instanceof ORYX.Core.Canvas)) { | |
|                       				candidate = curCan; | |
|                       				//check connection rules | |
|                       				canConnect = editorManager.getRules().canConnect({ | |
| 	                                  sourceShape: currentSelectedShape,  | |
| 	                                  edgeStencil: stencil,  | |
| 	                                  targetShape: curCan | |
| 	                                });  | |
|                       				curCan = curCan.parent; | |
|                     			} | |
|                     		} | |
|                   		} | |
|                   		var parentCandidate = editorManager.getCanvas(); | |
|                  | |
|                 		isValid = canConnect; | |
|                 		$scope.dragCurrentParent = parentCandidate; | |
|                         $scope.dragCurrentParentId = parentCandidate.id; | |
|                         $scope.dragCurrentParentStencil = parentCandidate.getStencil().id(); | |
|                 		$scope.dragCanContain = canConnect; | |
|                 		$scope.dropTargetElement = candidate; | |
|                 	}    | |
|              | |
|           		}  | |
| 
 | |
|                 editorManager.handleEvents({ | |
|           			type:   ORYX.CONFIG.EVENT_HIGHLIGHT_SHOW,  | |
|           			highlightId:'shapeMenu', | |
|           			elements: [candidate], | |
|           			color: isValid ? ORYX.CONFIG.SELECTION_VALID_COLOR : ORYX.CONFIG.SELECTION_INVALID_COLOR | |
|         		}); | |
|             } | |
|         }; | |
| 
 | |
|     }]); | |
| 
 | |
| var FLOWABLE = FLOWABLE || {}; | |
| //create command for undo/redo | |
| FLOWABLE.CreateCommand = ORYX.Core.Command.extend({ | |
|   construct: function(option, currentReference, position, facade){ | |
|     this.option = option; | |
|     this.currentReference = currentReference; | |
|     this.position = position; | |
|     this.facade = facade; | |
|     this.shape; | |
|     this.edge; | |
|     this.targetRefPos; | |
|     this.sourceRefPos; | |
|     /* | |
|      * clone options parameters | |
|      */ | |
|         this.connectedShape = option.connectedShape; | |
|         this.connectingType = option.connectingType; | |
|         this.namespace = option.namespace; | |
|         this.type = option.type; | |
|         this.containedStencil = option.containedStencil; | |
|         this.parent = option.parent; | |
|         this.currentReference = currentReference; | |
|         this.shapeOptions = option.shapeOptions; | |
|   },       | |
|   execute: function(){ | |
|      | |
|     if (this.shape) { | |
|       if (this.shape instanceof ORYX.Core.Node) { | |
|         this.parent.add(this.shape); | |
|         if (this.edge) { | |
|           this.facade.getCanvas().add(this.edge); | |
|           this.edge.dockers.first().setDockedShape(this.connectedShape); | |
|           this.edge.dockers.first().setReferencePoint(this.sourceRefPos); | |
|           this.edge.dockers.last().setDockedShape(this.shape); | |
|           this.edge.dockers.last().setReferencePoint(this.targetRefPos); | |
|         } | |
|          | |
|         this.facade.setSelection([this.shape]); | |
|          | |
|       } else if (this.shape instanceof ORYX.Core.Edge) { | |
|         this.facade.getCanvas().add(this.shape); | |
|         this.shape.dockers.first().setDockedShape(this.connectedShape); | |
|         this.shape.dockers.first().setReferencePoint(this.sourceRefPos); | |
|       } | |
|     } | |
|     else { | |
|       this.shape = this.facade.createShape(this.option); | |
|       this.edge = (!(this.shape instanceof ORYX.Core.Edge)) ? this.shape.getIncomingShapes().first() : undefined; | |
|     } | |
|      | |
|     if (this.currentReference && this.position) { | |
|        | |
|       if (this.shape instanceof ORYX.Core.Edge) { | |
|        | |
|         if (!(this.currentReference instanceof ORYX.Core.Canvas)) { | |
|           this.shape.dockers.last().setDockedShape(this.currentReference); | |
|            | |
|           if (this.currentReference.getStencil().idWithoutNs() === 'TextAnnotation') | |
|           { | |
|             var midpoint = {}; | |
|             midpoint.x = 0; | |
|             midpoint.y = this.currentReference.bounds.height() / 2; | |
|             this.shape.dockers.last().setReferencePoint(midpoint); | |
|           } | |
|           else | |
|           { | |
|             this.shape.dockers.last().setReferencePoint(this.currentReference.bounds.midPoint()); | |
|           } | |
|         } | |
|         else { | |
|           this.shape.dockers.last().bounds.centerMoveTo(this.position); | |
|         } | |
|         this.sourceRefPos = this.shape.dockers.first().referencePoint; | |
|         this.targetRefPos = this.shape.dockers.last().referencePoint; | |
|          | |
|       } else if (this.edge){ | |
|         this.sourceRefPos = this.edge.dockers.first().referencePoint; | |
|         this.targetRefPos = this.edge.dockers.last().referencePoint; | |
|       } | |
|     } else { | |
|       var containedStencil = this.containedStencil; | |
|       var connectedShape = this.connectedShape; | |
|       var bc = connectedShape.bounds; | |
|       var bs = this.shape.bounds; | |
|        | |
|       var pos = bc.center(); | |
|       if(containedStencil.defaultAlign()==="north") { | |
|         pos.y -= (bc.height() / 2) + ORYX.CONFIG.SHAPEMENU_CREATE_OFFSET + (bs.height()/2); | |
|       } else if(containedStencil.defaultAlign()==="northeast") { | |
|         pos.x += (bc.width() / 2) + ORYX.CONFIG.SHAPEMENU_CREATE_OFFSET_CORNER + (bs.width()/2); | |
|         pos.y -= (bc.height() / 2) + ORYX.CONFIG.SHAPEMENU_CREATE_OFFSET_CORNER + (bs.height()/2); | |
|       } else if(containedStencil.defaultAlign()==="southeast") { | |
|         pos.x += (bc.width() / 2) + ORYX.CONFIG.SHAPEMENU_CREATE_OFFSET_CORNER + (bs.width()/2); | |
|         pos.y += (bc.height() / 2) + ORYX.CONFIG.SHAPEMENU_CREATE_OFFSET_CORNER + (bs.height()/2); | |
|       } else if(containedStencil.defaultAlign()==="south") { | |
|         pos.y += (bc.height() / 2) + ORYX.CONFIG.SHAPEMENU_CREATE_OFFSET + (bs.height()/2); | |
|       } else if(containedStencil.defaultAlign()==="southwest") { | |
|         pos.x -= (bc.width() / 2) + ORYX.CONFIG.SHAPEMENU_CREATE_OFFSET_CORNER + (bs.width()/2); | |
|         pos.y += (bc.height() / 2) + ORYX.CONFIG.SHAPEMENU_CREATE_OFFSET_CORNER + (bs.height()/2); | |
|       } else if(containedStencil.defaultAlign()==="west") { | |
|         pos.x -= (bc.width() / 2) + ORYX.CONFIG.SHAPEMENU_CREATE_OFFSET + (bs.width()/2); | |
|       } else if(containedStencil.defaultAlign()==="northwest") { | |
|         pos.x -= (bc.width() / 2) + ORYX.CONFIG.SHAPEMENU_CREATE_OFFSET_CORNER + (bs.width()/2); | |
|         pos.y -= (bc.height() / 2) + ORYX.CONFIG.SHAPEMENU_CREATE_OFFSET_CORNER + (bs.height()/2); | |
|       } else { | |
|         pos.x += (bc.width() / 2) + ORYX.CONFIG.SHAPEMENU_CREATE_OFFSET + (bs.width()/2); | |
|       } | |
|        | |
|       // Move shape to the new position | |
|       this.shape.bounds.centerMoveTo(pos); | |
|        | |
|       // Move all dockers of a node to the position | |
|       if (this.shape instanceof ORYX.Core.Node){ | |
|         (this.shape.dockers||[]).each(function(docker){ | |
|           docker.bounds.centerMoveTo(pos); | |
|         }); | |
|       } | |
|        | |
|       //this.shape.update(); | |
|       this.position = pos; | |
|        | |
|       if (this.edge){ | |
|         this.sourceRefPos = this.edge.dockers.first().referencePoint; | |
|         this.targetRefPos = this.edge.dockers.last().referencePoint; | |
|       } | |
|     } | |
|      | |
|     this.facade.getCanvas().update(); | |
|     this.facade.updateSelection(); | |
| 
 | |
|   }, | |
|   rollback: function(){ | |
|     this.facade.deleteShape(this.shape); | |
|     if(this.edge) { | |
|       this.facade.deleteShape(this.edge); | |
|     } | |
|     //this.currentParent.update(); | |
|     this.facade.setSelection(this.facade.getSelection().without(this.shape, this.edge)); | |
|   } | |
| });
 | |
| 
 |