├── src ├── dist ├── Description.js ├── Header.js ├── styles.css ├── index.js ├── helpers.js └── vendor │ └── fpsmeter.min.js ├── .vscode └── settings.json ├── public ├── favicon.ico ├── manifest.json └── index.html ├── .gitignore ├── README.md └── package.json /src/dist: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /.vscode/settings.json: -------------------------------------------------------------------------------- 1 | { 2 | "eslint.enable": false, 3 | "cSpell.enabled": false 4 | } 5 | -------------------------------------------------------------------------------- /public/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/philipp-spiess/scheduletron3000/HEAD/public/favicon.ico -------------------------------------------------------------------------------- /public/manifest.json: -------------------------------------------------------------------------------- 1 | { 2 | "short_name": "React App", 3 | "name": "Create React App Sample", 4 | "icons": [ 5 | { 6 | "src": "favicon.ico", 7 | "sizes": "64x64 32x32 24x24 16x16", 8 | "type": "image/x-icon" 9 | } 10 | ], 11 | "start_url": ".", 12 | "display": "standalone", 13 | "theme_color": "#000000", 14 | "background_color": "#ffffff" 15 | } 16 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # See https://help.github.com/articles/ignoring-files/ for more about ignoring files. 2 | 3 | # dependencies 4 | /node_modules 5 | /.pnp 6 | .pnp.js 7 | 8 | # testing 9 | /coverage 10 | 11 | # production 12 | /build 13 | 14 | # misc 15 | .DS_Store 16 | .env.local 17 | .env.development.local 18 | .env.test.local 19 | .env.production.local 20 | 21 | npm-debug.log* 22 | yarn-debug.log* 23 | yarn-error.log* 24 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # ScheduleTron 3000 2 | 3 |

4 | 5 | ScheduleTron 3000 6 | 7 |

