middleman-wizard-template 1.0.2 → 1.0.3
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/lib/middleman-wizard-template/template/source/index.html.erb +1 -89
- data/lib/middleman-wizard-template/template/source/javascripts/_lib/ww/Matrix.js +449 -0
- data/lib/middleman-wizard-template/template/source/javascripts/_lib/ww/PxLoader/PxLoader.js +395 -0
- data/lib/middleman-wizard-template/template/source/javascripts/_lib/ww/PxLoader/PxLoaderImage.js +96 -0
- data/lib/middleman-wizard-template/template/source/javascripts/_lib/ww/PxLoader/PxLoaderSwiffy.js +68 -0
- data/lib/middleman-wizard-template/template/source/javascripts/_lib/ww/RouteRecognizer.js +506 -0
- data/lib/middleman-wizard-template/template/source/javascripts/_lib/ww/Slides.js +846 -0
- data/lib/middleman-wizard-template/template/source/javascripts/_lib/ww/Transform.js +312 -0
- data/lib/middleman-wizard-template/template/source/javascripts/_lib/{Tween.js → ww/Tween.js} +26 -11
- data/lib/middleman-wizard-template/template/source/javascripts/_lib/ww/base.js +8 -0
- data/lib/middleman-wizard-template/template/source/javascripts/_lib/ww/raf.js +131 -0
- data/lib/middleman-wizard-template/template/source/javascripts/_lib/ww/statemachine.js +1024 -0
- data/lib/middleman-wizard-template/template/source/javascripts/_lib/ww/useragent.js +1244 -0
- data/lib/middleman-wizard-template/template/source/javascripts/_lib/{util.js → ww/util.js} +48 -50
- data/lib/middleman-wizard-template/template/source/javascripts/_lib/ww/viewport.js +89 -0
- data/lib/middleman-wizard-template/template/source/javascripts/{app.js → site.js} +5 -5
- data/lib/middleman-wizard-template/template/source/layouts/layout.erb +85 -0
- data/lib/middleman-wizard-template/template/source/stylesheets/default.css +2 -1
- data/lib/middleman-wizard-template/template/source/stylesheets/site.css.scss +11 -3
- data/lib/middleman-wizard-template/version.rb +1 -1
- metadata +23 -23
- data/lib/middleman-wizard-template/template/source/javascripts/_lib/Transform.js +0 -401
- data/lib/middleman-wizard-template/template/source/javascripts/_lib/raf.js +0 -26
- data/lib/middleman-wizard-template/template/source/javascripts/_lib/router.js +0 -679
@@ -0,0 +1,1024 @@
|
|
1
|
+
goog.provide('ww.Statechart');
|
2
|
+
goog.provide('ww.State');
|
3
|
+
goog.require('goog.array');
|
4
|
+
|
5
|
+
// ed9c899ccd0b42c4e9da3dbcb70dbbc7e4918f75
|
6
|
+
|
7
|
+
// // Helper function for creating prototypical objects...
|
8
|
+
// var creator = function(){
|
9
|
+
// function F() {}
|
10
|
+
// F.prototype = this;
|
11
|
+
// return new F();
|
12
|
+
// };
|
13
|
+
|
14
|
+
// helper function for merging in properties
|
15
|
+
var merge = function(obj, configs){
|
16
|
+
var k;
|
17
|
+
obj = obj || {};
|
18
|
+
configs = configs || [];
|
19
|
+
configs.forEach( function(x){
|
20
|
+
if (typeof x === 'object'){
|
21
|
+
for (k in x){
|
22
|
+
if(x.hasOwnProperty(k)) obj[k] = x[k];
|
23
|
+
}
|
24
|
+
}
|
25
|
+
});
|
26
|
+
|
27
|
+
return obj;
|
28
|
+
};
|
29
|
+
|
30
|
+
/**
|
31
|
+
* StateMachine namespace.
|
32
|
+
*/
|
33
|
+
ww.Statechart = {
|
34
|
+
DEFAULT_TREE: 'default',
|
35
|
+
SUBSTATE_DELIM: 'SUBSTATE:',
|
36
|
+
version: '0.9.3'
|
37
|
+
};
|
38
|
+
|
39
|
+
if (goog.DEBUG) {
|
40
|
+
ww.Statechart.DebugMessagingObject = {
|
41
|
+
|
42
|
+
level: 0,
|
43
|
+
|
44
|
+
_buildOutput: function(type, state, details, tree){
|
45
|
+
tree = tree || ww.Statechart.DEFAULT_TREE;
|
46
|
+
var msg = "Global::["+tree+"] ";
|
47
|
+
msg = msg + "=> State::["+state+"]: ";
|
48
|
+
msg = msg + '{'+type+'} > '+details;
|
49
|
+
return msg;
|
50
|
+
},
|
51
|
+
|
52
|
+
sendLog: function(type, state, details, tree){
|
53
|
+
if (this.level > 0) return;
|
54
|
+
var msg = this._buildOutput(type, state, details, tree);
|
55
|
+
console.log(msg);
|
56
|
+
return msg;
|
57
|
+
},
|
58
|
+
|
59
|
+
sendInfo: function(type, state, details, tree){
|
60
|
+
if (this.level > 1) return;
|
61
|
+
var msg = this._buildOutput(type, state, details, tree);
|
62
|
+
console.info(msg);
|
63
|
+
return msg;
|
64
|
+
},
|
65
|
+
|
66
|
+
sendWarn: function(type, state, details, tree){
|
67
|
+
if (this.level > 2) return;
|
68
|
+
var msg = this._buildOutput(type, state, details, tree);
|
69
|
+
console.error(msg);
|
70
|
+
return msg;
|
71
|
+
},
|
72
|
+
|
73
|
+
sendError: function(type, state, details, tree){
|
74
|
+
if (this.level > 3) return;
|
75
|
+
var msg = this._buildOutput(type, state, details, tree);
|
76
|
+
console.error(msg);
|
77
|
+
return msg;
|
78
|
+
}
|
79
|
+
};
|
80
|
+
}
|
81
|
+
|
82
|
+
/**
|
83
|
+
* A State.
|
84
|
+
* @constructor
|
85
|
+
* @param {Object} configs Params.
|
86
|
+
*/
|
87
|
+
ww.State = function State(config) {
|
88
|
+
this._data = {};
|
89
|
+
merge(this, [config]);
|
90
|
+
};
|
91
|
+
|
92
|
+
ww.State.prototype = {
|
93
|
+
// walk like a duck
|
94
|
+
isState: true,
|
95
|
+
|
96
|
+
_data: null,
|
97
|
+
|
98
|
+
_isNone: function(value) {
|
99
|
+
return (value === undefined || value === null);
|
100
|
+
},
|
101
|
+
|
102
|
+
goToState: function(name, data) {
|
103
|
+
var sc = this.statechart;
|
104
|
+
if (sc) {
|
105
|
+
sc.goToState(
|
106
|
+
name,
|
107
|
+
this.globalConcurrentState,
|
108
|
+
this.localConcurrentState,
|
109
|
+
data
|
110
|
+
);
|
111
|
+
} else { // weird format for UglifyJS preprocessing
|
112
|
+
if (goog.DEBUG) {
|
113
|
+
throw 'Cannot goToState cause state doesnt have a statechart';
|
114
|
+
}
|
115
|
+
}
|
116
|
+
},
|
117
|
+
|
118
|
+
goToHistoryState: function(name, isRecursive) {
|
119
|
+
var sc = this.statechart;
|
120
|
+
if (sc) {
|
121
|
+
sc.goToHistoryState(
|
122
|
+
name,
|
123
|
+
this.globalConcurrentState,
|
124
|
+
this.localConcurrentState,
|
125
|
+
isRecursive
|
126
|
+
);
|
127
|
+
} else { // weird format for UglifyJS preprocessing
|
128
|
+
if (goog.DEBUG) {
|
129
|
+
throw 'Cannot goToState cause state doesnt have a statechart';
|
130
|
+
}
|
131
|
+
}
|
132
|
+
},
|
133
|
+
|
134
|
+
sendEvent: function(evt) {
|
135
|
+
var sc = this.statechart;
|
136
|
+
if (sc) { sc.sendEvent.apply(sc, arguments); }
|
137
|
+
else { // weird format for UglifyJS preprocessing
|
138
|
+
if (goog.DEBUG) {
|
139
|
+
throw 'Cannot sendEvent cause state doesnt have a statechart';
|
140
|
+
}
|
141
|
+
}
|
142
|
+
},
|
143
|
+
|
144
|
+
sendAction: function(evt){
|
145
|
+
return this.sendEvent.apply(this, arguments);
|
146
|
+
},
|
147
|
+
|
148
|
+
getData: function(key) {
|
149
|
+
if (this._isNone(key)) return key;
|
150
|
+
var sc = this.statechart, ret = this._data[key];
|
151
|
+
if (this._isNone(ret)) {
|
152
|
+
ret = sc.getData(key, this.parentState, this.globalConcurrentState);
|
153
|
+
}
|
154
|
+
return ret;
|
155
|
+
},
|
156
|
+
|
157
|
+
setData: function(key, value) {
|
158
|
+
if (this._isNone(key)) return value;
|
159
|
+
this._data[key] = value;
|
160
|
+
},
|
161
|
+
|
162
|
+
removeData: function(key){
|
163
|
+
if (this._isNone(key)) return key;
|
164
|
+
var sc = this.statechart, ret = this._data[key];
|
165
|
+
if (this._isNone(ret)) {
|
166
|
+
sc.removeData(key, this.parentState, this.globalConcurrentState);
|
167
|
+
} else delete this._data[key];
|
168
|
+
},
|
169
|
+
|
170
|
+
setHistoryState: function(state) {
|
171
|
+
this.history = this.history || {};
|
172
|
+
if (this.substatesAreConcurrent) {
|
173
|
+
this.history[this.localConcurrentState] = state.name;
|
174
|
+
if (goog.DEBUG) {
|
175
|
+
ww.Statechart.DebugMessagingObject.sendLog('HISTORY STATE SET', this.name, ' substree = '+this.localConcurrentState+' => history state set to: '+state.name, this.globalConcurrentState);
|
176
|
+
}
|
177
|
+
}
|
178
|
+
else {
|
179
|
+
this.history = state.name;
|
180
|
+
if (goog.DEBUG) {
|
181
|
+
ww.Statechart.DebugMessagingObject.sendLog('HISTORY STATE SET', this.name, ' history state set to: '+state.name, this.globalConcurrentState);
|
182
|
+
}
|
183
|
+
}
|
184
|
+
}
|
185
|
+
};
|
186
|
+
|
187
|
+
/**
|
188
|
+
* A state chart.
|
189
|
+
* @constructor
|
190
|
+
* @param {Object} config Options.
|
191
|
+
*/
|
192
|
+
ww.Statechart = function Statechart(config) {
|
193
|
+
// config all the internal information
|
194
|
+
this._all_states = {};
|
195
|
+
this._all_states[ww.Statechart.DEFAULT_TREE] = {};
|
196
|
+
this._states_with_concurrent_substates = {};
|
197
|
+
this._current_subtrees = {};
|
198
|
+
this._current_state = {};
|
199
|
+
this._current_state[ww.Statechart.DEFAULT_TREE] = null;
|
200
|
+
this._goToStateLocked = false;
|
201
|
+
this._sendEventLocked = false;
|
202
|
+
this._pendingStateTransitions = [];
|
203
|
+
this._pendingEvents = [];
|
204
|
+
this._active_subtrees = {};
|
205
|
+
|
206
|
+
if (goog.DEBUG) {
|
207
|
+
this.inState = function(name, tree) {
|
208
|
+
var ret = false, cStates = this.currentState(tree);
|
209
|
+
if (!cStates) {
|
210
|
+
throw [
|
211
|
+
"Doesn't appear that you are in any states,",
|
212
|
+
"perhaps you forgot to 'initStates'?"
|
213
|
+
].join(' ');
|
214
|
+
}
|
215
|
+
goog.array.forEach(cStates, function(x) {
|
216
|
+
if (x.name === name) ret = true;
|
217
|
+
});
|
218
|
+
return ret;
|
219
|
+
};
|
220
|
+
this.getActiveStates = this.currentState;
|
221
|
+
}
|
222
|
+
};
|
223
|
+
|
224
|
+
ww.Statechart.prototype = {
|
225
|
+
isStatechart: true,
|
226
|
+
|
227
|
+
addState: function(name) {
|
228
|
+
var tree, obj, hasConcurrentSubstates, pState, states,
|
229
|
+
cTree, nState, config, configs = [], len, i, that = this;
|
230
|
+
|
231
|
+
for (i = 1, len = arguments.length; i < len; i++) {
|
232
|
+
configs[i - 1] = config = arguments[i];
|
233
|
+
}
|
234
|
+
config = len === 1 ? {} : merge(null, configs);
|
235
|
+
// primary config is always the last config
|
236
|
+
config.name = name;
|
237
|
+
config.statechart = this;
|
238
|
+
config.history = null;
|
239
|
+
|
240
|
+
tree = config.globalConcurrentState || ww.Statechart.DEFAULT_TREE;
|
241
|
+
config.globalConcurrentState = tree;
|
242
|
+
|
243
|
+
// Concurrent Substate checks:
|
244
|
+
// Do i have substates?
|
245
|
+
hasConcurrentSubstates = config.substatesAreConcurrent;
|
246
|
+
pState = config.parentState;
|
247
|
+
cTree = this._states_with_concurrent_substates[tree];
|
248
|
+
if (hasConcurrentSubstates) {
|
249
|
+
obj = this._states_with_concurrent_substates[tree] || {};
|
250
|
+
obj[name] = true;
|
251
|
+
this._states_with_concurrent_substates[tree] = obj;
|
252
|
+
}
|
253
|
+
// Am I a Concurrent State of any parent State?
|
254
|
+
if (pState && cTree && cTree[pState]) {
|
255
|
+
pState = this._all_states[tree][pState];
|
256
|
+
if (pState) {
|
257
|
+
pState.substates = pState.substates || [];
|
258
|
+
pState.substates.push(name);
|
259
|
+
}
|
260
|
+
}
|
261
|
+
|
262
|
+
nState = new ww.State(config);
|
263
|
+
nState.sendAction = nState.sendEvent;
|
264
|
+
|
265
|
+
// Actually add the state to our statechart
|
266
|
+
obj = this._all_states[tree] || {};
|
267
|
+
if (goog.DEBUG) {
|
268
|
+
if (obj[name]) {
|
269
|
+
throw [
|
270
|
+
'Trying to add state',
|
271
|
+
name,
|
272
|
+
'to state tree',
|
273
|
+
tree,
|
274
|
+
'and it already exists'
|
275
|
+
].join(' ');
|
276
|
+
}
|
277
|
+
}
|
278
|
+
|
279
|
+
obj[name] = nState;
|
280
|
+
this._all_states[tree] = obj;
|
281
|
+
nState._beenAdded = true;
|
282
|
+
|
283
|
+
// Code to get the substates and add them.
|
284
|
+
states = nState.states || [];
|
285
|
+
if (goog.DEBUG) {
|
286
|
+
// weird format for UglifyJS preprocessing
|
287
|
+
if (states.length === 1 && nState.substatesAreConcurrent) {
|
288
|
+
throw [
|
289
|
+
'Trying to add substates in property \'states\' to ',
|
290
|
+
nState.name,
|
291
|
+
', but must have more than ONE substate'
|
292
|
+
].join(' ');
|
293
|
+
}
|
294
|
+
}
|
295
|
+
|
296
|
+
goog.array.forEach(states, function(x, idx) {
|
297
|
+
var args = [], good = false, last;
|
298
|
+
if (typeof x === 'object' && x.length > 0) {
|
299
|
+
if (goog.DEBUG) {
|
300
|
+
if (typeof x[0] !== 'string') {
|
301
|
+
var msg = '#addState: invalid substate array...Must have the name ';
|
302
|
+
msg += 'at index=0';
|
303
|
+
throw msg;
|
304
|
+
}
|
305
|
+
}
|
306
|
+
args = args.concat(x);
|
307
|
+
good = true;
|
308
|
+
}
|
309
|
+
else if (typeof x === 'string') {
|
310
|
+
args.push(x);
|
311
|
+
good = true;
|
312
|
+
}
|
313
|
+
else if (typeof x === 'object') {
|
314
|
+
if (typeof x.name !== 'string') {
|
315
|
+
if (goog.DEBUG) {
|
316
|
+
var msg2 = '#addState: invalid substate hash...Must have a ';
|
317
|
+
msg2 += '\'name\' property';
|
318
|
+
throw msg2;
|
319
|
+
}
|
320
|
+
}
|
321
|
+
args.push(x.name);
|
322
|
+
args.push(x);
|
323
|
+
good = true;
|
324
|
+
}
|
325
|
+
if (good) {
|
326
|
+
// add missing config parts to the last element.
|
327
|
+
last = args.length - 1;
|
328
|
+
args[last].parentState = name;
|
329
|
+
args[last].globalConcurrentState = tree;
|
330
|
+
that.addState.apply(that, args);
|
331
|
+
} else {
|
332
|
+
if (goog.DEBUG) throw '#addState: invalid substate at index=' + idx;
|
333
|
+
}
|
334
|
+
});
|
335
|
+
|
336
|
+
return this;
|
337
|
+
},
|
338
|
+
|
339
|
+
initStates: function(init) {
|
340
|
+
var x, state;
|
341
|
+
this._inInitialSetup = true;
|
342
|
+
if (typeof init === 'string') {
|
343
|
+
this.goToState(init, ww.Statechart.DEFAULT_TREE);
|
344
|
+
}
|
345
|
+
else if (typeof init === 'object') {
|
346
|
+
for (x in init) {
|
347
|
+
if (init.hasOwnProperty(x)) {
|
348
|
+
state = init[x];
|
349
|
+
this.goToState(state, x);
|
350
|
+
}
|
351
|
+
}
|
352
|
+
}
|
353
|
+
this._inInitialSetup = false;
|
354
|
+
this._flushPendingEvents();
|
355
|
+
|
356
|
+
return this;
|
357
|
+
},
|
358
|
+
|
359
|
+
goToState: function(requestedStateName, tree, concurrentTree, data) {
|
360
|
+
var cState, allStates = this._all_states[tree], idx, len,
|
361
|
+
enterStates = [], exitStates = [], haveExited,
|
362
|
+
enterMatchIndex, exitMatchIndex, that,
|
363
|
+
reqState, pState, i, substateTree,
|
364
|
+
enterStateHandled, exitStateHandled, substates;
|
365
|
+
|
366
|
+
if (goog.DEBUG) {
|
367
|
+
if (!tree) throw '#goToState: invalid global parallel state';
|
368
|
+
}
|
369
|
+
|
370
|
+
// First, find the current tree off of the concurrentTree, then the
|
371
|
+
// main tree
|
372
|
+
if (concurrentTree) {
|
373
|
+
cState = this._current_state[concurrentTree];
|
374
|
+
} else {
|
375
|
+
cState = this._current_state[tree];
|
376
|
+
}
|
377
|
+
|
378
|
+
reqState = allStates[requestedStateName];
|
379
|
+
|
380
|
+
if (goog.DEBUG) {
|
381
|
+
if (!reqState) {
|
382
|
+
var msg = '#goToState: Could not find requested state: ';
|
383
|
+
msg += requestedStateName;
|
384
|
+
throw msg;
|
385
|
+
}
|
386
|
+
}
|
387
|
+
|
388
|
+
// if the current state is the same as the requested state do nothing
|
389
|
+
if (this._checkAllCurrentStates(reqState, concurrentTree || tree)) return;
|
390
|
+
|
391
|
+
if (typeof data !== 'undefined' && data !== null) {
|
392
|
+
ww.Statechart.DebugMessagingObject.sendLog('SETTING DATA FOR TRANSITION FOR => '+requestedStateName);
|
393
|
+
if (typeof data === 'string') reqState.setData(data, data);
|
394
|
+
if (typeof data === 'object') {
|
395
|
+
for (var key in data) {
|
396
|
+
if (data.hasOwnProperty(key)) reqState.setData(key, data[key]);
|
397
|
+
}
|
398
|
+
}
|
399
|
+
}
|
400
|
+
|
401
|
+
if (this._goToStateLocked) {
|
402
|
+
// There is a state transition currently happening. Add this requested
|
403
|
+
// state transition to the queue of pending state transitions. The req
|
404
|
+
// will be invoked after the current state transition is finished
|
405
|
+
this._pendingStateTransitions.push({
|
406
|
+
requestedState: requestedStateName,
|
407
|
+
tree: tree
|
408
|
+
});
|
409
|
+
|
410
|
+
return;
|
411
|
+
}
|
412
|
+
|
413
|
+
// Lock for the current state transition, so that it all gets sorted out
|
414
|
+
// in the right order
|
415
|
+
this._goToStateLocked = true;
|
416
|
+
|
417
|
+
// Get the parent states for the current state and the registered state.
|
418
|
+
// we will use them to find the commen parent state
|
419
|
+
enterStates = this._parentStatesWithRoot(reqState);
|
420
|
+
exitStates = cState ? this._parentStatesWithRoot(cState) : [];
|
421
|
+
|
422
|
+
// continue by finding the common parent state for the current and
|
423
|
+
// requested states:
|
424
|
+
//
|
425
|
+
// At most, this takes O(m^2) time, where m is the maximum depth from the
|
426
|
+
// root of the tree to either the requested state or the current state.
|
427
|
+
// Will always be less than or equal to O(n^2), where n is the number
|
428
|
+
// of states in the tree
|
429
|
+
enterMatchIndex = -1;
|
430
|
+
for (idx = 0, len = exitStates.length; idx < len; idx++) {
|
431
|
+
exitMatchIndex = idx;
|
432
|
+
enterMatchIndex = goog.array.indexOf(enterStates, exitStates[idx]);
|
433
|
+
if (enterMatchIndex >= 0) break;
|
434
|
+
}
|
435
|
+
|
436
|
+
// In the case where we don't find a common parent state, we
|
437
|
+
// must enter from the root state
|
438
|
+
if (enterMatchIndex < 0) enterMatchIndex = enterStates.length - 1;
|
439
|
+
|
440
|
+
// Setup for the enter state sequence
|
441
|
+
this._enterStates = enterStates;
|
442
|
+
this._enterStateMatchIndex = enterMatchIndex;
|
443
|
+
this._enterStateConcurrentTree = concurrentTree;
|
444
|
+
this._enterStateTree = tree;
|
445
|
+
|
446
|
+
// Now, we will exit all the underlying states till we reach the common
|
447
|
+
// parent state. We do not exit the parent state because we transition
|
448
|
+
// within it.
|
449
|
+
this._exitStateStack = [];
|
450
|
+
if (cState && cState.substatesAreConcurrent) {
|
451
|
+
this._fullExitFromSubstates(tree, cState);
|
452
|
+
}
|
453
|
+
for (i = 0; i < exitMatchIndex; i += 1) {
|
454
|
+
cState = exitStates[i];
|
455
|
+
this._exitStateStack.push(cState);
|
456
|
+
}
|
457
|
+
|
458
|
+
// Now, that we have the full stack of states to exit
|
459
|
+
// We can exit them in an orderly fashion.
|
460
|
+
this._unwindExitStateStack();
|
461
|
+
},
|
462
|
+
|
463
|
+
goToHistoryState: function goToHistoryState(requestedState,
|
464
|
+
tree,
|
465
|
+
concurrentTree,
|
466
|
+
isRecursive) {
|
467
|
+
var allStateForTree = this._all_states[tree],
|
468
|
+
pState, realHistoryState;
|
469
|
+
|
470
|
+
if (goog.DEBUG) {
|
471
|
+
if (!tree || !allStateForTree) {
|
472
|
+
var msg = '#goToHistoryState: State requesting does not have a';
|
473
|
+
msg += 'valid global parallel tree';
|
474
|
+
throw msg;
|
475
|
+
}
|
476
|
+
}
|
477
|
+
|
478
|
+
pState = allStateForTree[requestedState];
|
479
|
+
if (pState) realHistoryState = pState.history || pState.initialSubstate;
|
480
|
+
|
481
|
+
if (!realHistoryState) {
|
482
|
+
realHistoryState = requestedState;
|
483
|
+
}
|
484
|
+
else if (isRecursive) {
|
485
|
+
this.goToHistoryState(realHistoryState, tree, isRecursive);
|
486
|
+
return;
|
487
|
+
}
|
488
|
+
this.goToState(realHistoryState, tree);
|
489
|
+
},
|
490
|
+
|
491
|
+
currentState: function(tree) {
|
492
|
+
var ret, tmp, sTree, aTrees, bTree, cStates = this._current_state,
|
493
|
+
cState, i, len, state, ps, aStates;
|
494
|
+
tree = tree || 'default';
|
495
|
+
cState = cStates[tree];
|
496
|
+
aStates = this._all_states[tree];
|
497
|
+
|
498
|
+
// now add all the parents of the current state...
|
499
|
+
if (cState && cState.isState) {
|
500
|
+
ret = this._parentStates(cState);
|
501
|
+
}
|
502
|
+
|
503
|
+
// Now see if it has substates...
|
504
|
+
if (cState && cState.substatesAreConcurrent) {
|
505
|
+
aTrees = this._active_subtrees[tree] || [];
|
506
|
+
for (i = 0, len = aTrees.length; i < len; i++) {
|
507
|
+
sTree = aTrees[i];
|
508
|
+
state = cStates[sTree];
|
509
|
+
if (state) ps = aStates[state.parentState];
|
510
|
+
if (ps && goog.array.indexOf(ret, ps) < 0) ret.unshift(ps);
|
511
|
+
if (state && goog.array.indexOf(ret, state) < 0) ret.unshift(state);
|
512
|
+
}
|
513
|
+
}
|
514
|
+
return ret;
|
515
|
+
},
|
516
|
+
|
517
|
+
sendEvent: function(evt) {
|
518
|
+
var args = [], len = arguments.length, i;
|
519
|
+
|
520
|
+
if (len < 1) return;
|
521
|
+
for (i = 1; i < len; i++) {
|
522
|
+
args[i - 1] = arguments[i];
|
523
|
+
}
|
524
|
+
|
525
|
+
try {
|
526
|
+
if (this._inInitialSetup || this._sendEventLocked ||
|
527
|
+
this._goToStateLocked) {
|
528
|
+
// We want to prevent any events from occurring until
|
529
|
+
// we have completed the state transitions and events
|
530
|
+
this._pendingEvents.push({
|
531
|
+
evt: evt,
|
532
|
+
args: args
|
533
|
+
});
|
534
|
+
|
535
|
+
return;
|
536
|
+
}
|
537
|
+
|
538
|
+
this._sendEventLocked = true;
|
539
|
+
|
540
|
+
// function that processes the event, diff for testing v. production
|
541
|
+
this._processEvent(evt, args);
|
542
|
+
} catch(err) {
|
543
|
+
this._restartEvents();
|
544
|
+
throw err;
|
545
|
+
}
|
546
|
+
|
547
|
+
this._restartEvents();
|
548
|
+
},
|
549
|
+
|
550
|
+
_processEvent: function(evt, args){
|
551
|
+
this._structureCrawl('_cascadeEvents', evt, args);
|
552
|
+
},
|
553
|
+
|
554
|
+
getData: function(key, stateName, tree) {
|
555
|
+
var allStates = this._all_states[tree], state;
|
556
|
+
if (!allStates) return null;
|
557
|
+
state = allStates[stateName];
|
558
|
+
if (state && state.isState) return state.getData(key);
|
559
|
+
},
|
560
|
+
|
561
|
+
removeData: function(key, statename, tree){
|
562
|
+
var allStates = this._all_states[tree], state;
|
563
|
+
if (!allStates) return null;
|
564
|
+
state = allStates[statename];
|
565
|
+
if (state && state.isState) return state.removeData(key);
|
566
|
+
},
|
567
|
+
|
568
|
+
getState: function(name, tree) {
|
569
|
+
var allStates, ret;
|
570
|
+
tree = tree || ww.Statechart.DEFAULT_TREE;
|
571
|
+
allStates = this._all_states[tree];
|
572
|
+
if (!allStates) return null;
|
573
|
+
ret = allStates[name];
|
574
|
+
return ret;
|
575
|
+
},
|
576
|
+
|
577
|
+
_restartEvents: function(){
|
578
|
+
// Now, that the states have a chance to process the first action
|
579
|
+
// we can go ahead and flush the queued events
|
580
|
+
this._sendEventLocked = false;
|
581
|
+
if (!this._inInitialSetup) this._flushPendingEvents();
|
582
|
+
},
|
583
|
+
|
584
|
+
_structureCrawl: function(func, evt, args) {
|
585
|
+
var tree, currentStates = this._current_state, i, len, sResponder, tmp,
|
586
|
+
allStates, responder, aTrees, sTree, handled, found,
|
587
|
+
ss = ww.Statechart.SUBSTATE_DELIM;
|
588
|
+
for (tree in currentStates) {
|
589
|
+
if (!currentStates.hasOwnProperty(tree)) continue;
|
590
|
+
|
591
|
+
handled = false;
|
592
|
+
sTree = null;
|
593
|
+
responder = currentStates[tree];
|
594
|
+
if (!responder || tree.slice(0, ss.length) === ss) continue;
|
595
|
+
// if we don't have an all state tree then we know that this is a
|
596
|
+
// substate tree
|
597
|
+
allStates = this._all_states[tree];
|
598
|
+
if (!allStates) continue;
|
599
|
+
aTrees = this._active_subtrees[tree] || [];
|
600
|
+
for (i = 0, len = aTrees.length; i < len; i++) {
|
601
|
+
sTree = aTrees[i];
|
602
|
+
sResponder = currentStates[sTree];
|
603
|
+
|
604
|
+
if (handled) {
|
605
|
+
tmp = [true, true];
|
606
|
+
} else {
|
607
|
+
tmp = this[func](evt, args, sResponder, allStates, sTree);
|
608
|
+
}
|
609
|
+
|
610
|
+
handled = tmp[0];
|
611
|
+
if (goog.DEBUG) found = tmp[1];
|
612
|
+
}
|
613
|
+
if (!handled) {
|
614
|
+
tmp = this[func](evt, args, responder, allStates, null);
|
615
|
+
handled = tmp[0];
|
616
|
+
if (goog.DEBUG) {
|
617
|
+
if (!found) found = tmp[1];
|
618
|
+
}
|
619
|
+
}
|
620
|
+
if (goog.DEBUG) {
|
621
|
+
if (!found) {
|
622
|
+
ww.Statechart.DebugMessagingObject.sendLog('EVENT', this.name, 'Fired {'+evt+'} with '+(args.length || 0)+' argument(s) found NO state to handle this', this.globalConcurrentState);
|
623
|
+
}
|
624
|
+
}
|
625
|
+
}
|
626
|
+
},
|
627
|
+
|
628
|
+
_checkAllCurrentStates: function(reqState, tree) {
|
629
|
+
var currentStates = this.currentState(tree) || [];
|
630
|
+
if (currentStates === reqState) {
|
631
|
+
return true;
|
632
|
+
} else if (typeof currentStates === 'string' &&
|
633
|
+
reqState === this._all_states[tree][currentStates]) {
|
634
|
+
return true;
|
635
|
+
} else if (goog.isArray(currentStates) &&
|
636
|
+
(goog.array.indexOf(currentStates, reqState) > -1)) {
|
637
|
+
return true;
|
638
|
+
} else {
|
639
|
+
return false;
|
640
|
+
}
|
641
|
+
},
|
642
|
+
|
643
|
+
_flushPendingEvents: function() {
|
644
|
+
var args, pa = this._pendingEvents.shift();
|
645
|
+
if (!pa) return;
|
646
|
+
args = pa.args;
|
647
|
+
args.unshift(pa.evt);
|
648
|
+
this.sendEvent.apply(this, args);
|
649
|
+
},
|
650
|
+
|
651
|
+
_flushPendingStateTransitions: function() {
|
652
|
+
var pending = this._pendingStateTransitions.shift(), msg;
|
653
|
+
if (!pending) return false;
|
654
|
+
this.goToState(pending.requestedState, pending.tree);
|
655
|
+
return true;
|
656
|
+
},
|
657
|
+
|
658
|
+
_parentStateObject: function(name, tree) {
|
659
|
+
if (name &&
|
660
|
+
tree &&
|
661
|
+
this._all_states[tree] &&
|
662
|
+
this._all_states[tree][name]) {
|
663
|
+
return this._all_states[tree][name];
|
664
|
+
}
|
665
|
+
},
|
666
|
+
|
667
|
+
_fullEnter: function(state) {
|
668
|
+
var pState, enterStateHandled = false;
|
669
|
+
if (!state) return;
|
670
|
+
|
671
|
+
try {
|
672
|
+
if (state.enterState) state.enterState();
|
673
|
+
if (state.didEnterState) state.didEnterState();
|
674
|
+
} catch(e){
|
675
|
+
if (DEBUG_MODE) {
|
676
|
+
Stativus.DebugMessagingObject.sendError('ENTER STATE', state.name, 'EXECEPTION ['+e+']', state.globalConcurrentState);
|
677
|
+
}
|
678
|
+
}
|
679
|
+
|
680
|
+
if (state.parentState) {
|
681
|
+
pState = state.statechart.getState(
|
682
|
+
state.parentState,
|
683
|
+
state.globalConcurrentState
|
684
|
+
);
|
685
|
+
pState.setHistoryState(state);
|
686
|
+
}
|
687
|
+
this._unwindEnterStateStack();
|
688
|
+
},
|
689
|
+
|
690
|
+
_fullExit: function(state) {
|
691
|
+
var pState;
|
692
|
+
if (!state) return;
|
693
|
+
var exitStateHandled = false;
|
694
|
+
|
695
|
+
try {
|
696
|
+
if (state.exitState) state.exitState();
|
697
|
+
if (state.didExitState) state.didExitState();
|
698
|
+
} catch (e){
|
699
|
+
if (DEBUG_MODE) {
|
700
|
+
Stativus.DebugMessagingObject.sendError('EXIT STATE', state.name, 'EXECEPTION ['+e+']', state.globalConcurrentState);
|
701
|
+
}
|
702
|
+
}
|
703
|
+
|
704
|
+
if (goog.DEBUG) {
|
705
|
+
ww.Statechart.DebugMessagingObject.sendInfo('EXIT STATE', state.name, 'Completed', state.globalConcurrentState);
|
706
|
+
}
|
707
|
+
this._unwindExitStateStack();
|
708
|
+
},
|
709
|
+
|
710
|
+
_initiateEnterStateSequence: function() {
|
711
|
+
var enterStates, enterMatchIndex, concurrentTree, tree,
|
712
|
+
allStates, i, cState;
|
713
|
+
|
714
|
+
enterStates = this._enterStates;
|
715
|
+
enterMatchIndex = this._enterStateMatchIndex;
|
716
|
+
concurrentTree = this._enterStateConcurrentTree;
|
717
|
+
tree = this._enterStateTree;
|
718
|
+
allStates = this._all_states[tree];
|
719
|
+
|
720
|
+
// Initialize the Enter State Stack
|
721
|
+
this._enterStateStack = this._enterStateStack || [];
|
722
|
+
|
723
|
+
// Finally, from the common parent state, but not including the parent
|
724
|
+
// state, enter the sub states down to the requested state. If the
|
725
|
+
// requested state has an initial sub state, then we must enter it too
|
726
|
+
i = enterMatchIndex - 1;
|
727
|
+
cState = enterStates[i];
|
728
|
+
if (cState) {
|
729
|
+
this._cascadeEnterSubstates(
|
730
|
+
cState,
|
731
|
+
enterStates,
|
732
|
+
(i - 1),
|
733
|
+
concurrentTree || tree,
|
734
|
+
allStates
|
735
|
+
);
|
736
|
+
}
|
737
|
+
|
738
|
+
// once, we have fully hydrated the Enter State Stack, we must
|
739
|
+
// actually async unwind it
|
740
|
+
this._unwindEnterStateStack();
|
741
|
+
|
742
|
+
// Cleanup
|
743
|
+
enterStates = null;
|
744
|
+
enterMatchIndex = null;
|
745
|
+
concurrentTree = null;
|
746
|
+
tree = null;
|
747
|
+
|
748
|
+
delete this._enterStates;
|
749
|
+
delete this._enterStateMatchIndex;
|
750
|
+
delete this._enterStateConcurrentTree;
|
751
|
+
delete this._enterStateTree;
|
752
|
+
},
|
753
|
+
|
754
|
+
_cascadeEnterSubstates: function(start,
|
755
|
+
requiredStates,
|
756
|
+
index,
|
757
|
+
tree,
|
758
|
+
allStates) {
|
759
|
+
var cState, len = requiredStates.length, pState, subStates,
|
760
|
+
that = this, nTree, bTree, name, currStates, aTrees;
|
761
|
+
|
762
|
+
if (!start) return;
|
763
|
+
|
764
|
+
name = start.name;
|
765
|
+
this._enterStateStack.push(start);
|
766
|
+
this._current_state[tree] = start;
|
767
|
+
start.localConcurrentState = tree;
|
768
|
+
if (start.substatesAreConcurrent) {
|
769
|
+
tree = start.globalConcurrentState || ww.Statechart.DEFAULT_TREE;
|
770
|
+
nTreeBase = [ww.Statechart.SUBSTATE_DELIM, tree, name].join('=>');
|
771
|
+
start.history = start.history || {};
|
772
|
+
subStates = start.substates || [];
|
773
|
+
goog.array.forEach(subStates, function(x) {
|
774
|
+
nTree = nTreeBase + '=>' + x;
|
775
|
+
cState = allStates[x];
|
776
|
+
|
777
|
+
// Now, we have to push the item onto the active subtrees for
|
778
|
+
// the base tree for later use of the events.
|
779
|
+
bTree = cState.globalConcurrentState || ww.Statechart.DEFAULT_TREE;
|
780
|
+
aTrees = that._active_subtrees[bTree] || [];
|
781
|
+
aTrees.unshift(nTree);
|
782
|
+
that._active_subtrees[bTree] = aTrees;
|
783
|
+
|
784
|
+
if (index > -1 && requiredStates[index] === cState) index -= 1;
|
785
|
+
that._cascadeEnterSubstates(
|
786
|
+
cState,
|
787
|
+
equiredStates,
|
788
|
+
index,
|
789
|
+
nTree,
|
790
|
+
allStates
|
791
|
+
);
|
792
|
+
});
|
793
|
+
return;
|
794
|
+
}
|
795
|
+
else {
|
796
|
+
// now we can trigger the lower levels of the state
|
797
|
+
cState = requiredStates[index];
|
798
|
+
if (cState) {
|
799
|
+
if (index > -1 && requiredStates[index] === cState) index -= 1;
|
800
|
+
this._cascadeEnterSubstates(
|
801
|
+
cState,
|
802
|
+
requiredStates,
|
803
|
+
index,
|
804
|
+
tree,
|
805
|
+
allStates
|
806
|
+
);
|
807
|
+
}
|
808
|
+
// now we will go into the initial substates of this state
|
809
|
+
else {
|
810
|
+
cState = allStates[start.initialSubstate];
|
811
|
+
this._cascadeEnterSubstates(
|
812
|
+
cState,
|
813
|
+
requiredStates,
|
814
|
+
index,
|
815
|
+
tree,
|
816
|
+
allStates
|
817
|
+
);
|
818
|
+
}
|
819
|
+
}
|
820
|
+
},
|
821
|
+
|
822
|
+
_fullExitFromSubstates: function(tree, stopState) {
|
823
|
+
var cStates, allStates, func, that = this;
|
824
|
+
if (!tree || !stopState || !tree || !stopState.substates) return;
|
825
|
+
|
826
|
+
allStates = this._all_states[tree];
|
827
|
+
cStates = this._current_state;
|
828
|
+
this._exitStateStack = this._exitStateStack || [];
|
829
|
+
|
830
|
+
goog.array.forEach(stopState.substates, function(state) {
|
831
|
+
var substateTree, currState, curr, exitStateHandled, aTrees;
|
832
|
+
substateTree = [
|
833
|
+
ww.Statechart.SUBSTATE_DELIM,
|
834
|
+
tree,
|
835
|
+
stopState.name,
|
836
|
+
state
|
837
|
+
].join('=>');
|
838
|
+
currState = cStates[substateTree];
|
839
|
+
while (currState && currState !== stopState) {
|
840
|
+
exitStateHandled = false;
|
841
|
+
if (!currState) continue;
|
842
|
+
|
843
|
+
that._exitStateStack.unshift(currState);
|
844
|
+
|
845
|
+
// check to see if it has substates
|
846
|
+
if (currState.substatesAreConcurrent) {
|
847
|
+
that._fullExitFromSubstates(tree, currState);
|
848
|
+
}
|
849
|
+
|
850
|
+
curr = currState.parentState;
|
851
|
+
currState = allStates[curr];
|
852
|
+
}
|
853
|
+
|
854
|
+
// Now, remove this from the active substate tree
|
855
|
+
that._active_subtrees[tree] = that._removeFromActiveTree(
|
856
|
+
tree,
|
857
|
+
substateTree
|
858
|
+
);
|
859
|
+
});
|
860
|
+
},
|
861
|
+
|
862
|
+
// this function unwinds the next item on the exitStateStack...
|
863
|
+
_unwindExitStateStack: function() {
|
864
|
+
var stateToExit, delayForAsync = false, stateRestart, sc = this;
|
865
|
+
this._exitStateStack = this._exitStateStack || [];
|
866
|
+
stateToExit = this._exitStateStack.shift();
|
867
|
+
if (stateToExit) {
|
868
|
+
if (stateToExit.willExitState) {
|
869
|
+
// Now for some amazing encapsulation magic with closures
|
870
|
+
// We are going to create a temporary object that gets passed
|
871
|
+
// into the willExitState call that will restart the state
|
872
|
+
// exit for this path as needed
|
873
|
+
stateRestart = function(){
|
874
|
+
var sc = this._statechart;
|
875
|
+
if (DEBUG_MODE) {
|
876
|
+
Stativus.DebugMessagingObject.sendLog('ASYNC', stateToExit.name, 'willExitState() completed!', stateToExit.globalConcurrentState);
|
877
|
+
}
|
878
|
+
if (sc) sc._fullExit(stateToExit);
|
879
|
+
};
|
880
|
+
delayForAsync = stateToExit.willExitState(stateRestart);
|
881
|
+
if (goog.DEBUG) {
|
882
|
+
if (delayForAsync) {
|
883
|
+
ww.Statechart.DebugMessagingObject.sendLog('ASYNC', stateToExit.name, 'exitState() delayed', stateToExit.globalConcurrentState);
|
884
|
+
} else {
|
885
|
+
ww.Statechart.DebugMessagingObject.sendWarn('ASYNC', stateToExit.name, 'Didn\'t return \'true\' willExitState() which is needed if you want async', stateToExit.globalConcurrentState);
|
886
|
+
}
|
887
|
+
}
|
888
|
+
}
|
889
|
+
if (!delayForAsync) this._fullExit(stateToExit);
|
890
|
+
}
|
891
|
+
else {
|
892
|
+
delete this._exitStateStack;
|
893
|
+
this._initiateEnterStateSequence();
|
894
|
+
}
|
895
|
+
},
|
896
|
+
|
897
|
+
// this function unwinds the next item on the enterStateStack...
|
898
|
+
_unwindEnterStateStack: function() {
|
899
|
+
var stateToEnter, delayForAsync = false, stateRestart, more, that = this;
|
900
|
+
this._exitStateStack = this._exitStateStack || [];
|
901
|
+
stateToEnter = this._enterStateStack.shift();
|
902
|
+
if (stateToEnter) {
|
903
|
+
if (stateToEnter.willEnterState) {
|
904
|
+
// Now for some amazing encapsulation magic with closures
|
905
|
+
// We are going to create a temporary object that gets passed
|
906
|
+
// into the willExitState call that will restart the state
|
907
|
+
// exit for this path as needed
|
908
|
+
stateRestart = function() {
|
909
|
+
if (goog.DEBUG) {
|
910
|
+
ww.Statechart.DebugMessagingObject.sendLog('ASYNC', stateToEnter.name, 'willEnterState() completed!', stateToEnter.globalConcurrentState);
|
911
|
+
}
|
912
|
+
if (that) that._fullEnter(stateToEnter);
|
913
|
+
};
|
914
|
+
delayForAsync = stateToEnter.willEnterState(stateRestart);
|
915
|
+
if (goog.DEBUG) {
|
916
|
+
if (delayForAsync) {
|
917
|
+
ww.Statechart.DebugMessagingObject.sendLog('ASYNC', stateToEnter.name, 'enterState() delayed', stateToEnter.globalConcurrentState);
|
918
|
+
} else {
|
919
|
+
ww.Statechart.DebugMessagingObject.sendWarn('ASYNC', stateToEnter.name, 'Didn\'t return \'true\' willEnterState() which is needed if you want async', stateToEnter.globalConcurrentState);
|
920
|
+
}
|
921
|
+
}
|
922
|
+
}
|
923
|
+
if (!delayForAsync) this._fullEnter(stateToEnter);
|
924
|
+
}
|
925
|
+
else {
|
926
|
+
delete this._enterStateStack;
|
927
|
+
|
928
|
+
// Ok, we're done with the current state transition. Make sure to unlock
|
929
|
+
// the goToState and let other pending state transitions
|
930
|
+
this._goToStateLocked = false;
|
931
|
+
more = this._flushPendingStateTransitions();
|
932
|
+
if (!more && !this._inInitialSetup) {
|
933
|
+
// Once pending state transitions are flushed then go ahead and
|
934
|
+
// start flush pending actions
|
935
|
+
this._flushPendingEvents();
|
936
|
+
}
|
937
|
+
}
|
938
|
+
},
|
939
|
+
|
940
|
+
// TODO: make this more efficient
|
941
|
+
_removeFromActiveTree: function(baseTree, tree) {
|
942
|
+
var nArray = [], aTrees = this._active_subtrees[baseTree];
|
943
|
+
if (!aTrees) return [];
|
944
|
+
if (!tree) return aTrees;
|
945
|
+
|
946
|
+
goog.array.forEach(aTrees, function(x) {
|
947
|
+
if (x !== tree) nArray.push(x);
|
948
|
+
});
|
949
|
+
|
950
|
+
return nArray;
|
951
|
+
},
|
952
|
+
|
953
|
+
_parentStates: function(state) {
|
954
|
+
var ret = [], curr = state;
|
955
|
+
// always add first state
|
956
|
+
ret.push(curr);
|
957
|
+
curr = this._parentStateObject(
|
958
|
+
curr.parentState,
|
959
|
+
curr.globalConcurrentState
|
960
|
+
);
|
961
|
+
|
962
|
+
while (curr) {
|
963
|
+
ret.push(curr);
|
964
|
+
curr = this._parentStateObject(
|
965
|
+
curr.parentState,
|
966
|
+
curr.globalConcurrentState
|
967
|
+
);
|
968
|
+
}
|
969
|
+
return ret;
|
970
|
+
},
|
971
|
+
|
972
|
+
_parentStatesWithRoot: function(state) {
|
973
|
+
var ret = this._parentStates(state);
|
974
|
+
ret.push('root');
|
975
|
+
return ret;
|
976
|
+
}
|
977
|
+
|
978
|
+
};
|
979
|
+
|
980
|
+
/**
|
981
|
+
* Cascade events.
|
982
|
+
* @param {Event} evt The original event.
|
983
|
+
* @param {Array} args Event args.
|
984
|
+
* @param {ww.State} responder Responder state.
|
985
|
+
* @param {Array} allStates All the states.
|
986
|
+
* @param {String} tree The state tree.
|
987
|
+
* @return {Array} Who handled the event.
|
988
|
+
*/
|
989
|
+
ww.Statechart.prototype['_cascadeEvents'] =
|
990
|
+
function(evt, args, responder, allStates, tree) {
|
991
|
+
var handled, trees, len, ssName, found = false;
|
992
|
+
|
993
|
+
// substate prep work...
|
994
|
+
if (tree) {
|
995
|
+
trees = tree.split('=>');
|
996
|
+
len = trees.length || 0;
|
997
|
+
ssName = trees[len - 1];
|
998
|
+
}
|
999
|
+
|
1000
|
+
while (!handled && responder) {
|
1001
|
+
if (responder[evt]) {
|
1002
|
+
if (goog.DEBUG) {
|
1003
|
+
ww.Statechart.DebugMessagingObject.sendInfo('EVENT', responder.name, 'Fired \''+evt+'\' with '+(args.length || 0)+' argument(s)', responder.globalConcurrentState);
|
1004
|
+
}
|
1005
|
+
try {
|
1006
|
+
handled = responder[evt].apply(responder, args);
|
1007
|
+
} catch(e){
|
1008
|
+
if (DEBUG_MODE) {
|
1009
|
+
Stativus.DebugMessagingObject.sendError('EVENT', responder.name, 'Fired \''+evt+'\': Exception: '+e, responder.globalConcurrentState);
|
1010
|
+
}
|
1011
|
+
}
|
1012
|
+
found = true;
|
1013
|
+
}
|
1014
|
+
// check to see if we have reached the end of this tree
|
1015
|
+
if (tree && ssName === responder.name) return [handled, found];
|
1016
|
+
if (!handled && responder.parentState) {
|
1017
|
+
responder = allStates[responder.parentState];
|
1018
|
+
} else {
|
1019
|
+
responder = null;
|
1020
|
+
}
|
1021
|
+
}
|
1022
|
+
|
1023
|
+
return [handled, found];
|
1024
|
+
};
|