stencil-controller.js 62 KB

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