diff options
Diffstat (limited to 'node_modules/montage/core/state-chart.js')
-rwxr-xr-x | node_modules/montage/core/state-chart.js | 595 |
1 files changed, 595 insertions, 0 deletions
diff --git a/node_modules/montage/core/state-chart.js b/node_modules/montage/core/state-chart.js new file mode 100755 index 00000000..1e7265d8 --- /dev/null +++ b/node_modules/montage/core/state-chart.js | |||
@@ -0,0 +1,595 @@ | |||
1 | /* <copyright> | ||
2 | This file contains proprietary software owned by Motorola Mobility, Inc.<br/> | ||
3 | No rights, expressed or implied, whatsoever to this software are provided by Motorola Mobility, Inc. hereunder.<br/> | ||
4 | (c) Copyright 2011 Motorola Mobility, Inc. All Rights Reserved. | ||
5 | </copyright> */ | ||
6 | /** | ||
7 | @module montage/core/state-chart | ||
8 | @requires montage | ||
9 | */ | ||
10 | var Montage = require("montage").Montage; | ||
11 | |||
12 | /** | ||
13 | @class module:montage/core/state-chart.State | ||
14 | @extends module:montage/core/core.Montage | ||
15 | */ | ||
16 | var State = exports.State = Montage.create(Montage, /** @lends module:montage/core/state-chart.State# */{ | ||
17 | |||
18 | _stateChart: { | ||
19 | enumerable: false, | ||
20 | value: null | ||
21 | }, | ||
22 | /** | ||
23 | Initializes a State object with a set of options. | ||
24 | @function | ||
25 | @param {String} options The options for the new State. | ||
26 | @returns {State} | ||
27 | */ | ||
28 | init: { | ||
29 | value: function(options) { | ||
30 | |||
31 | this.substates = {}; | ||
32 | this.enterState = null; | ||
33 | this.exitState = null; | ||
34 | |||
35 | var keys = Object.keys(options), | ||
36 | i = 0, | ||
37 | iKey, | ||
38 | iOption; | ||
39 | |||
40 | for (; (iKey = keys[i]); i++) { | ||
41 | |||
42 | iOption = options[iKey]; | ||
43 | |||
44 | if (iOption.prototype === State.prototype) { | ||
45 | iOption.name = iKey; | ||
46 | iOption.parentState = this; | ||
47 | this.substates[iKey] = iOption; | ||
48 | } | ||
49 | |||
50 | // TODO is it worth guarding against any old property being added to this state? | ||
51 | if (typeof iOption === "string" && "initialSubstate" !== iKey) { | ||
52 | // this is a simple gotoState transition syntax | ||
53 | this[iKey] = this._encloseGotoState(iOption); | ||
54 | } else { | ||
55 | this[iKey] = iOption; | ||
56 | } | ||
57 | } | ||
58 | |||
59 | return this; | ||
60 | } | ||
61 | }, | ||
62 | /** | ||
63 | The name of the state. | ||
64 | @type {Property} | ||
65 | @default {String} null | ||
66 | */ | ||
67 | name: { | ||
68 | enumerable: false, | ||
69 | value: null | ||
70 | }, | ||
71 | _initialSubstate: { | ||
72 | enumerable: false, | ||
73 | value: null | ||
74 | }, | ||
75 | |||
76 | /** | ||
77 | The intitial substate. | ||
78 | @type {Function} | ||
79 | @default {String} null | ||
80 | */ | ||
81 | initialSubstate: { | ||
82 | get: function() { | ||
83 | if (typeof this._initialSubstate === "string") { | ||
84 | this._initialSubstate = this[this._initialSubstate]; | ||
85 | } | ||
86 | return this._initialSubstate; | ||
87 | }, | ||
88 | set: function(value) { | ||
89 | this._initialSubstate = value; | ||
90 | } | ||
91 | }, | ||
92 | /** | ||
93 | The set of substates. | ||
94 | @type {Property} | ||
95 | @default {String} null | ||
96 | */ | ||
97 | substates: { | ||
98 | enumerable: false, | ||
99 | value: null | ||
100 | }, | ||
101 | /** | ||
102 | The state's parent state. | ||
103 | @type {Property} | ||
104 | @default {String} null | ||
105 | */ | ||
106 | parentState: { | ||
107 | enumerable: false, | ||
108 | value: null | ||
109 | }, | ||
110 | |||
111 | _path: { | ||
112 | enumerable: false, | ||
113 | value: null | ||
114 | }, | ||
115 | |||
116 | /** | ||
117 | @type {Function} | ||
118 | @default {String} null | ||
119 | */ | ||
120 | path: { | ||
121 | // TODO add dependency on parentState, remember to clear cached value | ||
122 | enumerable: false, | ||
123 | get: function() { | ||
124 | if (!this._path) { | ||
125 | |||
126 | if (this.parentState && this.parentState.path) { | ||
127 | this._path = this.parentState.path + "." + this.name; | ||
128 | } else { | ||
129 | this._path = this.name; | ||
130 | } | ||
131 | } | ||
132 | return this._path; | ||
133 | } | ||
134 | }, | ||
135 | /** | ||
136 | @type {Property} | ||
137 | @default {String} null | ||
138 | */ | ||
139 | enterState: { | ||
140 | enumerable: false, | ||
141 | value: null | ||
142 | }, | ||
143 | /** | ||
144 | @type {Property} | ||
145 | @default {String} null | ||
146 | */ | ||
147 | exitState: { | ||
148 | enumerable: false, | ||
149 | value: null | ||
150 | }, | ||
151 | /** | ||
152 | @function | ||
153 | @param {String} otherState | ||
154 | @returns !!this.path.match(new RegExp(".?" + otherState + ".?")) | ||
155 | */ | ||
156 | isInState: { | ||
157 | enumerable: false, | ||
158 | value: function(otherState) { | ||
159 | |||
160 | if (typeof otherState !== "string") { | ||
161 | otherState = otherState.name; | ||
162 | } | ||
163 | |||
164 | return !!this.path.match(new RegExp(".?" + otherState + ".?")); | ||
165 | } | ||
166 | }, | ||
167 | /** | ||
168 | @private | ||
169 | */ | ||
170 | _encloseGotoState: { | ||
171 | value: function(state) { | ||
172 | return (function(stateChart, owner) { | ||
173 | // Not relying on original implementation to save an extra function call, despite duplicated code | ||
174 | return this._stateChart._gotoState(state, owner); | ||
175 | }); | ||
176 | } | ||
177 | }, | ||
178 | /** | ||
179 | @function | ||
180 | @param {Property} state | ||
181 | @param {Property} owner | ||
182 | @returns this._stateChart._gotoState(state, owner) | ||
183 | */ | ||
184 | gotoState: { | ||
185 | value: function(state, owner) { | ||
186 | return this._stateChart._gotoState(state, owner); | ||
187 | } | ||
188 | }, | ||
189 | /** | ||
190 | @private | ||
191 | */ | ||
192 | _performAction: { | ||
193 | enumerable: null, | ||
194 | value: function(actionName, stateChart, owner) { | ||
195 | if (this[actionName]) { | ||
196 | // TODO what should the context be inside the action function: state or stateChart? | ||
197 | // state makes sense but requires that authors know that when building a stateChart | ||
198 | // it's easy to get annoyed with having to remember which one you're in when building | ||
199 | // a relatively complex stateChart | ||
200 | // we could just .call(this._stateChart, args) etc... | ||
201 | this[actionName](stateChart, owner); | ||
202 | |||
203 | } else if (this.parentState) { | ||
204 | this.parentState._performAction(actionName, stateChart, owner); | ||
205 | } else { | ||
206 | throw "Action '" + actionName + "' not available"; | ||
207 | } | ||
208 | } | ||
209 | }, | ||
210 | /** | ||
211 | @function | ||
212 | @returns "[State " + this.path + " ]" | ||
213 | */ | ||
214 | toString: { | ||
215 | enumerable: false, | ||
216 | value: function() { | ||
217 | return "[State " + this.path + " ]"; | ||
218 | } | ||
219 | } | ||
220 | |||
221 | }); | ||
222 | /** | ||
223 | @class module:montage/core/state-chart.StateChart | ||
224 | */ | ||
225 | var StateChart = exports.StateChart = Montage.create(Montage,/** @lends module:montage/core/state-chart.StateChart# */ { | ||
226 | /** | ||
227 | @type {Property} | ||
228 | @default {String} null | ||
229 | */ | ||
230 | delegate: { | ||
231 | enumerable: false, | ||
232 | value: null | ||
233 | }, | ||
234 | |||
235 | // When the ownerStateProperty is set, we expect all operations of this stateChart to rely on the owner property. | ||
236 | // That is, many objects can rely on this stateChart to manage the flow of state but keep track of their own | ||
237 | // currentState (exposed as ownerStateProperty and recorded at "_" + ownerStateProperty); | ||
< |