2013-09-05 16:02:48 +02:00
/ * *
2015-03-15 23:54:55 +01:00
* Copyright 2013 , 2015 IBM Corp .
2013-09-05 16:02:48 +02:00
*
* 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 .
* * /
2014-08-08 01:01:35 +02:00
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 = { } ;
2014-02-25 00:35:11 +01:00
var subflows = { } ;
2014-08-28 01:35:07 +02:00
2015-03-15 22:54:36 +01:00
var dirty = false ;
function setDirty ( d ) {
dirty = d ;
eventHandler . emit ( "change" , { dirty : dirty } ) ;
}
2014-08-28 01:35:07 +02:00
var registry = ( function ( ) {
var nodeList = [ ] ;
var nodeSets = { } ;
var typeToId = { } ;
var nodeDefinitions = { } ;
var exports = {
getNodeList : function ( ) {
return nodeList ;
} ,
setNodeList : function ( list ) {
nodeList = [ ] ;
for ( var i = 0 ; i < list . length ; i ++ ) {
var ns = list [ i ] ;
exports . addNodeSet ( ns ) ;
}
} ,
addNodeSet : function ( ns ) {
ns . added = false ;
nodeSets [ ns . id ] = ns ;
for ( var j = 0 ; j < ns . types . length ; j ++ ) {
typeToId [ ns . types [ j ] ] = ns . id ;
}
nodeList . push ( ns ) ;
} ,
removeNodeSet : function ( id ) {
var ns = nodeSets [ id ] ;
for ( var j = 0 ; j < ns . types . length ; j ++ ) {
if ( ns . added ) {
// TODO: too tightly coupled into palette UI
RED . palette . remove ( ns . types [ j ] ) ;
var def = nodeDefinitions [ ns . types [ j ] ] ;
if ( def . onpaletteremove && typeof def . onpaletteremove === "function" ) {
def . onpaletteremove . call ( def ) ;
}
}
delete typeToId [ ns . types [ j ] ] ;
}
delete nodeSets [ id ] ;
for ( var i = 0 ; i < nodeList . length ; i ++ ) {
if ( nodeList [ i ] . id == id ) {
nodeList . splice ( i , 1 ) ;
break ;
}
}
return ns ;
} ,
getNodeSet : function ( id ) {
return nodeSets [ id ] ;
} ,
enableNodeSet : function ( id ) {
var ns = nodeSets [ id ] ;
ns . enabled = true ;
for ( var j = 0 ; j < ns . types . length ; j ++ ) {
// TODO: too tightly coupled into palette UI
RED . palette . show ( ns . types [ j ] ) ;
var def = nodeDefinitions [ ns . types [ j ] ] ;
if ( def . onpaletteadd && typeof def . onpaletteadd === "function" ) {
def . onpaletteadd . call ( def ) ;
}
}
} ,
disableNodeSet : function ( id ) {
var ns = nodeSets [ id ] ;
ns . enabled = false ;
for ( var j = 0 ; j < ns . types . length ; j ++ ) {
// TODO: too tightly coupled into palette UI
RED . palette . hide ( ns . types [ j ] ) ;
var def = nodeDefinitions [ ns . types [ j ] ] ;
if ( def . onpaletteremove && typeof def . onpaletteremove === "function" ) {
def . onpaletteremove . call ( def ) ;
}
}
} ,
registerNodeType : function ( nt , def ) {
nodeDefinitions [ nt ] = def ;
2014-02-25 00:35:11 +01:00
if ( def . category != "subflows" ) {
nodeSets [ typeToId [ nt ] ] . added = true ;
// TODO: too tightly coupled into palette UI
}
2014-08-28 01:35:07 +02:00
RED . palette . add ( nt , def ) ;
if ( def . onpaletteadd && typeof def . onpaletteadd === "function" ) {
def . onpaletteadd . call ( def ) ;
}
} ,
2014-02-25 00:35:11 +01:00
removeNodeType : function ( nt ) {
if ( nt . substring ( 0 , 8 ) != "subflow:" ) {
throw new Error ( "this api is subflow only. called with:" , nt ) ;
}
delete nodeDefinitions [ nt ] ;
RED . palette . remove ( nt ) ;
} ,
2014-08-28 01:35:07 +02:00
getNodeType : function ( nt ) {
return nodeDefinitions [ nt ] ;
}
2014-09-24 10:57:45 +02:00
} ;
2014-08-28 01:35:07 +02:00
return exports ;
} ) ( ) ;
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 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 ;
2014-01-25 23:31:43 +01:00
var updatedConfigNode = false ;
for ( var d in n . _def . defaults ) {
2014-08-08 01:01:35 +02:00
if ( n . _def . defaults . hasOwnProperty ( d ) ) {
var property = n . _def . defaults [ d ] ;
if ( property . type ) {
2014-09-24 10:57:45 +02:00
var type = registry . getNodeType ( property . type ) ;
2014-08-08 01:01:35 +02:00
if ( type && type . category == "config" ) {
var configNode = configNodes [ n [ d ] ] ;
if ( configNode ) {
updatedConfigNode = true ;
configNode . users . push ( n ) ;
}
2014-01-25 23:31:43 +01:00
}
}
}
}
if ( updatedConfigNode ) {
2014-02-26 23:58:44 +01:00
RED . sidebar . config . refresh ( ) ;
2014-01-25 23:31:43 +01:00
}
2014-02-25 00:35:11 +01:00
if ( n . _def . category == "subflows" && typeof n . i === "undefined" ) {
var nextId = 0 ;
RED . nodes . eachNode ( function ( node ) {
nextId = Math . max ( nextId , node . i || 0 ) ;
} ) ;
n . i = nextId + 1 ;
}
nodes . push ( n ) ;
2013-09-05 16:02:48 +02:00
}
2015-03-04 14:18:59 +01:00
if ( n . _def . onadd ) {
n . _def . onadd . call ( n ) ;
}
2013-09-05 16:02:48 +02:00
}
function addLink ( l ) {
links . push ( l ) ;
}
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 = [ ] ;
2015-03-04 14:18:59 +01:00
var node ;
2013-09-05 16:02:48 +02:00
if ( id in configNodes ) {
2015-03-04 14:18:59 +01:00
node = configNodes [ id ] ;
2013-09-05 16:02:48 +02:00
delete configNodes [ id ] ;
2014-02-27 17:47:28 +01:00
RED . sidebar . config . refresh ( ) ;
2013-09-05 16:02:48 +02:00
} else {
2015-03-04 14:18:59 +01:00
node = getNode ( id ) ;
2013-09-05 16:02:48 +02:00
if ( node ) {
nodes . splice ( nodes . indexOf ( node ) , 1 ) ;
removedLinks = links . filter ( function ( l ) { return ( l . source === node ) || ( l . target === node ) ; } ) ;
2014-02-25 00:35:11 +01:00
removedLinks . forEach ( function ( l ) { links . splice ( links . indexOf ( l ) , 1 ) ; } ) ;
var updatedConfigNode = false ;
for ( var d in node . _def . defaults ) {
if ( node . _def . defaults . hasOwnProperty ( d ) ) {
var property = node . _def . defaults [ d ] ;
if ( property . type ) {
var type = registry . getNodeType ( 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 ) ;
}
2014-08-08 01:01:35 +02:00
}
2014-01-25 23:31:43 +01:00
}
}
}
2014-02-25 00:35:11 +01:00
if ( updatedConfigNode ) {
RED . sidebar . config . refresh ( ) ;
}
2014-01-25 23:31:43 +01:00
}
2013-09-05 16:02:48 +02:00
}
2015-03-04 14:18:59 +01:00
if ( node . _def . onremove ) {
node . _def . onremove . call ( n ) ;
}
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-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 = [ ] ;
2014-08-08 01:01:35 +02:00
var n ;
for ( n = 0 ; n < nodes . length ; n ++ ) {
2013-10-26 23:29:24 +02:00
var node = nodes [ n ] ;
if ( node . z == id ) {
removedNodes . push ( node ) ;
}
}
2014-08-08 01:01:35 +02:00
for ( n = 0 ; n < removedNodes . length ; n ++ ) {
2013-10-26 23:29:24 +02:00
var rmlinks = removeNode ( removedNodes [ n ] . id ) ;
removedLinks = removedLinks . concat ( rmlinks ) ;
}
return { nodes : removedNodes , links : removedLinks } ;
}
2013-12-11 23:22:33 +01:00
2015-03-28 23:12:18 +01:00
function addSubflow ( sf , createNewIds ) {
if ( createNewIds ) {
var subflowNames = Object . keys ( subflows ) . map ( function ( sfid ) {
return subflows [ sfid ] . name ;
} ) ;
subflowNames . sort ( ) ;
var copyNumber = 1 ;
var subflowName = sf . name ;
subflowNames . forEach ( function ( name ) {
if ( subflowName == name ) {
copyNumber ++ ;
subflowName = sf . name + " (" + copyNumber + ")" ;
}
} ) ;
sf . name = subflowName ;
}
2014-02-25 00:35:11 +01:00
subflows [ sf . id ] = sf ;
RED . nodes . registerType ( "subflow:" + sf . id , {
defaults : { name : { value : "" } } ,
icon : "subflow.png" ,
category : "subflows" ,
inputs : sf . in . length ,
outputs : sf . out . length ,
color : "#da9" ,
label : function ( ) { return this . name || RED . nodes . subflow ( sf . id ) . name } ,
labelStyle : function ( ) { return this . name ? "node_label_italic" : "" ; } ,
paletteLabel : function ( ) { return RED . nodes . subflow ( sf . id ) . name }
} ) ;
}
function getSubflow ( id ) {
return subflows [ id ] ;
}
function removeSubflow ( sf ) {
delete subflows [ sf . id ] ;
registry . removeNodeType ( "subflow:" + sf . id ) ;
}
function subflowContains ( sfid , nodeid ) {
for ( var i = 0 ; i < nodes . length ; i ++ ) {
var node = nodes [ i ] ;
if ( node . z === sfid ) {
var m = /^subflow:(.+)$/ . exec ( node . type ) ;
if ( m ) {
if ( m [ 1 ] === nodeid ) {
return true ;
} else {
var result = subflowContains ( m [ 1 ] , nodeid ) ;
if ( result ) {
return true ;
}
}
}
}
}
return false ;
}
2013-09-05 16:02:48 +02:00
function getAllFlowNodes ( node ) {
var visited = { } ;
visited [ node . id ] = true ;
var nns = [ node ] ;
var stack = [ node ] ;
2014-08-08 01:01:35 +02:00
while ( stack . length !== 0 ) {
2013-09-05 16:02:48 +02:00
var n = stack . shift ( ) ;
var childLinks = links . filter ( function ( d ) { return ( d . source === n ) || ( d . target === n ) ; } ) ;
2014-08-08 01:01:35 +02:00
for ( var i = 0 ; i < childLinks . length ; i ++ ) {
2013-09-05 16:02:48 +02:00
var child = ( childLinks [ i ] . source === n ) ? childLinks [ i ] . target : childLinks [ i ] . source ;
2014-02-25 00:35:11 +01:00
var id = child . id ;
if ( ! id ) {
id = child . direction + ":" + child . i ;
}
if ( ! visited [ id ] ) {
visited [ id ] = true ;
2013-09-05 16:02:48 +02:00
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
* * /
2014-06-24 12:13:19 +02:00
function convertNode ( n , exportCreds ) {
exportCreds = exportCreds || false ;
2013-09-05 16:02:48 +02:00
var node = { } ;
node . id = n . id ;
node . type = n . type ;
2014-10-30 22:41:42 +01:00
if ( node . type == "unknown" ) {
for ( var p in n . _orig ) {
if ( n . _orig . hasOwnProperty ( p ) ) {
node [ p ] = n . _orig [ p ] ;
}
2014-08-08 01:01:35 +02:00
}
2014-10-30 22:41:42 +01:00
} else {
for ( var d in n . _def . defaults ) {
if ( n . _def . defaults . hasOwnProperty ( d ) ) {
node [ d ] = n [ d ] ;
}
}
if ( exportCreds && n . credentials ) {
2014-11-23 23:25:09 +01:00
var credentialSet = { } ;
2014-10-30 22:41:42 +01:00
node . credentials = { } ;
for ( var cred in n . _def . credentials ) {
if ( n . _def . credentials . hasOwnProperty ( cred ) ) {
2014-11-23 23:25:09 +01:00
if ( n . _def . credentials [ cred ] . type == 'password' ) {
if ( n . credentials [ "has_" + cred ] != n . credentials . _ [ "has_" + cred ] ||
( n . credentials [ "has_" + cred ] && n . credentials [ cred ] ) ) {
credentialSet [ cred ] = n . credentials [ cred ] ;
}
} else if ( n . credentials [ cred ] != null && n . credentials [ cred ] != n . credentials . _ [ cred ] ) {
credentialSet [ cred ] = n . credentials [ cred ] ;
2014-10-30 22:41:42 +01:00
}
2014-07-19 01:16:21 +02:00
}
}
2014-11-23 23:25:09 +01:00
if ( Object . keys ( credentialSet ) . length > 0 ) {
node . credentials = credentialSet ;
}
2014-07-19 01:16:21 +02:00
}
2014-06-24 12:13:19 +02:00
}
2013-09-05 16:02:48 +02:00
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 ; } ) ;
2014-08-08 01:01:35 +02:00
for ( var j = 0 ; j < wires . length ; j ++ ) {
var w = wires [ j ] ;
2014-02-25 00:35:11 +01:00
if ( w . target . type != "subflow" ) {
node . wires [ w . sourcePort ] . push ( w . target . id ) ;
}
2013-09-05 16:02:48 +02:00
}
}
return node ;
}
2013-12-08 22:55:34 +01:00
2014-02-25 00:35:11 +01:00
function convertSubflow ( n ) {
var node = { } ;
node . id = n . id ;
node . type = n . type ;
node . name = n . name ;
node . in = [ ] ;
node . out = [ ] ;
n . in . forEach ( function ( p ) {
var nIn = { x : p . x , y : p . y , wires : [ ] } ;
var wires = links . filter ( function ( d ) { return d . source === p } ) ;
for ( var i = 0 ; i < wires . length ; i ++ ) {
var w = wires [ i ] ;
2015-01-08 23:34:26 +01:00
if ( w . target . type != "subflow" ) {
2014-02-25 00:35:11 +01:00
nIn . wires . push ( { id : w . target . id } )
}
}
node . in . push ( nIn ) ;
} ) ;
n . out . forEach ( function ( p , c ) {
var nOut = { x : p . x , y : p . y , wires : [ ] } ;
var wires = links . filter ( function ( d ) { return d . target === p } ) ;
for ( i = 0 ; i < wires . length ; i ++ ) {
if ( wires [ i ] . source . type != "subflow" ) {
nOut . wires . push ( { id : wires [ i ] . source . id , port : wires [ i ] . sourcePort } )
} else {
nOut . wires . push ( { id : n . id , port : 0 } )
}
}
node . out . push ( nOut ) ;
} ) ;
return node ;
}
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 = { } ;
2014-02-25 00:35:11 +01:00
var exportedSubflows = { } ;
2014-08-08 01:01:35 +02:00
for ( var n = 0 ; n < set . length ; n ++ ) {
2015-03-13 00:38:37 +01:00
var node = set [ n ] ;
2014-02-25 00:35:11 +01:00
if ( node . type . substring ( 0 , 8 ) == "subflow:" ) {
var subflowId = node . type . substring ( 8 ) ;
if ( ! exportedSubflows [ subflowId ] ) {
exportedSubflows [ subflowId ] = true ;
var subflow = getSubflow ( subflowId ) ;
2015-03-13 00:38:37 +01:00
var subflowSet = [ subflow ] ;
2014-02-25 00:35:11 +01:00
RED . nodes . eachNode ( function ( n ) {
if ( n . z == subflowId ) {
2015-03-13 00:38:37 +01:00
subflowSet . push ( n ) ;
2014-02-25 00:35:11 +01:00
}
} ) ;
var exportableSubflow = createExportableNodeSet ( subflowSet ) ;
nns = exportableSubflow . concat ( nns ) ;
}
}
if ( node . type != "subflow" ) {
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 = registry . getNodeType ( 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 ] = "" ;
2013-09-05 16:02:48 +02:00
}
}
}
2014-02-25 00:35:11 +01:00
nns . push ( convertedNode ) ;
} else {
var convertedSubflow = convertSubflow ( node ) ;
nns . push ( convertedSubflow ) ;
2013-09-05 16:02:48 +02:00
}
}
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 = [ ] ;
2014-08-08 01:01:35 +02:00
var i ;
for ( i in workspaces ) {
if ( workspaces . hasOwnProperty ( i ) ) {
2014-02-25 00:35:11 +01:00
if ( workspaces [ i ] . type == "tab" ) {
nns . push ( workspaces [ i ] ) ;
}
}
}
for ( i in subflows ) {
if ( subflows . hasOwnProperty ( i ) ) {
nns . push ( convertSubflow ( subflows [ i ] ) ) ;
2014-08-08 01:01:35 +02:00
}
2013-10-25 22:34:00 +02:00
}
2014-08-08 01:01:35 +02:00
for ( i in configNodes ) {
if ( configNodes . hasOwnProperty ( i ) ) {
nns . push ( convertNode ( configNodes [ i ] , true ) ) ;
}
2013-09-05 16:02:48 +02:00
}
2014-08-08 01:01:35 +02:00
for ( i = 0 ; i < nodes . length ; i ++ ) {
2013-09-05 16:02:48 +02:00
var node = nodes [ i ] ;
2014-06-24 12:13:19 +02:00
nns . push ( convertNode ( node , true ) ) ;
2013-09-05 16:02:48 +02:00
}
return nns ;
}
2013-12-08 22:55:34 +01:00
2013-09-05 16:02:48 +02:00
function importNodes ( newNodesObj , createNewIds ) {
2015-03-14 23:53:31 +01:00
var i ;
var n ;
var newNodes ;
if ( typeof newNodesObj === "string" ) {
if ( newNodesObj === "" ) {
return ;
}
try {
2013-09-05 16:02:48 +02:00
newNodes = JSON . parse ( newNodesObj ) ;
2015-03-14 23:53:31 +01:00
} catch ( err ) {
var e = new Error ( "Invalid flow: " + err . message ) ;
e . code = "NODE_RED" ;
throw e ;
2013-09-05 16:02:48 +02:00
}
2015-03-14 23:53:31 +01:00
} else {
newNodes = newNodesObj ;
}
2013-09-05 16:02:48 +02:00
2015-03-14 23:53:31 +01:00
if ( ! $ . isArray ( newNodes ) ) {
newNodes = [ newNodes ] ;
}
var unknownTypes = [ ] ;
for ( i = 0 ; i < newNodes . length ; i ++ ) {
n = newNodes [ i ] ;
// TODO: remove workspace in next release+1
if ( n . type != "workspace" &&
n . type != "tab" &&
n . type != "subflow" &&
! registry . getNodeType ( n . type ) &&
2015-03-19 23:56:59 +01:00
n . type . substring ( 0 , 8 ) != "subflow:" &&
unknownTypes . indexOf ( n . type ) == - 1 ) {
2015-03-14 23:53:31 +01:00
unknownTypes . push ( n . type ) ;
2013-09-05 16:02:48 +02:00
}
2015-03-14 23:53:31 +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-12-12 16:51:15 +01:00
2015-03-14 23:53:31 +01:00
var activeWorkspace = RED . workspaces . active ( ) ;
var activeSubflow = getSubflow ( activeWorkspace ) ;
if ( activeSubflow ) {
2014-08-08 01:01:35 +02:00
for ( i = 0 ; i < newNodes . length ; i ++ ) {
2015-03-14 23:53:31 +01:00
var m = /^subflow:(.+)$/ . exec ( newNodes [ i ] . type ) ;
if ( m ) {
var subflowId = m [ 1 ] ;
var err ;
if ( subflowId === activeSubflow . id ) {
err = new Error ( "Cannot add subflow to itself" ) ;
2013-10-25 22:34:00 +02:00
}
2015-03-14 23:53:31 +01:00
if ( subflowContains ( m [ 1 ] , activeSubflow . id ) ) {
err = new Error ( "Cannot add subflow - circular reference detected" ) ;
2014-09-08 11:53:18 +02:00
}
2015-03-14 23:53:31 +01:00
if ( err ) {
// TODO: standardise error codes
err . code = "NODE_RED" ;
throw err ;
2014-02-25 00:35:11 +01:00
}
2015-03-14 23:53:31 +01:00
}
}
}
var new _workspaces = [ ] ;
var workspace _map = { } ;
var new _subflows = [ ] ;
var subflow _map = { } ;
var nid ;
var def ;
for ( i = 0 ; i < newNodes . length ; i ++ ) {
n = newNodes [ i ] ;
// TODO: remove workspace in next release+1
if ( n . type === "workspace" || n . type === "tab" ) {
if ( n . type === "workspace" ) {
n . type = "tab" ;
}
if ( defaultWorkspace == null ) {
defaultWorkspace = n ;
}
if ( createNewIds ) {
nid = getID ( ) ;
workspace _map [ n . id ] = nid ;
n . id = nid ;
}
addWorkspace ( n ) ;
RED . workspaces . add ( n ) ;
new _workspaces . push ( n ) ;
} else if ( n . type === "subflow" ) {
subflow _map [ n . id ] = n ;
if ( createNewIds ) {
nid = getID ( ) ;
n . id = nid ;
}
// TODO: handle createNewIds - map old to new subflow ids
n . in . forEach ( function ( input , i ) {
input . type = "subflow" ;
input . direction = "in" ;
input . z = n . id ;
input . i = i ;
input . id = getID ( ) ;
} ) ;
n . out . forEach ( function ( output , i ) {
output . type = "subflow" ;
output . direction = "out" ;
output . z = n . id ;
output . i = i ;
output . id = getID ( ) ;
} ) ;
new _subflows . push ( n ) ;
2015-03-28 23:12:18 +01:00
addSubflow ( n , createNewIds ) ;
2015-03-14 23:53:31 +01:00
} else {
def = registry . getNodeType ( 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 ) {
if ( def . defaults . hasOwnProperty ( d ) ) {
configNode [ d ] = n [ d ] ;
2015-02-25 00:02:45 +01:00
}
}
2015-03-14 23:53:31 +01:00
configNode . label = def . label ;
configNode . _def = def ;
RED . nodes . add ( configNode ) ;
2015-02-25 00:02:45 +01:00
}
2013-10-25 22:34:00 +02:00
}
}
2015-03-14 23:53:31 +01:00
}
if ( defaultWorkspace == null ) {
defaultWorkspace = { type : "tab" , id : getID ( ) , label : "Sheet 1" } ;
addWorkspace ( defaultWorkspace ) ;
RED . workspaces . add ( defaultWorkspace ) ;
new _workspaces . push ( defaultWorkspace ) ;
activeWorkspace = RED . workspaces . active ( ) ;
}
2013-12-08 22:55:34 +01:00
2015-03-14 23:53:31 +01:00
var node _map = { } ;
var new _nodes = [ ] ;
var new _links = [ ] ;
2013-12-08 22:55:34 +01:00
2015-03-14 23:53:31 +01:00
for ( i = 0 ; i < newNodes . length ; i ++ ) {
n = newNodes [ i ] ;
// TODO: remove workspace in next release+1
if ( n . type !== "workspace" && n . type !== "tab" && n . type !== "subflow" ) {
def = registry . getNodeType ( n . type ) ;
if ( ! def || def . category != "config" ) {
var node = { x : n . x , y : n . y , z : n . z , type : 0 , wires : n . wires , changed : false } ;
if ( createNewIds ) {
if ( subflow _map [ node . z ] ) {
node . z = subflow _map [ node . z ] . id ;
2013-10-25 22:34:00 +02:00
} else {
2015-03-14 23:53:31 +01:00
node . z = workspace _map [ node . z ] ;
if ( ! workspaces [ node . z ] ) {
2014-02-25 00:35:11 +01:00
node . z = activeWorkspace ;
2013-10-25 22:34:00 +02:00
}
2013-09-05 16:02:48 +02:00
}
2015-03-14 23:53:31 +01:00
node . id = getID ( ) ;
} else {
node . id = n . id ;
if ( node . z == null || ( ! workspaces [ node . z ] && ! subflow _map [ node . z ] ) ) {
node . z = activeWorkspace ;
}
}
node . type = n . type ;
node . _def = def ;
if ( n . type . substring ( 0 , 7 ) === "subflow" ) {
var parentId = n . type . split ( ":" ) [ 1 ] ;
var subflow = subflow _map [ parentId ] || getSubflow ( parentId ) ;
if ( createNewIds ) {
parentId = subflow . id ;
node . type = "subflow:" + parentId ;
node . _def = registry . getNodeType ( node . type ) ;
delete node . i ;
}
node . name = n . name ;
node . outputs = subflow . out . length ;
node . inputs = subflow . in . length ;
} else {
if ( ! node . _def ) {
if ( node . x && node . y ) {
node . _def = {
color : "#fee" ,
defaults : { } ,
label : "unknown: " + n . type ,
labelStyle : "node_label_italic" ,
outputs : n . outputs || n . wires . length
2014-10-30 22:41:42 +01:00
}
2015-03-14 23:53:31 +01:00
} else {
node . _def = {
category : "config"
} ;
node . users = [ ] ;
2014-02-25 00:35:11 +01:00
}
2015-03-14 23:53:31 +01:00
var orig = { } ;
for ( var p in n ) {
if ( n . hasOwnProperty ( p ) && p != "x" && p != "y" && p != "z" && p != "id" && p != "wires" ) {
orig [ p ] = n [ p ] ;
2014-02-25 00:35:11 +01:00
}
2014-08-08 01:01:35 +02:00
}
2015-03-14 23:53:31 +01:00
node . _orig = orig ;
node . name = n . type ;
node . type = "unknown" ;
2013-09-05 16:02:48 +02:00
}
2014-10-30 22:41:42 +01:00
if ( node . _def . category != "config" ) {
2015-03-14 23:53:31 +01:00
node . inputs = n . inputs || node . _def . inputs ;
node . outputs = n . outputs || node . _def . outputs ;
for ( var d2 in node . _def . defaults ) {
if ( node . _def . defaults . hasOwnProperty ( d2 ) ) {
node [ d2 ] = n [ d2 ] ;
}
}
2014-10-30 22:41:42 +01:00
}
2013-09-05 16:02:48 +02:00
}
2015-03-14 23:53:31 +01:00
addNode ( node ) ;
RED . editor . validateNode ( node ) ;
node _map [ n . id ] = node ;
if ( node . _def . category != "config" ) {
new _nodes . push ( node ) ;
2013-09-05 16:02:48 +02:00
}
}
}
2015-03-14 23:53:31 +01:00
}
for ( i = 0 ; i < new _nodes . length ; i ++ ) {
n = new _nodes [ i ] ;
for ( var w1 = 0 ; w1 < n . wires . length ; w1 ++ ) {
var wires = ( n . wires [ w1 ] instanceof Array ) ? n . wires [ w1 ] : [ n . wires [ w1 ] ] ;
for ( var w2 = 0 ; w2 < wires . length ; w2 ++ ) {
if ( wires [ w2 ] in node _map ) {
var link = { source : n , sourcePort : w1 , target : node _map [ wires [ w2 ] ] } ;
2014-02-25 00:35:11 +01:00
addLink ( link ) ;
new _links . push ( link ) ;
2015-03-14 23:53:31 +01:00
}
}
}
delete n . wires ;
}
for ( i = 0 ; i < new _subflows . length ; i ++ ) {
n = new _subflows [ i ] ;
n . in . forEach ( function ( input ) {
input . wires . forEach ( function ( wire ) {
var link = { source : input , sourcePort : 0 , target : node _map [ wire . id ] } ;
addLink ( link ) ;
new _links . push ( link ) ;
2014-02-25 00:35:11 +01:00
} ) ;
2015-03-14 23:53:31 +01:00
delete input . wires ;
} ) ;
n . out . forEach ( function ( output ) {
output . wires . forEach ( function ( wire ) {
var link ;
2015-03-19 23:56:59 +01:00
if ( subflow _map [ wire . id ] && subflow _map [ wire . id ] . id == n . id ) {
2015-03-14 23:53:31 +01:00
link = { source : n . in [ wire . port ] , sourcePort : wire . port , target : output } ;
} else {
2015-03-19 23:56:59 +01:00
link = { source : node _map [ wire . id ] || subflow _map [ wire . id ] , sourcePort : wire . port , target : output } ;
2015-03-14 23:53:31 +01:00
}
addLink ( link ) ;
new _links . push ( link ) ;
2014-02-25 00:35:11 +01:00
} ) ;
2015-03-14 23:53:31 +01:00
delete output . wires ;
} ) ;
2013-09-05 16:02:48 +02:00
}
2015-03-14 23:53:31 +01:00
return [ new _nodes , new _links , new _workspaces , new _subflows ] ;
2013-09-05 16:02:48 +02:00
}
2015-03-04 14:18:59 +01:00
2015-03-19 23:56:59 +01:00
// TODO: supports filter.z|type
2015-03-16 00:31:38 +01:00
function filterNodes ( filter ) {
var result = [ ] ;
for ( var n = 0 ; n < nodes . length ; n ++ ) {
var node = nodes [ n ] ;
2015-04-22 23:53:21 +02:00
if ( filter . hasOwnProperty ( "z" ) && node . z !== filter . z ) {
2015-03-16 00:31:38 +01:00
continue ;
}
2015-04-22 23:53:21 +02:00
if ( filter . hasOwnProperty ( "type" ) && node . type !== filter . type ) {
2015-03-19 23:56:59 +01:00
continue ;
}
2015-03-16 00:31:38 +01:00
result . push ( node ) ;
}
return result ;
}
function filterLinks ( filter ) {
var result = [ ] ;
for ( var n = 0 ; n < links . length ; n ++ ) {
var link = links [ n ] ;
if ( filter . source ) {
2015-04-22 23:53:21 +02:00
if ( filter . source . hasOwnProperty ( "id" ) && link . source . id !== filter . source . id ) {
2015-03-16 00:31:38 +01:00
continue ;
}
2015-04-22 23:53:21 +02:00
if ( filter . source . hasOwnProperty ( "z" ) && link . source . z !== filter . source . z ) {
2015-03-16 00:31:38 +01:00
continue ;
}
}
if ( filter . target ) {
2015-04-22 23:53:21 +02:00
if ( filter . target . hasOwnProperty ( "id" ) && link . target . id !== filter . target . id ) {
2015-03-16 00:31:38 +01:00
continue ;
}
2015-04-22 23:53:21 +02:00
if ( filter . target . hasOwnProperty ( "z" ) && link . target . z !== filter . target . z ) {
2015-03-16 00:31:38 +01:00
continue ;
}
}
2015-04-22 23:53:21 +02:00
if ( filter . hasOwnProperty ( "sourcePort" ) && link . sourcePort !== filter . sourcePort ) {
2015-03-16 00:41:16 +01:00
continue ;
}
2015-03-16 00:31:38 +01:00
result . push ( link ) ;
}
return result ;
}
2015-03-15 22:54:36 +01:00
// TODO: DRY
var eventHandler = ( function ( ) {
var handlers = { } ;
return {
on : function ( evt , func ) {
handlers [ evt ] = handlers [ evt ] || [ ] ;
handlers [ evt ] . push ( func ) ;
} ,
emit : function ( evt , arg ) {
if ( handlers [ evt ] ) {
for ( var i = 0 ; i < handlers [ evt ] . length ; i ++ ) {
handlers [ evt ] [ i ] ( arg ) ;
}
}
}
}
} ) ( ) ;
2013-09-05 16:02:48 +02:00
return {
2015-03-15 22:54:36 +01:00
on : eventHandler . on ,
2014-08-28 01:35:07 +02:00
registry : registry ,
setNodeList : registry . setNodeList ,
getNodeSet : registry . getNodeSet ,
addNodeSet : registry . addNodeSet ,
removeNodeSet : registry . removeNodeSet ,
enableNodeSet : registry . enableNodeSet ,
disableNodeSet : registry . disableNodeSet ,
registerType : registry . registerNodeType ,
getType : registry . getNodeType ,
2013-09-05 16:02:48 +02:00
convertNode : convertNode ,
2014-02-25 00:35:11 +01:00
2013-09-05 16:02:48 +02:00
add : addNode ,
remove : removeNode ,
2014-02-25 00:35:11 +01:00
addLink : addLink ,
2013-09-05 16:02:48 +02:00
removeLink : removeLink ,
2014-02-25 00:35:11 +01:00
2013-10-25 22:34:00 +02:00
addWorkspace : addWorkspace ,
2013-10-26 23:29:24 +02:00
removeWorkspace : removeWorkspace ,
workspace : getWorkspace ,
2014-02-25 00:35:11 +01:00
addSubflow : addSubflow ,
removeSubflow : removeSubflow ,
subflow : getSubflow ,
subflowContains : subflowContains ,
2013-09-05 16:02:48 +02:00
eachNode : function ( cb ) {
2014-08-08 01:01:35 +02:00
for ( var n = 0 ; n < nodes . length ; n ++ ) {
2013-09-05 16:02:48 +02:00
cb ( nodes [ n ] ) ;
}
} ,
eachLink : function ( cb ) {
2014-08-08 01:01:35 +02:00
for ( var l = 0 ; l < links . length ; l ++ ) {
2013-09-05 16:02:48 +02:00
cb ( links [ l ] ) ;
}
} ,
eachConfig : function ( cb ) {
for ( var id in configNodes ) {
2014-08-08 01:01:35 +02:00
if ( configNodes . hasOwnProperty ( id ) ) {
cb ( configNodes [ id ] ) ;
}
2013-09-05 16:02:48 +02:00
}
} ,
2014-02-25 00:35:11 +01:00
eachSubflow : function ( cb ) {
for ( var id in subflows ) {
if ( subflows . hasOwnProperty ( id ) ) {
cb ( subflows [ id ] ) ;
}
}
} ,
2015-03-16 00:31:38 +01:00
2013-09-05 16:02:48 +02:00
node : getNode ,
2015-03-16 00:31:38 +01:00
filterNodes : filterNodes ,
filterLinks : filterLinks ,
2013-09-05 16:02:48 +02:00
import : importNodes ,
2015-03-19 23:56:59 +01:00
2013-09-05 16:02:48 +02:00
getAllFlowNodes : getAllFlowNodes ,
createExportableNodeSet : createExportableNodeSet ,
createCompleteNodeSet : createCompleteNodeSet ,
2013-10-25 22:34:00 +02:00
id : getID ,
2015-03-15 22:54:36 +01:00
dirty : function ( d ) {
if ( d == null ) {
return dirty ;
} else {
setDirty ( d ) ;
}
2015-03-16 00:31:38 +01:00
}
2013-09-05 16:02:48 +02:00
} ;
2014-08-08 01:01:35 +02:00
} ) ( ) ;