caixanegra 0.3.4 → 0.4.1
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/README.md +1 -1
- data/app/assets/javascripts/caixanegra/api.js +16 -0
- data/app/assets/javascripts/caixanegra/designer.js +289 -18
- data/app/assets/stylesheets/caixanegra/designer.scss +201 -5
- data/app/controllers/caixanegra/api/designer/inputs_controller.rb +32 -0
- data/app/views/caixanegra/designer/index.html.erb +47 -2
- data/app/views/layouts/caixanegra/application.html.erb +1 -1
- data/config/routes.rb +3 -0
- data/lib/caixanegra/manager.rb +1 -1
- data/lib/caixanegra/version.rb +1 -1
- metadata +3 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 182cc119673359db756adcca903e137025ed5283bdae367d7d6c662fc5fdc061
|
4
|
+
data.tar.gz: f075d702d2f361678a7775d286cb7f3b9ec1cb556778b22c4180588efe32d830
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 29c0edd8fab4949cab99f6d513b987cda6c87b2fb29fbeb33e447616ff580c0a9fbd3413a2fe4281828f28a8ac1123942fcecbdbeedf7511c64cb000384bd797
|
7
|
+
data.tar.gz: 23e9e398d71b3900759d8a944f20ddf1920ac1f38bb5961d3e36a34d112e36a488301d3c4709de8c9cfc48a8a9f995869ebfea90ed20acf72915b6c61ac48345
|
data/README.md
CHANGED
@@ -46,7 +46,7 @@ This will give you the UID that **caixanegra** designer will understand, using t
|
|
46
46
|
|
47
47
|
```ruby
|
48
48
|
my_flow = somewhere.get_flow # get from your own persistence or transport solution
|
49
|
-
uid = Caixanegra::Manager.handler(my_flow || {})
|
49
|
+
uid = Caixanegra::Manager.handler(flow_definition: my_flow || {})
|
50
50
|
```
|
51
51
|
|
52
52
|
You can then safely navigate to the designer.
|
@@ -66,4 +66,20 @@ window.Caixanegra.API = class {
|
|
66
66
|
request.send(JSON.stringify(initialCarryOver));
|
67
67
|
});
|
68
68
|
}
|
69
|
+
|
70
|
+
evaluateRegex(expression, sample) {
|
71
|
+
return new Promise((resolve, reject) => {
|
72
|
+
const request = new XMLHttpRequest();
|
73
|
+
|
74
|
+
request.addEventListener("load", (event) => {
|
75
|
+
resolve(JSON.parse(event.target.response));
|
76
|
+
});
|
77
|
+
request.addEventListener("error", () => {
|
78
|
+
reject();
|
79
|
+
});
|
80
|
+
|
81
|
+
request.open("GET", `${this.mountedPath()}/api/designer/inputs/evaluate_regex?expression=${encodeURIComponent(expression)}&sample=${encodeURIComponent(sample)}`);
|
82
|
+
request.send();
|
83
|
+
});
|
84
|
+
}
|
69
85
|
}
|
@@ -303,6 +303,15 @@ window.Caixanegra.Designer = {
|
|
303
303
|
document.querySelector("#console").addEventListener("click", this.toggleExecutionConsole.bind(this));
|
304
304
|
document.querySelector("#clearCarryOverObject").addEventListener("click", this.clearCarryOverObject.bind(this));
|
305
305
|
document.querySelector("#reset").addEventListener("click", this.resetExecution.bind(this));
|
306
|
+
document.querySelector("#duplicate").addEventListener("click", this.duplicateUnit.bind(this));
|
307
|
+
document.querySelector("#importFlow").addEventListener("click", this.#importFlow.bind(this));
|
308
|
+
document.querySelector("#exportFlow").addEventListener("click", this.#exportFlow.bind(this));
|
309
|
+
document.querySelector("#importFlowFile").addEventListener("change", this.#importFlowFileChanged.bind(this));
|
310
|
+
document.querySelector("#regexInput").addEventListener("keyup", this.#updateRegexInput.bind(this));
|
311
|
+
document.querySelector("#regexSample").addEventListener("keyup", this.#startRegexEvaluation.bind(this));
|
312
|
+
document.querySelector("#addRegexVariables").addEventListener("click", this.#addRegexVariablesRow.bind(this))
|
313
|
+
document.querySelector("#regexEditorToggleVars").addEventListener("click", this.#toggleRegexVariables.bind(this));
|
314
|
+
document.querySelector("#dismissRegexEditor").addEventListener("click", this.#dismissRegexEditor.bind(this));
|
306
315
|
this.toggleBlocker(true, "loading your flow");
|
307
316
|
window.addEventListener("resize", this.#windowResized.bind(drawingSurface));
|
308
317
|
drawingSurface.addEventListener("update_start", this.#engineUpdate.bind(this));
|
@@ -315,7 +324,6 @@ window.Caixanegra.Designer = {
|
|
315
324
|
|
316
325
|
this.gre.disable();
|
317
326
|
this.getUnits();
|
318
|
-
this.loadFlow();
|
319
327
|
this.gre.enable();
|
320
328
|
}
|
321
329
|
|
@@ -385,6 +393,8 @@ window.Caixanegra.Designer = {
|
|
385
393
|
}
|
386
394
|
|
387
395
|
container.append(wrapper);
|
396
|
+
|
397
|
+
return wrapper;
|
388
398
|
}
|
389
399
|
|
390
400
|
deleteUnit() {
|
@@ -394,6 +404,12 @@ window.Caixanegra.Designer = {
|
|
394
404
|
this.toggleDeleteConfirmation();
|
395
405
|
}
|
396
406
|
|
407
|
+
duplicateUnit() {
|
408
|
+
this.unitDetailPane.classList.remove("-open");
|
409
|
+
this.unitDebugHitsPane.classList.remove("-open");
|
410
|
+
this.cloneUnit(this.selectedUnit.oid);
|
411
|
+
}
|
412
|
+
|
397
413
|
flushToConsole(entries) {
|
398
414
|
const container = document.querySelector("#executionConsole");
|
399
415
|
container.innerHTML = "";
|
@@ -468,7 +484,8 @@ window.Caixanegra.Designer = {
|
|
468
484
|
flowSnapshot() {
|
469
485
|
const snapshot = {
|
470
486
|
entrypoint: this.#units.find((unit) => {return unit.type === "starter"})?.oid,
|
471
|
-
units: []
|
487
|
+
units: [],
|
488
|
+
initialCarryover: this.digInitialCarryOver(document.querySelector("#carryOverObject"))
|
472
489
|
};
|
473
490
|
|
474
491
|
this.#units.forEach((unit) => {
|
@@ -587,33 +604,85 @@ window.Caixanegra.Designer = {
|
|
587
604
|
});
|
588
605
|
|
589
606
|
this.#loadedComponents.catalog = true;
|
590
|
-
this
|
607
|
+
this.loadFlow();
|
591
608
|
});
|
592
609
|
}
|
593
610
|
|
594
611
|
loadFlow() {
|
595
612
|
this.api.getFlow(this.flowId).then((response) => {
|
596
613
|
const flowData = response;
|
597
|
-
this.gre.clear();
|
598
|
-
this.#units = [];
|
599
614
|
|
600
|
-
(flowData
|
601
|
-
this.createUnit(unit);
|
602
|
-
});
|
603
|
-
|
604
|
-
this.#units.forEach((unit) => {
|
605
|
-
(unit?.exits || []).forEach((exit) => {
|
606
|
-
const object = this.#units.find((targetUnit) => targetUnit.oid === exit.target);
|
607
|
-
if (object) { exit.target = object; }
|
608
|
-
});
|
609
|
-
});
|
615
|
+
this.#loadFlowFromJSON(flowData);
|
610
616
|
|
611
617
|
this.#loadedComponents.flow = true;
|
612
|
-
this
|
618
|
+
this.reveal();
|
619
|
+
});
|
620
|
+
}
|
621
|
+
|
622
|
+
#loadFlowFromJSON(flowData) {
|
623
|
+
this.gre.clear();
|
624
|
+
this.#units = [];
|
625
|
+
|
626
|
+
(flowData?.units || []).forEach((unit) => {
|
627
|
+
const catalogUnit = this.#catalog.find((cUnit) => cUnit.class === unit.class);
|
628
|
+
if (catalogUnit) {
|
629
|
+
unit.color = catalogUnit.color;
|
630
|
+
}
|
631
|
+
this.createUnit(unit);
|
632
|
+
});
|
633
|
+
|
634
|
+
this.#units.forEach((unit) => {
|
635
|
+
(unit?.exits || []).forEach((exit) => {
|
636
|
+
const object = this.#units.find((targetUnit) => targetUnit.oid === exit.target);
|
637
|
+
if (object) { exit.target = object; }
|
638
|
+
});
|
613
639
|
});
|
640
|
+
|
641
|
+
this.buildInitialCarryover(flowData.initialCarryover);
|
614
642
|
}
|
615
643
|
|
616
|
-
|
644
|
+
digBuildInitialCarryover(container, key, objectPointer) {
|
645
|
+
const lastChild = container.childNodes[container.childNodes.length -1];
|
646
|
+
|
647
|
+
if (key !== null) {
|
648
|
+
container.childNodes[0].querySelector("input").value = key;
|
649
|
+
}
|
650
|
+
|
651
|
+
if (Array.isArray(objectPointer)) {
|
652
|
+
this.coeAssignType(lastChild, "array");
|
653
|
+
|
654
|
+
for (let idx = 0; idx < objectPointer.length; idx++) {
|
655
|
+
this.digBuildInitialCarryover(
|
656
|
+
this.coeAddRow(container.querySelector(".rows")),
|
657
|
+
null,
|
658
|
+
objectPointer[idx]
|
659
|
+
);
|
660
|
+
}
|
661
|
+
} else if (typeof objectPointer === "object") {
|
662
|
+
this.coeAssignType(lastChild, "object");
|
663
|
+
|
664
|
+
for (const key in objectPointer) {
|
665
|
+
this.digBuildInitialCarryover(this.coeAddRow(container.querySelector(".rows")), key, objectPointer[key]);
|
666
|
+
}
|
667
|
+
} else if (typeof objectPointer === "number") {
|
668
|
+
this.coeAssignType(lastChild, "number");
|
669
|
+
lastChild.querySelector("input").value = objectPointer;
|
670
|
+
} else {
|
671
|
+
this.coeAssignType(lastChild, "string");
|
672
|
+
lastChild.querySelector("input").value = objectPointer;
|
673
|
+
}
|
674
|
+
}
|
675
|
+
|
676
|
+
buildInitialCarryover(initialCarryover) {
|
677
|
+
this.clearCarryOverObject();
|
678
|
+
const base = document.querySelector("#carryOverObject > .rows");
|
679
|
+
|
680
|
+
for (const key in initialCarryover) {
|
681
|
+
this.digBuildInitialCarryover(this.coeAddRow(base), key, initialCarryover[key]);
|
682
|
+
}
|
683
|
+
}
|
684
|
+
|
685
|
+
reveal() {
|
617
686
|
if (this.#loadedComponents.flow && this.#loadedComponents.catalog) {
|
618
687
|
this.toggleBlocker(false);
|
619
688
|
}
|
@@ -713,6 +782,21 @@ window.Caixanegra.Designer = {
|
|
713
782
|
});
|
714
783
|
}
|
715
784
|
|
785
|
+
cloneUnit(oid) {
|
786
|
+
const unit = this.#units.find((unit) => unit.oid === oid);
|
787
|
+
const dup = this.#dupObject(unit);
|
788
|
+
|
789
|
+
dup.oid = Sabertooth.Utils.generateOId();
|
790
|
+
dup.position = { x: unit.position.x + 25, y: unit.position.y + 25 };
|
791
|
+
dup.title = `${dup.title} (copy)`;
|
792
|
+
|
793
|
+
dup.exits.forEach(exit => {
|
794
|
+
delete exit.target;
|
795
|
+
});
|
796
|
+
|
797
|
+
this.createUnit(dup);
|
798
|
+
}
|
799
|
+
|
716
800
|
showError(message) {
|
717
801
|
this.actionMessage(true, message, "error");
|
718
802
|
setTimeout(() => {this.actionMessage(false)}, 5000);
|
@@ -762,6 +846,73 @@ window.Caixanegra.Designer = {
|
|
762
846
|
this.unitDebugHitsPane.classList.remove("-open");
|
763
847
|
}
|
764
848
|
|
849
|
+
#dupObject(obj) {
|
850
|
+
if (typeof obj !== 'object' || obj === null) {
|
851
|
+
return obj;
|
852
|
+
}
|
853
|
+
|
854
|
+
const clonedObject = Array.isArray(obj) ? [] : {};
|
855
|
+
|
856
|
+
for (const key in obj) {
|
857
|
+
if (obj.hasOwnProperty(key)) {
|
858
|
+
clonedObject[key] = this.#dupObject(obj[key]);
|
859
|
+
}
|
860
|
+
}
|
861
|
+
|
862
|
+
return clonedObject;
|
863
|
+
}
|
864
|
+
|
865
|
+
#importFlow() {
|
866
|
+
document.querySelector("#importFlowFile").click();
|
867
|
+
}
|
868
|
+
|
869
|
+
#importFlowFileChanged() {
|
870
|
+
const fileInput = document.querySelector("#importFlowFile");
|
871
|
+
|
872
|
+
if (fileInput.files.length === 0) {
|
873
|
+
return;
|
874
|
+
}
|
875
|
+
|
876
|
+
const selectedFile = fileInput.files[0];
|
877
|
+
const reader = new FileReader();
|
878
|
+
|
879
|
+
reader.onload = (event) => {
|
880
|
+
this.toggleBlocker(true, "importing your flow");
|
881
|
+
this.gre.disable();
|
882
|
+
const flowBackup = this.flowSnapshot();
|
883
|
+
|
884
|
+
try {
|
885
|
+
this.#loadFlowFromJSON(JSON.parse(event.target.result));
|
886
|
+
|
887
|
+
this.actionMessage(true, "Flow imported", "ok");
|
888
|
+
setTimeout(() => {this.actionMessage(false)}, 4000);
|
889
|
+
} catch (error) {
|
890
|
+
this.#loadFlowFromJSON(flowBackup);
|
891
|
+
|
892
|
+
this.actionMessage(true, "Failed to import flow. Rolled back", "error");
|
893
|
+
setTimeout(() => {this.actionMessage(false)}, 4000);
|
894
|
+
}
|
895
|
+
|
896
|
+
this.gre.enable();
|
897
|
+
this.reveal();
|
898
|
+
};
|
899
|
+
|
900
|
+
reader.readAsText(selectedFile);
|
901
|
+
}
|
902
|
+
|
903
|
+
#exportFlow() {
|
904
|
+
const blob = new Blob([JSON.stringify(this.flowSnapshot())], { type: "application/json" });
|
905
|
+
const url = URL.createObjectURL(blob);
|
906
|
+
const link = document.createElement('a');
|
907
|
+
link.href = url;
|
908
|
+
link.download = "flow_definition.cxn";
|
909
|
+
link.click();
|
910
|
+
URL.revokeObjectURL(url);
|
911
|
+
|
912
|
+
this.actionMessage(true, "Flow exported", "ok");
|
913
|
+
setTimeout(() => {this.actionMessage(false)}, 4000);
|
914
|
+
}
|
915
|
+
|
765
916
|
#executeFlow() {
|
766
917
|
this.toggleBlocker(true, "saving your flow");
|
767
918
|
|
@@ -1135,9 +1286,10 @@ window.Caixanegra.Designer = {
|
|
1135
1286
|
case "regex":
|
1136
1287
|
{
|
1137
1288
|
valueInput = document.createElement("input");
|
1138
|
-
if(matrix.type === "regex") { valueInput.classList.add("-
|
1289
|
+
if(matrix.type === "regex") { valueInput.classList.add("-regex")}
|
1139
1290
|
valueInput.value = unit.mappings[input]?.value || matrix.default || "";
|
1140
1291
|
valueInput.addEventListener("change", this.#unitInputValueChanged.bind(this));
|
1292
|
+
valueInput.addEventListener("click", this.#regexEditor.bind(this));
|
1141
1293
|
}
|
1142
1294
|
break;
|
1143
1295
|
case "dataset":
|
@@ -1237,6 +1389,125 @@ window.Caixanegra.Designer = {
|
|
1237
1389
|
this.selectedUnit.mappings[input].value = ev.target.value;
|
1238
1390
|
}
|
1239
1391
|
|
1392
|
+
#toggleRegexVariables() {
|
1393
|
+
const target = document.querySelector("#blocker .regexEditor > div:last-child");
|
1394
|
+
if(target.classList.contains("-visible")) {
|
1395
|
+
target.classList.remove("-visible");
|
1396
|
+
} else {
|
1397
|
+
target.classList.add("-visible");
|
1398
|
+
}
|
1399
|
+
}
|
1400
|
+
|
1401
|
+
#updateRegexInput() {
|
1402
|
+
this.blocker.editorListener.value = this.blocker.querySelector("#regexInput").value;
|
1403
|
+
|
1404
|
+
this.#startRegexEvaluation();
|
1405
|
+
}
|
1406
|
+
|
1407
|
+
#startRegexEvaluation() {
|
1408
|
+
window.clearTimeout(this.blocker.evalTimeout);
|
1409
|
+
this.blocker.querySelector("#regexResults").innerHTML = "wating input end...";
|
1410
|
+
this.blocker.evalTimeout = window.setTimeout(this.#evaluateRegex.bind(this), 1500);
|
1411
|
+
}
|
1412
|
+
|
1413
|
+
#evaluateRegex() {
|
1414
|
+
this.blocker.querySelector("#regexResults").innerHTML = "evaluating...";
|
1415
|
+
let expression = this.blocker.querySelector("#regexInput").value;
|
1416
|
+
const sample = this.blocker.querySelector("#regexSample").value;
|
1417
|
+
document.querySelectorAll("#regexVariablesList .regex-variable-entry").forEach((pair) => {
|
1418
|
+
const placeholder = pair.querySelector("input.placeholder").value;
|
1419
|
+
const value = pair.querySelector("input.value").value;
|
1420
|
+
expression = expression.replace(new RegExp(placeholder, "g"), value);
|
1421
|
+
});
|
1422
|
+
|
1423
|
+
this.api.evaluateRegex(expression, sample).then((response) => {
|
1424
|
+
const tableEntries = Object.keys(response).length - 2 > 0;
|
1425
|
+
|
1426
|
+
this.blocker.querySelector("#regexResults").innerHTML = "";
|
1427
|
+
const expression = document.createElement("div");
|
1428
|
+
const full_match = document.createElement("div");
|
1429
|
+
expression.innerHTML = `<strong>Expression: </strong><code>${response["_expression"] || "unable to get expression"}</code>`;
|
1430
|
+
full_match.innerHTML = `<strong>Full Match: </strong><code>${response["_full"] || "unable to get full match"}</code>`;
|
1431
|
+
|
1432
|
+
this.blocker.querySelector("#regexResults").append(expression, full_match);
|
1433
|
+
|
1434
|
+
if (tableEntries) {
|
1435
|
+
const table = document.createElement("table");
|
1436
|
+
let tr = document.createElement("tr");
|
1437
|
+
let th = document.createElement("th");
|
1438
|
+
th.innerHTML = "capture";
|
1439
|
+
tr.append(th);
|
1440
|
+
th = document.createElement("th");
|
1441
|
+
th.innerHTML = "match";
|
1442
|
+
tr.append(th);
|
1443
|
+
table.append(tr);
|
1444
|
+
|
1445
|
+
for (const key of Object.keys(response)) {
|
1446
|
+
if (key.startsWith("_")) { continue; }
|
1447
|
+
|
1448
|
+
tr = document.createElement("tr");
|
1449
|
+
let td = document.createElement("td");
|
1450
|
+
td.innerHTML = key;
|
1451
|
+
tr.append(td);
|
1452
|
+
td = document.createElement("td");
|
1453
|
+
td.innerHTML = response[key];
|
1454
|
+
tr.append(td);
|
1455
|
+
|
1456
|
+
table.append(tr);
|
1457
|
+
}
|
1458
|
+
|
1459
|
+
this.blocker.querySelector("#regexResults").append(table);
|
1460
|
+
}
|
1461
|
+
}).catch((error) => {
|
1462
|
+
this.blocker.querySelector("#regexResults").innerHTML = "unable to evaluate";
|
1463
|
+
});
|
1464
|
+
}
|
1465
|
+
|
1466
|
+
#regexEditor(ev) {
|
1467
|
+
this.blocker.classList.add("-in-regex-editor");
|
1468
|
+
this.blocker.classList.remove("-released");
|
1469
|
+
this.blocker.editorListener = ev.target;
|
1470
|
+
this.blocker.querySelector("#regexInput").value = ev.target.value;
|
1471
|
+
|
1472
|
+
this.#startRegexEvaluation();
|
1473
|
+
}
|
1474
|
+
|
1475
|
+
#dismissRegexEditor() {
|
1476
|
+
this.blocker.classList.remove("-in-regex-editor");
|
1477
|
+
this.blocker.classList.add("-released");
|
1478
|
+
}
|
1479
|
+
|
1480
|
+
#removeRegexVariable() {
|
1481
|
+
this.parentElement.remove();
|
1482
|
+
}
|
1483
|
+
|
1484
|
+
#addRegexVariablesRow() {
|
1485
|
+
const list = document.querySelector("#regexVariablesList");
|
1486
|
+
|
1487
|
+
const wrapper = document.createElement("div");
|
1488
|
+
const name = document.createElement("input");
|
1489
|
+
const value = document.createElement("input");
|
1490
|
+
const remove = document.createElement("button");
|
1491
|
+
|
1492
|
+
name.placeholder = "placeholder";
|
1493
|
+
name.classList.add("placeholder");
|
1494
|
+
value.classList.add("value");
|
1495
|
+
name.autocomplete=false;
|
1496
|
+
value.autocomplete=false;
|
1497
|
+
value.placeholder = "value";
|
1498
|
+
remove.innerHTML = "-";
|
1499
|
+
|
1500
|
+
remove.addEventListener("click", this.#removeRegexVariable.bind(remove));
|
1501
|
+
|
1502
|
+
name.addEventListener("keyup", this.#startRegexEvaluation.bind(this));
|
1503
|
+
value.addEventListener("keyup", this.#startRegexEvaluation.bind(this));
|
1504
|
+
|
1505
|
+
wrapper.classList.add("regex-variable-entry");
|
1506
|
+
|
1507
|
+
wrapper.append(name, value, remove);
|
1508
|
+
list.append(wrapper);
|
1509
|
+
}
|
1510
|
+
|
1240
1511
|
#addUnitExitMapping(ev) {
|
1241
1512
|
const unit = this.selectedUnit;
|
1242
1513
|
const mapping = { use: "", as: "" };
|
@@ -3,6 +3,10 @@ $accent-color: rgb(105, 105, 105);
|
|
3
3
|
$accent-highlight-color: lighten($accent-color, 20%);
|
4
4
|
$accent-contrast-color: darken($accent-color, 20%);
|
5
5
|
|
6
|
+
#importFlowFile {
|
7
|
+
display: none;
|
8
|
+
}
|
9
|
+
|
6
10
|
#execution {
|
7
11
|
position: fixed;
|
8
12
|
display: flex;
|
@@ -37,6 +41,33 @@ $accent-contrast-color: darken($accent-color, 20%);
|
|
37
41
|
}
|
38
42
|
}
|
39
43
|
|
44
|
+
#io {
|
45
|
+
position: fixed;
|
46
|
+
display: flex;
|
47
|
+
flex-direction: row;
|
48
|
+
bottom: 8px;
|
49
|
+
right: 8px;
|
50
|
+
|
51
|
+
div {
|
52
|
+
cursor: pointer;
|
53
|
+
transition: all 250ms ease-in-out;
|
54
|
+
height: 18px;
|
55
|
+
width: 18px;
|
56
|
+
margin-left: 8px;
|
57
|
+
|
58
|
+
&:hover {
|
59
|
+
transform: scale(1.1);
|
60
|
+
}
|
61
|
+
|
62
|
+
svg {
|
63
|
+
path, polygon, line, polyline {
|
64
|
+
fill: white;
|
65
|
+
stroke: white;
|
66
|
+
}
|
67
|
+
}
|
68
|
+
}
|
69
|
+
}
|
70
|
+
|
40
71
|
#addUnit {
|
41
72
|
position: fixed;
|
42
73
|
border-radius: 50%;
|
@@ -507,6 +538,7 @@ $accent-contrast-color: darken($accent-color, 20%);
|
|
507
538
|
}
|
508
539
|
|
509
540
|
input {
|
541
|
+
position: relative;
|
510
542
|
width: 100%;
|
511
543
|
box-sizing: border-box;
|
512
544
|
background-color: $accent-highlight-color;
|
@@ -514,7 +546,7 @@ $accent-contrast-color: darken($accent-color, 20%);
|
|
514
546
|
border: none;
|
515
547
|
outline: none;
|
516
548
|
|
517
|
-
&.-
|
549
|
+
&.-regex {
|
518
550
|
font-family: monospace;
|
519
551
|
}
|
520
552
|
}
|
@@ -649,10 +681,25 @@ $accent-contrast-color: darken($accent-color, 20%);
|
|
649
681
|
flex-direction: row;
|
650
682
|
justify-content: space-between;
|
651
683
|
|
652
|
-
.name {
|
653
|
-
font-size: 1em;
|
654
|
-
font-weight: bold;
|
684
|
+
.name-holder {
|
655
685
|
width: 100%;
|
686
|
+
display: flex;
|
687
|
+
flex-direction: row;
|
688
|
+
|
689
|
+
.name {
|
690
|
+
font-size: 1em;
|
691
|
+
font-weight: bold;
|
692
|
+
}
|
693
|
+
|
694
|
+
#duplicate {
|
695
|
+
margin-left: 5px;
|
696
|
+
width: 10px;
|
697
|
+
cursor: pointer;
|
698
|
+
|
699
|
+
svg path {
|
700
|
+
fill: $accent-contrast-color;
|
701
|
+
}
|
702
|
+
}
|
656
703
|
}
|
657
704
|
|
658
705
|
.delete {
|
@@ -744,7 +791,7 @@ $accent-contrast-color: darken($accent-color, 20%);
|
|
744
791
|
left:0;
|
745
792
|
right:0;
|
746
793
|
bottom:0;
|
747
|
-
background-color:
|
794
|
+
background-color: rgba(0,0,0, .5);
|
748
795
|
transition: opacity 250ms ease-in-out;
|
749
796
|
opacity: 1;
|
750
797
|
|
@@ -771,6 +818,155 @@ $accent-contrast-color: darken($accent-color, 20%);
|
|
771
818
|
}
|
772
819
|
}
|
773
820
|
|
821
|
+
.regexEditor {
|
822
|
+
position: absolute;
|
823
|
+
top: 50%;
|
824
|
+
left: 50%;
|
825
|
+
transform: translate(-50%, -50%);
|
826
|
+
display: none;
|
827
|
+
font-family: Verdana, Geneva, Tahoma, sans-serif;
|
828
|
+
min-width: 70%;
|
829
|
+
flex-direction: row;
|
830
|
+
align-items: center;
|
831
|
+
justify-content: center;
|
832
|
+
|
833
|
+
& > div {
|
834
|
+
background-color: $accent-color;
|
835
|
+
box-shadow: $accent-shadow;
|
836
|
+
border-radius: 5px;
|
837
|
+
display: flex;
|
838
|
+
flex-direction: column;
|
839
|
+
padding: 12px;
|
840
|
+
position: relative;
|
841
|
+
font-size: 16px;
|
842
|
+
|
843
|
+
&:first-child {
|
844
|
+
margin-right: 15px;
|
845
|
+
width: 70%;
|
846
|
+
z-index: 10;
|
847
|
+
font-size: 0.8em;
|
848
|
+
}
|
849
|
+
|
850
|
+
&:last-child {
|
851
|
+
font-size: 0.8em;
|
852
|
+
transform: translateX(-200%);
|
853
|
+
width: 0;
|
854
|
+
box-shadow: 0 0 0 rgba(0,0,0,0);
|
855
|
+
z-index: 9;
|
856
|
+
overflow: hidden;
|
857
|
+
transition: all 250ms ease-in-out;
|
858
|
+
|
859
|
+
&.-visible {
|
860
|
+
transform: translateX(0%);
|
861
|
+
width: 30%;
|
862
|
+
}
|
863
|
+
}
|
864
|
+
}
|
865
|
+
|
866
|
+
label {
|
867
|
+
margin-bottom: 4px;
|
868
|
+
}
|
869
|
+
|
870
|
+
input {
|
871
|
+
margin-bottom: 6px;
|
872
|
+
box-sizing: border-box;
|
873
|
+
background-color: #9c9c9c;
|
874
|
+
padding: 6px 4px;
|
875
|
+
border: none;
|
876
|
+
outline: none;
|
877
|
+
}
|
878
|
+
|
879
|
+
#regexInput {
|
880
|
+
font-family: monospace;
|
881
|
+
font-size: 1.1em;
|
882
|
+
}
|
883
|
+
|
884
|
+
#regexSample {
|
885
|
+
font-size: 0.8em;
|
886
|
+
}
|
887
|
+
|
888
|
+
#regexResults {
|
889
|
+
padding: 8px;
|
890
|
+
background-color:rgb(200, 200, 200);
|
891
|
+
color: black;
|
892
|
+
max-height: 50vh;
|
893
|
+
overflow: auto;
|
894
|
+
display: block;
|
895
|
+
margin-bottom: 30px;
|
896
|
+
font-size: 0.7em;
|
897
|
+
|
898
|
+
table {
|
899
|
+
margin-top: 15px;
|
900
|
+
width: 100%;
|
901
|
+
border-collapse: collapse;
|
902
|
+
|
903
|
+
th, td {
|
904
|
+
text-align: left;
|
905
|
+
border: 1px solid rgba(0,0,0,.3);
|
906
|
+
padding: 4px;
|
907
|
+
}
|
908
|
+
|
909
|
+
tr:nth-child(even) {
|
910
|
+
background-color: rgba(0,0,0,.15);
|
911
|
+
}
|
912
|
+
}
|
913
|
+
}
|
914
|
+
|
915
|
+
#regexVariables {
|
916
|
+
#regexVariablesList {
|
917
|
+
margin-bottom: 15px;
|
918
|
+
}
|
919
|
+
|
920
|
+
.regex-variable-entry {
|
921
|
+
display: flex;
|
922
|
+
flex-direction: row;
|
923
|
+
margin: 2px 0;
|
924
|
+
align-items: center;
|
925
|
+
|
926
|
+
input {
|
927
|
+
width: 100%;
|
928
|
+
margin-bottom: 0;
|
929
|
+
|
930
|
+
&:first-child {
|
931
|
+
margin-right: 5px;
|
932
|
+
}
|
933
|
+
}
|
934
|
+
|
935
|
+
button {
|
936
|
+
margin-left: 5px;
|
937
|
+
}
|
938
|
+
}
|
939
|
+
}
|
940
|
+
|
941
|
+
button {
|
942
|
+
background-color: $accent-highlight-color;
|
943
|
+
border: 1px solid $accent-color;
|
944
|
+
color: black;
|
945
|
+
padding: 3px 6px;
|
946
|
+
cursor: pointer;
|
947
|
+
transition: all 250ms ease-in-out;
|
948
|
+
|
949
|
+
&:hover {
|
950
|
+
background-color: $accent-contrast-color;
|
951
|
+
color: $accent-highlight-color;
|
952
|
+
}
|
953
|
+
}
|
954
|
+
|
955
|
+
#regexEditorToggleVars {
|
956
|
+
float: right;
|
957
|
+
}
|
958
|
+
}
|
959
|
+
|
960
|
+
&.-in-regex-editor {
|
961
|
+
.information {
|
962
|
+
display: none;
|
963
|
+
}
|
964
|
+
|
965
|
+
.regexEditor {
|
966
|
+
display: flex;
|
967
|
+
}
|
968
|
+
}
|
969
|
+
|
774
970
|
&.-released {
|
775
971
|
pointer-events: none;
|
776
972
|
opacity: 0;
|
@@ -0,0 +1,32 @@
|
|
1
|
+
module Caixanegra
|
2
|
+
module API
|
3
|
+
module Designer
|
4
|
+
class InputsController < ::Caixanegra::APIController
|
5
|
+
def evaluate_regex
|
6
|
+
full_match = sample.match(Regexp.new(expression, Regexp::IGNORECASE))
|
7
|
+
direct = { "_full": full_match.to_s, "_expression": expression }
|
8
|
+
captures = {}
|
9
|
+
|
10
|
+
(full_match&.captures || []).each_with_index do |capture, index|
|
11
|
+
captures["c#{index}"] = capture
|
12
|
+
end
|
13
|
+
named_captures = full_match&.named_captures || {}
|
14
|
+
|
15
|
+
render json: direct.merge(captures).merge(named_captures).symbolize_keys
|
16
|
+
rescue RegexpError
|
17
|
+
head 422
|
18
|
+
end
|
19
|
+
|
20
|
+
private
|
21
|
+
|
22
|
+
def expression
|
23
|
+
@expression ||= params[:expression]
|
24
|
+
end
|
25
|
+
|
26
|
+
def sample
|
27
|
+
@sample ||= params[:sample]
|
28
|
+
end
|
29
|
+
end
|
30
|
+
end
|
31
|
+
end
|
32
|
+
end
|
@@ -25,11 +25,28 @@
|
|
25
25
|
</svg>
|
26
26
|
</div>
|
27
27
|
<div id="console">
|
28
|
-
<svg version="1.1"
|
28
|
+
<svg version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" viewBox="0 0 24 24" xml:space="preserve">
|
29
29
|
<path d="M24,24H0V0h24V24z M2,22h20V2H6v2h16v2H2V22z M2,4h2V2H2V4z M16,16h-6v-2h6V16z M5.7,15.7l-1.4-1.4L6.6,12L4.3,9.7 l1.4-1.4L9.4,12L5.7,15.7z"/>
|
30
30
|
</svg>
|
31
31
|
</div>
|
32
32
|
</div>
|
33
|
+
<div id="io">
|
34
|
+
<input type="file" id="importFlowFile" accept=".cxn">
|
35
|
+
<div id="importFlow">
|
36
|
+
<svg version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" viewBox="0 0 24 24" xml:space="preserve">
|
37
|
+
<polyline id="primary" points="13 9 13 13 9 13" style="fill: none; stroke-linecap: round; stroke-linejoin: round; stroke-width: 2;"></polyline>
|
38
|
+
<line id="primary-2" data-name="primary" x1="3" y1="3" x2="13" y2="13" style="fill: none; stroke-linecap: round; stroke-linejoin: round; stroke-width: 2;"></line>
|
39
|
+
<path id="primary-3" data-name="primary" d="M13.89,5H20a1,1,0,0,1,1,1V20a1,1,0,0,1-1,1H6a1,1,0,0,1-1-1V13.89" style="fill: none; stroke-linecap: round; stroke-linejoin: round; stroke-width: 2;"></path>
|
40
|
+
</svg>
|
41
|
+
</div>
|
42
|
+
<div id="exportFlow">
|
43
|
+
<svg version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" viewBox="0 0 24 24" xml:space="preserve">
|
44
|
+
<polyline id="primary" points="17 3 21 3 21 7" style="fill: none; stroke-linecap: round; stroke-linejoin: round; stroke-width: 2;"></polyline>
|
45
|
+
<line id="primary-2" data-name="primary" x1="11" y1="13" x2="21" y2="3" style="fill: none; stroke-linecap: round; stroke-linejoin: round; stroke-width: 2;"></line>
|
46
|
+
<path id="primary-3" data-name="primary" d="M19,13.89V20a1,1,0,0,1-1,1H4a1,1,0,0,1-1-1V6A1,1,0,0,1,4,5h6.11" style="fill: none; stroke-linecap: round; stroke-linejoin: round; stroke-width: 2;"></path>
|
47
|
+
</svg>
|
48
|
+
</div>
|
49
|
+
</div>
|
33
50
|
<div id="addUnit"><span>+</span></div>
|
34
51
|
<div id="save"><span>save</span></div>
|
35
52
|
<div id="unitMenu"></div>
|
@@ -51,7 +68,14 @@
|
|
51
68
|
<div class="color-code"></div>
|
52
69
|
<div class="content">
|
53
70
|
<div id="unitDetailClass" class="unit-detail-headers">
|
54
|
-
<div class="name"
|
71
|
+
<div class="name-holder">
|
72
|
+
<div class="name"></div>
|
73
|
+
<span id="duplicate">
|
74
|
+
<svg viewBox="0 0 20 20" xmlns="http://www.w3.org/2000/svg">
|
75
|
+
<path d="M6 6V2c0-1.1.9-2 2-2h10a2 2 0 0 1 2 2v10a2 2 0 0 1-2 2h-4v4a2 2 0 0 1-2 2H2a2 2 0 0 1-2-2V8c0-1.1.9-2 2-2h4zm2 0h4a2 2 0 0 1 2 2v4h4V2H8v4zM2 8v10h10V8H2zm4 4v-2h2v2h2v2H8v2H6v-2H4v-2h2z"/>
|
76
|
+
</svg>
|
77
|
+
</span>
|
78
|
+
</div>
|
55
79
|
<div class="delete">
|
56
80
|
<div id="toggler">
|
57
81
|
<svg viewBox="0 0 56 56" xmlns="http://www.w3.org/2000/svg">
|
@@ -84,6 +108,27 @@
|
|
84
108
|
<div>caixa<span>negra</span></div>
|
85
109
|
<div class="message">loading your flow</div>
|
86
110
|
</div>
|
111
|
+
<div class="regexEditor">
|
112
|
+
<div>
|
113
|
+
<label for="regexInput">Regular Expression</label>
|
114
|
+
<input id="regexInput" type="text" />
|
115
|
+
<label for="regexSample" placeholder="type sample text here">Sample</label>
|
116
|
+
<input id="regexSample" type="text" />
|
117
|
+
<label for="regexResults">Result</label>
|
118
|
+
<div id="regexResults"></div>
|
119
|
+
<div>
|
120
|
+
<button id="dismissRegexEditor">dismiss</button>
|
121
|
+
<button id="regexEditorToggleVars">vars</button>
|
122
|
+
</div>
|
123
|
+
</div>
|
124
|
+
<div>
|
125
|
+
<label for="regexVariables">Variables</label>
|
126
|
+
<div id="regexVariables">
|
127
|
+
<div id="regexVariablesList"></div>
|
128
|
+
<button id="addRegexVariables">add</button>
|
129
|
+
</div>
|
130
|
+
</div>
|
131
|
+
</div>
|
87
132
|
</div>
|
88
133
|
<div id="action_messenger">
|
89
134
|
<div class="icon icon-error">
|
data/config/routes.rb
CHANGED
data/lib/caixanegra/manager.rb
CHANGED
data/lib/caixanegra/version.rb
CHANGED
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: caixanegra
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 0.4.1
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- sergiorribeiro
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2023-
|
11
|
+
date: 2023-10-24 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: rails
|
@@ -57,6 +57,7 @@ files:
|
|
57
57
|
- app/assets/stylesheets/caixanegra/application.css
|
58
58
|
- app/assets/stylesheets/caixanegra/designer.scss
|
59
59
|
- app/controllers/caixanegra/api/designer/flows_controller.rb
|
60
|
+
- app/controllers/caixanegra/api/designer/inputs_controller.rb
|
60
61
|
- app/controllers/caixanegra/api/designer/units_controller.rb
|
61
62
|
- app/controllers/caixanegra/api_controller.rb
|
62
63
|
- app/controllers/caixanegra/application_controller.rb
|