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