pict-section-flow 0.0.10 → 0.0.13

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.
Files changed (88) hide show
  1. package/.claude/launch.json +1 -1
  2. package/README.md +176 -0
  3. package/docs/.nojekyll +0 -0
  4. package/docs/Architecture.md +303 -0
  5. package/docs/Custom-Styling.md +275 -0
  6. package/docs/Data_Model.md +158 -0
  7. package/docs/Event_System.md +156 -0
  8. package/docs/Getting_Started.md +237 -0
  9. package/docs/Implementation_Reference.md +528 -0
  10. package/docs/Layout_Persistence.md +117 -0
  11. package/docs/README.md +115 -52
  12. package/docs/_cover.md +11 -0
  13. package/docs/_sidebar.md +52 -0
  14. package/docs/_topbar.md +8 -0
  15. package/docs/api/PictFlowCard.md +216 -0
  16. package/docs/api/PictFlowCardPropertiesPanel.md +235 -0
  17. package/docs/api/addConnection.md +101 -0
  18. package/docs/api/addNode.md +137 -0
  19. package/docs/api/autoLayout.md +77 -0
  20. package/docs/api/getFlowData.md +112 -0
  21. package/docs/api/marshalToView.md +95 -0
  22. package/docs/api/openPanel.md +128 -0
  23. package/docs/api/registerHandler.md +174 -0
  24. package/docs/api/registerNodeType.md +142 -0
  25. package/docs/api/removeConnection.md +57 -0
  26. package/docs/api/removeNode.md +80 -0
  27. package/docs/api/saveLayout.md +152 -0
  28. package/docs/api/screenToSVGCoords.md +68 -0
  29. package/docs/api/selectNode.md +116 -0
  30. package/docs/api/setTheme.md +168 -0
  31. package/docs/api/setZoom.md +97 -0
  32. package/docs/api/toggleFullscreen.md +68 -0
  33. package/docs/card-help/EACH.md +19 -0
  34. package/docs/card-help/FREAD.md +24 -0
  35. package/docs/card-help/FWRITE.md +24 -0
  36. package/docs/card-help/GET.md +22 -0
  37. package/docs/card-help/ITE.md +23 -0
  38. package/docs/card-help/LOG.md +23 -0
  39. package/docs/card-help/NOTE.md +17 -0
  40. package/docs/card-help/PREV.md +18 -0
  41. package/docs/card-help/SET.md +27 -0
  42. package/docs/card-help/SPKL.md +22 -0
  43. package/docs/card-help/STAT.md +23 -0
  44. package/docs/card-help/SW.md +25 -0
  45. package/docs/css/docuserve.css +73 -0
  46. package/docs/index.html +39 -0
  47. package/docs/retold-catalog.json +169 -0
  48. package/docs/retold-keyword-index.json +13942 -0
  49. package/example_applications/simple_cards/package.json +1 -0
  50. package/example_applications/simple_cards/source/card-help-content.js +16 -0
  51. package/example_applications/simple_cards/source/cards/FlowCard-Comment.js +2 -0
  52. package/example_applications/simple_cards/source/cards/FlowCard-DataPreview.js +2 -0
  53. package/example_applications/simple_cards/source/cards/FlowCard-Each.js +2 -0
  54. package/example_applications/simple_cards/source/cards/FlowCard-FileRead.js +2 -0
  55. package/example_applications/simple_cards/source/cards/FlowCard-FileWrite.js +2 -0
  56. package/example_applications/simple_cards/source/cards/FlowCard-GetValue.js +2 -0
  57. package/example_applications/simple_cards/source/cards/FlowCard-IfThenElse.js +2 -0
  58. package/example_applications/simple_cards/source/cards/FlowCard-LogValues.js +2 -0
  59. package/example_applications/simple_cards/source/cards/FlowCard-SetValue.js +2 -0
  60. package/example_applications/simple_cards/source/cards/FlowCard-Sparkline.js +2 -0
  61. package/example_applications/simple_cards/source/cards/FlowCard-StatusMonitor.js +2 -0
  62. package/example_applications/simple_cards/source/cards/FlowCard-Switch.js +2 -0
  63. package/package.json +11 -7
  64. package/scripts/generate-card-help.js +214 -0
  65. package/source/Pict-Section-Flow.js +4 -0
  66. package/source/PictFlowCard.js +3 -1
  67. package/source/providers/PictProvider-Flow-CSS.js +245 -152
  68. package/source/providers/PictProvider-Flow-ConnectorShapes.js +24 -0
  69. package/source/providers/PictProvider-Flow-Geometry.js +195 -38
  70. package/source/providers/PictProvider-Flow-PanelChrome.js +14 -12
  71. package/source/services/PictService-Flow-ConnectionHandleManager.js +263 -0
  72. package/source/services/PictService-Flow-ConnectionRenderer.js +134 -183
  73. package/source/services/PictService-Flow-DataManager.js +338 -0
  74. package/source/services/PictService-Flow-InteractionManager.js +165 -7
  75. package/source/services/PictService-Flow-PathGenerator.js +282 -0
  76. package/source/services/PictService-Flow-PortRenderer.js +269 -0
  77. package/source/services/PictService-Flow-RenderManager.js +281 -0
  78. package/source/services/PictService-Flow-Tether.js +6 -42
  79. package/source/views/PictView-Flow-Node.js +2 -220
  80. package/source/views/PictView-Flow-PropertiesPanel.js +89 -44
  81. package/source/views/PictView-Flow.js +130 -882
  82. package/test/ConnectionHandleManager_tests.js +717 -0
  83. package/test/ConnectionRenderer_tests.js +591 -0
  84. package/test/DataManager_tests.js +859 -0
  85. package/test/Geometry_tests.js +767 -0
  86. package/test/PathGenerator_tests.js +978 -0
  87. package/test/PortRenderer_tests.js +367 -0
  88. package/test/RenderManager_tests.js +756 -0
