shank 0.0.1
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.
- data/.gitignore +22 -0
- data/Gemfile +3 -0
- data/LICENSE +22 -0
- data/README.md +0 -0
- data/Rakefile +2 -0
- data/lib/assets/javascripts/engine.gamepads.coffee +46 -0
- data/lib/assets/javascripts/engine_stats.coffee +10 -0
- data/lib/assets/javascripts/error_handler.coffee +11 -0
- data/lib/assets/javascripts/game_keys.coffee +6 -0
- data/lib/assets/javascripts/gamepads.coffee +21 -0
- data/lib/assets/javascripts/gamepads.controller.coffee +138 -0
- data/lib/assets/javascripts/init.coffee +6 -0
- data/lib/assets/javascripts/joysticks.coffee +238 -0
- data/lib/assets/javascripts/jquery.hotkeys.coffee +161 -0
- data/lib/assets/javascripts/jquery.reverse_merge.coffee +21 -0
- data/lib/assets/javascripts/keydown.coffee +73 -0
- data/lib/assets/javascripts/mouse.coffee +66 -0
- data/lib/assets/javascripts/music.coffee +78 -0
- data/lib/assets/javascripts/pixie_canvas.coffee +739 -0
- data/lib/assets/javascripts/request_animation_frame.coffee +22 -0
- data/lib/assets/javascripts/shank.coffee +18 -0
- data/lib/assets/javascripts/sound.coffee +131 -0
- data/lib/assets/javascripts/storage.coffee +88 -0
- data/lib/shank/version.rb +3 -0
- data/lib/shank.rb +10 -0
- data/shank.gemspec +17 -0
- data/test/jquery.reverse_merge.coffee +43 -0
- data/test/keydown.coffee +19 -0
- data/test/pixie_canvas.coffee +18 -0
- data/test/request_animation_frame.coffee +7 -0
- data/test/sound.coffee +7 -0
- data/test/storage.coffee +47 -0
- data/test/xstats.coffee +7 -0
- data/vendor/assets/javascripts/xstats.js +767 -0
- metadata +113 -0
@@ -0,0 +1,767 @@
|
|
1
|
+
/*!
|
2
|
+
* xStats.js v1.0.0-pre
|
3
|
+
* Copyright 2011-2012 John-David Dalton <http://allyoucanleet.com/>
|
4
|
+
* Based on Stats.js, copyright Ricardo Cabello <http://mrdoob.com/>
|
5
|
+
* Available under MIT license <https://github.com/jdalton/xstats.js/raw/master/LICENSE.txt>
|
6
|
+
*/
|
7
|
+
;(function(window, document) {
|
8
|
+
'use strict';
|
9
|
+
|
10
|
+
/** Detect memory object */
|
11
|
+
var memoryNS = (memoryNS = window.performance || window.webkitPerformance || window.console) &&
|
12
|
+
memoryNS.memory && memoryNS;
|
13
|
+
|
14
|
+
/** Shortcut used to convert array-like objects to arrays */
|
15
|
+
var slice = [].slice;
|
16
|
+
|
17
|
+
/** Internal cached used by various methods */
|
18
|
+
var cache = {
|
19
|
+
'counter': 1,
|
20
|
+
'frameTimes': [],
|
21
|
+
'lastSecond': null,
|
22
|
+
'lastTime': null,
|
23
|
+
'data': { 'fps': new Data, 'ms': new Data, 'mem': new Data }
|
24
|
+
};
|
25
|
+
|
26
|
+
/** Math shortcuts */
|
27
|
+
var floor = Math.floor,
|
28
|
+
max = Math.max,
|
29
|
+
min = Math.min,
|
30
|
+
round = Math.round;
|
31
|
+
|
32
|
+
/*--------------------------------------------------------------------------*/
|
33
|
+
|
34
|
+
/**
|
35
|
+
* The Data object constructor.
|
36
|
+
*
|
37
|
+
* @private
|
38
|
+
* @constructor
|
39
|
+
*/
|
40
|
+
function Data() {
|
41
|
+
// add own properties to avoid lookups on the Array.prototype
|
42
|
+
return extend([], { 'max': null, 'min': null });
|
43
|
+
}
|
44
|
+
|
45
|
+
/**
|
46
|
+
* The Event constructor.
|
47
|
+
*
|
48
|
+
* @constructor
|
49
|
+
* @memberOf xStats
|
50
|
+
* @param {String|Object} type The event type.
|
51
|
+
*/
|
52
|
+
function Event(type) {
|
53
|
+
var me = this;
|
54
|
+
return (!me || me.constructor != Event)
|
55
|
+
? new Event(type)
|
56
|
+
: (type instanceof Event)
|
57
|
+
? type
|
58
|
+
: extend(me, typeof type == 'string' ? { 'type': type } : type);
|
59
|
+
}
|
60
|
+
|
61
|
+
/**
|
62
|
+
* The xStats constructor.
|
63
|
+
*
|
64
|
+
* @constructor
|
65
|
+
* @param {Object} [options={}] Options object.
|
66
|
+
* @example
|
67
|
+
*
|
68
|
+
* // basic usage (the `new` operator is optional)
|
69
|
+
* var stats = new xStats;
|
70
|
+
*
|
71
|
+
* // or using options
|
72
|
+
* var stats = new xStats({
|
73
|
+
* 'mode': 'ms',
|
74
|
+
* 'height': 130,
|
75
|
+
* 'width':200,
|
76
|
+
* 'padding':10,
|
77
|
+
* 'locked': false,
|
78
|
+
* 'fps': {
|
79
|
+
* 'bg': '#330000',
|
80
|
+
* 'fg': '#cc6600'
|
81
|
+
* },
|
82
|
+
* 'ms': {
|
83
|
+
* 'bg': '#000033',
|
84
|
+
* 'fg': '#3366ff'
|
85
|
+
* },
|
86
|
+
* 'mem': {
|
87
|
+
* 'bg': '#000033',
|
88
|
+
* 'fg': '#660099'
|
89
|
+
* }
|
90
|
+
* });
|
91
|
+
*
|
92
|
+
* // insert into document
|
93
|
+
* document.body.appendChild(stats.element);
|
94
|
+
*/
|
95
|
+
function xStats(options) {
|
96
|
+
var clipped,
|
97
|
+
element,
|
98
|
+
fps,
|
99
|
+
height,
|
100
|
+
mem,
|
101
|
+
ms,
|
102
|
+
padding,
|
103
|
+
uid,
|
104
|
+
width,
|
105
|
+
data = cache.data,
|
106
|
+
me = this,
|
107
|
+
tmp = {};
|
108
|
+
|
109
|
+
// allow instance creation without the `new` operator
|
110
|
+
if (!me || me.constructor != xStats) {
|
111
|
+
return new xStats(options);
|
112
|
+
}
|
113
|
+
|
114
|
+
element = document.createElement('div');
|
115
|
+
uid = 'xstats' + cache.counter++;
|
116
|
+
|
117
|
+
// apply options
|
118
|
+
extend(me, options || (options = {}));
|
119
|
+
me.uid = uid;
|
120
|
+
extend(tmp, me);
|
121
|
+
|
122
|
+
fps = me.fps = extend(extend({}, me.fps), options.fps);
|
123
|
+
ms = me.ms = extend(extend({}, me.ms), options.ms);
|
124
|
+
mem = me.mem = extend(extend({}, me.mem), options.mem);
|
125
|
+
|
126
|
+
// compute dimensions
|
127
|
+
padding = me.padding * 2;
|
128
|
+
height = me.height - padding;
|
129
|
+
width = me.width - padding;
|
130
|
+
clipped = max(1, round(width * 0.02));
|
131
|
+
width = floor(width / clipped);
|
132
|
+
|
133
|
+
// sweet spot for font-size, height, and width
|
134
|
+
tmp.titleHeight = round(height * 0.28);
|
135
|
+
tmp.barWidth = clipped + 4;
|
136
|
+
tmp.fontSize = (tmp.titleHeight / 22.2).toFixed(2);
|
137
|
+
tmp.innerWidth = clipped * width;
|
138
|
+
tmp.innerHeight = height - tmp.titleHeight;
|
139
|
+
tmp.padding = round((me.width - tmp.innerWidth) / 2);
|
140
|
+
|
141
|
+
// increase shared data if needed
|
142
|
+
if (data.ms.length < width) {
|
143
|
+
data.fps.length =
|
144
|
+
data.ms.length =
|
145
|
+
data.mem.length = width;
|
146
|
+
}
|
147
|
+
// append customized css
|
148
|
+
appendCSS(
|
149
|
+
interpolate(
|
150
|
+
'.#{uid},.#{uid} .bg,.#{uid} .fg{width:#{width}px;height:#{height}px}' +
|
151
|
+
'.#{uid} .mi{margin:#{padding}px;width:#{innerWidth}px}' +
|
152
|
+
'.#{uid} p{font-size:#{fontSize}em;height:#{titleHeight}px;width:#{innerWidth}px}' +
|
153
|
+
'.#{uid} ul{height:#{innerHeight}px;width:#{innerWidth}px}' +
|
154
|
+
'.#{uid} li{width:#{barWidth}px}', tmp) +
|
155
|
+
interpolate(
|
156
|
+
'.#{uid}.fps{color:#{fg}}' +
|
157
|
+
'.#{uid}.fps ul{background:#{fg}}' +
|
158
|
+
'.#{uid}.fps .bg,.#{uid}.fps li{background:#{bg}}', extend(tmp, fps)) +
|
159
|
+
interpolate(
|
160
|
+
'.#{uid}.ms{color:#{fg}}' +
|
161
|
+
'.#{uid}.ms ul{background:#{fg}}' +
|
162
|
+
'.#{uid}.ms .bg,.#{uid}.ms li{background:#{bg}}', extend(tmp, ms)) +
|
163
|
+
interpolate(
|
164
|
+
'.#{uid}.mem{color:#{fg}}' +
|
165
|
+
'.#{uid}.mem ul{background:#{fg}}' +
|
166
|
+
'.#{uid}.mem .bg,.#{uid}.mem li{background:#{bg}}', extend(tmp, mem)));
|
167
|
+
|
168
|
+
// build interface
|
169
|
+
element.className = 'xstats ' + uid + ' ' + me.mode;
|
170
|
+
element.innerHTML = '<div class=bg></div><div class=mi><p> </p><ul>' + repeat('<li></li>', width) + '</ul></div><div class=fg></div>';
|
171
|
+
|
172
|
+
// add element event listeners
|
173
|
+
if (typeof element.addEventListener != 'undefined') {
|
174
|
+
element.addEventListener('click', createSwapMode(me), false);
|
175
|
+
} else if (element.attachEvent != 'undefined') {
|
176
|
+
element.attachEvent('onclick', createSwapMode(me));
|
177
|
+
}
|
178
|
+
|
179
|
+
// grab elements
|
180
|
+
me.element = element;
|
181
|
+
me.canvas = element.getElementsByTagName('ul')[0];
|
182
|
+
me.title = element.getElementsByTagName('p')[0].firstChild;
|
183
|
+
|
184
|
+
// keep track of instances to animate
|
185
|
+
xStats.subclasses.push(me);
|
186
|
+
}
|
187
|
+
|
188
|
+
/*--------------------------------------------------------------------------*/
|
189
|
+
|
190
|
+
/**
|
191
|
+
* Adds a css class name to an element's className property.
|
192
|
+
*
|
193
|
+
* @private
|
194
|
+
* @param {Object} element The element.
|
195
|
+
* @param {String} className The class name.
|
196
|
+
*/
|
197
|
+
function addClass(element, className) {
|
198
|
+
element.className += (element.className ? ' ' : '') + className;
|
199
|
+
}
|
200
|
+
|
201
|
+
/**
|
202
|
+
* Appends CSS text to a planted style sheet.
|
203
|
+
*
|
204
|
+
* @private
|
205
|
+
* @param {String} cssText The CSS text.
|
206
|
+
*/
|
207
|
+
function appendCSS(cssText) {
|
208
|
+
var node,
|
209
|
+
prop = 'cssText',
|
210
|
+
sheet = cache.sheet;
|
211
|
+
|
212
|
+
if (!sheet) {
|
213
|
+
node = document.getElementsByTagName('head')[0];
|
214
|
+
sheet = cache.sheet = document.createElement('style');
|
215
|
+
sheet.type = 'text/css';
|
216
|
+
node.insertBefore(sheet, node.firstChild);
|
217
|
+
}
|
218
|
+
if (!(node = 'styleSheet' in sheet && sheet.styleSheet)) {
|
219
|
+
prop = 'nodeValue';
|
220
|
+
node = sheet.firstChild || sheet.appendChild(document.createTextNode(''));
|
221
|
+
}
|
222
|
+
node[prop] += cssText;
|
223
|
+
}
|
224
|
+
|
225
|
+
/**
|
226
|
+
* An iteration utility for arrays.
|
227
|
+
* Callbacks may terminate the loop by explicitly returning `false`.
|
228
|
+
*
|
229
|
+
* @private
|
230
|
+
* @param {Array} array The array to iterate over.
|
231
|
+
* @param {Function} callback The function called per iteration.
|
232
|
+
* @returns {Array} Returns the array iterated over.
|
233
|
+
*/
|
234
|
+
function each(array, callback) {
|
235
|
+
var index = -1,
|
236
|
+
length = array.length;
|
237
|
+
|
238
|
+
while (++index < length) {
|
239
|
+
if (callback(array[index], index, array) === false) {
|
240
|
+
break;
|
241
|
+
}
|
242
|
+
}
|
243
|
+
return array;
|
244
|
+
}
|
245
|
+
|
246
|
+
/**
|
247
|
+
* Copies enumerable properties from the source object to the destination object.
|
248
|
+
*
|
249
|
+
* @private
|
250
|
+
* @param {Object} destination The destination object.
|
251
|
+
* @param {Object} [source={}] The source object.
|
252
|
+
* @returns {Object} The destination object.
|
253
|
+
*/
|
254
|
+
function extend(destination, source) {
|
255
|
+
source || (source = {});
|
256
|
+
for (var key in source) {
|
257
|
+
destination[key] = source[key];
|
258
|
+
}
|
259
|
+
return destination;
|
260
|
+
}
|
261
|
+
|
262
|
+
/**
|
263
|
+
* Modify a string by replacing named tokens with matching object property values.
|
264
|
+
*
|
265
|
+
* @private
|
266
|
+
* @param {String} string The string to modify.
|
267
|
+
* @param {Object} object The template object.
|
268
|
+
* @returns {String} The modified string.
|
269
|
+
*/
|
270
|
+
function interpolate(string, object) {
|
271
|
+
for (var key in object) {
|
272
|
+
string = string.replace(RegExp('#\\{' + key + '\\}', 'g'), object[key]);
|
273
|
+
}
|
274
|
+
return string;
|
275
|
+
}
|
276
|
+
|
277
|
+
/**
|
278
|
+
* Removes a css class name from an element's className property.
|
279
|
+
*
|
280
|
+
* @private
|
281
|
+
* @param {Object} element The element.
|
282
|
+
* @param {String} className The class name.
|
283
|
+
*/
|
284
|
+
function removeClass(element, className) {
|
285
|
+
var cn,
|
286
|
+
classNames = element.className.split(' '),
|
287
|
+
filtered = [];
|
288
|
+
|
289
|
+
while ((cn = classNames.pop())) {
|
290
|
+
if (className != cn) {
|
291
|
+
filtered.push(cn);
|
292
|
+
}
|
293
|
+
}
|
294
|
+
element.className = filtered.join(' ');
|
295
|
+
}
|
296
|
+
|
297
|
+
/**
|
298
|
+
* Repeat a string a given number of times using the `Exponentiation by squaring` algorithm.
|
299
|
+
*
|
300
|
+
* @private
|
301
|
+
* @param {String} string The string to repeat.
|
302
|
+
* @param {Number} count The number of times to repeat the string.
|
303
|
+
* @returns {String} The repeated string.
|
304
|
+
* @see http://www.merlyn.demon.co.uk/js-misc0.htm#MLS
|
305
|
+
*/
|
306
|
+
function repeat(string, count) {
|
307
|
+
if (count < 1) return '';
|
308
|
+
if (count % 2) return repeat(string, count - 1) + string;
|
309
|
+
var half = repeat(string, count / 2);
|
310
|
+
return half + half;
|
311
|
+
}
|
312
|
+
|
313
|
+
/*--------------------------------------------------------------------------*/
|
314
|
+
|
315
|
+
/**
|
316
|
+
* Registers a single listener for the specified event type(s).
|
317
|
+
*
|
318
|
+
* @memberOf xStats
|
319
|
+
* @param {String} type The event type.
|
320
|
+
* @param {Function} listener The function called when the event occurs.
|
321
|
+
* @returns {Object} The xStats instance.
|
322
|
+
* @example
|
323
|
+
*
|
324
|
+
* // register a listener for an event type
|
325
|
+
* xs.addListener('sample', listener);
|
326
|
+
*
|
327
|
+
* // register a listener for multiple event types
|
328
|
+
* xs.addListener('start sample', listener);
|
329
|
+
*/
|
330
|
+
function addListener(type, listener) {
|
331
|
+
var me = this,
|
332
|
+
events = me.events || (me.events = {});
|
333
|
+
|
334
|
+
each(type.split(' '), function(type) {
|
335
|
+
(events[type] || (events[type] = [])).push(listener);
|
336
|
+
});
|
337
|
+
return me;
|
338
|
+
}
|
339
|
+
|
340
|
+
/**
|
341
|
+
* Executes all registered listeners of the specified event type.
|
342
|
+
*
|
343
|
+
* @memberOf xStats
|
344
|
+
* @param {String|Object} type The event type or object.
|
345
|
+
* @returns {Boolean} Returns `true` if all listeners were executed, else `false`.
|
346
|
+
*/
|
347
|
+
function emit(type) {
|
348
|
+
var me = this,
|
349
|
+
event = Event(type),
|
350
|
+
args = (arguments[0] = event, slice.call(arguments)),
|
351
|
+
events = me.events,
|
352
|
+
listeners = events && events[event.type] || [],
|
353
|
+
result = true;
|
354
|
+
|
355
|
+
each(listeners.slice(), function(listener) {
|
356
|
+
if (!(result = listener.apply(me, args) !== false)) {
|
357
|
+
return result;
|
358
|
+
}
|
359
|
+
});
|
360
|
+
return result;
|
361
|
+
}
|
362
|
+
|
363
|
+
/**
|
364
|
+
* Unregisters a single listener for the specified event type(s).
|
365
|
+
*
|
366
|
+
* @memberOf xStats
|
367
|
+
* @param {String} type The event type.
|
368
|
+
* @param {Function} listener The function to unregister.
|
369
|
+
* @returns {Object} The xStats instance.
|
370
|
+
* @example
|
371
|
+
*
|
372
|
+
* // unregister a listener for an event type
|
373
|
+
* xs.removeListener('sample', listener);
|
374
|
+
*
|
375
|
+
* // unregister a listener for multiple event types
|
376
|
+
* xs.removeListener('start sample', listener);
|
377
|
+
*/
|
378
|
+
function removeListener(type, listener) {
|
379
|
+
var me = this,
|
380
|
+
events = me.events;
|
381
|
+
|
382
|
+
each(type.split(' '), function(type) {
|
383
|
+
var listeners = events && events[type] || [],
|
384
|
+
index = indexOf(listeners, listener);
|
385
|
+
if (index > -1) {
|
386
|
+
listeners.splice(index, 1);
|
387
|
+
}
|
388
|
+
});
|
389
|
+
return me;
|
390
|
+
}
|
391
|
+
|
392
|
+
/**
|
393
|
+
* Unregisters all listeners or those for the specified event type(s).
|
394
|
+
*
|
395
|
+
* @memberOf xStats
|
396
|
+
* @param {String} type The event type.
|
397
|
+
* @returns {Object} The xStats instance.
|
398
|
+
* @example
|
399
|
+
*
|
400
|
+
* // unregister all listeners
|
401
|
+
* xs.removeAllListeners();
|
402
|
+
*
|
403
|
+
* // unregister all listeners for an event type
|
404
|
+
* xs.removeAllListeners('sample');
|
405
|
+
*
|
406
|
+
* // unregister all listeners for multiple event types
|
407
|
+
* xs.removeAllListeners('start sample complete');
|
408
|
+
*/
|
409
|
+
function removeAllListeners(type) {
|
410
|
+
var me = this,
|
411
|
+
events = me.events;
|
412
|
+
|
413
|
+
each(type ? type.split(' ') : events, function(type) {
|
414
|
+
(events && events[type] || []).length = 0;
|
415
|
+
});
|
416
|
+
return me;
|
417
|
+
}
|
418
|
+
|
419
|
+
/*--------------------------------------------------------------------------*/
|
420
|
+
|
421
|
+
/**
|
422
|
+
* Creates the click event handler that controls swaping modes and redrawing
|
423
|
+
* the display.
|
424
|
+
*
|
425
|
+
* @private
|
426
|
+
* @param {Object} me The xStats instance.
|
427
|
+
* @returns {Function} The event handler.
|
428
|
+
*/
|
429
|
+
function createSwapMode(me) {
|
430
|
+
return function() {
|
431
|
+
if (!me.locked) {
|
432
|
+
var mode = me.mode == 'fps' ? 'ms' : me.mode == 'ms' ? (memoryNS ? 'mem' : 'fps') : 'fps',
|
433
|
+
element = me.element,
|
434
|
+
nodes = me.canvas.childNodes,
|
435
|
+
data = cache.data[mode],
|
436
|
+
entry = data[0],
|
437
|
+
length = nodes.length;
|
438
|
+
|
439
|
+
me.mode = mode;
|
440
|
+
setTitle(me, entry && entry.value);
|
441
|
+
while (length--) {
|
442
|
+
entry = data[length];
|
443
|
+
setBar(me, nodes[length], entry && entry.percent);
|
444
|
+
}
|
445
|
+
removeClass(element, 'fps');
|
446
|
+
removeClass(element, 'ms');
|
447
|
+
removeClass(element, 'mem');
|
448
|
+
addClass(element, mode);
|
449
|
+
}
|
450
|
+
};
|
451
|
+
}
|
452
|
+
|
453
|
+
/**
|
454
|
+
* Records a value for the given mode.
|
455
|
+
*
|
456
|
+
* @private
|
457
|
+
* @param {String} mode The mode to record.
|
458
|
+
* @param {Mixed} value The value recorded.
|
459
|
+
*/
|
460
|
+
function record(mode, value) {
|
461
|
+
var data = cache.data[mode],
|
462
|
+
percent = min(100, 100 * (value / (mode == 'fps' ? 80 : mode == 'ms' ? 1e3 : 128)));
|
463
|
+
|
464
|
+
value = mode == 'mem' ? value.toFixed(2) : round(value);
|
465
|
+
data.length = [data.length, data.unshift({ 'value': value, 'percent': percent })][0];
|
466
|
+
|
467
|
+
value = floor(value);
|
468
|
+
data.min = min(data.min != null ? data.min : value, value);
|
469
|
+
data.max = max(data.max != null ? data.max : value, value);
|
470
|
+
}
|
471
|
+
|
472
|
+
/**
|
473
|
+
* Sets the LI element's height based on the given value.
|
474
|
+
*
|
475
|
+
* @private
|
476
|
+
* @param {Object} me The xStats instance.
|
477
|
+
* @param {Object} node The LI element.
|
478
|
+
* @param {Number} percent The bar height as a percentage.
|
479
|
+
*/
|
480
|
+
function setBar(me, node, percent) {
|
481
|
+
var height = 100,
|
482
|
+
base = (height / 16) * 15,
|
483
|
+
portion = (base / 100) * percent,
|
484
|
+
value = percent != null ? (base - portion).toFixed(2) : height;
|
485
|
+
|
486
|
+
node.style.height = value + '%';
|
487
|
+
}
|
488
|
+
|
489
|
+
/**
|
490
|
+
* Sets a chart's title based on the given value.
|
491
|
+
*
|
492
|
+
* @private
|
493
|
+
* @param {Object} me The xStats instance.
|
494
|
+
* @param {Number} value The value.
|
495
|
+
*/
|
496
|
+
function setTitle(me, value) {
|
497
|
+
var mode = me.mode,
|
498
|
+
unit = mode == 'mem' ? 'MB' : mode.toUpperCase(),
|
499
|
+
data = cache.data[mode];
|
500
|
+
|
501
|
+
me.title.nodeValue = value == null ? ' ' :
|
502
|
+
value + unit + ' (' + data.min + '-' + data.max + ')';
|
503
|
+
}
|
504
|
+
|
505
|
+
/**
|
506
|
+
* Updates chart data and display of all xStats instances.
|
507
|
+
*
|
508
|
+
* @private
|
509
|
+
*/
|
510
|
+
function update() {
|
511
|
+
var length,
|
512
|
+
data = cache.data,
|
513
|
+
frameTimes = cache.frameTimes,
|
514
|
+
fps = 0,
|
515
|
+
now = new Date,
|
516
|
+
secValue = now - cache.lastSecond,
|
517
|
+
lastSec = new Date - 1000;
|
518
|
+
|
519
|
+
// skip first call
|
520
|
+
if (cache.lastTime != null) {
|
521
|
+
// record data
|
522
|
+
frameTimes.push(new Date);
|
523
|
+
record('ms', now - cache.lastTime);
|
524
|
+
if (secValue > 999) {
|
525
|
+
length = frameTimes.length;
|
526
|
+
//console.log('length', length);
|
527
|
+
while (length--) {
|
528
|
+
if (frameTimes[length] < lastSec) {
|
529
|
+
break;
|
530
|
+
}
|
531
|
+
fps++;
|
532
|
+
}
|
533
|
+
//console.log('fps', fps);
|
534
|
+
record('fps', fps);
|
535
|
+
memoryNS && record('mem', memoryNS.memory.usedJSHeapSize / 1048576);
|
536
|
+
cache.frameTimes = frameTimes.slice(length);
|
537
|
+
cache.lastSecond = now;
|
538
|
+
}
|
539
|
+
// render instances
|
540
|
+
each(xStats.subclasses, function(subclass) {
|
541
|
+
var canvas = subclass.canvas,
|
542
|
+
mode = subclass.mode,
|
543
|
+
entry = data[mode][0];
|
544
|
+
|
545
|
+
if (entry && (mode == 'ms' || cache.lastSecond == now)) {
|
546
|
+
setTitle(subclass, entry.value);
|
547
|
+
setBar(subclass, canvas.insertBefore(canvas.lastChild, canvas.firstChild), entry.percent);
|
548
|
+
}
|
549
|
+
});
|
550
|
+
}
|
551
|
+
else {
|
552
|
+
cache.lastSecond = now;
|
553
|
+
}
|
554
|
+
cache.lastTime = now;
|
555
|
+
}
|
556
|
+
|
557
|
+
/*--------------------------------------------------------------------------*/
|
558
|
+
|
559
|
+
/**
|
560
|
+
* An array of xStat instances.
|
561
|
+
*
|
562
|
+
* @static
|
563
|
+
* @memberOf xStats
|
564
|
+
* @type Array
|
565
|
+
*/
|
566
|
+
xStats.subclasses = [];
|
567
|
+
|
568
|
+
/*--------------------------------------------------------------------------*/
|
569
|
+
|
570
|
+
extend(xStats.prototype, {
|
571
|
+
|
572
|
+
/**
|
573
|
+
* The height of the chart (px).
|
574
|
+
*
|
575
|
+
* @memberOf xStats
|
576
|
+
* @type Number
|
577
|
+
*/
|
578
|
+
'height': 48,
|
579
|
+
|
580
|
+
/**
|
581
|
+
* The width of the chart (px).
|
582
|
+
*
|
583
|
+
* @memberOf xStats
|
584
|
+
* @type Number
|
585
|
+
*/
|
586
|
+
'width': 94,
|
587
|
+
|
588
|
+
/**
|
589
|
+
* The inner padding of the chart that doesn't affect dimensions (px).
|
590
|
+
*
|
591
|
+
* @memberOf xStats
|
592
|
+
* @type Number
|
593
|
+
*/
|
594
|
+
'padding': 3,
|
595
|
+
|
596
|
+
/**
|
597
|
+
* A flag to indicate if the chart is locked at its current display mode.
|
598
|
+
*
|
599
|
+
* @memberOf xStats
|
600
|
+
* @type Boolean
|
601
|
+
*/
|
602
|
+
'locked': false,
|
603
|
+
|
604
|
+
/**
|
605
|
+
* The charts current display mode (fps, ms, mem).
|
606
|
+
*
|
607
|
+
* @memberOf xStats
|
608
|
+
* @type String
|
609
|
+
*/
|
610
|
+
'mode': 'fps',
|
611
|
+
|
612
|
+
/**
|
613
|
+
* The rate at which the "sample" event is emitted (secs).
|
614
|
+
*
|
615
|
+
* @memberOf xStats
|
616
|
+
* @type Number
|
617
|
+
*/
|
618
|
+
'sampleRate': 0,
|
619
|
+
|
620
|
+
/**
|
621
|
+
* Alias of [`xStats#addListener`](#xStats:addListener).
|
622
|
+
*
|
623
|
+
* @memberOf xStats
|
624
|
+
* @type Function
|
625
|
+
*/
|
626
|
+
'on': addListener,
|
627
|
+
|
628
|
+
/**
|
629
|
+
* The "frames per second" display mode options object.
|
630
|
+
*
|
631
|
+
* @memberOf xStats
|
632
|
+
* @type Object
|
633
|
+
*/
|
634
|
+
'fps': {
|
635
|
+
|
636
|
+
/**
|
637
|
+
* The background color of the chart for the display mode.
|
638
|
+
*
|
639
|
+
* @memberOf xStats#fps
|
640
|
+
* @type String
|
641
|
+
*/
|
642
|
+
'bg': '#282845',
|
643
|
+
|
644
|
+
/**
|
645
|
+
* The foreground color of the chart for the display mode.
|
646
|
+
*
|
647
|
+
* @memberOf xStats#fps
|
648
|
+
* @type String
|
649
|
+
*/
|
650
|
+
'fg': '#1affff'
|
651
|
+
},
|
652
|
+
|
653
|
+
/**
|
654
|
+
* The "millisecond" display mode options object.
|
655
|
+
*
|
656
|
+
* @memberOf xStats
|
657
|
+
* @type Object
|
658
|
+
*/
|
659
|
+
'ms': {
|
660
|
+
|
661
|
+
/**
|
662
|
+
* The background color of the chart for the display mode.
|
663
|
+
*
|
664
|
+
* @memberOf xStats#ms
|
665
|
+
* @type String
|
666
|
+
*/
|
667
|
+
'bg': '#284528',
|
668
|
+
|
669
|
+
/**
|
670
|
+
* The foreground color of the chart for the display mode.
|
671
|
+
*
|
672
|
+
* @memberOf xStats#ms
|
673
|
+
* @type String
|
674
|
+
*/
|
675
|
+
'fg': '#1aff1a'
|
676
|
+
},
|
677
|
+
|
678
|
+
/**
|
679
|
+
* The "memory" display mode options object.
|
680
|
+
*
|
681
|
+
* @memberOf xStats
|
682
|
+
* @type Object
|
683
|
+
*/
|
684
|
+
'mem': {
|
685
|
+
|
686
|
+
/**
|
687
|
+
* The background color of the chart for the display mode.
|
688
|
+
*
|
689
|
+
* @memberOf xStats#mem
|
690
|
+
* @type String
|
691
|
+
*/
|
692
|
+
'bg': '#452831',
|
693
|
+
|
694
|
+
/**
|
695
|
+
* The foreground color of the chart for the display mode.
|
696
|
+
*
|
697
|
+
* @memberOf xStats#mem
|
698
|
+
* @type String
|
699
|
+
*/
|
700
|
+
'fg': '#ff1a8d'
|
701
|
+
},
|
702
|
+
|
703
|
+
// registers a single listener
|
704
|
+
'addListener': addListener,
|
705
|
+
|
706
|
+
// executes listeners of a specified type
|
707
|
+
'emit': emit,
|
708
|
+
|
709
|
+
// removes all listeners of a specified type
|
710
|
+
'removeAllListeners': removeAllListeners,
|
711
|
+
|
712
|
+
// removes a single listener
|
713
|
+
'removeListener': removeListener
|
714
|
+
});
|
715
|
+
|
716
|
+
/*--------------------------------------------------------------------------*/
|
717
|
+
|
718
|
+
/**
|
719
|
+
* The event type.
|
720
|
+
*
|
721
|
+
* @memberOf xStats.Event
|
722
|
+
* @type String
|
723
|
+
*/
|
724
|
+
Event.prototype.type = '';
|
725
|
+
|
726
|
+
/*--------------------------------------------------------------------------*/
|
727
|
+
|
728
|
+
// expose Event
|
729
|
+
xStats.Event = Event;
|
730
|
+
|
731
|
+
// expose xStats
|
732
|
+
// use square bracket notation so Closure Compiler won't munge `xStats`
|
733
|
+
// http://code.google.com/closure/compiler/docs/api-tutorial3.html#export
|
734
|
+
window['xStats'] = xStats;
|
735
|
+
|
736
|
+
// ensure we can read memory info
|
737
|
+
memoryNS = memoryNS && !!memoryNS.memory.usedJSHeapSize && memoryNS;
|
738
|
+
|
739
|
+
// start recording
|
740
|
+
setInterval(update, 1e3 / 60);
|
741
|
+
|
742
|
+
// start sampling (once every two seconds)
|
743
|
+
setInterval(function() {
|
744
|
+
var data = cache.data,
|
745
|
+
fps = data.fps[0],
|
746
|
+
mem = data.mem[0],
|
747
|
+
ms = data.ms[0];
|
748
|
+
|
749
|
+
each(xStats.subclasses, function(subclass) {
|
750
|
+
subclass.emit('sample', {
|
751
|
+
'fps': fps && fps.value,
|
752
|
+
'mem': mem && mem.value,
|
753
|
+
'ms': ms && ms.value
|
754
|
+
});
|
755
|
+
});
|
756
|
+
}, 2e3);
|
757
|
+
|
758
|
+
// shared CSS
|
759
|
+
appendCSS(
|
760
|
+
'.xstats div{position:absolute;overflow:hidden}' +
|
761
|
+
'.xstats p{margin:0;overflow:hidden;font-family:sans-serif;-webkit-text-size-adjust:100%}' +
|
762
|
+
'.xstats ul{margin:0;padding:0;list-style:none;overflow:hidden}' +
|
763
|
+
'.xstats li{float:right;height:100%;margin-left:-4px}' +
|
764
|
+
'.xstats .bg{opacity:.5;filter:alpha(opacity=50)}' +
|
765
|
+
'.xstats{cursor:pointer;-webkit-user-select:none;-khtml-user-select:none;-moz-user-select:none;-o-user-select:none;user-select:none}');
|
766
|
+
|
767
|
+
}(this, this.document));
|