diff --git a/package.json b/package.json index b4a79e10..ddc53c3e 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "jssm", - "version": "0.9.1", + "version": "0.10.1", "description": "A Javascript state machine with a simple API. Well tested, and typed with Flowtype. MIT License.", "main": "dist/jssm.es5.browserified.js", "scripts": { diff --git a/src/js/jssm.js b/src/js/jssm.js index d97ee0fc..cf837048 100644 --- a/src/js/jssm.js +++ b/src/js/jssm.js @@ -23,6 +23,7 @@ class machine { _state : string; _states : Map; // todo whargarbl this really should't be string // remove mixed todo whargarbl _edges : Array; // remove mixed todo whargarbl + _edge_map : Map>; // remove mixed todo whargarbl _named_transitions : Map; // remove mixed todo whargarbl _actions : Map>; // remove mixed todo whargarbl @@ -31,6 +32,7 @@ class machine { this._state = initial_state; this._states = new Map(); this._edges = []; + this._edge_map = new Map(); this._named_transitions = new Map(); this._actions = new Map(); @@ -65,6 +67,16 @@ class machine { else { this._named_transitions.set(tr.name, thisEdgeId); } } + var from_mapping:any = (this._edge_map.get(tr.from) : any); + if (from_mapping === undefined) { + this._edge_map.set(tr.from, new Map()); + from_mapping = (this._edge_map.get(tr.from) : any); + } + + var to_mapping:any = (from_mapping.get(tr.to) : any); + if (to_mapping) { throw new Error(`from -> to already exists ${tr.from} ${tr.to}`); } + else { from_mapping.set(tr.to, thisEdgeId); } + }); } @@ -108,6 +120,16 @@ class machine { } + edge_id(from:any, to:any) { + return this._edge_map.has(from)? (this._edge_map.get(from) : any).get(to) : undefined; + } + + edge(from:any, to:any) { + const id = this.edge_id(from, to); + return (id === undefined)? undefined : this._edges[id]; + } + + transitions_for(whichState : string) : mixed { // todo whargarbl return {entrances: this.entrances_for(whichState), exits: this.exits_for(whichState)}; } @@ -178,6 +200,27 @@ class machine { } + viz() { + const l_states = this.states(); + const node_of = (state) => `n${l_states.indexOf(state)}`; + + const nodes = l_states.map( (s:any) => `${node_of(s)} [label="${s}"];`).join(' '); + + const edges = this.states().map( (s:any) => + + this.exits_for(s).map( (ex:any) => { + const edge = this.edge(s, ex), + label = edge? (edge.name || undefined) : undefined; + return `${node_of(s)}->${node_of(ex)} [${label? `label="${(label:any)}"`:''} len=2];`; + }).join(' ') + + ).join(' '); + + return `digraph G {\n fontname="helvetica neue";\n style=filled;\n bgcolor=lightgrey;\n node [shape=box; style=filled; fillcolor=white; fontname="helvetica neue"];\n edge [len=2; fontname="helvetica neue"];\n\n ${nodes}\n\n ${edges}\n}`; + + } + + }