├── .gitignore ├── LICENSE ├── README.md ├── dist ├── angular-memory-stats.js └── angular-memory-stats.min.js ├── gulpfile.js ├── lib ├── angular-memory-stats.coffee └── memory-stats.js ├── package.json ├── test ├── index.html └── test.module.coffee ├── webpack.config.build.js └── webpack.config.js /.gitignore: -------------------------------------------------------------------------------- 1 | # Logs 2 | logs 3 | *.log 4 | 5 | # Runtime data 6 | pids 7 | *.pid 8 | *.seed 9 | 10 | # Directory for instrumented libs generated by jscoverage/JSCover 11 | lib-cov 12 | 13 | # Coverage directory used by tools like istanbul 14 | coverage 15 | 16 | # Grunt intermediate storage (http://gruntjs.com/creating-plugins#storing-task-files) 17 | .grunt 18 | 19 | # Compiled binary addons (http://nodejs.org/api/addons.html) 20 | build/Release 21 | 22 | # Dependency directory 23 | # Commenting this out is preferred by some people, see 24 | # https://www.npmjs.org/doc/misc/npm-faq.html#should-i-check-my-node_modules-folder-into-git- 25 | node_modules 26 | 27 | # Users Environment Variables 28 | .lock-wscript 29 | www/ 30 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2015 LivingObjects Open Source Platform 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 all 13 | 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 THE 21 | SOFTWARE. 22 | 23 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # angular-memory-stats 2 | 3 | [![Join the chat at https://gitter.im/livingobjects/angular-memory-stats](https://badges.gitter.im/Join%20Chat.svg)](https://gitter.im/livingobjects/angular-memory-stats?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge) 4 | 5 | This plugin is based on Paul Irish's [memory-stats](https://github.com/paulirish/memory-stats.js). 6 | 7 | ![image](http://i.imgur.com/eUCFcAH.gif) 8 | 9 | Only 3.2Kb (minified version)! 10 | 11 | ## Install 12 | 13 | ``` 14 | npm i angular-memory-stats --save 15 | ``` 16 | 17 | ### Start Chrome with `--enable-precise-memory-info` 18 | 19 | ``` 20 | # Linux 21 | google-chrome --enable-precise-memory-info --enable-memory-info 22 | 23 | #MacOS 24 | /Applications/Google\ Chrome.app/Contents/MacOS/Google\ Chrome --enable-precise-memory-info --enable-memory-info 25 | ``` 26 | 27 | Otherwise the results from performance.memory are bucketed and less useful. 28 | 29 | ### Add the module to your Angular's dependencies 30 | 31 | ``` 32 | angular.module('yourModule', [ 33 | 'angular-memory-stats' 34 | ]) 35 | ``` 36 | 37 | ### Insert the directive in the dom 38 | 39 | ``` 40 | 41 | ``` 42 | 43 | ### Config 44 | 45 | angular-memory-stats is enabled by default, if you wish to disable it use the ```angularMemoryStatsProvider``` Provider 46 | 47 | ``` 48 | angular.module('yourModule').config(function(angularMemoryStatsProvider){ 49 | // Boolean 50 | angularMemoryStatsProvider.enable(true); 51 | 52 | // topLeft, topRight, bottomLeft, bottomRight (default) 53 | angularMemoryStatsProvider.setCorner('bottomRight'); 54 | 55 | // Customize the element (By default zIndex = 1 and position = fixed) 56 | angularMemoryStatsProvider.setCss({ 57 | right: '50px', 58 | zIndex: 999999 59 | }); 60 | }); 61 | ``` 62 | 63 | ## Contribute 64 | 65 | ``` 66 | sudo npm install webpack webpack-dev-server -g 67 | npm install 68 | webpack-dev-server --port 8080 69 | ``` 70 | 71 | Open ```http://localhost:8080``` 72 | -------------------------------------------------------------------------------- /dist/angular-memory-stats.js: -------------------------------------------------------------------------------- 1 | /** 2 | * angular-memory-stats - Angular Memory Stats displays your app memory activity 3 | * @version v1.0.0-beta1 4 | * @author shprink 5 | * @link https://github.com/livingobjects/angular-memory-stats 6 | * @license MIT 7 | */ 8 | /******/ (function(modules) { // webpackBootstrap 9 | /******/ // The module cache 10 | /******/ var installedModules = {}; 11 | /******/ 12 | /******/ // The require function 13 | /******/ function __webpack_require__(moduleId) { 14 | /******/ 15 | /******/ // Check if module is in cache 16 | /******/ if(installedModules[moduleId]) 17 | /******/ return installedModules[moduleId].exports; 18 | /******/ 19 | /******/ // Create a new module (and put it into the cache) 20 | /******/ var module = installedModules[moduleId] = { 21 | /******/ exports: {}, 22 | /******/ id: moduleId, 23 | /******/ loaded: false 24 | /******/ }; 25 | /******/ 26 | /******/ // Execute the module function 27 | /******/ modules[moduleId].call(module.exports, module, module.exports, __webpack_require__); 28 | /******/ 29 | /******/ // Flag the module as loaded 30 | /******/ module.loaded = true; 31 | /******/ 32 | /******/ // Return the exports of the module 33 | /******/ return module.exports; 34 | /******/ } 35 | /******/ 36 | /******/ 37 | /******/ // expose the modules object (__webpack_modules__) 38 | /******/ __webpack_require__.m = modules; 39 | /******/ 40 | /******/ // expose the module cache 41 | /******/ __webpack_require__.c = installedModules; 42 | /******/ 43 | /******/ // __webpack_public_path__ 44 | /******/ __webpack_require__.p = ""; 45 | /******/ 46 | /******/ // Load entry module and return exports 47 | /******/ return __webpack_require__(0); 48 | /******/ }) 49 | /************************************************************************/ 50 | /******/ ([ 51 | /* 0 */ 52 | /***/ function(module, exports, __webpack_require__) { 53 | 54 | var MemoryStats, RequestAnimationFrame, module; 55 | 56 | MemoryStats = __webpack_require__(1); 57 | 58 | RequestAnimationFrame = __webpack_require__(2); 59 | 60 | module.exports = module = angular.module('angular-memory-stats', []); 61 | 62 | module.provider('angularMemoryStats', function() { 63 | var $get, cornersAvailable, defaultOptions, enable, setCorner, setCss; 64 | cornersAvailable = ["topLeft", "topRight", "bottomLeft", "bottomRight"]; 65 | defaultOptions = { 66 | isEnabled: true, 67 | corner: 'bottomRight', 68 | css: {} 69 | }; 70 | this.isEnabled = defaultOptions.isEnabled; 71 | this.corner = defaultOptions.corner; 72 | this.css = defaultOptions.css; 73 | enable = (function(_this) { 74 | return function(enable) { 75 | if (enable == null) { 76 | enable = true; 77 | } 78 | return _this.isEnabled = enable; 79 | }; 80 | })(this); 81 | setCorner = (function(_this) { 82 | return function(corner) { 83 | if (corner && cornersAvailable.indexOf(corner) > -1) { 84 | return _this.corner = corner; 85 | } 86 | }; 87 | })(this); 88 | setCss = (function(_this) { 89 | return function(css) { 90 | if (css) { 91 | return _this.css = css; 92 | } 93 | }; 94 | })(this); 95 | $get = (function(_this) { 96 | return function() { 97 | return { 98 | isEnabled: function() { 99 | return _this.isEnabled; 100 | }, 101 | getCss: function() { 102 | var corner, css; 103 | css = { 104 | position: 'fixed', 105 | zIndex: 1 106 | }; 107 | corner = _this.corner; 108 | if (["topLeft", "topRight", "bottomLeft", "bottomRight"].indexOf(corner) === -1) { 109 | corner = defaultOptions.corner; 110 | } 111 | switch (corner) { 112 | case "topLeft": 113 | css.top = '5px'; 114 | css.left = '5px'; 115 | break; 116 | case "topRight": 117 | css.top = '5px'; 118 | css.right = '5px'; 119 | break; 120 | case "bottomLeft": 121 | css.bottom = '5px'; 122 | css.left = '5px'; 123 | break; 124 | case "bottomRight": 125 | css.bottom = '5px'; 126 | css.right = '5px'; 127 | } 128 | return angular.extend(css, _this.css); 129 | } 130 | }; 131 | }; 132 | })(this); 133 | return { 134 | enable: enable, 135 | setCorner: setCorner, 136 | setCss: setCss, 137 | $get: $get 138 | }; 139 | }); 140 | 141 | module.directive('angularMemoryStats', function() { 142 | return { 143 | restrict: 'E', 144 | scope: false, 145 | controller: ["$scope", "$element", "angularMemoryStats", function($scope, $element, angularMemoryStats) { 146 | var stats, update; 147 | if (!angularMemoryStats.isEnabled()) { 148 | return; 149 | } 150 | stats = new MemoryStats(); 151 | $element.css(angularMemoryStats.getCss()); 152 | $element.append(stats.domElement); 153 | update = function() { 154 | stats.update(); 155 | return RequestAnimationFrame(update); 156 | }; 157 | return RequestAnimationFrame(update); 158 | }] 159 | }; 160 | }); 161 | 162 | 163 | /***/ }, 164 | /* 1 */ 165 | /***/ function(module, exports, __webpack_require__) { 166 | 167 | /** 168 | * @author mrdoob / http://mrdoob.com/ 169 | * @author jetienne / http://jetienne.com/ 170 | * @author paulirish / http://paulirish.com/ 171 | */ 172 | module.exports = function () { 173 | 174 | var msMin = 100; 175 | var msMax = 0; 176 | 177 | var container = document.createElement('div'); 178 | container.id = 'stats'; 179 | container.style.cssText = 'width:80px;opacity:0.9;cursor:pointer'; 180 | 181 | var msDiv = document.createElement('div'); 182 | msDiv.id = 'ms'; 183 | msDiv.style.cssText = 'padding:0 0 3px 3px;text-align:left;background-color:#020;'; 184 | container.appendChild(msDiv); 185 | 186 | var msText = document.createElement('div'); 187 | msText.id = 'msText'; 188 | msText.style.cssText = 'color:#0f0;font-family:Helvetica,Arial,sans-serif;font-size:9px;font-weight:bold;line-height:15px'; 189 | msText.innerHTML = 'Memory'; 190 | msDiv.appendChild(msText); 191 | 192 | var msGraph = document.createElement('div'); 193 | msGraph.id = 'msGraph'; 194 | msGraph.style.cssText = 'position:relative;width:74px;height:30px;background-color:#0f0'; 195 | msDiv.appendChild(msGraph); 196 | 197 | while (msGraph.children.length < 74) { 198 | 199 | var bar = document.createElement('span'); 200 | bar.style.cssText = 'width:1px;height:30px;float:left;background-color:#131'; 201 | msGraph.appendChild(bar); 202 | 203 | } 204 | 205 | var updateGraph = function (dom, height, color) { 206 | 207 | var child = dom.appendChild(dom.firstChild); 208 | child.style.height = height + 'px'; 209 | if (color) child.style.backgroundColor = color; 210 | 211 | } 212 | 213 | // polyfill usedJSHeapSize 214 | if (window.performance && !performance.memory) { 215 | performance.memory = { 216 | usedJSHeapSize: 0 217 | }; 218 | } 219 | 220 | // support of the API? 221 | if (performance.memory.totalJSHeapSize === 0) { 222 | console.warn('totalJSHeapSize === 0... performance.memory is only available in Chrome .') 223 | } 224 | 225 | // TODO, add a sanity check to see if values are bucketed. 226 | // If so, reminde user to adopt the --enable-precise-memory-info flag. 227 | // open -a "/Applications/Google Chrome.app" --args --enable-precise-memory-info 228 | 229 | var lastTime = Date.now(); 230 | var lastUsedHeap = performance.memory.usedJSHeapSize; 231 | return { 232 | domElement: container, 233 | 234 | update: function () { 235 | 236 | // refresh only 30time per second 237 | if (Date.now() - lastTime < 1000 / 30) return; 238 | lastTime = Date.now() 239 | 240 | var delta = performance.memory.usedJSHeapSize - lastUsedHeap; 241 | lastUsedHeap = performance.memory.usedJSHeapSize; 242 | var color = delta < 0 ? '#830' : '#131'; 243 | 244 | var ms = performance.memory.usedJSHeapSize; 245 | msMin = Math.min(msMin, ms); 246 | msMax = Math.max(msMax, ms); 247 | msText.textContent = "Mem: " + bytesToSize(ms, 2); 248 | 249 | var normValue = ms / (30 * 1024 * 1024); 250 | var height = Math.min(30, 30 - normValue * 30); 251 | updateGraph(msGraph, height, color); 252 | 253 | function bytesToSize(bytes, nFractDigit) { 254 | var sizes = ['Bytes', 'KB', 'MB', 'GB', 'TB']; 255 | if (bytes == 0) return 'n/a'; 256 | nFractDigit = nFractDigit !== undefined ? nFractDigit : 0; 257 | var precision = Math.pow(10, nFractDigit); 258 | var i = Math.floor(Math.log(bytes) / Math.log(1024)); 259 | return Math.round(bytes * precision / Math.pow(1024, i)) / precision + ' ' + sizes[i]; 260 | }; 261 | } 262 | 263 | } 264 | 265 | }; 266 | 267 | /***/ }, 268 | /* 2 */ 269 | /***/ function(module, exports, __webpack_require__) { 270 | 271 | var __WEBPACK_AMD_DEFINE_RESULT__;/** 272 | * requestAnimationFrame version: "0.0.17" Copyright (c) 2011-2012, Cyril Agosta ( cyril.agosta.dev@gmail.com) All Rights Reserved. 273 | * Available via the MIT license. 274 | * see: http://github.com/cagosta/requestAnimationFrame for details 275 | * 276 | * http://paulirish.com/2011/requestanimationframe-for-smart-animating/ 277 | * http://my.opera.com/emoller/blog/2011/12/20/requestanimationframe-for-smart-er-animating 278 | * requestAnimationFrame polyfill by Erik Möller. fixes from Paul Irish and Tino Zijdel 279 | * MIT license 280 | * 281 | */ 282 | 283 | 284 | ( function( global ) { 285 | 286 | 287 | ( function() { 288 | 289 | 290 | if ( global.requestAnimationFrame ) { 291 | 292 | return; 293 | 294 | } 295 | 296 | if ( global.webkitRequestAnimationFrame ) { // Chrome <= 23, Safari <= 6.1, Blackberry 10 297 | 298 | global.requestAnimationFrame = global[ 'webkitRequestAnimationFrame' ]; 299 | global.cancelAnimationFrame = global[ 'webkitCancelAnimationFrame' ] || global[ 'webkitCancelRequestAnimationFrame' ]; 300 | 301 | } 302 | 303 | // IE <= 9, Android <= 4.3, very old/rare browsers 304 | 305 | var lastTime = 0; 306 | 307 | global.requestAnimationFrame = function( callback ) { 308 | 309 | var currTime = new Date().getTime(); 310 | 311 | var timeToCall = Math.max( 0, 16 - ( currTime - lastTime ) ); 312 | 313 | var id = global.setTimeout( function() { 314 | 315 | callback( currTime + timeToCall ); 316 | 317 | }, timeToCall ); 318 | 319 | lastTime = currTime + timeToCall; 320 | 321 | return id; // return the id for cancellation capabilities 322 | 323 | }; 324 | 325 | global.cancelAnimationFrame = function( id ) { 326 | 327 | clearTimeout( id ); 328 | 329 | }; 330 | 331 | } )(); 332 | 333 | if ( true ) { 334 | 335 | !(__WEBPACK_AMD_DEFINE_RESULT__ = function() { 336 | 337 | return global.requestAnimationFrame; 338 | 339 | }.call(exports, __webpack_require__, exports, module), __WEBPACK_AMD_DEFINE_RESULT__ !== undefined && (module.exports = __WEBPACK_AMD_DEFINE_RESULT__)); 340 | 341 | } 342 | 343 | } )( window ); 344 | 345 | /***/ } 346 | /******/ ]) -------------------------------------------------------------------------------- /dist/angular-memory-stats.min.js: -------------------------------------------------------------------------------- 1 | /** 2 | * angular-memory-stats - Angular Memory Stats displays your app memory activity 3 | * @version v1.0.0-beta1 4 | * @author shprink 5 | * @link https://github.com/livingobjects/angular-memory-stats 6 | * @license MIT 7 | */ 8 | !function(e){function t(r){if(n[r])return n[r].exports;var o=n[r]={exports:{},id:r,loaded:!1};return e[r].call(o.exports,o,o.exports,t),o.loaded=!0,o.exports}var n={};return t.m=e,t.c=n,t.p="",t(0)}([function(e,t,n){var r,o,e;r=n(1),o=n(2),e.exports=e=angular.module("angular-memory-stats",[]),e.provider("angularMemoryStats",function(){var e,t,n,r,o,i;return t=["topLeft","topRight","bottomLeft","bottomRight"],n={isEnabled:!0,corner:"bottomRight",css:{}},this.isEnabled=n.isEnabled,this.corner=n.corner,this.css=n.css,r=function(e){return function(t){return null==t&&(t=!0),e.isEnabled=t}}(this),o=function(e){return function(n){return n&&t.indexOf(n)>-1?e.corner=n:void 0}}(this),i=function(e){return function(t){return t?e.css=t:void 0}}(this),e=function(e){return function(){return{isEnabled:function(){return e.isEnabled},getCss:function(){var t,r;switch(r={position:"fixed",zIndex:1},t=e.corner,-1===["topLeft","topRight","bottomLeft","bottomRight"].indexOf(t)&&(t=n.corner),t){case"topLeft":r.top="5px",r.left="5px";break;case"topRight":r.top="5px",r.right="5px";break;case"bottomLeft":r.bottom="5px",r.left="5px";break;case"bottomRight":r.bottom="5px",r.right="5px"}return angular.extend(r,e.css)}}}}(this),{enable:r,setCorner:o,setCss:i,$get:e}}),e.directive("angularMemoryStats",function(){return{restrict:"E",scope:!1,controller:["$scope","$element","angularMemoryStats",function(e,t,n){var i,a;if(n.isEnabled())return i=new r,t.css(n.getCss()),t.append(i.domElement),a=function(){return i.update(),o(a)},o(a)}]}})},function(e){e.exports=function(){var e=100,t=0,n=document.createElement("div");n.id="stats",n.style.cssText="width:80px;opacity:0.9;cursor:pointer";var r=document.createElement("div");r.id="ms",r.style.cssText="padding:0 0 3px 3px;text-align:left;background-color:#020;",n.appendChild(r);var o=document.createElement("div");o.id="msText",o.style.cssText="color:#0f0;font-family:Helvetica,Arial,sans-serif;font-size:9px;font-weight:bold;line-height:15px",o.innerHTML="Memory",r.appendChild(o);var i=document.createElement("div");for(i.id="msGraph",i.style.cssText="position:relative;width:74px;height:30px;background-color:#0f0",r.appendChild(i);i.children.length<74;){var a=document.createElement("span");a.style.cssText="width:1px;height:30px;float:left;background-color:#131",i.appendChild(a)}var s=function(e,t,n){var r=e.appendChild(e.firstChild);r.style.height=t+"px",n&&(r.style.backgroundColor=n)};window.performance&&!performance.memory&&(performance.memory={usedJSHeapSize:0}),0===performance.memory.totalJSHeapSize&&console.warn("totalJSHeapSize === 0... performance.memory is only available in Chrome .");var c=Date.now(),m=performance.memory.usedJSHeapSize;return{domElement:n,update:function(){function n(e,t){var n=["Bytes","KB","MB","GB","TB"];if(0==e)return"n/a";t=void 0!==t?t:0;var r=Math.pow(10,t),o=Math.floor(Math.log(e)/Math.log(1024));return Math.round(e*r/Math.pow(1024,o))/r+" "+n[o]}if(!(Date.now()-c<1e3/30)){c=Date.now();var r=performance.memory.usedJSHeapSize-m;m=performance.memory.usedJSHeapSize;var a=0>r?"#830":"#131",u=performance.memory.usedJSHeapSize;e=Math.min(e,u),t=Math.max(t,u),o.textContent="Mem: "+n(u,2);var l=u/31457280,p=Math.min(30,30-30*l);s(i,p,a)}}}}},function(e,t,n){var r;!function(o){!function(){if(!o.requestAnimationFrame){o.webkitRequestAnimationFrame&&(o.requestAnimationFrame=o.webkitRequestAnimationFrame,o.cancelAnimationFrame=o.webkitCancelAnimationFrame||o.webkitCancelRequestAnimationFrame);var e=0;o.requestAnimationFrame=function(t){var n=(new Date).getTime(),r=Math.max(0,16-(n-e)),i=o.setTimeout(function(){t(n+r)},r);return e=n+r,i},o.cancelAnimationFrame=function(e){clearTimeout(e)}}}(),r=function(){return o.requestAnimationFrame}.call(t,n,t,e),!(void 0!==r&&(e.exports=r))}(window)}]); -------------------------------------------------------------------------------- /gulpfile.js: -------------------------------------------------------------------------------- 1 | var gulp = require('gulp'), 2 | path = require('path'), 3 | header = require('gulp-header'), 4 | webpack = require('webpack'), 5 | gulpWebpack = require('gulp-webpack'), 6 | extend = require('util')._extend, 7 | pkg = require('./package.json'), 8 | clone = require('clone'), 9 | distPath = path.join(__dirname, 'dist'), 10 | webpackBuildConfig = require("./webpack.config.build"); 11 | 12 | var banner = ['/**', 13 | ' * <%= pkg.name %> - <%= pkg.description %>', 14 | ' * @version v<%= pkg.version %>', 15 | ' * @author <%= pkg.author %>', 16 | ' * @link <%= pkg.homepage %>', 17 | ' * @license <%= pkg.license %>', 18 | ' */', 19 | '' 20 | ].join('\n'); 21 | 22 | gulp.task('default', ['build']); 23 | gulp.task('build', ['build:dev', 'build:prod']); 24 | 25 | gulp.task("build:dev", function (callback) { 26 | var webpackConfig = clone(webpackBuildConfig); 27 | webpackConfig.output.filename = pkg.name + '.js'; 28 | return gulp.src(webpackConfig.entry) 29 | .pipe(gulpWebpack(webpackConfig)) 30 | .pipe(header(banner, { 31 | pkg: pkg 32 | })) 33 | .pipe(gulp.dest(distPath)); 34 | }); 35 | 36 | gulp.task("build:prod", function (callback) { 37 | var webpackConfig = clone(webpackBuildConfig); 38 | webpackConfig.output.filename = pkg.name + '.min.js'; 39 | webpackConfig.plugins.push(new webpack.optimize.UglifyJsPlugin()); 40 | return gulp.src(webpackConfig.entry) 41 | .pipe(gulpWebpack(webpackConfig)) 42 | .pipe(header(banner, { 43 | pkg: pkg 44 | })) 45 | .pipe(gulp.dest(distPath)); 46 | }); -------------------------------------------------------------------------------- /lib/angular-memory-stats.coffee: -------------------------------------------------------------------------------- 1 | MemoryStats = require './memory-stats' 2 | RequestAnimationFrame = require 'requestanimationframe' 3 | 4 | module.exports = module = angular.module 'angular-memory-stats', [] 5 | 6 | module.provider 'angularMemoryStats', -> 7 | cornersAvailable = ["topLeft", "topRight", "bottomLeft", "bottomRight"] 8 | defaultOptions = 9 | isEnabled : true 10 | corner : 'bottomRight' 11 | css : {} 12 | 13 | @isEnabled = defaultOptions.isEnabled 14 | @corner = defaultOptions.corner 15 | @css = defaultOptions.css 16 | 17 | enable = (enable = true) => 18 | @isEnabled = enable 19 | 20 | setCorner = (corner) => 21 | @corner = corner if corner and cornersAvailable.indexOf(corner) > - 1 22 | 23 | setCss = (css) => 24 | @css = css if css 25 | 26 | $get = => 27 | isEnabled: => 28 | @isEnabled 29 | 30 | getCss: => 31 | css = 32 | position: 'fixed' 33 | zIndex: 1 34 | corner = @corner 35 | if ["topLeft", "topRight", "bottomLeft", "bottomRight"].indexOf(corner) is - 1 36 | corner = defaultOptions.corner 37 | switch corner 38 | when "topLeft" 39 | css.top = '5px' 40 | css.left = '5px' 41 | when "topRight" 42 | css.top = '5px' 43 | css.right = '5px' 44 | when "bottomLeft" 45 | css.bottom = '5px' 46 | css.left = '5px' 47 | when "bottomRight" 48 | css.bottom = '5px' 49 | css.right = '5px' 50 | return angular.extend css, @css 51 | 52 | enable: enable 53 | setCorner: setCorner 54 | setCss: setCss 55 | $get: $get 56 | 57 | module.directive 'angularMemoryStats' , -> 58 | restrict: 'E' 59 | scope: false 60 | controller: ($scope, $element, angularMemoryStats) -> 61 | if !angularMemoryStats.isEnabled() 62 | return 63 | stats = new MemoryStats() 64 | $element.css angularMemoryStats.getCss() 65 | $element.append stats.domElement 66 | update = -> 67 | stats.update() 68 | RequestAnimationFrame update 69 | RequestAnimationFrame update 70 | -------------------------------------------------------------------------------- /lib/memory-stats.js: -------------------------------------------------------------------------------- 1 | /** 2 | * @author mrdoob / http://mrdoob.com/ 3 | * @author jetienne / http://jetienne.com/ 4 | * @author paulirish / http://paulirish.com/ 5 | */ 6 | module.exports = function () { 7 | 8 | var msMin = 100; 9 | var msMax = 0; 10 | 11 | var container = document.createElement('div'); 12 | container.id = 'stats'; 13 | container.style.cssText = 'width:80px;opacity:0.9;cursor:pointer'; 14 | 15 | var msDiv = document.createElement('div'); 16 | msDiv.id = 'ms'; 17 | msDiv.style.cssText = 'padding:0 0 3px 3px;text-align:left;background-color:#020;'; 18 | container.appendChild(msDiv); 19 | 20 | var msText = document.createElement('div'); 21 | msText.id = 'msText'; 22 | msText.style.cssText = 'color:#0f0;font-family:Helvetica,Arial,sans-serif;font-size:9px;font-weight:bold;line-height:15px'; 23 | msText.innerHTML = 'Memory'; 24 | msDiv.appendChild(msText); 25 | 26 | var msGraph = document.createElement('div'); 27 | msGraph.id = 'msGraph'; 28 | msGraph.style.cssText = 'position:relative;width:74px;height:30px;background-color:#0f0'; 29 | msDiv.appendChild(msGraph); 30 | 31 | while (msGraph.children.length < 74) { 32 | 33 | var bar = document.createElement('span'); 34 | bar.style.cssText = 'width:1px;height:30px;float:left;background-color:#131'; 35 | msGraph.appendChild(bar); 36 | 37 | } 38 | 39 | var updateGraph = function (dom, height, color) { 40 | 41 | var child = dom.appendChild(dom.firstChild); 42 | child.style.height = height + 'px'; 43 | if (color) child.style.backgroundColor = color; 44 | 45 | } 46 | 47 | // polyfill usedJSHeapSize 48 | if (window.performance && !performance.memory) { 49 | performance.memory = { 50 | usedJSHeapSize: 0 51 | }; 52 | } 53 | 54 | // support of the API? 55 | if (performance.memory.totalJSHeapSize === 0) { 56 | console.warn('totalJSHeapSize === 0... performance.memory is only available in Chrome .') 57 | } 58 | 59 | // TODO, add a sanity check to see if values are bucketed. 60 | // If so, reminde user to adopt the --enable-precise-memory-info flag. 61 | // open -a "/Applications/Google Chrome.app" --args --enable-precise-memory-info 62 | 63 | var lastTime = Date.now(); 64 | var lastUsedHeap = performance.memory.usedJSHeapSize; 65 | return { 66 | domElement: container, 67 | 68 | update: function () { 69 | 70 | // refresh only 30time per second 71 | if (Date.now() - lastTime < 1000 / 30) return; 72 | lastTime = Date.now() 73 | 74 | var delta = performance.memory.usedJSHeapSize - lastUsedHeap; 75 | lastUsedHeap = performance.memory.usedJSHeapSize; 76 | var color = delta < 0 ? '#830' : '#131'; 77 | 78 | var ms = performance.memory.usedJSHeapSize; 79 | msMin = Math.min(msMin, ms); 80 | msMax = Math.max(msMax, ms); 81 | msText.textContent = "Mem: " + bytesToSize(ms, 2); 82 | 83 | var normValue = ms / (30 * 1024 * 1024); 84 | var height = Math.min(30, 30 - normValue * 30); 85 | updateGraph(msGraph, height, color); 86 | 87 | function bytesToSize(bytes, nFractDigit) { 88 | var sizes = ['Bytes', 'KB', 'MB', 'GB', 'TB']; 89 | if (bytes == 0) return 'n/a'; 90 | nFractDigit = nFractDigit !== undefined ? nFractDigit : 0; 91 | var precision = Math.pow(10, nFractDigit); 92 | var i = Math.floor(Math.log(bytes) / Math.log(1024)); 93 | return Math.round(bytes * precision / Math.pow(1024, i)) / precision + ' ' + sizes[i]; 94 | }; 95 | } 96 | 97 | } 98 | 99 | }; -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "angular-memory-stats", 3 | "version": "1.0.0-beta1", 4 | "description": "Angular Memory Stats displays your app memory activity", 5 | "main": "./dist/angular-memory-stats.js", 6 | "scripts": { 7 | "test": "echo \"Error: no test specified\" && exit 1" 8 | }, 9 | "keywords": [ 10 | "angular", 11 | "angularjs", 12 | "memory", 13 | "statistics", 14 | "stats" 15 | ], 16 | "repository": { 17 | "type": "git", 18 | "url": "https://github.com/livingobjects/angular-memory-stats.git" 19 | }, 20 | "author": "shprink ", 21 | "license": "MIT", 22 | "bugs": { 23 | "url": "https://github.com/livingobjects/angular-memory-stats/issues" 24 | }, 25 | "homepage": "https://github.com/livingobjects/angular-memory-stats", 26 | "devDependencies": { 27 | "angular": "^1.3.12", 28 | "clone": "^0.2.0", 29 | "coffee-loader": "^0.7.2", 30 | "coffee-script": "^1.9.0", 31 | "exports-loader": "^0.6.2", 32 | "expose-loader": "^0.6.0", 33 | "gulp": "^3.8.10", 34 | "gulp-header": "^1.2.2", 35 | "gulp-webpack": "^1.2.0", 36 | "html-webpack-plugin": "^1.1.0", 37 | "ng-annotate-webpack-plugin": "^0.1.2", 38 | "path": "^0.11.14" 39 | }, 40 | "dependencies": { 41 | "requestanimationframe": "0.0.22" 42 | } 43 | } -------------------------------------------------------------------------------- /test/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 |
12 | 13 | {% for (var chunk in o.htmlWebpackPlugin.assets) { %} 14 | 15 | {% } %} 16 |
17 | 18 | 19 | -------------------------------------------------------------------------------- /test/test.module.coffee: -------------------------------------------------------------------------------- 1 | require 'angular' 2 | require '../dist/angular-memory-stats' 3 | 4 | module.exports = module = angular.module 'test', [ 5 | 'angular-memory-stats' 6 | ] 7 | module.config (angularMemoryStatsProvider) -> 8 | angularMemoryStatsProvider.enable true 9 | angularMemoryStatsProvider.setCorner('bottomRight') # topLeft, topRight, bottomLeft, bottomRight 10 | angularMemoryStatsProvider.setCss 11 | right: '50px' 12 | 13 | module.run ($log) -> 14 | $log.info 'test running' 15 | -------------------------------------------------------------------------------- /webpack.config.build.js: -------------------------------------------------------------------------------- 1 | var path = require('path'), 2 | webpack = require("webpack"), 3 | libPath = path.join(__dirname, 'lib'), 4 | distPath = path.join(__dirname, 'dist'), 5 | ngAnnotatePlugin = require('ng-annotate-webpack-plugin'); 6 | 7 | module.exports = { 8 | entry: path.join(libPath, 'angular-memory-stats.coffee'), 9 | output: { 10 | path: distPath 11 | }, 12 | module: { 13 | loaders: [{ 14 | test: /\.coffee$/, 15 | loader: "coffee" 16 | }] 17 | }, 18 | plugins: [ 19 | new ngAnnotatePlugin({ 20 | add: true 21 | }), 22 | new webpack.optimize.DedupePlugin() 23 | ] 24 | }; -------------------------------------------------------------------------------- /webpack.config.js: -------------------------------------------------------------------------------- 1 | var path = require('path'), 2 | testPath = path.join(__dirname, 'test'), 3 | wwwPath = path.join(__dirname, 'www'), 4 | HtmlWebpackPlugin = require('html-webpack-plugin'), 5 | ngAnnotatePlugin = require('ng-annotate-webpack-plugin'); 6 | 7 | module.exports = { 8 | entry: path.join(testPath, 'test.module.coffee'), 9 | output: { 10 | path: wwwPath, 11 | filename: 'test.js' 12 | }, 13 | module: { 14 | loaders: [{ 15 | test: /[\/]angular\.js$/, 16 | loader: 'expose?angular!exports?window.angular' 17 | }, { 18 | test: /\.coffee$/, 19 | loader: "coffee" 20 | }] 21 | }, 22 | resolve: { 23 | extensions: ['', '.js', '.json', '.scss', '.coffee', '.html'] 24 | }, 25 | plugins: [new HtmlWebpackPlugin({ 26 | filename: 'index.html', 27 | title: 'wp-api-angularjs', 28 | template: path.join(testPath, 'index.html') 29 | }), 30 | new ngAnnotatePlugin({ 31 | add: true 32 | }) 33 | ] 34 | }; --------------------------------------------------------------------------------