bpmn-js 10.2.1 → 10.3.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1,5 +1,5 @@
1
1
  /*!
2
- * bpmn-js - bpmn-modeler v10.2.1
2
+ * bpmn-js - bpmn-modeler v10.3.0
3
3
  *
4
4
  * Copyright (c) 2014-present, camunda Services GmbH
5
5
  *
@@ -8,7 +8,7 @@
8
8
  *
9
9
  * Source Code: https://github.com/bpmn-io/bpmn-js
10
10
  *
11
- * Date: 2022-10-17
11
+ * Date: 2022-11-11
12
12
  */
13
13
  (function (global, factory) {
14
14
  typeof exports === 'object' && typeof module !== 'undefined' ? module.exports = factory() :
@@ -4278,6 +4278,8 @@
4278
4278
 
4279
4279
  var intersect = findPathIntersections;
4280
4280
 
4281
+ var intersectPaths = intersect;
4282
+
4281
4283
  function roundBounds(bounds) {
4282
4284
  return {
4283
4285
  x: Math.round(bounds.x),
@@ -4514,7 +4516,7 @@
4514
4516
 
4515
4517
 
4516
4518
  function getIntersections(a, b) {
4517
- return intersect(a, b);
4519
+ return intersectPaths(a, b);
4518
4520
  }
4519
4521
 
4520
4522
 
@@ -28699,6 +28701,8 @@
28699
28701
  middle: 'data:image/svg+xml,%3Csvg%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%20viewBox%3D%220%200%201800%201800%22%3E%3Cpath%20style%3D%22stroke%3AcurrentColor%3Bstroke-width%3A100%3Bstroke-linecap%3Around%22%20d%3D%22M150%20900h1500%22%2F%3E%3Crect%20x%3D%22150%22%20y%3D%22250%22%20width%3D%22600%22%20height%3D%221300%22%20rx%3D%221%22%20style%3D%22fill%3Anone%3Bstroke%3AcurrentColor%3Bstroke-width%3A100%22%2F%3E%3Crect%20x%3D%221050%22%20y%3D%22500%22%20width%3D%22600%22%20height%3D%22800%22%20rx%3D%221%22%20style%3D%22fill%3AcurrentColor%3Bstroke%3AcurrentColor%3Bstroke-width%3A100%3Bopacity%3A.5%22%2F%3E%3C%2Fsvg%3E'
28700
28702
  };
28701
28703
 
28704
+ var ICONS$1 = icons$1;
28705
+
28702
28706
  var LOW_PRIORITY$l = 900;
28703
28707
 
28704
28708
  /**
@@ -28742,7 +28746,7 @@
28742
28746
  'align-elements': {
28743
28747
  group: 'align-elements',
28744
28748
  title: self._translate('Align elements'),
28745
- imageUrl: icons$1['align'],
28749
+ imageUrl: ICONS$1['align'],
28746
28750
  action: {
28747
28751
  click: function(event, elements) {
28748
28752
  var position = self._getMenuPosition(elements);
@@ -28836,7 +28840,7 @@
28836
28840
  group: 'align',
28837
28841
  title: translate('Align elements ' + alignment),
28838
28842
  className: 'bjs-align-elements-menu-entry',
28839
- imageUrl: icons$1[alignment],
28843
+ imageUrl: ICONS$1[alignment],
28840
28844
  action: function(event, entry) {
28841
28845
  alignElements.trigger(elements, alignment);
28842
28846
  popupMenu.close();
@@ -30898,7 +30902,7 @@
30898
30902
 
30899
30903
  function getPathIntersection(waypoints, reference) {
30900
30904
 
30901
- var intersections = intersect(circlePath(reference, INTERSECTION_THRESHOLD$1), linePath(waypoints));
30905
+ var intersections = intersectPaths(circlePath(reference, INTERSECTION_THRESHOLD$1), linePath(waypoints));
30902
30906
 
30903
30907
  var a = intersections[0],
30904
30908
  b = intersections[intersections.length - 1],
@@ -34897,7 +34901,7 @@
34897
34901
  }
34898
34902
 
34899
34903
  // attachers (priority = 2)
34900
- if (isAttacher$1(element)) {
34904
+ if (isAttacher$2(element)) {
34901
34905
  descriptor.priority = 2;
34902
34906
 
34903
34907
  descriptor.host = element.host.id;
@@ -35359,7 +35363,7 @@
35359
35363
 
35360
35364
  // helpers //////////
35361
35365
 
35362
- function isAttacher$1(element) {
35366
+ function isAttacher$2(element) {
35363
35367
  return !!element.host;
35364
35368
  }
35365
35369
 
@@ -38865,6 +38869,8 @@
38865
38869
  vertical: 'data:image/svg+xml,%3Csvg%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%20viewBox%3D%220%200%201800%201800%22%3E%3Cpath%20style%3D%22fill%3Anone%3Bstroke%3AcurrentColor%3Bstroke-width%3A100%3Bstroke-linejoin%3Around%22%20d%3D%22M400%201350H150V450h250%22%2F%3E%3Crect%20x%3D%22450%22%20y%3D%22150%22%20width%3D%221200%22%20height%3D%22600%22%20rx%3D%221%22%20style%3D%22fill%3Anone%3Bstroke%3AcurrentColor%3Bstroke-width%3A100%22%2F%3E%3Crect%20x%3D%22450%22%20y%3D%221050%22%20width%3D%22800%22%20height%3D%22600%22%20rx%3D%221%22%20style%3D%22fill%3AcurrentColor%3Bstroke%3AcurrentColor%3Bstroke-width%3A100%3Bopacity%3A.5%22%2F%3E%3C%2Fsvg%3E',
38866
38870
  };
38867
38871
 
38872
+ var ICONS = icons;
38873
+
38868
38874
  var LOW_PRIORITY$g = 900;
38869
38875
 
38870
38876
  /**
@@ -38911,7 +38917,7 @@
38911
38917
  group: 'distribute',
38912
38918
  title: translate('Distribute elements horizontally'),
38913
38919
  className: 'bjs-align-elements-menu-entry',
38914
- imageUrl: icons['horizontal'],
38920
+ imageUrl: ICONS['horizontal'],
38915
38921
  action: function(event, entry) {
38916
38922
  distributeElements.trigger(elements, 'horizontal');
38917
38923
  popupMenu.close();
@@ -38920,7 +38926,7 @@
38920
38926
  'distribute-elements-vertical': {
38921
38927
  group: 'distribute',
38922
38928
  title: translate('Distribute elements vertically'),
38923
- imageUrl: icons['vertical'],
38929
+ imageUrl: ICONS['vertical'],
38924
38930
  action: function(event, entry) {
38925
38931
  distributeElements.trigger(elements, 'vertical');
38926
38932
  popupMenu.close();
@@ -48486,2407 +48492,2572 @@
48486
48492
  bpmnOrderingProvider: [ 'type', BpmnOrderingProvider ]
48487
48493
  };
48488
48494
 
48495
+ var LOW_PRIORITY$6 = 250;
48496
+
48489
48497
  /**
48490
- * A service that offers un- and redoable execution of commands.
48491
- *
48492
- * The command stack is responsible for executing modeling actions
48493
- * in a un- and redoable manner. To do this it delegates the actual
48494
- * command execution to {@link CommandHandler}s.
48495
- *
48496
- * Command handlers provide {@link CommandHandler#execute(ctx)} and
48497
- * {@link CommandHandler#revert(ctx)} methods to un- and redo a command
48498
- * identified by a command context.
48499
- *
48500
- *
48501
- * ## Life-Cycle events
48502
- *
48503
- * In the process the command stack fires a number of life-cycle events
48504
- * that other components to participate in the command execution.
48505
- *
48506
- * * preExecute
48507
- * * preExecuted
48508
- * * execute
48509
- * * executed
48510
- * * postExecute
48511
- * * postExecuted
48512
- * * revert
48513
- * * reverted
48514
- *
48515
- * A special event is used for validating, whether a command can be
48516
- * performed prior to its execution.
48517
- *
48518
- * * canExecute
48519
- *
48520
- * Each of the events is fired as `commandStack.{eventName}` and
48521
- * `commandStack.{commandName}.{eventName}`, respectively. This gives
48522
- * components fine grained control on where to hook into.
48523
- *
48524
- * The event object fired transports `command`, the name of the
48525
- * command and `context`, the command context.
48526
- *
48527
- *
48528
- * ## Creating Command Handlers
48529
- *
48530
- * Command handlers should provide the {@link CommandHandler#execute(ctx)}
48531
- * and {@link CommandHandler#revert(ctx)} methods to implement
48532
- * redoing and undoing of a command.
48533
- *
48534
- * A command handler _must_ ensure undo is performed properly in order
48535
- * not to break the undo chain. It must also return the shapes that
48536
- * got changed during the `execute` and `revert` operations.
48537
- *
48538
- * Command handlers may execute other modeling operations (and thus
48539
- * commands) in their `preExecute` and `postExecute` phases. The command
48540
- * stack will properly group all commands together into a logical unit
48541
- * that may be re- and undone atomically.
48542
- *
48543
- * Command handlers must not execute other commands from within their
48544
- * core implementation (`execute`, `revert`).
48545
- *
48546
- *
48547
- * ## Change Tracking
48548
- *
48549
- * During the execution of the CommandStack it will keep track of all
48550
- * elements that have been touched during the command's execution.
48551
- *
48552
- * At the end of the CommandStack execution it will notify interested
48553
- * components via an 'elements.changed' event with all the dirty
48554
- * elements.
48555
- *
48556
- * The event can be picked up by components that are interested in the fact
48557
- * that elements have been changed. One use case for this is updating
48558
- * their graphical representation after moving / resizing or deletion.
48559
- *
48560
- * @see CommandHandler
48498
+ * The tool manager acts as middle-man between the available tool's and the Palette,
48499
+ * it takes care of making sure that the correct active state is set.
48561
48500
  *
48562
- * @param {EventBus} eventBus
48563
- * @param {Injector} injector
48501
+ * @param {Object} eventBus
48502
+ * @param {Object} dragging
48564
48503
  */
48565
- function CommandStack(eventBus, injector) {
48504
+ function ToolManager(eventBus, dragging) {
48505
+ this._eventBus = eventBus;
48506
+ this._dragging = dragging;
48566
48507
 
48567
- /**
48568
- * A map of all registered command handlers.
48569
- *
48570
- * @type {Object}
48571
- */
48572
- this._handlerMap = {};
48508
+ this._tools = [];
48509
+ this._active = null;
48510
+ }
48573
48511
 
48574
- /**
48575
- * A stack containing all re/undoable actions on the diagram
48576
- *
48577
- * @type {Array<Object>}
48578
- */
48579
- this._stack = [];
48512
+ ToolManager.$inject = [ 'eventBus', 'dragging' ];
48580
48513
 
48581
- /**
48582
- * The current index on the stack
48583
- *
48584
- * @type {number}
48585
- */
48586
- this._stackIdx = -1;
48514
+ ToolManager.prototype.registerTool = function(name, events) {
48515
+ var tools = this._tools;
48587
48516
 
48588
- /**
48589
- * Current active commandStack execution
48590
- *
48591
- * @type {Object}
48592
- * @property {Object[]} actions
48593
- * @property {Object[]} dirty
48594
- * @property { 'undo' | 'redo' | 'clear' | 'execute' | null } trigger the cause of the current excecution
48595
- */
48596
- this._currentExecution = {
48597
- actions: [],
48598
- dirty: [],
48599
- trigger: null
48600
- };
48517
+ if (!events) {
48518
+ throw new Error('A tool has to be registered with it\'s "events"');
48519
+ }
48601
48520
 
48521
+ tools.push(name);
48602
48522
 
48603
- this._injector = injector;
48604
- this._eventBus = eventBus;
48523
+ this.bindEvents(name, events);
48524
+ };
48605
48525
 
48606
- this._uid = 1;
48526
+ ToolManager.prototype.isActive = function(tool) {
48527
+ return tool && this._active === tool;
48528
+ };
48607
48529
 
48608
- eventBus.on([
48609
- 'diagram.destroy',
48610
- 'diagram.clear'
48611
- ], function() {
48612
- this.clear(false);
48613
- }, this);
48614
- }
48530
+ ToolManager.prototype.length = function(tool) {
48531
+ return this._tools.length;
48532
+ };
48615
48533
 
48616
- CommandStack.$inject = [ 'eventBus', 'injector' ];
48534
+ ToolManager.prototype.setActive = function(tool) {
48535
+ var eventBus = this._eventBus;
48617
48536
 
48537
+ if (this._active !== tool) {
48538
+ this._active = tool;
48618
48539
 
48619
- /**
48620
- * Execute a command
48621
- *
48622
- * @param {string} command the command to execute
48623
- * @param {Object} context the environment to execute the command in
48624
- */
48625
- CommandStack.prototype.execute = function(command, context) {
48626
- if (!command) {
48627
- throw new Error('command required');
48540
+ eventBus.fire('tool-manager.update', { tool: tool });
48628
48541
  }
48542
+ };
48629
48543
 
48630
- this._currentExecution.trigger = 'execute';
48544
+ ToolManager.prototype.bindEvents = function(name, events) {
48545
+ var eventBus = this._eventBus,
48546
+ dragging = this._dragging;
48631
48547
 
48632
- const action = { command: command, context: context };
48548
+ var eventsToRegister = [];
48633
48549
 
48634
- this._pushAction(action);
48635
- this._internalExecute(action);
48636
- this._popAction(action);
48637
- };
48550
+ eventBus.on(events.tool + '.init', function(event) {
48551
+ var context = event.context;
48638
48552
 
48553
+ // Active tools that want to reactivate themselves must do this explicitly
48554
+ if (!context.reactivate && this.isActive(name)) {
48555
+ this.setActive(null);
48639
48556
 
48640
- /**
48641
- * Ask whether a given command can be executed.
48642
- *
48643
- * Implementors may hook into the mechanism on two ways:
48644
- *
48645
- * * in event listeners:
48646
- *
48647
- * Users may prevent the execution via an event listener.
48648
- * It must prevent the default action for `commandStack.(<command>.)canExecute` events.
48649
- *
48650
- * * in command handlers:
48651
- *
48652
- * If the method {@link CommandHandler#canExecute} is implemented in a handler
48653
- * it will be called to figure out whether the execution is allowed.
48654
- *
48655
- * @param {string} command the command to execute
48656
- * @param {Object} context the environment to execute the command in
48657
- *
48658
- * @return {boolean} true if the command can be executed
48659
- */
48660
- CommandStack.prototype.canExecute = function(command, context) {
48557
+ dragging.cancel();
48558
+ return;
48559
+ }
48661
48560
 
48662
- const action = { command: command, context: context };
48561
+ this.setActive(name);
48663
48562
 
48664
- const handler = this._getHandler(command);
48563
+ }, this);
48665
48564
 
48666
- let result = this._fire(command, 'canExecute', action);
48565
+ // Todo[ricardo]: add test cases
48566
+ forEach$1(events, function(event) {
48567
+ eventsToRegister.push(event + '.ended');
48568
+ eventsToRegister.push(event + '.canceled');
48569
+ });
48667
48570
 
48668
- // handler#canExecute will only be called if no listener
48669
- // decided on a result already
48670
- if (result === undefined) {
48671
- if (!handler) {
48672
- return false;
48571
+ eventBus.on(eventsToRegister, LOW_PRIORITY$6, function(event) {
48572
+
48573
+ // We defer the de-activation of the tool to the .activate phase,
48574
+ // so we're able to check if we want to toggle off the current
48575
+ // active tool or switch to a new one
48576
+ if (!this._active) {
48577
+ return;
48673
48578
  }
48674
48579
 
48675
- if (handler.canExecute) {
48676
- result = handler.canExecute(context);
48580
+ if (isPaletteClick(event)) {
48581
+ return;
48677
48582
  }
48678
- }
48679
48583
 
48680
- return result;
48584
+ this.setActive(null);
48585
+ }, this);
48586
+
48681
48587
  };
48682
48588
 
48683
48589
 
48590
+ // helpers ///////////////
48591
+
48684
48592
  /**
48685
- * Clear the command stack, erasing all undo / redo history
48593
+ * Check if a given event is a palette click event.
48594
+ *
48595
+ * @param {EventBus.Event} event
48596
+ *
48597
+ * @return {boolean}
48686
48598
  */
48687
- CommandStack.prototype.clear = function(emit) {
48688
- this._stack.length = 0;
48689
- this._stackIdx = -1;
48599
+ function isPaletteClick(event) {
48600
+ var target = event.originalEvent && event.originalEvent.target;
48690
48601
 
48691
- if (emit !== false) {
48692
- this._fire('changed', { trigger: 'clear' });
48693
- }
48694
- };
48602
+ return target && closest(target, '.group[data-group="tools"]');
48603
+ }
48695
48604
 
48605
+ var ToolManagerModule = {
48606
+ __depends__: [
48607
+ DraggingModule
48608
+ ],
48609
+ __init__: [ 'toolManager' ],
48610
+ toolManager: [ 'type', ToolManager ]
48611
+ };
48696
48612
 
48697
48613
  /**
48698
- * Undo last command(s)
48614
+ * Return direction given axis and delta.
48615
+ *
48616
+ * @param {string} axis
48617
+ * @param {number} delta
48618
+ *
48619
+ * @return {string}
48699
48620
  */
48700
- CommandStack.prototype.undo = function() {
48701
- let action = this._getUndoAction(),
48702
- next;
48703
-
48704
- if (action) {
48705
- this._currentExecution.trigger = 'undo';
48706
-
48707
- this._pushAction(action);
48621
+ function getDirection(axis, delta) {
48708
48622
 
48709
- while (action) {
48710
- this._internalUndo(action);
48711
- next = this._getUndoAction();
48623
+ if (axis === 'x') {
48624
+ if (delta > 0) {
48625
+ return 'e';
48626
+ }
48712
48627
 
48713
- if (!next || next.id !== action.id) {
48714
- break;
48715
- }
48628
+ if (delta < 0) {
48629
+ return 'w';
48630
+ }
48631
+ }
48716
48632
 
48717
- action = next;
48633
+ if (axis === 'y') {
48634
+ if (delta > 0) {
48635
+ return 's';
48718
48636
  }
48719
48637
 
48720
- this._popAction();
48638
+ if (delta < 0) {
48639
+ return 'n';
48640
+ }
48721
48641
  }
48722
- };
48723
48642
 
48643
+ return null;
48644
+ }
48724
48645
 
48725
48646
  /**
48726
- * Redo last command(s)
48647
+ * Returns connections whose waypoints are to be updated. Waypoints are to be updated if start
48648
+ * or end is to be moved or resized.
48649
+ *
48650
+ * @param {Array<djs.model.Shape} movingShapes
48651
+ * @param {Array<djs.model.Shape} resizingShapes
48652
+ *
48653
+ * @returns {Array<djs.model.Connection>}
48727
48654
  */
48728
- CommandStack.prototype.redo = function() {
48729
- let action = this._getRedoAction(),
48730
- next;
48655
+ function getWaypointsUpdatingConnections(movingShapes, resizingShapes) {
48656
+ var waypointsUpdatingConnections = [];
48731
48657
 
48732
- if (action) {
48733
- this._currentExecution.trigger = 'redo';
48658
+ forEach$1(movingShapes.concat(resizingShapes), function(shape) {
48659
+ var incoming = shape.incoming,
48660
+ outgoing = shape.outgoing;
48734
48661
 
48735
- this._pushAction(action);
48662
+ forEach$1(incoming.concat(outgoing), function(connection) {
48663
+ var source = connection.source,
48664
+ target = connection.target;
48736
48665
 
48737
- while (action) {
48738
- this._internalExecute(action, true);
48739
- next = this._getRedoAction();
48666
+ if (includes$4(movingShapes, source) ||
48667
+ includes$4(movingShapes, target) ||
48668
+ includes$4(resizingShapes, source) ||
48669
+ includes$4(resizingShapes, target)) {
48740
48670
 
48741
- if (!next || next.id !== action.id) {
48742
- break;
48671
+ if (!includes$4(waypointsUpdatingConnections, connection)) {
48672
+ waypointsUpdatingConnections.push(connection);
48673
+ }
48743
48674
  }
48675
+ });
48676
+ });
48744
48677
 
48745
- action = next;
48746
- }
48747
-
48748
- this._popAction();
48749
- }
48750
- };
48678
+ return waypointsUpdatingConnections;
48679
+ }
48751
48680
 
48681
+ function includes$4(array, item) {
48682
+ return array.indexOf(item) !== -1;
48683
+ }
48752
48684
 
48753
48685
  /**
48754
- * Register a handler instance with the command stack
48686
+ * Resize bounds.
48755
48687
  *
48756
- * @param {string} command
48757
- * @param {CommandHandler} handler
48758
- */
48759
- CommandStack.prototype.register = function(command, handler) {
48760
- this._setHandler(command, handler);
48761
- };
48762
-
48763
-
48764
- /**
48765
- * Register a handler type with the command stack
48766
- * by instantiating it and injecting its dependencies.
48688
+ * @param {Object} bounds
48689
+ * @param {number} bounds.x
48690
+ * @param {number} bounds.y
48691
+ * @param {number} bounds.width
48692
+ * @param {number} bounds.height
48693
+ * @param {string} direction
48694
+ * @param {Object} delta
48695
+ * @param {number} delta.x
48696
+ * @param {number} delta.y
48767
48697
  *
48768
- * @param {string} command
48769
- * @param {Function} a constructor for a {@link CommandHandler}
48698
+ * @return {Object}
48770
48699
  */
48771
- CommandStack.prototype.registerHandler = function(command, handlerCls) {
48700
+ function resizeBounds(bounds, direction, delta) {
48701
+ var x = bounds.x,
48702
+ y = bounds.y,
48703
+ width = bounds.width,
48704
+ height = bounds.height,
48705
+ dx = delta.x,
48706
+ dy = delta.y;
48772
48707
 
48773
- if (!command || !handlerCls) {
48774
- throw new Error('command and handlerCls must be defined');
48708
+ switch (direction) {
48709
+ case 'n':
48710
+ return {
48711
+ x: x,
48712
+ y: y + dy,
48713
+ width: width,
48714
+ height: height - dy
48715
+ };
48716
+ case 's':
48717
+ return {
48718
+ x: x,
48719
+ y: y,
48720
+ width: width,
48721
+ height: height + dy
48722
+ };
48723
+ case 'w':
48724
+ return {
48725
+ x: x + dx,
48726
+ y: y,
48727
+ width: width - dx,
48728
+ height: height
48729
+ };
48730
+ case 'e':
48731
+ return {
48732
+ x: x,
48733
+ y: y,
48734
+ width: width + dx,
48735
+ height: height
48736
+ };
48737
+ default:
48738
+ throw new Error('unknown direction: ' + direction);
48775
48739
  }
48740
+ }
48776
48741
 
48777
- const handler = this._injector.instantiate(handlerCls);
48778
- this.register(command, handler);
48779
- };
48780
-
48781
- CommandStack.prototype.canUndo = function() {
48782
- return !!this._getUndoAction();
48783
- };
48742
+ var abs$1 = Math.abs,
48743
+ round$4 = Math.round;
48784
48744
 
48785
- CommandStack.prototype.canRedo = function() {
48786
- return !!this._getRedoAction();
48745
+ var AXIS_TO_DIMENSION = {
48746
+ x: 'width',
48747
+ y: 'height'
48787
48748
  };
48788
48749
 
48789
- // stack access //////////////////////
48750
+ var CURSOR_CROSSHAIR = 'crosshair';
48790
48751
 
48791
- CommandStack.prototype._getRedoAction = function() {
48792
- return this._stack[this._stackIdx + 1];
48752
+ var DIRECTION_TO_TRBL = {
48753
+ n: 'top',
48754
+ w: 'left',
48755
+ s: 'bottom',
48756
+ e: 'right'
48793
48757
  };
48794
48758
 
48759
+ var HIGH_PRIORITY$5 = 1500;
48795
48760
 
48796
- CommandStack.prototype._getUndoAction = function() {
48797
- return this._stack[this._stackIdx];
48761
+ var DIRECTION_TO_OPPOSITE = {
48762
+ n: 's',
48763
+ w: 'e',
48764
+ s: 'n',
48765
+ e: 'w'
48798
48766
  };
48799
48767
 
48768
+ var PADDING = 20;
48800
48769
 
48801
- // internal functionality //////////////////////
48802
-
48803
- CommandStack.prototype._internalUndo = function(action) {
48804
- const command = action.command,
48805
- context = action.context;
48806
48770
 
48807
- const handler = this._getHandler(command);
48771
+ /**
48772
+ * Add or remove space by moving and resizing elements.
48773
+ *
48774
+ * @param {Canvas} canvas
48775
+ * @param {Dragging} dragging
48776
+ * @param {EventBus} eventBus
48777
+ * @param {Modeling} modeling
48778
+ * @param {Rules} rules
48779
+ * @param {ToolManager} toolManager
48780
+ * @param {Mouse} mouse
48781
+ */
48782
+ function SpaceTool(
48783
+ canvas, dragging, eventBus,
48784
+ modeling, rules, toolManager,
48785
+ mouse) {
48808
48786
 
48809
- // guard against illegal nested command stack invocations
48810
- this._atomicDo(() => {
48811
- this._fire(command, 'revert', action);
48787
+ this._canvas = canvas;
48788
+ this._dragging = dragging;
48789
+ this._eventBus = eventBus;
48790
+ this._modeling = modeling;
48791
+ this._rules = rules;
48792
+ this._toolManager = toolManager;
48793
+ this._mouse = mouse;
48812
48794
 
48813
- if (handler.revert) {
48814
- this._markDirty(handler.revert(context));
48815
- }
48795
+ var self = this;
48816
48796
 
48817
- this._revertedAction(action);
48797
+ toolManager.registerTool('space', {
48798
+ tool: 'spaceTool.selection',
48799
+ dragging: 'spaceTool'
48800
+ });
48818
48801
 
48819
- this._fire(command, 'reverted', action);
48802
+ eventBus.on('spaceTool.selection.end', function(event) {
48803
+ eventBus.once('spaceTool.selection.ended', function() {
48804
+ self.activateMakeSpace(event.originalEvent);
48805
+ });
48820
48806
  });
48821
- };
48822
48807
 
48808
+ eventBus.on('spaceTool.move', HIGH_PRIORITY$5 , function(event) {
48809
+ var context = event.context,
48810
+ initialized = context.initialized;
48823
48811
 
48824
- CommandStack.prototype._fire = function(command, qualifier, event) {
48825
- if (arguments.length < 3) {
48826
- event = qualifier;
48827
- qualifier = null;
48828
- }
48812
+ if (!initialized) {
48813
+ initialized = context.initialized = self.init(event, context);
48814
+ }
48829
48815
 
48830
- const names = qualifier ? [ command + '.' + qualifier, qualifier ] : [ command ];
48831
- let result;
48816
+ if (initialized) {
48817
+ ensureConstraints(event);
48818
+ }
48819
+ });
48832
48820
 
48833
- event = this._eventBus.createEvent(event);
48821
+ eventBus.on('spaceTool.end', function(event) {
48822
+ var context = event.context,
48823
+ axis = context.axis,
48824
+ direction = context.direction,
48825
+ movingShapes = context.movingShapes,
48826
+ resizingShapes = context.resizingShapes,
48827
+ start = context.start;
48834
48828
 
48835
- for (const name of names) {
48836
- result = this._eventBus.fire('commandStack.' + name, event);
48829
+ if (!context.initialized) {
48830
+ return;
48831
+ }
48837
48832
 
48838
- if (event.cancelBubble) {
48839
- break;
48840
- }
48841
- }
48842
-
48843
- return result;
48844
- };
48833
+ ensureConstraints(event);
48845
48834
 
48846
- CommandStack.prototype._createId = function() {
48847
- return this._uid++;
48848
- };
48835
+ var delta = {
48836
+ x: 0,
48837
+ y: 0
48838
+ };
48849
48839
 
48850
- CommandStack.prototype._atomicDo = function(fn) {
48840
+ delta[ axis ] = round$4(event[ 'd' + axis ]);
48851
48841
 
48852
- const execution = this._currentExecution;
48842
+ self.makeSpace(movingShapes, resizingShapes, delta, direction, start);
48853
48843
 
48854
- execution.atomic = true;
48844
+ eventBus.once('spaceTool.ended', function(event) {
48855
48845
 
48856
- try {
48857
- fn();
48858
- } finally {
48859
- execution.atomic = false;
48860
- }
48861
- };
48846
+ // activate space tool selection after make space
48847
+ self.activateSelection(event.originalEvent, true, true);
48848
+ });
48849
+ });
48850
+ }
48862
48851
 
48863
- CommandStack.prototype._internalExecute = function(action, redo) {
48864
- const command = action.command,
48865
- context = action.context;
48852
+ SpaceTool.$inject = [
48853
+ 'canvas',
48854
+ 'dragging',
48855
+ 'eventBus',
48856
+ 'modeling',
48857
+ 'rules',
48858
+ 'toolManager',
48859
+ 'mouse'
48860
+ ];
48866
48861
 
48867
- const handler = this._getHandler(command);
48862
+ /**
48863
+ * Activate space tool selection.
48864
+ *
48865
+ * @param {Object} event
48866
+ * @param {boolean} autoActivate
48867
+ */
48868
+ SpaceTool.prototype.activateSelection = function(event, autoActivate, reactivate) {
48869
+ this._dragging.init(event, 'spaceTool.selection', {
48870
+ autoActivate: autoActivate,
48871
+ cursor: CURSOR_CROSSHAIR,
48872
+ data: {
48873
+ context: {
48874
+ reactivate: reactivate
48875
+ }
48876
+ },
48877
+ trapClick: false
48878
+ });
48879
+ };
48868
48880
 
48869
- if (!handler) {
48870
- throw new Error('no command handler registered for <' + command + '>');
48871
- }
48881
+ /**
48882
+ * Activate space tool make space.
48883
+ *
48884
+ * @param {MouseEvent} event
48885
+ */
48886
+ SpaceTool.prototype.activateMakeSpace = function(event) {
48887
+ this._dragging.init(event, 'spaceTool', {
48888
+ autoActivate: true,
48889
+ cursor: CURSOR_CROSSHAIR,
48890
+ data: {
48891
+ context: {}
48892
+ }
48893
+ });
48894
+ };
48872
48895
 
48873
- this._pushAction(action);
48896
+ /**
48897
+ * Make space.
48898
+ *
48899
+ * @param {Array<djs.model.Shape>} movingShapes
48900
+ * @param {Array<djs.model.Shape>} resizingShapes
48901
+ * @param {Object} delta
48902
+ * @param {number} delta.x
48903
+ * @param {number} delta.y
48904
+ * @param {string} direction
48905
+ * @param {number} start
48906
+ */
48907
+ SpaceTool.prototype.makeSpace = function(movingShapes, resizingShapes, delta, direction, start) {
48908
+ return this._modeling.createSpace(movingShapes, resizingShapes, delta, direction, start);
48909
+ };
48874
48910
 
48875
- if (!redo) {
48876
- this._fire(command, 'preExecute', action);
48911
+ /**
48912
+ * Initialize make space and return true if that was successful.
48913
+ *
48914
+ * @param {Object} event
48915
+ * @param {Object} context
48916
+ *
48917
+ * @return {boolean}
48918
+ */
48919
+ SpaceTool.prototype.init = function(event, context) {
48920
+ var axis = abs$1(event.dx) > abs$1(event.dy) ? 'x' : 'y',
48921
+ delta = event[ 'd' + axis ],
48922
+ start = event[ axis ] - delta;
48877
48923
 
48878
- if (handler.preExecute) {
48879
- handler.preExecute(context);
48880
- }
48924
+ if (abs$1(delta) < 5) {
48925
+ return false;
48926
+ }
48881
48927
 
48882
- this._fire(command, 'preExecuted', action);
48928
+ // invert delta to remove space when moving left
48929
+ if (delta < 0) {
48930
+ delta *= -1;
48883
48931
  }
48884
48932
 
48885
- // guard against illegal nested command stack invocations
48886
- this._atomicDo(() => {
48933
+ // invert delta to add/remove space when removing/adding space if modifier key is pressed
48934
+ if (hasPrimaryModifier(event)) {
48935
+ delta *= -1;
48936
+ }
48887
48937
 
48888
- this._fire(command, 'execute', action);
48938
+ var direction = getDirection(axis, delta);
48889
48939
 
48890
- if (handler.execute) {
48940
+ var root = this._canvas.getRootElement();
48891
48941
 
48892
- // actual execute + mark return results as dirty
48893
- this._markDirty(handler.execute(context));
48894
- }
48942
+ var children = selfAndAllChildren(root, true);
48895
48943
 
48896
- // log to stack
48897
- this._executedAction(action, redo);
48944
+ var elements = this.calculateAdjustments(children, axis, delta, start);
48898
48945
 
48899
- this._fire(command, 'executed', action);
48946
+ var minDimensions = this._eventBus.fire('spaceTool.getMinDimensions', {
48947
+ axis: axis,
48948
+ direction: direction,
48949
+ shapes: elements.resizingShapes,
48950
+ start: start
48900
48951
  });
48901
48952
 
48902
- if (!redo) {
48903
- this._fire(command, 'postExecute', action);
48953
+ var spaceToolConstraints = getSpaceToolConstraints(elements, axis, direction, start, minDimensions);
48904
48954
 
48905
- if (handler.postExecute) {
48906
- handler.postExecute(context);
48955
+ assign$1(
48956
+ context,
48957
+ elements,
48958
+ {
48959
+ axis: axis,
48960
+ direction: direction,
48961
+ spaceToolConstraints: spaceToolConstraints,
48962
+ start: start
48907
48963
  }
48964
+ );
48908
48965
 
48909
- this._fire(command, 'postExecuted', action);
48910
- }
48966
+ set('resize-' + (axis === 'x' ? 'ew' : 'ns'));
48911
48967
 
48912
- this._popAction(action);
48968
+ return true;
48913
48969
  };
48914
48970
 
48971
+ /**
48972
+ * Get elements to be moved and resized.
48973
+ *
48974
+ * @param {Array<djs.model.Shape>} elements
48975
+ * @param {string} axis
48976
+ * @param {number} delta
48977
+ * @param {number} start
48978
+ *
48979
+ * @return {Object}
48980
+ */
48981
+ SpaceTool.prototype.calculateAdjustments = function(elements, axis, delta, start) {
48982
+ var rules = this._rules;
48915
48983
 
48916
- CommandStack.prototype._pushAction = function(action) {
48984
+ var movingShapes = [],
48985
+ resizingShapes = [];
48917
48986
 
48918
- const execution = this._currentExecution,
48919
- actions = execution.actions;
48987
+ var attachers = [],
48988
+ connections = [];
48920
48989
 
48921
- const baseAction = actions[0];
48990
+ function moveShape(shape) {
48991
+ if (!movingShapes.includes(shape)) {
48992
+ movingShapes.push(shape);
48993
+ }
48922
48994
 
48923
- if (execution.atomic) {
48924
- throw new Error('illegal invocation in <execute> or <revert> phase (action: ' + action.command + ')');
48925
- }
48995
+ var label = shape.label;
48926
48996
 
48927
- if (!action.id) {
48928
- action.id = (baseAction && baseAction.id) || this._createId();
48997
+ // move external label if its label target is moving
48998
+ if (label && !movingShapes.includes(label)) {
48999
+ movingShapes.push(label);
49000
+ }
48929
49001
  }
48930
49002
 
48931
- actions.push(action);
48932
- };
49003
+ function resizeShape(shape) {
49004
+ if (!resizingShapes.includes(shape)) {
49005
+ resizingShapes.push(shape);
49006
+ }
49007
+ }
48933
49008
 
49009
+ forEach$1(elements, function(element) {
49010
+ if (!element.parent || isLabel$2(element)) {
49011
+ return;
49012
+ }
48934
49013
 
48935
- CommandStack.prototype._popAction = function() {
48936
- const execution = this._currentExecution,
48937
- trigger = execution.trigger,
48938
- actions = execution.actions,
48939
- dirty = execution.dirty;
49014
+ // handle connections separately
49015
+ if (isConnection$7(element)) {
49016
+ connections.push(element);
48940
49017
 
48941
- actions.pop();
49018
+ return;
49019
+ }
48942
49020
 
48943
- if (!actions.length) {
48944
- this._eventBus.fire('elements.changed', { elements: uniqueBy('id', dirty.reverse()) });
49021
+ var shapeStart = element[ axis ],
49022
+ shapeEnd = shapeStart + element[ AXIS_TO_DIMENSION[ axis ] ];
48945
49023
 
48946
- dirty.length = 0;
49024
+ // handle attachers separately
49025
+ if (isAttacher$1(element)
49026
+ && ((delta > 0 && getMid(element)[ axis ] > start)
49027
+ || (delta < 0 && getMid(element)[ axis ] < start))) {
49028
+ attachers.push(element);
48947
49029
 
48948
- this._fire('changed', { trigger: trigger });
49030
+ return;
49031
+ }
48949
49032
 
48950
- execution.trigger = null;
48951
- }
48952
- };
49033
+ // move shape if its start is after space tool
49034
+ if ((delta > 0 && shapeStart > start)
49035
+ || (delta < 0 && shapeEnd < start)) {
49036
+ moveShape(element);
48953
49037
 
49038
+ return;
49039
+ }
48954
49040
 
48955
- CommandStack.prototype._markDirty = function(elements) {
48956
- const execution = this._currentExecution;
49041
+ // resize shape if it's resizable and its start is before and its end is after space tool
49042
+ if (shapeStart < start
49043
+ && shapeEnd > start
49044
+ && rules.allowed('shape.resize', { shape: element })
49045
+ ) {
49046
+ resizeShape(element);
48957
49047
 
48958
- if (!elements) {
48959
- return;
48960
- }
49048
+ return;
49049
+ }
49050
+ });
48961
49051
 
48962
- elements = isArray$3(elements) ? elements : [ elements ];
49052
+ // move attacher if its host is moving
49053
+ forEach$1(movingShapes, function(shape) {
49054
+ var attachers = shape.attachers;
48963
49055
 
48964
- execution.dirty = execution.dirty.concat(elements);
48965
- };
49056
+ if (attachers) {
49057
+ forEach$1(attachers, function(attacher) {
49058
+ moveShape(attacher);
49059
+ });
49060
+ }
49061
+ });
48966
49062
 
49063
+ var allShapes = movingShapes.concat(resizingShapes);
48967
49064
 
48968
- CommandStack.prototype._executedAction = function(action, redo) {
48969
- const stackIdx = ++this._stackIdx;
49065
+ // move attacher if its mid is after space tool and its host is moving or resizing
49066
+ forEach$1(attachers, function(attacher) {
49067
+ var host = attacher.host;
48970
49068
 
48971
- if (!redo) {
48972
- this._stack.splice(stackIdx, this._stack.length, action);
48973
- }
48974
- };
49069
+ if (includes$3(allShapes, host)) {
49070
+ moveShape(attacher);
49071
+ }
49072
+ });
48975
49073
 
49074
+ allShapes = movingShapes.concat(resizingShapes);
48976
49075
 
48977
- CommandStack.prototype._revertedAction = function(action) {
48978
- this._stackIdx--;
48979
- };
49076
+ // move external label if its label target's (connection) source and target are moving
49077
+ forEach$1(connections, function(connection) {
49078
+ var source = connection.source,
49079
+ target = connection.target,
49080
+ label = connection.label;
48980
49081
 
49082
+ if (includes$3(allShapes, source)
49083
+ && includes$3(allShapes, target)
49084
+ && label) {
49085
+ moveShape(label);
49086
+ }
49087
+ });
48981
49088
 
48982
- CommandStack.prototype._getHandler = function(command) {
48983
- return this._handlerMap[command];
49089
+ return {
49090
+ movingShapes: movingShapes,
49091
+ resizingShapes: resizingShapes
49092
+ };
48984
49093
  };
48985
49094
 
48986
- CommandStack.prototype._setHandler = function(command, handler) {
48987
- if (!command || !handler) {
48988
- throw new Error('command and handler required');
48989
- }
49095
+ SpaceTool.prototype.toggle = function() {
48990
49096
 
48991
- if (this._handlerMap[command]) {
48992
- throw new Error('overriding handler for command <' + command + '>');
49097
+ if (this.isActive()) {
49098
+ return this._dragging.cancel();
48993
49099
  }
48994
49100
 
48995
- this._handlerMap[command] = handler;
48996
- };
49101
+ var mouseEvent = this._mouse.getLastMoveEvent();
48997
49102
 
48998
- var CommandModule = {
48999
- commandStack: [ 'type', CommandStack ]
49103
+ this.activateSelection(mouseEvent, !!mouseEvent);
49000
49104
  };
49001
49105
 
49002
- // document wide unique tooltip ids
49003
- var ids = new IdGenerator('tt');
49004
-
49106
+ SpaceTool.prototype.isActive = function() {
49107
+ var context = this._dragging.context();
49005
49108
 
49006
- function createRoot(parentNode) {
49007
- var root = domify$1(
49008
- '<div class="djs-tooltip-container" />'
49009
- );
49109
+ if (context) {
49110
+ return /^spaceTool/.test(context.prefix);
49111
+ }
49010
49112
 
49011
- assign(root, {
49012
- position: 'absolute',
49013
- width: '0',
49014
- height: '0'
49015
- });
49113
+ return false;
49114
+ };
49016
49115
 
49017
- parentNode.insertBefore(root, parentNode.firstChild);
49116
+ // helpers //////////
49018
49117
 
49019
- return root;
49118
+ function addPadding(trbl) {
49119
+ return {
49120
+ top: trbl.top - PADDING,
49121
+ right: trbl.right + PADDING,
49122
+ bottom: trbl.bottom + PADDING,
49123
+ left: trbl.left - PADDING
49124
+ };
49020
49125
  }
49021
49126
 
49127
+ function ensureConstraints(event) {
49128
+ var context = event.context,
49129
+ spaceToolConstraints = context.spaceToolConstraints;
49022
49130
 
49023
- function setPosition(el, x, y) {
49024
- assign(el, { left: x + 'px', top: y + 'px' });
49025
- }
49131
+ if (!spaceToolConstraints) {
49132
+ return;
49133
+ }
49026
49134
 
49027
- function setVisible(el, visible) {
49028
- el.style.display = visible === false ? 'none' : '';
49029
- }
49135
+ var x, y;
49030
49136
 
49137
+ if (isNumber(spaceToolConstraints.left)) {
49138
+ x = Math.max(event.x, spaceToolConstraints.left);
49031
49139
 
49032
- var tooltipClass = 'djs-tooltip',
49033
- tooltipSelector = '.' + tooltipClass;
49140
+ event.dx = event.dx + x - event.x;
49141
+ event.x = x;
49142
+ }
49034
49143
 
49035
- /**
49036
- * A service that allows users to render tool tips on the diagram.
49037
- *
49038
- * The tooltip service will take care of updating the tooltip positioning
49039
- * during navigation + zooming.
49040
- *
49041
- * @example
49042
- *
49043
- * ```javascript
49044
- *
49045
- * // add a pink badge on the top left of the shape
49046
- * tooltips.add({
49047
- * position: {
49048
- * x: 50,
49049
- * y: 100
49050
- * },
49051
- * html: '<div style="width: 10px; background: fuchsia; color: white;">0</div>'
49052
- * });
49053
- *
49054
- * // or with optional life span
49055
- * tooltips.add({
49056
- * position: {
49057
- * top: -5,
49058
- * left: -5
49059
- * },
49060
- * html: '<div style="width: 10px; background: fuchsia; color: white;">0</div>',
49061
- * ttl: 2000
49062
- * });
49063
- *
49064
- * // remove a tool tip
49065
- * var id = tooltips.add(...);
49066
- * tooltips.remove(id);
49067
- * ```
49068
- *
49069
- * @param {EventBus} eventBus
49070
- * @param {Canvas} canvas
49071
- */
49072
- function Tooltips(eventBus, canvas) {
49144
+ if (isNumber(spaceToolConstraints.right)) {
49145
+ x = Math.min(event.x, spaceToolConstraints.right);
49073
49146
 
49074
- this._eventBus = eventBus;
49075
- this._canvas = canvas;
49147
+ event.dx = event.dx + x - event.x;
49148
+ event.x = x;
49149
+ }
49076
49150
 
49077
- this._ids = ids;
49151
+ if (isNumber(spaceToolConstraints.top)) {
49152
+ y = Math.max(event.y, spaceToolConstraints.top);
49078
49153
 
49079
- this._tooltipDefaults = {
49080
- show: {
49081
- minZoom: 0.7,
49082
- maxZoom: 5.0
49083
- }
49084
- };
49154
+ event.dy = event.dy + y - event.y;
49155
+ event.y = y;
49156
+ }
49085
49157
 
49086
- /**
49087
- * Mapping tooltipId -> tooltip
49088
- */
49089
- this._tooltips = {};
49158
+ if (isNumber(spaceToolConstraints.bottom)) {
49159
+ y = Math.min(event.y, spaceToolConstraints.bottom);
49090
49160
 
49091
- // root html element for all tooltips
49092
- this._tooltipRoot = createRoot(canvas.getContainer());
49161
+ event.dy = event.dy + y - event.y;
49162
+ event.y = y;
49163
+ }
49164
+ }
49093
49165
 
49166
+ function getSpaceToolConstraints(elements, axis, direction, start, minDimensions) {
49167
+ var movingShapes = elements.movingShapes,
49168
+ resizingShapes = elements.resizingShapes;
49094
49169
 
49095
- var self = this;
49170
+ if (!resizingShapes.length) {
49171
+ return;
49172
+ }
49096
49173
 
49097
- delegate.bind(this._tooltipRoot, tooltipSelector, 'mousedown', function(event) {
49098
- event.stopPropagation();
49099
- });
49174
+ var spaceToolConstraints = {},
49175
+ min,
49176
+ max;
49100
49177
 
49101
- delegate.bind(this._tooltipRoot, tooltipSelector, 'mouseover', function(event) {
49102
- self.trigger('mouseover', event);
49103
- });
49178
+ forEach$1(resizingShapes, function(resizingShape) {
49179
+ var attachers = resizingShape.attachers,
49180
+ children = resizingShape.children;
49104
49181
 
49105
- delegate.bind(this._tooltipRoot, tooltipSelector, 'mouseout', function(event) {
49106
- self.trigger('mouseout', event);
49107
- });
49182
+ var resizingShapeBBox = asTRBL(resizingShape);
49108
49183
 
49109
- this._init();
49110
- }
49184
+ // find children that are not moving or resizing
49185
+ var nonMovingResizingChildren = filter(children, function(child) {
49186
+ return !isConnection$7(child) &&
49187
+ !isLabel$2(child) &&
49188
+ !includes$3(movingShapes, child) &&
49189
+ !includes$3(resizingShapes, child);
49190
+ });
49111
49191
 
49192
+ // find children that are moving
49193
+ var movingChildren = filter(children, function(child) {
49194
+ return !isConnection$7(child) && !isLabel$2(child) && includes$3(movingShapes, child);
49195
+ });
49112
49196
 
49113
- Tooltips.$inject = [ 'eventBus', 'canvas' ];
49197
+ var minOrMax,
49198
+ nonMovingResizingChildrenBBox,
49199
+ movingChildrenBBox,
49200
+ movingAttachers = [],
49201
+ nonMovingAttachers = [],
49202
+ movingAttachersBBox,
49203
+ movingAttachersConstraint,
49204
+ nonMovingAttachersBBox,
49205
+ nonMovingAttachersConstraint;
49114
49206
 
49207
+ if (nonMovingResizingChildren.length) {
49208
+ nonMovingResizingChildrenBBox = addPadding(asTRBL(getBBox(nonMovingResizingChildren)));
49115
49209
 
49116
- /**
49117
- * Adds a HTML tooltip to the diagram
49118
- *
49119
- * @param {Object} tooltip the tooltip configuration
49120
- *
49121
- * @param {string|DOMElement} tooltip.html html element to use as an tooltip
49122
- * @param {Object} [tooltip.show] show configuration
49123
- * @param {number} [tooltip.show.minZoom] minimal zoom level to show the tooltip
49124
- * @param {number} [tooltip.show.maxZoom] maximum zoom level to show the tooltip
49125
- * @param {Object} tooltip.position where to attach the tooltip
49126
- * @param {number} [tooltip.position.left] relative to element bbox left attachment
49127
- * @param {number} [tooltip.position.top] relative to element bbox top attachment
49128
- * @param {number} [tooltip.position.bottom] relative to element bbox bottom attachment
49129
- * @param {number} [tooltip.position.right] relative to element bbox right attachment
49130
- * @param {number} [tooltip.timeout=-1]
49131
- *
49132
- * @return {string} id that may be used to reference the tooltip for update or removal
49133
- */
49134
- Tooltips.prototype.add = function(tooltip) {
49210
+ minOrMax = start -
49211
+ resizingShapeBBox[ DIRECTION_TO_TRBL[ direction ] ] +
49212
+ nonMovingResizingChildrenBBox[ DIRECTION_TO_TRBL[ direction ] ];
49135
49213
 
49136
- if (!tooltip.position) {
49137
- throw new Error('must specifiy tooltip position');
49138
- }
49214
+ if (direction === 'n') {
49215
+ spaceToolConstraints.bottom = max = isNumber(max) ? Math.min(max, minOrMax) : minOrMax;
49216
+ } else if (direction === 'w') {
49217
+ spaceToolConstraints.right = max = isNumber(max) ? Math.min(max, minOrMax) : minOrMax;
49218
+ } else if (direction === 's') {
49219
+ spaceToolConstraints.top = min = isNumber(min) ? Math.max(min, minOrMax) : minOrMax;
49220
+ } else if (direction === 'e') {
49221
+ spaceToolConstraints.left = min = isNumber(min) ? Math.max(min, minOrMax) : minOrMax;
49222
+ }
49223
+ }
49139
49224
 
49140
- if (!tooltip.html) {
49141
- throw new Error('must specifiy tooltip html');
49142
- }
49225
+ if (movingChildren.length) {
49226
+ movingChildrenBBox = addPadding(asTRBL(getBBox(movingChildren)));
49143
49227
 
49144
- var id = this._ids.next();
49228
+ minOrMax = start -
49229
+ movingChildrenBBox[ DIRECTION_TO_TRBL[ DIRECTION_TO_OPPOSITE[ direction ] ] ] +
49230
+ resizingShapeBBox[ DIRECTION_TO_TRBL[ DIRECTION_TO_OPPOSITE[ direction ] ] ];
49145
49231
 
49146
- tooltip = assign$1({}, this._tooltipDefaults, tooltip, {
49147
- id: id
49148
- });
49232
+ if (direction === 'n') {
49233
+ spaceToolConstraints.bottom = max = isNumber(max) ? Math.min(max, minOrMax) : minOrMax;
49234
+ } else if (direction === 'w') {
49235
+ spaceToolConstraints.right = max = isNumber(max) ? Math.min(max, minOrMax) : minOrMax;
49236
+ } else if (direction === 's') {
49237
+ spaceToolConstraints.top = min = isNumber(min) ? Math.max(min, minOrMax) : minOrMax;
49238
+ } else if (direction === 'e') {
49239
+ spaceToolConstraints.left = min = isNumber(min) ? Math.max(min, minOrMax) : minOrMax;
49240
+ }
49241
+ }
49149
49242
 
49150
- this._addTooltip(tooltip);
49243
+ if (attachers && attachers.length) {
49244
+ attachers.forEach(function(attacher) {
49245
+ if (includes$3(movingShapes, attacher)) {
49246
+ movingAttachers.push(attacher);
49247
+ } else {
49248
+ nonMovingAttachers.push(attacher);
49249
+ }
49250
+ });
49151
49251
 
49152
- if (tooltip.timeout) {
49153
- this.setTimeout(tooltip);
49154
- }
49252
+ if (movingAttachers.length) {
49253
+ movingAttachersBBox = asTRBL(getBBox(movingAttachers.map(getMid)));
49155
49254
 
49156
- return id;
49157
- };
49255
+ movingAttachersConstraint = resizingShapeBBox[ DIRECTION_TO_TRBL[ DIRECTION_TO_OPPOSITE[ direction ] ] ]
49256
+ - (movingAttachersBBox[ DIRECTION_TO_TRBL[ DIRECTION_TO_OPPOSITE[ direction ] ] ] - start);
49257
+ }
49158
49258
 
49159
- Tooltips.prototype.trigger = function(action, event) {
49259
+ if (nonMovingAttachers.length) {
49260
+ nonMovingAttachersBBox = asTRBL(getBBox(nonMovingAttachers.map(getMid)));
49160
49261
 
49161
- var node = event.delegateTarget || event.target;
49262
+ nonMovingAttachersConstraint = nonMovingAttachersBBox[ DIRECTION_TO_TRBL[ direction ] ]
49263
+ - (resizingShapeBBox[ DIRECTION_TO_TRBL[ direction ] ] - start);
49264
+ }
49162
49265
 
49163
- var tooltip = this.get(attr$1(node, 'data-tooltip-id'));
49266
+ if (direction === 'n') {
49267
+ minOrMax = Math.min(movingAttachersConstraint || Infinity, nonMovingAttachersConstraint || Infinity);
49164
49268
 
49165
- if (!tooltip) {
49166
- return;
49167
- }
49269
+ spaceToolConstraints.bottom = max = isNumber(max) ? Math.min(max, minOrMax) : minOrMax;
49270
+ } else if (direction === 'w') {
49271
+ minOrMax = Math.min(movingAttachersConstraint || Infinity, nonMovingAttachersConstraint || Infinity);
49168
49272
 
49169
- if (action === 'mouseover' && tooltip.timeout) {
49170
- this.clearTimeout(tooltip);
49171
- }
49273
+ spaceToolConstraints.right = max = isNumber(max) ? Math.min(max, minOrMax) : minOrMax;
49274
+ } else if (direction === 's') {
49275
+ minOrMax = Math.max(movingAttachersConstraint || -Infinity, nonMovingAttachersConstraint || -Infinity);
49172
49276
 
49173
- if (action === 'mouseout' && tooltip.timeout) {
49277
+ spaceToolConstraints.top = min = isNumber(min) ? Math.max(min, minOrMax) : minOrMax;
49278
+ } else if (direction === 'e') {
49279
+ minOrMax = Math.max(movingAttachersConstraint || -Infinity, nonMovingAttachersConstraint || -Infinity);
49174
49280
 
49175
- // cut timeout after mouse out
49176
- tooltip.timeout = 1000;
49281
+ spaceToolConstraints.left = min = isNumber(min) ? Math.max(min, minOrMax) : minOrMax;
49282
+ }
49283
+ }
49177
49284
 
49178
- this.setTimeout(tooltip);
49179
- }
49180
- };
49285
+ var resizingShapeMinDimensions = minDimensions && minDimensions[ resizingShape.id ];
49181
49286
 
49182
- /**
49183
- * Get a tooltip with the given id
49184
- *
49185
- * @param {string} id
49186
- */
49187
- Tooltips.prototype.get = function(id) {
49287
+ if (resizingShapeMinDimensions) {
49188
49288
 
49189
- if (typeof id !== 'string') {
49190
- id = id.id;
49191
- }
49289
+ if (direction === 'n') {
49290
+ minOrMax = start +
49291
+ resizingShape[ AXIS_TO_DIMENSION [ axis ] ] -
49292
+ resizingShapeMinDimensions[ AXIS_TO_DIMENSION[ axis ] ];
49192
49293
 
49193
- return this._tooltips[id];
49194
- };
49294
+ spaceToolConstraints.bottom = max = isNumber(max) ? Math.min(max, minOrMax) : minOrMax;
49295
+ } else if (direction === 'w') {
49296
+ minOrMax = start +
49297
+ resizingShape[ AXIS_TO_DIMENSION [ axis ] ] -
49298
+ resizingShapeMinDimensions[ AXIS_TO_DIMENSION[ axis ] ];
49195
49299
 
49196
- Tooltips.prototype.clearTimeout = function(tooltip) {
49300
+ spaceToolConstraints.right = max = isNumber(max) ? Math.min(max, minOrMax) : minOrMax;
49301
+ } else if (direction === 's') {
49302
+ minOrMax = start -
49303
+ resizingShape[ AXIS_TO_DIMENSION [ axis ] ] +
49304
+ resizingShapeMinDimensions[ AXIS_TO_DIMENSION[ axis ] ];
49197
49305
 
49198
- tooltip = this.get(tooltip);
49306
+ spaceToolConstraints.top = min = isNumber(min) ? Math.max(min, minOrMax) : minOrMax;
49307
+ } else if (direction === 'e') {
49308
+ minOrMax = start -
49309
+ resizingShape[ AXIS_TO_DIMENSION [ axis ] ] +
49310
+ resizingShapeMinDimensions[ AXIS_TO_DIMENSION[ axis ] ];
49199
49311
 
49200
- if (!tooltip) {
49201
- return;
49202
- }
49312
+ spaceToolConstraints.left = min = isNumber(min) ? Math.max(min, minOrMax) : minOrMax;
49313
+ }
49314
+ }
49315
+ });
49203
49316
 
49204
- var removeTimer = tooltip.removeTimer;
49317
+ return spaceToolConstraints;
49318
+ }
49205
49319
 
49206
- if (removeTimer) {
49207
- clearTimeout(removeTimer);
49208
- tooltip.removeTimer = null;
49209
- }
49210
- };
49320
+ function includes$3(array, item) {
49321
+ return array.indexOf(item) !== -1;
49322
+ }
49211
49323
 
49212
- Tooltips.prototype.setTimeout = function(tooltip) {
49324
+ function isAttacher$1(element) {
49325
+ return !!element.host;
49326
+ }
49213
49327
 
49214
- tooltip = this.get(tooltip);
49328
+ function isConnection$7(element) {
49329
+ return !!element.waypoints;
49330
+ }
49215
49331
 
49216
- if (!tooltip) {
49217
- return;
49218
- }
49332
+ function isLabel$2(element) {
49333
+ return !!element.labelTarget;
49334
+ }
49219
49335
 
49220
- this.clearTimeout(tooltip);
49336
+ var MARKER_DRAGGING$1 = 'djs-dragging',
49337
+ MARKER_RESIZING = 'djs-resizing';
49221
49338
 
49222
- var self = this;
49339
+ var LOW_PRIORITY$5 = 250;
49340
+
49341
+ var max = Math.max;
49223
49342
 
49224
- tooltip.removeTimer = setTimeout(function() {
49225
- self.remove(tooltip);
49226
- }, tooltip.timeout);
49227
- };
49228
49343
 
49229
49344
  /**
49230
- * Remove an tooltip with the given id
49345
+ * Provides previews for selecting/moving/resizing shapes when creating/removing space.
49231
49346
  *
49232
- * @param {string} id
49347
+ * @param {EventBus} eventBus
49348
+ * @param {ElementRegistry} elementRegistry
49349
+ * @param {Canvas} canvas
49350
+ * @param {Styles} styles
49233
49351
  */
49234
- Tooltips.prototype.remove = function(id) {
49235
-
49236
- var tooltip = this.get(id);
49237
-
49238
- if (tooltip) {
49239
- remove$2(tooltip.html);
49240
- remove$2(tooltip.htmlContainer);
49352
+ function SpaceToolPreview(
49353
+ eventBus, elementRegistry, canvas,
49354
+ styles, previewSupport) {
49241
49355
 
49242
- delete tooltip.htmlContainer;
49356
+ function addPreviewGfx(collection, dragGroup) {
49357
+ forEach$1(collection, function(element) {
49358
+ previewSupport.addDragger(element, dragGroup);
49243
49359
 
49244
- delete this._tooltips[tooltip.id];
49360
+ canvas.addMarker(element, MARKER_DRAGGING$1);
49361
+ });
49245
49362
  }
49246
- };
49247
49363
 
49364
+ // add crosshair
49365
+ eventBus.on('spaceTool.selection.start', function(event) {
49366
+ var space = canvas.getLayer('space'),
49367
+ context = event.context;
49248
49368
 
49249
- Tooltips.prototype.show = function() {
49250
- setVisible(this._tooltipRoot);
49251
- };
49369
+ var orientation = {
49370
+ x: 'M 0,-10000 L 0,10000',
49371
+ y: 'M -10000,0 L 10000,0'
49372
+ };
49252
49373
 
49374
+ var crosshairGroup = create$1('g');
49375
+ attr(crosshairGroup, styles.cls('djs-crosshair-group', [ 'no-events' ]));
49253
49376
 
49254
- Tooltips.prototype.hide = function() {
49255
- setVisible(this._tooltipRoot, false);
49256
- };
49377
+ append(space, crosshairGroup);
49257
49378
 
49379
+ // horizontal path
49380
+ var pathX = create$1('path');
49381
+ attr(pathX, 'd', orientation.x);
49382
+ classes(pathX).add('djs-crosshair');
49258
49383
 
49259
- Tooltips.prototype._updateRoot = function(viewbox) {
49260
- var a = viewbox.scale || 1;
49261
- var d = viewbox.scale || 1;
49384
+ append(crosshairGroup, pathX);
49262
49385
 
49263
- var matrix = 'matrix(' + a + ',0,0,' + d + ',' + (-1 * viewbox.x * a) + ',' + (-1 * viewbox.y * d) + ')';
49386
+ // vertical path
49387
+ var pathY = create$1('path');
49388
+ attr(pathY, 'd', orientation.y);
49389
+ classes(pathY).add('djs-crosshair');
49264
49390
 
49265
- this._tooltipRoot.style.transform = matrix;
49266
- this._tooltipRoot.style['-ms-transform'] = matrix;
49267
- };
49391
+ append(crosshairGroup, pathY);
49268
49392
 
49393
+ context.crosshairGroup = crosshairGroup;
49394
+ });
49269
49395
 
49270
- Tooltips.prototype._addTooltip = function(tooltip) {
49396
+ // update crosshair
49397
+ eventBus.on('spaceTool.selection.move', function(event) {
49398
+ var crosshairGroup = event.context.crosshairGroup;
49271
49399
 
49272
- var id = tooltip.id,
49273
- html = tooltip.html,
49274
- htmlContainer,
49275
- tooltipRoot = this._tooltipRoot;
49400
+ translate$2(crosshairGroup, event.x, event.y);
49401
+ });
49276
49402
 
49277
- // unwrap jquery (for those who need it)
49278
- if (html.get && html.constructor.prototype.jquery) {
49279
- html = html.get(0);
49280
- }
49403
+ // remove crosshair
49404
+ eventBus.on('spaceTool.selection.cleanup', function(event) {
49405
+ var context = event.context,
49406
+ crosshairGroup = context.crosshairGroup;
49281
49407
 
49282
- // create proper html elements from
49283
- // tooltip HTML strings
49284
- if (isString(html)) {
49285
- html = domify$1(html);
49286
- }
49408
+ if (crosshairGroup) {
49409
+ remove$1(crosshairGroup);
49410
+ }
49411
+ });
49287
49412
 
49288
- htmlContainer = domify$1('<div data-tooltip-id="' + id + '" class="' + tooltipClass + '">');
49289
- assign(htmlContainer, { position: 'absolute' });
49413
+ // add and update move/resize previews
49414
+ eventBus.on('spaceTool.move', LOW_PRIORITY$5, function(event) {
49290
49415
 
49291
- htmlContainer.appendChild(html);
49416
+ var context = event.context,
49417
+ line = context.line,
49418
+ axis = context.axis,
49419
+ movingShapes = context.movingShapes,
49420
+ resizingShapes = context.resizingShapes;
49292
49421
 
49293
- if (tooltip.type) {
49294
- classes$1(htmlContainer).add('djs-tooltip-' + tooltip.type);
49295
- }
49422
+ if (!context.initialized) {
49423
+ return;
49424
+ }
49296
49425
 
49297
- if (tooltip.className) {
49298
- classes$1(htmlContainer).add(tooltip.className);
49299
- }
49426
+ if (!context.dragGroup) {
49427
+ var spaceLayer = canvas.getLayer('space');
49300
49428
 
49301
- tooltip.htmlContainer = htmlContainer;
49429
+ line = create$1('path');
49430
+ attr(line, 'd', 'M0,0 L0,0');
49431
+ classes(line).add('djs-crosshair');
49302
49432
 
49303
- tooltipRoot.appendChild(htmlContainer);
49433
+ append(spaceLayer, line);
49304
49434
 
49305
- this._tooltips[id] = tooltip;
49435
+ context.line = line;
49306
49436
 
49307
- this._updateTooltip(tooltip);
49308
- };
49437
+ var dragGroup = create$1('g');
49438
+ attr(dragGroup, styles.cls('djs-drag-group', [ 'no-events' ]));
49309
49439
 
49440
+ append(canvas.getActiveLayer(), dragGroup);
49310
49441
 
49311
- Tooltips.prototype._updateTooltip = function(tooltip) {
49442
+ // shapes
49443
+ addPreviewGfx(movingShapes, dragGroup);
49312
49444
 
49313
- var position = tooltip.position,
49314
- htmlContainer = tooltip.htmlContainer;
49445
+ // connections
49446
+ var movingConnections = context.movingConnections = elementRegistry.filter(function(element) {
49447
+ var sourceIsMoving = false;
49315
49448
 
49316
- // update overlay html based on tooltip x, y
49449
+ forEach$1(movingShapes, function(shape) {
49450
+ forEach$1(shape.outgoing, function(connection) {
49451
+ if (element === connection) {
49452
+ sourceIsMoving = true;
49453
+ }
49454
+ });
49455
+ });
49317
49456
 
49318
- setPosition(htmlContainer, position.x, position.y);
49319
- };
49457
+ var targetIsMoving = false;
49320
49458
 
49459
+ forEach$1(movingShapes, function(shape) {
49460
+ forEach$1(shape.incoming, function(connection) {
49461
+ if (element === connection) {
49462
+ targetIsMoving = true;
49463
+ }
49464
+ });
49465
+ });
49321
49466
 
49322
- Tooltips.prototype._updateTooltipVisibilty = function(viewbox) {
49467
+ var sourceIsResizing = false;
49323
49468
 
49324
- forEach$1(this._tooltips, function(tooltip) {
49325
- var show = tooltip.show,
49326
- htmlContainer = tooltip.htmlContainer,
49327
- visible = true;
49469
+ forEach$1(resizingShapes, function(shape) {
49470
+ forEach$1(shape.outgoing, function(connection) {
49471
+ if (element === connection) {
49472
+ sourceIsResizing = true;
49473
+ }
49474
+ });
49475
+ });
49328
49476
 
49329
- if (show) {
49330
- if (show.minZoom > viewbox.scale ||
49331
- show.maxZoom < viewbox.scale) {
49332
- visible = false;
49333
- }
49477
+ var targetIsResizing = false;
49334
49478
 
49335
- setVisible(htmlContainer, visible);
49336
- }
49337
- });
49338
- };
49479
+ forEach$1(resizingShapes, function(shape) {
49480
+ forEach$1(shape.incoming, function(connection) {
49481
+ if (element === connection) {
49482
+ targetIsResizing = true;
49483
+ }
49484
+ });
49485
+ });
49339
49486
 
49340
- Tooltips.prototype._init = function() {
49487
+ return isConnection$6(element)
49488
+ && (sourceIsMoving || sourceIsResizing)
49489
+ && (targetIsMoving || targetIsResizing);
49490
+ });
49341
49491
 
49342
- var self = this;
49343
49492
 
49344
- // scroll/zoom integration
49493
+ addPreviewGfx(movingConnections, dragGroup);
49345
49494
 
49346
- function updateViewbox(viewbox) {
49347
- self._updateRoot(viewbox);
49348
- self._updateTooltipVisibilty(viewbox);
49495
+ context.dragGroup = dragGroup;
49496
+ }
49349
49497
 
49350
- self.show();
49351
- }
49498
+ if (!context.frameGroup) {
49499
+ var frameGroup = create$1('g');
49500
+ attr(frameGroup, styles.cls('djs-frame-group', [ 'no-events' ]));
49352
49501
 
49353
- this._eventBus.on('canvas.viewbox.changing', function(event) {
49354
- self.hide();
49355
- });
49502
+ append(canvas.getActiveLayer(), frameGroup);
49356
49503
 
49357
- this._eventBus.on('canvas.viewbox.changed', function(event) {
49358
- updateViewbox(event.viewbox);
49359
- });
49360
- };
49504
+ var frames = [];
49361
49505
 
49362
- var TooltipsModule = {
49363
- __init__: [ 'tooltips' ],
49364
- tooltips: [ 'type', Tooltips ]
49365
- };
49506
+ forEach$1(resizingShapes, function(shape) {
49507
+ var frame = previewSupport.addFrame(shape, frameGroup);
49366
49508
 
49367
- /**
49368
- * Remove from the beginning of a collection until it is empty.
49369
- *
49370
- * This is a null-safe operation that ensures elements
49371
- * are being removed from the given collection until the
49372
- * collection is empty.
49373
- *
49374
- * The implementation deals with the fact that a remove operation
49375
- * may touch, i.e. remove multiple elements in the collection
49376
- * at a time.
49377
- *
49378
- * @param {Array<Object>} [collection]
49379
- * @param {Function} removeFn
49380
- *
49381
- * @return {Array<Object>} the cleared collection
49382
- */
49383
- function saveClear(collection, removeFn) {
49509
+ var initialBounds = frame.getBBox();
49384
49510
 
49385
- if (typeof removeFn !== 'function') {
49386
- throw new Error('removeFn iterator must be a function');
49387
- }
49511
+ frames.push({
49512
+ element: frame,
49513
+ initialBounds: initialBounds
49514
+ });
49388
49515
 
49389
- if (!collection) {
49390
- return;
49391
- }
49516
+ canvas.addMarker(shape, MARKER_RESIZING);
49517
+ });
49392
49518
 
49393
- var e;
49519
+ context.frameGroup = frameGroup;
49520
+ context.frames = frames;
49521
+ }
49394
49522
 
49395
- while ((e = collection[0])) {
49396
- removeFn(e);
49397
- }
49523
+ var orientation = {
49524
+ x: 'M' + event.x + ', -10000 L' + event.x + ', 10000',
49525
+ y: 'M -10000, ' + event.y + ' L 10000, ' + event.y
49526
+ };
49398
49527
 
49399
- return collection;
49400
- }
49528
+ attr(line, { d: orientation[ axis ] });
49401
49529
 
49402
- var LOW_PRIORITY$6 = 250,
49403
- HIGH_PRIORITY$5 = 1400;
49530
+ var opposite = { x: 'y', y: 'x' };
49531
+ var delta = { x: event.dx, y: event.dy };
49532
+ delta[ opposite[ context.axis ] ] = 0;
49404
49533
 
49534
+ // update move previews
49535
+ translate$2(context.dragGroup, delta.x, delta.y);
49405
49536
 
49406
- /**
49407
- * A handler that makes sure labels are properly moved with
49408
- * their label targets.
49409
- *
49410
- * @param {didi.Injector} injector
49411
- * @param {EventBus} eventBus
49412
- * @param {Modeling} modeling
49413
- */
49414
- function LabelSupport(injector, eventBus, modeling) {
49537
+ // update resize previews
49538
+ forEach$1(context.frames, function(frame) {
49539
+ var element = frame.element,
49540
+ initialBounds = frame.initialBounds,
49541
+ width,
49542
+ height;
49415
49543
 
49416
- CommandInterceptor.call(this, eventBus);
49544
+ if (context.direction === 'e') {
49545
+ attr(element, {
49546
+ width: max(initialBounds.width + delta.x, 5)
49547
+ });
49548
+ } else {
49549
+ width = max(initialBounds.width - delta.x, 5);
49417
49550
 
49418
- var movePreview = injector.get('movePreview', false);
49551
+ attr(element, {
49552
+ width: width,
49553
+ x: initialBounds.x + initialBounds.width - width
49554
+ });
49555
+ }
49419
49556
 
49420
- // remove labels from the collection that are being
49421
- // moved with other elements anyway
49422
- eventBus.on('shape.move.start', HIGH_PRIORITY$5, function(e) {
49557
+ if (context.direction === 's') {
49558
+ attr(element, {
49559
+ height: max(initialBounds.height + delta.y, 5)
49560
+ });
49561
+ } else {
49562
+ height = max(initialBounds.height - delta.y, 5);
49423
49563
 
49424
- var context = e.context,
49425
- shapes = context.shapes,
49426
- validatedShapes = context.validatedShapes;
49564
+ attr(element, {
49565
+ height: height,
49566
+ y: initialBounds.y + initialBounds.height - height
49567
+ });
49568
+ }
49569
+ });
49427
49570
 
49428
- context.shapes = removeLabels(shapes);
49429
- context.validatedShapes = removeLabels(validatedShapes);
49430
49571
  });
49431
49572
 
49432
- // add labels to visual's group
49433
- movePreview && eventBus.on('shape.move.start', LOW_PRIORITY$6, function(e) {
49434
-
49435
- var context = e.context,
49436
- shapes = context.shapes;
49437
-
49438
- var labels = [];
49439
-
49440
- forEach$1(shapes, function(element) {
49441
-
49442
- forEach$1(element.labels, function(label) {
49573
+ // remove move/resize previews
49574
+ eventBus.on('spaceTool.cleanup', function(event) {
49443
49575
 
49444
- if (!label.hidden && context.shapes.indexOf(label) === -1) {
49445
- labels.push(label);
49446
- }
49576
+ var context = event.context,
49577
+ movingShapes = context.movingShapes,
49578
+ movingConnections = context.movingConnections,
49579
+ resizingShapes = context.resizingShapes,
49580
+ line = context.line,
49581
+ dragGroup = context.dragGroup,
49582
+ frameGroup = context.frameGroup;
49447
49583
 
49448
- if (element.labelTarget) {
49449
- labels.push(element);
49450
- }
49451
- });
49584
+ // moving shapes
49585
+ forEach$1(movingShapes, function(shape) {
49586
+ canvas.removeMarker(shape, MARKER_DRAGGING$1);
49452
49587
  });
49453
49588
 
49454
- forEach$1(labels, function(label) {
49455
- movePreview.makeDraggable(context, label, true);
49589
+ // moving connections
49590
+ forEach$1(movingConnections, function(connection) {
49591
+ canvas.removeMarker(connection, MARKER_DRAGGING$1);
49456
49592
  });
49457
49593
 
49458
- });
49459
-
49460
- // add all labels to move closure
49461
- this.preExecuted('elements.move', HIGH_PRIORITY$5, function(e) {
49462
- var context = e.context,
49463
- closure = context.closure,
49464
- enclosedElements = closure.enclosedElements;
49465
-
49466
- var enclosedLabels = [];
49467
-
49468
- // find labels that are not part of
49469
- // move closure yet and add them
49470
- forEach$1(enclosedElements, function(element) {
49471
- forEach$1(element.labels, function(label) {
49594
+ if (dragGroup) {
49595
+ remove$1(line);
49596
+ remove$1(dragGroup);
49597
+ }
49472
49598
 
49473
- if (!enclosedElements[label.id]) {
49474
- enclosedLabels.push(label);
49475
- }
49476
- });
49599
+ forEach$1(resizingShapes, function(shape) {
49600
+ canvas.removeMarker(shape, MARKER_RESIZING);
49477
49601
  });
49478
49602
 
49479
- closure.addAll(enclosedLabels);
49603
+ if (frameGroup) {
49604
+ remove$1(frameGroup);
49605
+ }
49480
49606
  });
49607
+ }
49481
49608
 
49482
-
49483
- this.preExecute([
49484
- 'connection.delete',
49485
- 'shape.delete'
49486
- ], function(e) {
49487
-
49488
- var context = e.context,
49489
- element = context.connection || context.shape;
49490
-
49491
- saveClear(element.labels, function(label) {
49492
- modeling.removeShape(label, { nested: true });
49493
- });
49494
- });
49609
+ SpaceToolPreview.$inject = [
49610
+ 'eventBus',
49611
+ 'elementRegistry',
49612
+ 'canvas',
49613
+ 'styles',
49614
+ 'previewSupport'
49615
+ ];
49495
49616
 
49496
49617
 
49497
- this.execute('shape.delete', function(e) {
49618
+ // helpers //////////////////////
49498
49619
 
49499
- var context = e.context,
49500
- shape = context.shape,
49501
- labelTarget = shape.labelTarget;
49620
+ /**
49621
+ * Checks if an element is a connection.
49622
+ */
49623
+ function isConnection$6(element) {
49624
+ return element.waypoints;
49625
+ }
49502
49626
 
49503
- // unset labelTarget
49504
- if (labelTarget) {
49505
- context.labelTargetIndex = indexOf(labelTarget.labels, shape);
49506
- context.labelTarget = labelTarget;
49627
+ var SpaceToolModule$1 = {
49628
+ __init__: [ 'spaceToolPreview' ],
49629
+ __depends__: [
49630
+ DraggingModule,
49631
+ RulesModule$1,
49632
+ ToolManagerModule,
49633
+ PreviewSupportModule,
49634
+ MouseModule
49635
+ ],
49636
+ spaceTool: [ 'type', SpaceTool ],
49637
+ spaceToolPreview: [ 'type', SpaceToolPreview ]
49638
+ };
49507
49639
 
49508
- shape.labelTarget = null;
49509
- }
49510
- });
49640
+ function BpmnSpaceTool(injector) {
49641
+ injector.invoke(SpaceTool, this);
49642
+ }
49511
49643
 
49512
- this.revert('shape.delete', function(e) {
49644
+ BpmnSpaceTool.$inject = [
49645
+ 'injector'
49646
+ ];
49513
49647
 
49514
- var context = e.context,
49515
- shape = context.shape,
49516
- labelTarget = context.labelTarget,
49517
- labelTargetIndex = context.labelTargetIndex;
49648
+ e(BpmnSpaceTool, SpaceTool);
49518
49649
 
49519
- // restore labelTarget
49520
- if (labelTarget) {
49521
- add(labelTarget.labels, shape, labelTargetIndex);
49650
+ BpmnSpaceTool.prototype.calculateAdjustments = function(elements, axis, delta, start) {
49651
+ var adjustments = SpaceTool.prototype.calculateAdjustments.call(this, elements, axis, delta, start);
49522
49652
 
49523
- shape.labelTarget = labelTarget;
49524
- }
49653
+ // do not resize text annotations
49654
+ adjustments.resizingShapes = adjustments.resizingShapes.filter(function(shape) {
49655
+ return !is$1(shape, 'bpmn:TextAnnotation');
49525
49656
  });
49526
49657
 
49527
- }
49528
-
49529
- e(LabelSupport, CommandInterceptor);
49530
-
49531
- LabelSupport.$inject = [
49532
- 'injector',
49533
- 'eventBus',
49534
- 'modeling'
49535
- ];
49658
+ return adjustments;
49659
+ };
49536
49660
 
49661
+ var SpaceToolModule = {
49662
+ __depends__: [ SpaceToolModule$1 ],
49663
+ spaceTool: [ 'type', BpmnSpaceTool ]
49664
+ };
49537
49665
 
49538
49666
  /**
49539
- * Return a filtered list of elements that do not
49540
- * contain attached elements with hosts being part
49541
- * of the selection.
49667
+ * A service that offers un- and redoable execution of commands.
49542
49668
  *
49543
- * @param {Array<djs.model.Base>} elements
49669
+ * The command stack is responsible for executing modeling actions
49670
+ * in a un- and redoable manner. To do this it delegates the actual
49671
+ * command execution to {@link CommandHandler}s.
49544
49672
  *
49545
- * @return {Array<djs.model.Base>} filtered
49673
+ * Command handlers provide {@link CommandHandler#execute(ctx)} and
49674
+ * {@link CommandHandler#revert(ctx)} methods to un- and redo a command
49675
+ * identified by a command context.
49676
+ *
49677
+ *
49678
+ * ## Life-Cycle events
49679
+ *
49680
+ * In the process the command stack fires a number of life-cycle events
49681
+ * that other components to participate in the command execution.
49682
+ *
49683
+ * * preExecute
49684
+ * * preExecuted
49685
+ * * execute
49686
+ * * executed
49687
+ * * postExecute
49688
+ * * postExecuted
49689
+ * * revert
49690
+ * * reverted
49691
+ *
49692
+ * A special event is used for validating, whether a command can be
49693
+ * performed prior to its execution.
49694
+ *
49695
+ * * canExecute
49696
+ *
49697
+ * Each of the events is fired as `commandStack.{eventName}` and
49698
+ * `commandStack.{commandName}.{eventName}`, respectively. This gives
49699
+ * components fine grained control on where to hook into.
49700
+ *
49701
+ * The event object fired transports `command`, the name of the
49702
+ * command and `context`, the command context.
49703
+ *
49704
+ *
49705
+ * ## Creating Command Handlers
49706
+ *
49707
+ * Command handlers should provide the {@link CommandHandler#execute(ctx)}
49708
+ * and {@link CommandHandler#revert(ctx)} methods to implement
49709
+ * redoing and undoing of a command.
49710
+ *
49711
+ * A command handler _must_ ensure undo is performed properly in order
49712
+ * not to break the undo chain. It must also return the shapes that
49713
+ * got changed during the `execute` and `revert` operations.
49714
+ *
49715
+ * Command handlers may execute other modeling operations (and thus
49716
+ * commands) in their `preExecute` and `postExecute` phases. The command
49717
+ * stack will properly group all commands together into a logical unit
49718
+ * that may be re- and undone atomically.
49719
+ *
49720
+ * Command handlers must not execute other commands from within their
49721
+ * core implementation (`execute`, `revert`).
49722
+ *
49723
+ *
49724
+ * ## Change Tracking
49725
+ *
49726
+ * During the execution of the CommandStack it will keep track of all
49727
+ * elements that have been touched during the command's execution.
49728
+ *
49729
+ * At the end of the CommandStack execution it will notify interested
49730
+ * components via an 'elements.changed' event with all the dirty
49731
+ * elements.
49732
+ *
49733
+ * The event can be picked up by components that are interested in the fact
49734
+ * that elements have been changed. One use case for this is updating
49735
+ * their graphical representation after moving / resizing or deletion.
49736
+ *
49737
+ * @see CommandHandler
49738
+ *
49739
+ * @param {EventBus} eventBus
49740
+ * @param {Injector} injector
49546
49741
  */
49547
- function removeLabels(elements) {
49742
+ function CommandStack(eventBus, injector) {
49548
49743
 
49549
- return filter(elements, function(element) {
49744
+ /**
49745
+ * A map of all registered command handlers.
49746
+ *
49747
+ * @type {Object}
49748
+ */
49749
+ this._handlerMap = {};
49550
49750
 
49551
- // filter out labels that are move together
49552
- // with their label targets
49553
- return elements.indexOf(element.labelTarget) === -1;
49554
- });
49751
+ /**
49752
+ * A stack containing all re/undoable actions on the diagram
49753
+ *
49754
+ * @type {Array<Object>}
49755
+ */
49756
+ this._stack = [];
49757
+
49758
+ /**
49759
+ * The current index on the stack
49760
+ *
49761
+ * @type {number}
49762
+ */
49763
+ this._stackIdx = -1;
49764
+
49765
+ /**
49766
+ * Current active commandStack execution
49767
+ *
49768
+ * @type {Object}
49769
+ * @property {Object[]} actions
49770
+ * @property {Object[]} dirty
49771
+ * @property { 'undo' | 'redo' | 'clear' | 'execute' | null } trigger the cause of the current excecution
49772
+ */
49773
+ this._currentExecution = {
49774
+ actions: [],
49775
+ dirty: [],
49776
+ trigger: null
49777
+ };
49778
+
49779
+
49780
+ this._injector = injector;
49781
+ this._eventBus = eventBus;
49782
+
49783
+ this._uid = 1;
49784
+
49785
+ eventBus.on([
49786
+ 'diagram.destroy',
49787
+ 'diagram.clear'
49788
+ ], function() {
49789
+ this.clear(false);
49790
+ }, this);
49555
49791
  }
49556
49792
 
49557
- var LabelSupportModule = {
49558
- __init__: [ 'labelSupport' ],
49559
- labelSupport: [ 'type', LabelSupport ]
49560
- };
49793
+ CommandStack.$inject = [ 'eventBus', 'injector' ];
49561
49794
 
49562
- var LOW_PRIORITY$5 = 251,
49563
- HIGH_PRIORITY$4 = 1401;
49564
49795
 
49565
- var MARKER_ATTACH$1 = 'attach-ok';
49796
+ /**
49797
+ * Execute a command
49798
+ *
49799
+ * @param {string} command the command to execute
49800
+ * @param {Object} context the environment to execute the command in
49801
+ */
49802
+ CommandStack.prototype.execute = function(command, context) {
49803
+ if (!command) {
49804
+ throw new Error('command required');
49805
+ }
49806
+
49807
+ this._currentExecution.trigger = 'execute';
49808
+
49809
+ const action = { command: command, context: context };
49810
+
49811
+ this._pushAction(action);
49812
+ this._internalExecute(action);
49813
+ this._popAction(action);
49814
+ };
49566
49815
 
49567
49816
 
49568
49817
  /**
49569
- * Adds the notion of attached elements to the modeler.
49818
+ * Ask whether a given command can be executed.
49570
49819
  *
49571
- * Optionally depends on `diagram-js/lib/features/move` to render
49572
- * the attached elements during move preview.
49820
+ * Implementors may hook into the mechanism on two ways:
49573
49821
  *
49574
- * Optionally depends on `diagram-js/lib/features/label-support`
49575
- * to render attached labels during move preview.
49822
+ * * in event listeners:
49576
49823
  *
49577
- * @param {didi.Injector} injector
49578
- * @param {EventBus} eventBus
49579
- * @param {Canvas} canvas
49580
- * @param {Rules} rules
49581
- * @param {Modeling} modeling
49824
+ * Users may prevent the execution via an event listener.
49825
+ * It must prevent the default action for `commandStack.(<command>.)canExecute` events.
49826
+ *
49827
+ * * in command handlers:
49828
+ *
49829
+ * If the method {@link CommandHandler#canExecute} is implemented in a handler
49830
+ * it will be called to figure out whether the execution is allowed.
49831
+ *
49832
+ * @param {string} command the command to execute
49833
+ * @param {Object} context the environment to execute the command in
49834
+ *
49835
+ * @return {boolean} true if the command can be executed
49582
49836
  */
49583
- function AttachSupport(injector, eventBus, canvas, rules, modeling) {
49584
-
49585
- CommandInterceptor.call(this, eventBus);
49837
+ CommandStack.prototype.canExecute = function(command, context) {
49586
49838
 
49587
- var movePreview = injector.get('movePreview', false);
49839
+ const action = { command: command, context: context };
49588
49840
 
49841
+ const handler = this._getHandler(command);
49589
49842
 
49590
- // remove all the attached elements from the shapes to be validated
49591
- // add all the attached shapes to the overall list of moved shapes
49592
- eventBus.on('shape.move.start', HIGH_PRIORITY$4, function(e) {
49843
+ let result = this._fire(command, 'canExecute', action);
49593
49844
 
49594
- var context = e.context,
49595
- shapes = context.shapes,
49596
- validatedShapes = context.validatedShapes;
49845
+ // handler#canExecute will only be called if no listener
49846
+ // decided on a result already
49847
+ if (result === undefined) {
49848
+ if (!handler) {
49849
+ return false;
49850
+ }
49597
49851
 
49598
- context.shapes = addAttached(shapes);
49852
+ if (handler.canExecute) {
49853
+ result = handler.canExecute(context);
49854
+ }
49855
+ }
49599
49856
 
49600
- context.validatedShapes = removeAttached(validatedShapes);
49601
- });
49857
+ return result;
49858
+ };
49602
49859
 
49603
- // add attachers to the visual's group
49604
- movePreview && eventBus.on('shape.move.start', LOW_PRIORITY$5, function(e) {
49605
49860
 
49606
- var context = e.context,
49607
- shapes = context.shapes,
49608
- attachers = getAttachers(shapes);
49861
+ /**
49862
+ * Clear the command stack, erasing all undo / redo history
49863
+ */
49864
+ CommandStack.prototype.clear = function(emit) {
49865
+ this._stack.length = 0;
49866
+ this._stackIdx = -1;
49609
49867
 
49610
- forEach$1(attachers, function(attacher) {
49611
- movePreview.makeDraggable(context, attacher, true);
49868
+ if (emit !== false) {
49869
+ this._fire('changed', { trigger: 'clear' });
49870
+ }
49871
+ };
49612
49872
 
49613
- forEach$1(attacher.labels, function(label) {
49614
- movePreview.makeDraggable(context, label, true);
49615
- });
49616
- });
49617
- });
49618
49873
 
49619
- // add attach-ok marker to current host
49620
- movePreview && eventBus.on('shape.move.start', function(event) {
49621
- var context = event.context,
49622
- shapes = context.shapes;
49874
+ /**
49875
+ * Undo last command(s)
49876
+ */
49877
+ CommandStack.prototype.undo = function() {
49878
+ let action = this._getUndoAction(),
49879
+ next;
49623
49880
 
49624
- if (shapes.length !== 1) {
49625
- return;
49626
- }
49881
+ if (action) {
49882
+ this._currentExecution.trigger = 'undo';
49627
49883
 
49628
- var shape = shapes[0];
49884
+ this._pushAction(action);
49629
49885
 
49630
- var host = shape.host;
49886
+ while (action) {
49887
+ this._internalUndo(action);
49888
+ next = this._getUndoAction();
49631
49889
 
49632
- if (host) {
49633
- canvas.addMarker(host, MARKER_ATTACH$1);
49890
+ if (!next || next.id !== action.id) {
49891
+ break;
49892
+ }
49634
49893
 
49635
- eventBus.once([
49636
- 'shape.move.out',
49637
- 'shape.move.cleanup'
49638
- ], function() {
49639
- canvas.removeMarker(host, MARKER_ATTACH$1);
49640
- });
49894
+ action = next;
49641
49895
  }
49642
- });
49643
49896
 
49644
- // add all attachers to move closure
49645
- this.preExecuted('elements.move', HIGH_PRIORITY$4, function(e) {
49646
- var context = e.context,
49647
- closure = context.closure,
49648
- shapes = context.shapes,
49649
- attachers = getAttachers(shapes);
49897
+ this._popAction();
49898
+ }
49899
+ };
49650
49900
 
49651
- forEach$1(attachers, function(attacher) {
49652
- closure.add(attacher, closure.topLevel[attacher.host.id]);
49653
- });
49654
- });
49655
49901
 
49656
- // perform the attaching after shapes are done moving
49657
- this.postExecuted('elements.move', function(e) {
49902
+ /**
49903
+ * Redo last command(s)
49904
+ */
49905
+ CommandStack.prototype.redo = function() {
49906
+ let action = this._getRedoAction(),
49907
+ next;
49658
49908
 
49659
- var context = e.context,
49660
- shapes = context.shapes,
49661
- newHost = context.newHost,
49662
- attachers;
49909
+ if (action) {
49910
+ this._currentExecution.trigger = 'redo';
49663
49911
 
49664
- // only single elements can be attached
49665
- // multiply elements can be detached
49666
- if (newHost && shapes.length !== 1) {
49667
- return;
49668
- }
49912
+ this._pushAction(action);
49669
49913
 
49670
- if (newHost) {
49671
- attachers = shapes;
49672
- } else {
49914
+ while (action) {
49915
+ this._internalExecute(action, true);
49916
+ next = this._getRedoAction();
49673
49917
 
49674
- // find attachers moved without host
49675
- attachers = filter(shapes, function(shape) {
49676
- var host = shape.host;
49918
+ if (!next || next.id !== action.id) {
49919
+ break;
49920
+ }
49677
49921
 
49678
- return isAttacher(shape) && !includes$4(shapes, host);
49679
- });
49922
+ action = next;
49680
49923
  }
49681
49924
 
49682
- forEach$1(attachers, function(attacher) {
49683
- modeling.updateAttachment(attacher, newHost);
49684
- });
49685
- });
49925
+ this._popAction();
49926
+ }
49927
+ };
49686
49928
 
49687
- // ensure invalid attachment connections are removed
49688
- this.postExecuted('elements.move', function(e) {
49689
49929
 
49690
- var shapes = e.context.shapes;
49930
+ /**
49931
+ * Register a handler instance with the command stack
49932
+ *
49933
+ * @param {string} command
49934
+ * @param {CommandHandler} handler
49935
+ */
49936
+ CommandStack.prototype.register = function(command, handler) {
49937
+ this._setHandler(command, handler);
49938
+ };
49691
49939
 
49692
- forEach$1(shapes, function(shape) {
49693
49940
 
49694
- forEach$1(shape.attachers, function(attacher) {
49941
+ /**
49942
+ * Register a handler type with the command stack
49943
+ * by instantiating it and injecting its dependencies.
49944
+ *
49945
+ * @param {string} command
49946
+ * @param {Function} a constructor for a {@link CommandHandler}
49947
+ */
49948
+ CommandStack.prototype.registerHandler = function(command, handlerCls) {
49695
49949
 
49696
- // remove invalid outgoing connections
49697
- forEach$1(attacher.outgoing.slice(), function(connection) {
49698
- var allowed = rules.allowed('connection.reconnect', {
49699
- connection: connection,
49700
- source: connection.source,
49701
- target: connection.target
49702
- });
49950
+ if (!command || !handlerCls) {
49951
+ throw new Error('command and handlerCls must be defined');
49952
+ }
49703
49953
 
49704
- if (!allowed) {
49705
- modeling.removeConnection(connection);
49706
- }
49707
- });
49954
+ const handler = this._injector.instantiate(handlerCls);
49955
+ this.register(command, handler);
49956
+ };
49708
49957
 
49709
- // remove invalid incoming connections
49710
- forEach$1(attacher.incoming.slice(), function(connection) {
49711
- var allowed = rules.allowed('connection.reconnect', {
49712
- connection: connection,
49713
- source: connection.source,
49714
- target: connection.target
49715
- });
49958
+ CommandStack.prototype.canUndo = function() {
49959
+ return !!this._getUndoAction();
49960
+ };
49716
49961
 
49717
- if (!allowed) {
49718
- modeling.removeConnection(connection);
49719
- }
49720
- });
49721
- });
49722
- });
49723
- });
49962
+ CommandStack.prototype.canRedo = function() {
49963
+ return !!this._getRedoAction();
49964
+ };
49724
49965
 
49725
- this.postExecute('shape.create', function(e) {
49726
- var context = e.context,
49727
- shape = context.shape,
49728
- host = context.host;
49966
+ // stack access //////////////////////
49729
49967
 
49730
- if (host) {
49731
- modeling.updateAttachment(shape, host);
49732
- }
49733
- });
49968
+ CommandStack.prototype._getRedoAction = function() {
49969
+ return this._stack[this._stackIdx + 1];
49970
+ };
49734
49971
 
49735
- // update attachments if the host is replaced
49736
- this.postExecute('shape.replace', function(e) {
49737
49972
 
49738
- var context = e.context,
49739
- oldShape = context.oldShape,
49740
- newShape = context.newShape;
49973
+ CommandStack.prototype._getUndoAction = function() {
49974
+ return this._stack[this._stackIdx];
49975
+ };
49741
49976
 
49742
- // move the attachers to the new host
49743
- saveClear(oldShape.attachers, function(attacher) {
49744
- var allowed = rules.allowed('elements.move', {
49745
- target: newShape,
49746
- shapes: [ attacher ]
49747
- });
49748
49977
 
49749
- if (allowed === 'attach') {
49750
- modeling.updateAttachment(attacher, newShape);
49751
- } else {
49752
- modeling.removeShape(attacher);
49753
- }
49754
- });
49978
+ // internal functionality //////////////////////
49755
49979
 
49756
- // move attachers if new host has different size
49757
- if (newShape.attachers.length) {
49980
+ CommandStack.prototype._internalUndo = function(action) {
49981
+ const command = action.command,
49982
+ context = action.context;
49758
49983
 
49759
- forEach$1(newShape.attachers, function(attacher) {
49760
- var delta = getNewAttachShapeDelta(attacher, oldShape, newShape);
49761
- modeling.moveShape(attacher, delta, attacher.parent);
49762
- });
49984
+ const handler = this._getHandler(command);
49985
+
49986
+ // guard against illegal nested command stack invocations
49987
+ this._atomicDo(() => {
49988
+ this._fire(command, 'revert', action);
49989
+
49990
+ if (handler.revert) {
49991
+ this._markDirty(handler.revert(context));
49763
49992
  }
49764
49993
 
49994
+ this._revertedAction(action);
49995
+
49996
+ this._fire(command, 'reverted', action);
49765
49997
  });
49998
+ };
49766
49999
 
49767
- // move shape on host resize
49768
- this.postExecute('shape.resize', function(event) {
49769
- var context = event.context,
49770
- shape = context.shape,
49771
- oldBounds = context.oldBounds,
49772
- newBounds = context.newBounds,
49773
- attachers = shape.attachers,
49774
- hints = context.hints || {};
49775
50000
 
49776
- if (hints.attachSupport === false) {
49777
- return;
49778
- }
50001
+ CommandStack.prototype._fire = function(command, qualifier, event) {
50002
+ if (arguments.length < 3) {
50003
+ event = qualifier;
50004
+ qualifier = null;
50005
+ }
49779
50006
 
49780
- forEach$1(attachers, function(attacher) {
49781
- var delta = getNewAttachShapeDelta(attacher, oldBounds, newBounds);
50007
+ const names = qualifier ? [ command + '.' + qualifier, qualifier ] : [ command ];
50008
+ let result;
49782
50009
 
49783
- modeling.moveShape(attacher, delta, attacher.parent);
50010
+ event = this._eventBus.createEvent(event);
49784
50011
 
49785
- forEach$1(attacher.labels, function(label) {
49786
- modeling.moveShape(label, delta, label.parent);
49787
- });
49788
- });
49789
- });
50012
+ for (const name of names) {
50013
+ result = this._eventBus.fire('commandStack.' + name, event);
49790
50014
 
49791
- // remove attachments
49792
- this.preExecute('shape.delete', function(event) {
50015
+ if (event.cancelBubble) {
50016
+ break;
50017
+ }
50018
+ }
49793
50019
 
49794
- var shape = event.context.shape;
50020
+ return result;
50021
+ };
49795
50022
 
49796
- saveClear(shape.attachers, function(attacher) {
49797
- modeling.removeShape(attacher);
49798
- });
50023
+ CommandStack.prototype._createId = function() {
50024
+ return this._uid++;
50025
+ };
49799
50026
 
49800
- if (shape.host) {
49801
- modeling.updateAttachment(shape, null);
49802
- }
49803
- });
49804
- }
50027
+ CommandStack.prototype._atomicDo = function(fn) {
49805
50028
 
49806
- e(AttachSupport, CommandInterceptor);
50029
+ const execution = this._currentExecution;
49807
50030
 
49808
- AttachSupport.$inject = [
49809
- 'injector',
49810
- 'eventBus',
49811
- 'canvas',
49812
- 'rules',
49813
- 'modeling'
49814
- ];
50031
+ execution.atomic = true;
49815
50032
 
50033
+ try {
50034
+ fn();
50035
+ } finally {
50036
+ execution.atomic = false;
50037
+ }
50038
+ };
49816
50039
 
49817
- /**
49818
- * Return attachers of the given shapes
49819
- *
49820
- * @param {Array<djs.model.Base>} shapes
49821
- * @return {Array<djs.model.Base>}
49822
- */
49823
- function getAttachers(shapes) {
49824
- return flatten(map$1(shapes, function(s) {
49825
- return s.attachers || [];
49826
- }));
49827
- }
50040
+ CommandStack.prototype._internalExecute = function(action, redo) {
50041
+ const command = action.command,
50042
+ context = action.context;
49828
50043
 
49829
- /**
49830
- * Return a combined list of elements and
49831
- * attachers.
49832
- *
49833
- * @param {Array<djs.model.Base>} elements
49834
- * @return {Array<djs.model.Base>} filtered
49835
- */
49836
- function addAttached(elements) {
49837
- var attachers = getAttachers(elements);
50044
+ const handler = this._getHandler(command);
49838
50045
 
49839
- return unionBy('id', elements, attachers);
49840
- }
50046
+ if (!handler) {
50047
+ throw new Error('no command handler registered for <' + command + '>');
50048
+ }
49841
50049
 
49842
- /**
49843
- * Return a filtered list of elements that do not
49844
- * contain attached elements with hosts being part
49845
- * of the selection.
49846
- *
49847
- * @param {Array<djs.model.Base>} elements
49848
- *
49849
- * @return {Array<djs.model.Base>} filtered
49850
- */
49851
- function removeAttached(elements) {
50050
+ this._pushAction(action);
49852
50051
 
49853
- var ids = groupBy(elements, 'id');
50052
+ if (!redo) {
50053
+ this._fire(command, 'preExecute', action);
49854
50054
 
49855
- return filter(elements, function(element) {
49856
- while (element) {
50055
+ if (handler.preExecute) {
50056
+ handler.preExecute(context);
50057
+ }
49857
50058
 
49858
- // host in selection
49859
- if (element.host && ids[element.host.id]) {
49860
- return false;
49861
- }
50059
+ this._fire(command, 'preExecuted', action);
50060
+ }
49862
50061
 
49863
- element = element.parent;
50062
+ // guard against illegal nested command stack invocations
50063
+ this._atomicDo(() => {
50064
+
50065
+ this._fire(command, 'execute', action);
50066
+
50067
+ if (handler.execute) {
50068
+
50069
+ // actual execute + mark return results as dirty
50070
+ this._markDirty(handler.execute(context));
49864
50071
  }
49865
50072
 
49866
- return true;
50073
+ // log to stack
50074
+ this._executedAction(action, redo);
50075
+
50076
+ this._fire(command, 'executed', action);
49867
50077
  });
49868
- }
49869
50078
 
49870
- function isAttacher(shape) {
49871
- return !!shape.host;
49872
- }
50079
+ if (!redo) {
50080
+ this._fire(command, 'postExecute', action);
49873
50081
 
49874
- function includes$4(array, item) {
49875
- return array.indexOf(item) !== -1;
49876
- }
50082
+ if (handler.postExecute) {
50083
+ handler.postExecute(context);
50084
+ }
49877
50085
 
49878
- var AttachSupportModule = {
49879
- __depends__: [
49880
- RulesModule$1
49881
- ],
49882
- __init__: [ 'attachSupport' ],
49883
- attachSupport: [ 'type', AttachSupport ]
49884
- };
50086
+ this._fire(command, 'postExecuted', action);
50087
+ }
49885
50088
 
49886
- var LOW_PRIORITY$4 = 250;
50089
+ this._popAction(action);
50090
+ };
49887
50091
 
49888
- /**
49889
- * The tool manager acts as middle-man between the available tool's and the Palette,
49890
- * it takes care of making sure that the correct active state is set.
49891
- *
49892
- * @param {Object} eventBus
49893
- * @param {Object} dragging
49894
- */
49895
- function ToolManager(eventBus, dragging) {
49896
- this._eventBus = eventBus;
49897
- this._dragging = dragging;
49898
50092
 
49899
- this._tools = [];
49900
- this._active = null;
49901
- }
50093
+ CommandStack.prototype._pushAction = function(action) {
49902
50094
 
49903
- ToolManager.$inject = [ 'eventBus', 'dragging' ];
50095
+ const execution = this._currentExecution,
50096
+ actions = execution.actions;
49904
50097
 
49905
- ToolManager.prototype.registerTool = function(name, events) {
49906
- var tools = this._tools;
50098
+ const baseAction = actions[0];
49907
50099
 
49908
- if (!events) {
49909
- throw new Error('A tool has to be registered with it\'s "events"');
50100
+ if (execution.atomic) {
50101
+ throw new Error('illegal invocation in <execute> or <revert> phase (action: ' + action.command + ')');
49910
50102
  }
49911
50103
 
49912
- tools.push(name);
50104
+ if (!action.id) {
50105
+ action.id = (baseAction && baseAction.id) || this._createId();
50106
+ }
49913
50107
 
49914
- this.bindEvents(name, events);
50108
+ actions.push(action);
49915
50109
  };
49916
50110
 
49917
- ToolManager.prototype.isActive = function(tool) {
49918
- return tool && this._active === tool;
50111
+
50112
+ CommandStack.prototype._popAction = function() {
50113
+ const execution = this._currentExecution,
50114
+ trigger = execution.trigger,
50115
+ actions = execution.actions,
50116
+ dirty = execution.dirty;
50117
+
50118
+ actions.pop();
50119
+
50120
+ if (!actions.length) {
50121
+ this._eventBus.fire('elements.changed', { elements: uniqueBy('id', dirty.reverse()) });
50122
+
50123
+ dirty.length = 0;
50124
+
50125
+ this._fire('changed', { trigger: trigger });
50126
+
50127
+ execution.trigger = null;
50128
+ }
49919
50129
  };
49920
50130
 
49921
- ToolManager.prototype.length = function(tool) {
49922
- return this._tools.length;
50131
+
50132
+ CommandStack.prototype._markDirty = function(elements) {
50133
+ const execution = this._currentExecution;
50134
+
50135
+ if (!elements) {
50136
+ return;
50137
+ }
50138
+
50139
+ elements = isArray$3(elements) ? elements : [ elements ];
50140
+
50141
+ execution.dirty = execution.dirty.concat(elements);
49923
50142
  };
49924
50143
 
49925
- ToolManager.prototype.setActive = function(tool) {
49926
- var eventBus = this._eventBus;
49927
50144
 
49928
- if (this._active !== tool) {
49929
- this._active = tool;
50145
+ CommandStack.prototype._executedAction = function(action, redo) {
50146
+ const stackIdx = ++this._stackIdx;
49930
50147
 
49931
- eventBus.fire('tool-manager.update', { tool: tool });
50148
+ if (!redo) {
50149
+ this._stack.splice(stackIdx, this._stack.length, action);
49932
50150
  }
49933
50151
  };
49934
50152
 
49935
- ToolManager.prototype.bindEvents = function(name, events) {
49936
- var eventBus = this._eventBus,
49937
- dragging = this._dragging;
49938
50153
 
49939
- var eventsToRegister = [];
50154
+ CommandStack.prototype._revertedAction = function(action) {
50155
+ this._stackIdx--;
50156
+ };
49940
50157
 
49941
- eventBus.on(events.tool + '.init', function(event) {
49942
- var context = event.context;
49943
50158
 
49944
- // Active tools that want to reactivate themselves must do this explicitly
49945
- if (!context.reactivate && this.isActive(name)) {
49946
- this.setActive(null);
50159
+ CommandStack.prototype._getHandler = function(command) {
50160
+ return this._handlerMap[command];
50161
+ };
49947
50162
 
49948
- dragging.cancel();
49949
- return;
49950
- }
50163
+ CommandStack.prototype._setHandler = function(command, handler) {
50164
+ if (!command || !handler) {
50165
+ throw new Error('command and handler required');
50166
+ }
49951
50167
 
49952
- this.setActive(name);
50168
+ if (this._handlerMap[command]) {
50169
+ throw new Error('overriding handler for command <' + command + '>');
50170
+ }
49953
50171
 
49954
- }, this);
50172
+ this._handlerMap[command] = handler;
50173
+ };
49955
50174
 
49956
- // Todo[ricardo]: add test cases
49957
- forEach$1(events, function(event) {
49958
- eventsToRegister.push(event + '.ended');
49959
- eventsToRegister.push(event + '.canceled');
49960
- });
50175
+ var CommandModule = {
50176
+ commandStack: [ 'type', CommandStack ]
50177
+ };
49961
50178
 
49962
- eventBus.on(eventsToRegister, LOW_PRIORITY$4, function(event) {
50179
+ // document wide unique tooltip ids
50180
+ var ids = new IdGenerator('tt');
49963
50181
 
49964
- // We defer the de-activation of the tool to the .activate phase,
49965
- // so we're able to check if we want to toggle off the current
49966
- // active tool or switch to a new one
49967
- if (!this._active) {
49968
- return;
49969
- }
49970
50182
 
49971
- if (isPaletteClick(event)) {
49972
- return;
49973
- }
50183
+ function createRoot(parentNode) {
50184
+ var root = domify$1(
50185
+ '<div class="djs-tooltip-container" />'
50186
+ );
49974
50187
 
49975
- this.setActive(null);
49976
- }, this);
50188
+ assign(root, {
50189
+ position: 'absolute',
50190
+ width: '0',
50191
+ height: '0'
50192
+ });
49977
50193
 
49978
- };
50194
+ parentNode.insertBefore(root, parentNode.firstChild);
49979
50195
 
50196
+ return root;
50197
+ }
49980
50198
 
49981
- // helpers ///////////////
49982
50199
 
49983
- /**
49984
- * Check if a given event is a palette click event.
49985
- *
49986
- * @param {EventBus.Event} event
49987
- *
49988
- * @return {boolean}
49989
- */
49990
- function isPaletteClick(event) {
49991
- var target = event.originalEvent && event.originalEvent.target;
50200
+ function setPosition(el, x, y) {
50201
+ assign(el, { left: x + 'px', top: y + 'px' });
50202
+ }
49992
50203
 
49993
- return target && closest(target, '.group[data-group="tools"]');
50204
+ function setVisible(el, visible) {
50205
+ el.style.display = visible === false ? 'none' : '';
49994
50206
  }
49995
50207
 
49996
- var ToolManagerModule = {
49997
- __depends__: [
49998
- DraggingModule
49999
- ],
50000
- __init__: [ 'toolManager' ],
50001
- toolManager: [ 'type', ToolManager ]
50002
- };
50208
+
50209
+ var tooltipClass = 'djs-tooltip',
50210
+ tooltipSelector = '.' + tooltipClass;
50003
50211
 
50004
50212
  /**
50005
- * Return direction given axis and delta.
50213
+ * A service that allows users to render tool tips on the diagram.
50006
50214
  *
50007
- * @param {string} axis
50008
- * @param {number} delta
50215
+ * The tooltip service will take care of updating the tooltip positioning
50216
+ * during navigation + zooming.
50009
50217
  *
50010
- * @return {string}
50218
+ * @example
50219
+ *
50220
+ * ```javascript
50221
+ *
50222
+ * // add a pink badge on the top left of the shape
50223
+ * tooltips.add({
50224
+ * position: {
50225
+ * x: 50,
50226
+ * y: 100
50227
+ * },
50228
+ * html: '<div style="width: 10px; background: fuchsia; color: white;">0</div>'
50229
+ * });
50230
+ *
50231
+ * // or with optional life span
50232
+ * tooltips.add({
50233
+ * position: {
50234
+ * top: -5,
50235
+ * left: -5
50236
+ * },
50237
+ * html: '<div style="width: 10px; background: fuchsia; color: white;">0</div>',
50238
+ * ttl: 2000
50239
+ * });
50240
+ *
50241
+ * // remove a tool tip
50242
+ * var id = tooltips.add(...);
50243
+ * tooltips.remove(id);
50244
+ * ```
50245
+ *
50246
+ * @param {EventBus} eventBus
50247
+ * @param {Canvas} canvas
50011
50248
  */
50012
- function getDirection(axis, delta) {
50249
+ function Tooltips(eventBus, canvas) {
50013
50250
 
50014
- if (axis === 'x') {
50015
- if (delta > 0) {
50016
- return 'e';
50017
- }
50251
+ this._eventBus = eventBus;
50252
+ this._canvas = canvas;
50018
50253
 
50019
- if (delta < 0) {
50020
- return 'w';
50021
- }
50022
- }
50254
+ this._ids = ids;
50023
50255
 
50024
- if (axis === 'y') {
50025
- if (delta > 0) {
50026
- return 's';
50256
+ this._tooltipDefaults = {
50257
+ show: {
50258
+ minZoom: 0.7,
50259
+ maxZoom: 5.0
50027
50260
  }
50261
+ };
50028
50262
 
50029
- if (delta < 0) {
50030
- return 'n';
50031
- }
50032
- }
50263
+ /**
50264
+ * Mapping tooltipId -> tooltip
50265
+ */
50266
+ this._tooltips = {};
50033
50267
 
50034
- return null;
50268
+ // root html element for all tooltips
50269
+ this._tooltipRoot = createRoot(canvas.getContainer());
50270
+
50271
+
50272
+ var self = this;
50273
+
50274
+ delegate.bind(this._tooltipRoot, tooltipSelector, 'mousedown', function(event) {
50275
+ event.stopPropagation();
50276
+ });
50277
+
50278
+ delegate.bind(this._tooltipRoot, tooltipSelector, 'mouseover', function(event) {
50279
+ self.trigger('mouseover', event);
50280
+ });
50281
+
50282
+ delegate.bind(this._tooltipRoot, tooltipSelector, 'mouseout', function(event) {
50283
+ self.trigger('mouseout', event);
50284
+ });
50285
+
50286
+ this._init();
50035
50287
  }
50036
50288
 
50289
+
50290
+ Tooltips.$inject = [ 'eventBus', 'canvas' ];
50291
+
50292
+
50037
50293
  /**
50038
- * Returns connections whose waypoints are to be updated. Waypoints are to be updated if start
50039
- * or end is to be moved or resized.
50294
+ * Adds a HTML tooltip to the diagram
50040
50295
  *
50041
- * @param {Array<djs.model.Shape} movingShapes
50042
- * @param {Array<djs.model.Shape} resizingShapes
50296
+ * @param {Object} tooltip the tooltip configuration
50043
50297
  *
50044
- * @returns {Array<djs.model.Connection>}
50298
+ * @param {string|DOMElement} tooltip.html html element to use as an tooltip
50299
+ * @param {Object} [tooltip.show] show configuration
50300
+ * @param {number} [tooltip.show.minZoom] minimal zoom level to show the tooltip
50301
+ * @param {number} [tooltip.show.maxZoom] maximum zoom level to show the tooltip
50302
+ * @param {Object} tooltip.position where to attach the tooltip
50303
+ * @param {number} [tooltip.position.left] relative to element bbox left attachment
50304
+ * @param {number} [tooltip.position.top] relative to element bbox top attachment
50305
+ * @param {number} [tooltip.position.bottom] relative to element bbox bottom attachment
50306
+ * @param {number} [tooltip.position.right] relative to element bbox right attachment
50307
+ * @param {number} [tooltip.timeout=-1]
50308
+ *
50309
+ * @return {string} id that may be used to reference the tooltip for update or removal
50045
50310
  */
50046
- function getWaypointsUpdatingConnections(movingShapes, resizingShapes) {
50047
- var waypointsUpdatingConnections = [];
50311
+ Tooltips.prototype.add = function(tooltip) {
50048
50312
 
50049
- forEach$1(movingShapes.concat(resizingShapes), function(shape) {
50050
- var incoming = shape.incoming,
50051
- outgoing = shape.outgoing;
50313
+ if (!tooltip.position) {
50314
+ throw new Error('must specifiy tooltip position');
50315
+ }
50052
50316
 
50053
- forEach$1(incoming.concat(outgoing), function(connection) {
50054
- var source = connection.source,
50055
- target = connection.target;
50317
+ if (!tooltip.html) {
50318
+ throw new Error('must specifiy tooltip html');
50319
+ }
50056
50320
 
50057
- if (includes$3(movingShapes, source) ||
50058
- includes$3(movingShapes, target) ||
50059
- includes$3(resizingShapes, source) ||
50060
- includes$3(resizingShapes, target)) {
50321
+ var id = this._ids.next();
50061
50322
 
50062
- if (!includes$3(waypointsUpdatingConnections, connection)) {
50063
- waypointsUpdatingConnections.push(connection);
50064
- }
50065
- }
50066
- });
50323
+ tooltip = assign$1({}, this._tooltipDefaults, tooltip, {
50324
+ id: id
50067
50325
  });
50068
50326
 
50069
- return waypointsUpdatingConnections;
50070
- }
50327
+ this._addTooltip(tooltip);
50071
50328
 
50072
- function includes$3(array, item) {
50073
- return array.indexOf(item) !== -1;
50074
- }
50329
+ if (tooltip.timeout) {
50330
+ this.setTimeout(tooltip);
50331
+ }
50332
+
50333
+ return id;
50334
+ };
50335
+
50336
+ Tooltips.prototype.trigger = function(action, event) {
50337
+
50338
+ var node = event.delegateTarget || event.target;
50339
+
50340
+ var tooltip = this.get(attr$1(node, 'data-tooltip-id'));
50341
+
50342
+ if (!tooltip) {
50343
+ return;
50344
+ }
50345
+
50346
+ if (action === 'mouseover' && tooltip.timeout) {
50347
+ this.clearTimeout(tooltip);
50348
+ }
50349
+
50350
+ if (action === 'mouseout' && tooltip.timeout) {
50351
+
50352
+ // cut timeout after mouse out
50353
+ tooltip.timeout = 1000;
50354
+
50355
+ this.setTimeout(tooltip);
50356
+ }
50357
+ };
50075
50358
 
50076
50359
  /**
50077
- * Resize bounds.
50078
- *
50079
- * @param {Object} bounds
50080
- * @param {number} bounds.x
50081
- * @param {number} bounds.y
50082
- * @param {number} bounds.width
50083
- * @param {number} bounds.height
50084
- * @param {string} direction
50085
- * @param {Object} delta
50086
- * @param {number} delta.x
50087
- * @param {number} delta.y
50360
+ * Get a tooltip with the given id
50088
50361
  *
50089
- * @return {Object}
50362
+ * @param {string} id
50090
50363
  */
50091
- function resizeBounds(bounds, direction, delta) {
50092
- var x = bounds.x,
50093
- y = bounds.y,
50094
- width = bounds.width,
50095
- height = bounds.height,
50096
- dx = delta.x,
50097
- dy = delta.y;
50364
+ Tooltips.prototype.get = function(id) {
50098
50365
 
50099
- switch (direction) {
50100
- case 'n':
50101
- return {
50102
- x: x,
50103
- y: y + dy,
50104
- width: width,
50105
- height: height - dy
50106
- };
50107
- case 's':
50108
- return {
50109
- x: x,
50110
- y: y,
50111
- width: width,
50112
- height: height + dy
50113
- };
50114
- case 'w':
50115
- return {
50116
- x: x + dx,
50117
- y: y,
50118
- width: width - dx,
50119
- height: height
50120
- };
50121
- case 'e':
50122
- return {
50123
- x: x,
50124
- y: y,
50125
- width: width + dx,
50126
- height: height
50127
- };
50128
- default:
50129
- throw new Error('unknown direction: ' + direction);
50366
+ if (typeof id !== 'string') {
50367
+ id = id.id;
50368
+ }
50369
+
50370
+ return this._tooltips[id];
50371
+ };
50372
+
50373
+ Tooltips.prototype.clearTimeout = function(tooltip) {
50374
+
50375
+ tooltip = this.get(tooltip);
50376
+
50377
+ if (!tooltip) {
50378
+ return;
50130
50379
  }
50131
- }
50132
50380
 
50133
- var abs$1 = Math.abs,
50134
- round$4 = Math.round;
50381
+ var removeTimer = tooltip.removeTimer;
50135
50382
 
50136
- var AXIS_TO_DIMENSION = {
50137
- x: 'width',
50138
- y: 'height'
50383
+ if (removeTimer) {
50384
+ clearTimeout(removeTimer);
50385
+ tooltip.removeTimer = null;
50386
+ }
50139
50387
  };
50140
50388
 
50141
- var CURSOR_CROSSHAIR = 'crosshair';
50389
+ Tooltips.prototype.setTimeout = function(tooltip) {
50142
50390
 
50143
- var DIRECTION_TO_TRBL = {
50144
- n: 'top',
50145
- w: 'left',
50146
- s: 'bottom',
50147
- e: 'right'
50148
- };
50391
+ tooltip = this.get(tooltip);
50149
50392
 
50150
- var HIGH_PRIORITY$3 = 1500;
50393
+ if (!tooltip) {
50394
+ return;
50395
+ }
50151
50396
 
50152
- var DIRECTION_TO_OPPOSITE = {
50153
- n: 's',
50154
- w: 'e',
50155
- s: 'n',
50156
- e: 'w'
50157
- };
50397
+ this.clearTimeout(tooltip);
50158
50398
 
50159
- var PADDING = 20;
50399
+ var self = this;
50160
50400
 
50401
+ tooltip.removeTimer = setTimeout(function() {
50402
+ self.remove(tooltip);
50403
+ }, tooltip.timeout);
50404
+ };
50161
50405
 
50162
50406
  /**
50163
- * Add or remove space by moving and resizing elements.
50407
+ * Remove an tooltip with the given id
50164
50408
  *
50165
- * @param {Canvas} canvas
50166
- * @param {Dragging} dragging
50167
- * @param {EventBus} eventBus
50168
- * @param {Modeling} modeling
50169
- * @param {Rules} rules
50170
- * @param {ToolManager} toolManager
50171
- * @param {Mouse} mouse
50409
+ * @param {string} id
50172
50410
  */
50173
- function SpaceTool(
50174
- canvas, dragging, eventBus,
50175
- modeling, rules, toolManager,
50176
- mouse) {
50177
-
50178
- this._canvas = canvas;
50179
- this._dragging = dragging;
50180
- this._eventBus = eventBus;
50181
- this._modeling = modeling;
50182
- this._rules = rules;
50183
- this._toolManager = toolManager;
50184
- this._mouse = mouse;
50185
-
50186
- var self = this;
50411
+ Tooltips.prototype.remove = function(id) {
50187
50412
 
50188
- toolManager.registerTool('space', {
50189
- tool: 'spaceTool.selection',
50190
- dragging: 'spaceTool'
50191
- });
50413
+ var tooltip = this.get(id);
50192
50414
 
50193
- eventBus.on('spaceTool.selection.end', function(event) {
50194
- eventBus.once('spaceTool.selection.ended', function() {
50195
- self.activateMakeSpace(event.originalEvent);
50196
- });
50197
- });
50415
+ if (tooltip) {
50416
+ remove$2(tooltip.html);
50417
+ remove$2(tooltip.htmlContainer);
50198
50418
 
50199
- eventBus.on('spaceTool.move', HIGH_PRIORITY$3 , function(event) {
50200
- var context = event.context,
50201
- initialized = context.initialized;
50419
+ delete tooltip.htmlContainer;
50202
50420
 
50203
- if (!initialized) {
50204
- initialized = context.initialized = self.init(event, context);
50205
- }
50421
+ delete this._tooltips[tooltip.id];
50422
+ }
50423
+ };
50206
50424
 
50207
- if (initialized) {
50208
- ensureConstraints(event);
50209
- }
50210
- });
50211
50425
 
50212
- eventBus.on('spaceTool.end', function(event) {
50213
- var context = event.context,
50214
- axis = context.axis,
50215
- direction = context.direction,
50216
- movingShapes = context.movingShapes,
50217
- resizingShapes = context.resizingShapes,
50218
- start = context.start;
50426
+ Tooltips.prototype.show = function() {
50427
+ setVisible(this._tooltipRoot);
50428
+ };
50219
50429
 
50220
- if (!context.initialized) {
50221
- return;
50222
- }
50223
50430
 
50224
- ensureConstraints(event);
50431
+ Tooltips.prototype.hide = function() {
50432
+ setVisible(this._tooltipRoot, false);
50433
+ };
50225
50434
 
50226
- var delta = {
50227
- x: 0,
50228
- y: 0
50229
- };
50230
50435
 
50231
- delta[ axis ] = round$4(event[ 'd' + axis ]);
50436
+ Tooltips.prototype._updateRoot = function(viewbox) {
50437
+ var a = viewbox.scale || 1;
50438
+ var d = viewbox.scale || 1;
50232
50439
 
50233
- self.makeSpace(movingShapes, resizingShapes, delta, direction, start);
50440
+ var matrix = 'matrix(' + a + ',0,0,' + d + ',' + (-1 * viewbox.x * a) + ',' + (-1 * viewbox.y * d) + ')';
50234
50441
 
50235
- eventBus.once('spaceTool.ended', function(event) {
50442
+ this._tooltipRoot.style.transform = matrix;
50443
+ this._tooltipRoot.style['-ms-transform'] = matrix;
50444
+ };
50236
50445
 
50237
- // activate space tool selection after make space
50238
- self.activateSelection(event.originalEvent, true, true);
50239
- });
50240
- });
50241
- }
50242
50446
 
50243
- SpaceTool.$inject = [
50244
- 'canvas',
50245
- 'dragging',
50246
- 'eventBus',
50247
- 'modeling',
50248
- 'rules',
50249
- 'toolManager',
50250
- 'mouse'
50251
- ];
50447
+ Tooltips.prototype._addTooltip = function(tooltip) {
50252
50448
 
50253
- /**
50254
- * Activate space tool selection.
50255
- *
50256
- * @param {Object} event
50257
- * @param {boolean} autoActivate
50258
- */
50259
- SpaceTool.prototype.activateSelection = function(event, autoActivate, reactivate) {
50260
- this._dragging.init(event, 'spaceTool.selection', {
50261
- autoActivate: autoActivate,
50262
- cursor: CURSOR_CROSSHAIR,
50263
- data: {
50264
- context: {
50265
- reactivate: reactivate
50266
- }
50267
- },
50268
- trapClick: false
50269
- });
50270
- };
50449
+ var id = tooltip.id,
50450
+ html = tooltip.html,
50451
+ htmlContainer,
50452
+ tooltipRoot = this._tooltipRoot;
50271
50453
 
50272
- /**
50273
- * Activate space tool make space.
50274
- *
50275
- * @param {MouseEvent} event
50276
- */
50277
- SpaceTool.prototype.activateMakeSpace = function(event) {
50278
- this._dragging.init(event, 'spaceTool', {
50279
- autoActivate: true,
50280
- cursor: CURSOR_CROSSHAIR,
50281
- data: {
50282
- context: {}
50283
- }
50284
- });
50285
- };
50454
+ // unwrap jquery (for those who need it)
50455
+ if (html.get && html.constructor.prototype.jquery) {
50456
+ html = html.get(0);
50457
+ }
50286
50458
 
50287
- /**
50288
- * Make space.
50289
- *
50290
- * @param {Array<djs.model.Shape>} movingShapes
50291
- * @param {Array<djs.model.Shape>} resizingShapes
50292
- * @param {Object} delta
50293
- * @param {number} delta.x
50294
- * @param {number} delta.y
50295
- * @param {string} direction
50296
- * @param {number} start
50297
- */
50298
- SpaceTool.prototype.makeSpace = function(movingShapes, resizingShapes, delta, direction, start) {
50299
- return this._modeling.createSpace(movingShapes, resizingShapes, delta, direction, start);
50300
- };
50459
+ // create proper html elements from
50460
+ // tooltip HTML strings
50461
+ if (isString(html)) {
50462
+ html = domify$1(html);
50463
+ }
50301
50464
 
50302
- /**
50303
- * Initialize make space and return true if that was successful.
50304
- *
50305
- * @param {Object} event
50306
- * @param {Object} context
50307
- *
50308
- * @return {boolean}
50309
- */
50310
- SpaceTool.prototype.init = function(event, context) {
50311
- var axis = abs$1(event.dx) > abs$1(event.dy) ? 'x' : 'y',
50312
- delta = event[ 'd' + axis ],
50313
- start = event[ axis ] - delta;
50465
+ htmlContainer = domify$1('<div data-tooltip-id="' + id + '" class="' + tooltipClass + '">');
50466
+ assign(htmlContainer, { position: 'absolute' });
50314
50467
 
50315
- if (abs$1(delta) < 5) {
50316
- return false;
50317
- }
50468
+ htmlContainer.appendChild(html);
50318
50469
 
50319
- // invert delta to remove space when moving left
50320
- if (delta < 0) {
50321
- delta *= -1;
50470
+ if (tooltip.type) {
50471
+ classes$1(htmlContainer).add('djs-tooltip-' + tooltip.type);
50322
50472
  }
50323
50473
 
50324
- // invert delta to add/remove space when removing/adding space if modifier key is pressed
50325
- if (hasPrimaryModifier(event)) {
50326
- delta *= -1;
50474
+ if (tooltip.className) {
50475
+ classes$1(htmlContainer).add(tooltip.className);
50327
50476
  }
50328
50477
 
50329
- var direction = getDirection(axis, delta);
50478
+ tooltip.htmlContainer = htmlContainer;
50330
50479
 
50331
- var root = this._canvas.getRootElement();
50480
+ tooltipRoot.appendChild(htmlContainer);
50332
50481
 
50333
- var children = selfAndAllChildren(root, true);
50482
+ this._tooltips[id] = tooltip;
50334
50483
 
50335
- var elements = this.calculateAdjustments(children, axis, delta, start);
50484
+ this._updateTooltip(tooltip);
50485
+ };
50336
50486
 
50337
- var minDimensions = this._eventBus.fire('spaceTool.getMinDimensions', {
50338
- axis: axis,
50339
- direction: direction,
50340
- shapes: elements.resizingShapes,
50341
- start: start
50342
- });
50343
50487
 
50344
- var spaceToolConstraints = getSpaceToolConstraints(elements, axis, direction, start, minDimensions);
50488
+ Tooltips.prototype._updateTooltip = function(tooltip) {
50345
50489
 
50346
- assign$1(
50347
- context,
50348
- elements,
50349
- {
50350
- axis: axis,
50351
- direction: direction,
50352
- spaceToolConstraints: spaceToolConstraints,
50353
- start: start
50354
- }
50355
- );
50490
+ var position = tooltip.position,
50491
+ htmlContainer = tooltip.htmlContainer;
50356
50492
 
50357
- set('resize-' + (axis === 'x' ? 'ew' : 'ns'));
50493
+ // update overlay html based on tooltip x, y
50358
50494
 
50359
- return true;
50495
+ setPosition(htmlContainer, position.x, position.y);
50360
50496
  };
50361
50497
 
50362
- /**
50363
- * Get elements to be moved and resized.
50364
- *
50365
- * @param {Array<djs.model.Shape>} elements
50366
- * @param {string} axis
50367
- * @param {number} delta
50368
- * @param {number} start
50369
- *
50370
- * @return {Object}
50371
- */
50372
- SpaceTool.prototype.calculateAdjustments = function(elements, axis, delta, start) {
50373
- var rules = this._rules;
50374
50498
 
50375
- var movingShapes = [],
50376
- resizingShapes = [];
50499
+ Tooltips.prototype._updateTooltipVisibilty = function(viewbox) {
50377
50500
 
50378
- forEach$1(elements, function(element) {
50379
- if (!element.parent || isConnection$7(element)) {
50380
- return;
50381
- }
50501
+ forEach$1(this._tooltips, function(tooltip) {
50502
+ var show = tooltip.show,
50503
+ htmlContainer = tooltip.htmlContainer,
50504
+ visible = true;
50382
50505
 
50383
- var shapeStart = element[ axis ],
50384
- shapeEnd = shapeStart + element[ AXIS_TO_DIMENSION[ axis ] ];
50506
+ if (show) {
50507
+ if (show.minZoom > viewbox.scale ||
50508
+ show.maxZoom < viewbox.scale) {
50509
+ visible = false;
50510
+ }
50385
50511
 
50386
- // shape to be moved
50387
- if ((delta > 0 && shapeStart > start) || (delta < 0 && shapeEnd < start)) {
50388
- return movingShapes.push(element);
50512
+ setVisible(htmlContainer, visible);
50389
50513
  }
50514
+ });
50515
+ };
50390
50516
 
50391
- // shape to be resized
50392
- if (shapeStart < start &&
50393
- shapeEnd > start &&
50394
- rules.allowed('shape.resize', { shape: element })
50395
- ) {
50517
+ Tooltips.prototype._init = function() {
50396
50518
 
50397
- return resizingShapes.push(element);
50398
- }
50399
- });
50519
+ var self = this;
50400
50520
 
50401
- return {
50402
- movingShapes: movingShapes,
50403
- resizingShapes: resizingShapes
50404
- };
50405
- };
50521
+ // scroll/zoom integration
50406
50522
 
50407
- SpaceTool.prototype.toggle = function() {
50523
+ function updateViewbox(viewbox) {
50524
+ self._updateRoot(viewbox);
50525
+ self._updateTooltipVisibilty(viewbox);
50408
50526
 
50409
- if (this.isActive()) {
50410
- return this._dragging.cancel();
50527
+ self.show();
50411
50528
  }
50412
50529
 
50413
- var mouseEvent = this._mouse.getLastMoveEvent();
50530
+ this._eventBus.on('canvas.viewbox.changing', function(event) {
50531
+ self.hide();
50532
+ });
50414
50533
 
50415
- this.activateSelection(mouseEvent, !!mouseEvent);
50534
+ this._eventBus.on('canvas.viewbox.changed', function(event) {
50535
+ updateViewbox(event.viewbox);
50536
+ });
50416
50537
  };
50417
50538
 
50418
- SpaceTool.prototype.isActive = function() {
50419
- var context = this._dragging.context();
50420
-
50421
- return context && /^spaceTool/.test(context.prefix);
50539
+ var TooltipsModule = {
50540
+ __init__: [ 'tooltips' ],
50541
+ tooltips: [ 'type', Tooltips ]
50422
50542
  };
50423
50543
 
50424
- // helpers //////////
50425
-
50426
- function addPadding(trbl) {
50427
- return {
50428
- top: trbl.top - PADDING,
50429
- right: trbl.right + PADDING,
50430
- bottom: trbl.bottom + PADDING,
50431
- left: trbl.left - PADDING
50432
- };
50433
- }
50544
+ /**
50545
+ * Remove from the beginning of a collection until it is empty.
50546
+ *
50547
+ * This is a null-safe operation that ensures elements
50548
+ * are being removed from the given collection until the
50549
+ * collection is empty.
50550
+ *
50551
+ * The implementation deals with the fact that a remove operation
50552
+ * may touch, i.e. remove multiple elements in the collection
50553
+ * at a time.
50554
+ *
50555
+ * @param {Array<Object>} [collection]
50556
+ * @param {Function} removeFn
50557
+ *
50558
+ * @return {Array<Object>} the cleared collection
50559
+ */
50560
+ function saveClear(collection, removeFn) {
50434
50561
 
50435
- function ensureConstraints(event) {
50436
- var context = event.context,
50437
- spaceToolConstraints = context.spaceToolConstraints;
50562
+ if (typeof removeFn !== 'function') {
50563
+ throw new Error('removeFn iterator must be a function');
50564
+ }
50438
50565
 
50439
- if (!spaceToolConstraints) {
50566
+ if (!collection) {
50440
50567
  return;
50441
50568
  }
50442
50569
 
50443
- var x, y;
50444
-
50445
- if (isNumber(spaceToolConstraints.left)) {
50446
- x = Math.max(event.x, spaceToolConstraints.left);
50570
+ var e;
50447
50571
 
50448
- event.dx = event.dx + x - event.x;
50449
- event.x = x;
50572
+ while ((e = collection[0])) {
50573
+ removeFn(e);
50450
50574
  }
50451
50575
 
50452
- if (isNumber(spaceToolConstraints.right)) {
50453
- x = Math.min(event.x, spaceToolConstraints.right);
50576
+ return collection;
50577
+ }
50454
50578
 
50455
- event.dx = event.dx + x - event.x;
50456
- event.x = x;
50457
- }
50579
+ var LOW_PRIORITY$4 = 250,
50580
+ HIGH_PRIORITY$4 = 1400;
50458
50581
 
50459
- if (isNumber(spaceToolConstraints.top)) {
50460
- y = Math.max(event.y, spaceToolConstraints.top);
50461
50582
 
50462
- event.dy = event.dy + y - event.y;
50463
- event.y = y;
50464
- }
50583
+ /**
50584
+ * A handler that makes sure labels are properly moved with
50585
+ * their label targets.
50586
+ *
50587
+ * @param {didi.Injector} injector
50588
+ * @param {EventBus} eventBus
50589
+ * @param {Modeling} modeling
50590
+ */
50591
+ function LabelSupport(injector, eventBus, modeling) {
50465
50592
 
50466
- if (isNumber(spaceToolConstraints.bottom)) {
50467
- y = Math.min(event.y, spaceToolConstraints.bottom);
50593
+ CommandInterceptor.call(this, eventBus);
50468
50594
 
50469
- event.dy = event.dy + y - event.y;
50470
- event.y = y;
50471
- }
50472
- }
50595
+ var movePreview = injector.get('movePreview', false);
50473
50596
 
50474
- function getSpaceToolConstraints(elements, axis, direction, start, minDimensions) {
50475
- var movingShapes = elements.movingShapes,
50476
- resizingShapes = elements.resizingShapes;
50597
+ // remove labels from the collection that are being
50598
+ // moved with other elements anyway
50599
+ eventBus.on('shape.move.start', HIGH_PRIORITY$4, function(e) {
50477
50600
 
50478
- if (!resizingShapes.length) {
50479
- return;
50480
- }
50601
+ var context = e.context,
50602
+ shapes = context.shapes,
50603
+ validatedShapes = context.validatedShapes;
50481
50604
 
50482
- var spaceToolConstraints = {},
50483
- min,
50484
- max;
50605
+ context.shapes = removeLabels(shapes);
50606
+ context.validatedShapes = removeLabels(validatedShapes);
50607
+ });
50485
50608
 
50486
- forEach$1(resizingShapes, function(resizingShape) {
50487
- var resizingShapeBBox = asTRBL(resizingShape);
50609
+ // add labels to visual's group
50610
+ movePreview && eventBus.on('shape.move.start', LOW_PRIORITY$4, function(e) {
50488
50611
 
50489
- // find children that are not moving or resizing
50490
- var nonMovingResizingChildren = filter(resizingShape.children, function(child) {
50491
- return !isConnection$7(child) &&
50492
- !isLabel$2(child) &&
50493
- !includes$2(movingShapes, child) &&
50494
- !includes$2(resizingShapes, child);
50612
+ var context = e.context,
50613
+ shapes = context.shapes;
50614
+
50615
+ var labels = [];
50616
+
50617
+ forEach$1(shapes, function(element) {
50618
+
50619
+ forEach$1(element.labels, function(label) {
50620
+
50621
+ if (!label.hidden && context.shapes.indexOf(label) === -1) {
50622
+ labels.push(label);
50623
+ }
50624
+
50625
+ if (element.labelTarget) {
50626
+ labels.push(element);
50627
+ }
50628
+ });
50495
50629
  });
50496
50630
 
50497
- // find children that are moving
50498
- var movingChildren = filter(resizingShape.children, function(child) {
50499
- return !isConnection$7(child) && !isLabel$2(child) && includes$2(movingShapes, child);
50631
+ forEach$1(labels, function(label) {
50632
+ movePreview.makeDraggable(context, label, true);
50500
50633
  });
50501
50634
 
50502
- var minOrMax,
50503
- nonMovingResizingChildrenBBox,
50504
- movingChildrenBBox;
50635
+ });
50505
50636
 
50506
- if (nonMovingResizingChildren.length) {
50507
- nonMovingResizingChildrenBBox = addPadding(asTRBL(getBBox(nonMovingResizingChildren)));
50637
+ // add all labels to move closure
50638
+ this.preExecuted('elements.move', HIGH_PRIORITY$4, function(e) {
50639
+ var context = e.context,
50640
+ closure = context.closure,
50641
+ enclosedElements = closure.enclosedElements;
50508
50642
 
50509
- minOrMax = start -
50510
- resizingShapeBBox[ DIRECTION_TO_TRBL[ direction ] ] +
50511
- nonMovingResizingChildrenBBox[ DIRECTION_TO_TRBL[ direction ] ];
50643
+ var enclosedLabels = [];
50512
50644
 
50513
- if (direction === 'n') {
50514
- spaceToolConstraints.bottom = max = isNumber(max) ? Math.min(max, minOrMax) : minOrMax;
50515
- } else if (direction === 'w') {
50516
- spaceToolConstraints.right = max = isNumber(max) ? Math.min(max, minOrMax) : minOrMax;
50517
- } else if (direction === 's') {
50518
- spaceToolConstraints.top = min = isNumber(min) ? Math.max(min, minOrMax) : minOrMax;
50519
- } else if (direction === 'e') {
50520
- spaceToolConstraints.left = min = isNumber(min) ? Math.max(min, minOrMax) : minOrMax;
50521
- }
50522
- }
50645
+ // find labels that are not part of
50646
+ // move closure yet and add them
50647
+ forEach$1(enclosedElements, function(element) {
50648
+ forEach$1(element.labels, function(label) {
50523
50649
 
50524
- if (movingChildren.length) {
50525
- movingChildrenBBox = addPadding(asTRBL(getBBox(movingChildren)));
50650
+ if (!enclosedElements[label.id]) {
50651
+ enclosedLabels.push(label);
50652
+ }
50653
+ });
50654
+ });
50526
50655
 
50527
- minOrMax = start -
50528
- movingChildrenBBox[ DIRECTION_TO_TRBL[ DIRECTION_TO_OPPOSITE[ direction ] ] ] +
50529
- resizingShapeBBox[ DIRECTION_TO_TRBL[ DIRECTION_TO_OPPOSITE[ direction ] ] ];
50656
+ closure.addAll(enclosedLabels);
50657
+ });
50530
50658
 
50531
- if (direction === 'n') {
50532
- spaceToolConstraints.bottom = max = isNumber(max) ? Math.min(max, minOrMax) : minOrMax;
50533
- } else if (direction === 'w') {
50534
- spaceToolConstraints.right = max = isNumber(max) ? Math.min(max, minOrMax) : minOrMax;
50535
- } else if (direction === 's') {
50536
- spaceToolConstraints.top = min = isNumber(min) ? Math.max(min, minOrMax) : minOrMax;
50537
- } else if (direction === 'e') {
50538
- spaceToolConstraints.left = min = isNumber(min) ? Math.max(min, minOrMax) : minOrMax;
50539
- }
50540
- }
50541
50659
 
50542
- var resizingShapeMinDimensions = minDimensions && minDimensions[ resizingShape.id ];
50660
+ this.preExecute([
50661
+ 'connection.delete',
50662
+ 'shape.delete'
50663
+ ], function(e) {
50664
+
50665
+ var context = e.context,
50666
+ element = context.connection || context.shape;
50667
+
50668
+ saveClear(element.labels, function(label) {
50669
+ modeling.removeShape(label, { nested: true });
50670
+ });
50671
+ });
50672
+
50673
+
50674
+ this.execute('shape.delete', function(e) {
50675
+
50676
+ var context = e.context,
50677
+ shape = context.shape,
50678
+ labelTarget = shape.labelTarget;
50679
+
50680
+ // unset labelTarget
50681
+ if (labelTarget) {
50682
+ context.labelTargetIndex = indexOf(labelTarget.labels, shape);
50683
+ context.labelTarget = labelTarget;
50543
50684
 
50544
- if (resizingShapeMinDimensions) {
50545
- if (direction === 'n') {
50546
- minOrMax = start +
50547
- resizingShape[ AXIS_TO_DIMENSION [ axis ] ] -
50548
- resizingShapeMinDimensions[ AXIS_TO_DIMENSION[ axis ] ];
50685
+ shape.labelTarget = null;
50686
+ }
50687
+ });
50549
50688
 
50550
- spaceToolConstraints.bottom = max = isNumber(max) ? Math.min(max, minOrMax) : minOrMax;
50551
- } else if (direction === 'w') {
50552
- minOrMax = start +
50553
- resizingShape[ AXIS_TO_DIMENSION [ axis ] ] -
50554
- resizingShapeMinDimensions[ AXIS_TO_DIMENSION[ axis ] ];
50689
+ this.revert('shape.delete', function(e) {
50555
50690
 
50556
- spaceToolConstraints.right = max = isNumber(max) ? Math.min(max, minOrMax) : minOrMax;
50557
- } else if (direction === 's') {
50558
- minOrMax = start -
50559
- resizingShape[ AXIS_TO_DIMENSION [ axis ] ] +
50560
- resizingShapeMinDimensions[ AXIS_TO_DIMENSION[ axis ] ];
50691
+ var context = e.context,
50692
+ shape = context.shape,
50693
+ labelTarget = context.labelTarget,
50694
+ labelTargetIndex = context.labelTargetIndex;
50561
50695
 
50562
- spaceToolConstraints.top = min = isNumber(min) ? Math.max(min, minOrMax) : minOrMax;
50563
- } else if (direction === 'e') {
50564
- minOrMax = start -
50565
- resizingShape[ AXIS_TO_DIMENSION [ axis ] ] +
50566
- resizingShapeMinDimensions[ AXIS_TO_DIMENSION[ axis ] ];
50696
+ // restore labelTarget
50697
+ if (labelTarget) {
50698
+ add(labelTarget.labels, shape, labelTargetIndex);
50567
50699
 
50568
- spaceToolConstraints.left = min = isNumber(min) ? Math.max(min, minOrMax) : minOrMax;
50569
- }
50700
+ shape.labelTarget = labelTarget;
50570
50701
  }
50571
50702
  });
50572
50703
 
50573
- return spaceToolConstraints;
50574
50704
  }
50575
50705
 
50576
- function includes$2(array, item) {
50577
- return array.indexOf(item) !== -1;
50578
- }
50706
+ e(LabelSupport, CommandInterceptor);
50579
50707
 
50580
- function isConnection$7(element) {
50581
- return !!element.waypoints;
50582
- }
50708
+ LabelSupport.$inject = [
50709
+ 'injector',
50710
+ 'eventBus',
50711
+ 'modeling'
50712
+ ];
50583
50713
 
50584
- function isLabel$2(element) {
50585
- return !!element.labelTarget;
50714
+
50715
+ /**
50716
+ * Return a filtered list of elements that do not
50717
+ * contain attached elements with hosts being part
50718
+ * of the selection.
50719
+ *
50720
+ * @param {Array<djs.model.Base>} elements
50721
+ *
50722
+ * @return {Array<djs.model.Base>} filtered
50723
+ */
50724
+ function removeLabels(elements) {
50725
+
50726
+ return filter(elements, function(element) {
50727
+
50728
+ // filter out labels that are move together
50729
+ // with their label targets
50730
+ return elements.indexOf(element.labelTarget) === -1;
50731
+ });
50586
50732
  }
50587
50733
 
50588
- var MARKER_DRAGGING$1 = 'djs-dragging',
50589
- MARKER_RESIZING = 'djs-resizing';
50734
+ var LabelSupportModule = {
50735
+ __init__: [ 'labelSupport' ],
50736
+ labelSupport: [ 'type', LabelSupport ]
50737
+ };
50590
50738
 
50591
- var LOW_PRIORITY$3 = 250;
50739
+ var LOW_PRIORITY$3 = 251,
50740
+ HIGH_PRIORITY$3 = 1401;
50592
50741
 
50593
- var max = Math.max;
50742
+ var MARKER_ATTACH$1 = 'attach-ok';
50594
50743
 
50595
50744
 
50596
50745
  /**
50597
- * Provides previews for selecting/moving/resizing shapes when creating/removing space.
50746
+ * Adds the notion of attached elements to the modeler.
50747
+ *
50748
+ * Optionally depends on `diagram-js/lib/features/move` to render
50749
+ * the attached elements during move preview.
50750
+ *
50751
+ * Optionally depends on `diagram-js/lib/features/label-support`
50752
+ * to render attached labels during move preview.
50598
50753
  *
50754
+ * @param {didi.Injector} injector
50599
50755
  * @param {EventBus} eventBus
50600
- * @param {ElementRegistry} elementRegistry
50601
50756
  * @param {Canvas} canvas
50602
- * @param {Styles} styles
50757
+ * @param {Rules} rules
50758
+ * @param {Modeling} modeling
50603
50759
  */
50604
- function SpaceToolPreview(
50605
- eventBus, elementRegistry, canvas,
50606
- styles, previewSupport) {
50760
+ function AttachSupport(injector, eventBus, canvas, rules, modeling) {
50607
50761
 
50608
- function addPreviewGfx(collection, dragGroup) {
50609
- forEach$1(collection, function(element) {
50610
- previewSupport.addDragger(element, dragGroup);
50762
+ CommandInterceptor.call(this, eventBus);
50611
50763
 
50612
- canvas.addMarker(element, MARKER_DRAGGING$1);
50613
- });
50614
- }
50764
+ var movePreview = injector.get('movePreview', false);
50615
50765
 
50616
- // add crosshair
50617
- eventBus.on('spaceTool.selection.start', function(event) {
50618
- var space = canvas.getLayer('space'),
50619
- context = event.context;
50620
50766
 
50621
- var orientation = {
50622
- x: 'M 0,-10000 L 0,10000',
50623
- y: 'M -10000,0 L 10000,0'
50624
- };
50767
+ // remove all the attached elements from the shapes to be validated
50768
+ // add all the attached shapes to the overall list of moved shapes
50769
+ eventBus.on('shape.move.start', HIGH_PRIORITY$3, function(e) {
50625
50770
 
50626
- var crosshairGroup = create$1('g');
50627
- attr(crosshairGroup, styles.cls('djs-crosshair-group', [ 'no-events' ]));
50771
+ var context = e.context,
50772
+ shapes = context.shapes,
50773
+ validatedShapes = context.validatedShapes;
50628
50774
 
50629
- append(space, crosshairGroup);
50775
+ context.shapes = addAttached(shapes);
50630
50776
 
50631
- // horizontal path
50632
- var pathX = create$1('path');
50633
- attr(pathX, 'd', orientation.x);
50634
- classes(pathX).add('djs-crosshair');
50777
+ context.validatedShapes = removeAttached(validatedShapes);
50778
+ });
50635
50779
 
50636
- append(crosshairGroup, pathX);
50780
+ // add attachers to the visual's group
50781
+ movePreview && eventBus.on('shape.move.start', LOW_PRIORITY$3, function(e) {
50637
50782
 
50638
- // vertical path
50639
- var pathY = create$1('path');
50640
- attr(pathY, 'd', orientation.y);
50641
- classes(pathY).add('djs-crosshair');
50783
+ var context = e.context,
50784
+ shapes = context.shapes,
50785
+ attachers = getAttachers(shapes);
50642
50786
 
50643
- append(crosshairGroup, pathY);
50787
+ forEach$1(attachers, function(attacher) {
50788
+ movePreview.makeDraggable(context, attacher, true);
50644
50789
 
50645
- context.crosshairGroup = crosshairGroup;
50790
+ forEach$1(attacher.labels, function(label) {
50791
+ movePreview.makeDraggable(context, label, true);
50792
+ });
50793
+ });
50646
50794
  });
50647
50795
 
50648
- // update crosshair
50649
- eventBus.on('spaceTool.selection.move', function(event) {
50650
- var crosshairGroup = event.context.crosshairGroup;
50796
+ // add attach-ok marker to current host
50797
+ movePreview && eventBus.on('shape.move.start', function(event) {
50798
+ var context = event.context,
50799
+ shapes = context.shapes;
50651
50800
 
50652
- translate$2(crosshairGroup, event.x, event.y);
50653
- });
50801
+ if (shapes.length !== 1) {
50802
+ return;
50803
+ }
50654
50804
 
50655
- // remove crosshair
50656
- eventBus.on('spaceTool.selection.cleanup', function(event) {
50657
- var context = event.context,
50658
- crosshairGroup = context.crosshairGroup;
50805
+ var shape = shapes[0];
50659
50806
 
50660
- if (crosshairGroup) {
50661
- remove$1(crosshairGroup);
50807
+ var host = shape.host;
50808
+
50809
+ if (host) {
50810
+ canvas.addMarker(host, MARKER_ATTACH$1);
50811
+
50812
+ eventBus.once([
50813
+ 'shape.move.out',
50814
+ 'shape.move.cleanup'
50815
+ ], function() {
50816
+ canvas.removeMarker(host, MARKER_ATTACH$1);
50817
+ });
50662
50818
  }
50663
50819
  });
50664
50820
 
50665
- // add and update move/resize previews
50666
- eventBus.on('spaceTool.move', LOW_PRIORITY$3, function(event) {
50821
+ // add all attachers to move closure
50822
+ this.preExecuted('elements.move', HIGH_PRIORITY$3, function(e) {
50823
+ var context = e.context,
50824
+ closure = context.closure,
50825
+ shapes = context.shapes,
50826
+ attachers = getAttachers(shapes);
50667
50827
 
50668
- var context = event.context,
50669
- line = context.line,
50670
- axis = context.axis,
50671
- movingShapes = context.movingShapes,
50672
- resizingShapes = context.resizingShapes;
50828
+ forEach$1(attachers, function(attacher) {
50829
+ closure.add(attacher, closure.topLevel[attacher.host.id]);
50830
+ });
50831
+ });
50673
50832
 
50674
- if (!context.initialized) {
50833
+ // perform the attaching after shapes are done moving
50834
+ this.postExecuted('elements.move', function(e) {
50835
+
50836
+ var context = e.context,
50837
+ shapes = context.shapes,
50838
+ newHost = context.newHost,
50839
+ attachers;
50840
+
50841
+ // only single elements can be attached
50842
+ // multiply elements can be detached
50843
+ if (newHost && shapes.length !== 1) {
50675
50844
  return;
50676
50845
  }
50677
50846
 
50678
- if (!context.dragGroup) {
50679
- var spaceLayer = canvas.getLayer('space');
50847
+ if (newHost) {
50848
+ attachers = shapes;
50849
+ } else {
50680
50850
 
50681
- line = create$1('path');
50682
- attr(line, 'd', 'M0,0 L0,0');
50683
- classes(line).add('djs-crosshair');
50851
+ // find attachers moved without host
50852
+ attachers = filter(shapes, function(shape) {
50853
+ var host = shape.host;
50684
50854
 
50685
- append(spaceLayer, line);
50855
+ return isAttacher(shape) && !includes$2(shapes, host);
50856
+ });
50857
+ }
50686
50858
 
50687
- context.line = line;
50859
+ forEach$1(attachers, function(attacher) {
50860
+ modeling.updateAttachment(attacher, newHost);
50861
+ });
50862
+ });
50688
50863
 
50689
- var dragGroup = create$1('g');
50690
- attr(dragGroup, styles.cls('djs-drag-group', [ 'no-events' ]));
50864
+ // ensure invalid attachment connections are removed
50865
+ this.postExecuted('elements.move', function(e) {
50691
50866
 
50692
- append(canvas.getActiveLayer(), dragGroup);
50867
+ var shapes = e.context.shapes;
50693
50868
 
50694
- // shapes
50695
- addPreviewGfx(movingShapes, dragGroup);
50869
+ forEach$1(shapes, function(shape) {
50696
50870
 
50697
- // connections
50698
- var movingConnections = context.movingConnections = elementRegistry.filter(function(element) {
50699
- var sourceIsMoving = false;
50871
+ forEach$1(shape.attachers, function(attacher) {
50700
50872
 
50701
- forEach$1(movingShapes, function(shape) {
50702
- forEach$1(shape.outgoing, function(connection) {
50703
- if (element === connection) {
50704
- sourceIsMoving = true;
50705
- }
50873
+ // remove invalid outgoing connections
50874
+ forEach$1(attacher.outgoing.slice(), function(connection) {
50875
+ var allowed = rules.allowed('connection.reconnect', {
50876
+ connection: connection,
50877
+ source: connection.source,
50878
+ target: connection.target
50706
50879
  });
50707
- });
50708
-
50709
- var targetIsMoving = false;
50710
50880
 
50711
- forEach$1(movingShapes, function(shape) {
50712
- forEach$1(shape.incoming, function(connection) {
50713
- if (element === connection) {
50714
- targetIsMoving = true;
50715
- }
50716
- });
50881
+ if (!allowed) {
50882
+ modeling.removeConnection(connection);
50883
+ }
50717
50884
  });
50718
50885
 
50719
- var sourceIsResizing = false;
50720
-
50721
- forEach$1(resizingShapes, function(shape) {
50722
- forEach$1(shape.outgoing, function(connection) {
50723
- if (element === connection) {
50724
- sourceIsResizing = true;
50725
- }
50886
+ // remove invalid incoming connections
50887
+ forEach$1(attacher.incoming.slice(), function(connection) {
50888
+ var allowed = rules.allowed('connection.reconnect', {
50889
+ connection: connection,
50890
+ source: connection.source,
50891
+ target: connection.target
50726
50892
  });
50727
- });
50728
-
50729
- var targetIsResizing = false;
50730
50893
 
50731
- forEach$1(resizingShapes, function(shape) {
50732
- forEach$1(shape.incoming, function(connection) {
50733
- if (element === connection) {
50734
- targetIsResizing = true;
50735
- }
50736
- });
50894
+ if (!allowed) {
50895
+ modeling.removeConnection(connection);
50896
+ }
50737
50897
  });
50738
-
50739
- return isConnection$6(element)
50740
- && (sourceIsMoving || sourceIsResizing)
50741
- && (targetIsMoving || targetIsResizing);
50742
50898
  });
50899
+ });
50900
+ });
50743
50901
 
50902
+ this.postExecute('shape.create', function(e) {
50903
+ var context = e.context,
50904
+ shape = context.shape,
50905
+ host = context.host;
50744
50906
 
50745
- addPreviewGfx(movingConnections, dragGroup);
50746
-
50747
- context.dragGroup = dragGroup;
50907
+ if (host) {
50908
+ modeling.updateAttachment(shape, host);
50748
50909
  }
50910
+ });
50749
50911
 
50750
- if (!context.frameGroup) {
50751
- var frameGroup = create$1('g');
50752
- attr(frameGroup, styles.cls('djs-frame-group', [ 'no-events' ]));
50753
-
50754
- append(canvas.getActiveLayer(), frameGroup);
50912
+ // update attachments if the host is replaced
50913
+ this.postExecute('shape.replace', function(e) {
50755
50914
 
50756
- var frames = [];
50915
+ var context = e.context,
50916
+ oldShape = context.oldShape,
50917
+ newShape = context.newShape;
50757
50918
 
50758
- forEach$1(resizingShapes, function(shape) {
50759
- var frame = previewSupport.addFrame(shape, frameGroup);
50919
+ // move the attachers to the new host
50920
+ saveClear(oldShape.attachers, function(attacher) {
50921
+ var allowed = rules.allowed('elements.move', {
50922
+ target: newShape,
50923
+ shapes: [ attacher ]
50924
+ });
50760
50925
 
50761
- var initialBounds = frame.getBBox();
50926
+ if (allowed === 'attach') {
50927
+ modeling.updateAttachment(attacher, newShape);
50928
+ } else {
50929
+ modeling.removeShape(attacher);
50930
+ }
50931
+ });
50762
50932
 
50763
- frames.push({
50764
- element: frame,
50765
- initialBounds: initialBounds
50766
- });
50933
+ // move attachers if new host has different size
50934
+ if (newShape.attachers.length) {
50767
50935
 
50768
- canvas.addMarker(shape, MARKER_RESIZING);
50936
+ forEach$1(newShape.attachers, function(attacher) {
50937
+ var delta = getNewAttachShapeDelta(attacher, oldShape, newShape);
50938
+ modeling.moveShape(attacher, delta, attacher.parent);
50769
50939
  });
50770
-
50771
- context.frameGroup = frameGroup;
50772
- context.frames = frames;
50773
50940
  }
50774
50941
 
50775
- var orientation = {
50776
- x: 'M' + event.x + ', -10000 L' + event.x + ', 10000',
50777
- y: 'M -10000, ' + event.y + ' L 10000, ' + event.y
50778
- };
50779
-
50780
- attr(line, { d: orientation[ axis ] });
50781
-
50782
- var opposite = { x: 'y', y: 'x' };
50783
- var delta = { x: event.dx, y: event.dy };
50784
- delta[ opposite[ context.axis ] ] = 0;
50785
-
50786
- // update move previews
50787
- translate$2(context.dragGroup, delta.x, delta.y);
50942
+ });
50788
50943
 
50789
- // update resize previews
50790
- forEach$1(context.frames, function(frame) {
50791
- var element = frame.element,
50792
- initialBounds = frame.initialBounds,
50793
- width,
50794
- height;
50944
+ // move shape on host resize
50945
+ this.postExecute('shape.resize', function(event) {
50946
+ var context = event.context,
50947
+ shape = context.shape,
50948
+ oldBounds = context.oldBounds,
50949
+ newBounds = context.newBounds,
50950
+ attachers = shape.attachers,
50951
+ hints = context.hints || {};
50795
50952
 
50796
- if (context.direction === 'e') {
50797
- attr(element, {
50798
- width: max(initialBounds.width + delta.x, 5)
50799
- });
50800
- } else {
50801
- width = max(initialBounds.width - delta.x, 5);
50953
+ if (hints.attachSupport === false) {
50954
+ return;
50955
+ }
50802
50956
 
50803
- attr(element, {
50804
- width: width,
50805
- x: initialBounds.x + initialBounds.width - width
50806
- });
50807
- }
50957
+ forEach$1(attachers, function(attacher) {
50958
+ var delta = getNewAttachShapeDelta(attacher, oldBounds, newBounds);
50808
50959
 
50809
- if (context.direction === 's') {
50810
- attr(element, {
50811
- height: max(initialBounds.height + delta.y, 5)
50812
- });
50813
- } else {
50814
- height = max(initialBounds.height - delta.y, 5);
50960
+ modeling.moveShape(attacher, delta, attacher.parent);
50815
50961
 
50816
- attr(element, {
50817
- height: height,
50818
- y: initialBounds.y + initialBounds.height - height
50819
- });
50820
- }
50962
+ forEach$1(attacher.labels, function(label) {
50963
+ modeling.moveShape(label, delta, label.parent);
50964
+ });
50821
50965
  });
50822
-
50823
50966
  });
50824
50967
 
50825
- // remove move/resize previews
50826
- eventBus.on('spaceTool.cleanup', function(event) {
50827
-
50828
- var context = event.context,
50829
- movingShapes = context.movingShapes,
50830
- movingConnections = context.movingConnections,
50831
- resizingShapes = context.resizingShapes,
50832
- line = context.line,
50833
- dragGroup = context.dragGroup,
50834
- frameGroup = context.frameGroup;
50835
-
50836
- // moving shapes
50837
- forEach$1(movingShapes, function(shape) {
50838
- canvas.removeMarker(shape, MARKER_DRAGGING$1);
50839
- });
50840
-
50841
- // moving connections
50842
- forEach$1(movingConnections, function(connection) {
50843
- canvas.removeMarker(connection, MARKER_DRAGGING$1);
50844
- });
50968
+ // remove attachments
50969
+ this.preExecute('shape.delete', function(event) {
50845
50970
 
50846
- if (dragGroup) {
50847
- remove$1(line);
50848
- remove$1(dragGroup);
50849
- }
50971
+ var shape = event.context.shape;
50850
50972
 
50851
- forEach$1(resizingShapes, function(shape) {
50852
- canvas.removeMarker(shape, MARKER_RESIZING);
50973
+ saveClear(shape.attachers, function(attacher) {
50974
+ modeling.removeShape(attacher);
50853
50975
  });
50854
50976
 
50855
- if (frameGroup) {
50856
- remove$1(frameGroup);
50977
+ if (shape.host) {
50978
+ modeling.updateAttachment(shape, null);
50857
50979
  }
50858
50980
  });
50859
50981
  }
50860
50982
 
50861
- SpaceToolPreview.$inject = [
50983
+ e(AttachSupport, CommandInterceptor);
50984
+
50985
+ AttachSupport.$inject = [
50986
+ 'injector',
50862
50987
  'eventBus',
50863
- 'elementRegistry',
50864
50988
  'canvas',
50865
- 'styles',
50866
- 'previewSupport'
50989
+ 'rules',
50990
+ 'modeling'
50867
50991
  ];
50868
50992
 
50869
50993
 
50870
- // helpers //////////////////////
50994
+ /**
50995
+ * Return attachers of the given shapes
50996
+ *
50997
+ * @param {Array<djs.model.Base>} shapes
50998
+ * @return {Array<djs.model.Base>}
50999
+ */
51000
+ function getAttachers(shapes) {
51001
+ return flatten(map$1(shapes, function(s) {
51002
+ return s.attachers || [];
51003
+ }));
51004
+ }
50871
51005
 
50872
51006
  /**
50873
- * Checks if an element is a connection.
51007
+ * Return a combined list of elements and
51008
+ * attachers.
51009
+ *
51010
+ * @param {Array<djs.model.Base>} elements
51011
+ * @return {Array<djs.model.Base>} filtered
50874
51012
  */
50875
- function isConnection$6(element) {
50876
- return element.waypoints;
51013
+ function addAttached(elements) {
51014
+ var attachers = getAttachers(elements);
51015
+
51016
+ return unionBy('id', elements, attachers);
50877
51017
  }
50878
51018
 
50879
- var SpaceToolModule = {
50880
- __init__: [ 'spaceToolPreview' ],
51019
+ /**
51020
+ * Return a filtered list of elements that do not
51021
+ * contain attached elements with hosts being part
51022
+ * of the selection.
51023
+ *
51024
+ * @param {Array<djs.model.Base>} elements
51025
+ *
51026
+ * @return {Array<djs.model.Base>} filtered
51027
+ */
51028
+ function removeAttached(elements) {
51029
+
51030
+ var ids = groupBy(elements, 'id');
51031
+
51032
+ return filter(elements, function(element) {
51033
+ while (element) {
51034
+
51035
+ // host in selection
51036
+ if (element.host && ids[element.host.id]) {
51037
+ return false;
51038
+ }
51039
+
51040
+ element = element.parent;
51041
+ }
51042
+
51043
+ return true;
51044
+ });
51045
+ }
51046
+
51047
+ function isAttacher(shape) {
51048
+ return !!shape.host;
51049
+ }
51050
+
51051
+ function includes$2(array, item) {
51052
+ return array.indexOf(item) !== -1;
51053
+ }
51054
+
51055
+ var AttachSupportModule = {
50881
51056
  __depends__: [
50882
- DraggingModule,
50883
- RulesModule$1,
50884
- ToolManagerModule,
50885
- PreviewSupportModule,
50886
- MouseModule
51057
+ RulesModule$1
50887
51058
  ],
50888
- spaceTool: [ 'type', SpaceTool ],
50889
- spaceToolPreview: [ 'type', SpaceToolPreview ]
51059
+ __init__: [ 'attachSupport' ],
51060
+ attachSupport: [ 'type', AttachSupport ]
50890
51061
  };
50891
51062
 
50892
51063
  function BpmnFactory(moddle) {
@@ -53554,9 +53725,7 @@
53554
53725
  target = connection.target,
53555
53726
  waypoints = copyWaypoints(connection),
53556
53727
  axis = getAxisFromDirection(direction),
53557
- layoutHints = {
53558
- labelBehavior: false
53559
- };
53728
+ layoutHints = {};
53560
53729
 
53561
53730
  if (includes$1(affectedShapes, source) && includes$1(affectedShapes, target)) {
53562
53731