middleman-wizard-template 1.0.2 → 1.0.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- 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
|
+
};
|