2013-09-05 16:02:48 +02:00
/ * *
* Copyright 2013 IBM Corp .
*
* Licensed under the Apache License , Version 2.0 ( the "License" ) ;
* you may not use this file except in compliance with the License .
* You may obtain a copy of the License at
*
* http : //www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing , software
* distributed under the License is distributed on an "AS IS" BASIS ,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND , either express or implied .
* See the License for the specific language governing permissions and
* limitations under the License .
* * /
RED . nodes = function ( ) {
2013-12-08 22:55:34 +01:00
2013-09-05 16:02:48 +02:00
var node _defs = { } ;
var nodes = [ ] ;
var configNodes = { } ;
var links = [ ] ;
2013-10-25 22:34:00 +02:00
var defaultWorkspace ;
var workspaces = { } ;
2013-12-08 22:55:34 +01:00
2013-09-05 16:02:48 +02:00
function registerType ( nt , def ) {
node _defs [ nt ] = def ;
2013-12-08 22:55:34 +01:00
// TODO: too tightly coupled into palette UI
2013-09-05 16:02:48 +02:00
RED . palette . add ( nt , def ) ;
}
2013-12-08 22:55:34 +01:00
2013-10-25 22:34:00 +02:00
function getID ( ) {
return ( 1 + Math . random ( ) * 4294967295 ) . toString ( 16 ) ;
}
2013-12-08 22:55:34 +01:00
2013-09-05 16:02:48 +02:00
function getType ( type ) {
return node _defs [ type ] ;
}
2013-12-08 22:55:34 +01:00
2013-09-05 16:02:48 +02:00
function addNode ( n ) {
if ( n . _def . category == "config" ) {
configNodes [ n . id ] = n ;
2014-02-26 23:58:44 +01:00
RED . sidebar . config . refresh ( ) ;
2013-09-05 16:02:48 +02:00
} else {
n . dirty = true ;
nodes . push ( n ) ;
2014-01-25 23:31:43 +01:00
var updatedConfigNode = false ;
for ( var d in n . _def . defaults ) {
var property = n . _def . defaults [ d ] ;
if ( property . type ) {
var type = getType ( property . type )
if ( type && type . category == "config" ) {
var configNode = configNodes [ n [ d ] ] ;
if ( configNode ) {
updatedConfigNode = true ;
configNode . users . push ( n ) ;
}
}
}
}
if ( updatedConfigNode ) {
2014-02-26 23:58:44 +01:00
RED . sidebar . config . refresh ( ) ;
2014-01-25 23:31:43 +01:00
}
2013-09-05 16:02:48 +02:00
}
}
function addLink ( l ) {
links . push ( l ) ;
}
function addConfig ( c ) {
configNodes [ c . id ] = c ;
}
2013-12-08 22:55:34 +01:00
2013-09-05 16:02:48 +02:00
function getNode ( id ) {
if ( id in configNodes ) {
return configNodes [ id ] ;
} else {
for ( var n in nodes ) {
if ( nodes [ n ] . id == id ) {
return nodes [ n ] ;
}
}
}
return null ;
}
2013-12-08 22:55:34 +01:00
2013-09-05 16:02:48 +02:00
function removeNode ( id ) {
var removedLinks = [ ] ;
if ( id in configNodes ) {
delete configNodes [ id ] ;
2014-02-27 17:47:28 +01:00
RED . sidebar . config . refresh ( ) ;
2013-09-05 16:02:48 +02:00
} else {
var node = getNode ( id ) ;
if ( node ) {
nodes . splice ( nodes . indexOf ( node ) , 1 ) ;
removedLinks = links . filter ( function ( l ) { return ( l . source === node ) || ( l . target === node ) ; } ) ;
removedLinks . map ( function ( l ) { links . splice ( links . indexOf ( l ) , 1 ) ; } ) ;
}
2014-01-25 23:31:43 +01:00
var updatedConfigNode = false ;
for ( var d in node . _def . defaults ) {
var property = node . _def . defaults [ d ] ;
if ( property . type ) {
var type = getType ( property . type )
if ( type && type . category == "config" ) {
var configNode = configNodes [ node [ d ] ] ;
if ( configNode ) {
updatedConfigNode = true ;
var users = configNode . users ;
users . splice ( users . indexOf ( node ) , 1 ) ;
}
}
}
}
if ( updatedConfigNode ) {
2014-02-27 17:47:28 +01:00
RED . sidebar . config . refresh ( ) ;
2014-01-25 23:31:43 +01:00
}
2013-09-05 16:02:48 +02:00
}
return removedLinks ;
}
2013-12-08 22:55:34 +01:00
2013-09-05 16:02:48 +02:00
function removeLink ( l ) {
var index = links . indexOf ( l ) ;
if ( index != - 1 ) {
links . splice ( index , 1 ) ;
}
}
2013-12-08 22:55:34 +01:00
2013-09-05 16:02:48 +02:00
function refreshValidation ( ) {
for ( var n in nodes ) {
RED . editor . validateNode ( nodes [ n ] ) ;
}
}
2013-12-08 22:55:34 +01:00
2013-10-25 22:34:00 +02:00
function addWorkspace ( ws ) {
workspaces [ ws . id ] = ws ;
}
2013-10-26 23:29:24 +02:00
function getWorkspace ( id ) {
return workspaces [ id ] ;
}
function removeWorkspace ( id ) {
delete workspaces [ id ] ;
var removedNodes = [ ] ;
var removedLinks = [ ] ;
for ( var n in nodes ) {
var node = nodes [ n ] ;
if ( node . z == id ) {
removedNodes . push ( node ) ;
}
}
for ( var n in removedNodes ) {
var rmlinks = removeNode ( removedNodes [ n ] . id ) ;
removedLinks = removedLinks . concat ( rmlinks ) ;
}
return { nodes : removedNodes , links : removedLinks } ;
}
2013-12-11 23:22:33 +01:00
2013-09-05 16:02:48 +02:00
function getAllFlowNodes ( node ) {
var visited = { } ;
visited [ node . id ] = true ;
var nns = [ node ] ;
var stack = [ node ] ;
while ( stack . length != 0 ) {
var n = stack . shift ( ) ;
var childLinks = links . filter ( function ( d ) { return ( d . source === n ) || ( d . target === n ) ; } ) ;
for ( var i in childLinks ) {
var child = ( childLinks [ i ] . source === n ) ? childLinks [ i ] . target : childLinks [ i ] . source ;
if ( ! visited [ child . id ] ) {
visited [ child . id ] = true ;
nns . push ( child ) ;
stack . push ( child ) ;
}
}
}
return nns ;
}
2013-12-08 22:55:34 +01:00
2013-09-05 16:02:48 +02:00
/ * *
2013-12-08 22:55:34 +01:00
* Converts a node to an exportable JSON Object
2013-09-05 16:02:48 +02:00
* * /
function convertNode ( n ) {
var node = { } ;
node . id = n . id ;
node . type = n . type ;
for ( var d in n . _def . defaults ) {
node [ d ] = n [ d ] ;
}
if ( n . _def . category != "config" ) {
node . x = n . x ;
node . y = n . y ;
2013-10-21 00:11:38 +02:00
node . z = n . z ;
2013-09-05 16:02:48 +02:00
node . wires = [ ] ;
for ( var i = 0 ; i < n . outputs ; i ++ ) {
node . wires . push ( [ ] ) ;
}
var wires = links . filter ( function ( d ) { return d . source === n ; } ) ;
for ( var i in wires ) {
var w = wires [ i ] ;
node . wires [ w . sourcePort ] . push ( w . target . id ) ;
}
}
return node ;
}
2013-12-08 22:55:34 +01:00
2013-09-05 16:02:48 +02:00
/ * *
* Converts the current node selection to an exportable JSON Object
* * /
function createExportableNodeSet ( set ) {
var nns = [ ] ;
var exportedConfigNodes = { } ;
for ( var n in set ) {
var node = set [ n ] . n ;
var convertedNode = RED . nodes . convertNode ( node ) ;
for ( var d in node . _def . defaults ) {
if ( node . _def . defaults [ d ] . type && node [ d ] in configNodes ) {
var confNode = configNodes [ node [ d ] ] ;
var exportable = getType ( node . _def . defaults [ d ] . type ) . exportable ;
if ( ( exportable == null || exportable ) ) {
if ( ! ( node [ d ] in exportedConfigNodes ) ) {
exportedConfigNodes [ node [ d ] ] = true ;
nns . unshift ( RED . nodes . convertNode ( confNode ) ) ;
}
} else {
convertedNode [ d ] = "" ;
}
}
}
nns . push ( convertedNode ) ;
}
return nns ;
}
2013-12-08 22:55:34 +01:00
2013-09-05 16:02:48 +02:00
//TODO: rename this (createCompleteNodeSet)
function createCompleteNodeSet ( ) {
var nns = [ ] ;
2013-10-25 22:34:00 +02:00
for ( var i in workspaces ) {
nns . push ( workspaces [ i ] ) ;
}
2013-09-05 16:02:48 +02:00
for ( var i in configNodes ) {
nns . push ( convertNode ( configNodes [ i ] ) ) ;
}
for ( var i in nodes ) {
var node = nodes [ i ] ;
nns . push ( convertNode ( node ) ) ;
}
return nns ;
}
2013-12-08 22:55:34 +01:00
2013-09-05 16:02:48 +02:00
function importNodes ( newNodesObj , createNewIds ) {
try {
var newNodes ;
if ( typeof newNodesObj === "string" ) {
if ( newNodesObj == "" ) {
return ;
}
newNodes = JSON . parse ( newNodesObj ) ;
} else {
newNodes = newNodesObj ;
}
if ( ! $ . isArray ( newNodes ) ) {
newNodes = [ newNodes ] ;
}
2013-12-12 16:51:15 +01:00
var unknownTypes = [ ] ;
2014-04-28 22:40:32 +02:00
for ( var i = 0 ; i < newNodes . length ; i ++ ) {
2013-09-05 16:02:48 +02:00
var n = newNodes [ i ] ;
2013-10-30 22:45:45 +01:00
// TODO: remove workspace in next release+1
if ( n . type != "workspace" && n . type != "tab" && ! getType ( n . type ) ) {
2013-09-05 16:02:48 +02:00
// TODO: get this UI thing out of here! (see below as well)
2013-12-12 16:51:15 +01:00
n . name = n . type ;
n . type = "unknown" ;
if ( unknownTypes . indexOf ( n . name ) == - 1 ) {
unknownTypes . push ( n . name ) ;
}
2014-04-28 22:40:32 +02:00
if ( n . x == null && n . y == null ) {
// config node - remove it
newNodes . splice ( i , 1 ) ;
i -- ;
}
2013-12-12 16:51:15 +01:00
}
2013-09-05 16:02:48 +02:00
}
2013-12-12 16:51:15 +01:00
if ( unknownTypes . length > 0 ) {
var typeList = "<ul><li>" + unknownTypes . join ( "</li><li>" ) + "</li></ul>" ;
var type = "type" + ( unknownTypes . length > 1 ? "s" : "" ) ;
RED . notify ( "<strong>Imported unrecognised " + type + ":</strong>" + typeList , "error" , false , 10000 ) ;
//"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");
}
2013-10-25 22:34:00 +02:00
for ( var i in newNodes ) {
var n = newNodes [ i ] ;
2013-10-30 22:45:45 +01:00
// TODO: remove workspace in next release+1
if ( n . type === "workspace" || n . type === "tab" ) {
if ( n . type === "workspace" ) {
n . type = "tab" ;
}
2013-10-25 22:34:00 +02:00
if ( defaultWorkspace == null ) {
defaultWorkspace = n ;
}
addWorkspace ( n ) ;
RED . view . addWorkspace ( n ) ;
}
}
if ( defaultWorkspace == null ) {
2013-10-30 22:45:45 +01:00
defaultWorkspace = { type : "tab" , id : getID ( ) , label : "Sheet 1" } ;
2013-10-25 22:34:00 +02:00
addWorkspace ( defaultWorkspace ) ;
RED . view . addWorkspace ( defaultWorkspace ) ;
}
2013-12-08 22:55:34 +01:00
2013-09-05 16:02:48 +02:00
var node _map = { } ;
var new _nodes = [ ] ;
var new _links = [ ] ;
2013-12-08 22:55:34 +01:00
2013-09-05 16:02:48 +02:00
for ( var i in newNodes ) {
var n = newNodes [ i ] ;
2013-10-30 22:45:45 +01:00
// TODO: remove workspace in next release+1
if ( n . type !== "workspace" && n . type !== "tab" ) {
2013-10-25 22:34:00 +02:00
var def = getType ( n . type ) ;
if ( def && def . category == "config" ) {
if ( ! RED . nodes . node ( n . id ) ) {
var configNode = { id : n . id , type : n . type , users : [ ] } ;
for ( var d in def . defaults ) {
configNode [ d ] = n [ d ] ;
}
configNode . label = def . label ;
configNode . _def = def ;
RED . nodes . add ( configNode ) ;
2013-09-05 16:02:48 +02:00
}
} else {
2013-11-16 00:40:36 +01:00
var node = { x : n . x , y : n . y , z : n . z , type : 0 , wires : n . wires , changed : false } ;
2013-10-25 22:34:00 +02:00
if ( createNewIds ) {
node . z = RED . view . getWorkspace ( ) ;
node . id = getID ( ) ;
} else {
node . id = n . id ;
if ( node . z == null || ! workspaces [ node . z ] ) {
node . z = RED . view . getWorkspace ( ) ;
}
2013-09-05 16:02:48 +02:00
}
2013-10-25 22:34:00 +02:00
node . type = n . type ;
node . _def = def ;
if ( ! node . _def ) {
node . _def = {
color : "#fee" ,
defaults : { } ,
label : "unknown: " + n . type ,
labelStyle : "node_label_italic" ,
outputs : n . outputs || n . wires . length
}
}
node . outputs = n . outputs || node . _def . outputs ;
2013-12-08 22:55:34 +01:00
2013-10-25 22:34:00 +02:00
for ( var d in node . _def . defaults ) {
node [ d ] = n [ d ] ;
2013-09-05 16:02:48 +02:00
}
2013-12-08 22:55:34 +01:00
2013-10-25 22:34:00 +02:00
addNode ( node ) ;
RED . editor . validateNode ( node ) ;
node _map [ n . id ] = node ;
new _nodes . push ( node ) ;
2013-09-05 16:02:48 +02:00
}
}
}
for ( var i in new _nodes ) {
var n = new _nodes [ i ] ;
for ( var w1 in n . wires ) {
var wires = ( n . wires [ w1 ] instanceof Array ) ? n . wires [ w1 ] : [ n . wires [ w1 ] ] ;
for ( var w2 in wires ) {
if ( wires [ w2 ] in node _map ) {
var link = { source : n , sourcePort : w1 , target : node _map [ wires [ w2 ] ] } ;
addLink ( link ) ;
new _links . push ( link ) ;
}
}
}
delete n . wires ;
}
return [ new _nodes , new _links ] ;
} catch ( error ) {
//TODO: get this UI thing out of here! (see above as well)
RED . notify ( "<strong>Error</strong>: " + error , "error" ) ;
return null ;
}
}
2013-12-08 22:55:34 +01:00
2013-09-05 16:02:48 +02:00
return {
registerType : registerType ,
getType : getType ,
convertNode : convertNode ,
add : addNode ,
addLink : addLink ,
remove : removeNode ,
removeLink : removeLink ,
2013-10-25 22:34:00 +02:00
addWorkspace : addWorkspace ,
2013-10-26 23:29:24 +02:00
removeWorkspace : removeWorkspace ,
workspace : getWorkspace ,
2013-09-05 16:02:48 +02:00
eachNode : function ( cb ) {
for ( var n in nodes ) {
cb ( nodes [ n ] ) ;
}
} ,
eachLink : function ( cb ) {
for ( var l in links ) {
cb ( links [ l ] ) ;
}
} ,
eachConfig : function ( cb ) {
for ( var id in configNodes ) {
cb ( configNodes [ id ] ) ;
}
} ,
node : getNode ,
import : importNodes ,
refreshValidation : refreshValidation ,
getAllFlowNodes : getAllFlowNodes ,
createExportableNodeSet : createExportableNodeSet ,
createCompleteNodeSet : createCompleteNodeSet ,
2013-10-25 22:34:00 +02:00
id : getID ,
2013-09-05 16:02:48 +02:00
nodes : nodes , // TODO: exposed for d3 vis
links : links // TODO: exposed for d3 vis
} ;
} ( ) ;