summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorSheshashailavas Chinthakayala <sc2914@att.com>2018-11-12 22:01:06 +0000
committerSheshashailavas Chinthakayala <sc2914@att.com>2018-11-12 22:02:12 +0000
commit3535a63cbbe1a41897a652bf1937368c9733cc90 (patch)
treef3ce60c1127a07370c2c9928b730d7984fd0f639
parent7e9bf97e8ae8dd7a4da324af175c7969fa0f9c24 (diff)
[CCSDK-680] fixed dg import from local git repo
and also added option to view difference of json and xml since import Change-Id: I9372c18658207ef55a568f219716a0b29f4f5eae Issue-ID: CCSDK-680 Signed-off-by: Sheshashailavas Chinthakayala <sc2914@att.com>
-rw-r--r--dgbuilder/public/index.html6
-rw-r--r--dgbuilder/public/red/main.js4
-rw-r--r--dgbuilder/public/red/ui/editor.js5
-rw-r--r--dgbuilder/public/red/ui/library.js5
-rw-r--r--dgbuilder/public/red/ui/view.js238
-rw-r--r--dgbuilder/public/util/js/dgeToXml.js5
-rw-r--r--dgbuilder/red/server.js103
-rw-r--r--dgbuilder/tools/splitFlows.js29
-rwxr-xr-xdgbuilder/tools/splitFlows.sh8
9 files changed, 393 insertions, 10 deletions
diff --git a/dgbuilder/public/index.html b/dgbuilder/public/index.html
index 480de5c4..499f48ab 100644
--- a/dgbuilder/public/index.html
+++ b/dgbuilder/public/index.html
@@ -29,7 +29,13 @@
<link rel="stylesheet" href="style.css">
<link rel="stylesheet" href="ace/ace-styles.css">
<script src="ace/ace.js" type="text/javascript" charset="utf-8"></script>
+<script src="ace/ace-diff.js"></script>
+<script src="ace/diff_match_patch.js"></script>
<script src="ace/mode-json.js"></script>
+<!-- <script src="ace/mode-javascript.js"></script> -->
+<script src="ace/mode-xml.js"></script>
+<!--<script src="ace/theme-dreamweaver.js"></script> -->
+<script src="ace/theme-eclipse.js"></script>
</head>
<body spellcheck="false">
<div id="header">
diff --git a/dgbuilder/public/red/main.js b/dgbuilder/public/red/main.js
index cf7bb287..561d0570 100644
--- a/dgbuilder/public/red/main.js
+++ b/dgbuilder/public/red/main.js
@@ -1868,11 +1868,11 @@ Added this logic because , when the configuration item is choosen in the menu th
{id:"btn-list-yang-files",icon:"fa fa-clipboard",label:"List Yang Files",onselect:listYangFiles},
]},
null,
- /* {id:"btn-dg-diff-menu",icon:"fa fa-sign-in",label:"DG diff",options:[
+ {id:"btn-dg-diff-menu",icon:"fa fa-sign-in",label:"DG diff",options:[
{id:"btn-diff-json",icon:"fa fa-clipboard",label:"Json diff since import",onselect:RED.view.diffJsonSinceImportDialog},
{id:"btn-diff-xml",icon:"fa fa-clipboard",label:"XML diff since import",onselect:RED.view.diffXmlSinceImportDialog},
]},
- null,*/
+ null,
{id:"btn-configure-upload",icon:"fa fa-book",label:"Configuration",toggle:false,onselect:updateConfiguration},
null,
{id:"btn-manage-tabs",icon:"fa fa-info",label:"Manage Tabs",toggle:false,onselect:showSelectedTabs},
diff --git a/dgbuilder/public/red/ui/editor.js b/dgbuilder/public/red/ui/editor.js
index 174af961..cae74d08 100644
--- a/dgbuilder/public/red/ui/editor.js
+++ b/dgbuilder/public/red/ui/editor.js
@@ -234,6 +234,11 @@ RED.editor = (function() {
}
}
} else if (RED.view.state() == RED.state.IMPORT) {
+ var nodeSet = getCurrentFlowNodeSet();
+ //console.dir(nodeSet);
+ if(nodeSet != null && nodeSet.length == 0){
+ RED.view.setIsImportAction(true);
+ }
RED.view.importNodes($("#node-input-import").val());
}
$( this ).dialog( "close" );
diff --git a/dgbuilder/public/red/ui/library.js b/dgbuilder/public/red/ui/library.js
index 10c99261..b3a556d7 100644
--- a/dgbuilder/public/red/ui/library.js
+++ b/dgbuilder/public/red/ui/library.js
@@ -51,6 +51,11 @@ RED.library = (function() {
a.flowName = root+(root!==""?"/":"")+data.f[i];
a.onclick = function() {
$.get('library/flows/'+this.flowName, function(data) {
+ var nodeSet = getCurrentFlowNodeSet();
+ //console.dir(nodeSet);
+ if(nodeSet != null && nodeSet.length == 0){
+ RED.view.setIsImportAction(true);
+ }
RED.view.importNodes(data);
});
};
diff --git a/dgbuilder/public/red/ui/view.js b/dgbuilder/public/red/ui/view.js
index 707bde9e..365b93c5 100644
--- a/dgbuilder/public/red/ui/view.js
+++ b/dgbuilder/public/red/ui/view.js
@@ -17,6 +17,7 @@
RED.view = (function() {
/* increasing the width and height from 5000 to 7500*/
+var isImportAction = false;
var space_width = 7500,
space_height = 7500,
lineCurveScale = 0.75,
@@ -323,6 +324,7 @@ RED.view = (function() {
}
function canvasMouseDown() {
+ console.log("The state in canvasMouseDown:" + RED.view.state());
if (!mousedown_node && !mousedown_link) {
selected_link = null;
updateSelection();
@@ -480,10 +482,11 @@ RED.view = (function() {
}
}
}
- redraw();
+ redraw();
}
function canvasMouseUp() {
+ console.log("The state in canvasMouseUp:" + RED.view.state());
if (mousedown_node && mouse_mode == RED.state.JOINING) {
drag_line.attr("class", "drag_line_hidden");
}
@@ -534,11 +537,40 @@ RED.view = (function() {
RED.keyboard.remove(/* ESCAPE */ 27);
setDirty(true);
}
- redraw();
- // clear mouse event vars
- resetMouseVars();
+ console.log("isImportAction:" + RED.view.getIsImportAction());
+ if (RED.view.getIsImportAction() === true){
+ RED.view.setIsImportAction(false);
+ console.log("updated isImportAction:" + isImportAction);
+ redraw();
+ // clear mouse event vars
+ resetMouseVars();
+ //save the imported DG
+ try{
+ var obj = getCurrentFlowNodeSet();
+ //console.dir(obj);
+ //console.log("workspace id:" + RED.view.getWorkspace());
+ var dgTabId = RED.view.getWorkspace();
+ console.log("dgTabId:" + dgTabId);
+ $.post("/saveImportedDG",{"importedNodes" :JSON.stringify(obj,null,4),"currTabId": dgTabId})
+ .done(function( data ) {
+ console.log("saved imported DG");
+ })
+ .fail(function(err) {
+ console.log("error saving imported DG");
+ })
+ .always(function() {
+ });
+ }catch(err){
+ console.log(err);
+ }
+ }else{
+ redraw();
+ // clear mouse event vars
+ resetMouseVars();
+ }
}
+
$('#btn-zoom-out').click(function() {zoomOut();});
$('#btn-zoom-zero').click(function() {zoomZero();});
$('#btn-zoom-in').click(function() {zoomIn();});
@@ -1621,6 +1653,8 @@ RED.view = (function() {
RED.keyboard.enable();
}
});
+
+
$( "#node-dialog-delete-workspace" ).dialog({
modal: true,
autoOpen: false,
@@ -1674,6 +1708,12 @@ RED.view = (function() {
getWorkspace: function() {
return activeWorkspace;
},
+ setIsImportAction: function(iaction) {
+ isImportAction = iaction ;
+ },
+ getIsImportAction: function() {
+ return isImportAction ;
+ },
showWorkspace: function(id) {
workspace_tabs.activateTab(id);
},
@@ -1700,7 +1740,7 @@ RED.view = (function() {
$(function() {
var htmlStr= "<div id='yang-upload-div' style='width:375;height:225'>" +
'<form id="uploadForm" name="uploadForm" enctype="multipart/form-data" action="/api/uploadyang" method="post" >' +
- "<input id='yang-file-id' name='yangFile' type='file' accept='.yang,.zip'><p style='font-size:0.7em'><i>For Module depending on multiple yang files, zip them and upload the zip file. The zip file name should match the exact name of the module with .zip extension</i</p><br><br><br><br><br><p id='yang-upload-status'></p>" +
+ "<input id='yang-file-id' name='yangFile' type='file' accept='.yang,.zip'><p style='font-size:0.7em'><i>For Module depending on multiple yang files, zip them and upload the zip file</i</p><br><br><br><br><br><p id='yang-upload-status'></p>" +
//'<input id="upload-yang-button-id" style="font-size:1em;font-weight:bold" type="button" value="Upload Yang" name="upload-yang-button">' +
"</form></div>";
@@ -2034,6 +2074,192 @@ RED.view = (function() {
RED.nodes.eachNode(function(n) { n.dirty = true;});
redraw();
},
+ diffJsonSinceImportDialog: function diffJsonSinceImportDialog(){
+ var currDGObj = getCurrentFlowNodeSet();
+ var currDGObjStr = JSON.stringify(currDGObj,null,4);
+ //console.log(currDGObjStr);
+ //$(function() {
+var htmlStr = "<div id=\"flex-container\">" +
+ "<div><div id=\"editor1\"></div></div>" +
+ "<div id=\"gutter\"></div>" +
+ "<div><div id=\"editor2\"></div></div>" +
+"</div>" +
+
+"<script>" +
+"$(function () {" +
+ "var aceDiffer = new AceDiff({" +
+ "mode: \"ace/mode/json\"," +
+ "theme: \"ace/theme/eclipse\"," +
+ "left: {" +
+ "id: \"editor1\"," +
+ "content: $(\"#example-content-1\").html()," +
+ "editable: false," +
+ "copyLinkEnabled: false" +
+ "}," +
+ "right: {" +
+ "id: \"editor2\"," +
+ "content: $(\"#example-content-2\").html()," +
+ "editable: false," +
+ "copyLinkEnabled: false" +
+ "}," +
+ "classes: {" +
+ "gutterID: \"gutter\"" +
+ "}" +
+ "});" +
+"});" +
+"</script>" ;
+var origDGFile ="[]";
+var diffStatus = "DG JSON UNCHANGED";
+ $.get("/readFile",{"filePath" : "orig_dgs/" + activeWorkspace })
+ .done(function( data ) {
+ if(data != undefined && data != null && data.output != undefined ){
+ origDGFile= data.output;
+ }
+ })
+ .fail(function(err) {
+ })
+ .always(function() {
+ if(origDGFile != currDGObjStr){
+ diffStatus="DG JSON CHANGED";
+ }
+ htmlStr += "<div id=\"example-content-1\" style=\"display: none\">" +
+ origDGFile +
+ "</div>" +
+ "<div id=\"example-content-2\" style=\"display: none\">" +
+ currDGObjStr +
+ "</div>" ;
+
+//var htmlStr='<object type="text/html" data="display-diff.html" ></object>';
+
+ $("#diff-browser-dialog").dialog({
+ modal:true,
+ autoOpen :false,
+ title: "Json Diff :" + diffStatus,
+ width: 1200,
+ height: 600,
+ minWidth :1200 ,
+ minHeight :600,
+ buttons :[
+ {
+ text: "Close",
+ click: function() {
+ //$( this ).dialog( "close" );
+ $("#diff-browser-dialog").dialog("close");
+ }
+ }
+ ],
+ open:function(){
+ $('#diff-browser-dialog').keypress(function(e) {
+ if (e.keyCode == $.ui.keyCode.ENTER) {
+ $('#diff-browser-dialog').parent().find('.ui-dialog-buttonpane button:first').click();
+ return false;
+ }
+ });
+ }
+ }).dialog('open').html(htmlStr);
+ });
+ //});
+ },
+ diffXmlSinceImportDialog: function diffXmlSinceImportDialog(){
+ var currDGObj = getCurrentFlowNodeSet();
+ //console.dir(currDGObj);
+ var currDGObjStr ="";
+ try{
+ currDGObjStr = getNodeToXml(JSON.stringify(currDGObj));
+ }catch(err){
+ }
+ var curr_formatted_xml = vkbeautify.xml(currDGObjStr);
+ //console.log(curr_formatted_xml);
+ //console.log(currDGObjStr);
+ //$(function() {
+var htmlStr = "<div id=\"flex-container\">" +
+ "<div><div id=\"editor1\"></div></div>" +
+ "<div id=\"gutter\"></div>" +
+ "<div><div id=\"editor2\"></div></div>" +
+"</div>" +
+
+"<script>" +
+"$(function () {" +
+ "var aceDiffer = new AceDiff({" +
+ "mode: \"ace/mode/xml\"," +
+ "theme: \"ace/theme/eclipse\"," +
+ //"theme: \"ace/theme/twilight\"," +
+ "left: {" +
+ "id: \"editor1\"," +
+ "content: $(\"#example-content-1\").html()," +
+ "editable: false," +
+ "copyLinkEnabled: false" +
+ "}," +
+ "right: {" +
+ "id: \"editor2\"," +
+ "content: $(\"#example-content-2\").html()," +
+ "editable: false," +
+ "copyLinkEnabled: false" +
+ "}," +
+ "classes: {" +
+ "gutterID: \"gutter\"" +
+ "}" +
+ "});" +
+"});" +
+"</script>" ;
+var origXmlFile ="";
+var origDGFile ="";
+var diffStatus = "DG XML UNCHANGED";
+ $.get("/readFile",{"filePath" : "orig_dgs/" + activeWorkspace })
+ .done(function( data ) {
+ if(data != undefined && data != null && data.output != undefined ){
+ origDGFile= data.output;
+ try{
+ var origDGObjStr = getNodeToXml(origDGFile);
+ origXmlFile = vkbeautify.xml(origDGObjStr);
+ }catch(err){
+ }
+ }
+ })
+ .fail(function(err) {
+ })
+ .always(function() {
+ if(origXmlFile != curr_formatted_xml){
+ diffStatus = "DG XML CHANGED";
+ }
+ htmlStr += "<div id=\"example-content-1\" style=\"display: none\">" +
+ origXmlFile +
+ "</div>" +
+ "<div id=\"example-content-2\" style=\"display: none\">" +
+ curr_formatted_xml +
+ "</div>" ;
+
+//var htmlStr='<object type="text/html" data="display-diff.html" ></object>';
+
+ $("#diff-browser-dialog").dialog({
+ modal:true,
+ autoOpen :false,
+ title: "XML Diff",
+ width: 1200,
+ height: 600,
+ minWidth : 1200,
+ minHeight :600,
+ buttons :[
+ {
+ text: "Close",
+ click: function() {
+ //$( this ).dialog( "close" );
+ $("#diff-browser-dialog").dialog("close");
+ }
+ }
+ ],
+ open:function(){
+ $('#diff-browser-dialog').keypress(function(e) {
+ if (e.keyCode == $.ui.keyCode.ENTER) {
+ $('#diff-browser-dialog').parent().find('.ui-dialog-buttonpane button:first').click();
+ return false;
+ }
+ });
+ }
+ }).dialog('open').html(htmlStr);
+ });
+ //});
+ },
showNodePalette: function(s) {
showNodePalette=s;
if(!s){
@@ -2044,10 +2270,10 @@ RED.view = (function() {
}
//console.log("showNodePalette:" + showNodePalette);
},
-
//TODO: should these move to an import/export module?
showImportNodesDialog: showImportNodesDialog,
showExportNodesDialog: showExportNodesDialog,
showExportNodesLibraryDialog: showExportNodesLibraryDialog
};
+
})();
diff --git a/dgbuilder/public/util/js/dgeToXml.js b/dgbuilder/public/util/js/dgeToXml.js
index e99d6495..7e5c9721 100644
--- a/dgbuilder/public/util/js/dgeToXml.js
+++ b/dgbuilder/public/util/js/dgeToXml.js
@@ -773,6 +773,11 @@ var jqxhr = $.post( "/getSharedFlow",{"filePath":filePath})
.done(function(data) {
$( "#dgflow-browser-dialog").dialog("close");
var migratedNodes = migrateNodes(data);
+ var nodeSet = getCurrentFlowNodeSet();
+ //console.dir(nodeSet);
+ if(nodeSet != null && nodeSet.length == 0){
+ RED.view.setIsImportAction(true);
+ }
//RED.view.importNodes(data)
RED.view.importNodes(JSON.stringify(migratedNodes));
//console.log( "import done");
diff --git a/dgbuilder/red/server.js b/dgbuilder/red/server.js
index c0a5125d..de945b74 100644
--- a/dgbuilder/red/server.js
+++ b/dgbuilder/red/server.js
@@ -226,6 +226,7 @@ function createServer(_server,_settings) {
slaActions.deleteDG(jsonObj,req,res);
});
+
app.get("/getCurrentSettings",function(req,res) {
var appDir = path.dirname(require.main.filename);
var userDir = appDir + "/" + settings.userDir;
@@ -304,8 +305,16 @@ function createServer(_server,_settings) {
function(req,res) {
var appDir = path.dirname(require.main.filename);
var gitLocalRepository = settings.gitLocalRepository;
+ /*
+ var userDir=settings.userDir;
+ var outputDir = appDir + "/" + userDir + "/orig_dgs";
+ if (!fs.existsSync(outputDir)){
+ fs.mkdirSync(outputDir);
+ }
+ */
//console.dir(req);
var filePath = req.query.filePath;
+ //var currTabId = req.query.currTabId;
var fullFilePath = gitLocalRepository +"/" + filePath ;
//console.log("fullFilePath:" + fullFilePath);
var exec = require('child_process').exec;
@@ -325,6 +334,15 @@ function createServer(_server,_settings) {
console.log("stderr:" + stderr);
}
if(stdout){
+ /*
+ var jsonStr= stdout;
+ var jsonStrFormatted=[];
+ try{
+ jsonStrFormatted= JSON.parse(jsonStr);
+ }catch(e){
+ }
+ fs.writeFileSync( outputDir + "/" +currTabId,JSON.stringify(jsonStrFormatted,null,4) );
+ */
//console.log("output:" + stdout);
res.send(200,{'stdout':stdout,'stderr':stderr});
}
@@ -332,6 +350,51 @@ function createServer(_server,_settings) {
});
});
+ app.post("/saveImportedDG",
+ express.json(),
+ function(req,res) {
+ var qs = require('querystring');
+ var body = '';
+ req.on('data', function (data) {
+ body += data;
+ });
+ req.on('end', function () {
+ var appDir = path.dirname(require.main.filename);
+ var userDir=settings.userDir;
+ var outputDir = appDir + "/" + userDir + "/orig_dgs";
+ if (!fs.existsSync(outputDir)){
+ fs.mkdirSync(outputDir);
+ }
+ var post = qs.parse(body);
+ var importedNodes = post.importedNodes;
+ var currTabId = post.currTabId;
+ fs.writeFileSync( outputDir + "/" +currTabId,importedNodes );
+ res.send(200,{"output":"SUCCESS"});
+ });
+ });
+
+ app.post("/saveImportedDG",
+ express.json(),
+ function(req,res) {
+ var qs = require('querystring');
+ var body = '';
+ req.on('data', function (data) {
+ body += data;
+ });
+ req.on('end', function () {
+ var appDir = path.dirname(require.main.filename);
+ var userDir=settings.userDir;
+ var outputDir = appDir + "/" + userDir + "/orig_dgs";
+ if (!fs.existsSync(outputDir)){
+ fs.mkdirSync(outputDir);
+ }
+ var post = qs.parse(body);
+ var importedNodes = post.importedNodes;
+ var currTabId = post.currTabId;
+ fs.writeFileSync( outputDir + "/" +currTabId,importedNodes );
+ res.send(200,{"output":"SUCCESS"});
+ });
+ });
app.get("/gitcheckout", function(req,res) {
var appDir = path.dirname(require.main.filename);
@@ -469,6 +532,7 @@ function createServer(_server,_settings) {
console.log("Error:" + e);
}
}
+
function getCurrentDate(){
var d = new Date();
var mm = d.getMonth() + 1;
@@ -882,6 +946,12 @@ function createServer(_server,_settings) {
//var release = userDir.replace(/releases/g,"release");
res.json({"release" : userDir});
});
+ app.get("/readFile",function(req,res) {
+ var userDir=settings.userDir;
+ var filePath = userDir + "/" + req.query.filePath;
+ var buf = fs.readFileSync(filePath, "utf8");
+ res.json({"output" :buf });
+ });
app.post("/getFiles/:id",function(req,res) {
var id = req.params.id;
//console.log("id:" + id);
@@ -1105,8 +1175,7 @@ function createServer(_server,_settings) {
var matchedArr = fileName.match(/.zip$/);
if(matchedArr != null && matchedArr.length >0){
console.log("uploaded zip file" + fileName);
- //commandToExec = appDir + "/tools/generate_props_from_yangs_zip.sh " + yangFileFullPath ;
- commandToExec = appDir + "/tools/generate_props_from_yang.sh " + yangFileFullPath ;
+ commandToExec = appDir + "/tools/generate_props_from_yangs_zip.sh " + yangFileFullPath ;
}else{
commandToExec = appDir + "/tools/generate_props_from_yang.sh " + yangFileFullPath ;
console.log("uploaded file" + fileName);
@@ -1280,6 +1349,7 @@ function createServer(_server,_settings) {
});
});
+
app.get("/getYangFiles",function(req,res) {
var appDir = path.dirname(require.main.filename);
var yangFilesDir=appDir + "/yangFiles";
@@ -1382,6 +1452,35 @@ function uninstallModule(module) {
function start() {
var defer = when.defer();
+ //split and save startup dgs if any from flows.json file
+ var appDir = path.dirname(require.main.filename);
+ var userDir = appDir + "/" + settings.userDir;
+ var flowFile = settings.flowFile;
+ var outputDir = userDir + "/orig_dgs";
+ console.log("appDir:" + appDir);
+ console.log("flowFile:" + flowFile);
+ var execFile = require('child_process').execFile;
+ var commandToExec = appDir + "/tools/splitFlows.sh" ;
+ console.log("commandToExec:" + commandToExec);
+ var args = [flowFile,outputDir];
+ var child = execFile(commandToExec ,args,function (error,stdout,stderr){
+ if(error){
+ console.log("Error occured:" + error);
+ if(stderr){
+ console.log("stderr:" + stderr);
+ }else{
+ console.log("error:" + error);
+ }
+ }else{
+ if(stderr){
+ console.log("stderr:" + stderr);
+ }
+ if(stdout){
+ console.log("output:" + stdout);
+ }
+ }
+ });
+
storage.init(settings).then(function() {
settings.load(storage).then(function() {
diff --git a/dgbuilder/tools/splitFlows.js b/dgbuilder/tools/splitFlows.js
new file mode 100644
index 00000000..39245a2b
--- /dev/null
+++ b/dgbuilder/tools/splitFlows.js
@@ -0,0 +1,29 @@
+var fs=require('fs');
+var path = require("path");
+var full_path_to_flows_json=process.argv[2];
+var output_dir=process.argv[3];
+console.log("full_path_to_flows_json:" + full_path_to_flows_json);
+console.log("output_dir:" + output_dir);
+var buf= null;
+if (fs.existsSync(full_path_to_flows_json)) {
+ buf = JSON.parse(fs.readFileSync(full_path_to_flows_json, "utf8"));
+}
+var tabs = [];
+for (var i=0;buf != null && i<buf.length;i++){
+ if(buf[i].type == "tab"){
+ tabs.push(buf[i]);
+ }
+}
+for(var i=0;tabs != null && i<tabs.length; i++){
+ var tab = tabs[i];
+ var tabId = tab.id;
+ var dgNodes=[];
+ for (var j=0;buf != null && j<buf.length;j++){
+ var zId = buf[j].z;
+ if(zId != undefined && zId != "" && tabId == zId){
+ dgNodes.push(buf[j]);
+ }
+ }
+
+ fs.writeFileSync( output_dir + "/" +tabId, JSON.stringify(dgNodes,null,4));
+}
diff --git a/dgbuilder/tools/splitFlows.sh b/dgbuilder/tools/splitFlows.sh
new file mode 100755
index 00000000..ccb70cf9
--- /dev/null
+++ b/dgbuilder/tools/splitFlows.sh
@@ -0,0 +1,8 @@
+if [ "$#" != "2" ]
+then
+ echo "Usage: $0 full_path_to_flows_json_file full_path_to_output_dir"
+ exit
+fi
+rm -rf "$2" 2>/dev/null
+mkdir "$2" 2>/dev/null
+node ${PROJECT_HOME}/tools/splitFlows.js $1 $2