@@ -0,0 +1,717 @@
1
+ const libFable = require('fable');
2
+ const libChai = require('chai');
3
+ const libExpect = libChai.expect;
4
+
5
+ const libConnectionHandleManager = require('../source/services/PictService-Flow-ConnectionHandleManager.js');
6
+
7
+ suite
8
+ (
9
+ 'PictService-Flow-ConnectionHandleManager',
10
+ function ()
11
+ {
12
+ let _Fable;
13
+ let _HandleManager;
14
+ let _MockFlowView;
15
+
16
+ setup
17
+ (
18
+ function ()
19
+ {
20
+ _Fable = new libFable({});
21
+
22
+ _MockFlowView =
23
+ {
24
+ fable: _Fable,
25
+ log: _Fable.log,
26
+ _FlowData:
27
+ {
28
+ Nodes:
29
+ [
30
+ {
31
+ Hash: 'n1',
32
+ X: 100, Y: 100,
33
+ Width: 160, Height: 80,
34
+ Ports:
35
+ [
36
+ { Hash: 'p-out', Direction: 'output', Side: 'right', Label: 'Out' }
37
+ ]
38
+ },
39
+ {
40
+ Hash: 'n2',
41
+ X: 400, Y: 100,
42
+ Width: 160, Height: 80,
43
+ Ports:
44
+ [
45
+ { Hash: 'p-in', Direction: 'input', Side: 'left', Label: 'In' }
46
+ ]
47
+ }
48
+ ],
49
+ Connections:
50
+ [
51
+ {
52
+ Hash: 'c1',
53
+ SourceNodeHash: 'n1',
54
+ SourcePortHash: 'p-out',
55
+ TargetNodeHash: 'n2',
56
+ TargetPortHash: 'p-in',
57
+ Data: {}
58
+ }
59
+ ],
60
+ OpenPanels: [],
61
+ ViewState:
62
+ {
63
+ SelectedConnectionHash: null
64
+ }
65
+ },
66
+ _ConnectionRenderer:
67
+ {
68
+ computeInsertionIndex: function () { return 0; },
69
+ _computeDirectionalGeometry: function (pStart, pEnd)
70
+ {
71
+ return {
72
+ departX: pStart.x + 20,
73
+ departY: pStart.y,
74
+ approachX: pEnd.x - 20,
75
+ approachY: pEnd.y,
76
+ startDir: { dx: 1, dy: 0 },
77
+ endDir: { dx: -1, dy: 0 }
78
+ };
79
+ }
80
+ },
81
+ _TetherService:
82
+ {
83
+ resetHandlesForNode: function () {},
84
+ resetHandlePositions: function () {}
85
+ },
86
+ _EventHandlerProvider:
87
+ {
88
+ fireEvent: function () {}
89
+ },
90
+ getConnection: function (pHash)
91
+ {
92
+ return _MockFlowView._FlowData.Connections.find(
93
+ (pConn) => pConn.Hash === pHash
94
+ ) || null;
95
+ },
96
+ getPortPosition: function (pNodeHash, pPortHash)
97
+ {
98
+ let tmpNode = _MockFlowView._FlowData.Nodes.find((n) => n.Hash === pNodeHash);
99
+ if (!tmpNode) return null;
100
+ let tmpPort = tmpNode.Ports.find((p) => p.Hash === pPortHash);
101
+ if (!tmpPort) return null;
102
+ return {
103
+ x: tmpPort.Side === 'right' ? tmpNode.X + tmpNode.Width : tmpNode.X,
104
+ y: tmpNode.Y + tmpNode.Height / 2,
105
+ side: tmpPort.Side
106
+ };
107
+ },
108
+ _renderSingleConnection: function () {},
109
+ renderFlow: function () {},
110
+ marshalFromView: function () {}
111
+ };
112
+
113
+ _HandleManager = new libConnectionHandleManager(_Fable, { FlowView: _MockFlowView }, 'HM-Test');
114
+ }
115
+ );
116
+
117
+ // ---- Constructor ----
118
+
119
+ suite
120
+ (
121
+ 'Constructor',
122
+ function ()
123
+ {
124
+ test
125
+ (
126
+ 'should instantiate with correct serviceType',
127
+ function (fDone)
128
+ {
129
+ libExpect(_HandleManager).to.be.an('object');
130
+ libExpect(_HandleManager.serviceType).to.equal('PictServiceFlowConnectionHandleManager');
131
+ fDone();
132
+ }
133
+ );
134
+
135
+ test
136
+ (
137
+ 'should store FlowView reference from options',
138
+ function (fDone)
139
+ {
140
+ libExpect(_HandleManager._FlowView).to.equal(_MockFlowView);
141
+ fDone();
142
+ }
143
+ );
144
+
145
+ test
146
+ (
147
+ 'should handle missing FlowView',
148
+ function (fDone)
149
+ {
150
+ let tmpManager = new libConnectionHandleManager(_Fable, {}, 'NoView');
151
+ libExpect(tmpManager._FlowView).to.be.null;
152
+ fDone();
153
+ }
154
+ );
155
+ }
156
+ );
157
+
158
+ // ---- updateConnectionHandle ----
159
+
160
+ suite
161
+ (
162
+ 'updateConnectionHandle',
163
+ function ()
164
+ {
165
+ test
166
+ (
167
+ 'should update bezier-handle-N position',
168
+ function (fDone)
169
+ {
170
+ let tmpConn = _MockFlowView._FlowData.Connections[0];
171
+ tmpConn.Data.BezierHandles = [{ x: 200, y: 100 }];
172
+ tmpConn.Data.HandleCustomized = true;
173
+
174
+ _HandleManager.updateConnectionHandle('c1', 'bezier-handle-0', 250, 150);
175
+
176
+ libExpect(tmpConn.Data.BezierHandles[0].x).to.equal(250);
177
+ libExpect(tmpConn.Data.BezierHandles[0].y).to.equal(150);
178
+ fDone();
179
+ }
180
+ );
181
+
182
+ test
183
+ (
184
+ 'should update bezier-midpoint with legacy sync',
185
+ function (fDone)
186
+ {
187
+ let tmpConn = _MockFlowView._FlowData.Connections[0];
188
+ tmpConn.Data = {};
189
+
190
+ _HandleManager.updateConnectionHandle('c1', 'bezier-midpoint', 300, 200);
191
+
192
+ libExpect(tmpConn.Data.HandleCustomized).to.be.true;
193
+ libExpect(tmpConn.Data.BezierHandles).to.have.length(1);
194
+ libExpect(tmpConn.Data.BezierHandles[0].x).to.equal(300);
195
+ libExpect(tmpConn.Data.BezierHandles[0].y).to.equal(200);
196
+ // Legacy fields should be kept in sync
197
+ libExpect(tmpConn.Data.BezierHandleX).to.equal(300);
198
+ libExpect(tmpConn.Data.BezierHandleY).to.equal(200);
199
+ fDone();
200
+ }
201
+ );
202
+
203
+ test
204
+ (
205
+ 'should update existing bezier-midpoint handle',
206
+ function (fDone)
207
+ {
208
+ let tmpConn = _MockFlowView._FlowData.Connections[0];
209
+ tmpConn.Data = { BezierHandles: [{ x: 100, y: 100 }] };
210
+
211
+ _HandleManager.updateConnectionHandle('c1', 'bezier-midpoint', 500, 500);
212
+
213
+ libExpect(tmpConn.Data.BezierHandles[0].x).to.equal(500);
214
+ libExpect(tmpConn.Data.BezierHandles[0].y).to.equal(500);
215
+ fDone();
216
+ }
217
+ );
218
+
219
+ test
220
+ (
221
+ 'should update ortho-corner1',
222
+ function (fDone)
223
+ {
224
+ let tmpConn = _MockFlowView._FlowData.Connections[0];
225
+ tmpConn.Data = {};
226
+
227
+ _HandleManager.updateConnectionHandle('c1', 'ortho-corner1', 250, 80);
228
+
229
+ libExpect(tmpConn.Data.HandleCustomized).to.be.true;
230
+ libExpect(tmpConn.Data.OrthoCorner1X).to.equal(250);
231
+ libExpect(tmpConn.Data.OrthoCorner1Y).to.equal(80);
232
+ fDone();
233
+ }
234
+ );
235
+
236
+ test
237
+ (
238
+ 'should update ortho-corner2',
239
+ function (fDone)
240
+ {
241
+ let tmpConn = _MockFlowView._FlowData.Connections[0];
242
+ tmpConn.Data = {};
243
+
244
+ _HandleManager.updateConnectionHandle('c1', 'ortho-corner2', 350, 120);
245
+
246
+ libExpect(tmpConn.Data.OrthoCorner2X).to.equal(350);
247
+ libExpect(tmpConn.Data.OrthoCorner2Y).to.equal(120);
248
+ fDone();
249
+ }
250
+ );
251
+
252
+ test
253
+ (
254
+ 'should compute OrthoMidOffset for horizontal departure',
255
+ function (fDone)
256
+ {
257
+ let tmpConn = _MockFlowView._FlowData.Connections[0];
258
+ tmpConn.Data = {};
259
+
260
+ _HandleManager.updateConnectionHandle('c1', 'ortho-midpoint', 300, 100);
261
+
262
+ libExpect(tmpConn.Data.OrthoMidOffset).to.be.a('number');
263
+ fDone();
264
+ }
265
+ );
266
+
267
+ test
268
+ (
269
+ 'should call _renderSingleConnection for real-time feedback',
270
+ function (fDone)
271
+ {
272
+ let tmpRenderCalled = false;
273
+ _MockFlowView._renderSingleConnection = function () { tmpRenderCalled = true; };
274
+
275
+ let tmpConn = _MockFlowView._FlowData.Connections[0];
276
+ tmpConn.Data = {};
277
+
278
+ _HandleManager.updateConnectionHandle('c1', 'ortho-corner1', 250, 80);
279
+
280
+ libExpect(tmpRenderCalled).to.be.true;
281
+ fDone();
282
+ }
283
+ );
284
+
285
+ test
286
+ (
287
+ 'should do nothing for non-existent connection',
288
+ function (fDone)
289
+ {
290
+ // Should not throw
291
+ _HandleManager.updateConnectionHandle('non-existent', 'bezier-midpoint', 100, 100);
292
+ fDone();
293
+ }
294
+ );
295
+
296
+ test
297
+ (
298
+ 'should do nothing when no FlowView',
299
+ function (fDone)
300
+ {
301
+ let tmpManager = new libConnectionHandleManager(_Fable, {}, 'NoView');
302
+ tmpManager.updateConnectionHandle('c1', 'bezier-midpoint', 100, 100);
303
+ fDone();
304
+ }
305
+ );
306
+ }
307
+ );
308
+
309
+ // ---- addConnectionHandle ----
310
+
311
+ suite
312
+ (
313
+ 'addConnectionHandle',
314
+ function ()
315
+ {
316
+ test
317
+ (
318
+ 'should add a handle to a connection',
319
+ function (fDone)
320
+ {
321
+ let tmpConn = _MockFlowView._FlowData.Connections[0];
322
+ tmpConn.Data = {};
323
+
324
+ _HandleManager.addConnectionHandle('c1', 250, 150);
325
+
326
+ libExpect(tmpConn.Data.BezierHandles).to.have.length(1);
327
+ libExpect(tmpConn.Data.BezierHandles[0].x).to.equal(250);
328
+ libExpect(tmpConn.Data.BezierHandles[0].y).to.equal(150);
329
+ libExpect(tmpConn.Data.HandleCustomized).to.be.true;
330
+ libExpect(tmpConn.Data.LineMode).to.equal('bezier');
331
+ fDone();
332
+ }
333
+ );
334
+
335
+ test
336
+ (
337
+ 'should migrate legacy format before adding',
338
+ function (fDone)
339
+ {
340
+ let tmpConn = _MockFlowView._FlowData.Connections[0];
341
+ tmpConn.Data = { BezierHandleX: 200, BezierHandleY: 100 };
342
+
343
+ _HandleManager.addConnectionHandle('c1', 300, 200);
344
+
345
+ // Should have migrated legacy + added new
346
+ libExpect(tmpConn.Data.BezierHandles).to.have.length(2);
347
+ fDone();
348
+ }
349
+ );
350
+
351
+ test
352
+ (
353
+ 'should use computeInsertionIndex for placement',
354
+ function (fDone)
355
+ {
356
+ let tmpInsertIndexUsed = false;
357
+ _MockFlowView._ConnectionRenderer.computeInsertionIndex = function ()
358
+ {
359
+ tmpInsertIndexUsed = true;
360
+ return 0;
361
+ };
362
+
363
+ let tmpConn = _MockFlowView._FlowData.Connections[0];
364
+ tmpConn.Data = {};
365
+
366
+ _HandleManager.addConnectionHandle('c1', 250, 150);
367
+
368
+ libExpect(tmpInsertIndexUsed).to.be.true;
369
+ fDone();
370
+ }
371
+ );
372
+
373
+ test
374
+ (
375
+ 'should call renderFlow and marshalFromView',
376
+ function (fDone)
377
+ {
378
+ let tmpRenderCalled = false;
379
+ let tmpMarshalCalled = false;
380
+ _MockFlowView.renderFlow = function () { tmpRenderCalled = true; };
381
+ _MockFlowView.marshalFromView = function () { tmpMarshalCalled = true; };
382
+
383
+ let tmpConn = _MockFlowView._FlowData.Connections[0];
384
+ tmpConn.Data = {};
385
+
386
+ _HandleManager.addConnectionHandle('c1', 250, 150);
387
+
388
+ libExpect(tmpRenderCalled).to.be.true;
389
+ libExpect(tmpMarshalCalled).to.be.true;
390
+ fDone();
391
+ }
392
+ );
393
+
394
+ test
395
+ (
396
+ 'should fire onFlowChanged event',
397
+ function (fDone)
398
+ {
399
+ let tmpEvents = [];
400
+ _MockFlowView._EventHandlerProvider.fireEvent = function (pEvent) { tmpEvents.push(pEvent); };
401
+
402
+ let tmpConn = _MockFlowView._FlowData.Connections[0];
403
+ tmpConn.Data = {};
404
+
405
+ _HandleManager.addConnectionHandle('c1', 250, 150);
406
+
407
+ libExpect(tmpEvents).to.include('onFlowChanged');
408
+ fDone();
409
+ }
410
+ );
411
+
412
+ test
413
+ (
414
+ 'should do nothing for non-existent connection',
415
+ function (fDone)
416
+ {
417
+ _HandleManager.addConnectionHandle('non-existent', 250, 150);
418
+ fDone();
419
+ }
420
+ );
421
+
422
+ test
423
+ (
424
+ 'should do nothing when no FlowView',
425
+ function (fDone)
426
+ {
427
+ let tmpManager = new libConnectionHandleManager(_Fable, {}, 'NoView');
428
+ tmpManager.addConnectionHandle('c1', 250, 150);
429
+ fDone();
430
+ }
431
+ );
432
+ }
433
+ );
434
+
435
+ // ---- removeConnectionHandle ----
436
+
437
+ suite
438
+ (
439
+ 'removeConnectionHandle',
440
+ function ()
441
+ {
442
+ test
443
+ (
444
+ 'should remove a handle by index',
445
+ function (fDone)
446
+ {
447
+ let tmpConn = _MockFlowView._FlowData.Connections[0];
448
+ tmpConn.Data = {
449
+ BezierHandles: [{ x: 200, y: 100 }, { x: 300, y: 150 }],
450
+ HandleCustomized: true
451
+ };
452
+
453
+ _HandleManager.removeConnectionHandle('c1', 0);
454
+
455
+ libExpect(tmpConn.Data.BezierHandles).to.have.length(1);
456
+ libExpect(tmpConn.Data.BezierHandles[0].x).to.equal(300);
457
+ fDone();
458
+ }
459
+ );
460
+
461
+ test
462
+ (
463
+ 'should reset HandleCustomized when last handle removed',
464
+ function (fDone)
465
+ {
466
+ let tmpConn = _MockFlowView._FlowData.Connections[0];
467
+ tmpConn.Data = {
468
+ BezierHandles: [{ x: 200, y: 100 }],
469
+ HandleCustomized: true,
470
+ BezierHandleX: 200,
471
+ BezierHandleY: 100
472
+ };
473
+
474
+ _HandleManager.removeConnectionHandle('c1', 0);
475
+
476
+ libExpect(tmpConn.Data.BezierHandles).to.have.length(0);
477
+ libExpect(tmpConn.Data.HandleCustomized).to.be.false;
478
+ libExpect(tmpConn.Data.BezierHandleX).to.be.null;
479
+ libExpect(tmpConn.Data.BezierHandleY).to.be.null;
480
+ fDone();
481
+ }
482
+ );
483
+
484
+ test
485
+ (
486
+ 'should do nothing for out-of-range index',
487
+ function (fDone)
488
+ {
489
+ let tmpConn = _MockFlowView._FlowData.Connections[0];
490
+ tmpConn.Data = {
491
+ BezierHandles: [{ x: 200, y: 100 }],
492
+ HandleCustomized: true
493
+ };
494
+
495
+ _HandleManager.removeConnectionHandle('c1', 5);
496
+ libExpect(tmpConn.Data.BezierHandles).to.have.length(1);
497
+
498
+ _HandleManager.removeConnectionHandle('c1', -1);
499
+ libExpect(tmpConn.Data.BezierHandles).to.have.length(1);
500
+ fDone();
501
+ }
502
+ );
503
+
504
+ test
505
+ (
506
+ 'should do nothing when no BezierHandles array',
507
+ function (fDone)
508
+ {
509
+ let tmpConn = _MockFlowView._FlowData.Connections[0];
510
+ tmpConn.Data = {};
511
+
512
+ _HandleManager.removeConnectionHandle('c1', 0);
513
+ // Should not throw
514
+ fDone();
515
+ }
516
+ );
517
+
518
+ test
519
+ (
520
+ 'should do nothing for non-existent connection',
521
+ function (fDone)
522
+ {
523
+ _HandleManager.removeConnectionHandle('non-existent', 0);
524
+ fDone();
525
+ }
526
+ );
527
+
528
+ test
529
+ (
530
+ 'should call renderFlow and marshalFromView',
531
+ function (fDone)
532
+ {
533
+ let tmpRenderCalled = false;
534
+ let tmpMarshalCalled = false;
535
+ _MockFlowView.renderFlow = function () { tmpRenderCalled = true; };
536
+ _MockFlowView.marshalFromView = function () { tmpMarshalCalled = true; };
537
+
538
+ let tmpConn = _MockFlowView._FlowData.Connections[0];
539
+ tmpConn.Data = {
540
+ BezierHandles: [{ x: 200, y: 100 }],
541
+ HandleCustomized: true
542
+ };
543
+
544
+ _HandleManager.removeConnectionHandle('c1', 0);
545
+
546
+ libExpect(tmpRenderCalled).to.be.true;
547
+ libExpect(tmpMarshalCalled).to.be.true;
548
+ fDone();
549
+ }
550
+ );
551
+ }
552
+ );
553
+
554
+ // ---- resetHandlesForNode ----
555
+
556
+ suite
557
+ (
558
+ 'resetHandlesForNode',
559
+ function ()
560
+ {
561
+ test
562
+ (
563
+ 'should reset customized handles for connections involving the node',
564
+ function (fDone)
565
+ {
566
+ let tmpConn = _MockFlowView._FlowData.Connections[0];
567
+ tmpConn.Data =
568
+ {
569
+ HandleCustomized: true,
570
+ BezierHandleX: 200,
571
+ BezierHandleY: 100,
572
+ OrthoCorner1X: 150,
573
+ OrthoCorner1Y: 80,
574
+ OrthoCorner2X: 350,
575
+ OrthoCorner2Y: 120,
576
+ OrthoMidOffset: 25
577
+ };
578
+
579
+ _HandleManager.resetHandlesForNode('n1');
580
+
581
+ libExpect(tmpConn.Data.HandleCustomized).to.be.false;
582
+ libExpect(tmpConn.Data.BezierHandleX).to.be.null;
583
+ libExpect(tmpConn.Data.BezierHandleY).to.be.null;
584
+ libExpect(tmpConn.Data.OrthoCorner1X).to.be.null;
585
+ libExpect(tmpConn.Data.OrthoCorner1Y).to.be.null;
586
+ libExpect(tmpConn.Data.OrthoCorner2X).to.be.null;
587
+ libExpect(tmpConn.Data.OrthoCorner2Y).to.be.null;
588
+ libExpect(tmpConn.Data.OrthoMidOffset).to.equal(0);
589
+ fDone();
590
+ }
591
+ );
592
+
593
+ test
594
+ (
595
+ 'should not reset connections not involving the node',
596
+ function (fDone)
597
+ {
598
+ _MockFlowView._FlowData.Connections.push({
599
+ Hash: 'c2',
600
+ SourceNodeHash: 'n3',
601
+ TargetNodeHash: 'n4',
602
+ Data: { HandleCustomized: true, BezierHandleX: 500 }
603
+ });
604
+
605
+ _HandleManager.resetHandlesForNode('n1');
606
+
607
+ // c2 should be untouched
608
+ let tmpC2 = _MockFlowView._FlowData.Connections.find((c) => c.Hash === 'c2');
609
+ libExpect(tmpC2.Data.HandleCustomized).to.be.true;
610
+ libExpect(tmpC2.Data.BezierHandleX).to.equal(500);
611
+ fDone();
612
+ }
613
+ );
614
+
615
+ test
616
+ (
617
+ 'should call TetherService.resetHandlesForNode',
618
+ function (fDone)
619
+ {
620
+ let tmpTetherResetCalled = false;
621
+ _MockFlowView._TetherService.resetHandlesForNode = function ()
622
+ {
623
+ tmpTetherResetCalled = true;
624
+ };
625
+
626
+ _HandleManager.resetHandlesForNode('n1');
627
+
628
+ libExpect(tmpTetherResetCalled).to.be.true;
629
+ fDone();
630
+ }
631
+ );
632
+
633
+ test
634
+ (
635
+ 'should skip connections without Data',
636
+ function (fDone)
637
+ {
638
+ _MockFlowView._FlowData.Connections[0].Data = null;
639
+ // Should not throw
640
+ _HandleManager.resetHandlesForNode('n1');
641
+ fDone();
642
+ }
643
+ );
644
+
645
+ test
646
+ (
647
+ 'should do nothing when no FlowView',
648
+ function (fDone)
649
+ {
650
+ let tmpManager = new libConnectionHandleManager(_Fable, {}, 'NoView');
651
+ tmpManager.resetHandlesForNode('n1');
652
+ fDone();
653
+ }
654
+ );
655
+ }
656
+ );
657
+
658
+ // ---- resetHandlesForPanel ----
659
+
660
+ suite
661
+ (
662
+ 'resetHandlesForPanel',
663
+ function ()
664
+ {
665
+ test
666
+ (
667
+ 'should call TetherService.resetHandlePositions for the panel',
668
+ function (fDone)
669
+ {
670
+ let tmpResetPanel = null;
671
+ _MockFlowView._TetherService.resetHandlePositions = function (pPanel)
672
+ {
673
+ tmpResetPanel = pPanel;
674
+ };
675
+
676
+ _MockFlowView._FlowData.OpenPanels = [{ Hash: 'panel-1', NodeHash: 'n1' }];
677
+
678
+ _HandleManager.resetHandlesForPanel('panel-1');
679
+
680
+ libExpect(tmpResetPanel).to.not.be.null;
681
+ libExpect(tmpResetPanel.Hash).to.equal('panel-1');
682
+ fDone();
683
+ }
684
+ );
685
+
686
+ test
687
+ (
688
+ 'should do nothing for non-existent panel',
689
+ function (fDone)
690
+ {
691
+ let tmpResetCalled = false;
692
+ _MockFlowView._TetherService.resetHandlePositions = function ()
693
+ {
694
+ tmpResetCalled = true;
695
+ };
696
+
697
+ _HandleManager.resetHandlesForPanel('non-existent');
698
+
699
+ libExpect(tmpResetCalled).to.be.false;
700
+ fDone();
701
+ }
702
+ );
703
+
704
+ test
705
+ (
706
+ 'should do nothing when no FlowView',
707
+ function (fDone)
708
+ {
709
+ let tmpManager = new libConnectionHandleManager(_Fable, {}, 'NoView');
710
+ tmpManager.resetHandlesForPanel('panel-1');
711
+ fDone();
712
+ }
713
+ );
714
+ }
715
+ );
716
+ }
717
+ );