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.

492 line
12KB

  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 addNode(n) {
  69. if (n._def.category == "config") {
  70. configNodes[n.id] = n;
  71. RED.sidebar.config.refresh();
  72. } else {
  73. n.dirty = true;
  74. nodes.push(n);
  75. var updatedConfigNode = false;
  76. for (var d in n._def.defaults) {
  77. if (n._def.defaults.hasOwnProperty(d)) {
  78. var property = n._def.defaults[d];
  79. if (property.type) {
  80. var type = getType(property.type)
  81. if (type && type.category == "config") {
  82. var configNode = configNodes[n[d]];
  83. if (configNode) {
  84. updatedConfigNode = true;
  85. configNode.users.push(n);
  86. }
  87. }
  88. }
  89. }
  90. }
  91. if (updatedConfigNode) {
  92. RED.sidebar.config.refresh();
  93. }
  94. }
  95. }
  96. function addLink(l) {
  97. links.push(l);
  98. }
  99. function addConfig(c) {
  100. configNodes[c.id] = c;
  101. }
  102. function getNode(id) {
  103. if (id in configNodes) {
  104. return configNodes[id];
  105. } else {
  106. for (var n in nodes) {
  107. if (nodes[n].id == id) {
  108. return nodes[n];
  109. }
  110. }
  111. }
  112. return null;
  113. }
  114. function removeNode(id) {
  115. var removedLinks = [];
  116. if (id in configNodes) {
  117. delete configNodes[id];
  118. RED.sidebar.config.refresh();
  119. } else {
  120. var node = getNode(id);
  121. if (node) {
  122. nodes.splice(nodes.indexOf(node),1);
  123. removedLinks = links.filter(function(l) { return (l.source === node) || (l.target === node); });
  124. removedLinks.map(function(l) {links.splice(links.indexOf(l), 1); });
  125. }
  126. var updatedConfigNode = false;
  127. for (var d in node._def.defaults) {
  128. if (node._def.defaults.hasOwnProperty(d)) {
  129. var property = node._def.defaults[d];
  130. if (property.type) {
  131. var type = getType(property.type)
  132. if (type && type.category == "config") {
  133. var configNode = configNodes[node[d]];
  134. if (configNode) {
  135. updatedConfigNode = true;
  136. var users = configNode.users;
  137. users.splice(users.indexOf(node),1);
  138. }
  139. }
  140. }
  141. }
  142. }
  143. if (updatedConfigNode) {
  144. RED.sidebar.config.refresh();
  145. }
  146. }
  147. return removedLinks;
  148. }
  149. function removeLink(l) {
  150. var index = links.indexOf(l);
  151. if (index != -1) {
  152. links.splice(index,1);
  153. }
  154. }
  155. function refreshValidation() {
  156. for (var n=0;n<nodes.length;n++) {
  157. RED.editor.validateNode(nodes[n]);
  158. }
  159. }
  160. function addWorkspace(ws) {
  161. workspaces[ws.id] = ws;
  162. }
  163. function getWorkspace(id) {
  164. return workspaces[id];
  165. }
  166. function removeWorkspace(id) {
  167. delete workspaces[id];
  168. var removedNodes = [];
  169. var removedLinks = [];
  170. var n;
  171. for (n=0;n<nodes.length;n++) {
  172. var node = nodes[n];
  173. if (node.z == id) {
  174. removedNodes.push(node);
  175. }
  176. }
  177. for (n=0;n<removedNodes.length;n++) {
  178. var rmlinks = removeNode(removedNodes[n].id);
  179. removedLinks = removedLinks.concat(rmlinks);
  180. }
  181. return {nodes:removedNodes,links:removedLinks};
  182. }
  183. function getAllFlowNodes(node) {
  184. var visited = {};
  185. visited[node.id] = true;
  186. var nns = [node];
  187. var stack = [node];
  188. while(stack.length !== 0) {
  189. var n = stack.shift();
  190. var childLinks = links.filter(function(d) { return (d.source === n) || (d.target === n);});
  191. for (var i=0;i<childLinks.length;i++) {
  192. var child = (childLinks[i].source === n)?childLinks[i].target:childLinks[i].source;
  193. if (!visited[child.id]) {
  194. visited[child.id] = true;
  195. nns.push(child);
  196. stack.push(child);
  197. }
  198. }
  199. }
  200. return nns;
  201. }
  202. /**
  203. * Converts a node to an exportable JSON Object
  204. **/
  205. function convertNode(n, exportCreds) {
  206. exportCreds = exportCreds || false;
  207. var node = {};
  208. node.id = n.id;
  209. node.type = n.type;
  210. for (var d in n._def.defaults) {
  211. if (n._def.defaults.hasOwnProperty(d)) {
  212. node[d] = n[d];
  213. }
  214. }
  215. if(exportCreds && n.credentials) {
  216. node.credentials = {};
  217. for (var cred in n._def.credentials) {
  218. if (n._def.credentials.hasOwnProperty(cred)) {
  219. if (n.credentials[cred] != null) {
  220. node.credentials[cred] = n.credentials[cred];
  221. }
  222. }
  223. }
  224. }
  225. if (n._def.category != "config") {
  226. node.x = n.x;
  227. node.y = n.y;
  228. node.z = n.z;
  229. node.wires = [];
  230. for(var i=0;i<n.outputs;i++) {
  231. node.wires.push([]);
  232. }
  233. var wires = links.filter(function(d){return d.source === n;});
  234. for (var j=0;j<wires.length;j++) {
  235. var w = wires[j];
  236. node.wires[w.sourcePort].push(w.target.id + ":" + w.targetPort);
  237. }
  238. }
  239. return node;
  240. }
  241. /**
  242. * Converts the current node selection to an exportable JSON Object
  243. **/
  244. function createExportableNodeSet(set) {
  245. var nns = [];
  246. var exportedConfigNodes = {};
  247. for (var n=0;n<set.length;n++) {
  248. var node = set[n].n;
  249. var convertedNode = RED.nodes.convertNode(node);
  250. for (var d in node._def.defaults) {
  251. if (node._def.defaults[d].type && node[d] in configNodes) {
  252. var confNode = configNodes[node[d]];
  253. var exportable = getType(node._def.defaults[d].type).exportable;
  254. if ((exportable == null || exportable)) {
  255. if (!(node[d] in exportedConfigNodes)) {
  256. exportedConfigNodes[node[d]] = true;
  257. nns.unshift(RED.nodes.convertNode(confNode));
  258. }
  259. } else {
  260. convertedNode[d] = "";
  261. }
  262. }
  263. }
  264. nns.push(convertedNode);
  265. }
  266. return nns;
  267. }
  268. //TODO: rename this (createCompleteNodeSet)
  269. function createCompleteNodeSet() {
  270. var nns = [];
  271. var i;
  272. for (i in workspaces) {
  273. if (workspaces.hasOwnProperty(i)) {
  274. nns.push(workspaces[i]);
  275. }
  276. }
  277. for (i in configNodes) {
  278. if (configNodes.hasOwnProperty(i)) {
  279. nns.push(convertNode(configNodes[i], true));
  280. }
  281. }
  282. for (i=0;i<nodes.length;i++) {
  283. var node = nodes[i];
  284. nns.push(convertNode(node, true));
  285. }
  286. return nns;
  287. }
  288. function importNodes(newNodesObj,createNewIds) {
  289. try {
  290. var i;
  291. var n;
  292. var newNodes;
  293. if (typeof newNodesObj === "string") {
  294. if (newNodesObj === "") {
  295. return;
  296. }
  297. newNodes = JSON.parse(newNodesObj);
  298. } else {
  299. newNodes = newNodesObj;
  300. }
  301. if (!$.isArray(newNodes)) {
  302. newNodes = [newNodes];
  303. }
  304. var unknownTypes = [];
  305. for (i=0;i<newNodes.length;i++) {
  306. n = newNodes[i];
  307. // TODO: remove workspace in next release+1
  308. if (n.type != "workspace" && n.type != "tab" && !getType(n.type)) {
  309. // TODO: get this UI thing out of here! (see below as well)
  310. n.name = n.type;
  311. n.type = "unknown";
  312. if (unknownTypes.indexOf(n.name)==-1) {
  313. unknownTypes.push(n.name);
  314. }
  315. if (n.x == null && n.y == null) {
  316. // config node - remove it
  317. newNodes.splice(i,1);
  318. i--;
  319. }
  320. }
  321. }
  322. /*
  323. if (unknownTypes.length > 0) {
  324. var typeList = "<ul><li>"+unknownTypes.join("</li><li>")+"</li></ul>";
  325. var type = "type"+(unknownTypes.length > 1?"s":"");
  326. RED.notify("<strong>Imported unrecognised "+type+":</strong>"+typeList,"error",false,10000);
  327. //"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");
  328. }
  329. for (i=0;i<newNodes.length;i++) {
  330. n = newNodes[i];
  331. // TODO: remove workspace in next release+1
  332. if (n.type === "workspace" || n.type === "tab") {
  333. if (n.type === "workspace") {
  334. n.type = "tab";
  335. }
  336. if (defaultWorkspace == null) {
  337. defaultWorkspace = n;
  338. }
  339. addWorkspace(n);
  340. RED.view.addWorkspace(n);
  341. }
  342. }
  343. if (defaultWorkspace == null) {
  344. defaultWorkspace = { type:"tab", id:getID(), label:"Sheet 1" };
  345. addWorkspace(defaultWorkspace);
  346. RED.view.addWorkspace(defaultWorkspace);
  347. }
  348. */
  349. var node_map = {};
  350. var new_nodes = [];
  351. var new_links = [];
  352. for (i=0;i<newNodes.length;i++) {
  353. n = newNodes[i];
  354. // TODO: remove workspace in next release+1
  355. if (n.type !== "workspace" && n.type !== "tab") {
  356. var def = getType(n.type);
  357. if (def && def.category == "config") {
  358. if (!RED.nodes.node(n.id)) {
  359. var configNode = {id:n.id,type:n.type,users:[]};
  360. for (var d in def.defaults) {
  361. if (def.defaults.hasOwnProperty(d)) {
  362. configNode[d] = n[d];
  363. }
  364. }
  365. configNode.label = def.label;
  366. configNode._def = def;
  367. RED.nodes.add(configNode);
  368. }
  369. } else {
  370. var node = {x:n.x,y:n.y,z:n.z,type:0,wires:n.wires,changed:false};
  371. if (createNewIds) {
  372. node.z = RED.view.getWorkspace();
  373. node.id = getID();
  374. } else {
  375. node.id = n.id;
  376. if (node.z == null || !workspaces[node.z]) {
  377. node.z = RED.view.getWorkspace();
  378. }
  379. }
  380. node.type = n.type;
  381. node._def = def;
  382. if (!node._def) {
  383. node._def = {
  384. color:"#fee",
  385. defaults: {},
  386. label: "unknown: "+n.type,
  387. labelStyle: "node_label_italic",
  388. outputs: n.outputs||n.wires.length
  389. }
  390. }
  391. node.outputs = n.outputs||node._def.outputs;
  392. for (var d2 in node._def.defaults) {
  393. if (node._def.defaults.hasOwnProperty(d2)) {
  394. node[d2] = n[d2];
  395. }
  396. }
  397. addNode(node);
  398. RED.editor.validateNode(node);
  399. node_map[n.id] = node;
  400. new_nodes.push(node);
  401. }
  402. }
  403. }
  404. for (i=0;i<new_nodes.length;i++) {
  405. n = new_nodes[i];
  406. for (var w1=0;w1<n.wires.length;w1++) {
  407. var wires = (n.wires[w1] instanceof Array)?n.wires[w1]:[n.wires[w1]];
  408. for (var w2=0;w2<wires.length;w2++) {
  409. var parts = wires[w2].split(":");
  410. if (parts.length == 2 && parts[0] in node_map) {
  411. var dst = node_map[parts[0]];
  412. var link = {source:n,sourcePort:w1,target:dst,targetPort:parts[1]};
  413. addLink(link);
  414. new_links.push(link);
  415. }
  416. }
  417. }
  418. delete n.wires;
  419. }
  420. return [new_nodes,new_links];
  421. } catch(error) {
  422. //TODO: get this UI thing out of here! (see above as well)
  423. RED.notify("<strong>Error</strong>: "+error,"error");
  424. return null;
  425. }
  426. }
  427. return {
  428. registerType: registerType,
  429. getType: getType,
  430. convertNode: convertNode,
  431. add: addNode,
  432. addLink: addLink,
  433. remove: removeNode,
  434. removeLink: removeLink,
  435. addWorkspace: addWorkspace,
  436. removeWorkspace: removeWorkspace,
  437. workspace: getWorkspace,
  438. eachNode: function(cb) {
  439. for (var n=0;n<nodes.length;n++) {
  440. cb(nodes[n]);
  441. }
  442. },
  443. eachLink: function(cb) {
  444. for (var l=0;l<links.length;l++) {
  445. cb(links[l]);
  446. }
  447. },
  448. eachConfig: function(cb) {
  449. for (var id in configNodes) {
  450. if (configNodes.hasOwnProperty(id)) {
  451. cb(configNodes[id]);
  452. }
  453. }
  454. },
  455. node: getNode,
  456. import: importNodes,
  457. refreshValidation: refreshValidation,
  458. getAllFlowNodes: getAllFlowNodes,
  459. createExportableNodeSet: createExportableNodeSet,
  460. createCompleteNodeSet: createCompleteNodeSet,
  461. id: getID,
  462. cppName: createUniqueCppName,
  463. nodes: nodes, // TODO: exposed for d3 vis
  464. links: links // TODO: exposed for d3 vis
  465. };
  466. })();