forked from flamejs/flame.js
-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathstatechart.js
129 lines (118 loc) · 4.35 KB
/
statechart.js
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
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
/*jshint loopfunc: true */
Flame.State = Ember.Object.extend({
gotoState: function(stateName) {
this.get('owner').gotoState(stateName);
},
// Touch events sometimes hide useful data in an originalEvent sub-hash.
normalizeTouchEvents: function(event) {
if (!event.touches) {
event.touches = event.originalEvent.touches;
}
if (!event.pageX) {
event.pageX = event.originalEvent.pageX;
}
if (!event.pageY) {
event.pageY = event.originalEvent.pageY;
}
if (!event.screenX) {
event.screenX = event.originalEvent.screenX;
}
if (!event.screenY) {
event.screenY = event.originalEvent.screenY;
}
if (!event.clientX) {
event.clientX = event.originalEvent.clientX;
}
if (!event.clientY) {
event.clientY = event.originalEvent.clientY;
}
return event;
},
$: function(args) {
args = Array.prototype.slice.call(arguments);
var owner = this.get('owner');
return owner.$.apply(owner, args);
}
});
Flame.State.reopenClass({
gotoHandler: function(stateName, returnValue) {
return function() {
this.gotoState(stateName);
return returnValue === undefined ? true : returnValue;
};
}
});
Flame.Statechart = {
initialState: null,
currentState: undefined,
_currentStateName: undefined,
init: function() {
this._super();
// Look for defined states and initialize them
var key;
for (key in this) {
var state = this[key];
if (Flame.State.detect(state)) {
this.set(key, state.create({owner: this}));
this._setupProxyMethods(this[key]);
}
}
Ember.assert("No initial state defined for statechart!", !Ember.none(this.get('initialState')));
this.gotoState(this.get('initialState'));
},
/**
Sets up proxy methods so that methods called on the owner of the statechart
will be invoked on the current state if they are not present on the owner of
the statechart.
*/
_setupProxyMethods: function(state) {
for (var property in state) {
if (state.constructor.prototype.hasOwnProperty(property) && Ember.typeOf(state[property]) === "function" &&
!this[property] && property !== "enterState" && property !== "exitState") {
this[property] = function(methodName) {
return function(args) {
args = Array.prototype.slice.call(arguments);
args.unshift(methodName);
return this.invokeStateMethod.apply(this, args);
};
}(property);
}
}
},
gotoState: function(stateName) {
Ember.assert("Cannot go to an undefined or null state!", !Ember.none(stateName));
var currentState = this.get('currentState');
var newState = this.get(stateName);
//do nothing if we are already in the state to go to
if (currentState === newState) {
return;
}
if (!Ember.none(newState) && newState instanceof Flame.State) {
if (!Ember.none(currentState)) {
if (currentState.exitState) currentState.exitState();
}
this._currentStateName = stateName;
this.set('currentState', newState);
if (newState.enterState) newState.enterState();
} else {
throw new Error("%@ is not a state!".fmt(stateName));
}
},
/**
* Is this state chart currently in a state with the given name?
* @param stateName
* @returns {Boolean} is this statechart currently in a state with the given name?
*/
isCurrentlyIn: function(stateName) {
return this._currentStateName === stateName;
},
invokeStateMethod: function(methodName, args) {
args = Array.prototype.slice.call(arguments); args.shift();
var state = this.get('currentState');
Ember.assert("Cannot invoke state method without having a current state!", !Ember.none(state) && state instanceof Flame.State);
var method = state[methodName];
if (Ember.typeOf(method) === "function") {
return method.apply(state, args);
}
}
};