| 1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109 |
1×
1×
3×
3×
3×
1×
1×
3×
3×
1×
1×
18×
6×
22×
22×
22×
22×
22×
3×
3×
3×
22×
20×
20×
1×
1×
1×
18×
18×
16×
18×
3×
3×
9×
9×
9×
3×
3×
3×
3×
2×
2×
2×
2×
2×
2×
2×
2×
2×
1×
2×
1×
3×
3×
3×
3×
1× | /**
* Created by thinhth2 on 5/25/2017.
*/
export interface IStateEvent {
[name:string]: {from: any, to: any};
}
export const EventStatus = {
ASYNC: "async",
CANCEL: "cancel",
CONTINUE: "continue"
};
export interface IStateOption {
initial: any;
events: IStateEvent;
}
export class FiniteStateMachine {
events: any = {};
current: any = "";
previous: any = "";
constructor() {
}
public setInitState(initState) {
this.current = initState;
this.previous = initState;
}
public registerEvent(name, callback, self) {
this.events[name] = callback;
this.events[name + "_self"] = self;
}
private runEvent(prefix, name, args, done) {
if (this.events[`${prefix}${name}`] && this.events[`${prefix}${name}`].call) {
return this.events[`${prefix}${name}`].call(this.events[`${prefix}${name}_self`], ...args, done);
}
}
private processEvent(steps) {
let step = steps.shift();
let isAsycn = true;
let doneCalled = false;
let done = () => {
Eif (isAsycn) {
this.processEvent(steps);
doneCalled = true;
}
};
if (step) {
let status = step(done);
switch (status) {
case EventStatus.CANCEL:
isAsycn = false;
break;
case EventStatus.ASYNC:
break;
default:
isAsycn = false;
if (!doneCalled) {
this.processEvent(steps);
}
break;
}
}
}
public pushEvents(events: IStateEvent) {
for (let name in events) {
Eif (events.hasOwnProperty(name)) {
let event = events[name];
this.events[name] = (...args) => {
Eif (this.current === event.from || event.from.indexOf(this.current) >= 0) {
this.processEvent([
(done)=>{return this.runEvent("before", "any", args, done)},
(done)=>{return this.runEvent("before", name, args, done)},
(done)=>{return this.runEvent("leave", this.current, args, done)},
(done)=>{return this.runEvent("leave", "any", args, done)},
(done)=>{
this.previous = this.current;
this.current = event.to;
done();
},
(done)=>{return this.runEvent("enter", "any", args, done)},
(done)=>{return this.runEvent("enter", this.current, args, done)},
(done)=>{return this.runEvent("after", name, args, done)},
(done)=>{return this.runEvent("after", "any", args, done)},
]);
}
};
}
}
}
}
export class FiniteStateMachineStatic {
public config(options: IStateOption): FiniteStateMachine {
let fsm = new FiniteStateMachine();
fsm.setInitState(options.initial || "");
fsm.pushEvents(options.events || {});
return fsm;
}
} |