ifandelse / machina.js

js ex machina - finite state machines in JavaScript
http://machina-js.org/
Other
1.93k stars 147 forks source link

Moving to parent's "neighbour" state from child state #106

Closed ghost closed 6 years ago

ghost commented 9 years ago

Please, mind the following code.

/* 3rd level */
var interimStateFSM = new machina.Fsm({

    namespace: "interimStateFSM",

    initialState: "moved2",

    states: {

        unselected2: {
            select: "selected2"
        },

        selected2: {
            unselect: "unselected2",
            dragged: "moved2"
        },

        moved2: {
            dragged: "moved2",
            select: "selected2"
        }
    }

});

/* 3rd level */
var nonCollabNormalStateInnerFSM = new machina.Fsm({

    namespace: "nonCollabNormalStateInnerFSM",

    initialState: "unselected",

    states: {

        unselected: {
            select: "selected"
        },

        selected: {
            unselect: "unselected",
            dragged: "moved"
        },

        moved: {
            dragged: "moved",
            select: "selected"
        }
    }
});

/* 3rd level */
var collabNormalStateInnerFSM = new machina.Fsm({

    namespace: "collabNormalStateInnerFSM",

    initialState: "unselected1",

    states: {

        unselected1: {
            select: "selected1"
        },

        selected1: {
            unselect: "unselected1",
            dragged: "interimState"
        }
    }

});

/* 2nd level */
var nonCollabNormalStateFSM = new machina.Fsm({

    namespace: "nonCollabNormalStateFSM",

    initialState: "normalState",

    states: {

        normalState: {
            _child: nonCollabNormalStateInnerFSM
        },

    }

});

/* 2nd level */
var collabStateInnerFSM = new machina.Fsm({

    namespace: "collabStateInnerFSM",

    initialState: "normalState1",

    states: {

        normalState1: {
            _child: collabNormalStateInnerFSM
        },

        interimState: {
            _child: interimStateFSM,
            accepted: "normalState1"
        }

    }

});

/* top-level */
var collaborationFSM = new machina.Fsm( {

    namespace: "collaborationFSM",

    initialState: "nonCollabMode",

    states: {
        nonCollabMode: {
            _child: nonCollabNormalStateFSM,
            activate: "collabMode"
        },
        collabMode: {
            _child: collabStateInnerFSM,
            deactivate: "nonCollabMode"
        }
    }

});

As you can see, it's an HSM. In 'collabNormalStateInnerFSM', in state 'selected1', there is the transition: ' dragged: "interimState" '. When I'm in: collabMode.normalState1.selected1 and I execute: collaborationFSM.handle("dragged"), I expect it to fail(apparently, as there is no 'interimState' in collabNormalStateInnerFSM. Also I get invalidState event), and the parent FSM to take over. The parent FSM is 'collabStateInnerFSM', which has 2 states: 'normalState1' and 'interimState'. So, I expect to do the transition to 'interimState'. But this this never happens.

Could you tell me if that is even possible? Or do I make a mistake?

I hope it's clear what I'm asking, Thanks!

EDIT: I tried using the transition: ' dragged: "collabMode.interimState" ', you know, as a hierarchy, but no change!

ifandelse commented 6 years ago

Even though the user that originally submitted this is no longer on GH, the answer is the dragged input needed to belong to the collabStateInnerFSM (2nd level), not the 3rd level down. When machina delegates a state to a child FSM, it doesn't preclude that state from having input handlers that are not handled by the child FSM. The working example based on his code:

const machina = require( "machina" );

var interimStateFSM = new machina.Fsm({
    namespace: "interimStateFSM",
    initialState: "moved2",
    states: {
        unselected2: {
            select: "selected2"
        },
        selected2: {
            unselect: "unselected2",
            dragged: "moved2"
        },
        moved2: {
            dragged: "moved2",
            select: "selected2"
        }
    }
});

/* 3rd level */
var nonCollabNormalStateInnerFSM = new machina.Fsm({
    namespace: "nonCollabNormalStateInnerFSM",
    initialState: "unselected",
    states: {
        unselected: {
            select: "selected"
        },
        selected: {
            unselect: "unselected",
            dragged: "moved"
        },
        moved: {
            dragged: "moved",
            select: "selected"
        }
    }
});

/* 3rd level */
var collabNormalStateInnerFSM = new machina.Fsm({
    namespace: "collabNormalStateInnerFSM",
    initialState: "unselected1",
    states: {
        unselected1: {
            select: "selected1"
        },
        selected1: {
            unselect: "unselected1"
        }
    }
});

/* 2nd level */
var nonCollabNormalStateFSM = new machina.Fsm({
    namespace: "nonCollabNormalStateFSM",
    initialState: "normalState",
    states: {
        normalState: {
            _child: nonCollabNormalStateInnerFSM
        },
    }
});

/* 2nd level */
var collabStateInnerFSM = new machina.Fsm({
    namespace: "collabStateInnerFSM",
    initialState: "normalState1",
    states: {
        normalState1: {
            _child: collabNormalStateInnerFSM,
            dragged: "interimState"
        },
        interimState: {
            _child: interimStateFSM,
            accepted: "normalState1"
        }
    }
});

/* top-level */
var collaborationFSM = new machina.Fsm({
    namespace: "collaborationFSM",
    initialState: "nonCollabMode",
    states: {
        nonCollabMode: {
            _child: nonCollabNormalStateFSM,
            activate: "collabMode"
        },
        collabMode: {
            _child: collabStateInnerFSM,
            deactivate: "nonCollabMode"
        }
    }
});

collaborationFSM.handle("activate");
collaborationFSM.handle("select");
collaborationFSM.handle("dragged");
console.log(collaborationFSM.compositeState());