8 | 9 | An example app used to demonstrate the new React features Concurrent React and the Scheduler. 10 | 11 | [Read more in this blog post about Scheduling in React.](https://philippspiess.com/scheduling-in-react/) 12 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "scheduletron3000", 3 | "version": "0.1.0", 4 | "private": true, 5 | "dependencies": { 6 | "react": "^16.7.0", 7 | "react-dom": "^16.7.0", 8 | "react-scripts": "2.1.5" 9 | }, 10 | "scripts": { 11 | "start": "react-scripts start", 12 | "build": "react-scripts build", 13 | "test": "react-scripts test", 14 | "eject": "react-scripts eject" 15 | }, 16 | "eslintConfig": { 17 | "extends": "react-app" 18 | }, 19 | "browserslist": [ 20 | ">0.2%", 21 | "not dead", 22 | "not ie <= 11", 23 | "not op_mini all" 24 | ] 25 | } 26 | -------------------------------------------------------------------------------- /src/Description.js: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | import { CONCURRENT_AND_SCHEDULED } from './index' 3 | import './vendor/fpsmeter.min' 4 | 5 | export default class Description extends React.Component { 6 | fpsRef = React.createRef(); 7 | 8 | componentDidMount() { 9 | const anchor = this.fpsRef.current; 10 | 11 | // eslint-disable-next-line no-undef 12 | const meter = new FPSMeter(anchor, { 13 | heat: true, 14 | graph: true 15 | }); 16 | 17 | function tick() { 18 | meter.tick(); 19 | requestAnimationFrame(tick); 20 | } 21 | 22 | tick(); 23 | } 24 | 25 | render() { 26 | return ( 27 |
28 | 29 | {CONCURRENT_AND_SCHEDULED 30 | ? "Concurrent and Scheduled" 31 | : "Synchronous"} 32 | 33 |
34 |
35 |
36 | ); 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /src/Header.js: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | 3 | const SPEED = 0.01; 4 | 5 | export default class Header extends React.PureComponent { 6 | state = { offset: 0 }; 7 | t0 = Date.now(); 8 | ref = React.createRef(); 9 | 10 | componentDidMount() { 11 | this.frame = requestAnimationFrame(this.animate); 12 | } 13 | 14 | componentWillUnmount() { 15 | if (this.frame) { 16 | cancelAnimationFrame(this.frame); 17 | } 18 | } 19 | 20 | animate = () => { 21 | requestAnimationFrame(this.animate); 22 | 23 | const text = this.props.children; 24 | 25 | const td = Date.now() - this.t0; 26 | const offset = td * SPEED; 27 | 28 | const h1 = this.ref.current; 29 | 30 | if (!h1) { 31 | return; 32 | } 33 | 34 | h1.innerHTML = ""; 35 | 36 | for (let i = 0; i < text.length; i++) { 37 | let charElem = document.createElement("span"); 38 | charElem.style.color = 39 | "hsl(" + (360 * (i + offset)) / text.length + ",80%,60%)"; 40 | charElem.innerHTML = text[i]; 41 | h1.appendChild(charElem); 42 | } 43 | }; 44 | 45 | render() { 46 | return

; 47 | } 48 | } 49 | -------------------------------------------------------------------------------- /src/styles.css: -------------------------------------------------------------------------------- 1 | .App { 2 | text-align: center; 3 | margin-bottom: 40vh; 4 | } 5 | 6 | body { 7 | background-color: #0b0b0c; 8 | color: white; 9 | font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Oxygen, 10 | Ubuntu, Cantarell, "Open Sans", "Helvetica Neue", sans-serif; 11 | } 12 | 13 | .input { 14 | padding: 16px; 15 | font-size: 30px; 16 | width: 80%; 17 | display: block; 18 | margin: 60px auto; 19 | } 20 | 21 | h1 { 22 | color: #fcd93b; 23 | font-size: 87px; 24 | margin: 60px; 25 | } 26 | 27 | .name { 28 | font-size: 30px; 29 | padding: 20px 0; 30 | width: 50%; 31 | float: left; 32 | color: #ffadb4; 33 | } 34 | 35 | .highlight { 36 | color: #000000; 37 | background: #ffadb4; 38 | padding: 5px; 39 | } 40 | 41 | .clearfix::after { 42 | clear: both; 43 | content: ""; 44 | } 45 | 46 | .marketing { 47 | background: linear-gradient(transparent 0%, black 80%); 48 | position: fixed; 49 | left: 0; 50 | right: 0; 51 | bottom: 0; 52 | height: 40vh; 53 | text-align: left; 54 | padding-left: 100px; 55 | padding-right: 100px; 56 | padding-bottom: 50px; 57 | display: flex; 58 | align-items: flex-end; 59 | } 60 | 61 | .marketing > span { 62 | font-size: 4rem; 63 | } 64 | 65 | .spacer { 66 | flex-grow: 1; 67 | } 68 | 69 | .fps { 70 | zoom: 2; 71 | position: relative; 72 | width: 120px; 73 | height: 40px; 74 | } 75 | -------------------------------------------------------------------------------- /public/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 10 | 11 | 15 | 16 | 25 | React App 26 | 27 | 28 | 29 |
30 | 40 | 41 | 42 | -------------------------------------------------------------------------------- /src/index.js: -------------------------------------------------------------------------------- 1 | import { NameList, sendAnalyticsPing } from "./helpers"; 2 | import React from "react"; 3 | import ReactDOM from "react-dom"; 4 | import Header from "./Header"; 5 | import Description from "./Description"; 6 | 7 | import { 8 | unstable_LowPriority, 9 | unstable_next, 10 | unstable_runWithPriority, 11 | unstable_scheduleCallback 12 | } from "scheduler"; 13 | 14 | import "./styles.css"; 15 | 16 | // Change the flag below to enable Concurrent React and Scheduler improvements 17 | // as layed out in https://philippspiess.com/scheduling-in-react 18 | export const CONCURRENT_AND_SCHEDULED = true; 19 | 20 | class App extends React.Component { 21 | state = { 22 | searchValue: "" 23 | }; 24 | 25 | handleChange = value => { 26 | this.setState({ searchValue: value }); 27 | }; 28 | 29 | render() { 30 | const { searchValue } = this.state; 31 | 32 | return ( 33 |
34 |
ScheduleTron 3000
35 | 36 | 37 | 38 |
39 | ); 40 | } 41 | } 42 | 43 | class SearchBox extends React.Component { 44 | state = { 45 | inputValue: "" 46 | }; 47 | 48 | handleChange = event => { 49 | const value = event.target.value; 50 | const onChange = this.props.onChange; 51 | 52 | if (CONCURRENT_AND_SCHEDULED) { 53 | this.setState({ inputValue: value }); 54 | 55 | unstable_next(function() { 56 | onChange(value); 57 | }); 58 | 59 | sendDeferredAnalyticsPing(value); 60 | } else { 61 | this.setState({ inputValue: value }); 62 | onChange(value); 63 | sendAnalyticsPing(value); 64 | } 65 | }; 66 | 67 | render() { 68 | const { inputValue } = this.state; 69 | 70 | return ( 71 |
72 | 79 |
80 | ); 81 | } 82 | } 83 | 84 | function sendDeferredAnalyticsPing(value) { 85 | unstable_runWithPriority(unstable_LowPriority, function() { 86 | unstable_scheduleCallback(function() { 87 | sendAnalyticsPing(value); 88 | }); 89 | }); 90 | } 91 | 92 | const rootElement = document.getElementById("root"); 93 | const Wrapper = ReactDOM.render( 94 | CONCURRENT_AND_SCHEDULED ? ( 95 | 96 | 97 | 98 | ) : ( 99 | 100 | ), 101 | rootElement 102 | ); 103 | -------------------------------------------------------------------------------- /src/helpers.js: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | 3 | export function NameList({ searchValue }) { 4 | return ( 5 |
6 | {names.map(name => ( 7 | 8 | ))} 9 |
10 | ); 11 | } 12 | 13 | export function Name({ name, searchValue }) { 14 | const matchesFilterAtIndex = name 15 | .toLowerCase() 16 | .indexOf(searchValue.toLowerCase()); 17 | 18 | miningBitcoin(2); 19 | 20 | if (matchesFilterAtIndex >= 0 && searchValue !== "") { 21 | return ( 22 |
23 | {name.substring(0, matchesFilterAtIndex)} 24 | 25 | {name.substring( 26 | matchesFilterAtIndex, 27 | matchesFilterAtIndex + searchValue.length 28 | )} 29 | 30 | 31 | {name.substring(matchesFilterAtIndex + searchValue.length)} 32 |
33 | ); 34 | } else { 35 | return
{name}
; 36 | } 37 | } 38 | 39 | export function sendAnalyticsPing(value) { 40 | performance.mark("analytics-start"); 41 | miningBitcoin(25); 42 | performance.mark("analytics-end"); 43 | performance.measure( 44 | "Analytics: " + value, 45 | "analytics-start", 46 | "analytics-end" 47 | ); 48 | } 49 | 50 | function miningBitcoin(ms) { 51 | let now = Date.now(); 52 | while (Date.now() < now + ms) { 53 | // noop 54 | } 55 | } 56 | 57 | const names = [ 58 | "Ada Moreno", 59 | "Ada Stewart", 60 | "Adele Valdez", 61 | "Adeline Knight", 62 | "Agnes Newman", 63 | "Alfred Stephens", 64 | "Allie Harris", 65 | "Andre Owen", 66 | "Andrew Clark", 67 | "Angel Murray", 68 | "Arthur Osborne", 69 | "Arthur Woods", 70 | "Barbara Holt", 71 | "Billy Baker", 72 | "Brian Vaughn", 73 | "Calvin Edwards", 74 | "Carl Pierce", 75 | "Charles Sanchez", 76 | "Charlie Hicks", 77 | "Charlie Matthews", 78 | "Chase Ruiz", 79 | "Chester Chandler", 80 | "Christopher Marsh", 81 | "Cordelia Brock", 82 | "Corey Gonzalez", 83 | "Cornelia Gordon", 84 | "Cory Lane", 85 | "Daisy Pena", 86 | "Dan Abramov", 87 | "Dollie Wilkerson", 88 | "Dominic Gannaway", 89 | "Dorothy Singleton", 90 | "Eddie Horton", 91 | "Lottie Harper", 92 | "Louis McBride", 93 | "Mabel Daniels", 94 | "Mabelle Bowman", 95 | "Martha Moreno", 96 | "Martin Simpson", 97 | "Mary Powers", 98 | "Mary Snyder", 99 | "Matthew Collier", 100 | "Maurice Myers", 101 | "Myra Jennings", 102 | "Myra Stephens", 103 | "Nellie Potter", 104 | "Nora Cannon", 105 | "Ola Bryant", 106 | "Paul Coleman", 107 | "Pearl Moss", 108 | "Phoebe Underwood", 109 | "Rachel May", 110 | "Sebastian Markbåge", 111 | "Shawn Lloyd", 112 | "Shawn Lynch", 113 | "Sophie Alpert", 114 | "Stella Lane", 115 | "Steve Byrd", 116 | "Steve Schmidt", 117 | "Sunil Pai", 118 | "Teresa Doyle", 119 | "Timothy Gray", 120 | "Timothy Harvey", 121 | "Todd Stewart", 122 | "Todd Washington" 123 | ]; 124 | -------------------------------------------------------------------------------- /src/vendor/fpsmeter.min.js: -------------------------------------------------------------------------------- 1 | /* eslint-disable */ 2 | 3 | /*! FPSMeter 0.3.0 - 24th Apr 2013 | https://github.com/Darsain/fpsmeter */ 4 | (function(r, j) { 5 | function s(a, e) { 6 | for (var g in e) 7 | try { 8 | a.style[g] = e[g]; 9 | } catch (j) {} 10 | return a; 11 | } 12 | function H(a) { 13 | return Object.prototype.toString 14 | .call(a) 15 | .match(/\s([a-z]+)/i)[1] 16 | .toLowerCase(); 17 | } 18 | function R(a, e) { 19 | if ("array" !== H(e)) return -1; 20 | if (e.indexOf) return e.indexOf(a); 21 | for (var g = 0, j = e.length; g < j; g++) if (e[g] === a) return g; 22 | return -1; 23 | } 24 | function I() { 25 | var a = arguments, 26 | e; 27 | for (e in a[1]) 28 | if (a[1].hasOwnProperty(e)) 29 | switch (H(a[1][e])) { 30 | case "object": 31 | a[0][e] = I({}, a[0][e], a[1][e]); 32 | break; 33 | case "array": 34 | a[0][e] = a[1][e].slice(0); 35 | break; 36 | default: 37 | a[0][e] = a[1][e]; 38 | } 39 | return 2 < a.length 40 | ? I.apply(null, [a[0]].concat(Array.prototype.slice.call(a, 2))) 41 | : a[0]; 42 | } 43 | function N(a) { 44 | a = Math.round(255 * a).toString(16); 45 | return 1 === a.length ? "0" + a : a; 46 | } 47 | function S(a, e, g, j) { 48 | if (a.addEventListener) 49 | a[j ? "removeEventListener" : "addEventListener"](e, g, !1); 50 | else if (a.attachEvent) a[j ? "detachEvent" : "attachEvent"]("on" + e, g); 51 | } 52 | function D(a, e) { 53 | function g(b, a, d, c) { 54 | return y[0 | b][Math.round(Math.min(((a - d) / (c - d)) * J, J))]; 55 | } 56 | function r() { 57 | f.legend.fps !== q && 58 | ((f.legend.fps = q), (f.legend[T] = q ? "FPS" : "ms")); 59 | K = q ? b.fps : b.duration; 60 | f.count[T] = 999 < K ? "999+" : K.toFixed(99 < K ? 0 : d.decimals); 61 | } 62 | function m() { 63 | z = A(); 64 | L < z - d.threshold && 65 | ((b.fps -= b.fps / Math.max(1, (60 * d.smoothing) / d.interval)), 66 | (b.duration = 1e3 / b.fps)); 67 | for (c = d.history; c--; ) 68 | (E[c] = 0 === c ? b.fps : E[c - 1]), 69 | (F[c] = 0 === c ? b.duration : F[c - 1]); 70 | r(); 71 | if (d.heat) { 72 | if (w.length) 73 | for (c = w.length; c--; ) 74 | w[c].el.style[h[w[c].name].heatOn] = q 75 | ? g(h[w[c].name].heatmap, b.fps, 0, d.maxFps) 76 | : g(h[w[c].name].heatmap, b.duration, d.threshold, 0); 77 | if (f.graph && h.column.heatOn) 78 | for (c = u.length; c--; ) 79 | u[c].style[h.column.heatOn] = q 80 | ? g(h.column.heatmap, E[c], 0, d.maxFps) 81 | : g(h.column.heatmap, F[c], d.threshold, 0); 82 | } 83 | if (f.graph) 84 | for (p = 0; p < d.history; p++) 85 | u[p].style.height = 86 | (q 87 | ? E[p] 88 | ? Math.round((O / d.maxFps) * Math.min(E[p], d.maxFps)) 89 | : 0 90 | : F[p] 91 | ? Math.round((O / d.threshold) * Math.min(F[p], d.threshold)) 92 | : 0) + "px"; 93 | } 94 | function k() { 95 | 20 > d.interval 96 | ? ((x = M(k)), m()) 97 | : ((x = setTimeout(k, d.interval)), (P = M(m))); 98 | } 99 | function G(a) { 100 | a = a || window.event; 101 | a.preventDefault 102 | ? (a.preventDefault(), a.stopPropagation()) 103 | : ((a.returnValue = !1), (a.cancelBubble = !0)); 104 | b.toggle(); 105 | } 106 | function U() { 107 | d.toggleOn && S(f.container, d.toggleOn, G, 1); 108 | a.removeChild(f.container); 109 | } 110 | function V() { 111 | f.container && U(); 112 | h = D.theme[d.theme]; 113 | y = h.compiledHeatmaps || []; 114 | if (!y.length && h.heatmaps.length) { 115 | for (p = 0; p < h.heatmaps.length; p++) { 116 | y[p] = []; 117 | for (c = 0; c <= J; c++) { 118 | var b = y[p], 119 | e = c, 120 | g; 121 | g = (0.33 / J) * c; 122 | var j = h.heatmaps[p].saturation, 123 | m = h.heatmaps[p].lightness, 124 | n = void 0, 125 | k = void 0, 126 | l = void 0, 127 | t = (l = void 0), 128 | v = (n = k = void 0), 129 | v = void 0, 130 | l = 0.5 >= m ? m * (1 + j) : m + j - m * j; 131 | 0 === l 132 | ? (g = "#000") 133 | : ((t = 2 * m - l), 134 | (k = (l - t) / l), 135 | (g *= 6), 136 | (n = Math.floor(g)), 137 | (v = g - n), 138 | (v *= l * k), 139 | 0 === n || 6 === n 140 | ? ((n = l), (k = t + v), (l = t)) 141 | : 1 === n 142 | ? ((n = l - v), (k = l), (l = t)) 143 | : 2 === n 144 | ? ((n = t), (k = l), (l = t + v)) 145 | : 3 === n 146 | ? ((n = t), (k = l - v)) 147 | : 4 === n 148 | ? ((n = t + v), (k = t)) 149 | : ((n = l), (k = t), (l -= v)), 150 | (g = "#" + N(n) + N(k) + N(l))); 151 | b[e] = g; 152 | } 153 | } 154 | h.compiledHeatmaps = y; 155 | } 156 | f.container = s(document.createElement("div"), h.container); 157 | f.count = f.container.appendChild( 158 | s(document.createElement("div"), h.count) 159 | ); 160 | f.legend = f.container.appendChild( 161 | s(document.createElement("div"), h.legend) 162 | ); 163 | f.graph = d.graph 164 | ? f.container.appendChild(s(document.createElement("div"), h.graph)) 165 | : 0; 166 | w.length = 0; 167 | for (var q in f) f[q] && h[q].heatOn && w.push({ name: q, el: f[q] }); 168 | u.length = 0; 169 | if (f.graph) { 170 | f.graph.style.width = 171 | d.history * h.column.width + 172 | (d.history - 1) * h.column.spacing + 173 | "px"; 174 | for (c = 0; c < d.history; c++) 175 | (u[c] = f.graph.appendChild( 176 | s(document.createElement("div"), h.column) 177 | )), 178 | (u[c].style.position = "absolute"), 179 | (u[c].style.bottom = 0), 180 | (u[c].style.right = 181 | c * h.column.width + c * h.column.spacing + "px"), 182 | (u[c].style.width = h.column.width + "px"), 183 | (u[c].style.height = "0px"); 184 | } 185 | s(f.container, d); 186 | r(); 187 | a.appendChild(f.container); 188 | f.graph && (O = f.graph.clientHeight); 189 | d.toggleOn && 190 | ("click" === d.toggleOn && (f.container.style.cursor = "pointer"), 191 | S(f.container, d.toggleOn, G)); 192 | } 193 | "object" === H(a) && a.nodeType === j && ((e = a), (a = document.body)); 194 | a || (a = document.body); 195 | var b = this, 196 | d = I({}, D.defaults, e || {}), 197 | f = {}, 198 | u = [], 199 | h, 200 | y, 201 | J = 100, 202 | w = [], 203 | W = 0, 204 | B = d.threshold, 205 | Q = 0, 206 | L = A() - B, 207 | z, 208 | E = [], 209 | F = [], 210 | x, 211 | P, 212 | q = "fps" === d.show, 213 | O, 214 | K, 215 | c, 216 | p; 217 | b.options = d; 218 | b.fps = 0; 219 | b.duration = 0; 220 | b.isPaused = 0; 221 | b.tickStart = function() { 222 | Q = A(); 223 | }; 224 | b.tick = function() { 225 | z = A(); 226 | W = z - L; 227 | B += (W - B) / d.smoothing; 228 | b.fps = 1e3 / B; 229 | b.duration = Q < L ? B : z - Q; 230 | L = z; 231 | }; 232 | b.pause = function() { 233 | x && ((b.isPaused = 1), clearTimeout(x), C(x), C(P), (x = P = 0)); 234 | return b; 235 | }; 236 | b.resume = function() { 237 | x || ((b.isPaused = 0), k()); 238 | return b; 239 | }; 240 | b.set = function(a, c) { 241 | d[a] = c; 242 | q = "fps" === d.show; 243 | -1 !== R(a, X) && V(); 244 | -1 !== R(a, Y) && s(f.container, d); 245 | return b; 246 | }; 247 | b.showDuration = function() { 248 | b.set("show", "ms"); 249 | return b; 250 | }; 251 | b.showFps = function() { 252 | b.set("show", "fps"); 253 | return b; 254 | }; 255 | b.toggle = function() { 256 | b.set("show", q ? "ms" : "fps"); 257 | return b; 258 | }; 259 | b.hide = function() { 260 | b.pause(); 261 | f.container.style.display = "none"; 262 | return b; 263 | }; 264 | b.show = function() { 265 | b.resume(); 266 | f.container.style.display = "block"; 267 | return b; 268 | }; 269 | b.destroy = function() { 270 | b.pause(); 271 | U(); 272 | b.tick = b.tickStart = function() {}; 273 | }; 274 | V(); 275 | k(); 276 | } 277 | var A, 278 | m = r.performance; 279 | A = m 280 | ? m[m.now ? "now" : "webkitNow"].bind(m) 281 | : function() { 282 | return +new Date(); 283 | }; 284 | for ( 285 | var C = r.cancelAnimationFrame || r.cancelRequestAnimationFrame, 286 | M = r.requestAnimationFrame, 287 | m = ["moz", "webkit", "o"], 288 | G = 0, 289 | k = 0, 290 | Z = m.length; 291 | k < Z && !C; 292 | ++k 293 | ) 294 | M = 295 | (C = 296 | r[m[k] + "CancelAnimationFrame"] || 297 | r[m[k] + "CancelRequestAnimationFrame"]) && 298 | r[m[k] + "RequestAnimationFrame"]; 299 | C || 300 | ((M = function(a) { 301 | var e = A(), 302 | g = Math.max(0, 16 - (e - G)); 303 | G = e + g; 304 | return r.setTimeout(function() { 305 | a(e + g); 306 | }, g); 307 | }), 308 | (C = function(a) { 309 | clearTimeout(a); 310 | })); 311 | var T = 312 | "string" === H(document.createElement("div").textContent) 313 | ? "textContent" 314 | : "innerText"; 315 | D.extend = I; 316 | window.FPSMeter = D; 317 | D.defaults = { 318 | interval: 100, 319 | smoothing: 10, 320 | show: "fps", 321 | toggleOn: "click", 322 | decimals: 1, 323 | maxFps: 60, 324 | threshold: 100, 325 | position: "absolute", 326 | zIndex: 10, 327 | left: "5px", 328 | top: "5px", 329 | right: "auto", 330 | bottom: "auto", 331 | margin: "0 0 0 0", 332 | theme: "dark", 333 | heat: 0, 334 | graph: 0, 335 | history: 20 336 | }; 337 | var X = ["toggleOn", "theme", "heat", "graph", "history"], 338 | Y = "position zIndex left top right bottom margin".split(" "); 339 | })(window); 340 | (function(r, j) { 341 | j.theme = {}; 342 | var s = (j.theme.base = { 343 | heatmaps: [], 344 | container: { 345 | heatOn: null, 346 | heatmap: null, 347 | padding: "5px", 348 | minWidth: "95px", 349 | height: "30px", 350 | lineHeight: "30px", 351 | textAlign: "right", 352 | textShadow: "none" 353 | }, 354 | count: { 355 | heatOn: null, 356 | heatmap: null, 357 | position: "absolute", 358 | top: 0, 359 | right: 0, 360 | padding: "5px 10px", 361 | height: "30px", 362 | fontSize: "24px", 363 | fontFamily: "Consolas, Andale Mono, monospace", 364 | zIndex: 2 365 | }, 366 | legend: { 367 | heatOn: null, 368 | heatmap: null, 369 | position: "absolute", 370 | top: 0, 371 | left: 0, 372 | padding: "5px 10px", 373 | height: "30px", 374 | fontSize: "12px", 375 | lineHeight: "32px", 376 | fontFamily: "sans-serif", 377 | textAlign: "left", 378 | zIndex: 2 379 | }, 380 | graph: { 381 | heatOn: null, 382 | heatmap: null, 383 | position: "relative", 384 | boxSizing: "padding-box", 385 | MozBoxSizing: "padding-box", 386 | height: "100%", 387 | zIndex: 1 388 | }, 389 | column: { width: 4, spacing: 1, heatOn: null, heatmap: null } 390 | }); 391 | j.theme.dark = j.extend({}, s, { 392 | heatmaps: [{ saturation: 0.8, lightness: 0.8 }], 393 | container: { 394 | background: "#222", 395 | color: "#fff", 396 | border: "1px solid #1a1a1a", 397 | textShadow: "1px 1px 0 #222" 398 | }, 399 | count: { heatOn: "color" }, 400 | column: { background: "#3f3f3f" } 401 | }); 402 | j.theme.light = j.extend({}, s, { 403 | heatmaps: [{ saturation: 0.5, lightness: 0.5 }], 404 | container: { 405 | color: "#666", 406 | background: "#fff", 407 | textShadow: 408 | "1px 1px 0 rgba(255,255,255,.5), -1px -1px 0 rgba(255,255,255,.5)", 409 | boxShadow: "0 0 0 1px rgba(0,0,0,.1)" 410 | }, 411 | count: { heatOn: "color" }, 412 | column: { background: "#eaeaea" } 413 | }); 414 | j.theme.colorful = j.extend({}, s, { 415 | heatmaps: [{ saturation: 0.5, lightness: 0.6 }], 416 | container: { 417 | heatOn: "backgroundColor", 418 | background: "#888", 419 | color: "#fff", 420 | textShadow: "1px 1px 0 rgba(0,0,0,.2)", 421 | boxShadow: "0 0 0 1px rgba(0,0,0,.1)" 422 | }, 423 | column: { background: "#777", backgroundColor: "rgba(0,0,0,.2)" } 424 | }); 425 | j.theme.transparent = j.extend({}, s, { 426 | heatmaps: [{ saturation: 0.8, lightness: 0.5 }], 427 | container: { 428 | padding: 0, 429 | color: "#fff", 430 | textShadow: "1px 1px 0 rgba(0,0,0,.5)" 431 | }, 432 | count: { padding: "0 5px", height: "40px", lineHeight: "40px" }, 433 | legend: { padding: "0 5px", height: "40px", lineHeight: "42px" }, 434 | graph: { height: "40px" }, 435 | column: { 436 | width: 5, 437 | background: "#999", 438 | heatOn: "backgroundColor", 439 | opacity: 0.5 440 | } 441 | }); 442 | })(window, FPSMeter); 443 | --------------------------------------------------------------------------------