You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

преди 10 години
преди 10 години
преди 10 години
преди 10 години
преди 10 години
преди 10 години
преди 10 години
преди 10 години
преди 10 години
преди 10 години
преди 10 години
преди 10 години
преди 10 години
преди 10 години
преди 10 години
преди 10 години
преди 10 години
преди 10 години
преди 10 години
преди 10 години
преди 10 години
преди 10 години
преди 10 години
преди 10 години
преди 10 години
преди 10 години
преди 10 години
преди 10 години
преди 10 години
преди 10 години
преди 10 години
преди 10 години
преди 10 години
преди 10 години
преди 10 години
преди 10 години
преди 10 години
преди 10 години
преди 10 години
преди 10 години
преди 10 години
преди 10 години
преди 10 години
преди 10 години
преди 10 години
преди 10 години
преди 10 години
преди 10 години
преди 10 години
преди 10 години
преди 10 години
преди 10 години
преди 10 години
преди 10 години
преди 10 години
преди 10 години
преди 10 години
преди 10 години
преди 10 години
преди 10 години
преди 10 години
преди 10 години
преди 10 години
преди 10 години
преди 10 години
преди 10 години
преди 10 години
преди 10 години
преди 10 години
преди 10 години
преди 10 години
преди 10 години
преди 10 години
преди 10 години
преди 10 години
преди 10 години
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686
  1. /** Modified from original Node-Red source, for audio system visualization
  2. * vim: set ts=4:
  3. * Copyright 2013 IBM Corp.
  4. *
  5. * Licensed under the Apache License, Version 2.0 (the "License");
  6. * you may not use this file except in compliance with the License.
  7. * You may obtain a copy of the License at
  8. *
  9. * http://www.apache.org/licenses/LICENSE-2.0
  10. *
  11. * Unless required by applicable law or agreed to in writing, software
  12. * distributed under the License is distributed on an "AS IS" BASIS,
  13. * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  14. * See the License for the specific language governing permissions and
  15. * limitations under the License.
  16. **/
  17. RED.nodes = (function() {
  18. var node_defs = {};
  19. var nodes = [];
  20. var configNodes = {};
  21. var links = [];
  22. //var defaultWorkspace;
  23. var workspaces = {};
  24. function registerType(nt,def) {
  25. node_defs[nt] = def;
  26. // TODO: too tightly coupled into palette UI
  27. RED.palette.add(nt,def);
  28. }
  29. function getID() {
  30. var str = (1+Math.random()*4294967295).toString(16);
  31. console.log("getID = " + str);
  32. return str;
  33. }
  34. function checkID(name) {
  35. var i;
  36. for (i=0;i<nodes.length;i++) {
  37. console.log("checkID, nodes[i].id = " + nodes[i].id);
  38. if (nodes[i].id == name) return true;
  39. }
  40. /*
  41. for (i in workspaces) {
  42. if (workspaces.hasOwnProperty(i)) { }
  43. }
  44. for (i in configNodes) {
  45. if (configNodes.hasOwnProperty(i)) { }
  46. }
  47. */
  48. return false;
  49. }
  50. function createUniqueCppName(n) {
  51. console.log("getUniqueCppName, n.type=" + n.type + ", n._def.shortName=" + n._def.shortName);
  52. var basename = (n._def.shortName) ? n._def.shortName : n.type.replace(/^Analog/, "");
  53. console.log("getUniqueCppName, using basename=" + basename);
  54. var count = 1;
  55. var sep = /[0-9]$/.test(basename) ? "_" : "";
  56. var name;
  57. while (1) {
  58. name = basename + sep + count;
  59. if (!checkID(name)) break;
  60. count++;
  61. }
  62. console.log("getUniqueCppName, unique name=" + name);
  63. return name;
  64. }
  65. function getType(type) {
  66. return node_defs[type];
  67. }
  68. function selectNode(name) {
  69. // window.history.pushState(null, null, window.location.protocol + "//"
  70. // + window.location.host + window.location.pathname + '?info=' + name);
  71. }
  72. function addNode(n) {
  73. if (n._def.category == "config") {
  74. configNodes[n.id] = n;
  75. RED.sidebar.config.refresh();
  76. } else {
  77. n.dirty = true;
  78. nodes.push(n);
  79. var updatedConfigNode = false;
  80. for (var d in n._def.defaults) {
  81. if (n._def.defaults.hasOwnProperty(d)) {
  82. var property = n._def.defaults[d];
  83. if (property.type) {
  84. var type = getType(property.type);
  85. if (type && type.category == "config") {
  86. var configNode = configNodes[n[d]];
  87. if (configNode) {
  88. updatedConfigNode = true;
  89. configNode.users.push(n);
  90. }
  91. }
  92. }
  93. }
  94. }
  95. if (updatedConfigNode) {
  96. RED.sidebar.config.refresh();
  97. }
  98. }
  99. }
  100. function addLink(l) {
  101. links.push(l);
  102. }
  103. /*
  104. function addConfig(c) {
  105. configNodes[c.id] = c;
  106. }
  107. */
  108. function getNode(id) {
  109. if (id in configNodes) {
  110. return configNodes[id];
  111. } else {
  112. for (var n in nodes) {
  113. if (nodes[n].id == id) {
  114. return nodes[n];
  115. }
  116. }
  117. }
  118. return null;
  119. }
  120. function removeNode(id) {
  121. var removedLinks = [];
  122. if (id in configNodes) {
  123. delete configNodes[id];
  124. RED.sidebar.config.refresh();
  125. } else {
  126. var node = getNode(id);
  127. if (node) {
  128. nodes.splice(nodes.indexOf(node),1);
  129. removedLinks = links.filter(function(l) { return (l.source === node) || (l.target === node); });
  130. removedLinks.map(function(l) {links.splice(links.indexOf(l), 1); });
  131. }
  132. var updatedConfigNode = false;
  133. for (var d in node._def.defaults) {
  134. if (node._def.defaults.hasOwnProperty(d)) {
  135. var property = node._def.defaults[d];
  136. if (property.type) {
  137. var type = getType(property.type);
  138. if (type && type.category == "config") {
  139. var configNode = configNodes[node[d]];
  140. if (configNode) {
  141. updatedConfigNode = true;
  142. var users = configNode.users;
  143. users.splice(users.indexOf(node),1);
  144. }
  145. }
  146. }
  147. }
  148. }
  149. if (updatedConfigNode) {
  150. RED.sidebar.config.refresh();
  151. }
  152. }
  153. return removedLinks;
  154. }
  155. function removeLink(l) {
  156. var index = links.indexOf(l);
  157. if (index != -1) {
  158. links.splice(index,1);
  159. }
  160. }
  161. function refreshValidation() {
  162. for (var n=0;n<nodes.length;n++) {
  163. RED.editor.validateNode(nodes[n]);
  164. }
  165. }
  166. function addWorkspace(ws) {
  167. workspaces[ws.id] = ws;
  168. }
  169. function getWorkspace(id) {
  170. return workspaces[id];
  171. }
  172. function removeWorkspace(id) {
  173. delete workspaces[id];
  174. var removedNodes = [];
  175. var removedLinks = [];
  176. var n;
  177. for (n=0;n<nodes.length;n++) {
  178. var node = nodes[n];
  179. if (node.z == id) {
  180. removedNodes.push(node);
  181. }
  182. }
  183. for (n=0;n<removedNodes.length;n++) {
  184. var rmlinks = removeNode(removedNodes[n].id);
  185. removedLinks = removedLinks.concat(rmlinks);
  186. }
  187. return {nodes:removedNodes,links:removedLinks};
  188. }
  189. function getAllFlowNodes(node) {
  190. var visited = {};
  191. visited[node.id] = true;
  192. var nns = [node];
  193. var stack = [node];
  194. while(stack.length !== 0) {
  195. var n = stack.shift();
  196. var childLinks = links.filter(function(d) { return (d.source === n) || (d.target === n);});
  197. for (var i=0;i<childLinks.length;i++) {
  198. var child = (childLinks[i].source === n)?childLinks[i].target:childLinks[i].source;
  199. if (!visited[child.id]) {
  200. visited[child.id] = true;
  201. nns.push(child);
  202. stack.push(child);
  203. }
  204. }
  205. }
  206. return nns;
  207. }
  208. /**
  209. * Converts a node to an exportable JSON Object
  210. **/
  211. function convertNode(n, exportCreds) {
  212. exportCreds = exportCreds || false;
  213. var node = {};
  214. node.id = n.id;
  215. node.type = n.type;
  216. for (var d in n._def.defaults) {
  217. if (n._def.defaults.hasOwnProperty(d)) {
  218. node[d] = n[d];
  219. }
  220. }
  221. if(exportCreds && n.credentials) {
  222. node.credentials = {};
  223. for (var cred in n._def.credentials) {
  224. if (n._def.credentials.hasOwnProperty(cred)) {
  225. if (n.credentials[cred] != null) {
  226. node.credentials[cred] = n.credentials[cred];
  227. }
  228. }
  229. }
  230. }
  231. if (n._def.category != "config") {
  232. node.x = n.x;
  233. node.y = n.y;
  234. node.z = n.z;
  235. node.wires = [];
  236. for(var i=0;i<n.outputs;i++) {
  237. node.wires.push([]);
  238. }
  239. var wires = links.filter(function(d){return d.source === n;});
  240. for (var j=0;j<wires.length;j++) {
  241. var w = wires[j];
  242. node.wires[w.sourcePort].push(w.target.id + ":" + w.targetPort);
  243. }
  244. }
  245. return node;
  246. }
  247. /**
  248. * Converts the current node selection to an exportable JSON Object
  249. **/
  250. function createExportableNodeSet(set) {
  251. var nns = [];
  252. var exportedConfigNodes = {};
  253. for (var n=0;n<set.length;n++) {
  254. var node = set[n].n;
  255. var convertedNode = RED.nodes.convertNode(node);
  256. for (var d in node._def.defaults) {
  257. if (node._def.defaults[d].type && node[d] in configNodes) {
  258. var confNode = configNodes[node[d]];
  259. var exportable = getType(node._def.defaults[d].type).exportable;
  260. if ((exportable == null || exportable)) {
  261. if (!(node[d] in exportedConfigNodes)) {
  262. exportedConfigNodes[node[d]] = true;
  263. nns.unshift(RED.nodes.convertNode(confNode));
  264. }
  265. } else {
  266. convertedNode[d] = "";
  267. }
  268. }
  269. }
  270. nns.push(convertedNode);
  271. }
  272. return nns;
  273. }
  274. //TODO: rename this (createCompleteNodeSet)
  275. function createCompleteNodeSet() {
  276. var nns = [];
  277. var i;
  278. for (i in workspaces) {
  279. if (workspaces.hasOwnProperty(i)) {
  280. nns.push(workspaces[i]);
  281. }
  282. }
  283. for (i in configNodes) {
  284. if (configNodes.hasOwnProperty(i)) {
  285. nns.push(convertNode(configNodes[i], true));
  286. }
  287. }
  288. for (i=0;i<nodes.length;i++) {
  289. var node = nodes[i];
  290. nns.push(convertNode(node, true));
  291. }
  292. return nns;
  293. }
  294. /**
  295. * Parses the input string which contains copied code from the Arduino IDE, scans the
  296. * nodes and connections and forms them into a JSON representation which will be
  297. * returned as string.
  298. *
  299. * So the result may directly imported in the localStorage or the import dialog.
  300. */
  301. function cppToJSON(newNodesStr) {
  302. var newNodes = [];
  303. var skipped = [];
  304. var cables = [];
  305. var words = [];
  306. var lastY = 0;
  307. const NODE_COMMENT = "//";
  308. const NODE_AC = "AudioConnection";
  309. var parseLine = function(line) {
  310. var parts = line.match(/^(\S+)\s(.*)/);
  311. if (parts == null) {
  312. return
  313. }
  314. parts = parts.slice(1);
  315. if (parts == null || parts.length <= 1) {
  316. return
  317. }
  318. var type = $.trim(parts[0]);
  319. line = $.trim(parts[1]) + " ";
  320. var name = "";
  321. var coords = [0, 0];
  322. var conn = [];
  323. parts = line.match(/^([^;]{0,});(.*)/);
  324. if (parts && parts.length >= 2) {
  325. parts = parts.slice(1);
  326. if (parts && parts.length >= 1) {
  327. name = $.trim(parts[0]);
  328. coords = $.trim(parts[1]);
  329. parts = coords.match(/^([^\/]{0,})\/\/xy=(.*)/);
  330. if (parts) {
  331. parts = parts.slice(1);
  332. coords = $.trim(parts[1]).split(",");
  333. }
  334. }
  335. }
  336. if (type == NODE_AC) {
  337. parts = name.match(/^([^\(]*\()([^\)]*)(.*)/);
  338. if (parts && parts.length > 1) {
  339. conn = $.trim(parts[2]).split(",");
  340. cables.push(conn);
  341. }
  342. } else if (type == NODE_COMMENT) {
  343. // do nothing
  344. } else {
  345. var names = [];
  346. var yPos = [];
  347. if (name.indexOf(",") >= 0) {
  348. names = name.split(",");
  349. } else {
  350. names.push(name);
  351. }
  352. for (var n = 0; n < names.length; n++) {
  353. name = names[n].trim();
  354. var gap = 10;
  355. var def = node_defs[type];
  356. var dW = Math.max(RED.view.defaults.width, RED.view.calculateTextWidth(name) + (def.inputs > 0 ? 7 : 0));
  357. var dH = Math.max(RED.view.defaults.height,(Math.max(def.outputs, def.inputs)||0) * 15);
  358. var newX = parseInt(coords ? coords[0] : 0);
  359. var newY = parseInt(coords ? coords[1] : 0);
  360. //newY = newY == 0 ? lastY + (dH * n) + gap : newY;
  361. //lastY = Math.max(lastY, newY);
  362. var node = new Object({"order": n, "id": name, "type": type, "x": newX, "y": newY, "z": 0, "wires": []});
  363. // first solution: skip existing id
  364. if (RED.nodes.node(node.id) !== null) {
  365. skipped.push(node.id);
  366. } else {
  367. newNodes.push(node);
  368. }
  369. }
  370. }
  371. };
  372. var findNode = function(name) {
  373. var len = newNodes.length;
  374. for (var key = 0; key < len; key++) {
  375. if (newNodes[key].id == name) {
  376. return newNodes[key];
  377. }
  378. }
  379. };
  380. var linkCables = function(cables) {
  381. $.each(cables, function(i, item) {
  382. var conn = item;
  383. // when there are only two entries in the array, there
  384. // is only one output to connect to one input, so we have
  385. // to extend the array with the appropriate index "0" for
  386. // both parst (in and out)
  387. if (conn.length == 2) {
  388. conn[2] = conn[1];
  389. conn[1] = conn[3] = 0;
  390. }
  391. // now we assign the outputs (marked by the "idx" of the array)
  392. // to the inputs describend by text
  393. var currNode = findNode($.trim(conn[0]));
  394. var idx = parseInt($.trim(conn[1]));
  395. if (currNode) {
  396. if ($.trim(conn[2]) != "" && $.trim(conn[3]) != "") {
  397. var wire = $.trim(conn[2]) + ":" + $.trim(conn[3]);
  398. var tmp = currNode.wires[idx] ? currNode.wires[idx] : [];
  399. tmp.push(wire);
  400. currNode.wires[idx] = tmp;
  401. }
  402. }
  403. });
  404. };
  405. var traverseLines = function(raw) {
  406. var lines = raw.split("\n");
  407. for (var i = 0; i < lines.length; i++) {
  408. var line = lines[i].trim();
  409. // we reached the setup or loop part ...
  410. var pattSu = new RegExp(/\s*void\s*setup\s*\(\s*\).*/);
  411. var pattLo = new RegExp(/\s*void\s*loop\s*\(\s*\).*/);
  412. if (pattSu.test(line) || pattLo.test(line)) {
  413. break;
  414. }
  415. // we need at least two parts to examine
  416. var parts = line.match(/^(\S+)\s(.*)/);
  417. if (parts == null || parts.length == 1) {
  418. continue;
  419. }
  420. // ... and it has to end with an semikolon ...
  421. var pattSe = new RegExp(/.*;$/);
  422. if (pattSe.test(line)) {
  423. var word = parts[1].trim();
  424. if (words.indexOf(word) >= 0) {
  425. parseLine(line);
  426. }
  427. }
  428. }
  429. };
  430. /*
  431. var readCode = function() {
  432. var fileImport = $("#importInput")[0];
  433. var regex = /^([a-zA-Z0-9\s_\\.\-:])+(.ino|.txt)$/;
  434. if (regex.test(fileImport.value.toLowerCase())) {
  435. if (typeof (FileReader) != "undefined") {
  436. var reader = new FileReader();
  437. $(reader).on("load", function (e) {
  438. });
  439. reader.readAsText(fileImport.files[0]);
  440. } else {
  441. alert("This browser does not support HTML5.");
  442. }
  443. } else {
  444. alert("Please upload a valid INO or text file.");
  445. }
  446. };
  447. */
  448. function startImport() {
  449. words = Array(NODE_AC);
  450. $.each(node_defs, function (key, obj) {
  451. words.push(key);
  452. });
  453. traverseLines(newNodesStr);
  454. linkCables(cables);
  455. }
  456. startImport();
  457. return {
  458. count: newNodes.length,
  459. skipped: skipped.length,
  460. data: newNodes.length > 0 ? JSON.stringify(newNodes) : ""
  461. };
  462. }
  463. function importNodes(newNodesObj,createNewIds) {
  464. try {
  465. var i;
  466. var n;
  467. var newNodes;
  468. if (typeof newNodesObj === "string") {
  469. if (newNodesObj === "") {
  470. return;
  471. }
  472. newNodes = JSON.parse(newNodesObj);
  473. } else {
  474. newNodes = newNodesObj;
  475. }
  476. if (!$.isArray(newNodes)) {
  477. newNodes = [newNodes];
  478. }
  479. var unknownTypes = [];
  480. for (i=0;i<newNodes.length;i++) {
  481. n = newNodes[i];
  482. // TODO: remove workspace in next release+1
  483. if (n.type != "workspace" && n.type != "tab" && !getType(n.type)) {
  484. // TODO: get this UI thing out of here! (see below as well)
  485. n.name = n.type;
  486. n.type = "unknown";
  487. if (unknownTypes.indexOf(n.name)==-1) {
  488. unknownTypes.push(n.name);
  489. }
  490. if (n.x == null && n.y == null) {
  491. // config node - remove it
  492. newNodes.splice(i,1);
  493. i--;
  494. }
  495. }
  496. }
  497. /*
  498. if (unknownTypes.length > 0) {
  499. var typeList = "<ul><li>"+unknownTypes.join("</li><li>")+"</li></ul>";
  500. var type = "type"+(unknownTypes.length > 1?"s":"");
  501. RED.notify("<strong>Imported unrecognised "+type+":</strong>"+typeList,"error",false,10000);
  502. //"DO NOT DEPLOY while in this state.<br/>Either, add missing types to Node-RED, restart and then reload page,<br/>or delete unknown "+n.name+", rewire as required, and then deploy.","error");
  503. }
  504. for (i=0;i<newNodes.length;i++) {
  505. n = newNodes[i];
  506. // TODO: remove workspace in next release+1
  507. if (n.type === "workspace" || n.type === "tab") {
  508. if (n.type === "workspace") {
  509. n.type = "tab";
  510. }
  511. if (defaultWorkspace == null) {
  512. defaultWorkspace = n;
  513. }
  514. addWorkspace(n);
  515. RED.view.addWorkspace(n);
  516. }
  517. }
  518. if (defaultWorkspace == null) {
  519. defaultWorkspace = { type:"tab", id:getID(), label:"Sheet 1" };
  520. addWorkspace(defaultWorkspace);
  521. RED.view.addWorkspace(defaultWorkspace);
  522. }
  523. */
  524. var node_map = {};
  525. var new_nodes = [];
  526. var new_links = [];
  527. for (i=0;i<newNodes.length;i++) {
  528. n = newNodes[i];
  529. // TODO: remove workspace in next release+1
  530. if (n.type !== "workspace" && n.type !== "tab") {
  531. var def = getType(n.type);
  532. if (def && def.category == "config") {
  533. if (!RED.nodes.node(n.id)) {
  534. var configNode = {id:n.id,type:n.type,users:[]};
  535. for (var d in def.defaults) {
  536. if (def.defaults.hasOwnProperty(d)) {
  537. configNode[d] = n[d];
  538. }
  539. }
  540. configNode.label = def.label;
  541. configNode._def = def;
  542. RED.nodes.add(configNode);
  543. }
  544. } else {
  545. var node = {x:n.x,y:n.y,z:n.z,type:0,wires:n.wires,changed:false};
  546. if (createNewIds) {
  547. node.z = RED.view.getWorkspace();
  548. node.id = getID();
  549. } else {
  550. node.id = n.id;
  551. if (node.z == null || !workspaces[node.z]) {
  552. node.z = RED.view.getWorkspace();
  553. }
  554. }
  555. node.type = n.type;
  556. node._def = def;
  557. if (!node._def) {
  558. node._def = {
  559. color:"#fee",
  560. defaults: {},
  561. label: "unknown: "+n.type,
  562. labelStyle: "node_label_italic",
  563. outputs: n.outputs||n.wires.length
  564. }
  565. }
  566. node.outputs = n.outputs||node._def.outputs;
  567. for (var d2 in node._def.defaults) {
  568. if (node._def.defaults.hasOwnProperty(d2)) {
  569. node[d2] = n[d2];
  570. }
  571. }
  572. addNode(node);
  573. RED.editor.validateNode(node);
  574. node_map[n.id] = node;
  575. new_nodes.push(node);
  576. }
  577. }
  578. }
  579. for (i=0;i<new_nodes.length;i++) {
  580. n = new_nodes[i];
  581. for (var w1=0;w1<n.wires.length;w1++) {
  582. var wires = (n.wires[w1] instanceof Array)?n.wires[w1]:[n.wires[w1]];
  583. for (var w2=0;w2<wires.length;w2++) {
  584. var parts = wires[w2].split(":");
  585. if (parts.length == 2 && parts[0] in node_map) {
  586. var dst = node_map[parts[0]];
  587. var link = {source:n,sourcePort:w1,target:dst,targetPort:parts[1]};
  588. addLink(link);
  589. new_links.push(link);
  590. }
  591. }
  592. }
  593. delete n.wires;
  594. }
  595. return [new_nodes,new_links];
  596. } catch(error) {
  597. //TODO: get this UI thing out of here! (see above as well)
  598. RED.notify("<strong>Error</strong>: "+error,"error");
  599. return null;
  600. }
  601. }
  602. return {
  603. registerType: registerType,
  604. getType: getType,
  605. convertNode: convertNode,
  606. selectNode: selectNode,
  607. add: addNode,
  608. addLink: addLink,
  609. remove: removeNode,
  610. removeLink: removeLink,
  611. addWorkspace: addWorkspace,
  612. removeWorkspace: removeWorkspace,
  613. workspace: getWorkspace,
  614. eachNode: function(cb) {
  615. for (var n=0;n<nodes.length;n++) {
  616. cb(nodes[n]);
  617. }
  618. },
  619. eachLink: function(cb) {
  620. for (var l=0;l<links.length;l++) {
  621. cb(links[l]);
  622. }
  623. },
  624. eachConfig: function(cb) {
  625. for (var id in configNodes) {
  626. if (configNodes.hasOwnProperty(id)) {
  627. cb(configNodes[id]);
  628. }
  629. }
  630. },
  631. node: getNode,
  632. cppToJSON: cppToJSON,
  633. import: importNodes,
  634. refreshValidation: refreshValidation,
  635. getAllFlowNodes: getAllFlowNodes,
  636. createExportableNodeSet: createExportableNodeSet,
  637. createCompleteNodeSet: createCompleteNodeSet,
  638. id: getID,
  639. cppName: createUniqueCppName,
  640. nodes: nodes, // TODO: exposed for d3 vis
  641. links: links // TODO: exposed for d3 vis
  642. };
  643. })();