├── LICENSE ├── README.md ├── demo ├── disc.png ├── header.jpg ├── index.html ├── rStats.css └── three.min.js ├── package.json └── src ├── rStats.extras.js └── rStats.js /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2014 Jaume Sanchez Elias 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in 13 | all copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 21 | THE SOFTWARE. -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # rStats 2 | 3 | rStats aims to provide a way of measuring and visualizing performance of your code, mainly in apps based on an update loop, like games or interactive experiences. 4 | 5 | You can read more here [Introduction and demos](http://spite.github.io/rstats) 6 | 7 | # Credits 8 | 9 | Jaume Sanchez Elias [@thespite](http://www.twitter.com/thespite) 10 | [www.clicktorelease.com](http://www.clicktorelease.com) -------------------------------------------------------------------------------- /demo/disc.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/spite/rstats/7b10eba2c14e27b5dfa575614abbb49241427ac5/demo/disc.png -------------------------------------------------------------------------------- /demo/header.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/spite/rstats/7b10eba2c14e27b5dfa575614abbb49241427ac5/demo/header.jpg -------------------------------------------------------------------------------- /demo/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | rStats test 6 | 7 | 8 | 54 | 55 | 56 |
57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 395 | 396 | 397 | 398 | -------------------------------------------------------------------------------- /demo/rStats.css: -------------------------------------------------------------------------------- 1 | .alarm{ 2 | color: #b70000; 3 | text-shadow: 0 0 0 #b70000, 4 | 0 0 1px #fff, 5 | 0 0 1px #fff, 6 | 0 0 2px #fff, 7 | 0 0 2px #fff, 8 | 0 0 3px #fff, 9 | 0 0 3px #fff, 10 | 0 0 4px #fff, 11 | 0 0 4px #fff; 12 | } 13 | 14 | .rs-base{ 15 | position: absolute; 16 | z-index: 10000; 17 | padding: 10px; 18 | background-color: #222; 19 | font-size: 10px; 20 | line-height: 1.2em; 21 | width: 350px; 22 | font-family: 'Roboto Condensed', tahoma, sans-serif; 23 | left: 0; 24 | top: 0; 25 | overflow: hidden; 26 | } 27 | 28 | .rs-base h1{ 29 | margin: 0; 30 | padding: 0; 31 | font-size: 1.4em; 32 | color: #fff; 33 | margin-bottom: 5px; 34 | cursor: pointer; 35 | } 36 | 37 | .rs-base div.rs-group{ 38 | margin-bottom: 10px; 39 | } 40 | 41 | .rs-base div.rs-group.hidden{ 42 | display: none; 43 | } 44 | 45 | .rs-base div.rs-fraction{ 46 | position: relative; 47 | margin-bottom: 5px; 48 | } 49 | 50 | .rs-base div.rs-fraction p{ 51 | width: 120px; 52 | text-align: right; 53 | margin: 0; 54 | padding: 0; 55 | } 56 | 57 | .rs-base div.rs-legend{ 58 | position: absolute; 59 | line-height: 1em; 60 | } 61 | 62 | .rs-base div.rs-counter-base{ 63 | position: relative; 64 | margin: 2px 0; 65 | height: 1em; 66 | } 67 | 68 | .rs-base span.rs-counter-id{ 69 | position: absolute; 70 | left: 0; 71 | top: 0; 72 | } 73 | 74 | .rs-base div.rs-counter-value{ 75 | position: absolute; 76 | left: 90px; 77 | width: 30px; 78 | height: 1em; 79 | top: 0; 80 | text-align: right; 81 | } 82 | 83 | .rs-base canvas.rs-canvas{ 84 | position: absolute; 85 | right: 0; 86 | } 87 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "rstatsjs", 3 | "version": "1.0.1", 4 | "description": "JavaScript Performance Monitor", 5 | "main": "index.js", 6 | "scripts": { 7 | "test": "echo \"Error: no test specified\" && exit 1" 8 | }, 9 | "repository": { 10 | "type": "git", 11 | "url": "git+https://github.com/spite/rstats.git" 12 | }, 13 | "keywords": [ 14 | "stats" 15 | ], 16 | "author": "Jaume Sanchez (https://www.clicktorelease.com)", 17 | "license": "MIT", 18 | "bugs": { 19 | "url": "https://github.com/spite/rstats/issues" 20 | }, 21 | "homepage": "https://github.com/spite/rstats#readme" 22 | } 23 | -------------------------------------------------------------------------------- /src/rStats.extras.js: -------------------------------------------------------------------------------- 1 | window.glStats = function () { 2 | 3 | var _rS = null; 4 | 5 | var _totalDrawArraysCalls = 0, 6 | _totalDrawElementsCalls = 0, 7 | _totalUseProgramCalls = 0, 8 | _totalFaces = 0, 9 | _totalVertices = 0, 10 | _totalPoints = 0, 11 | _totalBindTexures = 0; 12 | 13 | function _h ( f, c ) { 14 | return function () { 15 | c.apply( this, arguments ); 16 | f.apply( this, arguments ); 17 | }; 18 | } 19 | 20 | WebGLRenderingContext.prototype.drawArrays = _h( WebGLRenderingContext.prototype.drawArrays, function () { 21 | _totalDrawArraysCalls++; 22 | if ( arguments[ 0 ] == this.POINTS ) _totalPoints += arguments[ 2 ]; 23 | else _totalVertices += arguments[ 2 ]; 24 | } ); 25 | 26 | WebGLRenderingContext.prototype.drawElements = _h( WebGLRenderingContext.prototype.drawElements, function () { 27 | _totalDrawElementsCalls++; 28 | _totalFaces += arguments[ 1 ] / 3; 29 | _totalVertices += arguments[ 1 ]; 30 | } ); 31 | 32 | WebGLRenderingContext.prototype.useProgram = _h( WebGLRenderingContext.prototype.useProgram, function () { 33 | _totalUseProgramCalls++; 34 | } ); 35 | 36 | WebGLRenderingContext.prototype.bindTexture = _h( WebGLRenderingContext.prototype.bindTexture, function () { 37 | _totalBindTexures++; 38 | } ); 39 | 40 | var _values = { 41 | allcalls: { 42 | over: 3000, 43 | caption: 'Calls (hook)' 44 | }, 45 | drawelements: { 46 | caption: 'drawElements (hook)' 47 | }, 48 | drawarrays: { 49 | caption: 'drawArrays (hook)' 50 | } 51 | }; 52 | 53 | var _groups = [ { 54 | caption: 'WebGL', 55 | values: [ 'allcalls', 'drawelements', 'drawarrays', 'useprogram', 'bindtexture', 'glfaces', 'glvertices', 'glpoints' ] 56 | } ]; 57 | 58 | var _fractions = [ { 59 | base: 'allcalls', 60 | steps: [ 'drawelements', 'drawarrays' ] 61 | } ]; 62 | 63 | function _update () { 64 | _rS( 'allcalls' ).set( _totalDrawArraysCalls + _totalDrawElementsCalls ); 65 | _rS( 'drawElements' ).set( _totalDrawElementsCalls ); 66 | _rS( 'drawArrays' ).set( _totalDrawArraysCalls ); 67 | _rS( 'bindTexture' ).set( _totalBindTexures ); 68 | _rS( 'useProgram' ).set( _totalUseProgramCalls ); 69 | _rS( 'glfaces' ).set( _totalFaces ); 70 | _rS( 'glvertices' ).set( _totalVertices ); 71 | _rS( 'glpoints' ).set( _totalPoints ); 72 | } 73 | 74 | function _start () { 75 | _totalDrawArraysCalls = 0; 76 | _totalDrawElementsCalls = 0; 77 | _totalUseProgramCalls = 0; 78 | _totalFaces = 0; 79 | _totalVertices = 0; 80 | _totalPoints = 0; 81 | _totalBindTexures = 0; 82 | } 83 | 84 | function _end () {} 85 | 86 | function _attach ( r ) { 87 | _rS = r; 88 | } 89 | 90 | return { 91 | update: _update, 92 | start: _start, 93 | end: _end, 94 | attach: _attach, 95 | values: _values, 96 | groups: _groups, 97 | fractions: _fractions 98 | }; 99 | 100 | }; 101 | 102 | window.threeStats = function ( renderer ) { 103 | 104 | var _rS = null; 105 | 106 | var _values = { 107 | 'renderer.info.memory.geometries': { 108 | caption: 'Geometries' 109 | }, 110 | 'renderer.info.memory.textures': { 111 | caption: 'Textures' 112 | }, 113 | 'renderer.info.programs': { 114 | caption: 'Programs' 115 | }, 116 | 'renderer.info.render.calls': { 117 | caption: 'Calls' 118 | }, 119 | 'renderer.info.render.faces': { 120 | caption: 'Faces', 121 | over: 1000 122 | }, 123 | 'renderer.info.render.points': { 124 | caption: 'Points' 125 | }, 126 | 'renderer.info.render.vertices': { 127 | caption: 'Vertices' 128 | } 129 | }; 130 | 131 | var _groups = [ { 132 | caption: 'Three.js - Memory', 133 | values: [ 'renderer.info.memory.geometries', 'renderer.info.programs', 'renderer.info.memory.textures' ] 134 | }, { 135 | caption: 'Three.js - Render', 136 | values: [ 'renderer.info.render.calls', 'renderer.info.render.faces', 'renderer.info.render.points', 'renderer.info.render.vertices' ] 137 | } ]; 138 | 139 | var _fractions = []; 140 | 141 | function _update () { 142 | 143 | _rS( 'renderer.info.memory.geometries' ).set( renderer.info.memory.geometries ); 144 | //_rS( 'renderer.info.programs' ).set( renderer.info.programs.length ); 145 | _rS( 'renderer.info.memory.textures' ).set( renderer.info.memory.textures ); 146 | _rS( 'renderer.info.render.calls' ).set( renderer.info.render.calls ); 147 | _rS( 'renderer.info.render.faces' ).set( renderer.info.render.faces ); 148 | _rS( 'renderer.info.render.points' ).set( renderer.info.render.points ); 149 | _rS( 'renderer.info.render.vertices' ).set( renderer.info.render.vertices ); 150 | 151 | } 152 | 153 | function _start () {} 154 | 155 | function _end () {} 156 | 157 | function _attach ( r ) { 158 | _rS = r; 159 | } 160 | 161 | return { 162 | update: _update, 163 | start: _start, 164 | end: _end, 165 | attach: _attach, 166 | values: _values, 167 | groups: _groups, 168 | fractions: _fractions 169 | }; 170 | 171 | }; 172 | 173 | /* 174 | * From https://github.com/paulirish/memory-stats.js 175 | */ 176 | 177 | window.BrowserStats = function () { 178 | 179 | var _rS = null; 180 | 181 | var _usedJSHeapSize = 0, 182 | _totalJSHeapSize = 0; 183 | 184 | var memory = { 185 | usedJSHeapSize: 0, 186 | totalJSHeapSize: 0 187 | }; 188 | 189 | if ( window.performance && performance.memory ) 190 | memory = performance.memory; 191 | 192 | if ( memory.totalJSHeapSize === 0 ) { 193 | console.warn( 'totalJSHeapSize === 0... performance.memory is only available in Chrome .' ); 194 | } 195 | 196 | var _values = { 197 | memory: { 198 | caption: 'Used Memory', 199 | average: true, 200 | avgMs: 1000, 201 | over: 22 202 | }, 203 | total: { 204 | caption: 'Total Memory' 205 | } 206 | }; 207 | 208 | var _groups = [ { 209 | caption: 'Browser', 210 | values: [ 'memory', 'total' ] 211 | } ]; 212 | 213 | var _fractions = [ { 214 | base: 'total', 215 | steps: [ 'memory' ] 216 | } ]; 217 | 218 | var log1024 = Math.log( 1024 ); 219 | 220 | function _size ( v ) { 221 | 222 | var precision = 100; //Math.pow(10, 2); 223 | var i = Math.floor( Math.log( v ) / log1024 ); 224 | if( v === 0 ) i = 1; 225 | return Math.round( v * precision / Math.pow( 1024, i ) ) / precision; // + ' ' + sizes[i]; 226 | 227 | } 228 | 229 | function _update () { 230 | _usedJSHeapSize = _size( memory.usedJSHeapSize ); 231 | _totalJSHeapSize = _size( memory.totalJSHeapSize ); 232 | 233 | _rS( 'memory' ).set( _usedJSHeapSize ); 234 | _rS( 'total' ).set( _totalJSHeapSize ); 235 | } 236 | 237 | function _start () { 238 | _usedJSHeapSize = 0; 239 | } 240 | 241 | function _end () {} 242 | 243 | function _attach ( r ) { 244 | _rS = r; 245 | } 246 | 247 | return { 248 | update: _update, 249 | start: _start, 250 | end: _end, 251 | attach: _attach, 252 | values: _values, 253 | groups: _groups, 254 | fractions: _fractions 255 | }; 256 | 257 | }; 258 | 259 | if (typeof module === 'object') { 260 | module.exports = { 261 | glStats: window.glStats, 262 | threeStats: window.threeStats, 263 | BrowserStats: window.BrowserStats 264 | }; 265 | } 266 | -------------------------------------------------------------------------------- /src/rStats.js: -------------------------------------------------------------------------------- 1 | // performance.now() polyfill from https://gist.github.com/paulirish/5438650 2 | 'use strict'; 3 | 4 | ( function () { 5 | 6 | // prepare base perf object 7 | if ( typeof window.performance === 'undefined' ) { 8 | window.performance = {}; 9 | } 10 | 11 | if ( !window.performance.now ) { 12 | 13 | var nowOffset = Date.now(); 14 | 15 | if ( performance.timing && performance.timing.navigationStart ) { 16 | nowOffset = performance.timing.navigationStart; 17 | } 18 | 19 | window.performance.now = function now () { 20 | return Date.now() - nowOffset; 21 | }; 22 | 23 | } 24 | 25 | if( !window.performance.mark ) { 26 | window.performance.mark = function(){} 27 | } 28 | 29 | if( !window.performance.measure ) { 30 | window.performance.measure = function(){} 31 | } 32 | 33 | } )(); 34 | 35 | /** 36 | * @class rStats 37 | * @param {rStats~Settings} [settings] Settings for the rStats instance. 38 | */ 39 | 40 | /** 41 | * @typedef {Object} rStats~Settings 42 | * @property {Array.} [colours] An array of CSS colour values. 43 | * @property {String} [CSSPath=''] Base URL where rStats.css is located. 44 | * @property {Array.} [css] URLs of CSS or font files to import. 45 | * @property {Object.} [values] Properties to use for each counter. 46 | * @property {Array.} [groups] Define groups of counters. 47 | * @property {Array.} [fractions] Define stacked counters. 48 | * @property {Array.} [plugins] Additional plugins. 49 | */ 50 | 51 | /** 52 | * @typedef {Object} rStats~CounterProperties 53 | * @property {String} [caption] Caption for this counter. 54 | * @property {Boolean} [average=false] Whether the values should be averaged. 55 | * @property {Number} [avgMs=1000] Duration for which the values should be averaged. 56 | * @property {Number} [below] Value below which the graph should be highlighted. 57 | * @property {Number} [over] Value over which the graph should be highlighted. 58 | * @property {Boolean} [interpolate=true] Whether framerate should be interpolated. 59 | */ 60 | 61 | window.rStats = function rStats ( settings ) { 62 | 63 | function iterateKeys ( array, callback ) { 64 | var keys = Object.keys( array ); 65 | for ( var j = 0, l = keys.length; j < l; j++ ) { 66 | callback( keys[ j ] ); 67 | } 68 | } 69 | 70 | function importCSS ( url ) { 71 | 72 | var element = document.createElement( 'link' ); 73 | element.href = url; 74 | element.rel = 'stylesheet'; 75 | element.type = 'text/css'; 76 | document.getElementsByTagName( 'head' )[ 0 ].appendChild( element ); 77 | 78 | } 79 | 80 | var _settings = settings || {}; 81 | var _colours = _settings.colours || [ '#850700', '#c74900', '#fcb300', '#284280', '#4c7c0c' ]; 82 | 83 | var _cssFont = 'https://fonts.googleapis.com/css?family=Roboto+Condensed:400,700,300'; 84 | var _cssRStats = ( _settings.CSSPath ? _settings.CSSPath : '' ) + 'rStats.css'; 85 | 86 | var _css = _settings.css || [ _cssFont, _cssRStats ]; 87 | _css.forEach(function (uri) { 88 | importCSS( uri ); 89 | }); 90 | 91 | if ( !_settings.values ) _settings.values = {}; 92 | 93 | var _base, _div, _elHeight = 10, _elWidth = 200; 94 | var _perfCounters = {}; 95 | 96 | 97 | function Graph ( _dom, _id, _defArg ) { 98 | 99 | var _def = _defArg || {}; 100 | var _canvas = document.createElement( 'canvas' ), 101 | _ctx = _canvas.getContext( '2d' ), 102 | _max = 0, 103 | _current = 0; 104 | 105 | var c = _def.color ? _def.color : '#666666'; 106 | 107 | var _dotCanvas = document.createElement( 'canvas' ), 108 | _dotCtx = _dotCanvas.getContext( '2d' ); 109 | _dotCanvas.width = 1; 110 | _dotCanvas.height = 2 * _elHeight; 111 | _dotCtx.fillStyle = '#444444'; 112 | _dotCtx.fillRect( 0, 0, 1, 2 * _elHeight ); 113 | _dotCtx.fillStyle = c; 114 | _dotCtx.fillRect( 0, _elHeight, 1, _elHeight ); 115 | _dotCtx.fillStyle = '#ffffff'; 116 | _dotCtx.globalAlpha = 0.5; 117 | _dotCtx.fillRect( 0, _elHeight, 1, 1 ); 118 | _dotCtx.globalAlpha = 1; 119 | 120 | var _alarmCanvas = document.createElement( 'canvas' ), 121 | _alarmCtx = _alarmCanvas.getContext( '2d' ); 122 | _alarmCanvas.width = 1; 123 | _alarmCanvas.height = 2 * _elHeight; 124 | _alarmCtx.fillStyle = '#444444'; 125 | _alarmCtx.fillRect( 0, 0, 1, 2 * _elHeight ); 126 | _alarmCtx.fillStyle = '#b70000'; 127 | _alarmCtx.fillRect( 0, _elHeight, 1, _elHeight ); 128 | _alarmCtx.globalAlpha = 0.5; 129 | _alarmCtx.fillStyle = '#ffffff'; 130 | _alarmCtx.fillRect( 0, _elHeight, 1, 1 ); 131 | _alarmCtx.globalAlpha = 1; 132 | 133 | function _init () { 134 | 135 | _canvas.width = _elWidth; 136 | _canvas.height = _elHeight; 137 | _canvas.style.width = _canvas.width + 'px'; 138 | _canvas.style.height = _canvas.height + 'px'; 139 | _canvas.className = 'rs-canvas'; 140 | _dom.appendChild( _canvas ); 141 | 142 | _ctx.fillStyle = '#444444'; 143 | _ctx.fillRect( 0, 0, _canvas.width, _canvas.height ); 144 | 145 | } 146 | 147 | function _draw ( v, alarm ) { 148 | _current += ( v - _current ) * 0.1; 149 | _max *= 0.99; 150 | if ( _current > _max ) _max = _current; 151 | _ctx.drawImage( _canvas, 1, 0, _canvas.width - 1, _canvas.height, 0, 0, _canvas.width - 1, _canvas.height ); 152 | if ( alarm ) { 153 | _ctx.drawImage( _alarmCanvas, _canvas.width - 1, _canvas.height - _current * _canvas.height / _max - _elHeight ); 154 | } else { 155 | _ctx.drawImage( _dotCanvas, _canvas.width - 1, _canvas.height - _current * _canvas.height / _max - _elHeight ); 156 | } 157 | } 158 | 159 | _init(); 160 | 161 | return { 162 | draw: _draw 163 | }; 164 | 165 | } 166 | 167 | function StackGraph ( _dom, _num ) { 168 | 169 | var _canvas = document.createElement( 'canvas' ), 170 | _ctx = _canvas.getContext( '2d' ); 171 | 172 | function _init () { 173 | 174 | _canvas.width = _elWidth; 175 | _canvas.height = _elHeight * _num; 176 | _canvas.style.width = _canvas.width + 'px'; 177 | _canvas.style.height = _canvas.height + 'px'; 178 | _canvas.className = 'rs-canvas'; 179 | _dom.appendChild( _canvas ); 180 | 181 | _ctx.fillStyle = '#444444'; 182 | _ctx.fillRect( 0, 0, _canvas.width, _canvas.height ); 183 | 184 | } 185 | 186 | function _draw ( v ) { 187 | _ctx.drawImage( _canvas, 1, 0, _canvas.width - 1, _canvas.height, 0, 0, _canvas.width - 1, _canvas.height ); 188 | var th = 0; 189 | iterateKeys( v, function ( j ) { 190 | var h = v[ j ] * _canvas.height; 191 | _ctx.fillStyle = _colours[ j ]; 192 | _ctx.fillRect( _canvas.width - 1, th, 1, h ); 193 | th += h; 194 | } ); 195 | } 196 | 197 | _init(); 198 | 199 | return { 200 | draw: _draw 201 | }; 202 | 203 | } 204 | 205 | function PerfCounter ( id, group ) { 206 | 207 | var _id = id, 208 | _time, 209 | _value = 0, 210 | _total = 0, 211 | _averageValue = 0, 212 | _accumValue = 0, 213 | _accumStart = performance.now(), 214 | _accumSamples = 0, 215 | _dom = document.createElement( 'div' ), 216 | _spanId = document.createElement( 'span' ), 217 | _spanValue = document.createElement( 'div' ), 218 | _spanValueText = document.createTextNode( '' ), 219 | _def = _settings ? _settings.values[ _id.toLowerCase() ] : null, 220 | _graph = new Graph( _dom, _id, _def ), 221 | _started = false; 222 | 223 | _spanId.className = 'rs-counter-id'; 224 | _spanId.textContent = ( _def && _def.caption ) ? _def.caption : _id; 225 | 226 | _spanValue.className = 'rs-counter-value'; 227 | _spanValue.appendChild( _spanValueText ); 228 | 229 | _dom.appendChild( _spanId ); 230 | _dom.appendChild( _spanValue ); 231 | if ( group ) group.div.appendChild( _dom ); 232 | else _div.appendChild( _dom ); 233 | 234 | _time = performance.now(); 235 | 236 | function _average ( v ) { 237 | if ( _def && _def.average ) { 238 | _accumValue += v; 239 | _accumSamples++; 240 | var t = performance.now(); 241 | if ( t - _accumStart >= ( _def.avgMs || 1000 ) ) { 242 | _averageValue = _accumValue / _accumSamples; 243 | _accumValue = 0; 244 | _accumStart = t; 245 | _accumSamples = 0; 246 | } 247 | } 248 | } 249 | 250 | function _start () { 251 | _time = performance.now(); 252 | if( _settings.userTimingAPI ) performance.mark( _id + '-start' ); 253 | _started = true; 254 | } 255 | 256 | function _end () { 257 | _value = performance.now() - _time; 258 | if( _settings.userTimingAPI ) { 259 | performance.mark( _id + '-end' ); 260 | if( _started ) { 261 | performance.measure( _id, _id + '-start', _id + '-end' ); 262 | } 263 | } 264 | _average( _value ); 265 | } 266 | 267 | function _tick () { 268 | _end(); 269 | _start(); 270 | } 271 | 272 | function _draw () { 273 | var v = ( _def && _def.average ) ? _averageValue : _value; 274 | _spanValueText.nodeValue = Math.round( v * 100 ) / 100; 275 | var a = ( _def && ( ( _def.below && _value < _def.below ) || ( _def.over && _value > _def.over ) ) ); 276 | _graph.draw( _value, a ); 277 | _dom.className = a ? 'rs-counter-base alarm' : 'rs-counter-base'; 278 | 279 | } 280 | 281 | function _frame () { 282 | var t = performance.now(); 283 | var e = t - _time; 284 | _total++; 285 | if ( e > 1000 ) { 286 | if ( _def && _def.interpolate === false ) { 287 | _value = _total; 288 | } else { 289 | _value = _total * 1000 / e; 290 | } 291 | _total = 0; 292 | _time = t; 293 | _average( _value ); 294 | } 295 | } 296 | 297 | function _set ( v ) { 298 | _value = v; 299 | _average( _value ); 300 | } 301 | 302 | return { 303 | set: _set, 304 | start: _start, 305 | tick: _tick, 306 | end: _end, 307 | frame: _frame, 308 | value: function () { 309 | return _value; 310 | }, 311 | draw: _draw 312 | }; 313 | 314 | } 315 | 316 | function sample () { 317 | 318 | var _value = 0; 319 | 320 | function _set ( v ) { 321 | _value = v; 322 | } 323 | 324 | return { 325 | set: _set, 326 | value: function () { 327 | return _value; 328 | } 329 | }; 330 | 331 | } 332 | 333 | function _perf ( idArg ) { 334 | 335 | var id = idArg.toLowerCase(); 336 | if ( id === undefined ) id = 'default'; 337 | if ( _perfCounters[ id ] ) return _perfCounters[ id ]; 338 | 339 | var group = null; 340 | if ( _settings && _settings.groups ) { 341 | iterateKeys( _settings.groups, function ( j ) { 342 | var g = _settings.groups[ parseInt( j, 10 ) ]; 343 | if ( !group && g.values.indexOf( id.toLowerCase() ) !== -1 ) { 344 | group = g; 345 | } 346 | } ); 347 | } 348 | 349 | var p = new PerfCounter( id, group ); 350 | _perfCounters[ id ] = p; 351 | return p; 352 | 353 | } 354 | 355 | function _init () { 356 | 357 | if ( _settings.plugins ) { 358 | if ( !_settings.values ) _settings.values = {}; 359 | if ( !_settings.groups ) _settings.groups = []; 360 | if ( !_settings.fractions ) _settings.fractions = []; 361 | for ( var j = 0; j < _settings.plugins.length; j++ ) { 362 | _settings.plugins[ j ].attach( _perf ); 363 | iterateKeys( _settings.plugins[ j ].values, function ( k ) { 364 | _settings.values[ k ] = _settings.plugins[ j ].values[ k ]; 365 | } ); 366 | _settings.groups = _settings.groups.concat( _settings.plugins[ j ].groups ); 367 | _settings.fractions = _settings.fractions.concat( _settings.plugins[ j ].fractions ); 368 | } 369 | } else { 370 | _settings.plugins = {}; 371 | } 372 | 373 | _base = document.createElement( 'div' ); 374 | _base.className = 'rs-base'; 375 | _div = document.createElement( 'div' ); 376 | _div.className = 'rs-container'; 377 | _div.style.height = 'auto'; 378 | _base.appendChild( _div ); 379 | document.body.appendChild( _base ); 380 | 381 | if ( !_settings ) return; 382 | 383 | if ( _settings.groups ) { 384 | iterateKeys( _settings.groups, function ( j ) { 385 | var g = _settings.groups[ parseInt( j, 10 ) ]; 386 | var div = document.createElement( 'div' ); 387 | div.className = 'rs-group'; 388 | g.div = div; 389 | var h1 = document.createElement( 'h1' ); 390 | h1.textContent = g.caption; 391 | h1.addEventListener( 'click', function ( e ) { 392 | this.classList.toggle( 'hidden' ); 393 | e.preventDefault(); 394 | }.bind( div ) ); 395 | _div.appendChild( h1 ); 396 | _div.appendChild( div ); 397 | } ); 398 | } 399 | 400 | if ( _settings.fractions ) { 401 | iterateKeys( _settings.fractions, function ( j ) { 402 | var f = _settings.fractions[ parseInt( j, 10 ) ]; 403 | var div = document.createElement( 'div' ); 404 | div.className = 'rs-fraction'; 405 | var legend = document.createElement( 'div' ); 406 | legend.className = 'rs-legend'; 407 | 408 | var h = 0; 409 | iterateKeys( _settings.fractions[ j ].steps, function ( k ) { 410 | var p = document.createElement( 'p' ); 411 | p.textContent = _settings.fractions[ j ].steps[ k ]; 412 | p.style.color = _colours[ h ]; 413 | legend.appendChild( p ); 414 | h++; 415 | } ); 416 | div.appendChild( legend ); 417 | div.style.height = h * _elHeight + 'px'; 418 | f.div = div; 419 | var graph = new StackGraph( div, h ); 420 | f.graph = graph; 421 | _div.appendChild( div ); 422 | } ); 423 | } 424 | 425 | } 426 | 427 | function _update () { 428 | 429 | iterateKeys( _settings.plugins, function ( j ) { 430 | _settings.plugins[ j ].update(); 431 | } ); 432 | 433 | iterateKeys( _perfCounters, function ( j ) { 434 | _perfCounters[ j ].draw(); 435 | } ); 436 | 437 | if ( _settings && _settings.fractions ) { 438 | iterateKeys( _settings.fractions, function ( j ) { 439 | var f = _settings.fractions[ parseInt( j, 10 ) ]; 440 | var v = []; 441 | var base = _perfCounters[ f.base.toLowerCase() ]; 442 | if ( base ) { 443 | base = base.value(); 444 | iterateKeys( _settings.fractions[ j ].steps, function ( k ) { 445 | var s = _settings.fractions[ j ].steps[ parseInt( k, 10 ) ].toLowerCase(); 446 | var val = _perfCounters[ s ]; 447 | if ( val ) { 448 | v.push( val.value() / base ); 449 | } 450 | } ); 451 | } 452 | f.graph.draw( v ); 453 | } ); 454 | } 455 | 456 | /*if( _height != _div.clientHeight ) { 457 | _height = _div.clientHeight; 458 | _base.style.height = _height + 2 * _elHeight + 'px'; 459 | console.log( _base.clientHeight ); 460 | }*/ 461 | 462 | } 463 | 464 | _init(); 465 | 466 | return function ( id ) { 467 | if ( id ) return _perf( id ); 468 | return { 469 | element: _base, 470 | update: _update 471 | }; 472 | }; 473 | 474 | } 475 | 476 | if (typeof module === 'object') { 477 | module.exports = window.rStats; 478 | } 479 | --------------------------------------------------------------------------------