var N = { ID:{ Walk:0, Instance:0 }, Create(inMeta) { return { ID:{ Walk:0, Instance:N.ID.Instance++ }, Meta:inMeta||{}, Link:{} }; }, Connect(inNodeMajor, inNodeMinor, inKey, inUnique) { if(inUnique) // bail if the nodes are already connected { let check = N.Step(inNodeMajor, inKey, true); if(check) { if(check.indexOf(inNodeMinor) !== -1) { return; } } } N.Step(inNodeMajor, inKey, true, true).push(inNodeMinor); N.Step(inNodeMinor, inKey, false, true).push(inNodeMajor); }, Disconnect(inNodeMajor, inNodeMinor, inKey) { let remove = (inArray, inMatch) => inArray.findIndex( (inMember, inIndex, inArray) => (inMember === inMatch) ? inArray.splice(inIndex, 1) : false ); // if no specific child was passed if(inNodeMinor === null) { // get all the children let check = N.Step(inNodeMajor, inKey); if(!check){ return; } // go down to each child ... check.forEach( inNodeMinor => { let connections = inNodeMinor.Link[inKey]; remove( connections.Get, inNodeMajor); // ... and remove any reference to the parent // if after the remove operation, this child has no connections on inKey, scrub the key if(!connections.Set.length && !connections.Get.length) { delete inNodeMinor.Link[inKey]; } }); // we just wiped out all outgoing connections to the parent, if incoming connections are empty too we can purge the key there as well if(inNodeMajor.Link[inKey].Get.length == 0) { delete inNodeMajor.Link[inKey]; } return; } // if no specific parent was passed if(inNodeMajor === null) { // get all the parents let check = N.Step(inNodeMinor, inKey, false); if(!check){ return; } // go up to each parent ... check.forEach( inNodeMajor => { let connections = inNodeMajor.Link[inKey]; remove( connections.Set, inNodeMinor); // ... and remove any reference to the child // if after the remove operation, this parent has no connections on inKey, scrub the key if( !connections.Set.length && !connections.Get.length ) { delete inNodeMajor.Link[inKey]; } }); // we just wiped out all incoming connections to the child, if outgoing connections are empty too we can purge the key there as well if(inNodeMinor.Link[inKey].Set.length == 0) { delete inNodeMinor.Link[inKey]; } return; } // if a specific parent and child were passed if(inNodeMajor.Link[inKey].Set.length == 1) { delete inNodeMajor.Link[inKey]; } else { remove(inNodeMajor.Link[inKey].Set, inNodeMinor); } if(inNodeMinor.Link[inKey].Get.length == 1) { delete inNodeMinor.Link[inKey]; } else { remove(inNodeMinor.Link[inKey].Get, inNodeMajor); } }, Step(inNode, inKey, inForward, inForceCreate) { let connectionGroup = inNode.Link[inKey]; if(!connectionGroup) { if(inForceCreate === true) { inNode.Link[inKey] = connectionGroup = {Get:[], Set:[]}; } else { return false; } } return (inForward === undefined || inForward === true) ? connectionGroup.Set : connectionGroup.Get; }, Walk(inIterator, inNode, inKey, inForward, inTerminal) { let array = N.Step(inNode, inKey, inForward); if(!array.length && inTerminal) { return inTerminal(inNode); } for(let i=0; i