├── .eslintrc.yml ├── .gitignore ├── .npmignore ├── README.md ├── bower.json ├── example ├── index.coffee ├── index.js └── vendor │ ├── dat.gui.js │ └── phaser.js ├── index.coffee ├── index.html ├── index.js ├── package-lock.json ├── package.json └── screenshots ├── debug.png ├── domMeter.png ├── domText.png ├── graph.png ├── meter.png └── text.png /.eslintrc.yml: -------------------------------------------------------------------------------- 1 | env: 2 | browser: yes 3 | extends: 'eslint:recommended' 4 | globals: 5 | Phaser: no 6 | rules: 7 | indent: 8 | - 'off' 9 | linebreak-style: 10 | - error 11 | - unix 12 | no-shadow: 13 | - warn 14 | no-unused-vars: 15 | - warn 16 | quotes: 17 | - error 18 | - double 19 | semi: 20 | - error 21 | - always 22 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/samme/phaser-plugin-advanced-timing/417176e75ff45c91ee02e5b2af4c6efb8bc86f7a/.gitignore -------------------------------------------------------------------------------- /.npmignore: -------------------------------------------------------------------------------- 1 | *.coffee 2 | *.html 3 | *.png 4 | bower.json 5 | example/ 6 | index.html 7 | screenshots/ 8 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | Shows FPS, frame intervals, draw count, and other performance info. [Demo](https://samme.github.io/phaser-plugin-advanced-timing/) 2 | 3 | Usage 4 | ----- 5 | 6 | ```javascript 7 | game.plugins.add(Phaser.Plugin.AdvancedTiming); 8 | // or 9 | game.plugins.add(Phaser.Plugin.AdvancedTiming, {mode: 'graph'}); 10 | ``` 11 | 12 | The display modes are `domMeter`, `domText`, `graph`, `meter`, and `text`. The default mode is `text`. 13 | 14 | You can save a reference to switch modes later: 15 | 16 | ```javascript 17 | var plugin = game.plugins.add(Phaser.Plugin.AdvancedTiming); 18 | // … 19 | plugin.mode = 'text'; 20 | ``` 21 | 22 | The plugin also provides two [debug methods](#debug-methods): 23 | 24 | ```javascript 25 | game.debug.gameInfo(x, y); 26 | game.debug.gameTimeInfo(x, y); 27 | ``` 28 | 29 | Beware that [debug display can be slow in WebGL](https://phaser.io/docs/2.6.2/Phaser.Utils.Debug.html). 30 | 31 | DOM Text, Text 32 | -------------- 33 | 34 | ```javascript 35 | plugin.mode = 'domText'; 36 | plugin.mode = 'text'; 37 | ``` 38 | 39 | ![Text Mode](https://samme.github.io/phaser-plugin-advanced-timing/screenshots/text.png) 40 | 41 | Both show [FPS][fps], [render type][renderType], and WebGL draw count. 42 | 43 | `text` is drawn on the game canvas. `domText` is a separate HTML element. 44 | 45 | The `domText` element can be styled as 46 | 47 | ```css 48 | .ppat-text { 49 | position: absolute; 50 | left: 0; 51 | top: 0; 52 | margin: 0; 53 | font: 16px/1 monospace; 54 | } 55 | ``` 56 | 57 | DOM Meter 58 | --------- 59 | 60 | ```javascript 61 | plugin.mode = 'domMeter'; 62 | ``` 63 | 64 | ![DOM Meter Mode](https://samme.github.io/phaser-plugin-advanced-timing/screenshots/domMeter.png) 65 | 66 | Shows [FPS](fps). It can be styled as 67 | 68 | ```css 69 | .ppat-fps { 70 | position: absolute; 71 | left: 0; 72 | top: 0; 73 | } 74 | ``` 75 | 76 | Graph 77 | ----- 78 | 79 | ![Graph Mode](https://samme.github.io/phaser-plugin-advanced-timing/screenshots/graph.png) 80 | 81 | ```javascript 82 | plugin.mode = 'graph'; 83 | ``` 84 | 85 | Plots values for the last 60 updates: 86 | 87 | - [FPS](fps) (blue) 88 | - [update duration][updateLogic] (orange) 89 | - [render duration][updateRender] (violet) 90 | - spiraling (red) 91 | - [updatesThisFrame](updatesThisFrame) (dark blue; only when [forceSingleUpdate][forceSingleUpdate] is off) 92 | 93 | Meter 94 | ----- 95 | 96 | ```javascript 97 | plugin.mode = 'meter'; 98 | ``` 99 | 100 | ![Meter Mode](https://samme.github.io/phaser-plugin-advanced-timing/screenshots/meter.png) 101 | 102 | Shows [FPS](fps) (blue), [update duration][updateLogic] (orange), and [render duration][updateRender] (violet). 103 | 104 | Debug Methods 105 | ------------- 106 | 107 | ```javascript 108 | game.debug.gameInfo(x, y); 109 | game.debug.gameTimeInfo(x, y); 110 | ``` 111 | 112 | ![Example output of debug.gameInfo() and debug.gameTimeInfo()](https://samme.github.io/phaser-plugin-advanced-timing/screenshots/debug.png) 113 | 114 | ### debug.gameInfo() 115 | 116 | Prints values for 117 | 118 | - [game.forceSingleUpdate][forceSingleUpdate] 119 | - `game._lastCount`: “how many ‘catch-up’ iterations were used on the logic update last frame” 120 | - [game.lockRender][lockRender] 121 | - [game.renderType][renderType] 122 | - `game._spiraling`: “if the ‘catch-up’ iterations are spiraling out of control, this counter is incremented” 123 | - [game.updatesThisFrame][updatesThisFrame]: “number of logic updates expected to occur this render frame; will be 1 unless there are catch-ups required (and allowed)” 124 | 125 | ### debug.gameTimeInfo() 126 | 127 | Prints values for 128 | 129 | - [game.time.desiredFps][desiredFps] 130 | - [game.time.elapsed][elapsed] (and min–max range) 131 | - [game.time.elapsedMS][elapsedMS] 132 | - [game.time.fps][fps] (and min–max range) 133 | - [game.time.physicsElapsedMS][physicsElapsedMS] 134 | - [game.time.slowMotion][slowMotion] 135 | - [game.time.suggestedFps][suggestedFps] 136 | 137 | 138 | [desiredFps]: http://phaser.io/docs/2.6.2/Phaser.Time.html#desiredFps 139 | [elapsed]: http://phaser.io/docs/2.6.2/Phaser.Time.html#elapsed 140 | [elapsedMS]: http://phaser.io/docs/2.6.2/Phaser.Time.html#elapsedMS 141 | [forceSingleUpdate]: http://phaser.io/docs/2.6.2/Phaser.Game.html#forceSingleUpdate 142 | [fps]: http://phaser.io/docs/2.6.2/Phaser.Time.html#fps 143 | [lockRender]: http://phaser.io/docs/2.6.2/Phaser.Game.html#lockRender 144 | [physicsElapsedMS]: http://phaser.io/docs/2.6.2/Phaser.Time.html#physicsElapsedMS 145 | [renderType]: http://phaser.io/docs/2.6.2/Phaser.Game.html#renderType 146 | [slowMotion]: http://phaser.io/docs/2.6.2/Phaser.Time.html#slowMotion 147 | [suggestedFps]: http://phaser.io/docs/2.6.2/Phaser.Time.html#suggestedFps 148 | [updatesThisFrame]: http://phaser.io/docs/2.6.2/Phaser.Game.html#updatesThisFrame 149 | [updateLogic]: http://phaser.io/docs/2.6.2/Phaser.Game.html#updateLogic 150 | [updateRender]: http://phaser.io/docs/2.6.2/Phaser.Game.html#updateRender 151 | -------------------------------------------------------------------------------- /bower.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "phaser-plugin-advanced-timing", 3 | "description": "Shows FPS, frame intervals, draw count, and other performance info", 4 | "main": "index.js", 5 | "authors": [ 6 | "samme" 7 | ], 8 | "license": "MIT", 9 | "homepage": "https://github.com/samme/phaser-plugin-advanced-timing", 10 | "private": true, 11 | "ignore": [ 12 | "**/.*", 13 | "node_modules", 14 | "bower_components", 15 | "test", 16 | "tests", 17 | "*.coffee", 18 | "example", 19 | "index.js", 20 | "screenshots" 21 | ], 22 | "dependencies": { 23 | "phaser": "^2.6.2" 24 | }, 25 | "keywords": [ 26 | "phaser", 27 | "phaser-plugin" 28 | ] 29 | } 30 | -------------------------------------------------------------------------------- /example/index.coffee: -------------------------------------------------------------------------------- 1 | "use strict" 2 | 3 | {dat, Phaser} = this 4 | 5 | {SECOND} = Phaser.Timer 6 | 7 | BUNNY_COUNT = 1e4 8 | BUNNY_LIFESPAN = 10 * SECOND 9 | BUNNY_INTERVAL = 100 10 | BUNNIES_PER_EMIT = 10 11 | RENDER_MODE = Phaser.WEBGL 12 | 13 | debugSettings = 14 | "debug.gameInfo()": no 15 | "debug.gameTimeInfo()": no 16 | 17 | debugSettingsGui = (_debugSettings, gui) -> 18 | for key in Object.keys(_debugSettings) 19 | gui.add _debugSettings, key 20 | gui 21 | 22 | emitterGui = (emitter, gui) -> 23 | gui.add emitter, "_flowQuantity", 0, 100, 5 24 | gui.add emitter, "frequency", 0, 1 * SECOND, 50 25 | gui.add emitter, "lifespan", 0, 10 * SECOND, 100 26 | gui.add emitter, "makeBunnies" 27 | gui.add emitter, "maxParticles" 28 | gui.add emitter, "length" 29 | .listen() 30 | gui.add emitter, "removeAll" 31 | gui.add emitter, "on" 32 | gui 33 | 34 | gameGui = (game, gui) -> 35 | gui.add game, "disableStep" 36 | gui.add game, "enableStep" 37 | gui.add game, "forceSingleUpdate" 38 | gui.add game, "lockRender" 39 | gui.add game, "paused" 40 | gui.add game, "step" 41 | gui 42 | 43 | gameScaleGui = (scale, gui) -> 44 | gui.add scale, "fullScreenScaleMode", 45 | NO_SCALE: Phaser.ScaleManager.NO_SCALE 46 | RESIZE: Phaser.ScaleManager.RESIZE 47 | SHOW_ALL: Phaser.ScaleManager.SHOW_ALL 48 | gui.add scale, "scaleMode", 49 | NO_SCALE: Phaser.ScaleManager.NO_SCALE 50 | RESIZE: Phaser.ScaleManager.RESIZE 51 | SHOW_ALL: Phaser.ScaleManager.SHOW_ALL 52 | gui.add scale, "startFullScreen" 53 | 54 | gameTimeGui = (time, gui) -> 55 | gui.add time, "desiredFps", 5, 120, 5 56 | gui.add time, "refresh" 57 | gui.add time, "reset" 58 | gui.add time, "slowMotion", 0, 2, 0.25 59 | gui 60 | 61 | pluginGui = (plugin, gui) -> 62 | {constructor} = plugin 63 | gui.add plugin, "active" 64 | gui.add plugin, "mode", constructor.modes 65 | gui.add plugin, "reset" 66 | gui.add plugin, "visible" 67 | gui.add plugin, "showElapsed" 68 | gui.add plugin, "showDurations" 69 | gui.add plugin, "showSpiraling" 70 | gui 71 | 72 | @GAME = new Phaser.Game( 73 | 74 | antialias: on 75 | height: window.innerHeight 76 | renderer: RENDER_MODE 77 | resolution: 1 78 | scaleMode: Phaser.ScaleManager.NO_SCALE 79 | width: window.innerWidth 80 | 81 | state: 82 | 83 | init: -> 84 | {game} = this 85 | game.clearBeforeRender = off 86 | game.forceSingleUpdate = off 87 | game.debug.font = "16px monospace" 88 | game.debug.lineHeight = 20 89 | game.scale.fullScreenScaleMode = game.scale.scaleMode 90 | game.scale.parentIsWindow = yes 91 | game.tweens.frameBased = on 92 | game.input.destroy() 93 | unless game.timing 94 | game.timing = game.plugins.add Phaser.Plugin.AdvancedTiming 95 | # game.timing = game.plugins.add Phaser.Plugin.AdvancedTiming, mode: "domText" 96 | # game.timing.meters.scale.set 2 97 | return 98 | 99 | preload: -> 100 | @load.baseURL = "https://examples.phaser.io/assets/" 101 | @load.crossOrigin = "anonymous" 102 | @load.image "bunny", "sprites/wabbit.png" 103 | @load.image "sky", "skies/cavern2.png" 104 | return 105 | 106 | create: -> 107 | world = @world 108 | sky = @add.image 0, 0, "sky" 109 | sky.height = world.height 110 | sky.width = world.width 111 | emitter = @emitter = @add.emitter(world.bounds.left, world.centerY, BUNNY_COUNT) 112 | emitter.makeBunnies = @emitterMakeBunnies.bind emitter 113 | emitter.makeBunnies() 114 | emitter.flow BUNNY_LIFESPAN, BUNNY_INTERVAL, BUNNIES_PER_EMIT 115 | @add.tween(emitter).to { emitX: world.width }, 2000, Phaser.Easing.Sinusoidal.InOut, yes, 0, -1, yes 116 | @gui = new dat.GUI width: 320 117 | emitterGui emitter, @gui.addFolder "bunnies" 118 | gameGui @game, @gui.addFolder "game" 119 | gameScaleGui @game.scale, @gui.addFolder "game.scale" 120 | gameTimeGui @game.time, @gui.addFolder "game.time" 121 | pluginGui @game.timing, pluginGuiFolder = @gui.addFolder "plugin" 122 | pluginGuiFolder.open() 123 | debugSettingsGui debugSettings, @gui.addFolder "debug" 124 | return 125 | 126 | render: -> 127 | {debug} = @game 128 | debug.gameInfo 300, 20 if debugSettings["debug.gameInfo()"] 129 | debug.gameTimeInfo 300, 120 if debugSettings["debug.gameTimeInfo()"] 130 | return 131 | 132 | shutdown: -> 133 | @gui.destroy() 134 | return 135 | 136 | emitterMakeBunnies: -> 137 | @makeParticles "bunny", null, @maxParticles 138 | return 139 | ) 140 | -------------------------------------------------------------------------------- /example/index.js: -------------------------------------------------------------------------------- 1 | // Generated by CoffeeScript 1.10.0 2 | (function() { 3 | "use strict"; 4 | var BUNNIES_PER_EMIT, BUNNY_COUNT, BUNNY_INTERVAL, BUNNY_LIFESPAN, Phaser, RENDER_MODE, SECOND, dat, debugSettings, debugSettingsGui, emitterGui, gameGui, gameScaleGui, gameTimeGui, pluginGui; 5 | 6 | dat = this.dat, Phaser = this.Phaser; 7 | 8 | SECOND = Phaser.Timer.SECOND; 9 | 10 | BUNNY_COUNT = 1e4; 11 | 12 | BUNNY_LIFESPAN = 10 * SECOND; 13 | 14 | BUNNY_INTERVAL = 100; 15 | 16 | BUNNIES_PER_EMIT = 10; 17 | 18 | RENDER_MODE = Phaser.WEBGL; 19 | 20 | debugSettings = { 21 | "debug.gameInfo()": false, 22 | "debug.gameTimeInfo()": false 23 | }; 24 | 25 | debugSettingsGui = function(_debugSettings, gui) { 26 | var i, key, len, ref; 27 | ref = Object.keys(_debugSettings); 28 | for (i = 0, len = ref.length; i < len; i++) { 29 | key = ref[i]; 30 | gui.add(_debugSettings, key); 31 | } 32 | return gui; 33 | }; 34 | 35 | emitterGui = function(emitter, gui) { 36 | gui.add(emitter, "_flowQuantity", 0, 100, 5); 37 | gui.add(emitter, "frequency", 0, 1 * SECOND, 50); 38 | gui.add(emitter, "lifespan", 0, 10 * SECOND, 100); 39 | gui.add(emitter, "makeBunnies"); 40 | gui.add(emitter, "maxParticles"); 41 | gui.add(emitter, "length").listen(); 42 | gui.add(emitter, "removeAll"); 43 | gui.add(emitter, "on"); 44 | return gui; 45 | }; 46 | 47 | gameGui = function(game, gui) { 48 | gui.add(game, "disableStep"); 49 | gui.add(game, "enableStep"); 50 | gui.add(game, "forceSingleUpdate"); 51 | gui.add(game, "lockRender"); 52 | gui.add(game, "paused"); 53 | gui.add(game, "step"); 54 | return gui; 55 | }; 56 | 57 | gameScaleGui = function(scale, gui) { 58 | gui.add(scale, "fullScreenScaleMode", { 59 | NO_SCALE: Phaser.ScaleManager.NO_SCALE, 60 | RESIZE: Phaser.ScaleManager.RESIZE, 61 | SHOW_ALL: Phaser.ScaleManager.SHOW_ALL 62 | }); 63 | gui.add(scale, "scaleMode", { 64 | NO_SCALE: Phaser.ScaleManager.NO_SCALE, 65 | RESIZE: Phaser.ScaleManager.RESIZE, 66 | SHOW_ALL: Phaser.ScaleManager.SHOW_ALL 67 | }); 68 | return gui.add(scale, "startFullScreen"); 69 | }; 70 | 71 | gameTimeGui = function(time, gui) { 72 | gui.add(time, "desiredFps", 5, 120, 5); 73 | gui.add(time, "refresh"); 74 | gui.add(time, "reset"); 75 | gui.add(time, "slowMotion", 0, 2, 0.25); 76 | return gui; 77 | }; 78 | 79 | pluginGui = function(plugin, gui) { 80 | var constructor; 81 | constructor = plugin.constructor; 82 | gui.add(plugin, "active"); 83 | gui.add(plugin, "mode", constructor.modes); 84 | gui.add(plugin, "reset"); 85 | gui.add(plugin, "visible"); 86 | gui.add(plugin, "showElapsed"); 87 | gui.add(plugin, "showDurations"); 88 | gui.add(plugin, "showSpiraling"); 89 | return gui; 90 | }; 91 | 92 | this.GAME = new Phaser.Game({ 93 | antialias: true, 94 | height: window.innerHeight, 95 | renderer: RENDER_MODE, 96 | resolution: 1, 97 | scaleMode: Phaser.ScaleManager.NO_SCALE, 98 | width: window.innerWidth, 99 | state: { 100 | init: function() { 101 | var game; 102 | game = this.game; 103 | game.clearBeforeRender = false; 104 | game.forceSingleUpdate = false; 105 | game.debug.font = "16px monospace"; 106 | game.debug.lineHeight = 20; 107 | game.scale.fullScreenScaleMode = game.scale.scaleMode; 108 | game.scale.parentIsWindow = true; 109 | game.tweens.frameBased = true; 110 | game.input.destroy(); 111 | if (!game.timing) { 112 | game.timing = game.plugins.add(Phaser.Plugin.AdvancedTiming); 113 | } 114 | }, 115 | preload: function() { 116 | this.load.baseURL = "https://examples.phaser.io/assets/"; 117 | this.load.crossOrigin = "anonymous"; 118 | this.load.image("bunny", "sprites/wabbit.png"); 119 | this.load.image("sky", "skies/cavern2.png"); 120 | }, 121 | create: function() { 122 | var emitter, pluginGuiFolder, sky, world; 123 | world = this.world; 124 | sky = this.add.image(0, 0, "sky"); 125 | sky.height = world.height; 126 | sky.width = world.width; 127 | emitter = this.emitter = this.add.emitter(world.bounds.left, world.centerY, BUNNY_COUNT); 128 | emitter.makeBunnies = this.emitterMakeBunnies.bind(emitter); 129 | emitter.makeBunnies(); 130 | emitter.flow(BUNNY_LIFESPAN, BUNNY_INTERVAL, BUNNIES_PER_EMIT); 131 | this.add.tween(emitter).to({ 132 | emitX: world.width 133 | }, 2000, Phaser.Easing.Sinusoidal.InOut, true, 0, -1, true); 134 | this.gui = new dat.GUI({ 135 | width: 320 136 | }); 137 | emitterGui(emitter, this.gui.addFolder("bunnies")); 138 | gameGui(this.game, this.gui.addFolder("game")); 139 | gameScaleGui(this.game.scale, this.gui.addFolder("game.scale")); 140 | gameTimeGui(this.game.time, this.gui.addFolder("game.time")); 141 | pluginGui(this.game.timing, pluginGuiFolder = this.gui.addFolder("plugin")); 142 | pluginGuiFolder.open(); 143 | debugSettingsGui(debugSettings, this.gui.addFolder("debug")); 144 | }, 145 | render: function() { 146 | var debug; 147 | debug = this.game.debug; 148 | if (debugSettings["debug.gameInfo()"]) { 149 | debug.gameInfo(300, 20); 150 | } 151 | if (debugSettings["debug.gameTimeInfo()"]) { 152 | debug.gameTimeInfo(300, 120); 153 | } 154 | }, 155 | shutdown: function() { 156 | this.gui.destroy(); 157 | }, 158 | emitterMakeBunnies: function() { 159 | this.makeParticles("bunny", null, this.maxParticles); 160 | } 161 | } 162 | }); 163 | 164 | }).call(this); 165 | -------------------------------------------------------------------------------- /example/vendor/dat.gui.js: -------------------------------------------------------------------------------- 1 | /** 2 | * dat-gui JavaScript Controller Library 3 | * http://code.google.com/p/dat-gui 4 | * 5 | * Copyright 2011 Data Arts Team, Google Creative Lab 6 | * 7 | * Licensed under the Apache License, Version 2.0 (the "License"); 8 | * you may not use this file except in compliance with the License. 9 | * You may obtain a copy of the License at 10 | * 11 | * http://www.apache.org/licenses/LICENSE-2.0 12 | */ 13 | 14 | (function (global, factory) { 15 | typeof exports === 'object' && typeof module !== 'undefined' ? factory(exports) : 16 | typeof define === 'function' && define.amd ? define(['exports'], factory) : 17 | (factory((global.dat = {}))); 18 | }(this, (function (exports) { 'use strict'; 19 | 20 | function ___$insertStyle(css) { 21 | if (!css) { 22 | return; 23 | } 24 | if (typeof window === 'undefined') { 25 | return; 26 | } 27 | 28 | var style = document.createElement('style'); 29 | 30 | style.setAttribute('type', 'text/css'); 31 | style.innerHTML = css; 32 | document.head.appendChild(style); 33 | 34 | return css; 35 | } 36 | 37 | function colorToString (color, forceCSSHex) { 38 | var colorFormat = color.__state.conversionName.toString(); 39 | var r = Math.round(color.r); 40 | var g = Math.round(color.g); 41 | var b = Math.round(color.b); 42 | var a = color.a; 43 | var h = Math.round(color.h); 44 | var s = color.s.toFixed(1); 45 | var v = color.v.toFixed(1); 46 | if (forceCSSHex || colorFormat === 'THREE_CHAR_HEX' || colorFormat === 'SIX_CHAR_HEX') { 47 | var str = color.hex.toString(16); 48 | while (str.length < 6) { 49 | str = '0' + str; 50 | } 51 | return '#' + str; 52 | } else if (colorFormat === 'CSS_RGB') { 53 | return 'rgb(' + r + ',' + g + ',' + b + ')'; 54 | } else if (colorFormat === 'CSS_RGBA') { 55 | return 'rgba(' + r + ',' + g + ',' + b + ',' + a + ')'; 56 | } else if (colorFormat === 'HEX') { 57 | return '0x' + color.hex.toString(16); 58 | } else if (colorFormat === 'RGB_ARRAY') { 59 | return '[' + r + ',' + g + ',' + b + ']'; 60 | } else if (colorFormat === 'RGBA_ARRAY') { 61 | return '[' + r + ',' + g + ',' + b + ',' + a + ']'; 62 | } else if (colorFormat === 'RGB_OBJ') { 63 | return '{r:' + r + ',g:' + g + ',b:' + b + '}'; 64 | } else if (colorFormat === 'RGBA_OBJ') { 65 | return '{r:' + r + ',g:' + g + ',b:' + b + ',a:' + a + '}'; 66 | } else if (colorFormat === 'HSV_OBJ') { 67 | return '{h:' + h + ',s:' + s + ',v:' + v + '}'; 68 | } else if (colorFormat === 'HSVA_OBJ') { 69 | return '{h:' + h + ',s:' + s + ',v:' + v + ',a:' + a + '}'; 70 | } 71 | return 'unknown format'; 72 | } 73 | 74 | var ARR_EACH = Array.prototype.forEach; 75 | var ARR_SLICE = Array.prototype.slice; 76 | var Common = { 77 | BREAK: {}, 78 | extend: function extend(target) { 79 | this.each(ARR_SLICE.call(arguments, 1), function (obj) { 80 | var keys = this.isObject(obj) ? Object.keys(obj) : []; 81 | keys.forEach(function (key) { 82 | if (!this.isUndefined(obj[key])) { 83 | target[key] = obj[key]; 84 | } 85 | }.bind(this)); 86 | }, this); 87 | return target; 88 | }, 89 | defaults: function defaults(target) { 90 | this.each(ARR_SLICE.call(arguments, 1), function (obj) { 91 | var keys = this.isObject(obj) ? Object.keys(obj) : []; 92 | keys.forEach(function (key) { 93 | if (this.isUndefined(target[key])) { 94 | target[key] = obj[key]; 95 | } 96 | }.bind(this)); 97 | }, this); 98 | return target; 99 | }, 100 | compose: function compose() { 101 | var toCall = ARR_SLICE.call(arguments); 102 | return function () { 103 | var args = ARR_SLICE.call(arguments); 104 | for (var i = toCall.length - 1; i >= 0; i--) { 105 | args = [toCall[i].apply(this, args)]; 106 | } 107 | return args[0]; 108 | }; 109 | }, 110 | each: function each(obj, itr, scope) { 111 | if (!obj) { 112 | return; 113 | } 114 | if (ARR_EACH && obj.forEach && obj.forEach === ARR_EACH) { 115 | obj.forEach(itr, scope); 116 | } else if (obj.length === obj.length + 0) { 117 | var key = void 0; 118 | var l = void 0; 119 | for (key = 0, l = obj.length; key < l; key++) { 120 | if (key in obj && itr.call(scope, obj[key], key) === this.BREAK) { 121 | return; 122 | } 123 | } 124 | } else { 125 | for (var _key in obj) { 126 | if (itr.call(scope, obj[_key], _key) === this.BREAK) { 127 | return; 128 | } 129 | } 130 | } 131 | }, 132 | defer: function defer(fnc) { 133 | setTimeout(fnc, 0); 134 | }, 135 | debounce: function debounce(func, threshold, callImmediately) { 136 | var timeout = void 0; 137 | return function () { 138 | var obj = this; 139 | var args = arguments; 140 | function delayed() { 141 | timeout = null; 142 | if (!callImmediately) func.apply(obj, args); 143 | } 144 | var callNow = callImmediately || !timeout; 145 | clearTimeout(timeout); 146 | timeout = setTimeout(delayed, threshold); 147 | if (callNow) { 148 | func.apply(obj, args); 149 | } 150 | }; 151 | }, 152 | toArray: function toArray(obj) { 153 | if (obj.toArray) return obj.toArray(); 154 | return ARR_SLICE.call(obj); 155 | }, 156 | isUndefined: function isUndefined(obj) { 157 | return obj === undefined; 158 | }, 159 | isNull: function isNull(obj) { 160 | return obj === null; 161 | }, 162 | isNaN: function (_isNaN) { 163 | function isNaN(_x) { 164 | return _isNaN.apply(this, arguments); 165 | } 166 | isNaN.toString = function () { 167 | return _isNaN.toString(); 168 | }; 169 | return isNaN; 170 | }(function (obj) { 171 | return isNaN(obj); 172 | }), 173 | isArray: Array.isArray || function (obj) { 174 | return obj.constructor === Array; 175 | }, 176 | isObject: function isObject(obj) { 177 | return obj === Object(obj); 178 | }, 179 | isNumber: function isNumber(obj) { 180 | return obj === obj + 0; 181 | }, 182 | isString: function isString(obj) { 183 | return obj === obj + ''; 184 | }, 185 | isBoolean: function isBoolean(obj) { 186 | return obj === false || obj === true; 187 | }, 188 | isFunction: function isFunction(obj) { 189 | return Object.prototype.toString.call(obj) === '[object Function]'; 190 | } 191 | }; 192 | 193 | var INTERPRETATIONS = [ 194 | { 195 | litmus: Common.isString, 196 | conversions: { 197 | THREE_CHAR_HEX: { 198 | read: function read(original) { 199 | var test = original.match(/^#([A-F0-9])([A-F0-9])([A-F0-9])$/i); 200 | if (test === null) { 201 | return false; 202 | } 203 | return { 204 | space: 'HEX', 205 | hex: parseInt('0x' + test[1].toString() + test[1].toString() + test[2].toString() + test[2].toString() + test[3].toString() + test[3].toString(), 0) 206 | }; 207 | }, 208 | write: colorToString 209 | }, 210 | SIX_CHAR_HEX: { 211 | read: function read(original) { 212 | var test = original.match(/^#([A-F0-9]{6})$/i); 213 | if (test === null) { 214 | return false; 215 | } 216 | return { 217 | space: 'HEX', 218 | hex: parseInt('0x' + test[1].toString(), 0) 219 | }; 220 | }, 221 | write: colorToString 222 | }, 223 | CSS_RGB: { 224 | read: function read(original) { 225 | var test = original.match(/^rgb\(\s*(.+)\s*,\s*(.+)\s*,\s*(.+)\s*\)/); 226 | if (test === null) { 227 | return false; 228 | } 229 | return { 230 | space: 'RGB', 231 | r: parseFloat(test[1]), 232 | g: parseFloat(test[2]), 233 | b: parseFloat(test[3]) 234 | }; 235 | }, 236 | write: colorToString 237 | }, 238 | CSS_RGBA: { 239 | read: function read(original) { 240 | var test = original.match(/^rgba\(\s*(.+)\s*,\s*(.+)\s*,\s*(.+)\s*,\s*(.+)\s*\)/); 241 | if (test === null) { 242 | return false; 243 | } 244 | return { 245 | space: 'RGB', 246 | r: parseFloat(test[1]), 247 | g: parseFloat(test[2]), 248 | b: parseFloat(test[3]), 249 | a: parseFloat(test[4]) 250 | }; 251 | }, 252 | write: colorToString 253 | } 254 | } 255 | }, 256 | { 257 | litmus: Common.isNumber, 258 | conversions: { 259 | HEX: { 260 | read: function read(original) { 261 | return { 262 | space: 'HEX', 263 | hex: original, 264 | conversionName: 'HEX' 265 | }; 266 | }, 267 | write: function write(color) { 268 | return color.hex; 269 | } 270 | } 271 | } 272 | }, 273 | { 274 | litmus: Common.isArray, 275 | conversions: { 276 | RGB_ARRAY: { 277 | read: function read(original) { 278 | if (original.length !== 3) { 279 | return false; 280 | } 281 | return { 282 | space: 'RGB', 283 | r: original[0], 284 | g: original[1], 285 | b: original[2] 286 | }; 287 | }, 288 | write: function write(color) { 289 | return [color.r, color.g, color.b]; 290 | } 291 | }, 292 | RGBA_ARRAY: { 293 | read: function read(original) { 294 | if (original.length !== 4) return false; 295 | return { 296 | space: 'RGB', 297 | r: original[0], 298 | g: original[1], 299 | b: original[2], 300 | a: original[3] 301 | }; 302 | }, 303 | write: function write(color) { 304 | return [color.r, color.g, color.b, color.a]; 305 | } 306 | } 307 | } 308 | }, 309 | { 310 | litmus: Common.isObject, 311 | conversions: { 312 | RGBA_OBJ: { 313 | read: function read(original) { 314 | if (Common.isNumber(original.r) && Common.isNumber(original.g) && Common.isNumber(original.b) && Common.isNumber(original.a)) { 315 | return { 316 | space: 'RGB', 317 | r: original.r, 318 | g: original.g, 319 | b: original.b, 320 | a: original.a 321 | }; 322 | } 323 | return false; 324 | }, 325 | write: function write(color) { 326 | return { 327 | r: color.r, 328 | g: color.g, 329 | b: color.b, 330 | a: color.a 331 | }; 332 | } 333 | }, 334 | RGB_OBJ: { 335 | read: function read(original) { 336 | if (Common.isNumber(original.r) && Common.isNumber(original.g) && Common.isNumber(original.b)) { 337 | return { 338 | space: 'RGB', 339 | r: original.r, 340 | g: original.g, 341 | b: original.b 342 | }; 343 | } 344 | return false; 345 | }, 346 | write: function write(color) { 347 | return { 348 | r: color.r, 349 | g: color.g, 350 | b: color.b 351 | }; 352 | } 353 | }, 354 | HSVA_OBJ: { 355 | read: function read(original) { 356 | if (Common.isNumber(original.h) && Common.isNumber(original.s) && Common.isNumber(original.v) && Common.isNumber(original.a)) { 357 | return { 358 | space: 'HSV', 359 | h: original.h, 360 | s: original.s, 361 | v: original.v, 362 | a: original.a 363 | }; 364 | } 365 | return false; 366 | }, 367 | write: function write(color) { 368 | return { 369 | h: color.h, 370 | s: color.s, 371 | v: color.v, 372 | a: color.a 373 | }; 374 | } 375 | }, 376 | HSV_OBJ: { 377 | read: function read(original) { 378 | if (Common.isNumber(original.h) && Common.isNumber(original.s) && Common.isNumber(original.v)) { 379 | return { 380 | space: 'HSV', 381 | h: original.h, 382 | s: original.s, 383 | v: original.v 384 | }; 385 | } 386 | return false; 387 | }, 388 | write: function write(color) { 389 | return { 390 | h: color.h, 391 | s: color.s, 392 | v: color.v 393 | }; 394 | } 395 | } 396 | } 397 | }]; 398 | var result = void 0; 399 | var toReturn = void 0; 400 | var interpret = function interpret() { 401 | toReturn = false; 402 | var original = arguments.length > 1 ? Common.toArray(arguments) : arguments[0]; 403 | Common.each(INTERPRETATIONS, function (family) { 404 | if (family.litmus(original)) { 405 | Common.each(family.conversions, function (conversion, conversionName) { 406 | result = conversion.read(original); 407 | if (toReturn === false && result !== false) { 408 | toReturn = result; 409 | result.conversionName = conversionName; 410 | result.conversion = conversion; 411 | return Common.BREAK; 412 | } 413 | }); 414 | return Common.BREAK; 415 | } 416 | }); 417 | return toReturn; 418 | }; 419 | 420 | var tmpComponent = void 0; 421 | var ColorMath = { 422 | hsv_to_rgb: function hsv_to_rgb(h, s, v) { 423 | var hi = Math.floor(h / 60) % 6; 424 | var f = h / 60 - Math.floor(h / 60); 425 | var p = v * (1.0 - s); 426 | var q = v * (1.0 - f * s); 427 | var t = v * (1.0 - (1.0 - f) * s); 428 | var c = [[v, t, p], [q, v, p], [p, v, t], [p, q, v], [t, p, v], [v, p, q]][hi]; 429 | return { 430 | r: c[0] * 255, 431 | g: c[1] * 255, 432 | b: c[2] * 255 433 | }; 434 | }, 435 | rgb_to_hsv: function rgb_to_hsv(r, g, b) { 436 | var min = Math.min(r, g, b); 437 | var max = Math.max(r, g, b); 438 | var delta = max - min; 439 | var h = void 0; 440 | var s = void 0; 441 | if (max !== 0) { 442 | s = delta / max; 443 | } else { 444 | return { 445 | h: NaN, 446 | s: 0, 447 | v: 0 448 | }; 449 | } 450 | if (r === max) { 451 | h = (g - b) / delta; 452 | } else if (g === max) { 453 | h = 2 + (b - r) / delta; 454 | } else { 455 | h = 4 + (r - g) / delta; 456 | } 457 | h /= 6; 458 | if (h < 0) { 459 | h += 1; 460 | } 461 | return { 462 | h: h * 360, 463 | s: s, 464 | v: max / 255 465 | }; 466 | }, 467 | rgb_to_hex: function rgb_to_hex(r, g, b) { 468 | var hex = this.hex_with_component(0, 2, r); 469 | hex = this.hex_with_component(hex, 1, g); 470 | hex = this.hex_with_component(hex, 0, b); 471 | return hex; 472 | }, 473 | component_from_hex: function component_from_hex(hex, componentIndex) { 474 | return hex >> componentIndex * 8 & 0xFF; 475 | }, 476 | hex_with_component: function hex_with_component(hex, componentIndex, value) { 477 | return value << (tmpComponent = componentIndex * 8) | hex & ~(0xFF << tmpComponent); 478 | } 479 | }; 480 | 481 | var _typeof = typeof Symbol === "function" && typeof Symbol.iterator === "symbol" ? function (obj) { 482 | return typeof obj; 483 | } : function (obj) { 484 | return obj && typeof Symbol === "function" && obj.constructor === Symbol && obj !== Symbol.prototype ? "symbol" : typeof obj; 485 | }; 486 | 487 | 488 | 489 | 490 | 491 | 492 | 493 | 494 | 495 | 496 | 497 | var classCallCheck = function (instance, Constructor) { 498 | if (!(instance instanceof Constructor)) { 499 | throw new TypeError("Cannot call a class as a function"); 500 | } 501 | }; 502 | 503 | var createClass = function () { 504 | function defineProperties(target, props) { 505 | for (var i = 0; i < props.length; i++) { 506 | var descriptor = props[i]; 507 | descriptor.enumerable = descriptor.enumerable || false; 508 | descriptor.configurable = true; 509 | if ("value" in descriptor) descriptor.writable = true; 510 | Object.defineProperty(target, descriptor.key, descriptor); 511 | } 512 | } 513 | 514 | return function (Constructor, protoProps, staticProps) { 515 | if (protoProps) defineProperties(Constructor.prototype, protoProps); 516 | if (staticProps) defineProperties(Constructor, staticProps); 517 | return Constructor; 518 | }; 519 | }(); 520 | 521 | 522 | 523 | 524 | 525 | 526 | 527 | var get = function get(object, property, receiver) { 528 | if (object === null) object = Function.prototype; 529 | var desc = Object.getOwnPropertyDescriptor(object, property); 530 | 531 | if (desc === undefined) { 532 | var parent = Object.getPrototypeOf(object); 533 | 534 | if (parent === null) { 535 | return undefined; 536 | } else { 537 | return get(parent, property, receiver); 538 | } 539 | } else if ("value" in desc) { 540 | return desc.value; 541 | } else { 542 | var getter = desc.get; 543 | 544 | if (getter === undefined) { 545 | return undefined; 546 | } 547 | 548 | return getter.call(receiver); 549 | } 550 | }; 551 | 552 | var inherits = function (subClass, superClass) { 553 | if (typeof superClass !== "function" && superClass !== null) { 554 | throw new TypeError("Super expression must either be null or a function, not " + typeof superClass); 555 | } 556 | 557 | subClass.prototype = Object.create(superClass && superClass.prototype, { 558 | constructor: { 559 | value: subClass, 560 | enumerable: false, 561 | writable: true, 562 | configurable: true 563 | } 564 | }); 565 | if (superClass) Object.setPrototypeOf ? Object.setPrototypeOf(subClass, superClass) : subClass.__proto__ = superClass; 566 | }; 567 | 568 | 569 | 570 | 571 | 572 | 573 | 574 | 575 | 576 | 577 | 578 | var possibleConstructorReturn = function (self, call) { 579 | if (!self) { 580 | throw new ReferenceError("this hasn't been initialised - super() hasn't been called"); 581 | } 582 | 583 | return call && (typeof call === "object" || typeof call === "function") ? call : self; 584 | }; 585 | 586 | var Color = function () { 587 | function Color() { 588 | classCallCheck(this, Color); 589 | this.__state = interpret.apply(this, arguments); 590 | if (this.__state === false) { 591 | throw new Error('Failed to interpret color arguments'); 592 | } 593 | this.__state.a = this.__state.a || 1; 594 | } 595 | createClass(Color, [{ 596 | key: 'toString', 597 | value: function toString() { 598 | return colorToString(this); 599 | } 600 | }, { 601 | key: 'toHexString', 602 | value: function toHexString() { 603 | return colorToString(this, true); 604 | } 605 | }, { 606 | key: 'toOriginal', 607 | value: function toOriginal() { 608 | return this.__state.conversion.write(this); 609 | } 610 | }]); 611 | return Color; 612 | }(); 613 | function defineRGBComponent(target, component, componentHexIndex) { 614 | Object.defineProperty(target, component, { 615 | get: function get$$1() { 616 | if (this.__state.space === 'RGB') { 617 | return this.__state[component]; 618 | } 619 | Color.recalculateRGB(this, component, componentHexIndex); 620 | return this.__state[component]; 621 | }, 622 | set: function set$$1(v) { 623 | if (this.__state.space !== 'RGB') { 624 | Color.recalculateRGB(this, component, componentHexIndex); 625 | this.__state.space = 'RGB'; 626 | } 627 | this.__state[component] = v; 628 | } 629 | }); 630 | } 631 | function defineHSVComponent(target, component) { 632 | Object.defineProperty(target, component, { 633 | get: function get$$1() { 634 | if (this.__state.space === 'HSV') { 635 | return this.__state[component]; 636 | } 637 | Color.recalculateHSV(this); 638 | return this.__state[component]; 639 | }, 640 | set: function set$$1(v) { 641 | if (this.__state.space !== 'HSV') { 642 | Color.recalculateHSV(this); 643 | this.__state.space = 'HSV'; 644 | } 645 | this.__state[component] = v; 646 | } 647 | }); 648 | } 649 | Color.recalculateRGB = function (color, component, componentHexIndex) { 650 | if (color.__state.space === 'HEX') { 651 | color.__state[component] = ColorMath.component_from_hex(color.__state.hex, componentHexIndex); 652 | } else if (color.__state.space === 'HSV') { 653 | Common.extend(color.__state, ColorMath.hsv_to_rgb(color.__state.h, color.__state.s, color.__state.v)); 654 | } else { 655 | throw new Error('Corrupted color state'); 656 | } 657 | }; 658 | Color.recalculateHSV = function (color) { 659 | var result = ColorMath.rgb_to_hsv(color.r, color.g, color.b); 660 | Common.extend(color.__state, { 661 | s: result.s, 662 | v: result.v 663 | }); 664 | if (!Common.isNaN(result.h)) { 665 | color.__state.h = result.h; 666 | } else if (Common.isUndefined(color.__state.h)) { 667 | color.__state.h = 0; 668 | } 669 | }; 670 | Color.COMPONENTS = ['r', 'g', 'b', 'h', 's', 'v', 'hex', 'a']; 671 | defineRGBComponent(Color.prototype, 'r', 2); 672 | defineRGBComponent(Color.prototype, 'g', 1); 673 | defineRGBComponent(Color.prototype, 'b', 0); 674 | defineHSVComponent(Color.prototype, 'h'); 675 | defineHSVComponent(Color.prototype, 's'); 676 | defineHSVComponent(Color.prototype, 'v'); 677 | Object.defineProperty(Color.prototype, 'a', { 678 | get: function get$$1() { 679 | return this.__state.a; 680 | }, 681 | set: function set$$1(v) { 682 | this.__state.a = v; 683 | } 684 | }); 685 | Object.defineProperty(Color.prototype, 'hex', { 686 | get: function get$$1() { 687 | if (!this.__state.space !== 'HEX') { 688 | this.__state.hex = ColorMath.rgb_to_hex(this.r, this.g, this.b); 689 | } 690 | return this.__state.hex; 691 | }, 692 | set: function set$$1(v) { 693 | this.__state.space = 'HEX'; 694 | this.__state.hex = v; 695 | } 696 | }); 697 | 698 | var Controller = function () { 699 | function Controller(object, property) { 700 | classCallCheck(this, Controller); 701 | this.initialValue = object[property]; 702 | this.domElement = document.createElement('div'); 703 | this.object = object; 704 | this.property = property; 705 | this.__onChange = undefined; 706 | this.__onFinishChange = undefined; 707 | } 708 | createClass(Controller, [{ 709 | key: 'onChange', 710 | value: function onChange(fnc) { 711 | this.__onChange = fnc; 712 | return this; 713 | } 714 | }, { 715 | key: 'onFinishChange', 716 | value: function onFinishChange(fnc) { 717 | this.__onFinishChange = fnc; 718 | return this; 719 | } 720 | }, { 721 | key: 'setValue', 722 | value: function setValue(newValue) { 723 | this.object[this.property] = newValue; 724 | if (this.__onChange) { 725 | this.__onChange.call(this, newValue); 726 | } 727 | this.updateDisplay(); 728 | return this; 729 | } 730 | }, { 731 | key: 'getValue', 732 | value: function getValue() { 733 | return this.object[this.property]; 734 | } 735 | }, { 736 | key: 'updateDisplay', 737 | value: function updateDisplay() { 738 | return this; 739 | } 740 | }, { 741 | key: 'isModified', 742 | value: function isModified() { 743 | return this.initialValue !== this.getValue(); 744 | } 745 | }]); 746 | return Controller; 747 | }(); 748 | 749 | var EVENT_MAP = { 750 | HTMLEvents: ['change'], 751 | MouseEvents: ['click', 'mousemove', 'mousedown', 'mouseup', 'mouseover'], 752 | KeyboardEvents: ['keydown'] 753 | }; 754 | var EVENT_MAP_INV = {}; 755 | Common.each(EVENT_MAP, function (v, k) { 756 | Common.each(v, function (e) { 757 | EVENT_MAP_INV[e] = k; 758 | }); 759 | }); 760 | var CSS_VALUE_PIXELS = /(\d+(\.\d+)?)px/; 761 | function cssValueToPixels(val) { 762 | if (val === '0' || Common.isUndefined(val)) { 763 | return 0; 764 | } 765 | var match = val.match(CSS_VALUE_PIXELS); 766 | if (!Common.isNull(match)) { 767 | return parseFloat(match[1]); 768 | } 769 | return 0; 770 | } 771 | var dom = { 772 | makeSelectable: function makeSelectable(elem, selectable) { 773 | if (elem === undefined || elem.style === undefined) return; 774 | elem.onselectstart = selectable ? function () { 775 | return false; 776 | } : function () {}; 777 | elem.style.MozUserSelect = selectable ? 'auto' : 'none'; 778 | elem.style.KhtmlUserSelect = selectable ? 'auto' : 'none'; 779 | elem.unselectable = selectable ? 'on' : 'off'; 780 | }, 781 | makeFullscreen: function makeFullscreen(elem, hor, vert) { 782 | var vertical = vert; 783 | var horizontal = hor; 784 | if (Common.isUndefined(horizontal)) { 785 | horizontal = true; 786 | } 787 | if (Common.isUndefined(vertical)) { 788 | vertical = true; 789 | } 790 | elem.style.position = 'absolute'; 791 | if (horizontal) { 792 | elem.style.left = 0; 793 | elem.style.right = 0; 794 | } 795 | if (vertical) { 796 | elem.style.top = 0; 797 | elem.style.bottom = 0; 798 | } 799 | }, 800 | fakeEvent: function fakeEvent(elem, eventType, pars, aux) { 801 | var params = pars || {}; 802 | var className = EVENT_MAP_INV[eventType]; 803 | if (!className) { 804 | throw new Error('Event type ' + eventType + ' not supported.'); 805 | } 806 | var evt = document.createEvent(className); 807 | switch (className) { 808 | case 'MouseEvents': 809 | { 810 | var clientX = params.x || params.clientX || 0; 811 | var clientY = params.y || params.clientY || 0; 812 | evt.initMouseEvent(eventType, params.bubbles || false, params.cancelable || true, window, params.clickCount || 1, 0, 813 | 0, 814 | clientX, 815 | clientY, 816 | false, false, false, false, 0, null); 817 | break; 818 | } 819 | case 'KeyboardEvents': 820 | { 821 | var init = evt.initKeyboardEvent || evt.initKeyEvent; 822 | Common.defaults(params, { 823 | cancelable: true, 824 | ctrlKey: false, 825 | altKey: false, 826 | shiftKey: false, 827 | metaKey: false, 828 | keyCode: undefined, 829 | charCode: undefined 830 | }); 831 | init(eventType, params.bubbles || false, params.cancelable, window, params.ctrlKey, params.altKey, params.shiftKey, params.metaKey, params.keyCode, params.charCode); 832 | break; 833 | } 834 | default: 835 | { 836 | evt.initEvent(eventType, params.bubbles || false, params.cancelable || true); 837 | break; 838 | } 839 | } 840 | Common.defaults(evt, aux); 841 | elem.dispatchEvent(evt); 842 | }, 843 | bind: function bind(elem, event, func, newBool) { 844 | var bool = newBool || false; 845 | if (elem.addEventListener) { 846 | elem.addEventListener(event, func, bool); 847 | } else if (elem.attachEvent) { 848 | elem.attachEvent('on' + event, func); 849 | } 850 | return dom; 851 | }, 852 | unbind: function unbind(elem, event, func, newBool) { 853 | var bool = newBool || false; 854 | if (elem.removeEventListener) { 855 | elem.removeEventListener(event, func, bool); 856 | } else if (elem.detachEvent) { 857 | elem.detachEvent('on' + event, func); 858 | } 859 | return dom; 860 | }, 861 | addClass: function addClass(elem, className) { 862 | if (elem.className === undefined) { 863 | elem.className = className; 864 | } else if (elem.className !== className) { 865 | var classes = elem.className.split(/ +/); 866 | if (classes.indexOf(className) === -1) { 867 | classes.push(className); 868 | elem.className = classes.join(' ').replace(/^\s+/, '').replace(/\s+$/, ''); 869 | } 870 | } 871 | return dom; 872 | }, 873 | removeClass: function removeClass(elem, className) { 874 | if (className) { 875 | if (elem.className === className) { 876 | elem.removeAttribute('class'); 877 | } else { 878 | var classes = elem.className.split(/ +/); 879 | var index = classes.indexOf(className); 880 | if (index !== -1) { 881 | classes.splice(index, 1); 882 | elem.className = classes.join(' '); 883 | } 884 | } 885 | } else { 886 | elem.className = undefined; 887 | } 888 | return dom; 889 | }, 890 | hasClass: function hasClass(elem, className) { 891 | return new RegExp('(?:^|\\s+)' + className + '(?:\\s+|$)').test(elem.className) || false; 892 | }, 893 | getWidth: function getWidth(elem) { 894 | var style = getComputedStyle(elem); 895 | return cssValueToPixels(style['border-left-width']) + cssValueToPixels(style['border-right-width']) + cssValueToPixels(style['padding-left']) + cssValueToPixels(style['padding-right']) + cssValueToPixels(style.width); 896 | }, 897 | getHeight: function getHeight(elem) { 898 | var style = getComputedStyle(elem); 899 | return cssValueToPixels(style['border-top-width']) + cssValueToPixels(style['border-bottom-width']) + cssValueToPixels(style['padding-top']) + cssValueToPixels(style['padding-bottom']) + cssValueToPixels(style.height); 900 | }, 901 | getOffset: function getOffset(el) { 902 | var elem = el; 903 | var offset = { left: 0, top: 0 }; 904 | if (elem.offsetParent) { 905 | do { 906 | offset.left += elem.offsetLeft; 907 | offset.top += elem.offsetTop; 908 | elem = elem.offsetParent; 909 | } while (elem); 910 | } 911 | return offset; 912 | }, 913 | isActive: function isActive(elem) { 914 | return elem === document.activeElement && (elem.type || elem.href); 915 | } 916 | }; 917 | 918 | var BooleanController = function (_Controller) { 919 | inherits(BooleanController, _Controller); 920 | function BooleanController(object, property) { 921 | classCallCheck(this, BooleanController); 922 | var _this2 = possibleConstructorReturn(this, (BooleanController.__proto__ || Object.getPrototypeOf(BooleanController)).call(this, object, property)); 923 | var _this = _this2; 924 | _this2.__prev = _this2.getValue(); 925 | _this2.__checkbox = document.createElement('input'); 926 | _this2.__checkbox.setAttribute('type', 'checkbox'); 927 | function onChange() { 928 | _this.setValue(!_this.__prev); 929 | } 930 | dom.bind(_this2.__checkbox, 'change', onChange, false); 931 | _this2.domElement.appendChild(_this2.__checkbox); 932 | _this2.updateDisplay(); 933 | return _this2; 934 | } 935 | createClass(BooleanController, [{ 936 | key: 'setValue', 937 | value: function setValue(v) { 938 | var toReturn = get(BooleanController.prototype.__proto__ || Object.getPrototypeOf(BooleanController.prototype), 'setValue', this).call(this, v); 939 | if (this.__onFinishChange) { 940 | this.__onFinishChange.call(this, this.getValue()); 941 | } 942 | this.__prev = this.getValue(); 943 | return toReturn; 944 | } 945 | }, { 946 | key: 'updateDisplay', 947 | value: function updateDisplay() { 948 | if (this.getValue() === true) { 949 | this.__checkbox.setAttribute('checked', 'checked'); 950 | this.__checkbox.checked = true; 951 | this.__prev = true; 952 | } else { 953 | this.__checkbox.checked = false; 954 | this.__prev = false; 955 | } 956 | return get(BooleanController.prototype.__proto__ || Object.getPrototypeOf(BooleanController.prototype), 'updateDisplay', this).call(this); 957 | } 958 | }]); 959 | return BooleanController; 960 | }(Controller); 961 | 962 | var OptionController = function (_Controller) { 963 | inherits(OptionController, _Controller); 964 | function OptionController(object, property, opts) { 965 | classCallCheck(this, OptionController); 966 | var _this2 = possibleConstructorReturn(this, (OptionController.__proto__ || Object.getPrototypeOf(OptionController)).call(this, object, property)); 967 | var options = opts; 968 | var _this = _this2; 969 | _this2.__select = document.createElement('select'); 970 | if (Common.isArray(options)) { 971 | var map = {}; 972 | Common.each(options, function (element) { 973 | map[element] = element; 974 | }); 975 | options = map; 976 | } 977 | Common.each(options, function (value, key) { 978 | var opt = document.createElement('option'); 979 | opt.innerHTML = key; 980 | opt.setAttribute('value', value); 981 | _this.__select.appendChild(opt); 982 | }); 983 | _this2.updateDisplay(); 984 | dom.bind(_this2.__select, 'change', function () { 985 | var desiredValue = this.options[this.selectedIndex].value; 986 | _this.setValue(desiredValue); 987 | }); 988 | _this2.domElement.appendChild(_this2.__select); 989 | return _this2; 990 | } 991 | createClass(OptionController, [{ 992 | key: 'setValue', 993 | value: function setValue(v) { 994 | var toReturn = get(OptionController.prototype.__proto__ || Object.getPrototypeOf(OptionController.prototype), 'setValue', this).call(this, v); 995 | if (this.__onFinishChange) { 996 | this.__onFinishChange.call(this, this.getValue()); 997 | } 998 | return toReturn; 999 | } 1000 | }, { 1001 | key: 'updateDisplay', 1002 | value: function updateDisplay() { 1003 | if (dom.isActive(this.__select)) return this; 1004 | this.__select.value = this.getValue(); 1005 | return get(OptionController.prototype.__proto__ || Object.getPrototypeOf(OptionController.prototype), 'updateDisplay', this).call(this); 1006 | } 1007 | }]); 1008 | return OptionController; 1009 | }(Controller); 1010 | 1011 | var StringController = function (_Controller) { 1012 | inherits(StringController, _Controller); 1013 | function StringController(object, property) { 1014 | classCallCheck(this, StringController); 1015 | var _this2 = possibleConstructorReturn(this, (StringController.__proto__ || Object.getPrototypeOf(StringController)).call(this, object, property)); 1016 | var _this = _this2; 1017 | function onChange() { 1018 | _this.setValue(_this.__input.value); 1019 | } 1020 | function onBlur() { 1021 | if (_this.__onFinishChange) { 1022 | _this.__onFinishChange.call(_this, _this.getValue()); 1023 | } 1024 | } 1025 | _this2.__input = document.createElement('input'); 1026 | _this2.__input.setAttribute('type', 'text'); 1027 | dom.bind(_this2.__input, 'keyup', onChange); 1028 | dom.bind(_this2.__input, 'change', onChange); 1029 | dom.bind(_this2.__input, 'blur', onBlur); 1030 | dom.bind(_this2.__input, 'keydown', function (e) { 1031 | if (e.keyCode === 13) { 1032 | this.blur(); 1033 | } 1034 | }); 1035 | _this2.updateDisplay(); 1036 | _this2.domElement.appendChild(_this2.__input); 1037 | return _this2; 1038 | } 1039 | createClass(StringController, [{ 1040 | key: 'updateDisplay', 1041 | value: function updateDisplay() { 1042 | if (!dom.isActive(this.__input)) { 1043 | this.__input.value = this.getValue(); 1044 | } 1045 | return get(StringController.prototype.__proto__ || Object.getPrototypeOf(StringController.prototype), 'updateDisplay', this).call(this); 1046 | } 1047 | }]); 1048 | return StringController; 1049 | }(Controller); 1050 | 1051 | function numDecimals(x) { 1052 | var _x = x.toString(); 1053 | if (_x.indexOf('.') > -1) { 1054 | return _x.length - _x.indexOf('.') - 1; 1055 | } 1056 | return 0; 1057 | } 1058 | var NumberController = function (_Controller) { 1059 | inherits(NumberController, _Controller); 1060 | function NumberController(object, property, params) { 1061 | classCallCheck(this, NumberController); 1062 | var _this = possibleConstructorReturn(this, (NumberController.__proto__ || Object.getPrototypeOf(NumberController)).call(this, object, property)); 1063 | var _params = params || {}; 1064 | _this.__min = _params.min; 1065 | _this.__max = _params.max; 1066 | _this.__step = _params.step; 1067 | if (Common.isUndefined(_this.__step)) { 1068 | if (_this.initialValue === 0) { 1069 | _this.__impliedStep = 1; 1070 | } else { 1071 | _this.__impliedStep = Math.pow(10, Math.floor(Math.log(Math.abs(_this.initialValue)) / Math.LN10)) / 10; 1072 | } 1073 | } else { 1074 | _this.__impliedStep = _this.__step; 1075 | } 1076 | _this.__precision = numDecimals(_this.__impliedStep); 1077 | return _this; 1078 | } 1079 | createClass(NumberController, [{ 1080 | key: 'setValue', 1081 | value: function setValue(v) { 1082 | var _v = v; 1083 | if (this.__min !== undefined && _v < this.__min) { 1084 | _v = this.__min; 1085 | } else if (this.__max !== undefined && _v > this.__max) { 1086 | _v = this.__max; 1087 | } 1088 | if (this.__step !== undefined && _v % this.__step !== 0) { 1089 | _v = Math.round(_v / this.__step) * this.__step; 1090 | } 1091 | return get(NumberController.prototype.__proto__ || Object.getPrototypeOf(NumberController.prototype), 'setValue', this).call(this, _v); 1092 | } 1093 | }, { 1094 | key: 'min', 1095 | value: function min(minValue) { 1096 | this.__min = minValue; 1097 | return this; 1098 | } 1099 | }, { 1100 | key: 'max', 1101 | value: function max(maxValue) { 1102 | this.__max = maxValue; 1103 | return this; 1104 | } 1105 | }, { 1106 | key: 'step', 1107 | value: function step(stepValue) { 1108 | this.__step = stepValue; 1109 | this.__impliedStep = stepValue; 1110 | this.__precision = numDecimals(stepValue); 1111 | return this; 1112 | } 1113 | }]); 1114 | return NumberController; 1115 | }(Controller); 1116 | 1117 | function roundToDecimal(value, decimals) { 1118 | var tenTo = Math.pow(10, decimals); 1119 | return Math.round(value * tenTo) / tenTo; 1120 | } 1121 | var NumberControllerBox = function (_NumberController) { 1122 | inherits(NumberControllerBox, _NumberController); 1123 | function NumberControllerBox(object, property, params) { 1124 | classCallCheck(this, NumberControllerBox); 1125 | var _this2 = possibleConstructorReturn(this, (NumberControllerBox.__proto__ || Object.getPrototypeOf(NumberControllerBox)).call(this, object, property, params)); 1126 | _this2.__truncationSuspended = false; 1127 | var _this = _this2; 1128 | var prevY = void 0; 1129 | function onChange() { 1130 | var attempted = parseFloat(_this.__input.value); 1131 | if (!Common.isNaN(attempted)) { 1132 | _this.setValue(attempted); 1133 | } 1134 | } 1135 | function onFinish() { 1136 | if (_this.__onFinishChange) { 1137 | _this.__onFinishChange.call(_this, _this.getValue()); 1138 | } 1139 | } 1140 | function onBlur() { 1141 | onFinish(); 1142 | } 1143 | function onMouseDrag(e) { 1144 | var diff = prevY - e.clientY; 1145 | _this.setValue(_this.getValue() + diff * _this.__impliedStep); 1146 | prevY = e.clientY; 1147 | } 1148 | function onMouseUp() { 1149 | dom.unbind(window, 'mousemove', onMouseDrag); 1150 | dom.unbind(window, 'mouseup', onMouseUp); 1151 | onFinish(); 1152 | } 1153 | function onMouseDown(e) { 1154 | dom.bind(window, 'mousemove', onMouseDrag); 1155 | dom.bind(window, 'mouseup', onMouseUp); 1156 | prevY = e.clientY; 1157 | } 1158 | _this2.__input = document.createElement('input'); 1159 | _this2.__input.setAttribute('type', 'text'); 1160 | dom.bind(_this2.__input, 'change', onChange); 1161 | dom.bind(_this2.__input, 'blur', onBlur); 1162 | dom.bind(_this2.__input, 'mousedown', onMouseDown); 1163 | dom.bind(_this2.__input, 'keydown', function (e) { 1164 | if (e.keyCode === 13) { 1165 | _this.__truncationSuspended = true; 1166 | this.blur(); 1167 | _this.__truncationSuspended = false; 1168 | onFinish(); 1169 | } 1170 | }); 1171 | _this2.updateDisplay(); 1172 | _this2.domElement.appendChild(_this2.__input); 1173 | return _this2; 1174 | } 1175 | createClass(NumberControllerBox, [{ 1176 | key: 'updateDisplay', 1177 | value: function updateDisplay() { 1178 | this.__input.value = this.__truncationSuspended ? this.getValue() : roundToDecimal(this.getValue(), this.__precision); 1179 | return get(NumberControllerBox.prototype.__proto__ || Object.getPrototypeOf(NumberControllerBox.prototype), 'updateDisplay', this).call(this); 1180 | } 1181 | }]); 1182 | return NumberControllerBox; 1183 | }(NumberController); 1184 | 1185 | function map(v, i1, i2, o1, o2) { 1186 | return o1 + (o2 - o1) * ((v - i1) / (i2 - i1)); 1187 | } 1188 | var NumberControllerSlider = function (_NumberController) { 1189 | inherits(NumberControllerSlider, _NumberController); 1190 | function NumberControllerSlider(object, property, min, max, step) { 1191 | classCallCheck(this, NumberControllerSlider); 1192 | var _this2 = possibleConstructorReturn(this, (NumberControllerSlider.__proto__ || Object.getPrototypeOf(NumberControllerSlider)).call(this, object, property, { min: min, max: max, step: step })); 1193 | var _this = _this2; 1194 | _this2.__background = document.createElement('div'); 1195 | _this2.__foreground = document.createElement('div'); 1196 | dom.bind(_this2.__background, 'mousedown', onMouseDown); 1197 | dom.bind(_this2.__background, 'touchstart', onTouchStart); 1198 | dom.addClass(_this2.__background, 'slider'); 1199 | dom.addClass(_this2.__foreground, 'slider-fg'); 1200 | function onMouseDown(e) { 1201 | document.activeElement.blur(); 1202 | dom.bind(window, 'mousemove', onMouseDrag); 1203 | dom.bind(window, 'mouseup', onMouseUp); 1204 | onMouseDrag(e); 1205 | } 1206 | function onMouseDrag(e) { 1207 | e.preventDefault(); 1208 | var bgRect = _this.__background.getBoundingClientRect(); 1209 | _this.setValue(map(e.clientX, bgRect.left, bgRect.right, _this.__min, _this.__max)); 1210 | return false; 1211 | } 1212 | function onMouseUp() { 1213 | dom.unbind(window, 'mousemove', onMouseDrag); 1214 | dom.unbind(window, 'mouseup', onMouseUp); 1215 | if (_this.__onFinishChange) { 1216 | _this.__onFinishChange.call(_this, _this.getValue()); 1217 | } 1218 | } 1219 | function onTouchStart(e) { 1220 | if (e.touches.length !== 1) { 1221 | return; 1222 | } 1223 | dom.bind(window, 'touchmove', onTouchMove); 1224 | dom.bind(window, 'touchend', onTouchEnd); 1225 | onTouchMove(e); 1226 | } 1227 | function onTouchMove(e) { 1228 | var clientX = e.touches[0].clientX; 1229 | var bgRect = _this.__background.getBoundingClientRect(); 1230 | _this.setValue(map(clientX, bgRect.left, bgRect.right, _this.__min, _this.__max)); 1231 | } 1232 | function onTouchEnd() { 1233 | dom.unbind(window, 'touchmove', onTouchMove); 1234 | dom.unbind(window, 'touchend', onTouchEnd); 1235 | if (_this.__onFinishChange) { 1236 | _this.__onFinishChange.call(_this, _this.getValue()); 1237 | } 1238 | } 1239 | _this2.updateDisplay(); 1240 | _this2.__background.appendChild(_this2.__foreground); 1241 | _this2.domElement.appendChild(_this2.__background); 1242 | return _this2; 1243 | } 1244 | createClass(NumberControllerSlider, [{ 1245 | key: 'updateDisplay', 1246 | value: function updateDisplay() { 1247 | var pct = (this.getValue() - this.__min) / (this.__max - this.__min); 1248 | this.__foreground.style.width = pct * 100 + '%'; 1249 | return get(NumberControllerSlider.prototype.__proto__ || Object.getPrototypeOf(NumberControllerSlider.prototype), 'updateDisplay', this).call(this); 1250 | } 1251 | }]); 1252 | return NumberControllerSlider; 1253 | }(NumberController); 1254 | 1255 | var FunctionController = function (_Controller) { 1256 | inherits(FunctionController, _Controller); 1257 | function FunctionController(object, property, text) { 1258 | classCallCheck(this, FunctionController); 1259 | var _this2 = possibleConstructorReturn(this, (FunctionController.__proto__ || Object.getPrototypeOf(FunctionController)).call(this, object, property)); 1260 | var _this = _this2; 1261 | _this2.__button = document.createElement('div'); 1262 | _this2.__button.innerHTML = text === undefined ? 'Fire' : text; 1263 | dom.bind(_this2.__button, 'click', function (e) { 1264 | e.preventDefault(); 1265 | _this.fire(); 1266 | return false; 1267 | }); 1268 | dom.addClass(_this2.__button, 'button'); 1269 | _this2.domElement.appendChild(_this2.__button); 1270 | return _this2; 1271 | } 1272 | createClass(FunctionController, [{ 1273 | key: 'fire', 1274 | value: function fire() { 1275 | if (this.__onChange) { 1276 | this.__onChange.call(this); 1277 | } 1278 | this.getValue().call(this.object); 1279 | if (this.__onFinishChange) { 1280 | this.__onFinishChange.call(this, this.getValue()); 1281 | } 1282 | } 1283 | }]); 1284 | return FunctionController; 1285 | }(Controller); 1286 | 1287 | var ColorController = function (_Controller) { 1288 | inherits(ColorController, _Controller); 1289 | function ColorController(object, property) { 1290 | classCallCheck(this, ColorController); 1291 | var _this2 = possibleConstructorReturn(this, (ColorController.__proto__ || Object.getPrototypeOf(ColorController)).call(this, object, property)); 1292 | _this2.__color = new Color(_this2.getValue()); 1293 | _this2.__temp = new Color(0); 1294 | var _this = _this2; 1295 | _this2.domElement = document.createElement('div'); 1296 | dom.makeSelectable(_this2.domElement, false); 1297 | _this2.__selector = document.createElement('div'); 1298 | _this2.__selector.className = 'selector'; 1299 | _this2.__saturation_field = document.createElement('div'); 1300 | _this2.__saturation_field.className = 'saturation-field'; 1301 | _this2.__field_knob = document.createElement('div'); 1302 | _this2.__field_knob.className = 'field-knob'; 1303 | _this2.__field_knob_border = '2px solid '; 1304 | _this2.__hue_knob = document.createElement('div'); 1305 | _this2.__hue_knob.className = 'hue-knob'; 1306 | _this2.__hue_field = document.createElement('div'); 1307 | _this2.__hue_field.className = 'hue-field'; 1308 | _this2.__input = document.createElement('input'); 1309 | _this2.__input.type = 'text'; 1310 | _this2.__input_textShadow = '0 1px 1px '; 1311 | dom.bind(_this2.__input, 'keydown', function (e) { 1312 | if (e.keyCode === 13) { 1313 | onBlur.call(this); 1314 | } 1315 | }); 1316 | dom.bind(_this2.__input, 'blur', onBlur); 1317 | dom.bind(_this2.__selector, 'mousedown', function () { 1318 | dom.addClass(this, 'drag').bind(window, 'mouseup', function () { 1319 | dom.removeClass(_this.__selector, 'drag'); 1320 | }); 1321 | }); 1322 | dom.bind(_this2.__selector, 'touchstart', function () { 1323 | dom.addClass(this, 'drag').bind(window, 'touchend', function () { 1324 | dom.removeClass(_this.__selector, 'drag'); 1325 | }); 1326 | }); 1327 | var valueField = document.createElement('div'); 1328 | Common.extend(_this2.__selector.style, { 1329 | width: '122px', 1330 | height: '102px', 1331 | padding: '3px', 1332 | backgroundColor: '#222', 1333 | boxShadow: '0px 1px 3px rgba(0,0,0,0.3)' 1334 | }); 1335 | Common.extend(_this2.__field_knob.style, { 1336 | position: 'absolute', 1337 | width: '12px', 1338 | height: '12px', 1339 | border: _this2.__field_knob_border + (_this2.__color.v < 0.5 ? '#fff' : '#000'), 1340 | boxShadow: '0px 1px 3px rgba(0,0,0,0.5)', 1341 | borderRadius: '12px', 1342 | zIndex: 1 1343 | }); 1344 | Common.extend(_this2.__hue_knob.style, { 1345 | position: 'absolute', 1346 | width: '15px', 1347 | height: '2px', 1348 | borderRight: '4px solid #fff', 1349 | zIndex: 1 1350 | }); 1351 | Common.extend(_this2.__saturation_field.style, { 1352 | width: '100px', 1353 | height: '100px', 1354 | border: '1px solid #555', 1355 | marginRight: '3px', 1356 | display: 'inline-block', 1357 | cursor: 'pointer' 1358 | }); 1359 | Common.extend(valueField.style, { 1360 | width: '100%', 1361 | height: '100%', 1362 | background: 'none' 1363 | }); 1364 | linearGradient(valueField, 'top', 'rgba(0,0,0,0)', '#000'); 1365 | Common.extend(_this2.__hue_field.style, { 1366 | width: '15px', 1367 | height: '100px', 1368 | border: '1px solid #555', 1369 | cursor: 'ns-resize', 1370 | position: 'absolute', 1371 | top: '3px', 1372 | right: '3px' 1373 | }); 1374 | hueGradient(_this2.__hue_field); 1375 | Common.extend(_this2.__input.style, { 1376 | outline: 'none', 1377 | textAlign: 'center', 1378 | color: '#fff', 1379 | border: 0, 1380 | fontWeight: 'bold', 1381 | textShadow: _this2.__input_textShadow + 'rgba(0,0,0,0.7)' 1382 | }); 1383 | dom.bind(_this2.__saturation_field, 'mousedown', fieldDown); 1384 | dom.bind(_this2.__saturation_field, 'touchstart', fieldDown); 1385 | dom.bind(_this2.__field_knob, 'mousedown', fieldDown); 1386 | dom.bind(_this2.__field_knob, 'touchstart', fieldDown); 1387 | dom.bind(_this2.__hue_field, 'mousedown', fieldDownH); 1388 | dom.bind(_this2.__hue_field, 'touchstart', fieldDownH); 1389 | function fieldDown(e) { 1390 | setSV(e); 1391 | dom.bind(window, 'mousemove', setSV); 1392 | dom.bind(window, 'touchmove', setSV); 1393 | dom.bind(window, 'mouseup', fieldUpSV); 1394 | dom.bind(window, 'touchend', fieldUpSV); 1395 | } 1396 | function fieldDownH(e) { 1397 | setH(e); 1398 | dom.bind(window, 'mousemove', setH); 1399 | dom.bind(window, 'touchmove', setH); 1400 | dom.bind(window, 'mouseup', fieldUpH); 1401 | dom.bind(window, 'touchend', fieldUpH); 1402 | } 1403 | function fieldUpSV() { 1404 | dom.unbind(window, 'mousemove', setSV); 1405 | dom.unbind(window, 'touchmove', setSV); 1406 | dom.unbind(window, 'mouseup', fieldUpSV); 1407 | dom.unbind(window, 'touchend', fieldUpSV); 1408 | onFinish(); 1409 | } 1410 | function fieldUpH() { 1411 | dom.unbind(window, 'mousemove', setH); 1412 | dom.unbind(window, 'touchmove', setH); 1413 | dom.unbind(window, 'mouseup', fieldUpH); 1414 | dom.unbind(window, 'touchend', fieldUpH); 1415 | onFinish(); 1416 | } 1417 | function onBlur() { 1418 | var i = interpret(this.value); 1419 | if (i !== false) { 1420 | _this.__color.__state = i; 1421 | _this.setValue(_this.__color.toOriginal()); 1422 | } else { 1423 | this.value = _this.__color.toString(); 1424 | } 1425 | } 1426 | function onFinish() { 1427 | if (_this.__onFinishChange) { 1428 | _this.__onFinishChange.call(_this, _this.__color.toOriginal()); 1429 | } 1430 | } 1431 | _this2.__saturation_field.appendChild(valueField); 1432 | _this2.__selector.appendChild(_this2.__field_knob); 1433 | _this2.__selector.appendChild(_this2.__saturation_field); 1434 | _this2.__selector.appendChild(_this2.__hue_field); 1435 | _this2.__hue_field.appendChild(_this2.__hue_knob); 1436 | _this2.domElement.appendChild(_this2.__input); 1437 | _this2.domElement.appendChild(_this2.__selector); 1438 | _this2.updateDisplay(); 1439 | function setSV(e) { 1440 | if (e.type.indexOf('touch') === -1) { 1441 | e.preventDefault(); 1442 | } 1443 | var fieldRect = _this.__saturation_field.getBoundingClientRect(); 1444 | var _ref = e.touches && e.touches[0] || e, 1445 | clientX = _ref.clientX, 1446 | clientY = _ref.clientY; 1447 | var s = (clientX - fieldRect.left) / (fieldRect.right - fieldRect.left); 1448 | var v = 1 - (clientY - fieldRect.top) / (fieldRect.bottom - fieldRect.top); 1449 | if (v > 1) { 1450 | v = 1; 1451 | } else if (v < 0) { 1452 | v = 0; 1453 | } 1454 | if (s > 1) { 1455 | s = 1; 1456 | } else if (s < 0) { 1457 | s = 0; 1458 | } 1459 | _this.__color.v = v; 1460 | _this.__color.s = s; 1461 | _this.setValue(_this.__color.toOriginal()); 1462 | return false; 1463 | } 1464 | function setH(e) { 1465 | if (e.type.indexOf('touch') === -1) { 1466 | e.preventDefault(); 1467 | } 1468 | var fieldRect = _this.__hue_field.getBoundingClientRect(); 1469 | var _ref2 = e.touches && e.touches[0] || e, 1470 | clientY = _ref2.clientY; 1471 | var h = 1 - (clientY - fieldRect.top) / (fieldRect.bottom - fieldRect.top); 1472 | if (h > 1) { 1473 | h = 1; 1474 | } else if (h < 0) { 1475 | h = 0; 1476 | } 1477 | _this.__color.h = h * 360; 1478 | _this.setValue(_this.__color.toOriginal()); 1479 | return false; 1480 | } 1481 | return _this2; 1482 | } 1483 | createClass(ColorController, [{ 1484 | key: 'updateDisplay', 1485 | value: function updateDisplay() { 1486 | var i = interpret(this.getValue()); 1487 | if (i !== false) { 1488 | var mismatch = false; 1489 | Common.each(Color.COMPONENTS, function (component) { 1490 | if (!Common.isUndefined(i[component]) && !Common.isUndefined(this.__color.__state[component]) && i[component] !== this.__color.__state[component]) { 1491 | mismatch = true; 1492 | return {}; 1493 | } 1494 | }, this); 1495 | if (mismatch) { 1496 | Common.extend(this.__color.__state, i); 1497 | } 1498 | } 1499 | Common.extend(this.__temp.__state, this.__color.__state); 1500 | this.__temp.a = 1; 1501 | var flip = this.__color.v < 0.5 || this.__color.s > 0.5 ? 255 : 0; 1502 | var _flip = 255 - flip; 1503 | Common.extend(this.__field_knob.style, { 1504 | marginLeft: 100 * this.__color.s - 7 + 'px', 1505 | marginTop: 100 * (1 - this.__color.v) - 7 + 'px', 1506 | backgroundColor: this.__temp.toHexString(), 1507 | border: this.__field_knob_border + 'rgb(' + flip + ',' + flip + ',' + flip + ')' 1508 | }); 1509 | this.__hue_knob.style.marginTop = (1 - this.__color.h / 360) * 100 + 'px'; 1510 | this.__temp.s = 1; 1511 | this.__temp.v = 1; 1512 | linearGradient(this.__saturation_field, 'left', '#fff', this.__temp.toHexString()); 1513 | this.__input.value = this.__color.toString(); 1514 | Common.extend(this.__input.style, { 1515 | backgroundColor: this.__color.toHexString(), 1516 | color: 'rgb(' + flip + ',' + flip + ',' + flip + ')', 1517 | textShadow: this.__input_textShadow + 'rgba(' + _flip + ',' + _flip + ',' + _flip + ',.7)' 1518 | }); 1519 | } 1520 | }]); 1521 | return ColorController; 1522 | }(Controller); 1523 | var vendors = ['-moz-', '-o-', '-webkit-', '-ms-', '']; 1524 | function linearGradient(elem, x, a, b) { 1525 | elem.style.background = ''; 1526 | Common.each(vendors, function (vendor) { 1527 | elem.style.cssText += 'background: ' + vendor + 'linear-gradient(' + x + ', ' + a + ' 0%, ' + b + ' 100%); '; 1528 | }); 1529 | } 1530 | function hueGradient(elem) { 1531 | elem.style.background = ''; 1532 | elem.style.cssText += 'background: -moz-linear-gradient(top, #ff0000 0%, #ff00ff 17%, #0000ff 34%, #00ffff 50%, #00ff00 67%, #ffff00 84%, #ff0000 100%);'; 1533 | elem.style.cssText += 'background: -webkit-linear-gradient(top, #ff0000 0%,#ff00ff 17%,#0000ff 34%,#00ffff 50%,#00ff00 67%,#ffff00 84%,#ff0000 100%);'; 1534 | elem.style.cssText += 'background: -o-linear-gradient(top, #ff0000 0%,#ff00ff 17%,#0000ff 34%,#00ffff 50%,#00ff00 67%,#ffff00 84%,#ff0000 100%);'; 1535 | elem.style.cssText += 'background: -ms-linear-gradient(top, #ff0000 0%,#ff00ff 17%,#0000ff 34%,#00ffff 50%,#00ff00 67%,#ffff00 84%,#ff0000 100%);'; 1536 | elem.style.cssText += 'background: linear-gradient(top, #ff0000 0%,#ff00ff 17%,#0000ff 34%,#00ffff 50%,#00ff00 67%,#ffff00 84%,#ff0000 100%);'; 1537 | } 1538 | 1539 | var css = { 1540 | load: function load(url, indoc) { 1541 | var doc = indoc || document; 1542 | var link = doc.createElement('link'); 1543 | link.type = 'text/css'; 1544 | link.rel = 'stylesheet'; 1545 | link.href = url; 1546 | doc.getElementsByTagName('head')[0].appendChild(link); 1547 | }, 1548 | inject: function inject(cssContent, indoc) { 1549 | var doc = indoc || document; 1550 | var injected = document.createElement('style'); 1551 | injected.type = 'text/css'; 1552 | injected.innerHTML = cssContent; 1553 | var head = doc.getElementsByTagName('head')[0]; 1554 | try { 1555 | head.appendChild(injected); 1556 | } catch (e) { 1557 | } 1558 | } 1559 | }; 1560 | 1561 | var saveDialogContents = "
\n\n Here's the new load parameter for your GUI's constructor:\n\n \n\n
\n\n Automatically save\n values to localStorage on exit.\n\n
The values saved to localStorage will\n override those passed to dat.GUI's constructor. This makes it\n easier to work incrementally, but localStorage is fragile,\n and your friends may not see the same values you do.\n\n
\n\n
\n\n
"; 1562 | 1563 | var ControllerFactory = function ControllerFactory(object, property) { 1564 | var initialValue = object[property]; 1565 | if (Common.isArray(arguments[2]) || Common.isObject(arguments[2])) { 1566 | return new OptionController(object, property, arguments[2]); 1567 | } 1568 | if (Common.isNumber(initialValue)) { 1569 | if (Common.isNumber(arguments[2]) && Common.isNumber(arguments[3])) { 1570 | if (Common.isNumber(arguments[4])) { 1571 | return new NumberControllerSlider(object, property, arguments[2], arguments[3], arguments[4]); 1572 | } 1573 | return new NumberControllerSlider(object, property, arguments[2], arguments[3]); 1574 | } 1575 | if (Common.isNumber(arguments[4])) { 1576 | return new NumberControllerBox(object, property, { min: arguments[2], max: arguments[3], step: arguments[4] }); 1577 | } 1578 | return new NumberControllerBox(object, property, { min: arguments[2], max: arguments[3] }); 1579 | } 1580 | if (Common.isString(initialValue)) { 1581 | return new StringController(object, property); 1582 | } 1583 | if (Common.isFunction(initialValue)) { 1584 | return new FunctionController(object, property, ''); 1585 | } 1586 | if (Common.isBoolean(initialValue)) { 1587 | return new BooleanController(object, property); 1588 | } 1589 | return null; 1590 | }; 1591 | 1592 | function requestAnimationFrame(callback) { 1593 | setTimeout(callback, 1000 / 60); 1594 | } 1595 | var requestAnimationFrame$1 = window.requestAnimationFrame || window.webkitRequestAnimationFrame || window.mozRequestAnimationFrame || window.oRequestAnimationFrame || window.msRequestAnimationFrame || requestAnimationFrame; 1596 | 1597 | var CenteredDiv = function () { 1598 | function CenteredDiv() { 1599 | classCallCheck(this, CenteredDiv); 1600 | this.backgroundElement = document.createElement('div'); 1601 | Common.extend(this.backgroundElement.style, { 1602 | backgroundColor: 'rgba(0,0,0,0.8)', 1603 | top: 0, 1604 | left: 0, 1605 | display: 'none', 1606 | zIndex: '1000', 1607 | opacity: 0, 1608 | WebkitTransition: 'opacity 0.2s linear', 1609 | transition: 'opacity 0.2s linear' 1610 | }); 1611 | dom.makeFullscreen(this.backgroundElement); 1612 | this.backgroundElement.style.position = 'fixed'; 1613 | this.domElement = document.createElement('div'); 1614 | Common.extend(this.domElement.style, { 1615 | position: 'fixed', 1616 | display: 'none', 1617 | zIndex: '1001', 1618 | opacity: 0, 1619 | WebkitTransition: '-webkit-transform 0.2s ease-out, opacity 0.2s linear', 1620 | transition: 'transform 0.2s ease-out, opacity 0.2s linear' 1621 | }); 1622 | document.body.appendChild(this.backgroundElement); 1623 | document.body.appendChild(this.domElement); 1624 | var _this = this; 1625 | dom.bind(this.backgroundElement, 'click', function () { 1626 | _this.hide(); 1627 | }); 1628 | } 1629 | createClass(CenteredDiv, [{ 1630 | key: 'show', 1631 | value: function show() { 1632 | var _this = this; 1633 | this.backgroundElement.style.display = 'block'; 1634 | this.domElement.style.display = 'block'; 1635 | this.domElement.style.opacity = 0; 1636 | this.domElement.style.webkitTransform = 'scale(1.1)'; 1637 | this.layout(); 1638 | Common.defer(function () { 1639 | _this.backgroundElement.style.opacity = 1; 1640 | _this.domElement.style.opacity = 1; 1641 | _this.domElement.style.webkitTransform = 'scale(1)'; 1642 | }); 1643 | } 1644 | }, { 1645 | key: 'hide', 1646 | value: function hide() { 1647 | var _this = this; 1648 | var hide = function hide() { 1649 | _this.domElement.style.display = 'none'; 1650 | _this.backgroundElement.style.display = 'none'; 1651 | dom.unbind(_this.domElement, 'webkitTransitionEnd', hide); 1652 | dom.unbind(_this.domElement, 'transitionend', hide); 1653 | dom.unbind(_this.domElement, 'oTransitionEnd', hide); 1654 | }; 1655 | dom.bind(this.domElement, 'webkitTransitionEnd', hide); 1656 | dom.bind(this.domElement, 'transitionend', hide); 1657 | dom.bind(this.domElement, 'oTransitionEnd', hide); 1658 | this.backgroundElement.style.opacity = 0; 1659 | this.domElement.style.opacity = 0; 1660 | this.domElement.style.webkitTransform = 'scale(1.1)'; 1661 | } 1662 | }, { 1663 | key: 'layout', 1664 | value: function layout() { 1665 | this.domElement.style.left = window.innerWidth / 2 - dom.getWidth(this.domElement) / 2 + 'px'; 1666 | this.domElement.style.top = window.innerHeight / 2 - dom.getHeight(this.domElement) / 2 + 'px'; 1667 | } 1668 | }]); 1669 | return CenteredDiv; 1670 | }(); 1671 | 1672 | var styleSheet = ___$insertStyle(".dg ul{list-style:none;margin:0;padding:0;width:100%;clear:both}.dg.ac{position:fixed;top:0;left:0;right:0;height:0;z-index:0}.dg:not(.ac) .main{overflow:hidden}.dg.main{-webkit-transition:opacity .1s linear;-o-transition:opacity .1s linear;-moz-transition:opacity .1s linear;transition:opacity .1s linear}.dg.main.taller-than-window{overflow-y:auto}.dg.main.taller-than-window .close-button{opacity:1;margin-top:-1px;border-top:1px solid #2c2c2c}.dg.main ul.closed .close-button{opacity:1 !important}.dg.main:hover .close-button,.dg.main .close-button.drag{opacity:1}.dg.main .close-button{-webkit-transition:opacity .1s linear;-o-transition:opacity .1s linear;-moz-transition:opacity .1s linear;transition:opacity .1s linear;border:0;line-height:19px;height:20px;cursor:pointer;text-align:center;background-color:#000}.dg.main .close-button.close-top{position:relative}.dg.main .close-button.close-bottom{position:absolute}.dg.main .close-button:hover{background-color:#111}.dg.a{float:right;margin-right:15px;overflow-y:visible}.dg.a.has-save>ul.close-top{margin-top:0}.dg.a.has-save>ul.close-bottom{margin-top:27px}.dg.a.has-save>ul.closed{margin-top:0}.dg.a .save-row{top:0;z-index:1002}.dg.a .save-row.close-top{position:relative}.dg.a .save-row.close-bottom{position:fixed}.dg li{-webkit-transition:height .1s ease-out;-o-transition:height .1s ease-out;-moz-transition:height .1s ease-out;transition:height .1s ease-out;-webkit-transition:overflow .1s linear;-o-transition:overflow .1s linear;-moz-transition:overflow .1s linear;transition:overflow .1s linear}.dg li:not(.folder){cursor:auto;height:27px;line-height:27px;padding:0 4px 0 5px}.dg li.folder{padding:0;border-left:4px solid rgba(0,0,0,0)}.dg li.title{cursor:pointer;margin-left:-4px}.dg .closed li:not(.title),.dg .closed ul li,.dg .closed ul li>*{height:0;overflow:hidden;border:0}.dg .cr{clear:both;padding-left:3px;height:27px;overflow:hidden}.dg .property-name{cursor:default;float:left;clear:left;width:40%;overflow:hidden;text-overflow:ellipsis}.dg .c{float:left;width:60%;position:relative}.dg .c input[type=text]{border:0;margin-top:4px;padding:3px;width:100%;float:right}.dg .has-slider input[type=text]{width:30%;margin-left:0}.dg .slider{float:left;width:66%;margin-left:-5px;margin-right:0;height:19px;margin-top:4px}.dg .slider-fg{height:100%}.dg .c input[type=checkbox]{margin-top:7px}.dg .c select{margin-top:5px}.dg .cr.function,.dg .cr.function .property-name,.dg .cr.function *,.dg .cr.boolean,.dg .cr.boolean *{cursor:pointer}.dg .cr.color{overflow:visible}.dg .selector{display:none;position:absolute;margin-left:-9px;margin-top:23px;z-index:10}.dg .c:hover .selector,.dg .selector.drag{display:block}.dg li.save-row{padding:0}.dg li.save-row .button{display:inline-block;padding:0px 6px}.dg.dialogue{background-color:#222;width:460px;padding:15px;font-size:13px;line-height:15px}#dg-new-constructor{padding:10px;color:#222;font-family:Monaco, monospace;font-size:10px;border:0;resize:none;box-shadow:inset 1px 1px 1px #888;word-wrap:break-word;margin:12px 0;display:block;width:440px;overflow-y:scroll;height:100px;position:relative}#dg-local-explain{display:none;font-size:11px;line-height:17px;border-radius:3px;background-color:#333;padding:8px;margin-top:10px}#dg-local-explain code{font-size:10px}#dat-gui-save-locally{display:none}.dg{color:#eee;font:11px 'Lucida Grande', sans-serif;text-shadow:0 -1px 0 #111}.dg.main::-webkit-scrollbar{width:5px;background:#1a1a1a}.dg.main::-webkit-scrollbar-corner{height:0;display:none}.dg.main::-webkit-scrollbar-thumb{border-radius:5px;background:#676767}.dg li:not(.folder){background:#1a1a1a;border-bottom:1px solid #2c2c2c}.dg li.save-row{line-height:25px;background:#dad5cb;border:0}.dg li.save-row select{margin-left:5px;width:108px}.dg li.save-row .button{margin-left:5px;margin-top:1px;border-radius:2px;font-size:9px;line-height:7px;padding:4px 4px 5px 4px;background:#c5bdad;color:#fff;text-shadow:0 1px 0 #b0a58f;box-shadow:0 -1px 0 #b0a58f;cursor:pointer}.dg li.save-row .button.gears{background:#c5bdad url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAsAAAANCAYAAAB/9ZQ7AAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAAQJJREFUeNpiYKAU/P//PwGIC/ApCABiBSAW+I8AClAcgKxQ4T9hoMAEUrxx2QSGN6+egDX+/vWT4e7N82AMYoPAx/evwWoYoSYbACX2s7KxCxzcsezDh3evFoDEBYTEEqycggWAzA9AuUSQQgeYPa9fPv6/YWm/Acx5IPb7ty/fw+QZblw67vDs8R0YHyQhgObx+yAJkBqmG5dPPDh1aPOGR/eugW0G4vlIoTIfyFcA+QekhhHJhPdQxbiAIguMBTQZrPD7108M6roWYDFQiIAAv6Aow/1bFwXgis+f2LUAynwoIaNcz8XNx3Dl7MEJUDGQpx9gtQ8YCueB+D26OECAAQDadt7e46D42QAAAABJRU5ErkJggg==) 2px 1px no-repeat;height:7px;width:8px}.dg li.save-row .button:hover{background-color:#bab19e;box-shadow:0 -1px 0 #b0a58f}.dg li.folder{border-bottom:0}.dg li.title{padding-left:16px;background:#000 url(data:image/gif;base64,R0lGODlhBQAFAJEAAP////Pz8////////yH5BAEAAAIALAAAAAAFAAUAAAIIlI+hKgFxoCgAOw==) 6px 10px no-repeat;cursor:pointer;border-bottom:1px solid rgba(255,255,255,0.2)}.dg .closed li.title{background-image:url(data:image/gif;base64,R0lGODlhBQAFAJEAAP////Pz8////////yH5BAEAAAIALAAAAAAFAAUAAAIIlGIWqMCbWAEAOw==)}.dg .cr.boolean{border-left:3px solid #806787}.dg .cr.color{border-left:3px solid}.dg .cr.function{border-left:3px solid #e61d5f}.dg .cr.number{border-left:3px solid #2FA1D6}.dg .cr.number input[type=text]{color:#2FA1D6}.dg .cr.string{border-left:3px solid #1ed36f}.dg .cr.string input[type=text]{color:#1ed36f}.dg .cr.function:hover,.dg .cr.boolean:hover{background:#111}.dg .c input[type=text]{background:#303030;outline:none}.dg .c input[type=text]:hover{background:#3c3c3c}.dg .c input[type=text]:focus{background:#494949;color:#fff}.dg .c .slider{background:#303030;cursor:ew-resize}.dg .c .slider-fg{background:#2FA1D6;max-width:100%}.dg .c .slider:hover{background:#3c3c3c}.dg .c .slider:hover .slider-fg{background:#44abda}\n"); 1673 | 1674 | css.inject(styleSheet); 1675 | var CSS_NAMESPACE = 'dg'; 1676 | var HIDE_KEY_CODE = 72; 1677 | var CLOSE_BUTTON_HEIGHT = 20; 1678 | var DEFAULT_DEFAULT_PRESET_NAME = 'Default'; 1679 | var SUPPORTS_LOCAL_STORAGE = function () { 1680 | try { 1681 | return !!window.localStorage; 1682 | } catch (e) { 1683 | return false; 1684 | } 1685 | }(); 1686 | var SAVE_DIALOGUE = void 0; 1687 | var autoPlaceVirgin = true; 1688 | var autoPlaceContainer = void 0; 1689 | var hide = false; 1690 | var hideableGuis = []; 1691 | var GUI = function GUI(pars) { 1692 | var _this = this; 1693 | var params = pars || {}; 1694 | this.domElement = document.createElement('div'); 1695 | this.__ul = document.createElement('ul'); 1696 | this.domElement.appendChild(this.__ul); 1697 | dom.addClass(this.domElement, CSS_NAMESPACE); 1698 | this.__folders = {}; 1699 | this.__controllers = []; 1700 | this.__rememberedObjects = []; 1701 | this.__rememberedObjectIndecesToControllers = []; 1702 | this.__listening = []; 1703 | params = Common.defaults(params, { 1704 | closeOnTop: false, 1705 | autoPlace: true, 1706 | width: GUI.DEFAULT_WIDTH 1707 | }); 1708 | params = Common.defaults(params, { 1709 | resizable: params.autoPlace, 1710 | hideable: params.autoPlace 1711 | }); 1712 | if (!Common.isUndefined(params.load)) { 1713 | if (params.preset) { 1714 | params.load.preset = params.preset; 1715 | } 1716 | } else { 1717 | params.load = { preset: DEFAULT_DEFAULT_PRESET_NAME }; 1718 | } 1719 | if (Common.isUndefined(params.parent) && params.hideable) { 1720 | hideableGuis.push(this); 1721 | } 1722 | params.resizable = Common.isUndefined(params.parent) && params.resizable; 1723 | if (params.autoPlace && Common.isUndefined(params.scrollable)) { 1724 | params.scrollable = true; 1725 | } 1726 | var useLocalStorage = SUPPORTS_LOCAL_STORAGE && localStorage.getItem(getLocalStorageHash(this, 'isLocal')) === 'true'; 1727 | var saveToLocalStorage = void 0; 1728 | var titleRow = void 0; 1729 | Object.defineProperties(this, 1730 | { 1731 | parent: { 1732 | get: function get$$1() { 1733 | return params.parent; 1734 | } 1735 | }, 1736 | scrollable: { 1737 | get: function get$$1() { 1738 | return params.scrollable; 1739 | } 1740 | }, 1741 | autoPlace: { 1742 | get: function get$$1() { 1743 | return params.autoPlace; 1744 | } 1745 | }, 1746 | closeOnTop: { 1747 | get: function get$$1() { 1748 | return params.closeOnTop; 1749 | } 1750 | }, 1751 | preset: { 1752 | get: function get$$1() { 1753 | if (_this.parent) { 1754 | return _this.getRoot().preset; 1755 | } 1756 | return params.load.preset; 1757 | }, 1758 | set: function set$$1(v) { 1759 | if (_this.parent) { 1760 | _this.getRoot().preset = v; 1761 | } else { 1762 | params.load.preset = v; 1763 | } 1764 | setPresetSelectIndex(this); 1765 | _this.revert(); 1766 | } 1767 | }, 1768 | width: { 1769 | get: function get$$1() { 1770 | return params.width; 1771 | }, 1772 | set: function set$$1(v) { 1773 | params.width = v; 1774 | setWidth(_this, v); 1775 | } 1776 | }, 1777 | name: { 1778 | get: function get$$1() { 1779 | return params.name; 1780 | }, 1781 | set: function set$$1(v) { 1782 | params.name = v; 1783 | if (titleRow) { 1784 | titleRow.innerHTML = params.name; 1785 | } 1786 | } 1787 | }, 1788 | closed: { 1789 | get: function get$$1() { 1790 | return params.closed; 1791 | }, 1792 | set: function set$$1(v) { 1793 | params.closed = v; 1794 | if (params.closed) { 1795 | dom.addClass(_this.__ul, GUI.CLASS_CLOSED); 1796 | } else { 1797 | dom.removeClass(_this.__ul, GUI.CLASS_CLOSED); 1798 | } 1799 | this.onResize(); 1800 | if (_this.__closeButton) { 1801 | _this.__closeButton.innerHTML = v ? GUI.TEXT_OPEN : GUI.TEXT_CLOSED; 1802 | } 1803 | } 1804 | }, 1805 | load: { 1806 | get: function get$$1() { 1807 | return params.load; 1808 | } 1809 | }, 1810 | useLocalStorage: { 1811 | get: function get$$1() { 1812 | return useLocalStorage; 1813 | }, 1814 | set: function set$$1(bool) { 1815 | if (SUPPORTS_LOCAL_STORAGE) { 1816 | useLocalStorage = bool; 1817 | if (bool) { 1818 | dom.bind(window, 'unload', saveToLocalStorage); 1819 | } else { 1820 | dom.unbind(window, 'unload', saveToLocalStorage); 1821 | } 1822 | localStorage.setItem(getLocalStorageHash(_this, 'isLocal'), bool); 1823 | } 1824 | } 1825 | } 1826 | }); 1827 | if (Common.isUndefined(params.parent)) { 1828 | this.closed = params.closed || false; 1829 | dom.addClass(this.domElement, GUI.CLASS_MAIN); 1830 | dom.makeSelectable(this.domElement, false); 1831 | if (SUPPORTS_LOCAL_STORAGE) { 1832 | if (useLocalStorage) { 1833 | _this.useLocalStorage = true; 1834 | var savedGui = localStorage.getItem(getLocalStorageHash(this, 'gui')); 1835 | if (savedGui) { 1836 | params.load = JSON.parse(savedGui); 1837 | } 1838 | } 1839 | } 1840 | this.__closeButton = document.createElement('div'); 1841 | this.__closeButton.innerHTML = GUI.TEXT_CLOSED; 1842 | dom.addClass(this.__closeButton, GUI.CLASS_CLOSE_BUTTON); 1843 | if (params.closeOnTop) { 1844 | dom.addClass(this.__closeButton, GUI.CLASS_CLOSE_TOP); 1845 | this.domElement.insertBefore(this.__closeButton, this.domElement.childNodes[0]); 1846 | } else { 1847 | dom.addClass(this.__closeButton, GUI.CLASS_CLOSE_BOTTOM); 1848 | this.domElement.appendChild(this.__closeButton); 1849 | } 1850 | dom.bind(this.__closeButton, 'click', function () { 1851 | _this.closed = !_this.closed; 1852 | }); 1853 | } else { 1854 | if (params.closed === undefined) { 1855 | params.closed = true; 1856 | } 1857 | var titleRowName = document.createTextNode(params.name); 1858 | dom.addClass(titleRowName, 'controller-name'); 1859 | titleRow = addRow(_this, titleRowName); 1860 | var onClickTitle = function onClickTitle(e) { 1861 | e.preventDefault(); 1862 | _this.closed = !_this.closed; 1863 | return false; 1864 | }; 1865 | dom.addClass(this.__ul, GUI.CLASS_CLOSED); 1866 | dom.addClass(titleRow, 'title'); 1867 | dom.bind(titleRow, 'click', onClickTitle); 1868 | if (!params.closed) { 1869 | this.closed = false; 1870 | } 1871 | } 1872 | if (params.autoPlace) { 1873 | if (Common.isUndefined(params.parent)) { 1874 | if (autoPlaceVirgin) { 1875 | autoPlaceContainer = document.createElement('div'); 1876 | dom.addClass(autoPlaceContainer, CSS_NAMESPACE); 1877 | dom.addClass(autoPlaceContainer, GUI.CLASS_AUTO_PLACE_CONTAINER); 1878 | document.body.appendChild(autoPlaceContainer); 1879 | autoPlaceVirgin = false; 1880 | } 1881 | autoPlaceContainer.appendChild(this.domElement); 1882 | dom.addClass(this.domElement, GUI.CLASS_AUTO_PLACE); 1883 | } 1884 | if (!this.parent) { 1885 | setWidth(_this, params.width); 1886 | } 1887 | } 1888 | this.__resizeHandler = function () { 1889 | _this.onResizeDebounced(); 1890 | }; 1891 | dom.bind(window, 'resize', this.__resizeHandler); 1892 | dom.bind(this.__ul, 'webkitTransitionEnd', this.__resizeHandler); 1893 | dom.bind(this.__ul, 'transitionend', this.__resizeHandler); 1894 | dom.bind(this.__ul, 'oTransitionEnd', this.__resizeHandler); 1895 | this.onResize(); 1896 | if (params.resizable) { 1897 | addResizeHandle(this); 1898 | } 1899 | saveToLocalStorage = function saveToLocalStorage() { 1900 | if (SUPPORTS_LOCAL_STORAGE && localStorage.getItem(getLocalStorageHash(_this, 'isLocal')) === 'true') { 1901 | localStorage.setItem(getLocalStorageHash(_this, 'gui'), JSON.stringify(_this.getSaveObject())); 1902 | } 1903 | }; 1904 | this.saveToLocalStorageIfPossible = saveToLocalStorage; 1905 | function resetWidth() { 1906 | var root = _this.getRoot(); 1907 | root.width += 1; 1908 | Common.defer(function () { 1909 | root.width -= 1; 1910 | }); 1911 | } 1912 | if (!params.parent) { 1913 | resetWidth(); 1914 | } 1915 | }; 1916 | GUI.toggleHide = function () { 1917 | hide = !hide; 1918 | Common.each(hideableGuis, function (gui) { 1919 | gui.domElement.style.display = hide ? 'none' : ''; 1920 | }); 1921 | }; 1922 | GUI.CLASS_AUTO_PLACE = 'a'; 1923 | GUI.CLASS_AUTO_PLACE_CONTAINER = 'ac'; 1924 | GUI.CLASS_MAIN = 'main'; 1925 | GUI.CLASS_CONTROLLER_ROW = 'cr'; 1926 | GUI.CLASS_TOO_TALL = 'taller-than-window'; 1927 | GUI.CLASS_CLOSED = 'closed'; 1928 | GUI.CLASS_CLOSE_BUTTON = 'close-button'; 1929 | GUI.CLASS_CLOSE_TOP = 'close-top'; 1930 | GUI.CLASS_CLOSE_BOTTOM = 'close-bottom'; 1931 | GUI.CLASS_DRAG = 'drag'; 1932 | GUI.DEFAULT_WIDTH = 245; 1933 | GUI.TEXT_CLOSED = 'Close Controls'; 1934 | GUI.TEXT_OPEN = 'Open Controls'; 1935 | GUI._keydownHandler = function (e) { 1936 | if (document.activeElement.type !== 'text' && (e.which === HIDE_KEY_CODE || e.keyCode === HIDE_KEY_CODE)) { 1937 | GUI.toggleHide(); 1938 | } 1939 | }; 1940 | dom.bind(window, 'keydown', GUI._keydownHandler, false); 1941 | Common.extend(GUI.prototype, 1942 | { 1943 | add: function add(object, property) { 1944 | return _add(this, object, property, { 1945 | factoryArgs: Array.prototype.slice.call(arguments, 2) 1946 | }); 1947 | }, 1948 | addColor: function addColor(object, property) { 1949 | return _add(this, object, property, { 1950 | color: true 1951 | }); 1952 | }, 1953 | remove: function remove(controller) { 1954 | this.__ul.removeChild(controller.__li); 1955 | this.__controllers.splice(this.__controllers.indexOf(controller), 1); 1956 | var _this = this; 1957 | Common.defer(function () { 1958 | _this.onResize(); 1959 | }); 1960 | }, 1961 | destroy: function destroy() { 1962 | if (this.parent) { 1963 | throw new Error('Only the root GUI should be removed with .destroy(). ' + 'For subfolders, use gui.removeFolder(folder) instead.'); 1964 | } 1965 | if (this.autoPlace) { 1966 | autoPlaceContainer.removeChild(this.domElement); 1967 | } 1968 | var _this = this; 1969 | Common.each(this.__folders, function (subfolder) { 1970 | _this.removeFolder(subfolder); 1971 | }); 1972 | dom.unbind(window, 'keydown', GUI._keydownHandler, false); 1973 | removeListeners(this); 1974 | }, 1975 | addFolder: function addFolder(name) { 1976 | if (this.__folders[name] !== undefined) { 1977 | throw new Error('You already have a folder in this GUI by the' + ' name "' + name + '"'); 1978 | } 1979 | var newGuiParams = { name: name, parent: this }; 1980 | newGuiParams.autoPlace = this.autoPlace; 1981 | if (this.load && 1982 | this.load.folders && 1983 | this.load.folders[name]) { 1984 | newGuiParams.closed = this.load.folders[name].closed; 1985 | newGuiParams.load = this.load.folders[name]; 1986 | } 1987 | var gui = new GUI(newGuiParams); 1988 | this.__folders[name] = gui; 1989 | var li = addRow(this, gui.domElement); 1990 | dom.addClass(li, 'folder'); 1991 | return gui; 1992 | }, 1993 | removeFolder: function removeFolder(folder) { 1994 | this.__ul.removeChild(folder.domElement.parentElement); 1995 | delete this.__folders[folder.name]; 1996 | if (this.load && 1997 | this.load.folders && 1998 | this.load.folders[folder.name]) { 1999 | delete this.load.folders[folder.name]; 2000 | } 2001 | removeListeners(folder); 2002 | var _this = this; 2003 | Common.each(folder.__folders, function (subfolder) { 2004 | folder.removeFolder(subfolder); 2005 | }); 2006 | Common.defer(function () { 2007 | _this.onResize(); 2008 | }); 2009 | }, 2010 | open: function open() { 2011 | this.closed = false; 2012 | }, 2013 | close: function close() { 2014 | this.closed = true; 2015 | }, 2016 | hide: function hide() { 2017 | this.domElement.style.display = 'none'; 2018 | }, 2019 | show: function show() { 2020 | this.domElement.style.display = ''; 2021 | }, 2022 | onResize: function onResize() { 2023 | var root = this.getRoot(); 2024 | if (root.scrollable) { 2025 | var top = dom.getOffset(root.__ul).top; 2026 | var h = 0; 2027 | Common.each(root.__ul.childNodes, function (node) { 2028 | if (!(root.autoPlace && node === root.__save_row)) { 2029 | h += dom.getHeight(node); 2030 | } 2031 | }); 2032 | if (window.innerHeight - top - CLOSE_BUTTON_HEIGHT < h) { 2033 | dom.addClass(root.domElement, GUI.CLASS_TOO_TALL); 2034 | root.__ul.style.height = window.innerHeight - top - CLOSE_BUTTON_HEIGHT + 'px'; 2035 | } else { 2036 | dom.removeClass(root.domElement, GUI.CLASS_TOO_TALL); 2037 | root.__ul.style.height = 'auto'; 2038 | } 2039 | } 2040 | if (root.__resize_handle) { 2041 | Common.defer(function () { 2042 | root.__resize_handle.style.height = root.__ul.offsetHeight + 'px'; 2043 | }); 2044 | } 2045 | if (root.__closeButton) { 2046 | root.__closeButton.style.width = root.width + 'px'; 2047 | } 2048 | }, 2049 | onResizeDebounced: Common.debounce(function () { 2050 | this.onResize(); 2051 | }, 50), 2052 | remember: function remember() { 2053 | if (Common.isUndefined(SAVE_DIALOGUE)) { 2054 | SAVE_DIALOGUE = new CenteredDiv(); 2055 | SAVE_DIALOGUE.domElement.innerHTML = saveDialogContents; 2056 | } 2057 | if (this.parent) { 2058 | throw new Error('You can only call remember on a top level GUI.'); 2059 | } 2060 | var _this = this; 2061 | Common.each(Array.prototype.slice.call(arguments), function (object) { 2062 | if (_this.__rememberedObjects.length === 0) { 2063 | addSaveMenu(_this); 2064 | } 2065 | if (_this.__rememberedObjects.indexOf(object) === -1) { 2066 | _this.__rememberedObjects.push(object); 2067 | } 2068 | }); 2069 | if (this.autoPlace) { 2070 | setWidth(this, this.width); 2071 | } 2072 | }, 2073 | getRoot: function getRoot() { 2074 | var gui = this; 2075 | while (gui.parent) { 2076 | gui = gui.parent; 2077 | } 2078 | return gui; 2079 | }, 2080 | getSaveObject: function getSaveObject() { 2081 | var toReturn = this.load; 2082 | toReturn.closed = this.closed; 2083 | if (this.__rememberedObjects.length > 0) { 2084 | toReturn.preset = this.preset; 2085 | if (!toReturn.remembered) { 2086 | toReturn.remembered = {}; 2087 | } 2088 | toReturn.remembered[this.preset] = getCurrentPreset(this); 2089 | } 2090 | toReturn.folders = {}; 2091 | Common.each(this.__folders, function (element, key) { 2092 | toReturn.folders[key] = element.getSaveObject(); 2093 | }); 2094 | return toReturn; 2095 | }, 2096 | save: function save() { 2097 | if (!this.load.remembered) { 2098 | this.load.remembered = {}; 2099 | } 2100 | this.load.remembered[this.preset] = getCurrentPreset(this); 2101 | markPresetModified(this, false); 2102 | this.saveToLocalStorageIfPossible(); 2103 | }, 2104 | saveAs: function saveAs(presetName) { 2105 | if (!this.load.remembered) { 2106 | this.load.remembered = {}; 2107 | this.load.remembered[DEFAULT_DEFAULT_PRESET_NAME] = getCurrentPreset(this, true); 2108 | } 2109 | this.load.remembered[presetName] = getCurrentPreset(this); 2110 | this.preset = presetName; 2111 | addPresetOption(this, presetName, true); 2112 | this.saveToLocalStorageIfPossible(); 2113 | }, 2114 | revert: function revert(gui) { 2115 | Common.each(this.__controllers, function (controller) { 2116 | if (!this.getRoot().load.remembered) { 2117 | controller.setValue(controller.initialValue); 2118 | } else { 2119 | recallSavedValue(gui || this.getRoot(), controller); 2120 | } 2121 | if (controller.__onFinishChange) { 2122 | controller.__onFinishChange.call(controller, controller.getValue()); 2123 | } 2124 | }, this); 2125 | Common.each(this.__folders, function (folder) { 2126 | folder.revert(folder); 2127 | }); 2128 | if (!gui) { 2129 | markPresetModified(this.getRoot(), false); 2130 | } 2131 | }, 2132 | listen: function listen(controller) { 2133 | var init = this.__listening.length === 0; 2134 | this.__listening.push(controller); 2135 | if (init) { 2136 | updateDisplays(this.__listening); 2137 | } 2138 | }, 2139 | updateDisplay: function updateDisplay() { 2140 | Common.each(this.__controllers, function (controller) { 2141 | controller.updateDisplay(); 2142 | }); 2143 | Common.each(this.__folders, function (folder) { 2144 | folder.updateDisplay(); 2145 | }); 2146 | } 2147 | }); 2148 | function addRow(gui, newDom, liBefore) { 2149 | var li = document.createElement('li'); 2150 | if (newDom) { 2151 | li.appendChild(newDom); 2152 | } 2153 | if (liBefore) { 2154 | gui.__ul.insertBefore(li, liBefore); 2155 | } else { 2156 | gui.__ul.appendChild(li); 2157 | } 2158 | gui.onResize(); 2159 | return li; 2160 | } 2161 | function removeListeners(gui) { 2162 | dom.unbind(window, 'resize', gui.__resizeHandler); 2163 | if (gui.saveToLocalStorageIfPossible) { 2164 | dom.unbind(window, 'unload', gui.saveToLocalStorageIfPossible); 2165 | } 2166 | } 2167 | function markPresetModified(gui, modified) { 2168 | var opt = gui.__preset_select[gui.__preset_select.selectedIndex]; 2169 | if (modified) { 2170 | opt.innerHTML = opt.value + '*'; 2171 | } else { 2172 | opt.innerHTML = opt.value; 2173 | } 2174 | } 2175 | function augmentController(gui, li, controller) { 2176 | controller.__li = li; 2177 | controller.__gui = gui; 2178 | Common.extend(controller, { 2179 | options: function options(_options) { 2180 | if (arguments.length > 1) { 2181 | var nextSibling = controller.__li.nextElementSibling; 2182 | controller.remove(); 2183 | return _add(gui, controller.object, controller.property, { 2184 | before: nextSibling, 2185 | factoryArgs: [Common.toArray(arguments)] 2186 | }); 2187 | } 2188 | if (Common.isArray(_options) || Common.isObject(_options)) { 2189 | var _nextSibling = controller.__li.nextElementSibling; 2190 | controller.remove(); 2191 | return _add(gui, controller.object, controller.property, { 2192 | before: _nextSibling, 2193 | factoryArgs: [_options] 2194 | }); 2195 | } 2196 | }, 2197 | name: function name(_name) { 2198 | controller.__li.firstElementChild.firstElementChild.innerHTML = _name; 2199 | return controller; 2200 | }, 2201 | listen: function listen() { 2202 | controller.__gui.listen(controller); 2203 | return controller; 2204 | }, 2205 | remove: function remove() { 2206 | controller.__gui.remove(controller); 2207 | return controller; 2208 | } 2209 | }); 2210 | if (controller instanceof NumberControllerSlider) { 2211 | var box = new NumberControllerBox(controller.object, controller.property, { min: controller.__min, max: controller.__max, step: controller.__step }); 2212 | Common.each(['updateDisplay', 'onChange', 'onFinishChange', 'step', 'min', 'max'], function (method) { 2213 | var pc = controller[method]; 2214 | var pb = box[method]; 2215 | controller[method] = box[method] = function () { 2216 | var args = Array.prototype.slice.call(arguments); 2217 | pb.apply(box, args); 2218 | return pc.apply(controller, args); 2219 | }; 2220 | }); 2221 | dom.addClass(li, 'has-slider'); 2222 | controller.domElement.insertBefore(box.domElement, controller.domElement.firstElementChild); 2223 | } else if (controller instanceof NumberControllerBox) { 2224 | var r = function r(returned) { 2225 | if (Common.isNumber(controller.__min) && Common.isNumber(controller.__max)) { 2226 | var oldName = controller.__li.firstElementChild.firstElementChild.innerHTML; 2227 | var wasListening = controller.__gui.__listening.indexOf(controller) > -1; 2228 | controller.remove(); 2229 | var newController = _add(gui, controller.object, controller.property, { 2230 | before: controller.__li.nextElementSibling, 2231 | factoryArgs: [controller.__min, controller.__max, controller.__step] 2232 | }); 2233 | newController.name(oldName); 2234 | if (wasListening) newController.listen(); 2235 | return newController; 2236 | } 2237 | return returned; 2238 | }; 2239 | controller.min = Common.compose(r, controller.min); 2240 | controller.max = Common.compose(r, controller.max); 2241 | } else if (controller instanceof BooleanController) { 2242 | dom.bind(li, 'click', function () { 2243 | dom.fakeEvent(controller.__checkbox, 'click'); 2244 | }); 2245 | dom.bind(controller.__checkbox, 'click', function (e) { 2246 | e.stopPropagation(); 2247 | }); 2248 | } else if (controller instanceof FunctionController) { 2249 | dom.bind(li, 'click', function () { 2250 | dom.fakeEvent(controller.__button, 'click'); 2251 | }); 2252 | dom.bind(li, 'mouseover', function () { 2253 | dom.addClass(controller.__button, 'hover'); 2254 | }); 2255 | dom.bind(li, 'mouseout', function () { 2256 | dom.removeClass(controller.__button, 'hover'); 2257 | }); 2258 | } else if (controller instanceof ColorController) { 2259 | dom.addClass(li, 'color'); 2260 | controller.updateDisplay = Common.compose(function (val) { 2261 | li.style.borderLeftColor = controller.__color.toString(); 2262 | return val; 2263 | }, controller.updateDisplay); 2264 | controller.updateDisplay(); 2265 | } 2266 | controller.setValue = Common.compose(function (val) { 2267 | if (gui.getRoot().__preset_select && controller.isModified()) { 2268 | markPresetModified(gui.getRoot(), true); 2269 | } 2270 | return val; 2271 | }, controller.setValue); 2272 | } 2273 | function recallSavedValue(gui, controller) { 2274 | var root = gui.getRoot(); 2275 | var matchedIndex = root.__rememberedObjects.indexOf(controller.object); 2276 | if (matchedIndex !== -1) { 2277 | var controllerMap = root.__rememberedObjectIndecesToControllers[matchedIndex]; 2278 | if (controllerMap === undefined) { 2279 | controllerMap = {}; 2280 | root.__rememberedObjectIndecesToControllers[matchedIndex] = controllerMap; 2281 | } 2282 | controllerMap[controller.property] = controller; 2283 | if (root.load && root.load.remembered) { 2284 | var presetMap = root.load.remembered; 2285 | var preset = void 0; 2286 | if (presetMap[gui.preset]) { 2287 | preset = presetMap[gui.preset]; 2288 | } else if (presetMap[DEFAULT_DEFAULT_PRESET_NAME]) { 2289 | preset = presetMap[DEFAULT_DEFAULT_PRESET_NAME]; 2290 | } else { 2291 | return; 2292 | } 2293 | if (preset[matchedIndex] && preset[matchedIndex][controller.property] !== undefined) { 2294 | var value = preset[matchedIndex][controller.property]; 2295 | controller.initialValue = value; 2296 | controller.setValue(value); 2297 | } 2298 | } 2299 | } 2300 | } 2301 | function _add(gui, object, property, params) { 2302 | if (object[property] === undefined) { 2303 | throw new Error('Object "' + object + '" has no property "' + property + '"'); 2304 | } 2305 | var controller = void 0; 2306 | if (params.color) { 2307 | controller = new ColorController(object, property); 2308 | } else { 2309 | var factoryArgs = [object, property].concat(params.factoryArgs); 2310 | controller = ControllerFactory.apply(gui, factoryArgs); 2311 | } 2312 | if (params.before instanceof Controller) { 2313 | params.before = params.before.__li; 2314 | } 2315 | recallSavedValue(gui, controller); 2316 | dom.addClass(controller.domElement, 'c'); 2317 | var name = document.createElement('span'); 2318 | dom.addClass(name, 'property-name'); 2319 | name.innerHTML = controller.property; 2320 | var container = document.createElement('div'); 2321 | container.appendChild(name); 2322 | container.appendChild(controller.domElement); 2323 | var li = addRow(gui, container, params.before); 2324 | dom.addClass(li, GUI.CLASS_CONTROLLER_ROW); 2325 | if (controller instanceof ColorController) { 2326 | dom.addClass(li, 'color'); 2327 | } else { 2328 | dom.addClass(li, _typeof(controller.getValue())); 2329 | } 2330 | augmentController(gui, li, controller); 2331 | gui.__controllers.push(controller); 2332 | return controller; 2333 | } 2334 | function getLocalStorageHash(gui, key) { 2335 | return document.location.href + '.' + key; 2336 | } 2337 | function addPresetOption(gui, name, setSelected) { 2338 | var opt = document.createElement('option'); 2339 | opt.innerHTML = name; 2340 | opt.value = name; 2341 | gui.__preset_select.appendChild(opt); 2342 | if (setSelected) { 2343 | gui.__preset_select.selectedIndex = gui.__preset_select.length - 1; 2344 | } 2345 | } 2346 | function showHideExplain(gui, explain) { 2347 | explain.style.display = gui.useLocalStorage ? 'block' : 'none'; 2348 | } 2349 | function addSaveMenu(gui) { 2350 | var div = gui.__save_row = document.createElement('li'); 2351 | dom.addClass(gui.domElement, 'has-save'); 2352 | gui.__ul.insertBefore(div, gui.__ul.firstChild); 2353 | dom.addClass(div, 'save-row'); 2354 | var gears = document.createElement('span'); 2355 | gears.innerHTML = ' '; 2356 | dom.addClass(gears, 'button gears'); 2357 | var button = document.createElement('span'); 2358 | button.innerHTML = 'Save'; 2359 | dom.addClass(button, 'button'); 2360 | dom.addClass(button, 'save'); 2361 | var button2 = document.createElement('span'); 2362 | button2.innerHTML = 'New'; 2363 | dom.addClass(button2, 'button'); 2364 | dom.addClass(button2, 'save-as'); 2365 | var button3 = document.createElement('span'); 2366 | button3.innerHTML = 'Revert'; 2367 | dom.addClass(button3, 'button'); 2368 | dom.addClass(button3, 'revert'); 2369 | var select = gui.__preset_select = document.createElement('select'); 2370 | if (gui.load && gui.load.remembered) { 2371 | Common.each(gui.load.remembered, function (value, key) { 2372 | addPresetOption(gui, key, key === gui.preset); 2373 | }); 2374 | } else { 2375 | addPresetOption(gui, DEFAULT_DEFAULT_PRESET_NAME, false); 2376 | } 2377 | dom.bind(select, 'change', function () { 2378 | for (var index = 0; index < gui.__preset_select.length; index++) { 2379 | gui.__preset_select[index].innerHTML = gui.__preset_select[index].value; 2380 | } 2381 | gui.preset = this.value; 2382 | }); 2383 | div.appendChild(select); 2384 | div.appendChild(gears); 2385 | div.appendChild(button); 2386 | div.appendChild(button2); 2387 | div.appendChild(button3); 2388 | if (SUPPORTS_LOCAL_STORAGE) { 2389 | var explain = document.getElementById('dg-local-explain'); 2390 | var localStorageCheckBox = document.getElementById('dg-local-storage'); 2391 | var saveLocally = document.getElementById('dg-save-locally'); 2392 | saveLocally.style.display = 'block'; 2393 | if (localStorage.getItem(getLocalStorageHash(gui, 'isLocal')) === 'true') { 2394 | localStorageCheckBox.setAttribute('checked', 'checked'); 2395 | } 2396 | showHideExplain(gui, explain); 2397 | dom.bind(localStorageCheckBox, 'change', function () { 2398 | gui.useLocalStorage = !gui.useLocalStorage; 2399 | showHideExplain(gui, explain); 2400 | }); 2401 | } 2402 | var newConstructorTextArea = document.getElementById('dg-new-constructor'); 2403 | dom.bind(newConstructorTextArea, 'keydown', function (e) { 2404 | if (e.metaKey && (e.which === 67 || e.keyCode === 67)) { 2405 | SAVE_DIALOGUE.hide(); 2406 | } 2407 | }); 2408 | dom.bind(gears, 'click', function () { 2409 | newConstructorTextArea.innerHTML = JSON.stringify(gui.getSaveObject(), undefined, 2); 2410 | SAVE_DIALOGUE.show(); 2411 | newConstructorTextArea.focus(); 2412 | newConstructorTextArea.select(); 2413 | }); 2414 | dom.bind(button, 'click', function () { 2415 | gui.save(); 2416 | }); 2417 | dom.bind(button2, 'click', function () { 2418 | var presetName = prompt('Enter a new preset name.'); 2419 | if (presetName) { 2420 | gui.saveAs(presetName); 2421 | } 2422 | }); 2423 | dom.bind(button3, 'click', function () { 2424 | gui.revert(); 2425 | }); 2426 | } 2427 | function addResizeHandle(gui) { 2428 | var pmouseX = void 0; 2429 | gui.__resize_handle = document.createElement('div'); 2430 | Common.extend(gui.__resize_handle.style, { 2431 | width: '6px', 2432 | marginLeft: '-3px', 2433 | height: '200px', 2434 | cursor: 'ew-resize', 2435 | position: 'absolute' 2436 | }); 2437 | function drag(e) { 2438 | e.preventDefault(); 2439 | gui.width += pmouseX - e.clientX; 2440 | gui.onResize(); 2441 | pmouseX = e.clientX; 2442 | return false; 2443 | } 2444 | function dragStop() { 2445 | dom.removeClass(gui.__closeButton, GUI.CLASS_DRAG); 2446 | dom.unbind(window, 'mousemove', drag); 2447 | dom.unbind(window, 'mouseup', dragStop); 2448 | } 2449 | function dragStart(e) { 2450 | e.preventDefault(); 2451 | pmouseX = e.clientX; 2452 | dom.addClass(gui.__closeButton, GUI.CLASS_DRAG); 2453 | dom.bind(window, 'mousemove', drag); 2454 | dom.bind(window, 'mouseup', dragStop); 2455 | return false; 2456 | } 2457 | dom.bind(gui.__resize_handle, 'mousedown', dragStart); 2458 | dom.bind(gui.__closeButton, 'mousedown', dragStart); 2459 | gui.domElement.insertBefore(gui.__resize_handle, gui.domElement.firstElementChild); 2460 | } 2461 | function setWidth(gui, w) { 2462 | gui.domElement.style.width = w + 'px'; 2463 | if (gui.__save_row && gui.autoPlace) { 2464 | gui.__save_row.style.width = w + 'px'; 2465 | } 2466 | if (gui.__closeButton) { 2467 | gui.__closeButton.style.width = w + 'px'; 2468 | } 2469 | } 2470 | function getCurrentPreset(gui, useInitialValues) { 2471 | var toReturn = {}; 2472 | Common.each(gui.__rememberedObjects, function (val, index) { 2473 | var savedValues = {}; 2474 | var controllerMap = gui.__rememberedObjectIndecesToControllers[index]; 2475 | Common.each(controllerMap, function (controller, property) { 2476 | savedValues[property] = useInitialValues ? controller.initialValue : controller.getValue(); 2477 | }); 2478 | toReturn[index] = savedValues; 2479 | }); 2480 | return toReturn; 2481 | } 2482 | function setPresetSelectIndex(gui) { 2483 | for (var index = 0; index < gui.__preset_select.length; index++) { 2484 | if (gui.__preset_select[index].value === gui.preset) { 2485 | gui.__preset_select.selectedIndex = index; 2486 | } 2487 | } 2488 | } 2489 | function updateDisplays(controllerArray) { 2490 | if (controllerArray.length !== 0) { 2491 | requestAnimationFrame$1.call(window, function () { 2492 | updateDisplays(controllerArray); 2493 | }); 2494 | } 2495 | Common.each(controllerArray, function (c) { 2496 | c.updateDisplay(); 2497 | }); 2498 | } 2499 | 2500 | var color = { 2501 | Color: Color, 2502 | math: ColorMath, 2503 | interpret: interpret 2504 | }; 2505 | var controllers = { 2506 | Controller: Controller, 2507 | BooleanController: BooleanController, 2508 | OptionController: OptionController, 2509 | StringController: StringController, 2510 | NumberController: NumberController, 2511 | NumberControllerBox: NumberControllerBox, 2512 | NumberControllerSlider: NumberControllerSlider, 2513 | FunctionController: FunctionController, 2514 | ColorController: ColorController 2515 | }; 2516 | var dom$1 = { dom: dom }; 2517 | var gui = { GUI: GUI }; 2518 | var GUI$1 = GUI; 2519 | var index = { 2520 | color: color, 2521 | controllers: controllers, 2522 | dom: dom$1, 2523 | gui: gui, 2524 | GUI: GUI$1 2525 | }; 2526 | 2527 | exports.color = color; 2528 | exports.controllers = controllers; 2529 | exports.dom = dom$1; 2530 | exports.gui = gui; 2531 | exports.GUI = GUI$1; 2532 | exports['default'] = index; 2533 | 2534 | Object.defineProperty(exports, '__esModule', { value: true }); 2535 | 2536 | }))); 2537 | //# sourceMappingURL=dat.gui.js.map 2538 | -------------------------------------------------------------------------------- /index.coffee: -------------------------------------------------------------------------------- 1 | "use strict" 2 | 3 | Phaser = Phaser or @Phaser or window.Phaser or require? "phaser" 4 | 5 | throw new Error "Couldn't find `Phaser` or require 'phaser'." unless Phaser 6 | 7 | {isFinite} = Number 8 | 9 | now = window.performance?.now.bind(window.performance) or Date.now.bind(Date) 10 | 11 | class Phaser.Plugin.AdvancedTiming extends Phaser.Plugin 12 | 13 | @MODE_GRAPH = "graph" 14 | @MODE_METER = "meter" 15 | @MODE_TEXT = "text" 16 | @MODE_DOM_METER = "domMeter" 17 | @MODE_DOM_TEXT = "domText" 18 | @MODE_DEFAULT = @MODE_TEXT 19 | 20 | @colors = 21 | AQUA: "#7FDBFF" 22 | BLUE: "#0074D9" 23 | GRAY: "#666666" 24 | GREEN: "#2ECC40" 25 | NAVY: "#001F3F" 26 | ORANGE: "#FF851B" 27 | PURPLE: "#B10DC9" 28 | RED: "#FF4136" 29 | WHITE: "#FFFFFF" 30 | YELLOW: "#FFDC00" 31 | 32 | @hexColors = 33 | AQUA: 0x7FDBFF 34 | BLUE: 0x0074D9 35 | GRAY: 0x666666 36 | GREEN: 0x2ECC40 37 | NAVY: 0x001F3F 38 | ORANGE: 0xFF851B 39 | PURPLE: 0xB10DC9 40 | RED: 0xFF4136 41 | WHITE: 0xFFFFFF 42 | YELLOW: 0xFFDC00 43 | 44 | @modes = [ 45 | @MODE_DEFAULT 46 | @MODE_GRAPH 47 | @MODE_METER 48 | @MODE_TEXT 49 | @MODE_DOM_METER 50 | @MODE_DOM_TEXT 51 | ] 52 | 53 | @renderTypes = [null, "Canvas", "WebGL", "Headless"] 54 | 55 | alpha: 0.75 56 | enableResumeHandler: yes 57 | lastTextContent: null 58 | name: "Advanced Timing Plugin" 59 | renderDuration: 0 60 | showDurations: yes 61 | showElapsed: no 62 | showSpiraling: yes 63 | styleDomTextLikeDebugFont: no 64 | updateDuration: 0 65 | _mode: null 66 | 67 | Object.defineProperty @prototype, "mode", 68 | get: -> 69 | @_mode 70 | set: (val) -> 71 | return @_mode if val is @_mode 72 | switch val 73 | when @constructor.MODE_GRAPH, @constructor.MODE_METER, @constructor.MODE_TEXT, @constructor.MODE_DOM_TEXT, @constructor.MODE_DOM_METER 74 | @_mode = val 75 | @add() 76 | @activeDisplay = @display[ @_mode ] 77 | else 78 | throw new Error "No such mode: '#{val}'" 79 | @refresh() 80 | @_mode 81 | 82 | init: (options) -> 83 | {game} = this 84 | game.time.advancedTiming = on 85 | @_gameUpdateLogic = @game.updateLogic .bind @game 86 | @_gameUpdateRender = @game.updateRender.bind @game 87 | @game.updateLogic = @updateLogic .bind this 88 | @game.updateRender = @updateRender.bind this 89 | @group = game.make.group null, "advancedTimingPlugin", yes 90 | @position = new Phaser.Point 91 | @renderType = @constructor.renderTypes[game.renderType] 92 | @reset() 93 | game.onResume.add @onResume, this 94 | game.debug.gameInfo = @debugGameInfo.bind this 95 | game.debug.gameTimeInfo = @debugGameTimeInfo.bind this 96 | @display = {} 97 | if options 98 | {mode} = options 99 | delete options.mode 100 | Phaser.Utils.extend this, options 101 | @mode = mode or @constructor.MODE_DEFAULT 102 | return 103 | 104 | update: -> 105 | @group.visible = @visible 106 | if @visible 107 | @updateGraph() if @graphGroup and @graphGroup.visible 108 | @updateMeters() if @meters and @meters.visible 109 | @updateText() if @text and @text.visible 110 | @updateDomMeter() if @domMeter 111 | @updateDomText() if @domText 112 | return 113 | 114 | updateLogic: (timeStep) -> 115 | time = now() 116 | @_gameUpdateLogic timeStep 117 | @updateDuration = now() - time 118 | return 119 | 120 | updateRender: (elapsedTime) -> 121 | time = now() 122 | @_gameUpdateRender elapsedTime 123 | @renderDuration = now() - time 124 | return 125 | 126 | destroy: -> 127 | super 128 | @graph.destroy() 129 | @group.destroy() 130 | return 131 | 132 | add: -> 133 | switch @_mode 134 | when @constructor.MODE_GRAPH then @addGraph() unless @graphGroup 135 | when @constructor.MODE_METER then @addMeters() unless @meters 136 | when @constructor.MODE_TEXT then @addText() unless @text 137 | when @constructor.MODE_DOM_METER then @addDomMeter() unless @domMeter 138 | when @constructor.MODE_DOM_TEXT then @addDomText() unless @domText 139 | else throw new Error "Nothing to add (bad mode: #{@_mode})" 140 | return 141 | 142 | addDomMeter: -> 143 | @domMeter = document.createElement "meter" 144 | @domMeter.setAttribute "class", "ppat-fps ppat-meter" 145 | @domMeter.setAttribute "min", 0 146 | @domMeter.setAttribute "max", @game.time.desiredFps 147 | @domMeter.setAttribute "optimum", @game.time.desiredFps 148 | @game.canvas.parentNode.appendChild @domMeter; 149 | 150 | @display[ @constructor.MODE_DOM_METER ] = @domMeter 151 | 152 | return 153 | 154 | addDomText: -> 155 | @domText = document.createElement "pre" 156 | @domText.setAttribute "class", "ppat-text" 157 | @domText.style.font = @game.debug.font if @styleDomTextLikeDebugFont 158 | @game.canvas.parentNode.appendChild @domText; 159 | 160 | @display[ @constructor.MODE_DOM_TEXT ] = @domText 161 | 162 | return 163 | 164 | addGraph: (x = @position.x, y = @position.y) -> 165 | {desiredFps} = @game.time 166 | desiredMs = @desiredMs() 167 | style = fill: "white", font: "10px monospace" 168 | 169 | @graphGroup = @game.add.group @group, "advancedTimingPluginGraphGroup" 170 | @graphGroup.x = x 171 | @graphGroup.y = y 172 | 173 | @graph = @game.make.bitmapData 60, 60, "advancedTimingPluginGraph" 174 | @graph.fill 0, 0, 0 175 | 176 | @graphX = 0 177 | @graphImage = @game.add.image 0, 0, @graph, null, @graphGroup 178 | @graphImage.alpha = @alpha 179 | @graphImage.scale.set 2 180 | @graphImage.smoothed = no 181 | 182 | {width, height} = @graphImage 183 | scaleY = @graphImage.scale.y 184 | 185 | @game.add.text width, height - scaleY * desiredFps, "#{desiredFps} fps", style, @graphGroup 186 | @game.add.text width, height - scaleY * desiredMs, "#{desiredMs} ms", style, @graphGroup 187 | 188 | @display[ @constructor.MODE_GRAPH ] = @graphGroup 189 | 190 | return 191 | 192 | addMeter: (name, x, y, key, tint, group) -> 193 | name = "#{name}Meter" 194 | meter = group.create x, y, key 195 | meter.height = 10 196 | meter.tint = tint 197 | this[name] = meter 198 | 199 | addMeters: (x = @position.x, y = @position.y) -> 200 | {hexColors} = @constructor 201 | 202 | bt = @game.make.bitmapData(1, 1).fill(255, 255, 255) 203 | 204 | @meters = @game.add.group @group, "advancedTimingPluginMeters" 205 | @meters.alpha = @alpha 206 | @meters.classType = Phaser.Image 207 | @meters.x = x 208 | @meters.y = y 209 | 210 | @fpsMeters = @game.add.group @meters, "advancedTimingPluginFpsMeters" 211 | @elapsedMeters = @game.add.group @meters, "advancedTimingPluginElapsedMeters" 212 | @durationMeters = @game.add.group @meters, "advancedTimingPluginDurationMeters" 213 | 214 | @addMeter "desiredFps", 0, 0, bt, hexColors.GRAY, @fpsMeters 215 | @addMeter "fps", 0, 0, bt, hexColors.BLUE, @fpsMeters 216 | @addMeter "desiredMs", 0, 20, bt, hexColors.GRAY, @elapsedMeters 217 | @addMeter "elapsed", 0, 20, bt, hexColors.GREEN, @elapsedMeters 218 | @addMeter "ms", 0, 20, bt, hexColors.YELLOW, @elapsedMeters 219 | @addMeter "desiredDur", 0, 10, bt, hexColors.GRAY, @durationMeters 220 | @addMeter "updateDuration", 0, 10, bt, hexColors.ORANGE, @durationMeters 221 | @addMeter "renderDuration", 0, 10, bt, hexColors.PURPLE, @durationMeters 222 | 223 | @display[ @constructor.MODE_METER ] = @meters 224 | 225 | return 226 | 227 | addText: (x = @position.x, y = @position.y) -> 228 | @text = @game.add.text x, y, null, 229 | fill: @constructor.colors.WHITE 230 | font: @game.debug.font 231 | , @group 232 | @text.name = "advancedTimingPluginText" 233 | # @text.setTextBounds 10, 10 234 | 235 | @display[ @constructor.MODE_TEXT ] = @text 236 | 237 | return 238 | 239 | debugGameInfo: (x, y, color) -> 240 | {game} = this 241 | {debug} = game 242 | 243 | debug.start x, y, color 244 | debug.line "renderType: #{@renderType}" 245 | debug.line "lockRender: #{game.lockRender}" 246 | debug.line "forceSingleUpdate: #{game.forceSingleUpdate}" 247 | debug.line "updatesThisFrame: #{game.updatesThisFrame}" 248 | debug.line "lastCount: #{game._lastCount}" 249 | debug.line "spiraling: #{game._spiraling}" 250 | debug.stop() 251 | 252 | return 253 | 254 | debugGameTimeInfo: (x, y, color) -> 255 | {game} = this 256 | {debug, time} = game 257 | 258 | debug.start x, y, color 259 | debug.line "fps: #{time.fps} #{@fpsRangeStr()}" 260 | debug.line "desiredFps: #{time.desiredFps}" 261 | debug.line "suggestedFps: #{time.suggestedFps}" 262 | debug.line "elapsed: #{time.elapsed} ms #{@elapsedRangeStr()}" 263 | debug.line "elapsedMS: #{time.elapsedMS} ms" 264 | debug.line "physicsElapsedMS: #{time.physicsElapsedMS.toFixed(2)} ms" 265 | debug.line "slowMotion: #{time.slowMotion}" 266 | debug.stop() 267 | 268 | return 269 | 270 | desiredMs: -> 271 | Math.ceil 1000 / @game.time.desiredFps 272 | 273 | elapsedRange: -> 274 | @game.time.msMax - @game.time.msMin 275 | 276 | elapsedRangeStr: -> 277 | {msMax, msMin} = @game.time 278 | if isFinite(msMax) and isFinite(msMin) then "(#{msMin}–#{msMax})" else "" 279 | 280 | fpsColor: (fps = @game.time.fps) -> 281 | {desiredFps} = @game.time 282 | {colors} = @constructor 283 | switch 284 | when fps < (desiredFps / 2) then return colors.ORANGE 285 | when fps < desiredFps then return colors.YELLOW 286 | else return colors.WHITE 287 | 288 | fpsRange: -> 289 | @game.time.fpsMax - @game.time.fpsMin 290 | 291 | fpsRangeStr: -> 292 | {fpsMax, fpsMin} = @game.time 293 | if isFinite(fpsMax) and isFinite(fpsMin) then "(#{fpsMin}–#{fpsMax})" else "" 294 | 295 | onResume: -> 296 | @reset() 297 | return 298 | 299 | refresh: -> 300 | for name, obj of @display 301 | if obj.setAttribute 302 | if name is @_mode 303 | obj.removeAttribute "hidden" 304 | else 305 | obj.setAttribute "hidden", "" 306 | else 307 | obj.visible = name is @_mode 308 | return 309 | 310 | reset: (fpsMin = Infinity, fpsMax = 0, msMin = Infinity, msMax = 0) -> 311 | {time} = @game 312 | time.fpsMin = fpsMin 313 | time.fpsMax = fpsMax 314 | time.msMin = msMin 315 | time.msMax = msMax 316 | return 317 | 318 | resetElapsed: -> 319 | {time} = @game 320 | time.elapsed = time.now - time.prevTime 321 | return 322 | 323 | textContent: -> 324 | {drawCount} = @game.renderer.renderSession 325 | "#{@game.time.fps} fps #{@renderType}" + (if drawCount then " (#{drawCount})" else "") 326 | 327 | updateDomMeter: -> 328 | @domMeter.value = @game.time.fps 329 | return 330 | 331 | updateDomText: -> 332 | content = @textContent() 333 | 334 | unless content is @lastTextContent 335 | @domText.textContent = @lastTextContent = content 336 | @domText.style.color = @fpsColor() 337 | 338 | return 339 | 340 | updateGraph: -> 341 | {forceSingleUpdate, _spiraling, updatesThisFrame} = @game 342 | {elapsed, elapsedMS, fps} = @game.time 343 | {graph, graphX} = this 344 | {colors} = @constructor 345 | {height} = graph 346 | 347 | graph.dirty = yes 348 | graph.rect graphX, 0, 1, height, "black" 349 | 350 | if fps <= height 351 | graph.rect graphX, (height - fps), 1, 1, colors.BLUE 352 | 353 | if @showElapsed 354 | if elapsed <= height 355 | graph.rect graphX, (height - elapsed), 1, 1, colors.GREEN 356 | if elapsed isnt elapsedMS and elapsed <= height 357 | graph.rect graphX, (height - elapsedMS), 1, 1, colors.YELLOW 358 | unless forceSingleUpdate 359 | graph.rect graphX, (height - updatesThisFrame), 1, 1, colors.NAVY 360 | 361 | if @showDurations 362 | graph.rect graphX, (height - ~~@updateDuration), 1, 1, colors.ORANGE 363 | graph.rect graphX, (height - ~~@renderDuration), 1, 1, colors.PURPLE 364 | 365 | if @showSpiraling and _spiraling > 0 366 | graph.rect graphX, (height - _spiraling), 1, 1, colors.RED 367 | 368 | @graphX += 1 369 | @graphX %= graph.width 370 | return 371 | 372 | updateMeters: -> 373 | {desiredFps, elapsed, elapsedMS, fps} = @game.time 374 | 375 | desiredMs = @desiredMs() 376 | 377 | @desiredFpsMeter.scale.x = desiredFps 378 | @fpsMeter.scale.x = fps 379 | 380 | @elapsedMeters.visible = @showElapsed 381 | if @showElapsed 382 | @desiredMsMeter.scale.x = desiredMs 383 | @msMeter.scale.x = elapsedMS 384 | @elapsedMeter.scale.x = elapsed 385 | 386 | @durationMeters.visible = @showDurations 387 | if @showDurations 388 | @desiredDurMeter.scale.x = desiredMs 389 | @updateDurationMeter.scale.x = @updateDuration 390 | @renderDurationMeter.scale.x = @renderDuration 391 | @renderDurationMeter.x = @updateDurationMeter.width 392 | 393 | return 394 | 395 | updateText: -> 396 | @text.text = @textContent() 397 | @text.style.fill = @fpsColor() 398 | return 399 | 400 | module?.exports = Phaser.Plugin.AdvancedTiming 401 | -------------------------------------------------------------------------------- /index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 10 | Phaser Advanced Timing plugin example 11 | 12 | 13 | 14 | 15 | -------------------------------------------------------------------------------- /index.js: -------------------------------------------------------------------------------- 1 | // Generated by CoffeeScript 1.10.0 2 | (function() { 3 | "use strict"; 4 | var Phaser, isFinite, now, ref, 5 | extend = function(child, parent) { for (var key in parent) { if (hasProp.call(parent, key)) child[key] = parent[key]; } function ctor() { this.constructor = child; } ctor.prototype = parent.prototype; child.prototype = new ctor(); child.__super__ = parent.prototype; return child; }, 6 | hasProp = {}.hasOwnProperty; 7 | 8 | Phaser = Phaser || this.Phaser || window.Phaser || (typeof require === "function" ? require("phaser") : void 0); 9 | 10 | if (!Phaser) { 11 | throw new Error("Couldn't find `Phaser` or require 'phaser'."); 12 | } 13 | 14 | isFinite = Number.isFinite; 15 | 16 | now = ((ref = window.performance) != null ? ref.now.bind(window.performance) : void 0) || Date.now.bind(Date); 17 | 18 | Phaser.Plugin.AdvancedTiming = (function(superClass) { 19 | extend(AdvancedTiming, superClass); 20 | 21 | function AdvancedTiming() { 22 | return AdvancedTiming.__super__.constructor.apply(this, arguments); 23 | } 24 | 25 | AdvancedTiming.MODE_GRAPH = "graph"; 26 | 27 | AdvancedTiming.MODE_METER = "meter"; 28 | 29 | AdvancedTiming.MODE_TEXT = "text"; 30 | 31 | AdvancedTiming.MODE_DOM_METER = "domMeter"; 32 | 33 | AdvancedTiming.MODE_DOM_TEXT = "domText"; 34 | 35 | AdvancedTiming.MODE_DEFAULT = AdvancedTiming.MODE_TEXT; 36 | 37 | AdvancedTiming.colors = { 38 | AQUA: "#7FDBFF", 39 | BLUE: "#0074D9", 40 | GRAY: "#666666", 41 | GREEN: "#2ECC40", 42 | NAVY: "#001F3F", 43 | ORANGE: "#FF851B", 44 | PURPLE: "#B10DC9", 45 | RED: "#FF4136", 46 | WHITE: "#FFFFFF", 47 | YELLOW: "#FFDC00" 48 | }; 49 | 50 | AdvancedTiming.hexColors = { 51 | AQUA: 0x7FDBFF, 52 | BLUE: 0x0074D9, 53 | GRAY: 0x666666, 54 | GREEN: 0x2ECC40, 55 | NAVY: 0x001F3F, 56 | ORANGE: 0xFF851B, 57 | PURPLE: 0xB10DC9, 58 | RED: 0xFF4136, 59 | WHITE: 0xFFFFFF, 60 | YELLOW: 0xFFDC00 61 | }; 62 | 63 | AdvancedTiming.modes = [AdvancedTiming.MODE_DEFAULT, AdvancedTiming.MODE_GRAPH, AdvancedTiming.MODE_METER, AdvancedTiming.MODE_TEXT, AdvancedTiming.MODE_DOM_METER, AdvancedTiming.MODE_DOM_TEXT]; 64 | 65 | AdvancedTiming.renderTypes = [null, "Canvas", "WebGL", "Headless"]; 66 | 67 | AdvancedTiming.prototype.alpha = 0.75; 68 | 69 | AdvancedTiming.prototype.enableResumeHandler = true; 70 | 71 | AdvancedTiming.prototype.lastTextContent = null; 72 | 73 | AdvancedTiming.prototype.name = "Advanced Timing Plugin"; 74 | 75 | AdvancedTiming.prototype.renderDuration = 0; 76 | 77 | AdvancedTiming.prototype.showDurations = true; 78 | 79 | AdvancedTiming.prototype.showElapsed = false; 80 | 81 | AdvancedTiming.prototype.showSpiraling = true; 82 | 83 | AdvancedTiming.prototype.styleDomTextLikeDebugFont = false; 84 | 85 | AdvancedTiming.prototype.updateDuration = 0; 86 | 87 | AdvancedTiming.prototype._mode = null; 88 | 89 | Object.defineProperty(AdvancedTiming.prototype, "mode", { 90 | get: function() { 91 | return this._mode; 92 | }, 93 | set: function(val) { 94 | if (val === this._mode) { 95 | return this._mode; 96 | } 97 | switch (val) { 98 | case this.constructor.MODE_GRAPH: 99 | case this.constructor.MODE_METER: 100 | case this.constructor.MODE_TEXT: 101 | case this.constructor.MODE_DOM_TEXT: 102 | case this.constructor.MODE_DOM_METER: 103 | this._mode = val; 104 | this.add(); 105 | this.activeDisplay = this.display[this._mode]; 106 | break; 107 | default: 108 | throw new Error("No such mode: '" + val + "'"); 109 | } 110 | this.refresh(); 111 | return this._mode; 112 | } 113 | }); 114 | 115 | AdvancedTiming.prototype.init = function(options) { 116 | var game, mode; 117 | game = this.game; 118 | game.time.advancedTiming = true; 119 | this._gameUpdateLogic = this.game.updateLogic.bind(this.game); 120 | this._gameUpdateRender = this.game.updateRender.bind(this.game); 121 | this.game.updateLogic = this.updateLogic.bind(this); 122 | this.game.updateRender = this.updateRender.bind(this); 123 | this.group = game.make.group(null, "advancedTimingPlugin", true); 124 | this.position = new Phaser.Point; 125 | this.renderType = this.constructor.renderTypes[game.renderType]; 126 | this.reset(); 127 | game.onResume.add(this.onResume, this); 128 | game.debug.gameInfo = this.debugGameInfo.bind(this); 129 | game.debug.gameTimeInfo = this.debugGameTimeInfo.bind(this); 130 | this.display = {}; 131 | if (options) { 132 | mode = options.mode; 133 | delete options.mode; 134 | Phaser.Utils.extend(this, options); 135 | } 136 | this.mode = mode || this.constructor.MODE_DEFAULT; 137 | }; 138 | 139 | AdvancedTiming.prototype.update = function() { 140 | this.group.visible = this.visible; 141 | if (this.visible) { 142 | if (this.graphGroup && this.graphGroup.visible) { 143 | this.updateGraph(); 144 | } 145 | if (this.meters && this.meters.visible) { 146 | this.updateMeters(); 147 | } 148 | if (this.text && this.text.visible) { 149 | this.updateText(); 150 | } 151 | if (this.domMeter) { 152 | this.updateDomMeter(); 153 | } 154 | if (this.domText) { 155 | this.updateDomText(); 156 | } 157 | } 158 | }; 159 | 160 | AdvancedTiming.prototype.updateLogic = function(timeStep) { 161 | var time; 162 | time = now(); 163 | this._gameUpdateLogic(timeStep); 164 | this.updateDuration = now() - time; 165 | }; 166 | 167 | AdvancedTiming.prototype.updateRender = function(elapsedTime) { 168 | var time; 169 | time = now(); 170 | this._gameUpdateRender(elapsedTime); 171 | this.renderDuration = now() - time; 172 | }; 173 | 174 | AdvancedTiming.prototype.destroy = function() { 175 | AdvancedTiming.__super__.destroy.apply(this, arguments); 176 | this.graph.destroy(); 177 | this.group.destroy(); 178 | }; 179 | 180 | AdvancedTiming.prototype.add = function() { 181 | switch (this._mode) { 182 | case this.constructor.MODE_GRAPH: 183 | if (!this.graphGroup) { 184 | this.addGraph(); 185 | } 186 | break; 187 | case this.constructor.MODE_METER: 188 | if (!this.meters) { 189 | this.addMeters(); 190 | } 191 | break; 192 | case this.constructor.MODE_TEXT: 193 | if (!this.text) { 194 | this.addText(); 195 | } 196 | break; 197 | case this.constructor.MODE_DOM_METER: 198 | if (!this.domMeter) { 199 | this.addDomMeter(); 200 | } 201 | break; 202 | case this.constructor.MODE_DOM_TEXT: 203 | if (!this.domText) { 204 | this.addDomText(); 205 | } 206 | break; 207 | default: 208 | throw new Error("Nothing to add (bad mode: " + this._mode + ")"); 209 | } 210 | }; 211 | 212 | AdvancedTiming.prototype.addDomMeter = function() { 213 | this.domMeter = document.createElement("meter"); 214 | this.domMeter.setAttribute("class", "ppat-fps ppat-meter"); 215 | this.domMeter.setAttribute("min", 0); 216 | this.domMeter.setAttribute("max", this.game.time.desiredFps); 217 | this.domMeter.setAttribute("optimum", this.game.time.desiredFps); 218 | this.game.canvas.parentNode.appendChild(this.domMeter); 219 | this.display[this.constructor.MODE_DOM_METER] = this.domMeter; 220 | }; 221 | 222 | AdvancedTiming.prototype.addDomText = function() { 223 | this.domText = document.createElement("pre"); 224 | this.domText.setAttribute("class", "ppat-text"); 225 | if (this.styleDomTextLikeDebugFont) { 226 | this.domText.style.font = this.game.debug.font; 227 | } 228 | this.game.canvas.parentNode.appendChild(this.domText); 229 | this.display[this.constructor.MODE_DOM_TEXT] = this.domText; 230 | }; 231 | 232 | AdvancedTiming.prototype.addGraph = function(x, y) { 233 | var desiredFps, desiredMs, height, ref1, scaleY, style, width; 234 | if (x == null) { 235 | x = this.position.x; 236 | } 237 | if (y == null) { 238 | y = this.position.y; 239 | } 240 | desiredFps = this.game.time.desiredFps; 241 | desiredMs = this.desiredMs(); 242 | style = { 243 | fill: "white", 244 | font: "10px monospace" 245 | }; 246 | this.graphGroup = this.game.add.group(this.group, "advancedTimingPluginGraphGroup"); 247 | this.graphGroup.x = x; 248 | this.graphGroup.y = y; 249 | this.graph = this.game.make.bitmapData(60, 60, "advancedTimingPluginGraph"); 250 | this.graph.fill(0, 0, 0); 251 | this.graphX = 0; 252 | this.graphImage = this.game.add.image(0, 0, this.graph, null, this.graphGroup); 253 | this.graphImage.alpha = this.alpha; 254 | this.graphImage.scale.set(2); 255 | this.graphImage.smoothed = false; 256 | ref1 = this.graphImage, width = ref1.width, height = ref1.height; 257 | scaleY = this.graphImage.scale.y; 258 | this.game.add.text(width, height - scaleY * desiredFps, desiredFps + " fps", style, this.graphGroup); 259 | this.game.add.text(width, height - scaleY * desiredMs, desiredMs + " ms", style, this.graphGroup); 260 | this.display[this.constructor.MODE_GRAPH] = this.graphGroup; 261 | }; 262 | 263 | AdvancedTiming.prototype.addMeter = function(name, x, y, key, tint, group) { 264 | var meter; 265 | name = name + "Meter"; 266 | meter = group.create(x, y, key); 267 | meter.height = 10; 268 | meter.tint = tint; 269 | return this[name] = meter; 270 | }; 271 | 272 | AdvancedTiming.prototype.addMeters = function(x, y) { 273 | var bt, hexColors; 274 | if (x == null) { 275 | x = this.position.x; 276 | } 277 | if (y == null) { 278 | y = this.position.y; 279 | } 280 | hexColors = this.constructor.hexColors; 281 | bt = this.game.make.bitmapData(1, 1).fill(255, 255, 255); 282 | this.meters = this.game.add.group(this.group, "advancedTimingPluginMeters"); 283 | this.meters.alpha = this.alpha; 284 | this.meters.classType = Phaser.Image; 285 | this.meters.x = x; 286 | this.meters.y = y; 287 | this.fpsMeters = this.game.add.group(this.meters, "advancedTimingPluginFpsMeters"); 288 | this.elapsedMeters = this.game.add.group(this.meters, "advancedTimingPluginElapsedMeters"); 289 | this.durationMeters = this.game.add.group(this.meters, "advancedTimingPluginDurationMeters"); 290 | this.addMeter("desiredFps", 0, 0, bt, hexColors.GRAY, this.fpsMeters); 291 | this.addMeter("fps", 0, 0, bt, hexColors.BLUE, this.fpsMeters); 292 | this.addMeter("desiredMs", 0, 20, bt, hexColors.GRAY, this.elapsedMeters); 293 | this.addMeter("elapsed", 0, 20, bt, hexColors.GREEN, this.elapsedMeters); 294 | this.addMeter("ms", 0, 20, bt, hexColors.YELLOW, this.elapsedMeters); 295 | this.addMeter("desiredDur", 0, 10, bt, hexColors.GRAY, this.durationMeters); 296 | this.addMeter("updateDuration", 0, 10, bt, hexColors.ORANGE, this.durationMeters); 297 | this.addMeter("renderDuration", 0, 10, bt, hexColors.PURPLE, this.durationMeters); 298 | this.display[this.constructor.MODE_METER] = this.meters; 299 | }; 300 | 301 | AdvancedTiming.prototype.addText = function(x, y) { 302 | if (x == null) { 303 | x = this.position.x; 304 | } 305 | if (y == null) { 306 | y = this.position.y; 307 | } 308 | this.text = this.game.add.text(x, y, null, { 309 | fill: this.constructor.colors.WHITE, 310 | font: this.game.debug.font 311 | }, this.group); 312 | this.text.name = "advancedTimingPluginText"; 313 | this.display[this.constructor.MODE_TEXT] = this.text; 314 | }; 315 | 316 | AdvancedTiming.prototype.debugGameInfo = function(x, y, color) { 317 | var debug, game; 318 | game = this.game; 319 | debug = game.debug; 320 | debug.start(x, y, color); 321 | debug.line("renderType: " + this.renderType); 322 | debug.line("lockRender: " + game.lockRender); 323 | debug.line("forceSingleUpdate: " + game.forceSingleUpdate); 324 | debug.line("updatesThisFrame: " + game.updatesThisFrame); 325 | debug.line("lastCount: " + game._lastCount); 326 | debug.line("spiraling: " + game._spiraling); 327 | debug.stop(); 328 | }; 329 | 330 | AdvancedTiming.prototype.debugGameTimeInfo = function(x, y, color) { 331 | var debug, game, time; 332 | game = this.game; 333 | debug = game.debug, time = game.time; 334 | debug.start(x, y, color); 335 | debug.line("fps: " + time.fps + " " + (this.fpsRangeStr())); 336 | debug.line("desiredFps: " + time.desiredFps); 337 | debug.line("suggestedFps: " + time.suggestedFps); 338 | debug.line("elapsed: " + time.elapsed + " ms " + (this.elapsedRangeStr())); 339 | debug.line("elapsedMS: " + time.elapsedMS + " ms"); 340 | debug.line("physicsElapsedMS: " + (time.physicsElapsedMS.toFixed(2)) + " ms"); 341 | debug.line("slowMotion: " + time.slowMotion); 342 | debug.stop(); 343 | }; 344 | 345 | AdvancedTiming.prototype.desiredMs = function() { 346 | return Math.ceil(1000 / this.game.time.desiredFps); 347 | }; 348 | 349 | AdvancedTiming.prototype.elapsedRange = function() { 350 | return this.game.time.msMax - this.game.time.msMin; 351 | }; 352 | 353 | AdvancedTiming.prototype.elapsedRangeStr = function() { 354 | var msMax, msMin, ref1; 355 | ref1 = this.game.time, msMax = ref1.msMax, msMin = ref1.msMin; 356 | if (isFinite(msMax) && isFinite(msMin)) { 357 | return "(" + msMin + "–" + msMax + ")"; 358 | } else { 359 | return ""; 360 | } 361 | }; 362 | 363 | AdvancedTiming.prototype.fpsColor = function(fps) { 364 | var colors, desiredFps; 365 | if (fps == null) { 366 | fps = this.game.time.fps; 367 | } 368 | desiredFps = this.game.time.desiredFps; 369 | colors = this.constructor.colors; 370 | switch (false) { 371 | case !(fps < (desiredFps / 2)): 372 | return colors.ORANGE; 373 | case !(fps < desiredFps): 374 | return colors.YELLOW; 375 | default: 376 | return colors.WHITE; 377 | } 378 | }; 379 | 380 | AdvancedTiming.prototype.fpsRange = function() { 381 | return this.game.time.fpsMax - this.game.time.fpsMin; 382 | }; 383 | 384 | AdvancedTiming.prototype.fpsRangeStr = function() { 385 | var fpsMax, fpsMin, ref1; 386 | ref1 = this.game.time, fpsMax = ref1.fpsMax, fpsMin = ref1.fpsMin; 387 | if (isFinite(fpsMax) && isFinite(fpsMin)) { 388 | return "(" + fpsMin + "–" + fpsMax + ")"; 389 | } else { 390 | return ""; 391 | } 392 | }; 393 | 394 | AdvancedTiming.prototype.onResume = function() { 395 | this.reset(); 396 | }; 397 | 398 | AdvancedTiming.prototype.refresh = function() { 399 | var name, obj, ref1; 400 | ref1 = this.display; 401 | for (name in ref1) { 402 | obj = ref1[name]; 403 | if (obj.setAttribute) { 404 | if (name === this._mode) { 405 | obj.removeAttribute("hidden"); 406 | } else { 407 | obj.setAttribute("hidden", ""); 408 | } 409 | } else { 410 | obj.visible = name === this._mode; 411 | } 412 | } 413 | }; 414 | 415 | AdvancedTiming.prototype.reset = function(fpsMin, fpsMax, msMin, msMax) { 416 | var time; 417 | if (fpsMin == null) { 418 | fpsMin = Infinity; 419 | } 420 | if (fpsMax == null) { 421 | fpsMax = 0; 422 | } 423 | if (msMin == null) { 424 | msMin = Infinity; 425 | } 426 | if (msMax == null) { 427 | msMax = 0; 428 | } 429 | time = this.game.time; 430 | time.fpsMin = fpsMin; 431 | time.fpsMax = fpsMax; 432 | time.msMin = msMin; 433 | time.msMax = msMax; 434 | }; 435 | 436 | AdvancedTiming.prototype.resetElapsed = function() { 437 | var time; 438 | time = this.game.time; 439 | time.elapsed = time.now - time.prevTime; 440 | }; 441 | 442 | AdvancedTiming.prototype.textContent = function() { 443 | var drawCount; 444 | drawCount = this.game.renderer.renderSession.drawCount; 445 | return (this.game.time.fps + " fps " + this.renderType) + (drawCount ? " (" + drawCount + ")" : ""); 446 | }; 447 | 448 | AdvancedTiming.prototype.updateDomMeter = function() { 449 | this.domMeter.value = this.game.time.fps; 450 | }; 451 | 452 | AdvancedTiming.prototype.updateDomText = function() { 453 | var content; 454 | content = this.textContent(); 455 | if (content !== this.lastTextContent) { 456 | this.domText.textContent = this.lastTextContent = content; 457 | this.domText.style.color = this.fpsColor(); 458 | } 459 | }; 460 | 461 | AdvancedTiming.prototype.updateGraph = function() { 462 | var _spiraling, colors, elapsed, elapsedMS, forceSingleUpdate, fps, graph, graphX, height, ref1, ref2, updatesThisFrame; 463 | ref1 = this.game, forceSingleUpdate = ref1.forceSingleUpdate, _spiraling = ref1._spiraling, updatesThisFrame = ref1.updatesThisFrame; 464 | ref2 = this.game.time, elapsed = ref2.elapsed, elapsedMS = ref2.elapsedMS, fps = ref2.fps; 465 | graph = this.graph, graphX = this.graphX; 466 | colors = this.constructor.colors; 467 | height = graph.height; 468 | graph.dirty = true; 469 | graph.rect(graphX, 0, 1, height, "black"); 470 | if (fps <= height) { 471 | graph.rect(graphX, height - fps, 1, 1, colors.BLUE); 472 | } 473 | if (this.showElapsed) { 474 | if (elapsed <= height) { 475 | graph.rect(graphX, height - elapsed, 1, 1, colors.GREEN); 476 | } 477 | if (elapsed !== elapsedMS && elapsed <= height) { 478 | graph.rect(graphX, height - elapsedMS, 1, 1, colors.YELLOW); 479 | } 480 | if (!forceSingleUpdate) { 481 | graph.rect(graphX, height - updatesThisFrame, 1, 1, colors.NAVY); 482 | } 483 | } 484 | if (this.showDurations) { 485 | graph.rect(graphX, height - ~~this.updateDuration, 1, 1, colors.ORANGE); 486 | graph.rect(graphX, height - ~~this.renderDuration, 1, 1, colors.PURPLE); 487 | } 488 | if (this.showSpiraling && _spiraling > 0) { 489 | graph.rect(graphX, height - _spiraling, 1, 1, colors.RED); 490 | } 491 | this.graphX += 1; 492 | this.graphX %= graph.width; 493 | }; 494 | 495 | AdvancedTiming.prototype.updateMeters = function() { 496 | var desiredFps, desiredMs, elapsed, elapsedMS, fps, ref1; 497 | ref1 = this.game.time, desiredFps = ref1.desiredFps, elapsed = ref1.elapsed, elapsedMS = ref1.elapsedMS, fps = ref1.fps; 498 | desiredMs = this.desiredMs(); 499 | this.desiredFpsMeter.scale.x = desiredFps; 500 | this.fpsMeter.scale.x = fps; 501 | this.elapsedMeters.visible = this.showElapsed; 502 | if (this.showElapsed) { 503 | this.desiredMsMeter.scale.x = desiredMs; 504 | this.msMeter.scale.x = elapsedMS; 505 | this.elapsedMeter.scale.x = elapsed; 506 | } 507 | this.durationMeters.visible = this.showDurations; 508 | if (this.showDurations) { 509 | this.desiredDurMeter.scale.x = desiredMs; 510 | this.updateDurationMeter.scale.x = this.updateDuration; 511 | this.renderDurationMeter.scale.x = this.renderDuration; 512 | this.renderDurationMeter.x = this.updateDurationMeter.width; 513 | } 514 | }; 515 | 516 | AdvancedTiming.prototype.updateText = function() { 517 | this.text.text = this.textContent(); 518 | this.text.style.fill = this.fpsColor(); 519 | }; 520 | 521 | return AdvancedTiming; 522 | 523 | })(Phaser.Plugin); 524 | 525 | if (typeof module !== "undefined" && module !== null) { 526 | module.exports = Phaser.Plugin.AdvancedTiming; 527 | } 528 | 529 | }).call(this); 530 | -------------------------------------------------------------------------------- /package-lock.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "phaser-plugin-advanced-timing", 3 | "version": "0.5.1", 4 | "lockfileVersion": 1, 5 | "requires": true, 6 | "dependencies": { 7 | "align-text": { 8 | "version": "0.1.4", 9 | "resolved": "https://registry.npmjs.org/align-text/-/align-text-0.1.4.tgz", 10 | "integrity": "sha1-DNkKVhCT810KmSVsIrcGlDP60Rc=", 11 | "dev": true, 12 | "requires": { 13 | "kind-of": "^3.0.2", 14 | "longest": "^1.0.1", 15 | "repeat-string": "^1.5.2" 16 | } 17 | }, 18 | "async": { 19 | "version": "1.5.2", 20 | "resolved": "https://registry.npmjs.org/async/-/async-1.5.2.tgz", 21 | "integrity": "sha1-7GphrlZIDAw8skHJVhjiCJL5Zyo=", 22 | "dev": true 23 | }, 24 | "balanced-match": { 25 | "version": "1.0.0", 26 | "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.0.tgz", 27 | "integrity": "sha1-ibTRmasr7kneFk6gK4nORi1xt2c=", 28 | "dev": true 29 | }, 30 | "beholder": { 31 | "version": "1.0.0", 32 | "resolved": "https://registry.npmjs.org/beholder/-/beholder-1.0.0.tgz", 33 | "integrity": "sha1-kSpx1YBA9kwuUdaHom0xXfR/TfI=", 34 | "dev": true, 35 | "requires": { 36 | "async": "~1.5.2", 37 | "glob": "~7.0.0", 38 | "minimatch": "^3.0.0" 39 | }, 40 | "dependencies": { 41 | "glob": { 42 | "version": "7.0.6", 43 | "resolved": "https://registry.npmjs.org/glob/-/glob-7.0.6.tgz", 44 | "integrity": "sha1-IRuvr0nlJbjNkyYNFKsTYVKz9Xo=", 45 | "dev": true, 46 | "requires": { 47 | "fs.realpath": "^1.0.0", 48 | "inflight": "^1.0.4", 49 | "inherits": "2", 50 | "minimatch": "^3.0.2", 51 | "once": "^1.3.0", 52 | "path-is-absolute": "^1.0.0" 53 | } 54 | } 55 | } 56 | }, 57 | "brace-expansion": { 58 | "version": "1.1.8", 59 | "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.8.tgz", 60 | "integrity": "sha1-wHshHHyVLsH479Uad+8NHTmQopI=", 61 | "dev": true, 62 | "requires": { 63 | "balanced-match": "^1.0.0", 64 | "concat-map": "0.0.1" 65 | } 66 | }, 67 | "camelcase": { 68 | "version": "1.2.1", 69 | "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-1.2.1.tgz", 70 | "integrity": "sha1-m7UwTS4LVmmLLHWLCKPqqdqlijk=", 71 | "dev": true 72 | }, 73 | "center-align": { 74 | "version": "0.1.3", 75 | "resolved": "https://registry.npmjs.org/center-align/-/center-align-0.1.3.tgz", 76 | "integrity": "sha1-qg0yYptu6XIgBBHL1EYckHvCt60=", 77 | "dev": true, 78 | "requires": { 79 | "align-text": "^0.1.3", 80 | "lazy-cache": "^1.0.3" 81 | } 82 | }, 83 | "cliui": { 84 | "version": "2.1.0", 85 | "resolved": "https://registry.npmjs.org/cliui/-/cliui-2.1.0.tgz", 86 | "integrity": "sha1-S0dXYP+AJkx2LDoXGQMukcf+oNE=", 87 | "dev": true, 88 | "requires": { 89 | "center-align": "^0.1.1", 90 | "right-align": "^0.1.1", 91 | "wordwrap": "0.0.2" 92 | } 93 | }, 94 | "coffee-script": { 95 | "version": "1.10.0", 96 | "resolved": "https://registry.npmjs.org/coffee-script/-/coffee-script-1.10.0.tgz", 97 | "integrity": "sha1-EpOLz5vhlI+gBvkuDEyegXBRCMA=", 98 | "dev": true 99 | }, 100 | "coffeebar": { 101 | "version": "1.0.0", 102 | "resolved": "https://registry.npmjs.org/coffeebar/-/coffeebar-1.0.0.tgz", 103 | "integrity": "sha1-TCFu9keyBcPrFE2+xRdLUz0XNo0=", 104 | "dev": true, 105 | "requires": { 106 | "beholder": "^1.0.0", 107 | "coffee-script": "1.10.0", 108 | "commander": "^2.9.0", 109 | "glob": "^7.0.0", 110 | "mkdirp": "^0.5.1", 111 | "source-map": "^0.5.3", 112 | "uglify-js": "^2.6.1", 113 | "xcolor": "^0.1.0" 114 | } 115 | }, 116 | "commander": { 117 | "version": "2.11.0", 118 | "resolved": "https://registry.npmjs.org/commander/-/commander-2.11.0.tgz", 119 | "integrity": "sha512-b0553uYA5YAEGgyYIGYROzKQ7X5RAqedkfjiZxwi0kL1g3bOaBNNZfYkzt/CL0umgD5wc9Jec2FbB98CjkMRvQ==", 120 | "dev": true 121 | }, 122 | "concat-map": { 123 | "version": "0.0.1", 124 | "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", 125 | "integrity": "sha1-2Klr13/Wjfd5OnMDajug1UBdR3s=", 126 | "dev": true 127 | }, 128 | "decamelize": { 129 | "version": "1.2.0", 130 | "resolved": "https://registry.npmjs.org/decamelize/-/decamelize-1.2.0.tgz", 131 | "integrity": "sha1-9lNNFRSCabIDUue+4m9QH5oZEpA=", 132 | "dev": true 133 | }, 134 | "fs.realpath": { 135 | "version": "1.0.0", 136 | "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", 137 | "integrity": "sha1-FQStJSMVjKpA20onh8sBQRmU6k8=", 138 | "dev": true 139 | }, 140 | "glob": { 141 | "version": "7.1.2", 142 | "resolved": "https://registry.npmjs.org/glob/-/glob-7.1.2.tgz", 143 | "integrity": "sha512-MJTUg1kjuLeQCJ+ccE4Vpa6kKVXkPYJ2mOCQyUuKLcLQsdrMCpBPUi8qVE6+YuaJkozeA9NusTAw3hLr8Xe5EQ==", 144 | "dev": true, 145 | "requires": { 146 | "fs.realpath": "^1.0.0", 147 | "inflight": "^1.0.4", 148 | "inherits": "2", 149 | "minimatch": "^3.0.4", 150 | "once": "^1.3.0", 151 | "path-is-absolute": "^1.0.0" 152 | } 153 | }, 154 | "inflight": { 155 | "version": "1.0.6", 156 | "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz", 157 | "integrity": "sha1-Sb1jMdfQLQwJvJEKEHW6gWW1bfk=", 158 | "dev": true, 159 | "requires": { 160 | "once": "^1.3.0", 161 | "wrappy": "1" 162 | } 163 | }, 164 | "inherits": { 165 | "version": "2.0.3", 166 | "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.3.tgz", 167 | "integrity": "sha1-Yzwsg+PaQqUC9SRmAiSA9CCCYd4=", 168 | "dev": true 169 | }, 170 | "is-buffer": { 171 | "version": "1.1.5", 172 | "resolved": "https://registry.npmjs.org/is-buffer/-/is-buffer-1.1.5.tgz", 173 | "integrity": "sha1-Hzsm72E7IUuIy8ojzGwB2Hlh7sw=", 174 | "dev": true 175 | }, 176 | "kind-of": { 177 | "version": "3.2.2", 178 | "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", 179 | "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", 180 | "dev": true, 181 | "requires": { 182 | "is-buffer": "^1.1.5" 183 | } 184 | }, 185 | "lazy-cache": { 186 | "version": "1.0.4", 187 | "resolved": "https://registry.npmjs.org/lazy-cache/-/lazy-cache-1.0.4.tgz", 188 | "integrity": "sha1-odePw6UEdMuAhF07O24dpJpEbo4=", 189 | "dev": true 190 | }, 191 | "longest": { 192 | "version": "1.0.1", 193 | "resolved": "https://registry.npmjs.org/longest/-/longest-1.0.1.tgz", 194 | "integrity": "sha1-MKCy2jj3N3DoKUoNIuZiXtd9AJc=", 195 | "dev": true 196 | }, 197 | "minimatch": { 198 | "version": "3.1.2", 199 | "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", 200 | "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", 201 | "dev": true, 202 | "requires": { 203 | "brace-expansion": "^1.1.7" 204 | } 205 | }, 206 | "minimist": { 207 | "version": "0.0.8", 208 | "resolved": "https://registry.npmjs.org/minimist/-/minimist-0.0.8.tgz", 209 | "integrity": "sha1-hX/Kv8M5fSYluCKCYuhqp6ARsF0=", 210 | "dev": true 211 | }, 212 | "mkdirp": { 213 | "version": "0.5.1", 214 | "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.1.tgz", 215 | "integrity": "sha1-MAV0OOrGz3+MR2fzhkjWaX11yQM=", 216 | "dev": true, 217 | "requires": { 218 | "minimist": "0.0.8" 219 | } 220 | }, 221 | "once": { 222 | "version": "1.4.0", 223 | "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", 224 | "integrity": "sha1-WDsap3WWHUsROsF9nFC6753Xa9E=", 225 | "dev": true, 226 | "requires": { 227 | "wrappy": "1" 228 | } 229 | }, 230 | "path-is-absolute": { 231 | "version": "1.0.1", 232 | "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz", 233 | "integrity": "sha1-F0uSaHNVNP+8es5r9TpanhtcX18=", 234 | "dev": true 235 | }, 236 | "phaser": { 237 | "version": "2.6.2", 238 | "resolved": "https://registry.npmjs.org/phaser/-/phaser-2.6.2.tgz", 239 | "integrity": "sha1-6zkSFyWiFJxJ9GtdFEMYwivAkkk=" 240 | }, 241 | "repeat-string": { 242 | "version": "1.6.1", 243 | "resolved": "https://registry.npmjs.org/repeat-string/-/repeat-string-1.6.1.tgz", 244 | "integrity": "sha1-jcrkcOHIirwtYA//Sndihtp15jc=", 245 | "dev": true 246 | }, 247 | "right-align": { 248 | "version": "0.1.3", 249 | "resolved": "https://registry.npmjs.org/right-align/-/right-align-0.1.3.tgz", 250 | "integrity": "sha1-YTObci/mo1FWiSENJOFMlhSGE+8=", 251 | "dev": true, 252 | "requires": { 253 | "align-text": "^0.1.1" 254 | } 255 | }, 256 | "source-map": { 257 | "version": "0.5.7", 258 | "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.5.7.tgz", 259 | "integrity": "sha1-igOdLRAh0i0eoUyA2OpGi6LvP8w=", 260 | "dev": true 261 | }, 262 | "uglify-js": { 263 | "version": "2.8.29", 264 | "resolved": "https://registry.npmjs.org/uglify-js/-/uglify-js-2.8.29.tgz", 265 | "integrity": "sha1-KcVzMUgFe7Th913zW3qcty5qWd0=", 266 | "dev": true, 267 | "requires": { 268 | "source-map": "~0.5.1", 269 | "uglify-to-browserify": "~1.0.0", 270 | "yargs": "~3.10.0" 271 | } 272 | }, 273 | "uglify-to-browserify": { 274 | "version": "1.0.2", 275 | "resolved": "https://registry.npmjs.org/uglify-to-browserify/-/uglify-to-browserify-1.0.2.tgz", 276 | "integrity": "sha1-bgkk1r2mta/jSeOabWMoUKD4grc=", 277 | "dev": true, 278 | "optional": true 279 | }, 280 | "window-size": { 281 | "version": "0.1.0", 282 | "resolved": "https://registry.npmjs.org/window-size/-/window-size-0.1.0.tgz", 283 | "integrity": "sha1-VDjNLqk7IC76Ohn+iIeu58lPnJ0=", 284 | "dev": true 285 | }, 286 | "wordwrap": { 287 | "version": "0.0.2", 288 | "resolved": "https://registry.npmjs.org/wordwrap/-/wordwrap-0.0.2.tgz", 289 | "integrity": "sha1-t5Zpu0LstAn4PVg8rVLKF+qhZD8=", 290 | "dev": true 291 | }, 292 | "wrappy": { 293 | "version": "1.0.2", 294 | "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", 295 | "integrity": "sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8=", 296 | "dev": true 297 | }, 298 | "xcolor": { 299 | "version": "0.1.0", 300 | "resolved": "https://registry.npmjs.org/xcolor/-/xcolor-0.1.0.tgz", 301 | "integrity": "sha1-A4W8GjuLPry/Ivd1NlHCC2RiYxA=", 302 | "dev": true 303 | }, 304 | "yargs": { 305 | "version": "3.10.0", 306 | "resolved": "https://registry.npmjs.org/yargs/-/yargs-3.10.0.tgz", 307 | "integrity": "sha1-9+572FfdfB0tOMDnTvvWgdFDH9E=", 308 | "dev": true, 309 | "requires": { 310 | "camelcase": "^1.0.2", 311 | "cliui": "^2.1.0", 312 | "decamelize": "^1.0.0", 313 | "window-size": "0.1.0" 314 | } 315 | } 316 | } 317 | } 318 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "phaser-plugin-advanced-timing", 3 | "version": "0.5.1", 4 | "description": "Shows FPS, frame intervals, draw count, and other performance info", 5 | "main": "index.js", 6 | "scripts": { 7 | "prestart": "open index.html", 8 | "start": "coffeebar --watch *.coffee example/*.coffee", 9 | "preversion": "node -c index.js", 10 | "pub": "np --no-cleanup", 11 | "update": "cp -v node_modules/phaser/build/phaser.js example/vendor/" 12 | }, 13 | "author": "samme", 14 | "license": "MIT", 15 | "directories": { 16 | "example": "example" 17 | }, 18 | "keywords": [ 19 | "phaser", 20 | "phaser-plugin" 21 | ], 22 | "dependencies": { 23 | "phaser": "^2.6.2" 24 | }, 25 | "devDependencies": { 26 | "coffeebar": "^1.0.0" 27 | }, 28 | "repository": { 29 | "type": "git", 30 | "url": "git+https://github.com/samme/phaser-plugin-advanced-timing.git" 31 | }, 32 | "bugs": { 33 | "url": "https://github.com/samme/phaser-plugin-advanced-timing/issues" 34 | }, 35 | "homepage": "https://github.com/samme/phaser-plugin-advanced-timing#readme" 36 | } 37 | -------------------------------------------------------------------------------- /screenshots/debug.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/samme/phaser-plugin-advanced-timing/417176e75ff45c91ee02e5b2af4c6efb8bc86f7a/screenshots/debug.png -------------------------------------------------------------------------------- /screenshots/domMeter.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/samme/phaser-plugin-advanced-timing/417176e75ff45c91ee02e5b2af4c6efb8bc86f7a/screenshots/domMeter.png -------------------------------------------------------------------------------- /screenshots/domText.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/samme/phaser-plugin-advanced-timing/417176e75ff45c91ee02e5b2af4c6efb8bc86f7a/screenshots/domText.png -------------------------------------------------------------------------------- /screenshots/graph.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/samme/phaser-plugin-advanced-timing/417176e75ff45c91ee02e5b2af4c6efb8bc86f7a/screenshots/graph.png -------------------------------------------------------------------------------- /screenshots/meter.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/samme/phaser-plugin-advanced-timing/417176e75ff45c91ee02e5b2af4c6efb8bc86f7a/screenshots/meter.png -------------------------------------------------------------------------------- /screenshots/text.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/samme/phaser-plugin-advanced-timing/417176e75ff45c91ee02e5b2af4c6efb8bc86f7a/screenshots/text.png --------------------------------------------------------------------------------