caixanegra 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,1281 @@
1
+ window.Caixanegra.Designer = {
2
+ Unit: class extends Sabertooth.Object {
3
+ #drawValues;
4
+ title;
5
+ class;
6
+ type;
7
+ exits;
8
+ mappings;
9
+ connectingExit;
10
+ UNIT_WIDTH;
11
+ SNAP;
12
+ MARGIN;
13
+ debugHits;
14
+ #beingDragged;
15
+
16
+ constructor(params = {}) {
17
+ super(params);
18
+ this.title = params.title || "untitled";
19
+ this.type = params.type || "unspecified";
20
+ this.class = params.class || "unspecified";
21
+ this.exits = params.exits || [];
22
+ this.mappings = params.mappings || {};
23
+ this.#drawValues = {};
24
+ this.MARGIN = 10;
25
+ this.SNAP = 10;
26
+ this.UNIT_WIDTH = 300;
27
+ this.connectingExit = null;
28
+ this.debugHits = [];
29
+ this.#beingDragged = false;
30
+ }
31
+
32
+ initialize(context) {
33
+ const ctx = context.context2d;
34
+ this.#drawValues.position = this.getPosition(context.referential);
35
+ this.size = new Sabertooth.Vector2(0, 0);
36
+
37
+ this.size.x = this.UNIT_WIDTH;
38
+ let hCursor = this.#drawValues.position.y;
39
+ this.#drawValues.unitBgColor = Caixanegra.Designer.typeColor(this.type);
40
+ this.#drawValues.text = {};
41
+ this.#drawValues.exitRectangles = [];
42
+
43
+ hCursor += this.MARGIN;
44
+ this.#drawValues.text.title = { y: hCursor };
45
+ hCursor += 25; //title
46
+ hCursor += this.MARGIN;
47
+ this.#drawValues.text.class = { y: hCursor };
48
+ hCursor += 18; //class
49
+ hCursor += Math.round(this.MARGIN * (this.exits.length > 0 ? 1.8 : 1));
50
+
51
+ const eH = 14;
52
+ this.#drawValues.exits = [];
53
+ ctx.font = "bold 14px monospace";
54
+ ctx.textAlign = "right";
55
+ ctx.lineWidth = 2;
56
+ this.exits.forEach((exit) => {
57
+ const exitToPush ={
58
+ reference: exit,
59
+ x: this.#drawValues.position.x + this.size.x - this.MARGIN,
60
+ markerY: hCursor + Math.round(eH / 2),
61
+ y: hCursor
62
+ };
63
+ const exitNameWidth = ctx.measureText(exit.name).width;
64
+ exitToPush.rectangle = new Sabertooth.Rectangle(
65
+ Sabertooth.Vector2.subtract(
66
+ new Sabertooth.Vector2(exitToPush.x - exitNameWidth, hCursor),
67
+ this.#drawValues.position
68
+ ),
69
+ new Sabertooth.Vector2(exitNameWidth, eH)
70
+ )
71
+ this.#drawValues.exits.push(exitToPush);
72
+
73
+ hCursor += this.MARGIN;
74
+ hCursor += eH; //exit
75
+ });
76
+
77
+ this.size.y = hCursor - this.#drawValues.position.y;
78
+
79
+ this.#drawValues.center = new Sabertooth.Vector2(
80
+ this.#drawValues.position.x + Math.round(this.size.x / 2),
81
+ this.#drawValues.position.y + Math.round(this.size.y / 2)
82
+ );
83
+ }
84
+
85
+ update(context) {
86
+ if (context.mouse.down &&
87
+ context.mouse.down.button === 1 &&
88
+ context.mouse.down.cursorAt &&
89
+ context.mouse.down.cursorAt.object.oid === this.oid) {
90
+ this.#beingDragged = true;
91
+ } else {
92
+ this.#beingDragged = false;
93
+ }
94
+
95
+ if (context.mouse.move &&
96
+ context.mouse.move.button === 1 &&
97
+ context.mouse.down &&
98
+ context.mouse.down.cursorAt &&
99
+ context.mouse.down.cursorAt.object.oid === this.oid) {
100
+ const intersection = new Sabertooth.Vector2(
101
+ context.mouse.down.cursorAt.intersection.x,
102
+ context.mouse.down.cursorAt.intersection.y
103
+ );
104
+
105
+ if (this.connectingExit === null) {
106
+ for (let eidx = 0; eidx < this.#drawValues.exits.length; eidx++) {
107
+ if (this.#drawValues.exits[eidx].rectangle.intersectionPoint(intersection)) {
108
+ this.connectingExit = this.#drawValues.exits[eidx];
109
+ break;
110
+ }
111
+ }
112
+ }
113
+
114
+ if (!this.connectingExit) {
115
+ this.position.x = context.mouse.move.x - intersection.x;
116
+ this.position.y = context.mouse.move.y - intersection.y;
117
+ this.position.x = Math.round(this.position.x / this.SNAP) * this.SNAP;
118
+ this.position.y = Math.round(this.position.y / this.SNAP) * this.SNAP;
119
+ this.initialize(context);
120
+ }
121
+ }
122
+ this.#drawValues.position = this.getPosition(context.referential);
123
+ }
124
+
125
+ draw(context, pass) {
126
+ const ctx = context.context2d;
127
+
128
+ switch(pass) {
129
+ case "base":
130
+ const selected = window.Caixanegra.Designer.core.selectedUnit?.oid === this.oid;
131
+ if (this.#beingDragged || selected) {
132
+ ctx.strokeStyle = "rgba(255, 255, 255, 0.05)";
133
+ ctx.lineWidth = 2;
134
+
135
+ ctx.beginPath();
136
+ ctx.moveTo(0, this.#drawValues.center.y);
137
+ ctx.lineTo(context.canvas.width, this.#drawValues.center.y);
138
+ ctx.moveTo(this.#drawValues.center.x, 0);
139
+ ctx.lineTo(this.#drawValues.center.x, context.canvas.height);
140
+ ctx.stroke();
141
+
142
+ ctx.strokeStyle = "rgba(255, 255, 255, 0.15)";
143
+ ctx.beginPath();
144
+ ctx.moveTo(0, this.#drawValues.position.y);
145
+ ctx.lineTo(context.canvas.width, this.#drawValues.position.y);
146
+ ctx.moveTo(0, this.#drawValues.position.y + this.size.y);
147
+ ctx.lineTo(context.canvas.width, this.#drawValues.position.y + this.size.y);
148
+
149
+ ctx.moveTo(this.#drawValues.position.x, 0);
150
+ ctx.lineTo(this.#drawValues.position.x, context.canvas.height);
151
+ ctx.moveTo(this.#drawValues.position.x + this.size.x, 0);
152
+ ctx.lineTo(this.#drawValues.position.x + this.size.x, context.canvas.height);
153
+
154
+ ctx.stroke();
155
+ }
156
+
157
+ this.#drawValues.exits.forEach((exit) => {
158
+ const hitsForExit = this.debugHits.filter(hit => hit?.out?.result?.exit_through === exit.reference.name);
159
+ if (hitsForExit.length > 0) {
160
+ ctx.strokeStyle = "#0F0";
161
+ ctx.lineWidth = 3;
162
+ } else {
163
+ ctx.strokeStyle = "#FFF";
164
+ ctx.lineWidth = 2;
165
+ }
166
+ ctx.beginPath();
167
+ ctx.moveTo(this.#drawValues.position.x + this.size.x, exit.markerY);
168
+ ctx.lineTo(this.#drawValues.position.x + this.size.x + 25, exit.markerY);
169
+
170
+ if (this.connectingExit === exit && context.mouse.move) {
171
+ ctx.lineTo(context.mouse.move.x, context.mouse.move.y);
172
+ } else if (exit.reference.target) {
173
+ const targetCenter = exit.reference.target.getCenter(context.referential);
174
+ ctx.lineTo(targetCenter.x, targetCenter.y);
175
+ }
176
+
177
+ ctx.stroke();
178
+ });
179
+ break;
180
+ case "extra":
181
+ ctx.beginPath();
182
+ ctx.roundRect(this.#drawValues.position.x, this.#drawValues.position.y, this.size.x, this.size.y, 5);
183
+ ctx.fillStyle = this.#drawValues.unitBgColor;
184
+ ctx.fill();
185
+
186
+ ctx.beginPath();
187
+ ctx.roundRect(this.#drawValues.position.x, this.#drawValues.position.y, this.size.x, this.size.y, 5);
188
+ ctx.strokeStyle = "#FFF";
189
+ ctx.stroke();
190
+
191
+ let gco = ctx.globalCompositeOperation;
192
+ ctx.globalCompositeOperation = "difference";
193
+ ctx.textBaseline = "bottom";
194
+ ctx.textAlign = "left";
195
+ ctx.fillStyle = "rgba(255, 255, 255, 0.75)";
196
+ ctx.font = "15px Helvetica";
197
+ ctx.fillText(
198
+ this.oid,
199
+ this.#drawValues.position.x,
200
+ this.#drawValues.position.y - ((this.debugHits.length > 0) ? 15 : 5),
201
+ this.UNIT_WIDTH
202
+ );
203
+ ctx.globalCompositeOperation = gco;
204
+
205
+ ctx.textBaseline = "top";
206
+ ctx.textAlign = "center";
207
+ ctx.fillStyle = "#000";
208
+ ctx.font = "25px Helvetica";
209
+ ctx.fillText(this.title, this.#drawValues.center.x, this.#drawValues.text.title.y, this.UNIT_WIDTH - this.MARGIN * 2);
210
+
211
+ ctx.fillStyle = "rgba(0,0,0,0.5)";
212
+ ctx.font = "18px Helvetica";
213
+ ctx.fillText(this.class, this.#drawValues.center.x, this.#drawValues.text.class.y, this.UNIT_WIDTH - this.MARGIN * 2);
214
+
215
+ ctx.strokeStyle = "#FFF";
216
+ ctx.fillStyle = "rgba(0,0,0,0.8)";
217
+ ctx.font = "bold 14px monospace";
218
+ ctx.textAlign = "right";
219
+ ctx.lineWidth = 2;
220
+ this.#drawValues.exits.forEach((exit) => {
221
+ ctx.fillText(exit.reference.name, exit.x, exit.y, this.UNIT_WIDTH - this.MARGIN * 2);
222
+ });
223
+ break;
224
+ case "debug":
225
+ if (this.debugHits.length > 0) {
226
+ const lastDebugHit = this.debugHits[this.debugHits.length - 1];
227
+ ctx.beginPath();
228
+ ctx.lineWidth = 2;
229
+ if ("exception" in lastDebugHit) {
230
+ ctx.strokeStyle = "#F00";
231
+ } else {
232
+ ctx.strokeStyle = "#0F0";
233
+ }
234
+ ctx.roundRect(
235
+ this.#drawValues.position.x - 10,
236
+ this.#drawValues.position.y - 10,
237
+ this.size.x + 20,
238
+ this.size.y + 20,
239
+ 5
240
+ );
241
+ ctx.stroke();
242
+
243
+ ctx.fillStyle = "#FFF";
244
+ ctx.font = "bold 14px monospace";
245
+ ctx.textAlign = "right";
246
+ ctx.lineWidth = 2;
247
+ ctx.fillText(
248
+ this.debugHits.length,
249
+ this.#drawValues.position.x + this.size.x + 10,
250
+ this.#drawValues.position.y - 30,
251
+ this.size.x
252
+ );
253
+ }
254
+ break;
255
+ }
256
+ }
257
+ },
258
+ Core: class {
259
+ #loadedComponents;
260
+ #catalog;
261
+ #units;
262
+ #sequence;
263
+ gre; //graphic rendering engine
264
+ api;
265
+ blocker;
266
+ messenger;
267
+ unitDetailPane;
268
+ unitDebugHitsPane;
269
+ selectedUnit;
270
+ flowId;
271
+ unitScope;
272
+
273
+ constructor(drawingSurface) {
274
+ this.flowId = this.#extractFlowId();
275
+ this.unitScope = this.#extractUnitScope();
276
+ this.#sequence = 0;
277
+ this.#loadedComponents = {
278
+ catalog: false,
279
+ flow: false
280
+ }
281
+ this.gre = new Sabertooth.Engine(drawingSurface);
282
+ this.gre.drawPasses.push("extra");
283
+ this.gre.drawPasses.push("debug");
284
+ this.api = new Caixanegra.API();
285
+ this.blocker = document.querySelector("#blocker");
286
+ this.messenger = document.querySelector("#action_messenger");
287
+ this.unitDetailPane = document.querySelector("#unitDetail");
288
+ this.unitDebugHitsPane = document.querySelector("#unitDebugHits");
289
+ document.querySelector("#save").addEventListener("click", this.saveFlow.bind(this));
290
+ document.querySelector("#addUnit").addEventListener("click", this.toggleUnitMenu.bind(this));
291
+ document.querySelector("#unitDetailTitle").addEventListener("keyup", this.updateSelectedUnit.bind(this));
292
+ document.querySelector("#toggler").addEventListener("click", this.toggleDeleteConfirmation.bind(this));
293
+ document.querySelector("#cancelDelete").addEventListener("click", this.toggleDeleteConfirmation.bind(this));
294
+ document.querySelector("#confirmDelete").addEventListener("click", this.deleteUnit.bind(this));
295
+ document.querySelector("#play").addEventListener("click", this.#executeFlow.bind(this));
296
+ document.querySelector("#configure").addEventListener("click", this.toggleExecutionConfiguration.bind(this));
297
+ document.querySelector("#console").addEventListener("click", this.toggleExecutionConsole.bind(this));
298
+ document.querySelector("#clearCarryOverObject").addEventListener("click", this.clearCarryOverObject.bind(this));
299
+ document.querySelector("#reset").addEventListener("click", this.resetExecution.bind(this));
300
+ this.toggleBlocker(true, "loading your flow");
301
+ window.addEventListener("resize", this.#windowResized.bind(drawingSurface));
302
+ drawingSurface.addEventListener("update_start", this.#engineUpdate.bind(this));
303
+ drawingSurface.addEventListener("drag_finished", this.#dragFinished.bind(this));
304
+ drawingSurface.addEventListener("clicked", this.#clicked.bind(this));
305
+ this.#windowResized.bind(drawingSurface)({ target: window });
306
+ this.#units = new Array();
307
+
308
+ this.coeAssignType(document.querySelector("#carryOverObject"), "object");
309
+
310
+ this.gre.disable();
311
+ this.getUnits();
312
+ this.loadFlow();
313
+ this.gre.enable();
314
+ }
315
+
316
+ clearCarryOverObject() {
317
+ document.querySelector("#carryOverObject").innerHTML = "";
318
+ this.coeAssignType(document.querySelector("#carryOverObject"), "object");
319
+ }
320
+
321
+ coeAssignType(container, type) {
322
+ container.innerHTML = "";
323
+ container.dataset.type = type
324
+
325
+ switch (type) {
326
+ case "object":
327
+ case "array":
328
+ container.parentElement.classList.add("-indent");
329
+ const rows = document.createElement("div");
330
+ rows.classList.add("rows");
331
+ const addRow = document.createElement("button");
332
+
333
+ addRow.innerHTML = "+";
334
+ addRow.addEventListener("click", this.coeAddRow.bind(this, rows));
335
+
336
+ container.append(rows, addRow);
337
+ break;
338
+ case "string":
339
+ case "number":
340
+ container.parentElement.classList.add("-inline");
341
+ const input = document.createElement("input");
342
+ input.placeholder = `${type} value`;
343
+ container.append(input);
344
+ break;
345
+ }
346
+ }
347
+
348
+ coeTypeSelectors(container) {
349
+ const symbols = {object: "{}", array: "[]", string: "ab", number: "12"};
350
+ ["object", "array", "string", "number"].forEach((type) => {
351
+ const button = document.createElement("button");
352
+ button.innerHTML = symbols[type];
353
+ button.addEventListener("click", this.coeAssignType.bind(this, container, type));
354
+ container.append(button);
355
+ });
356
+ }
357
+
358
+ coeAddRow(container) {
359
+ const type = container.parentElement.dataset.type || "object";
360
+ const wrapper = document.createElement("div");
361
+ wrapper.classList.add("row");
362
+
363
+ switch (type) {
364
+ case "object":
365
+ const keySide = document.createElement("div");
366
+ const valueSide = document.createElement("div");
367
+ const key = document.createElement("input");
368
+ key.placeholder = "object key";
369
+ this.coeTypeSelectors(valueSide);
370
+ keySide.classList.add("key-side");
371
+ keySide.append(key);
372
+ wrapper.append(keySide, valueSide);
373
+ break;
374
+ case "array":
375
+ const arrayItem = document.createElement("div");
376
+ this.coeTypeSelectors(arrayItem);
377
+ wrapper.append(arrayItem);
378
+ break;
379
+ }
380
+
381
+ container.append(wrapper);
382
+ }
383
+
384
+ deleteUnit() {
385
+ this.removeUnit(this.selectedUnit.oid);
386
+ this.toggleDeleteConfirmation();
387
+ this.unitDetailPane.classList.remove("-open");
388
+ this.unitDebugHitsPane.classList.remove("-open");
389
+ }
390
+
391
+ flushToConsole(entries) {
392
+ const container = document.querySelector("#executionConsole");
393
+ container.innerHTML = "";
394
+
395
+ entries.forEach((entry) => {
396
+ const entryWrapper = document.createElement("div");
397
+ const timestamp = document.createElement("div");
398
+ const message = document.createElement("div");
399
+ entryWrapper.classList.add("console-entry");
400
+ timestamp.classList.add("timestamp");
401
+ message.classList.add("message");
402
+ const date = new Date(entry.timestamp * 1000);
403
+ timestamp.innerHTML = date.toLocaleString();
404
+ message.innerHTML = entry.message;
405
+ entryWrapper.append(timestamp, message);
406
+
407
+ container.append(entryWrapper);
408
+ });
409
+ }
410
+
411
+ toggleExecutionConfiguration() {
412
+ const pane = document.querySelector("#executionConfiguration");
413
+ if (pane.classList.contains("-open")) {
414
+ pane.classList.remove("-open");
415
+ } else {
416
+ document.querySelector("#executionConsole").classList.remove("-open");
417
+ pane.classList.add("-open");
418
+ }
419
+ }
420
+
421
+ toggleExecutionConsole() {
422
+ const pane = document.querySelector("#executionConsole");
423
+ if (pane.classList.contains("-open")) {
424
+ pane.classList.remove("-open");
425
+ } else {
426
+ document.querySelector("#executionConfiguration").classList.remove("-open");
427
+ pane.classList.add("-open");
428
+ }
429
+ }
430
+
431
+ toggleDeleteConfirmation() {
432
+ const deleteToggle = document.querySelector(".delete");
433
+
434
+ if (deleteToggle.classList.contains("-confirming")) {
435
+ deleteToggle.classList.remove("-confirming");
436
+ } else {
437
+ deleteToggle.classList.add("-confirming");
438
+ }
439
+ }
440
+
441
+ toggleUnitMenu() {
442
+ const unitMenu = document.querySelector("#unitMenu");
443
+ const addUnit = document.querySelector("#addUnit");
444
+
445
+ if(unitMenu.classList.contains("-open")) {
446
+ unitMenu.classList.remove("-open");
447
+ addUnit.classList.remove("-open");
448
+ } else {
449
+ unitMenu.classList.add("-open");
450
+ addUnit.classList.add("-open");
451
+ }
452
+ }
453
+
454
+ saveFlow() {
455
+ this.actionMessage(true, "Saving...", "working");
456
+ this.api.saveFlow(this.flowId, this.flowSnapshot()).then((response) => {
457
+ this.actionMessage(true, "Saved", "ok");
458
+ setTimeout(() => {this.actionMessage(false)}, 4000);
459
+ });
460
+ }
461
+
462
+ flowSnapshot() {
463
+ const snapshot = {
464
+ entrypoint: this.#units.find((unit) => {return unit.type === "starter"})?.oid,
465
+ units: []
466
+ };
467
+
468
+ this.#units.forEach((unit) => {
469
+ if (unit.type === "starter" && snapshot.entrypoint !== null) {
470
+ snapshot.entrypoint = unit.oid;
471
+ }
472
+
473
+ const unitToPush = {
474
+ oid: unit.oid,
475
+ class: unit.class,
476
+ type: unit.type,
477
+ title: unit.title,
478
+ position: unit.position.toAnonObject(),
479
+ exits: [],
480
+ mappings: {}
481
+ };
482
+
483
+ unit.exits.forEach((exit) => {
484
+ unitToPush.exits.push({
485
+ name: exit.name,
486
+ target: exit?.target?.oid,
487
+ mappings: exit.mappings || []
488
+ });
489
+ });
490
+
491
+ Object.keys(unit?.mappings || {}).forEach((key) => {
492
+ unitToPush.mappings[key] = {
493
+ type: unit.mappings[key].type,
494
+ value: unit.mappings[key].value
495
+ }
496
+ });
497
+
498
+ snapshot.units.push(unitToPush);
499
+ });
500
+
501
+ return snapshot;
502
+ }
503
+
504
+ #extractUnitScope() {
505
+ const params = window.location.search.replace("?", "").split("&");
506
+ for(let pidx = 0; pidx < params.length; pidx++) {
507
+ const [key, value] = params[pidx].split("=");
508
+ if(key === "unit_scope")
509
+ return value;
510
+ }
511
+
512
+ return "";
513
+ }
514
+
515
+ #extractFlowId() {
516
+ const splittedURL = window.location.pathname.split("/");
517
+ return splittedURL[splittedURL.length - 1];
518
+ }
519
+
520
+ getUnits() {
521
+ this.api.units(this.unitScope).then((response) => {
522
+ this.#catalog = response;
523
+ const unitMenu = document.querySelector("#unitMenu");
524
+ unitMenu.innerHTML = "";
525
+ this.#catalog.forEach((unitData) => {
526
+ const item = document.createElement("div");
527
+ const content = document.createElement("div");
528
+ const colorCode = document.createElement("div");
529
+ const header = document.createElement("div");
530
+ const name = document.createElement("span");
531
+ const type = document.createElement("span");
532
+ const description = document.createElement("div");
533
+ description.classList.add("description");
534
+ item.classList.add("unit");
535
+ header.classList.add("header");
536
+ content.classList.add("content");
537
+ colorCode.classList.add("color-code");
538
+ name.classList.add("name");
539
+ type.classList.add("type");
540
+
541
+ name.innerHTML = unitData.title;
542
+ type.innerHTML = unitData.type;
543
+ description.innerHTML = unitData.description;
544
+ colorCode.style.backgroundColor = Caixanegra.Designer.typeColor(unitData.type);
545
+
546
+ header.append(name, type);
547
+ content.append(header, description);
548
+ item.append(colorCode, content);
549
+ item.addEventListener("click", this.createUnit.bind(this, unitData));
550
+ unitMenu.appendChild(item);
551
+ });
552
+ this.#loadedComponents.catalog = true;
553
+ this.#reveal();
554
+ });
555
+ }
556
+
557
+ loadFlow() {
558
+ this.api.getFlow(this.flowId).then((response) => {
559
+ const flowData = response;
560
+ this.gre.clear();
561
+ this.#units = [];
562
+
563
+ (flowData?.units || []).forEach((unit) => {
564
+ this.createUnit(unit);
565
+ });
566
+
567
+ this.#units.forEach((unit) => {
568
+ (unit?.exits || []).forEach((exit) => {
569
+ const object = this.#units.find((targetUnit) => targetUnit.oid === exit.target);
570
+ if (object) { exit.target = object; }
571
+ });
572
+ });
573
+
574
+ this.#loadedComponents.flow = true;
575
+ this.#reveal();
576
+ });
577
+ }
578
+
579
+ #reveal() {
580
+ if (this.#loadedComponents.flow && this.#loadedComponents.catalog) {
581
+ this.toggleBlocker(false);
582
+ }
583
+ }
584
+
585
+ toggleBlocker(toggle, message = "") {
586
+ if (toggle) {
587
+ this.blocker.classList.remove("-released");
588
+ } else {
589
+ this.blocker.classList.add("-released");
590
+ }
591
+
592
+ if(toggle) {
593
+ this.blocker.querySelector(".message").innerHTML = message;
594
+ }
595
+ }
596
+
597
+ actionMessage(toggle, message = "", icon = "") {
598
+ if (toggle) {
599
+ this.messenger.classList.add("-visible");
600
+ } else {
601
+ this.messenger.classList.remove("-visible");
602
+ }
603
+
604
+ this.messenger.querySelectorAll(".icon").forEach((i) => { i.style.display = "none"; });
605
+ if (icon !== "") {
606
+ const iconElement = this.messenger.querySelector(`.icon.icon-${icon}`);
607
+
608
+ if(iconElement)
609
+ iconElement.style.display = "block";
610
+ }
611
+
612
+ this.messenger.querySelector(".message").innerHTML = message;
613
+ }
614
+
615
+ createUnit(params) {
616
+ this.#sequence++;
617
+ const newUnit = new Caixanegra.Designer.Unit({
618
+ position: new Sabertooth.Vector2(20, 20),
619
+ type: params.type,
620
+ title: params.title,
621
+ class: params.class,
622
+ zIndex: this.#sequence
623
+ });
624
+
625
+ if (params.oid) {
626
+ newUnit.oid = params.oid;
627
+ newUnit.position = new Sabertooth.Vector2(params.position.x, params.position.y);
628
+ newUnit.title = params.title;
629
+ }
630
+
631
+ if (params.exits && params.exits.length > 0) {
632
+ newUnit.exits = params.exits.map((exit) => {
633
+ const newExit = { name: exit.name };
634
+ if (exit.target) {
635
+ newExit.target = exit.target;
636
+ }
637
+ if (exit.mappings) {
638
+ newExit.mappings = exit.mappings;
639
+ }
640
+ return newExit;
641
+ });
642
+ }
643
+
644
+ const mappingKeys = Object.keys(params?.mappings || {});
645
+ if (params.mappings && mappingKeys.length > 0) {
646
+ mappingKeys.forEach((key) => {
647
+ newUnit.mappings[key] = {};
648
+ if (params.mappings[key].type) {
649
+ newUnit.mappings[key].type = params.mappings[key].type;
650
+ }
651
+ if (params.mappings[key].value) {
652
+ newUnit.mappings[key].value = params.mappings[key].value;
653
+ }
654
+ });
655
+ }
656
+
657
+ this.#units.push(newUnit);
658
+ this.gre.addObject(newUnit);
659
+ }
660
+
661
+ removeUnit(oid) {
662
+ const killIndex = this.#units.findIndex((unit) => {return unit.oid === oid});
663
+ this.#units.splice(killIndex, 1);
664
+ this.gre.removeObject(oid);
665
+
666
+ this.#units.forEach((unit) => {
667
+ unit.exits.forEach((exit) => {
668
+ if (exit.target.oid === oid) {
669
+ exit.target = null;
670
+ }
671
+ });
672
+ });
673
+ }
674
+
675
+ showError(message) {
676
+ this.actionMessage(true, message, "error");
677
+ setTimeout(() => {this.actionMessage(false)}, 5000);
678
+ }
679
+
680
+ digInitialCarryOver(base) {
681
+ const baseType = base.dataset.type;
682
+ let baseReturn = null;
683
+
684
+ switch(baseType) {
685
+ case "object":
686
+ baseReturn = {};
687
+ break;
688
+ case "array":
689
+ baseReturn = [];
690
+ break;
691
+ case "number":
692
+ return base.querySelector("input").value * 1;
693
+ case "string":
694
+ return base.querySelector("input").value;
695
+ }
696
+
697
+ base.querySelector(".rows").childNodes.forEach((row) => {
698
+ switch(baseType) {
699
+ case "object":
700
+ const key = row.querySelector(".key-side input").value;
701
+ baseReturn[key] = this.digInitialCarryOver(row.querySelector("[data-type]"));
702
+ break;
703
+ case "array":
704
+ baseReturn.push(this.digInitialCarryOver(row.querySelector("[data-type]")));
705
+ break;
706
+ }
707
+ });
708
+
709
+ return baseReturn;
710
+ }
711
+
712
+ resetExecution() {
713
+ for(let idx = 0; idx < this.#units.length; idx++) {
714
+ this.#units[idx].debugHits = [];
715
+ }
716
+ document.querySelector("#console").style.display = "none";
717
+ document.querySelector("#reset").style.display = "none";
718
+ document.querySelector("#play").style.display = "block";
719
+ document.querySelector("#configure").style.display = "block";
720
+ this.unitDetailPane.classList.remove("-open");
721
+ this.unitDebugHitsPane.classList.remove("-open");
722
+ }
723
+
724
+ #executeFlow() {
725
+ this.toggleBlocker(true, "saving your flow");
726
+
727
+ this.api.saveFlow(this.flowId, this.flowSnapshot()).then(() => {
728
+ this.toggleBlocker(true, "running your flow");
729
+ let initialCarryOver = {};
730
+ try {
731
+ initialCarryOver = this.digInitialCarryOver(document.querySelector("#carryOverObject"));
732
+ } catch (error) {
733
+ initialCarryOver = null;
734
+ }
735
+
736
+ if (initialCarryOver === null) {
737
+ this.showError("Invalid initial carry-over");
738
+ this.toggleBlocker(false);
739
+ return;
740
+ }
741
+ this.api.debugRun(this.flowId, this.unitScope, initialCarryOver).then((response) => {
742
+ const executionData = response;
743
+ executionData.debug.steps.forEach((step) => {
744
+ const unit = this.#units.find((unit) => unit.oid === step.oid);
745
+ if (unit) {
746
+ unit.debugHits.push(step);
747
+ }
748
+ });
749
+
750
+ this.flushToConsole(executionData.debug.history);
751
+ document.querySelector("#console").style.display = "block";
752
+ document.querySelector("#reset").style.display = "block";
753
+ document.querySelector("#play").style.display = "none";
754
+ document.querySelector("#configure").style.display = "none";
755
+ document.querySelector("#executionConfiguration").classList.remove("-open");
756
+
757
+ this.toggleBlocker(false);
758
+ });
759
+ });
760
+ }
761
+
762
+ #dragFinished(ev) {
763
+ const moments = ev.detail;
764
+
765
+ const startObject = moments.start.cursorAt?.object;
766
+ const endObject = moments.end.cursorAt?.object;
767
+
768
+ if (startObject && endObject &&
769
+ startObject?.connectingExit &&
770
+ startObject.oid !== endObject.oid) {
771
+ startObject.connectingExit.reference.target = moments.end.cursorAt.object;
772
+ startObject.connectingExit = null;
773
+ } else if (startObject && !endObject && startObject?.connectingExit) {
774
+ startObject.connectingExit.reference.target = null;
775
+ startObject.connectingExit = null;
776
+ } else if (startObject && endObject && startObject.oid === endObject.oid) {
777
+ startObject.connectingExit = null;
778
+ }
779
+ }
780
+
781
+ #clicked(ev) {
782
+ const moments = ev.detail;
783
+ const endObject = moments.end.cursorAt?.object;
784
+
785
+ if (endObject) {
786
+ this.#fillDetailPane(endObject);
787
+ } else {
788
+ const unitMenu = document.querySelector("#unitMenu");
789
+ const addUnit = document.querySelector("#addUnit");
790
+
791
+ this.unitDetailPane.classList.remove("-open");
792
+ this.unitDebugHitsPane.classList.remove("-open");
793
+ unitMenu.classList.remove("-open");
794
+ addUnit.classList.remove("-open");
795
+
796
+ this.selectedUnit = null;
797
+ }
798
+ }
799
+
800
+ #buildStepHitFold(flow, data) {
801
+ if (data === undefined) {
802
+ return null
803
+ }
804
+
805
+ const wrapper = document.createElement("div");
806
+ const header = document.createElement("div");
807
+ const content = document.createElement("div");
808
+ wrapper.classList.add("hit-fold");
809
+ header.classList.add("header");
810
+ content.classList.add("content", "-open");
811
+ header.innerHTML = flow;
812
+
813
+ header.addEventListener("click", (e) => {
814
+ const content = e.target.parentElement.querySelector(".content");
815
+ if (content.classList.contains("-open")) {
816
+ content.classList.remove("-open");
817
+ } else {
818
+ content.classList.add("-open");
819
+ }
820
+ });
821
+
822
+ switch(flow) {
823
+ case "in":
824
+ case "out":
825
+ const carryOver = document.createElement("div");
826
+ carryOver.classList.add("carry-over");
827
+ let jsonString = JSON.stringify(
828
+ (flow === "in" ? {carryover: data.carry_over || {}, storage: data.storage || {}} : data.result) || {},
829
+ null, "\t"
830
+ );
831
+
832
+ jsonString = jsonString.replace(/\t/g, "<span class='json-indent'></span>");
833
+ jsonString = jsonString.replace(/\n/g, "<br />");
834
+
835
+ carryOver.innerHTML = jsonString;
836
+
837
+ content.append(carryOver);
838
+ break;
839
+ case "exception":
840
+ const message = data.message;
841
+ const backtrace = data.backtrace;
842
+
843
+ const messageElement = document.createElement("div");
844
+ const backtraceElement = document.createElement("div");
845
+
846
+ messageElement.innerHTML = message;
847
+ backtraceElement.innerHTML = backtrace[0];
848
+
849
+ content.append(messageElement, backtraceElement);
850
+ break;
851
+ }
852
+
853
+ wrapper.append(header, content);
854
+
855
+ return wrapper;
856
+ }
857
+
858
+ #fillDebugStepPane(container, hits) {
859
+ container.innerHTML = "";
860
+
861
+ for(let idx = 0; idx < hits.length; idx++) {
862
+ const hit = hits[idx];
863
+
864
+ const hitWrapper = document.createElement("div");
865
+ hitWrapper.classList.add("step-hit");
866
+ if(hit.exception) {
867
+ hitWrapper.classList.add("-nok");
868
+ } else {
869
+ hitWrapper.classList.add("-ok");
870
+ }
871
+ const header = document.createElement("div");
872
+ header.classList.add("hit-header");
873
+ header.innerHTML = `hit #${idx + 1}`;
874
+ hitWrapper.append(header);
875
+
876
+ let section = this.#buildStepHitFold("in", hit.in);
877
+ if (section !== null) { hitWrapper.append(section); }
878
+
879
+ section = this.#buildStepHitFold("out", hit.out);
880
+ if (section !== null) { hitWrapper.append(section); }
881
+
882
+ section = this.#buildStepHitFold("exception", hit.exception);
883
+ if (section !== null) { hitWrapper.append(section); }
884
+
885
+ container.append(hitWrapper);
886
+ }
887
+ }
888
+
889
+ #fillDetailPane(object) {
890
+ this.selectedUnit = object;
891
+ const matrix = this.#catalog.find((unit) => unit.class === object.class);
892
+ const pane = this.unitDetailPane;
893
+ const debugPane = this.unitDebugHitsPane;
894
+ const dynamicContent = pane.querySelector("#dynamicContent");
895
+ pane.querySelector(".color-code").style.backgroundColor = Caixanegra.Designer.typeColor(object.type);
896
+ pane.classList.add("-open");
897
+
898
+ if (object.debugHits.length > 0) {
899
+ debugPane.classList.add("-open");
900
+ this.#fillDebugStepPane(debugPane.querySelector("#debugData"), object.debugHits);
901
+ } else {
902
+ debugPane.querySelector("#debugData").innerHTML = "";
903
+ debugPane.classList.remove("-open");
904
+ }
905
+
906
+ pane.querySelector("#unitDetailTitle").value = object.title;
907
+ pane.querySelector("#unitDetailClass .name").innerHTML = matrix.class;
908
+ pane.querySelector("#unitDetailDescription").innerHTML = matrix.description;
909
+ pane.querySelector(".delete").classList.remove("-confirming");
910
+
911
+ dynamicContent.innerHTML = "";
912
+
913
+ if (Object.keys((matrix?.inputs || {})).length > 0) {
914
+ const inputsHeader = document.createElement("div");
915
+ inputsHeader.classList.add("unit-detail-headers");
916
+ inputsHeader.innerHTML = "input mapping"
917
+ dynamicContent.appendChild(inputsHeader);
918
+
919
+ Object.keys(matrix?.inputs).forEach((key) => {
920
+ dynamicContent.append(this.#buildInputConfigHandler(key, matrix.inputs[key]));
921
+ })
922
+ }
923
+
924
+ const unitAssignmentsDatalist = document.createElement("datalist");
925
+ unitAssignmentsDatalist.id = `${object.oid}-assignments`;
926
+
927
+ (matrix.assignments || []).forEach((assignment) => {
928
+ const option = document.createElement("option");
929
+ option.setAttribute("value", assignment);
930
+ unitAssignmentsDatalist.append(option);
931
+ });
932
+ dynamicContent.appendChild(unitAssignmentsDatalist);
933
+
934
+ if ((matrix?.exits || []).length > 0) {
935
+ const exitsHeader = document.createElement("div");
936
+ exitsHeader.classList.add("unit-detail-headers");
937
+ exitsHeader.innerHTML = "exit mapping"
938
+ dynamicContent.appendChild(exitsHeader);
939
+
940
+ object.exits.forEach((exit) => {
941
+ dynamicContent.append(this.#buildExitConfigHandler(exit));
942
+ });
943
+ }
944
+ }
945
+
946
+ #buildExitConfigHandler(exit) {
947
+ const unit = this.selectedUnit;
948
+ const wrapper = document.createElement("div");
949
+ wrapper.classList.add("unit-exit");
950
+ wrapper.dataset.exit = exit.name;
951
+ const name = document.createElement("div");
952
+ name.classList.add("field-name");
953
+ name.innerHTML = exit.name;
954
+ wrapper.append(name);
955
+
956
+ const mappingsWrapper = document.createElement("div");
957
+ mappingsWrapper.classList.add("mappings");
958
+
959
+ const targetUnitInputDatalist = document.createElement("datalist");
960
+ targetUnitInputDatalist.id = `${unit.oid}-${exit.name}-target-inputs`;
961
+
962
+ const target = this.#catalog.find((cUnit) => cUnit.class === exit?.target?.class);
963
+ if (target) {
964
+ Object.keys(target.inputs || []).forEach((input) => {
965
+ const option = document.createElement("option");
966
+ option.setAttribute("value", input);
967
+ targetUnitInputDatalist.append(option);
968
+ });
969
+ }
970
+
971
+ dynamicContent.append(targetUnitInputDatalist);
972
+ let datalists = {
973
+ unitAssignments: `${unit.oid}-assignments`,
974
+ targetUnitInputs: targetUnitInputDatalist.id,
975
+ };
976
+
977
+ (exit.mappings || []).forEach((mapping) => {
978
+ mappingsWrapper.append(this.#buildExitMappingHandler(datalists, mapping));
979
+ });
980
+
981
+ wrapper.append(mappingsWrapper);
982
+
983
+ const addButton = document.createElement("button");
984
+ addButton.type = "button";
985
+ addButton.innerHTML = "new mapping";
986
+ addButton.dataset.exitName = exit.name;
987
+ addButton.addEventListener("click", this.#addUnitExitMapping.bind(this));
988
+ wrapper.append(addButton);
989
+
990
+ return wrapper;
991
+ }
992
+
993
+ #buildExitMappingHandler(datalists, mapping) {
994
+ const mappingWrapper = document.createElement("div");
995
+ const valueWrapper = document.createElement("div");
996
+ valueWrapper.classList.add("mapping-values");
997
+ const usePair = document.createElement("div");
998
+ const asPair = document.createElement("div");
999
+ usePair.classList.add("label-pair");
1000
+ asPair.classList.add("label-pair");
1001
+ const useLabel = document.createElement("div");
1002
+ const asLabel = document.createElement("div");
1003
+ useLabel.classList.add("label");
1004
+ asLabel.classList.add("label");
1005
+ useLabel.innerHTML = "Use";
1006
+ asLabel.innerHTML = "As";
1007
+ mappingWrapper.classList.add("exit-mapping");
1008
+ const use = document.createElement("input");
1009
+ use.classList.add("use");
1010
+ use.value = mapping.use;
1011
+ const as = document.createElement("input");
1012
+ as.classList.add("as");
1013
+ as.value = mapping.as;
1014
+
1015
+ usePair.append(useLabel, use);
1016
+ asPair.append(asLabel, as);
1017
+
1018
+ use.addEventListener("change", this.#unitExitMappingsChanged.bind(this));
1019
+ as.addEventListener("change", this.#unitExitMappingsChanged.bind(this));
1020
+
1021
+ use.setAttribute("list", datalists["unitAssignments"]);
1022
+ as.setAttribute("list", datalists["targetUnitInputs"]);
1023
+
1024
+ const deleteButton = document.createElement("button");
1025
+ deleteButton.type = "button";
1026
+ deleteButton.innerHTML = "&times;";
1027
+ deleteButton.addEventListener("click", this.#deleteExitMapping.bind(this));
1028
+
1029
+ valueWrapper.append(usePair, asPair);
1030
+ mappingWrapper.append(valueWrapper, deleteButton);
1031
+ return mappingWrapper;
1032
+ }
1033
+
1034
+ #deleteExitMapping(ev) {
1035
+ let mappingWrapper = ev.target;
1036
+ while (!mappingWrapper.classList.contains("exit-mapping")) {
1037
+ mappingWrapper = mappingWrapper.parentElement;
1038
+ if (mappingWrapper === null) { return null; }
1039
+ }
1040
+
1041
+ mappingWrapper.remove();
1042
+
1043
+ this.#unitExitMappingsChanged();
1044
+ }
1045
+
1046
+ #buildInputConfigHandler(input, matrix) {
1047
+ const unit = this.selectedUnit;
1048
+ const wrapper = document.createElement("div");
1049
+ const valueWrapper = document.createElement("div");
1050
+ wrapper.classList.add("unit-input", "field");
1051
+ valueWrapper.classList.add("unit-input-value");
1052
+ wrapper.dataset.input = input;
1053
+
1054
+ if(matrix.editable) {
1055
+ const typeSelectorHeader = document.createElement("div");
1056
+ const fieldDescription = document.createElement("div");
1057
+ fieldDescription.classList.add("field-description");
1058
+ typeSelectorHeader.classList.add("field-header");
1059
+ const name = document.createElement("div");
1060
+ name.classList.add("name");
1061
+ const displayName = document.createElement("div");
1062
+ const internalName = document.createElement("div");
1063
+ displayName.innerHTML = matrix.display;
1064
+ internalName.innerHTML = input;
1065
+ displayName.classList.add("display");
1066
+ internalName.classList.add("internal");
1067
+ name.append(displayName, internalName);
1068
+ fieldDescription.innerHTML = matrix.description;
1069
+ const typeSelector = document.createElement("select");
1070
+ [
1071
+ {value: "carryover", display: "Carry-over"},
1072
+ {value: "user", display: "User"},
1073
+ {value: "storage", display: "Global storage"},
1074
+ ].forEach((option) => {
1075
+ const optionElement = document.createElement("option");
1076
+ optionElement.innerHTML = option.display;
1077
+ optionElement.setAttribute("value", option.value);
1078
+ if(unit.mappings[input]?.type === option.value) {
1079
+ optionElement.setAttribute("selected", "selected");
1080
+ }
1081
+ typeSelector.append(optionElement);
1082
+ });
1083
+ typeSelector.addEventListener("change", this.#unitInputTypeChanged.bind(this));
1084
+ typeSelectorHeader.append(name, typeSelector);
1085
+
1086
+ wrapper.appendChild(typeSelectorHeader);
1087
+ wrapper.appendChild(fieldDescription);
1088
+
1089
+ let valueInput = null;
1090
+ switch (matrix.type) {
1091
+ case "string":
1092
+ case "number":
1093
+ case "regex":
1094
+ {
1095
+ valueInput = document.createElement("input");
1096
+ if(matrix.type === "regex") { valueInput.classList.add("-monospace")}
1097
+ valueInput.value = unit.mappings[input]?.value || matrix.default || "";
1098
+ valueInput.addEventListener("change", this.#unitInputValueChanged.bind(this));
1099
+ }
1100
+ break;
1101
+ case "dataset":
1102
+ {
1103
+ const selectedValue = unit.mappings[input]?.value || matrix.default || "";
1104
+ valueInput = document.createElement("select");
1105
+ matrix.set.forEach((setOption => {
1106
+ const option = document.createElement("option");
1107
+ option.value = setOption.value;
1108
+ option.innerHTML = setOption.display;
1109
+ if (setOption.value === selectedValue) {
1110
+ option.setAttribute("selected", "selected");
1111
+ }
1112
+ valueInput.appendChild(option);
1113
+ }));
1114
+ valueInput.addEventListener("change", this.#unitInputValueChanged.bind(this));
1115
+ }
1116
+ break;
1117
+ }
1118
+
1119
+ if (unit.mappings[input]?.type !== "user") {
1120
+ valueWrapper.classList.add("-disabled");
1121
+ }
1122
+ valueWrapper.appendChild(valueInput);
1123
+
1124
+ wrapper.append(valueWrapper);
1125
+ } else {
1126
+ const fieldHeader = document.createElement("div");
1127
+ fieldHeader.classList.add("field-header");
1128
+ const fieldDescription = document.createElement("div");
1129
+ fieldDescription.classList.add("field-description");
1130
+ fieldDescription.innerHTML = matrix.description;
1131
+ const name = document.createElement("div");
1132
+ const displayName = document.createElement("div");
1133
+ const internalName = document.createElement("div");
1134
+ displayName.innerHTML = matrix.display;
1135
+ internalName.innerHTML = input;
1136
+ displayName.classList.add("display");
1137
+ internalName.classList.add("internal");
1138
+ const type = document.createElement("div");
1139
+ name.classList.add("name");
1140
+ type.classList.add("type");
1141
+ type.innerHTML = "Carry-over";
1142
+ name.append(displayName, internalName);
1143
+ fieldHeader.append(name, type);
1144
+ wrapper.append(fieldHeader, fieldDescription);
1145
+ }
1146
+
1147
+ return wrapper;
1148
+ }
1149
+
1150
+ #unitInputValueChanged(ev) {
1151
+ let valueWrapper = ev.target;
1152
+ while (!valueWrapper.classList.contains("unit-input")) {
1153
+ valueWrapper = valueWrapper.parentElement;
1154
+ if (valueWrapper === null) { return null; }
1155
+ }
1156
+
1157
+ const input = valueWrapper.dataset.input;
1158
+ this.selectedUnit.mappings[input] = this.selectedUnit.mappings[input] || {};
1159
+ this.selectedUnit.mappings[input].value = ev.target.value;
1160
+ }
1161
+
1162
+ #addUnitExitMapping(ev) {
1163
+ const unit = this.selectedUnit;
1164
+ const mapping = { use: "", as: "" };
1165
+ const exitName = ev.target.dataset.exitName;
1166
+
1167
+ let datalists = {
1168
+ targetUnitInputs: `${unit.oid}-${exitName}-target-inputs`,
1169
+ unitAssignments: `${unit.oid}-assignments`
1170
+ };
1171
+
1172
+ ev.target.parentElement.querySelector(".mappings").append(
1173
+ this.#buildExitMappingHandler(datalists, mapping)
1174
+ );
1175
+
1176
+ this.#unitExitMappingsChanged();
1177
+ }
1178
+
1179
+ #unitExitMappingsChanged() {
1180
+ const unit = this.selectedUnit;
1181
+ const exits = this.unitDetailPane.querySelectorAll(".unit-exit");
1182
+ const exitMappings = {};
1183
+
1184
+ Array.from(exits).forEach((exit) => {
1185
+ exitMappings[exit.dataset.exit] =
1186
+ Array.from(exit.querySelectorAll(".exit-mapping")).map((mapping) => {
1187
+ const useValue = mapping.querySelector("input.use").value;
1188
+ const asValue = mapping.querySelector("input.as").value;
1189
+ return { use: useValue, as: asValue };
1190
+ });
1191
+ });
1192
+
1193
+ for(let idx = 0; idx < unit.exits.length; idx++) {
1194
+ const newMappings = exitMappings[unit.exits[idx].name];
1195
+
1196
+ if (newMappings) {
1197
+ unit.exits[idx].mappings = newMappings;
1198
+ }
1199
+ }
1200
+ }
1201
+
1202
+ #unitInputTypeChanged(ev) {
1203
+ let valueWrapper = ev.target;
1204
+
1205
+ while (!valueWrapper.classList.contains("unit-input")) {
1206
+ valueWrapper = valueWrapper.parentElement;
1207
+ if (valueWrapper === null) { return null; }
1208
+ }
1209
+
1210
+ const input = valueWrapper.dataset.input;
1211
+ valueWrapper = valueWrapper.querySelector(".unit-input-value");
1212
+
1213
+ switch (ev.target.value) {
1214
+ case "carryover":
1215
+ case "storage":
1216
+ valueWrapper.classList.add("-disabled");
1217
+ break;
1218
+ case "user":
1219
+ valueWrapper.classList.remove("-disabled");
1220
+ break;
1221
+ }
1222
+
1223
+ this.selectedUnit.mappings[input] = this.selectedUnit.mappings[input] || {};
1224
+ this.selectedUnit.mappings[input].type = ev.target.value;
1225
+ }
1226
+
1227
+ updateSelectedUnit() {
1228
+ const title = document.querySelector("#unitDetailTitle").value;
1229
+ this.selectedUnit.title = title === "" ? "untitled" : title;
1230
+ }
1231
+
1232
+ #engineUpdate(ev) {
1233
+ const context = ev.detail;
1234
+
1235
+ if (context.mouse.move &&
1236
+ [1, 2].includes(context.mouse.move.button) &&
1237
+ context.mouse.down &&
1238
+ context.mouse.down.cursorAt === null) {
1239
+ const offset = {
1240
+ x: context.mouse.move.internal_x - context.mouse.down.internal_x,
1241
+ y: context.mouse.move.internal_y - context.mouse.down.internal_y
1242
+ }
1243
+
1244
+ this.gre.worldCenter.x = context.mouse.down.referential.x + offset.x;
1245
+ this.gre.worldCenter.y = context.mouse.down.referential.y + offset.y;
1246
+
1247
+ for (let oidx = 0; oidx < context.objects.length; oidx++) {
1248
+ context.objects[oidx].initialize(this.gre.engineContext());
1249
+ }
1250
+ }
1251
+ }
1252
+
1253
+ #windowResized(ev) {
1254
+ this.width = ev.target.innerWidth * Sabertooth.UPSCALE_FACTOR;
1255
+ this.height = ev.target.innerHeight * Sabertooth.UPSCALE_FACTOR;
1256
+ this.style.width = `${this.width / Sabertooth.UPSCALE_FACTOR}px`;
1257
+ this.style.height = `${this.height / Sabertooth.UPSCALE_FACTOR}px`;
1258
+ }
1259
+ },
1260
+
1261
+ typeColor: (type) => {
1262
+ switch (type) {
1263
+ case "starter":
1264
+ return "#65CCA9";
1265
+ case "terminator":
1266
+ return "#E44";
1267
+ case "blackbox":
1268
+ return "#EEE";
1269
+ case "passthrough":
1270
+ return "#c4c66a";
1271
+ case "fork":
1272
+ return "#ffcc5c"
1273
+ case "feeder":
1274
+ return "#5a92d8";
1275
+ default:
1276
+ return "#FFF";
1277
+ }
1278
+ }
1279
+ }
1280
+
1281
+ window.Caixanegra.Designer.core = new Caixanegra.Designer.Core(document.querySelector("canvas"));