├── .gitignore ├── src ├── embed.html ├── js │ ├── wasm │ │ ├── engine.wasm │ │ ├── README.txt │ │ ├── engine.c │ │ └── engine.js │ ├── index.js │ ├── patterns.js │ ├── wasmEngine.js │ ├── events.js │ ├── renderer.js │ ├── engine.js │ ├── demo.js │ └── embed.js ├── styles │ ├── github-corner.scss │ ├── screen.css │ ├── main.scss │ └── embed.scss └── index.html ├── .travis.yml ├── .babelrc ├── docs ├── engine.wasm ├── js │ ├── engine.wasm │ ├── lab-bundle.js │ ├── demo-styles.js │ └── demo-bundle.js ├── css │ ├── styles.css.map │ └── styles.css ├── embed.html └── index.html ├── LICENSE ├── README.md ├── package.json └── conf ├── webpack.config.demo.js └── webpack.config.embed.js /.gitignore: -------------------------------------------------------------------------------- 1 | node_modules/ 2 | -------------------------------------------------------------------------------- /src/embed.html: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: node_js 2 | node_js: 3 | - "12" -------------------------------------------------------------------------------- /.babelrc: -------------------------------------------------------------------------------- 1 | { 2 | "presets": ["@babel/preset-env"] 3 | } 4 | -------------------------------------------------------------------------------- /docs/engine.wasm: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/blaze33/way-of-life/HEAD/docs/engine.wasm -------------------------------------------------------------------------------- /docs/js/engine.wasm: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/blaze33/way-of-life/HEAD/docs/js/engine.wasm -------------------------------------------------------------------------------- /src/js/wasm/engine.wasm: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/blaze33/way-of-life/HEAD/src/js/wasm/engine.wasm -------------------------------------------------------------------------------- /docs/css/styles.css.map: -------------------------------------------------------------------------------- 1 | {"version":3,"sources":[],"names":[],"mappings":"","file":"css/styles.css?01393879","sourceRoot":""} -------------------------------------------------------------------------------- /docs/embed.html: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /src/js/index.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | 3 | import Engine from './engine' 4 | import Renderer from './renderer' 5 | import { acorn, cross, erase } from './patterns' 6 | import MouseEventHandler from './events' 7 | 8 | export { 9 | Engine, Renderer, 10 | acorn, cross, erase, 11 | MouseEventHandler 12 | } 13 | -------------------------------------------------------------------------------- /src/js/patterns.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | 3 | function acorn (engine, i, j) { 4 | engine.set(i - 1, j) 5 | engine.set(i, j + 2) 6 | engine.set(i + 1, j - 1) 7 | engine.set(i + 1, j) 8 | engine.set(i + 1, j + 3) 9 | engine.set(i + 1, j + 4) 10 | engine.set(i + 1, j + 5) 11 | } 12 | 13 | function cross (engine, i, j) { 14 | engine.set(i - 1, j) 15 | engine.set(i, j - 1) 16 | engine.set(i, j) 17 | engine.set(i, j + 1) 18 | engine.set(i + 1, j) 19 | } 20 | 21 | function erase (engine, i, j) { 22 | engine.set(i - 1, j - 1, 0) 23 | engine.set(i - 1, j, 0) 24 | engine.set(i - 1, j + 1, 0) 25 | engine.set(i, j - 1, 0) 26 | engine.set(i, j, 0) 27 | engine.set(i, j + 1, 0) 28 | engine.set(i + 1, j - 1, 0) 29 | engine.set(i + 1, j, 0) 30 | engine.set(i + 1, j + 1, 0) 31 | } 32 | 33 | export { 34 | acorn, cross, erase 35 | } 36 | -------------------------------------------------------------------------------- /src/js/wasm/README.txt: -------------------------------------------------------------------------------- 1 | How to compile C code to a WASM module using emscripten 2 | 3 | 1. Emscripten installation 4 | cf. https://emscripten.org/docs/getting_started/downloads.html 5 | 6 | # Get the emsdk repo 7 | git clone https://github.com/emscripten-core/emsdk.git 8 | 9 | # Enter that directory 10 | cd emsdk 11 | 12 | # Fetch the latest version of the emsdk (not needed the first time you clone) 13 | git pull 14 | 15 | # Download and install the latest SDK tools. 16 | ./emsdk install latest 17 | 18 | # Make the "latest" SDK "active" for the current user. (writes ~/.emscripten file) 19 | ./emsdk activate latest 20 | 21 | # Activate PATH and other environment variables in the current terminal 22 | source ./emsdk_env.sh 23 | 24 | 2. Compilation 25 | `emcc engine.c -o engine.js -Os -s WASM=1 -Wall -s MODULARIZE=1` 26 | -------------------------------------------------------------------------------- /src/styles/github-corner.scss: -------------------------------------------------------------------------------- 1 | .github-corner svg { 2 | fill: rgba(222,122,39,0.5); 3 | color: #fff; 4 | position: fixed; 5 | top: 0; 6 | border: 0; 7 | right: 0; 8 | } 9 | 10 | .github-corner svg:hover { 11 | fill: rgba(255,118,5,0.5); 12 | } 13 | 14 | .github-corner:hover .octo-arm { 15 | animation: octocat-wave 560ms ease-in-out 16 | } 17 | 18 | @keyframes octocat-wave { 19 | 0%, 20 | 100% { 21 | transform: rotate(0) 22 | } 23 | 20%, 24 | 60% { 25 | transform: rotate(-25deg) 26 | } 27 | 40%, 28 | 80% { 29 | transform: rotate(10deg) 30 | } 31 | } 32 | 33 | @media (max-width:500px) { 34 | .github-corner:hover .octo-arm { 35 | animation: none 36 | } 37 | .github-corner .octo-arm { 38 | animation: octocat-wave 560ms ease-in-out 39 | } 40 | } 41 | 42 | 43 | .github-button, iframe { 44 | position: fixed; 45 | top: 20px; 46 | left: 20px; 47 | } -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2017 Maxime Rouyrre. 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /src/js/wasmEngine.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | import Module from './wasm/engine.js' 3 | 4 | class WasmEngine { 5 | constructor (width, height) { 6 | this.wasm = true 7 | this.width = width 8 | this.height = height 9 | this.module = Module({ wasmBinaryFile: 'wasm/engine.wasm' }).then( 10 | Module => { Module.calledRun = true } 11 | ) 12 | window.module = this.module 13 | } 14 | 15 | init () { 16 | // _init returns a pointer to the array of the current game state 17 | // we'll save it to have a fast access to the state in cellSafe 18 | this.currentAdress = this.module._init(this.width, this.height) 19 | } 20 | 21 | cellSafe (i, j) { 22 | // cellSafe should only be called when we know i and j are within 23 | // the bounds of the game state array. 24 | return this.module.HEAP8[this.currentAdress + (i + 1) * (this.width + 2) + j + 1] 25 | // return this.module.getValue(this.currentAdress + i * this.width + j, 'i8') 26 | } 27 | 28 | computeNextState () { 29 | this.module._computeNextState() 30 | } 31 | 32 | set (i, j, value = 1) { 33 | this.module._set(i, j, value) 34 | } 35 | } 36 | 37 | export { WasmEngine as default } 38 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # The Way of Life 2 | 3 | Conway's game of life implemented both in JavaScript and in WebAssembly using a canvas element for rendering. 4 | 5 | * [Demo](https://blaze33.github.io/way-of-life/) 6 | * [Demo up to 60FPS, 1 pixel per cell](https://blaze33.github.io/way-of-life/?desiredFPS=60&pixelsPerCell=1) 7 | 8 | [![npm](https://img.shields.io/npm/v/way-of-life.svg)](https://www.npmjs.com/package/way-of-life) 9 | [![Build Status](https://travis-ci.org/blaze33/way-of-life.svg?branch=master)](https://travis-ci.org/blaze33/way-of-life) 10 | 11 | ## Installation 12 | 13 | The npm package only links to the ES6 code so it's only usable as a dependency for now. 14 | ```bash 15 | npm install --save-dev way-of-life 16 | ``` 17 | 18 | ## Usage 19 | 20 | ```javascript 21 | import {Engine, acorn} from 'way-of-life' 22 | 23 | // initialize the game with an empty 40x40 matrix 24 | const engine = new Engine(40, 40) 25 | engine.init() 26 | 27 | // initialize some living cells around the center of the matrix 28 | acorn(engine, 20, 20) 29 | 30 | // compute the next state of game 31 | engine.computeNextState() 32 | ``` 33 | 34 | See the [demo code](https://github.com/blaze33/way-of-life/blob/master/src/js/demo.js) for a more advanced usage. 35 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "way-of-life", 3 | "version": "0.3.0", 4 | "description": "Conway's game of life", 5 | "repository": { 6 | "type": "git", 7 | "url": "git://github.com/blaze33/way-of-life.git" 8 | }, 9 | "homepage": "https://blaze33.github.io/way-of-life/", 10 | "bugs": { 11 | "url": "https://github.com/blaze33/way-of-life/issues" 12 | }, 13 | "main": "src/js/index.js", 14 | "scripts": { 15 | "demo": "webpack --config conf/webpack.config.demo.js -p --env production", 16 | "lab": "webpack --config conf/webpack.config.embed.js -p --env production", 17 | "build": "npm run demo && npm run lab", 18 | "watch": "webpack --config conf/webpack.config.demo.js --watch", 19 | "serve": "webpack-dev-server --config conf/webpack.config.demo.js --open", 20 | "serveLab": "webpack-dev-server --config conf/webpack.config.embed.js --open", 21 | "test": "standard src/**/*.js conf/webpack.config.*.js", 22 | "fix": "standard src/**/*.js conf/webpack.config.*.js --fix" 23 | }, 24 | "keywords": [ 25 | "conway", 26 | "game", 27 | "game of life" 28 | ], 29 | "author": { 30 | "name": "Maxime Rouyrre", 31 | "url": "http://www.openbloc.fr/" 32 | }, 33 | "license": "MIT", 34 | "devDependencies": { 35 | "@babel/core": "^7.4.5", 36 | "@babel/preset-env": "^7.4.5", 37 | "babel-loader": "^8.0.6", 38 | "copy-webpack-plugin": "^5.0.3", 39 | "css-loader": "^3.0.0", 40 | "glob": "^7.1.4", 41 | "html-loader": "^0.5.5", 42 | "html-webpack-plugin": "^3.2.0", 43 | "image-webpack-loader": "^5.0.0", 44 | "node-sass": "^4.12.0", 45 | "normalize.css": "^8.0.1", 46 | "purify-css": "^1.2.5", 47 | "purifycss-webpack": "^0.7.0", 48 | "query-string": "^6.8.1", 49 | "sass-loader": "^7.1.0", 50 | "standard": "^12.0.1", 51 | "style-loader": "^0.23.1", 52 | "surface": "github:blaze33/surface", 53 | "wasm-loader": "^1.3.0", 54 | "webpack": "^4.35.2", 55 | "webpack-cli": "^3.3.5", 56 | "webpack-dev-server": "^3.7.2" 57 | }, 58 | "dependencies": {} 59 | } 60 | -------------------------------------------------------------------------------- /src/js/events.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | 3 | import { erase, cross } from './patterns' 4 | 5 | class MouseEventHandler { 6 | constructor (canvas, engine, renderer) { 7 | this.canvas = canvas 8 | this.engine = engine 9 | this.renderer = renderer 10 | this.mouseDown = false 11 | this.listeners = [] 12 | 13 | this.addEvents([ 14 | { eventType: 'mousedown', callback: this.mouseIsDown.bind(this) }, 15 | { eventType: 'mouseup', callback: this.mouseIsUp.bind(this) }, 16 | { eventType: 'mousemove', callback: this.addCells.bind(this) }, 17 | { eventType: 'touchmove', 18 | callback: event => { 19 | for (let i = 0; i < event.touches.length; i++) { 20 | this.addCells(event.touches[i], true) 21 | } 22 | } } 23 | ]) 24 | } 25 | 26 | addEvents (events = []) { 27 | events.forEach(event => { 28 | this.listeners.push(event) 29 | let target = document 30 | if (event.selector) { 31 | target = document.querySelector(event.selector) 32 | } 33 | if (target) { 34 | target.addEventListener(event.eventType, event.callback) 35 | } 36 | }) 37 | } 38 | 39 | addCells (event, touch = false) { 40 | const rect = this.canvas.getBoundingClientRect() 41 | const mousePos = { 42 | x: (event.clientX - rect.left) / (rect.right - rect.left) * this.canvas.clientWidth, 43 | y: (event.clientY - rect.top) / (rect.bottom - rect.top) * this.canvas.clientHeight 44 | } 45 | const pos = { 46 | i: ~~(mousePos.y / this.renderer.pixelsPerCell), 47 | j: ~~(mousePos.x / this.renderer.pixelsPerCell) 48 | } 49 | if (this.mouseDown || touch) { 50 | if (event.ctrlKey) { 51 | erase(this.engine, pos.i, pos.j) 52 | } else { 53 | cross(this.engine, pos.i, pos.j) 54 | } 55 | } 56 | } 57 | 58 | mouseIsDown (event) { 59 | if (event.button === 0) { 60 | this.mouseDown = true 61 | this.addCells(event) 62 | } 63 | } 64 | 65 | mouseIsUp (event) { 66 | this.mouseDown = false 67 | } 68 | } 69 | 70 | export { MouseEventHandler as default } 71 | -------------------------------------------------------------------------------- /src/styles/screen.css: -------------------------------------------------------------------------------- 1 | * { 2 | -moz-box-sizing: border-box; 3 | -webkit-box-sizing: border-box; 4 | box-sizing: border-box 5 | } 6 | 7 | html { 8 | background-color: #fff; 9 | padding: 0; 10 | margin: 0; 11 | width: 100% 12 | } 13 | 14 | body { 15 | width: 100%; 16 | height: 100%; 17 | padding: 1em; 18 | margin: 0; 19 | font-family: "Alegreya", Palatino, Georgia, serif; 20 | font-size: 100%; 21 | color: #444 22 | } 23 | 24 | a, a:visited { 25 | color: #444; 26 | transition: color 0.3s ease; 27 | } 28 | 29 | a:hover { 30 | color: #000 31 | } 32 | 33 | .content { 34 | padding: 0.809em; 35 | max-width: 30em; 36 | margin: auto 37 | } 38 | 39 | .content>footer { 40 | margin-top: 4em; 41 | margin-bottom: 2em; 42 | border-top: 1px solid #eee; 43 | display: none 44 | } 45 | 46 | article>h1:first-child { 47 | display: none 48 | } 49 | 50 | article h1, 51 | article h2, 52 | article h3, 53 | article h4, 54 | article h5 { 55 | font-weight: normal; 56 | margin-top: 2em; 57 | color: black 58 | } 59 | 60 | article h1, 61 | article h2 { 62 | font-size: 1.5em 63 | } 64 | 65 | article h3 { 66 | font-family: "Alegreya SC", Palatino, Georgia, serif; 67 | font-weight: normal; 68 | text-transform: lowercase 69 | } 70 | 71 | article h4 { 72 | font-size: 1em; 73 | font-style: italic 74 | } 75 | 76 | article h5 { 77 | font-style: normal 78 | } 79 | 80 | article ul, 81 | article ol { 82 | list-style-position: outside; 83 | padding: 0 84 | } 85 | 86 | article blockquote { 87 | margin: 0; 88 | padding-left: 1em; 89 | border-left: 1px solid #aaa 90 | } 91 | 92 | article img, 93 | article object, 94 | article video, 95 | article audio, 96 | article figure, 97 | article iframe { 98 | max-width: 100% 99 | } 100 | 101 | @media all and (min-width: 33.236em) { 102 | .content { 103 | padding: 1.618em; 104 | margin: 0 auto; 105 | max-width: 45rem; 106 | font-size: 125%; 107 | } 108 | } 109 | 110 | @media all and (min-width: 33.236em) and screen and (min-width: 30em) and (max-width: 63.236em) { 111 | .content, .text-content { 112 | width: 30em 113 | } 114 | } 115 | -------------------------------------------------------------------------------- /conf/webpack.config.demo.js: -------------------------------------------------------------------------------- 1 | const path = require('path') 2 | const glob = require('glob') 3 | const HtmlWebpackPlugin = require('html-webpack-plugin') 4 | const PurifyCSSPlugin = require('purifycss-webpack') 5 | const CopyWebpackPlugin = require('copy-webpack-plugin') 6 | 7 | module.exports = function (env) { 8 | const outputPath = '../docs' 9 | const publicPath = '' 10 | 11 | return { 12 | entry: { 13 | bundle: './src/js/demo.js' 14 | }, 15 | output: { 16 | path: path.resolve(__dirname, outputPath), 17 | publicPath: publicPath, 18 | filename: `js/demo-[name].js${env === 'production' ? '?[chunkhash:8]' : ''}` 19 | }, 20 | resolve: { 21 | extensions: ['*', '.js', '.jsx', '.wasm'] 22 | }, 23 | module: { 24 | rules: [ 25 | { 26 | test: /\.(js|jsx)$/, 27 | use: 'babel-loader' 28 | }, { 29 | test: /[.]html$/, 30 | loader: 'html-loader?' + JSON.stringify({ pretty: true }) 31 | }, { 32 | test: /\.scss$/, 33 | use: [ 34 | 'style-loader?sourceMap', 35 | 'css-loader?sourceMap', 36 | 'sass-loader?sourceMap' 37 | ] 38 | }, { 39 | test: /\.woff(2)?(\?v=[0-9]\.[0-9]\.[0-9])?$/, 40 | loader: 'url-loader?limit=10000&mimetype=application/font-woff' + 41 | '&hash=sha512&digest=hex&name=fonts/[name].[ext]?[hash:8]' 42 | }, 43 | { 44 | test: /\.(ttf|eot|svg)(\?v=[0-9]\.[0-9]\.[0-9])?$/, 45 | loader: 'file-loader?hash=sha512&digest=hex&name=fonts/[name].[ext]?[hash:8]' 46 | }, { 47 | test: /\.(jpe?g|png|gif|svg|ico)$/, 48 | loaders: [ 49 | 'file-loader?hash=sha512&digest=hex&name=images/[name].[ext]?[hash:8]' 50 | ] 51 | }, { 52 | test: /\.wasm$/, 53 | loaders: ['wasm-loader'] 54 | } 55 | ] 56 | }, 57 | plugins: [ 58 | new HtmlWebpackPlugin({ 59 | template: 'src/index.html' 60 | }), 61 | new PurifyCSSPlugin({ 62 | paths: glob.sync(path.join(__dirname, 'src/*.html')) 63 | }), 64 | new CopyWebpackPlugin([{ 65 | from: 'src/js/wasm/*.wasm', 66 | to: 'js/[name].wasm' 67 | }]) 68 | ], 69 | devServer: { 70 | host: '0.0.0.0', 71 | disableHostCheck: true 72 | }, 73 | devtool: env === 'production' ? 'source-map' : 'source-map', 74 | node: { fs: 'empty' } 75 | } 76 | } 77 | -------------------------------------------------------------------------------- /conf/webpack.config.embed.js: -------------------------------------------------------------------------------- 1 | const path = require('path') 2 | const glob = require('glob') 3 | const HtmlWebpackPlugin = require('html-webpack-plugin') 4 | const PurifyCSSPlugin = require('purifycss-webpack') 5 | const CopyWebpackPlugin = require('copy-webpack-plugin') 6 | 7 | module.exports = function (env) { 8 | const outputPath = '../docs' 9 | const publicPath = env === 'production' ? 'https://lab.openbloc.fr/way-of-life/' : '' 10 | 11 | return { 12 | entry: { 13 | bundle: './src/js/embed.js' 14 | }, 15 | output: { 16 | path: path.resolve(__dirname, outputPath), 17 | publicPath: publicPath, 18 | filename: `js/lab-[name].js${env === 'production' ? '?[chunkhash:8]' : ''}` 19 | }, 20 | resolve: { 21 | extensions: ['*', '.js', '.jsx', '.wasm'] 22 | }, 23 | module: { 24 | rules: [ 25 | { 26 | test: /\.(js|jsx)$/, 27 | use: 'babel-loader' 28 | }, { 29 | test: /[.]html$/, 30 | loader: 'html-loader?' + JSON.stringify({ pretty: true }) 31 | }, { 32 | test: /\.scss$/, 33 | use: [ 34 | 'style-loader?sourceMap', 35 | 'css-loader?sourceMap', 36 | 'sass-loader?sourceMap' 37 | ] 38 | }, { 39 | test: /\.woff(2)?(\?v=[0-9]\.[0-9]\.[0-9])?$/, 40 | loader: 'url-loader?limit=10000&mimetype=application/font-woff' + 41 | '&hash=sha512&digest=hex&name=fonts/[name].[ext]?[hash:8]' 42 | }, 43 | { 44 | test: /\.(ttf|eot|svg)(\?v=[0-9]\.[0-9]\.[0-9])?$/, 45 | loader: 'file-loader?hash=sha512&digest=hex&name=fonts/[name].[ext]?[hash:8]' 46 | }, { 47 | test: /\.(jpe?g|png|gif|svg|ico)$/, 48 | loaders: [ 49 | 'file-loader?hash=sha512&digest=hex&name=images/[name].[ext]?[hash:8]' 50 | ] 51 | }, { 52 | test: /\.wasm$/, 53 | loaders: ['wasm-loader'] 54 | } 55 | ] 56 | }, 57 | plugins: [ 58 | new HtmlWebpackPlugin({ 59 | template: 'src/embed.html', 60 | filename: 'embed.html' 61 | }), 62 | new PurifyCSSPlugin({ 63 | paths: glob.sync(path.join(__dirname, 'src/*.html')) 64 | }), 65 | new CopyWebpackPlugin([{ 66 | from: 'src/js/wasm/*.wasm', 67 | to: 'js/[name].wasm' 68 | }]) 69 | ], 70 | devServer: { 71 | host: '0.0.0.0', 72 | disableHostCheck: true 73 | }, 74 | devtool: env === 'production' ? 'source-map' : 'source-map', 75 | node: { fs: 'empty' } 76 | } 77 | } 78 | -------------------------------------------------------------------------------- /src/styles/main.scss: -------------------------------------------------------------------------------- 1 | // normalize.css 2 | @import "~normalize.css"; 3 | 4 | // surface imports and customization 5 | // Colors 6 | @import "~surface/src/scss/_imports/_colors.scss"; 7 | $primary: #e67e22; 8 | $secondary: $asbestos; 9 | $accent: #ff5722; 10 | 11 | @import "~surface/src/scss/buttons.scss"; 12 | @import "~surface/src/scss/form.scss"; 13 | 14 | @import 'screen.css'; 15 | 16 | @import 'github-corner.scss'; 17 | 18 | ::-moz-selection { 19 | background: #fff !important; 20 | color: #444 !important; 21 | } 22 | 23 | ::selection { 24 | background: rgba(255, 255, 255, 0) !important; 25 | color: #444 !important; 26 | } 27 | 28 | #universe { 29 | border: 0; 30 | position: fixed; 31 | top: 0; 32 | left: 0; 33 | width: 100%; 34 | height: 100%; 35 | z-index: -1; 36 | cursor: crosshair; 37 | } 38 | 39 | .APL { 40 | background: rgba(243, 243, 243, 0.5); 41 | font-family: "APL385", "APL385 Unicode", "APLX Upright", "Courier APL2 Unicode", "SImPL", "SiMPL medium", monospace; 42 | text-align: center; 43 | padding: 0.5em 0; 44 | border-radius: 0.5em; 45 | // -webkit-text-fill-color: #444; 46 | a { 47 | text-decoration: none; 48 | } 49 | } 50 | 51 | .controls { 52 | text-align: center; 53 | position: fixed; 54 | bottom: 0; 55 | margin-left: auto; 56 | margin-right: auto; 57 | left: 0; 58 | right: 0; 59 | 60 | button { 61 | margin: 0.5rem 1rem; 62 | } 63 | } 64 | 65 | .hidden { 66 | display: none; 67 | } 68 | 69 | .github-link { 70 | position: fixed; 71 | top: 24px; 72 | left: 125px; 73 | font-size: 1rem; 74 | a { 75 | text-decoration: none; 76 | } 77 | .octicon { 78 | height: 20px; 79 | vertical-align: sub; 80 | } 81 | } 82 | 83 | #fps-info { 84 | min-width: 10rem; 85 | position: fixed; 86 | top: 26px; 87 | left: 0; 88 | right: 80px; 89 | text-align: right; 90 | } 91 | 92 | .text-content { 93 | position: fixed; 94 | top: 55px; 95 | bottom: 115px; 96 | left: 0; 97 | right: 0; 98 | max-width: 45rem; 99 | overflow: scroll; 100 | overflow-x: hidden; 101 | margin: auto; 102 | 103 | // nice in FF, broken in chrome 104 | // -webkit-text-fill-color: transparent; 105 | // background: -webkit-linear-gradient(transparent, #444 5%, #444 95%, transparent); 106 | // background: -o-linear-gradient(transparent, #444 5%, #444 95%, transparent); 107 | // -webkit-background-clip: text; 108 | } 109 | 110 | small { 111 | font-size: 0.9rem; 112 | } -------------------------------------------------------------------------------- /src/js/renderer.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | 3 | class Renderer { 4 | constructor (canvas, engine, options = {}) { 5 | this.canvas = canvas 6 | this.context = canvas.getContext('2d') 7 | this.engine = engine 8 | 9 | // options 10 | this.pixelsPerCell = options.pixelsPerCell || 5 11 | this.desiredFPS = options.desiredFPS || 30 12 | this.fpsNode = options.fpsNode || false 13 | this.strokeStyle = options.strokeStyle || 'rgba(255,118,5,0.5)' 14 | this.fillStyle = options.fillStyle || 'rgba(222,122,39,0.5)' 15 | 16 | // renderer variables 17 | this.play = false 18 | this.fpsTime = 0 19 | this.engineTime = 0 20 | this.fps = 0 21 | this.frameNumber = 0 22 | 23 | // setup canvas with correct size 24 | this.canvas.width = this.engine.width * this.pixelsPerCell 25 | this.canvas.height = this.engine.height * this.pixelsPerCell 26 | } 27 | 28 | togglePlay () { 29 | this.play = !this.play 30 | } 31 | 32 | draw (timeStamp) { 33 | window.requestAnimationFrame(this.draw.bind(this)) 34 | 35 | // display engine state on each frame 36 | this.context.clearRect(0, 0, this.canvas.width, this.canvas.height) 37 | this.context.strokeStyle = this.strokeStyle 38 | this.context.fillStyle = this.fillStyle 39 | const shouldFillRect = this.pixelsPerCell > 1 40 | for (let i = 0; i < this.engine.height; i++) { 41 | for (let j = 0; j < this.engine.width; j++) { 42 | if (this.engine.cellSafe(i, j)) { 43 | const jPx = this.pixelsPerCell * j 44 | const iPx = this.pixelsPerCell * i 45 | this.context.strokeRect( 46 | jPx, iPx, this.pixelsPerCell, this.pixelsPerCell 47 | ) 48 | if (shouldFillRect) { 49 | this.context.fillRect( 50 | jPx, iPx, this.pixelsPerCell, this.pixelsPerCell 51 | ) 52 | } 53 | } 54 | } 55 | } 56 | 57 | // compute engine next step with appropriate frequency 58 | const engineElapsed = timeStamp - this.engineTime 59 | if (engineElapsed > 1000 / this.desiredFPS && this.play) { 60 | this.engine.computeNextState() 61 | this.frameNumber += 1 62 | this.engineTime = timeStamp - (engineElapsed % (1000 / this.desiredFPS)) 63 | } 64 | 65 | // Update FPS display every half second 66 | if (this.fpsNode) { 67 | const fpsElapsed = timeStamp - this.fpsTime 68 | if (fpsElapsed > 500) { 69 | this.fps = 1000 / fpsElapsed * this.frameNumber 70 | this.fpsNode.textContent = `${this.fps.toFixed(2)} FPS` 71 | this.fpsTime = timeStamp 72 | this.frameNumber = 0 73 | } 74 | } 75 | } 76 | 77 | start () { 78 | this.engine.computeNextState() 79 | this.play = true 80 | window.requestAnimationFrame(this.draw.bind(this)) 81 | } 82 | } 83 | 84 | export { Renderer as default } 85 | -------------------------------------------------------------------------------- /src/styles/embed.scss: -------------------------------------------------------------------------------- 1 | // // surface imports and customization 2 | // // Colors 3 | // @import "~surface/src/scss/_imports/_colors.scss"; 4 | // $primary: #e67e22; 5 | // $secondary: $asbestos; 6 | // $accent: #ff5722; 7 | 8 | // @import "~surface/src/scss/buttons.scss"; 9 | // @import "~surface/src/scss/form.scss"; 10 | 11 | // ::-moz-selection { 12 | // background: #fff !important; 13 | // color: #444 !important; 14 | // } 15 | 16 | // ::selection { 17 | // background: rgba(255, 255, 255, 0) !important; 18 | // color: #444 !important; 19 | // } 20 | 21 | #universe { 22 | border: 0; 23 | position: relative; 24 | width: 100%; 25 | height: 400px; 26 | z-index: 1; 27 | cursor: crosshair; 28 | border: 1px grey solid; 29 | border-radius: 4px; 30 | box-shadow: 0 0 2em #bbb; 31 | margin: 0 0 1.75em 0; 32 | } 33 | 34 | // .APL { 35 | // background: rgba(243, 243, 243, 0.5); 36 | // font-family: "APL385", "APL385 Unicode", "APLX Upright", "Courier APL2 Unicode", "SImPL", "SiMPL medium", monospace; 37 | // text-align: center; 38 | // padding: 0.5em 0; 39 | // border-radius: 0.5em; 40 | // // -webkit-text-fill-color: #444; 41 | // a { 42 | // text-decoration: none; 43 | // } 44 | // } 45 | 46 | // .controls { 47 | // text-align: center; 48 | // position: fixed; 49 | // bottom: 0; 50 | // margin-left: auto; 51 | // margin-right: auto; 52 | // left: 0; 53 | // right: 0; 54 | 55 | // button { 56 | // margin: 0.5rem 1rem; 57 | // } 58 | // } 59 | 60 | // .hidden { 61 | // display: none; 62 | // } 63 | 64 | // .github-link { 65 | // position: fixed; 66 | // top: 24px; 67 | // left: 125px; 68 | // font-size: 1rem; 69 | // a { 70 | // text-decoration: none; 71 | // } 72 | // .octicon { 73 | // height: 20px; 74 | // vertical-align: sub; 75 | // } 76 | // } 77 | 78 | // #fps-info { 79 | // min-width: 10rem; 80 | // position: fixed; 81 | // top: 26px; 82 | // left: 0; 83 | // right: 80px; 84 | // text-align: right; 85 | // } 86 | 87 | // .text-content { 88 | // position: fixed; 89 | // top: 55px; 90 | // bottom: 115px; 91 | // left: 0; 92 | // right: 0; 93 | // max-width: 45rem; 94 | // overflow: scroll; 95 | // overflow-x: hidden; 96 | // margin: auto; 97 | 98 | // // nice in FF, broken in chrome 99 | // // -webkit-text-fill-color: transparent; 100 | // // background: -webkit-linear-gradient(transparent, #444 5%, #444 95%, transparent); 101 | // // background: -o-linear-gradient(transparent, #444 5%, #444 95%, transparent); 102 | // // -webkit-background-clip: text; 103 | // } 104 | 105 | // small { 106 | // font-size: 0.9rem; 107 | // } -------------------------------------------------------------------------------- /src/js/wasm/engine.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | 6 | 7 | int width = 0; 8 | int height = 0; 9 | char *current; 10 | char *next; 11 | 12 | EMSCRIPTEN_KEEPALIVE 13 | char *init(int w, int h) { 14 | width = w + 2; 15 | height = h + 2; 16 | current = malloc(width * height * sizeof(char)); 17 | next = malloc(width * height * sizeof(char)); 18 | return current; 19 | } 20 | 21 | int cell_index(int i, int j) { 22 | return i * width + j; 23 | } 24 | 25 | EMSCRIPTEN_KEEPALIVE 26 | char cell(int i, int j) { 27 | return current[cell_index(i, j)]; 28 | } 29 | 30 | EMSCRIPTEN_KEEPALIVE 31 | char cellSafe(int cellIndex) { 32 | return current[cellIndex]; 33 | } 34 | 35 | EMSCRIPTEN_KEEPALIVE 36 | char getNext(int i, int j) { 37 | return next[cell_index(i, j)]; 38 | } 39 | 40 | void loopCurrentState() { 41 | for (int j=1; j < width + 1; j++) { 42 | current[cell_index(0, j)] = current[cell_index(height - 2, j)]; 43 | current[cell_index(height - 1, j)] = current[cell_index(1, j)]; 44 | } 45 | for (int i=1; i < height + 1; i++) { 46 | current[cell_index(i, 0)] = current[cell_index(i, width - 2)]; 47 | current[cell_index(i, width - 1)] = current[cell_index(i, 1)]; 48 | } 49 | current[cell_index(0, 0)] = current[cell_index(height - 2, width - 2)]; 50 | current[cell_index(0, width - 1)] = current[cell_index(height - 2, 1)]; 51 | current[cell_index(height - 1, 0)] = current[cell_index(1, width - 2)]; 52 | current[cell_index(height - 1, width - 1)] = current[cell_index(1, 1)]; 53 | } 54 | 55 | EMSCRIPTEN_KEEPALIVE 56 | void computeNextState () { 57 | loopCurrentState(); 58 | 59 | int neighbors = 0; 60 | int i_m1, i_p1, i_; 61 | int j_m1, j_p1; 62 | int height_limit = height - 1; 63 | int width_limit = width - 1; 64 | for (int i = 1; i < height_limit; i++) { 65 | i_m1 = (i - 1) * width; 66 | i_p1 = (i + 1) * width; 67 | i_ = i * width; 68 | for (int j = 1; j < width_limit; j++) { 69 | j_m1 = j - 1; 70 | j_p1 = j + 1; 71 | neighbors = current[i_m1 + j_m1]; 72 | neighbors += current[i_m1 + j]; 73 | neighbors += current[i_m1 + j_p1]; 74 | neighbors += current[i_+ j_m1]; 75 | neighbors += current[i_ + j_p1]; 76 | neighbors += current[i_p1 + j_m1]; 77 | neighbors += current[i_p1 + j]; 78 | neighbors += current[i_p1 + j_p1]; 79 | if (neighbors == 3) { 80 | next[i_ + j] = 1; 81 | } else if (neighbors == 2) { 82 | next[i_ + j] = current[i_ + j]; 83 | } else { 84 | next[i_ + j] = 0; 85 | } 86 | } 87 | } 88 | memcpy(current, next, width * height); 89 | } 90 | 91 | EMSCRIPTEN_KEEPALIVE 92 | void set (int i, int j, int value) { 93 | current[cell_index(i, j)] = value; 94 | } 95 | 96 | EMSCRIPTEN_KEEPALIVE 97 | void setNext (int i,int j, int value) { 98 | next[cell_index(i, j)] = value; 99 | } 100 | 101 | EMSCRIPTEN_KEEPALIVE 102 | int main() { 103 | EM_ASM( 104 | console.log("ok, run"); 105 | ); 106 | return 0; 107 | } -------------------------------------------------------------------------------- /src/js/engine.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | 3 | class Engine { 4 | constructor (width, height) { 5 | this.wasm = false 6 | this.width = width 7 | this._width = width + 2 8 | this.height = height 9 | this._height = height + 2 10 | this.module = { calledRun: true } 11 | } 12 | 13 | init () { 14 | const buffer = new ArrayBuffer(this._width * this._height) 15 | this._current = new Uint8Array(buffer) 16 | const nextBuffer = new ArrayBuffer(this._width * this._height) 17 | this._next = new Uint8Array(nextBuffer) 18 | this.module = { calledRun: true } 19 | } 20 | 21 | index (i, j) { 22 | return i * this._width + j 23 | } 24 | 25 | cell (i, j) { 26 | return this._current[this.index(i, j)] 27 | } 28 | 29 | cellSafe (i, j) { 30 | return this._current[(i + 1) * this._width + j + 1] 31 | } 32 | 33 | next (i, j) { 34 | return this._next[this.index(i, j)] 35 | } 36 | 37 | loopCurrentState () { 38 | for (let j = 1; j < this._width + 1; j++) { 39 | this._current[this.index(0, j)] = this._current[this.index(this._height - 2, j)] 40 | this._current[this.index(this._height - 1, j)] = this._current[this.index(1, j)] 41 | } 42 | for (let i = 1; i < this._height + 1; i++) { 43 | this._current[this.index(i, 0)] = this._current[this.index(i, this._width - 2)] 44 | this._current[this.index(i, this._width - 1)] = this._current[this.index(i, 1)] 45 | } 46 | this._current[this.index(0, 0)] = this._current[this.index(this._height - 2, this._width - 2)] 47 | this._current[this.index(0, this._width - 1)] = this._current[this.index(this._height - 2, 1)] 48 | this._current[this.index(this._height - 1, 0)] = this._current[this.index(1, this._width - 2)] 49 | this._current[this.index(this._height - 1, this._width - 1)] = this._current[this.index(1, 1)] 50 | } 51 | 52 | computeNextState () { 53 | let neighbors, iM1, iP1, i_, jM1, jP1 54 | 55 | this.loopCurrentState() 56 | 57 | for (let i = 1; i < this._height - 1; i++) { 58 | iM1 = (i - 1) * this._width 59 | iP1 = (i + 1) * this._width 60 | i_ = i * this._width 61 | for (let j = 1; j < this._width - 1; j++) { 62 | jM1 = j - 1 63 | jP1 = j + 1 64 | neighbors = this._current[iM1 + jM1] 65 | neighbors += this._current[iM1 + j] 66 | neighbors += this._current[iM1 + jP1] 67 | neighbors += this._current[i_ + jM1] 68 | neighbors += this._current[i_ + jP1] 69 | neighbors += this._current[iP1 + jM1] 70 | neighbors += this._current[iP1 + j] 71 | neighbors += this._current[iP1 + jP1] 72 | if (neighbors === 3) { 73 | this._next[i_ + j] = 1 74 | } else if (neighbors === 2) { 75 | this._next[i_ + j] = this._current[i_ + j] 76 | } else { 77 | this._next[i_ + j] = 0 78 | } 79 | } 80 | } 81 | this._current.set(this._next) 82 | } 83 | 84 | set (i, j, value = 1) { 85 | this._current[this.index(i, j)] = value 86 | } 87 | 88 | setNext (i, j, value = 1) { 89 | this._next[this.index(i, j)] = value 90 | } 91 | } 92 | 93 | export { Engine as default } 94 | -------------------------------------------------------------------------------- /docs/css/styles.css: -------------------------------------------------------------------------------- 1 | * { 2 | -moz-box-sizing: border-box; 3 | -webkit-box-sizing: border-box; 4 | box-sizing: border-box; 5 | } 6 | 7 | html { 8 | background-color: #fff; 9 | padding: 0; 10 | } 11 | 12 | body,html { 13 | margin: 0; 14 | width: 100%; 15 | } 16 | 17 | body { 18 | height: 100%; 19 | padding: 1em; 20 | font-family: Alegreya,Palatino,Georgia,serif; 21 | font-size: 100%; 22 | color: #444; 23 | } 24 | 25 | /*! normalize.css v7.0.0 | MIT License | github.com/necolas/normalize.css */ 26 | 27 | html { 28 | line-height: 1.15; 29 | -ms-text-size-adjust: 100%; 30 | -webkit-text-size-adjust: 100%; 31 | } 32 | 33 | body { 34 | margin: 0; 35 | } 36 | 37 | [type=reset],[type=submit],html [type=button] { 38 | -webkit-appearance: button; 39 | } 40 | 41 | [type=button]::-moz-focus-inner,[type=reset]::-moz-focus-inner,[type=submit]::-moz-focus-inner { 42 | border-style: none; 43 | padding: 0; 44 | } 45 | 46 | [type=button]:-moz-focusring,[type=reset]:-moz-focusring,[type=submit]:-moz-focusring { 47 | outline: 1px dotted ButtonText; 48 | } 49 | 50 | [type=checkbox],[type=radio] { 51 | box-sizing: border-box; 52 | padding: 0; 53 | } 54 | 55 | [type=number]::-webkit-inner-spin-button,[type=number]::-webkit-outer-spin-button { 56 | height: auto; 57 | } 58 | 59 | [type=search] { 60 | -webkit-appearance: textfield; 61 | outline-offset: -2px; 62 | } 63 | 64 | [type=search]::-webkit-search-cancel-button,[type=search]::-webkit-search-decoration { 65 | -webkit-appearance: none; 66 | } 67 | 68 | ::-webkit-file-upload-button { 69 | -webkit-appearance: button; 70 | font: inherit; 71 | } 72 | 73 | [hidden] { 74 | display: none; 75 | } 76 | 77 | [class^=btn] { 78 | transition: all .3s cubic-bezier(.64,.09,.08,1); 79 | box-shadow: 0 2px 5px 0 rgba(0,0,0,.14),0 2px 10px 0 rgba(0,0,0,.1); 80 | position: relative; 81 | cursor: pointer; 82 | text-transform: uppercase; 83 | margin-bottom: 10px; 84 | background-image: none; 85 | background-size: 0; 86 | background-repeat: no-repeat; 87 | background-position: 50% 50%; 88 | transition: background-color .3s cubic-bezier(.64,.09,.08,1),box-shadow .3s cubic-bezier(.64,.09,.08,1); 89 | will-change: background-size,background-image; 90 | padding: 10px 20px; 91 | display: inline-block; 92 | font-family: Roboto; 93 | border: 0; 94 | } 95 | 96 | [class^=btn]:after { 97 | position: absolute; 98 | content: ""; 99 | transition: none; 100 | background: radial-gradient(circle,#fff 95%,transparent 0); 101 | background-size: .7%; 102 | background-position: 50% 50%; 103 | background-repeat: no-repeat; 104 | } 105 | 106 | [class^=btn]:focus { 107 | outline: none; 108 | background-size: 1000%; 109 | transition: all 1s cubic-bezier(.64,.09,.08,1); 110 | } 111 | 112 | @keyframes octocat-wave { 113 | 0%, to { 114 | transform: rotate(0); 115 | } 116 | 117 | 20%, 60% { 118 | transform: rotate(-25deg); 119 | } 120 | 121 | 40%, 80% { 122 | transform: rotate(10deg); 123 | } 124 | } 125 | 126 | ::-moz-selection { 127 | background: #fff!important; 128 | color: #444!important; 129 | } 130 | 131 | ::selection { 132 | background: hsla(0,0%,100%,0)!important; 133 | color: #444!important; 134 | } 135 | /*# sourceMappingURL=styles.css.map?01393879*/ -------------------------------------------------------------------------------- /src/js/demo.js: -------------------------------------------------------------------------------- 1 | import Engine from './engine' 2 | import WasmEngine from './wasmEngine' 3 | import { acorn } from './patterns' 4 | import Renderer from './renderer' 5 | import MouseEventHandler from './events' 6 | import queryString from 'query-string' 7 | 8 | import '../styles/main.scss' 9 | 10 | const defaultOptions = { 11 | canvasSelector: '#universe', 12 | fpsNodeSelector: '#fps-info', 13 | playButtonSelector: '#ctrl-play-pause', 14 | hideButtonSelector: '#ctrl-hide-show', 15 | switchEngineSelector: '#ctrl-engine', 16 | desiredFPS: 30, 17 | pixelsPerCell: 5, 18 | strokeStyle: 'rgba(255,118,5,0.5)', 19 | fillStyle: 'rgba(222,122,39,0.5)', 20 | showText: true, 21 | useWasm: true 22 | } 23 | const urlOptions = queryString.parse(window.location.search) 24 | if (urlOptions.desiredFPS || urlOptions.pixelsperCell) { 25 | defaultOptions.showText = false 26 | } 27 | const options = Object.assign(defaultOptions, urlOptions) 28 | options.desiredFPS = parseInt(options.desiredFPS, 10) 29 | options.pixelsperCell = parseInt(options.pixelsperCell, 10) 30 | 31 | const gameOfLife = () => { 32 | const canvas = document.querySelector(options.canvasSelector) 33 | 34 | const width = ~~(canvas.clientWidth / options.pixelsPerCell) 35 | const height = ~~(canvas.clientHeight / options.pixelsPerCell) 36 | const wasmEngine = new WasmEngine(width, height) 37 | const jsEngine = new Engine(width, height) 38 | var engine 39 | if (options.useWasm === true) { 40 | engine = wasmEngine 41 | } else { 42 | engine = jsEngine 43 | } 44 | window.engine = engine 45 | 46 | const renderer = new Renderer(canvas, engine, { 47 | desiredFPS: options.desiredFPS, 48 | pixelsPerCell: options.pixelsPerCell, 49 | fpsNode: document.querySelector(options.fpsNodeSelector), 50 | strokeStyle: options.strokeStyle, 51 | fillStyle: options.fillStyle 52 | }) 53 | 54 | // mouse events 55 | const playPauseToggle = event => { 56 | renderer.togglePlay() 57 | event.target.textContent = event.target.textContent === 'Pause' ? 'Play' : 'Pause' 58 | } 59 | const hideContentToggle = event => { 60 | var content = document.querySelector('.text-content') 61 | content.classList.toggle('hidden') 62 | event.target.textContent = event.target.textContent === 'Hide text' ? 'Show text' : 'Hide text' 63 | } 64 | const hideText = () => { 65 | var content = document.querySelector('.text-content') 66 | content.classList.add('hidden') 67 | const hideButton = document.querySelector(options.hideButtonSelector) 68 | hideButton.textContent = 'Show text' 69 | } 70 | if (options.showText === false) { 71 | hideText() 72 | } 73 | const switchEngine = event => { 74 | if (engine instanceof WasmEngine) { 75 | engine = jsEngine 76 | } else { 77 | engine = wasmEngine 78 | } 79 | renderer.engine = engine 80 | events.engine = engine 81 | event.target.textContent = event.target.textContent === 'Use js engine' ? 'Use wasm engine' : 'Use js engine' 82 | } 83 | const events = new MouseEventHandler(canvas, engine, renderer) 84 | events.addEvents([ 85 | { 86 | selector: options.playButtonSelector, 87 | eventType: 'click', 88 | callback: playPauseToggle 89 | }, 90 | { 91 | selector: options.hideButtonSelector, 92 | eventType: 'click', 93 | callback: hideContentToggle 94 | }, 95 | { 96 | selector: options.switchEngineSelector, 97 | eventType: 'click', 98 | callback: switchEngine 99 | } 100 | ]) 101 | const checkFlag = () => { 102 | if (engine.module.calledRun !== true) { 103 | window.setTimeout(checkFlag.bind(this), 100) 104 | } else { 105 | // allocate the engines state memory 106 | wasmEngine.init() 107 | jsEngine.init() 108 | // initialize some cells at the center 109 | acorn(wasmEngine, ~~(height / 2), ~~(width / 2)) 110 | acorn(wasmEngine, 0, 0) 111 | acorn(jsEngine, ~~(height / 2), ~~(width / 2)) 112 | acorn(jsEngine, 0, 0) 113 | // start 114 | renderer.start() 115 | } 116 | } 117 | checkFlag() 118 | } 119 | 120 | window.onload = gameOfLife 121 | -------------------------------------------------------------------------------- /docs/index.html: -------------------------------------------------------------------------------- 1 | Way of Life

The Way of Life

Click or touch the screen to add new cells.

The Conway's game of life features an infinite grid of square cells which can be alive or dead. Each cell interacts with its eight neighbours following two rules:

  1. An empty cell that has exactly 3 neighbors is "born" into in the next generation.
  2. An occupied cell that has exactly 2 or 3 neighbors survives to the next generation. Otherwise, its occupant dies (of loneliness or overcrowding).

There is a one line implementation of this game in APL:

life←{ ↑1 ⍵∨.∧3 4=+/,¯1 0 1∘.⊖¯1 0 1∘.⌽⊂⍵ }

What you are seeing is a more naïve javascript implementation of conway's game of life using an HTML canvas to draw it.

This implementation features a universe that is a torus, ie. screen borders are connected and a cell that goes through the right border will reappear on the left one.


Made with ☮ by Maxime R.
Star -------------------------------------------------------------------------------- /src/js/embed.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | 3 | import Engine from './engine' 4 | // import WasmEngine from './wasmEngine' 5 | import { acorn } from './patterns' 6 | import Renderer from './renderer' 7 | import MouseEventHandler from './events' 8 | import queryString from 'query-string' 9 | 10 | // styles 11 | import '../styles/embed.scss' 12 | 13 | const defaultOptions = { 14 | canvasSelector: '#universe', 15 | fpsNodeSelector: '#fps-info', 16 | playButtonSelector: '#ctrl-play-pause', 17 | hideButtonSelector: '#ctrl-hide-show', 18 | switchEngineSelector: '#ctrl-engine', 19 | desiredFPS: 30, 20 | pixelsPerCell: 5, 21 | strokeStyle: 'rgba(255,118,5,0.5)', 22 | fillStyle: 'rgba(222,122,39,0.5)', 23 | showText: true, 24 | useWasm: false 25 | } 26 | const urlOptions = queryString.parse(window.location.search) 27 | if (urlOptions.desiredFPS || urlOptions.pixelsperCell) { 28 | defaultOptions.showText = false 29 | } 30 | const options = Object.assign(defaultOptions, urlOptions) 31 | options.desiredFPS = parseInt(options.desiredFPS, 10) 32 | options.pixelsperCell = parseInt(options.pixelsperCell, 10) 33 | 34 | const gameOfLife = () => { 35 | const canvas = document.querySelector(options.canvasSelector) 36 | 37 | const width = ~~(canvas.clientWidth / options.pixelsPerCell) 38 | const height = ~~(canvas.clientHeight / options.pixelsPerCell) 39 | const wasmEngine = new Engine(width, height) 40 | const jsEngine = new Engine(width, height) 41 | var engine 42 | if (options.useWasm === true) { 43 | engine = wasmEngine 44 | } else { 45 | engine = jsEngine 46 | } 47 | window.engine = engine 48 | 49 | const renderer = new Renderer(canvas, engine, { 50 | desiredFPS: options.desiredFPS, 51 | pixelsPerCell: options.pixelsPerCell, 52 | fpsNode: document.querySelector(options.fpsNodeSelector), 53 | strokeStyle: options.strokeStyle, 54 | fillStyle: options.fillStyle 55 | }) 56 | 57 | // mouse events 58 | const playPauseToggle = event => { 59 | renderer.togglePlay() 60 | event.target.textContent = event.target.textContent === 'Pause' ? 'Play' : 'Pause' 61 | } 62 | const hideContentToggle = event => { 63 | var content = document.querySelector('.text-content') 64 | content.classList.toggle('hidden') 65 | event.target.textContent = event.target.textContent === 'Hide text' ? 'Show text' : 'Hide text' 66 | } 67 | const hideText = () => { 68 | var content = document.querySelector('.text-content') 69 | content.classList.add('hidden') 70 | const hideButton = document.querySelector(options.hideButtonSelector) 71 | hideButton.textContent = 'Show text' 72 | } 73 | if (options.showText === false) { 74 | hideText() 75 | } 76 | const switchEngine = event => { 77 | if (engine instanceof Engine) { 78 | engine = jsEngine 79 | } else { 80 | engine = wasmEngine 81 | } 82 | renderer.engine = engine 83 | events.engine = engine 84 | event.target.textContent = event.target.textContent === 'Use js engine' ? 'Use wasm engine' : 'Use js engine' 85 | } 86 | const events = new MouseEventHandler(canvas, engine, renderer) 87 | events.addEvents([ 88 | { 89 | selector: options.playButtonSelector, 90 | eventType: 'click', 91 | callback: playPauseToggle 92 | }, 93 | { 94 | selector: options.hideButtonSelector, 95 | eventType: 'click', 96 | callback: hideContentToggle 97 | }, 98 | { 99 | selector: options.switchEngineSelector, 100 | eventType: 'click', 101 | callback: switchEngine 102 | } 103 | ]) 104 | const checkFlag = () => { 105 | if (engine.module.calledRun !== true) { 106 | window.setTimeout(checkFlag.bind(this), 100) 107 | } else { 108 | // allocate the engines state memory 109 | wasmEngine.init() 110 | jsEngine.init() 111 | // initialize some cells at the center 112 | acorn(wasmEngine, ~~(height / 2), ~~(width / 2)) 113 | acorn(wasmEngine, 0, 0) 114 | acorn(jsEngine, ~~(height / 2), ~~(width / 2)) 115 | acorn(jsEngine, 0, 0) 116 | // start 117 | renderer.start() 118 | } 119 | } 120 | checkFlag() 121 | } 122 | 123 | window.onload = gameOfLife 124 | -------------------------------------------------------------------------------- /src/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Way of Life 6 | 7 | 8 |
9 | 10 |
11 |
12 |

The Way of Life

13 |

Click or touch the screen to add new cells.

14 |

15 | The Conway's game of life features an infinite grid of square cells which can be alive or dead. Each cell interacts with its eight neighbours following two rules: 16 |

17 |
    18 |
  1. An empty cell that has exactly 3 neighbors is "born" into in the next generation.
  2. 19 |
  3. An occupied cell that has exactly 2 or 3 neighbors survives to the next generation. Otherwise, its occupant dies (of loneliness or overcrowding).
  4. 20 |
21 |

There is a one line implementation of this game in APL:

22 |

23 | 24 | life←{ ↑1 ⍵∨.∧3 4=+/,¯1 0 1∘.⊖¯1 0 1∘.⌽⊂⍵ } 25 | 26 |

27 |

What you are seeing is a more naïve javascript implementation of conway's game of life using an HTML canvas to draw it.

28 |

This implementation features a universe that is a torus, ie. screen borders are connected and a cell that goes through the right border will reappear on the left one.

29 |
30 |
31 | 32 | 33 | 34 |
35 | Made with ☮ by Maxime R. 36 |
37 |
38 | 39 | 45 | 46 | Star 47 | 53 | 54 | 62 | 63 | 64 | -------------------------------------------------------------------------------- /src/js/wasm/engine.js: -------------------------------------------------------------------------------- 1 | 2 | var Module = (function() { 3 | var _scriptDir = typeof document !== 'undefined' && document.currentScript ? document.currentScript.src : undefined; 4 | return ( 5 | function(Module) { 6 | Module = Module || {}; 7 | 8 | var Module=typeof Module!=="undefined"?Module:{};var moduleOverrides={};var key;for(key in Module){if(Module.hasOwnProperty(key)){moduleOverrides[key]=Module[key]}}var arguments_=[];var thisProgram="./this.program";var quit_=function(status,toThrow){throw toThrow};var ENVIRONMENT_IS_WEB=false;var ENVIRONMENT_IS_WORKER=false;var ENVIRONMENT_IS_NODE=false;var ENVIRONMENT_HAS_NODE=false;var ENVIRONMENT_IS_SHELL=false;ENVIRONMENT_IS_WEB=typeof window==="object";ENVIRONMENT_IS_WORKER=typeof importScripts==="function";ENVIRONMENT_HAS_NODE=typeof process==="object"&&typeof process.versions==="object"&&typeof process.versions.node==="string";ENVIRONMENT_IS_NODE=ENVIRONMENT_HAS_NODE&&!ENVIRONMENT_IS_WEB&&!ENVIRONMENT_IS_WORKER;ENVIRONMENT_IS_SHELL=!ENVIRONMENT_IS_WEB&&!ENVIRONMENT_IS_NODE&&!ENVIRONMENT_IS_WORKER;var scriptDirectory="";function locateFile(path){if(Module["locateFile"]){return Module["locateFile"](path,scriptDirectory)}return scriptDirectory+path}var read_,readAsync,readBinary,setWindowTitle;var nodeFS;var nodePath;if(ENVIRONMENT_IS_NODE){scriptDirectory=__dirname+"/";read_=function shell_read(filename,binary){if(!nodeFS)nodeFS=require("fs");if(!nodePath)nodePath=require("path");filename=nodePath["normalize"](filename);return nodeFS["readFileSync"](filename,binary?null:"utf8")};readBinary=function readBinary(filename){var ret=read_(filename,true);if(!ret.buffer){ret=new Uint8Array(ret)}assert(ret.buffer);return ret};if(process["argv"].length>1){thisProgram=process["argv"][1].replace(/\\/g,"/")}arguments_=process["argv"].slice(2);process["on"]("uncaughtException",function(ex){if(!(ex instanceof ExitStatus)){throw ex}});process["on"]("unhandledRejection",abort);quit_=function(status){process["exit"](status)};Module["inspect"]=function(){return"[Emscripten Module object]"}}else if(ENVIRONMENT_IS_SHELL){if(typeof read!="undefined"){read_=function shell_read(f){return read(f)}}readBinary=function readBinary(f){var data;if(typeof readbuffer==="function"){return new Uint8Array(readbuffer(f))}data=read(f,"binary");assert(typeof data==="object");return data};if(typeof scriptArgs!="undefined"){arguments_=scriptArgs}else if(typeof arguments!="undefined"){arguments_=arguments}if(typeof quit==="function"){quit_=function(status){quit(status)}}if(typeof print!=="undefined"){if(typeof console==="undefined")console={};console.log=print;console.warn=console.error=typeof printErr!=="undefined"?printErr:print}}else if(ENVIRONMENT_IS_WEB||ENVIRONMENT_IS_WORKER){if(ENVIRONMENT_IS_WORKER){scriptDirectory=self.location.href}else if(document.currentScript){scriptDirectory=document.currentScript.src}if(_scriptDir){scriptDirectory=_scriptDir}if(scriptDirectory.indexOf("blob:")!==0){scriptDirectory=scriptDirectory.substr(0,scriptDirectory.lastIndexOf("/")+1)}else{scriptDirectory=""}{read_=function shell_read(url){var xhr=new XMLHttpRequest;xhr.open("GET",url,false);xhr.send(null);return xhr.responseText};if(ENVIRONMENT_IS_WORKER){readBinary=function readBinary(url){var xhr=new XMLHttpRequest;xhr.open("GET",url,false);xhr.responseType="arraybuffer";xhr.send(null);return new Uint8Array(xhr.response)}}readAsync=function readAsync(url,onload,onerror){var xhr=new XMLHttpRequest;xhr.open("GET",url,true);xhr.responseType="arraybuffer";xhr.onload=function xhr_onload(){if(xhr.status==200||xhr.status==0&&xhr.response){onload(xhr.response);return}onerror()};xhr.onerror=onerror;xhr.send(null)}}setWindowTitle=function(title){document.title=title}}else{}var out=Module["print"]||console.log.bind(console);var err=Module["printErr"]||console.warn.bind(console);for(key in moduleOverrides){if(moduleOverrides.hasOwnProperty(key)){Module[key]=moduleOverrides[key]}}moduleOverrides=null;if(Module["arguments"])arguments_=Module["arguments"];if(Module["thisProgram"])thisProgram=Module["thisProgram"];if(Module["quit"])quit_=Module["quit"];var STACK_ALIGN=16;function alignMemory(size,factor){if(!factor)factor=STACK_ALIGN;return Math.ceil(size/factor)*factor}var wasmBinary;if(Module["wasmBinary"])wasmBinary=Module["wasmBinary"];var noExitRuntime;if(Module["noExitRuntime"])noExitRuntime=Module["noExitRuntime"];if(typeof WebAssembly!=="object"){err("no native wasm support detected")}var wasmMemory;var wasmTable=new WebAssembly.Table({"initial":1,"maximum":1+0,"element":"anyfunc"});var ABORT=false;var EXITSTATUS=0;function assert(condition,text){if(!condition){abort("Assertion failed: "+text)}}var UTF8Decoder=typeof TextDecoder!=="undefined"?new TextDecoder("utf8"):undefined;var UTF16Decoder=typeof TextDecoder!=="undefined"?new TextDecoder("utf-16le"):undefined;var WASM_PAGE_SIZE=65536;var buffer,HEAP8,HEAPU8,HEAP16,HEAPU16,HEAP32,HEAPU32,HEAPF32,HEAPF64;function updateGlobalBufferAndViews(buf){buffer=buf;Module["HEAP8"]=HEAP8=new Int8Array(buf);Module["HEAP16"]=HEAP16=new Int16Array(buf);Module["HEAP32"]=HEAP32=new Int32Array(buf);Module["HEAPU8"]=HEAPU8=new Uint8Array(buf);Module["HEAPU16"]=HEAPU16=new Uint16Array(buf);Module["HEAPU32"]=HEAPU32=new Uint32Array(buf);Module["HEAPF32"]=HEAPF32=new Float32Array(buf);Module["HEAPF64"]=HEAPF64=new Float64Array(buf)}var DYNAMIC_BASE=5244624,DYNAMICTOP_PTR=1584;var INITIAL_TOTAL_MEMORY=Module["TOTAL_MEMORY"]||16777216;if(Module["wasmMemory"]){wasmMemory=Module["wasmMemory"]}else{wasmMemory=new WebAssembly.Memory({"initial":INITIAL_TOTAL_MEMORY/WASM_PAGE_SIZE,"maximum":INITIAL_TOTAL_MEMORY/WASM_PAGE_SIZE})}if(wasmMemory){buffer=wasmMemory.buffer}INITIAL_TOTAL_MEMORY=buffer.byteLength;updateGlobalBufferAndViews(buffer);HEAP32[DYNAMICTOP_PTR>>2]=DYNAMIC_BASE;function callRuntimeCallbacks(callbacks){while(callbacks.length>0){var callback=callbacks.shift();if(typeof callback=="function"){callback();continue}var func=callback.func;if(typeof func==="number"){if(callback.arg===undefined){Module["dynCall_v"](func)}else{Module["dynCall_vi"](func,callback.arg)}}else{func(callback.arg===undefined?null:callback.arg)}}}var __ATPRERUN__=[];var __ATINIT__=[];var __ATMAIN__=[];var __ATPOSTRUN__=[];var runtimeInitialized=false;var runtimeExited=false;function preRun(){if(Module["preRun"]){if(typeof Module["preRun"]=="function")Module["preRun"]=[Module["preRun"]];while(Module["preRun"].length){addOnPreRun(Module["preRun"].shift())}}callRuntimeCallbacks(__ATPRERUN__)}function initRuntime(){runtimeInitialized=true;callRuntimeCallbacks(__ATINIT__)}function preMain(){callRuntimeCallbacks(__ATMAIN__)}function exitRuntime(){runtimeExited=true}function postRun(){if(Module["postRun"]){if(typeof Module["postRun"]=="function")Module["postRun"]=[Module["postRun"]];while(Module["postRun"].length){addOnPostRun(Module["postRun"].shift())}}callRuntimeCallbacks(__ATPOSTRUN__)}function addOnPreRun(cb){__ATPRERUN__.unshift(cb)}function addOnPostRun(cb){__ATPOSTRUN__.unshift(cb)}var runDependencies=0;var runDependencyWatcher=null;var dependenciesFulfilled=null;function addRunDependency(id){runDependencies++;if(Module["monitorRunDependencies"]){Module["monitorRunDependencies"](runDependencies)}}function removeRunDependency(id){runDependencies--;if(Module["monitorRunDependencies"]){Module["monitorRunDependencies"](runDependencies)}if(runDependencies==0){if(runDependencyWatcher!==null){clearInterval(runDependencyWatcher);runDependencyWatcher=null}if(dependenciesFulfilled){var callback=dependenciesFulfilled;dependenciesFulfilled=null;callback()}}}Module["preloadedImages"]={};Module["preloadedAudios"]={};function abort(what){if(Module["onAbort"]){Module["onAbort"](what)}what+="";out(what);err(what);ABORT=true;EXITSTATUS=1;what="abort("+what+"). Build with -s ASSERTIONS=1 for more info.";throw new WebAssembly.RuntimeError(what)}var dataURIPrefix="data:application/octet-stream;base64,";function isDataURI(filename){return String.prototype.startsWith?filename.startsWith(dataURIPrefix):filename.indexOf(dataURIPrefix)===0}var wasmBinaryFile="engine.wasm";if(!isDataURI(wasmBinaryFile)){wasmBinaryFile=locateFile(wasmBinaryFile)}function getBinary(){try{if(wasmBinary){return new Uint8Array(wasmBinary)}if(readBinary){return readBinary(wasmBinaryFile)}else{throw"both async and sync fetching of the wasm failed"}}catch(err){abort(err)}}function getBinaryPromise(){if(!wasmBinary&&(ENVIRONMENT_IS_WEB||ENVIRONMENT_IS_WORKER)&&typeof fetch==="function"){return fetch(wasmBinaryFile,{credentials:"same-origin"}).then(function(response){if(!response["ok"]){throw"failed to load wasm binary file at '"+wasmBinaryFile+"'"}return response["arrayBuffer"]()}).catch(function(){return getBinary()})}return new Promise(function(resolve,reject){resolve(getBinary())})}function createWasm(){var info={"env":asmLibraryArg,"wasi_unstable":asmLibraryArg};function receiveInstance(instance,module){var exports=instance.exports;Module["asm"]=exports;removeRunDependency("wasm-instantiate")}addRunDependency("wasm-instantiate");function receiveInstantiatedSource(output){receiveInstance(output["instance"])}function instantiateArrayBuffer(receiver){return getBinaryPromise().then(function(binary){return WebAssembly.instantiate(binary,info)}).then(receiver,function(reason){err("failed to asynchronously prepare wasm: "+reason);abort(reason)})}function instantiateAsync(){if(!wasmBinary&&typeof WebAssembly.instantiateStreaming==="function"&&!isDataURI(wasmBinaryFile)&&typeof fetch==="function"){fetch(wasmBinaryFile,{credentials:"same-origin"}).then(function(response){var result=WebAssembly.instantiateStreaming(response,info);return result.then(receiveInstantiatedSource,function(reason){err("wasm streaming compile failed: "+reason);err("falling back to ArrayBuffer instantiation");instantiateArrayBuffer(receiveInstantiatedSource)})})}else{return instantiateArrayBuffer(receiveInstantiatedSource)}}if(Module["instantiateWasm"]){try{var exports=Module["instantiateWasm"](info,receiveInstance);return exports}catch(e){err("Module.instantiateWasm callback failed with error: "+e);return false}}instantiateAsync();return{}}var ASM_CONSTS={1024:function(){console.log("ok, run")}};var _readAsmConstArgsArray=[];function readAsmConstArgs(sigPtr,buf){var args=_readAsmConstArgsArray;args.length=0;while(1){var ch=HEAPU8[sigPtr++];if(!ch)return args;if(ch==="d".charCodeAt(0)||ch==="f".charCodeAt(0)){buf=alignMemory(buf,8);args.push(HEAPF64[buf>>3]);buf+=8}else if(ch==="i".charCodeAt(0)){buf=alignMemory(buf,4);args.push(HEAP32[buf>>2]);buf+=4}}}function _emscripten_asm_const_iii(code,sigPtr,argbuf){var args=readAsmConstArgs(sigPtr,argbuf);return ASM_CONSTS[code].apply(null,args)}__ATINIT__.push({func:function(){___wasm_call_ctors()}});function _emscripten_memcpy_big(dest,src,num){HEAPU8.set(HEAPU8.subarray(src,src+num),dest)}function abortOnCannotGrowMemory(requestedSize){abort("OOM")}function _emscripten_resize_heap(requestedSize){abortOnCannotGrowMemory(requestedSize)}var asmLibraryArg={"a":_emscripten_asm_const_iii,"b":_emscripten_memcpy_big,"c":_emscripten_resize_heap,"memory":wasmMemory,"table":wasmTable};var asm=createWasm();Module["asm"]=asm;var ___wasm_call_ctors=Module["___wasm_call_ctors"]=function(){return Module["asm"]["d"].apply(null,arguments)};var _init=Module["_init"]=function(){return Module["asm"]["e"].apply(null,arguments)};var _cell=Module["_cell"]=function(){return Module["asm"]["f"].apply(null,arguments)};var _cellSafe=Module["_cellSafe"]=function(){return Module["asm"]["g"].apply(null,arguments)};var _getNext=Module["_getNext"]=function(){return Module["asm"]["h"].apply(null,arguments)};var _computeNextState=Module["_computeNextState"]=function(){return Module["asm"]["i"].apply(null,arguments)};var _set=Module["_set"]=function(){return Module["asm"]["j"].apply(null,arguments)};var _setNext=Module["_setNext"]=function(){return Module["asm"]["k"].apply(null,arguments)};var ___original_main=Module["___original_main"]=function(){return Module["asm"]["l"].apply(null,arguments)};var _main=Module["_main"]=function(){return Module["asm"]["m"].apply(null,arguments)};Module["asm"]=asm;var calledRun;Module["then"]=function(func){if(calledRun){func(Module)}else{var old=Module["onRuntimeInitialized"];Module["onRuntimeInitialized"]=function(){if(old)old();func(Module)}}return Module};function ExitStatus(status){this.name="ExitStatus";this.message="Program terminated with exit("+status+")";this.status=status}var calledMain=false;dependenciesFulfilled=function runCaller(){if(!calledRun)run();if(!calledRun)dependenciesFulfilled=runCaller};function callMain(args){var entryFunction=Module["_main"];var argc=0;var argv=0;try{var ret=entryFunction(argc,argv);exit(ret,true)}catch(e){if(e instanceof ExitStatus){return}else if(e=="SimulateInfiniteLoop"){noExitRuntime=true;return}else{var toLog=e;if(e&&typeof e==="object"&&e.stack){toLog=[e,e.stack]}err("exception thrown: "+toLog);quit_(1,e)}}finally{calledMain=true}}function run(args){args=args||arguments_;if(runDependencies>0){return}preRun();if(runDependencies>0)return;function doRun(){if(calledRun)return;calledRun=true;if(ABORT)return;initRuntime();preMain();if(Module["onRuntimeInitialized"])Module["onRuntimeInitialized"]();if(shouldRunNow)callMain(args);postRun()}if(Module["setStatus"]){Module["setStatus"]("Running...");setTimeout(function(){setTimeout(function(){Module["setStatus"]("")},1);doRun()},1)}else{doRun()}}Module["run"]=run;function exit(status,implicit){if(implicit&&noExitRuntime&&status===0){return}if(noExitRuntime){}else{ABORT=true;EXITSTATUS=status;exitRuntime();if(Module["onExit"])Module["onExit"](status)}quit_(status,new ExitStatus(status))}if(Module["preInit"]){if(typeof Module["preInit"]=="function")Module["preInit"]=[Module["preInit"]];while(Module["preInit"].length>0){Module["preInit"].pop()()}}var shouldRunNow=true;if(Module["noInitialRun"])shouldRunNow=false;noExitRuntime=true;run(); 9 | 10 | 11 | return Module 12 | } 13 | ); 14 | })(); 15 | if (typeof exports === 'object' && typeof module === 'object') 16 | module.exports = Module; 17 | else if (typeof define === 'function' && define['amd']) 18 | define([], function() { return Module; }); 19 | else if (typeof exports === 'object') 20 | exports["Module"] = Module; 21 | -------------------------------------------------------------------------------- /docs/js/lab-bundle.js: -------------------------------------------------------------------------------- 1 | !function(e){var t={};function n(r){if(t[r])return t[r].exports;var i=t[r]={i:r,l:!1,exports:{}};return e[r].call(i.exports,i,i.exports,n),i.l=!0,i.exports}n.m=e,n.c=t,n.d=function(e,t,r){n.o(e,t)||Object.defineProperty(e,t,{enumerable:!0,get:r})},n.r=function(e){"undefined"!=typeof Symbol&&Symbol.toStringTag&&Object.defineProperty(e,Symbol.toStringTag,{value:"Module"}),Object.defineProperty(e,"__esModule",{value:!0})},n.t=function(e,t){if(1&t&&(e=n(e)),8&t)return e;if(4&t&&"object"==typeof e&&e&&e.__esModule)return e;var r=Object.create(null);if(n.r(r),Object.defineProperty(r,"default",{enumerable:!0,value:e}),2&t&&"string"!=typeof e)for(var i in e)n.d(r,i,function(t){return e[t]}.bind(null,i));return r},n.n=function(e){var t=e&&e.__esModule?function(){return e.default}:function(){return e};return n.d(t,"a",t),t},n.o=function(e,t){return Object.prototype.hasOwnProperty.call(e,t)},n.p="https://lab.openbloc.fr/way-of-life/",n(n.s=9)}([function(e,t,n){"use strict";const r=n(1),i=n(2),o=n(3);function s(e,t){return t.encode?t.strict?r(e):encodeURIComponent(e):e}function a(e,t){return t.decode?i(e):e}function c(e){const t=e.indexOf("#");return-1!==t&&(e=e.slice(0,t)),e}function l(e){const t=(e=c(e)).indexOf("?");return-1===t?"":e.slice(t+1)}function u(e,t){return t.parseNumbers&&!Number.isNaN(Number(e))&&"string"==typeof e&&""!==e.trim()?e=Number(e):!t.parseBooleans||null===e||"true"!==e.toLowerCase()&&"false"!==e.toLowerCase()||(e="true"===e.toLowerCase()),e}function h(e,t){const n=function(e){let t;switch(e.arrayFormat){case"index":return(e,n,r)=>{t=/\[(\d*)\]$/.exec(e),e=e.replace(/\[\d*\]$/,""),t?(void 0===r[e]&&(r[e]={}),r[e][t[1]]=n):r[e]=n};case"bracket":return(e,n,r)=>{t=/(\[\])$/.exec(e),e=e.replace(/\[\]$/,""),t?void 0!==r[e]?r[e]=[].concat(r[e],n):r[e]=[n]:r[e]=n};case"comma":return(e,t,n)=>{const r="string"==typeof t&&t.split("").indexOf(",")>-1?t.split(","):t;n[e]=r};default:return(e,t,n)=>{void 0!==n[e]?n[e]=[].concat(n[e],t):n[e]=t}}}(t=Object.assign({decode:!0,sort:!0,arrayFormat:"none",parseNumbers:!1,parseBooleans:!1},t)),r=Object.create(null);if("string"!=typeof e)return r;if(!(e=e.trim().replace(/^[?#&]/,"")))return r;for(const i of e.split("&")){let[e,s]=o(i.replace(/\+/g," "),"=");s=void 0===s?null:a(s,t),n(a(e,t),s,r)}for(const e of Object.keys(r)){const n=r[e];if("object"==typeof n&&null!==n)for(const e of Object.keys(n))n[e]=u(n[e],t);else r[e]=u(n,t)}return!1===t.sort?r:(!0===t.sort?Object.keys(r).sort():Object.keys(r).sort(t.sort)).reduce((e,t)=>{const n=r[t];return Boolean(n)&&"object"==typeof n&&!Array.isArray(n)?e[t]=function e(t){return Array.isArray(t)?t.sort():"object"==typeof t?e(Object.keys(t)).sort((e,t)=>Number(e)-Number(t)).map(e=>t[e]):t}(n):e[t]=n,e},Object.create(null))}t.extract=l,t.parse=h,t.stringify=(e,t)=>{if(!e)return"";const n=function(e){switch(e.arrayFormat){case"index":return t=>(n,r)=>{const i=n.length;return void 0===r?n:null===r?[...n,[s(t,e),"[",i,"]"].join("")]:[...n,[s(t,e),"[",s(i,e),"]=",s(r,e)].join("")]};case"bracket":return t=>(n,r)=>void 0===r?n:null===r?[...n,[s(t,e),"[]"].join("")]:[...n,[s(t,e),"[]=",s(r,e)].join("")];case"comma":return t=>(n,r,i)=>null==r||0===r.length?n:0===i?[[s(t,e),"=",s(r,e)].join("")]:[[n,s(r,e)].join(",")];default:return t=>(n,r)=>void 0===r?n:null===r?[...n,s(t,e)]:[...n,[s(t,e),"=",s(r,e)].join("")]}}(t=Object.assign({encode:!0,strict:!0,arrayFormat:"none"},t)),r=Object.keys(e);return!1!==t.sort&&r.sort(t.sort),r.map(r=>{const i=e[r];return void 0===i?"":null===i?s(r,t):Array.isArray(i)?i.reduce(n(r),[]).join("&"):s(r,t)+"="+s(i,t)}).filter(e=>e.length>0).join("&")},t.parseUrl=(e,t)=>({url:c(e).split("?")[0]||"",query:h(l(e),t)})},function(e,t,n){"use strict";e.exports=e=>encodeURIComponent(e).replace(/[!'()*]/g,e=>`%${e.charCodeAt(0).toString(16).toUpperCase()}`)},function(e,t,n){"use strict";var r=new RegExp("%[a-f0-9]{2}","gi"),i=new RegExp("(%[a-f0-9]{2})+","gi");function o(e,t){try{return decodeURIComponent(e.join(""))}catch(e){}if(1===e.length)return e;t=t||1;var n=e.slice(0,t),r=e.slice(t);return Array.prototype.concat.call([],o(n),o(r))}function s(e){try{return decodeURIComponent(e)}catch(i){for(var t=e.match(r),n=1;n{if("string"!=typeof e||"string"!=typeof t)throw new TypeError("Expected the arguments to be of type `string`");if(""===t)return[e];const n=e.indexOf(t);return-1===n?[e]:[e.slice(0,n),e.slice(n+t.length)]}},function(e,t,n){var r=n(5);"string"==typeof r&&(r=[[e.i,r,""]]);var i={sourceMap:!0,hmr:!0,transform:void 0,insertInto:void 0};n(7)(r,i);r.locals&&(e.exports=r.locals)},function(e,t,n){(e.exports=n(6)(!0)).push([e.i,"#universe{border:0;position:relative;width:100%;height:400px;z-index:1;cursor:crosshair;border:1px grey solid;border-radius:4px;box-shadow:0 0 2em #bbb;margin:0 0 1.75em 0}\n","",{version:3,sources:["/home/maxime/github/way-of-life/src/styles/embed.scss"],names:[],mappings:"AAoBA,UACI,QAAS,CACT,iBAAkB,CAClB,UAAW,CACX,YAAa,CACb,SAAU,CACV,gBAAiB,CACjB,qBAAsB,CACtB,iBAAkB,CAClB,uBAAwB,CACxB,mBAAoB",file:"embed.scss",sourcesContent:['// // surface imports and customization\n// // Colors\n// @import "~surface/src/scss/_imports/_colors.scss";\n// $primary: #e67e22;\n// $secondary: $asbestos;\n// $accent: #ff5722;\n\n// @import "~surface/src/scss/buttons.scss";\n// @import "~surface/src/scss/form.scss";\n\n// ::-moz-selection {\n// background: #fff !important;\n// color: #444 !important;\n// }\n\n// ::selection {\n// background: rgba(255, 255, 255, 0) !important;\n// color: #444 !important;\n// }\n\n#universe {\n border: 0;\n position: relative;\n width: 100%;\n height: 400px;\n z-index: 1;\n cursor: crosshair;\n border: 1px grey solid;\n border-radius: 4px;\n box-shadow: 0 0 2em #bbb;\n margin: 0 0 1.75em 0;\n}\n\n// .APL {\n// background: rgba(243, 243, 243, 0.5);\n// font-family: "APL385", "APL385 Unicode", "APLX Upright", "Courier APL2 Unicode", "SImPL", "SiMPL medium", monospace;\n// text-align: center;\n// padding: 0.5em 0;\n// border-radius: 0.5em;\n// // -webkit-text-fill-color: #444;\n// a {\n// text-decoration: none;\n// }\n// }\n\n// .controls {\n// text-align: center;\n// position: fixed;\n// bottom: 0;\n// margin-left: auto;\n// margin-right: auto;\n// left: 0;\n// right: 0;\n\n// button {\n// margin: 0.5rem 1rem;\n// }\n// }\n\n// .hidden {\n// display: none;\n// }\n\n// .github-link {\n// position: fixed;\n// top: 24px;\n// left: 125px;\n// font-size: 1rem;\n// a {\n// text-decoration: none;\n// }\n// .octicon {\n// height: 20px;\n// vertical-align: sub;\n// }\n// }\n\n// #fps-info {\n// min-width: 10rem;\n// position: fixed;\n// top: 26px;\n// left: 0;\n// right: 80px;\n// text-align: right;\n// }\n\n// .text-content {\n// position: fixed;\n// top: 55px;\n// bottom: 115px;\n// left: 0;\n// right: 0;\n// max-width: 45rem;\n// overflow: scroll;\n// overflow-x: hidden;\n// margin: auto;\n\n// // nice in FF, broken in chrome\n// // -webkit-text-fill-color: transparent;\n// // background: -webkit-linear-gradient(transparent, #444 5%, #444 95%, transparent);\n// // background: -o-linear-gradient(transparent, #444 5%, #444 95%, transparent);\n// // -webkit-background-clip: text;\n// }\n\n// small {\n// font-size: 0.9rem;\n// }']}])},function(e,t,n){"use strict";e.exports=function(e){var t=[];return t.toString=function(){return this.map((function(t){var n=function(e,t){var n=e[1]||"",r=e[3];if(!r)return n;if(t&&"function"==typeof btoa){var i=(s=r,a=btoa(unescape(encodeURIComponent(JSON.stringify(s)))),c="sourceMappingURL=data:application/json;charset=utf-8;base64,".concat(a),"/*# ".concat(c," */")),o=r.sources.map((function(e){return"/*# sourceURL=".concat(r.sourceRoot).concat(e," */")}));return[n].concat(o).concat([i]).join("\n")}var s,a,c;return[n].join("\n")}(t,e);return t[2]?"@media ".concat(t[2],"{").concat(n,"}"):n})).join("")},t.i=function(e,n){"string"==typeof e&&(e=[[null,e,""]]);for(var r={},i=0;i=0&&h.splice(t,1)}function g(e){var t=document.createElement("style");if(void 0===e.attrs.type&&(e.attrs.type="text/css"),void 0===e.attrs.nonce){var r=function(){0;return n.nc}();r&&(e.attrs.nonce=r)}return y(t,e.attrs),m(e,t),t}function y(e,t){Object.keys(t).forEach((function(n){e.setAttribute(n,t[n])}))}function b(e,t){var n,r,i,o;if(t.transform&&e.css){if(!(o="function"==typeof t.transform?t.transform(e.css):t.transform.default(e.css)))return function(){};e.css=o}if(t.singleton){var s=u++;n=l||(l=g(t)),r=C.bind(null,n,s,!1),i=C.bind(null,n,s,!0)}else e.sourceMap&&"function"==typeof URL&&"function"==typeof URL.createObjectURL&&"function"==typeof URL.revokeObjectURL&&"function"==typeof Blob&&"function"==typeof btoa?(n=function(e){var t=document.createElement("link");return void 0===e.attrs.type&&(e.attrs.type="text/css"),e.attrs.rel="stylesheet",y(t,e.attrs),m(e,t),t}(t),r=_.bind(null,n,t),i=function(){v(n),n.href&&URL.revokeObjectURL(n.href)}):(n=g(t),r=S.bind(null,n),i=function(){v(n)});return r(e),function(t){if(t){if(t.css===e.css&&t.media===e.media&&t.sourceMap===e.sourceMap)return;r(e=t)}else i()}}e.exports=function(e,t){if("undefined"!=typeof DEBUG&&DEBUG&&"object"!=typeof document)throw new Error("The style-loader cannot be used in a non-browser environment");(t=t||{}).attrs="object"==typeof t.attrs?t.attrs:{},t.singleton||"boolean"==typeof t.singleton||(t.singleton=s()),t.insertInto||(t.insertInto="head"),t.insertAt||(t.insertAt="bottom");var n=p(e,t);return d(n,t),function(e){for(var r=[],i=0;i2&&void 0!==arguments[2]?arguments[2]:1;this._current[this.index(e,t)]=n}},{key:"setNext",value:function(e,t){var n=arguments.length>2&&void 0!==arguments[2]?arguments[2]:1;this._next[this.index(e,t)]=n}}])&&r(t.prototype,n),i&&r(t,i),e}();function o(e,t,n){e.set(t-1,n),e.set(t,n+2),e.set(t+1,n-1),e.set(t+1,n),e.set(t+1,n+3),e.set(t+1,n+4),e.set(t+1,n+5)}function s(e,t,n){e.set(t-1,n),e.set(t,n-1),e.set(t,n),e.set(t,n+1),e.set(t+1,n)}function a(e,t,n){e.set(t-1,n-1,0),e.set(t-1,n,0),e.set(t-1,n+1,0),e.set(t,n-1,0),e.set(t,n,0),e.set(t,n+1,0),e.set(t+1,n-1,0),e.set(t+1,n,0),e.set(t+1,n+1,0)}function c(e,t){for(var n=0;n2&&void 0!==arguments[2]?arguments[2]:{};!function(e,t){if(!(e instanceof t))throw new TypeError("Cannot call a class as a function")}(this,e),this.canvas=t,this.context=t.getContext("2d"),this.engine=n,this.pixelsPerCell=r.pixelsPerCell||5,this.desiredFPS=r.desiredFPS||30,this.fpsNode=r.fpsNode||!1,this.strokeStyle=r.strokeStyle||"rgba(255,118,5,0.5)",this.fillStyle=r.fillStyle||"rgba(222,122,39,0.5)",this.play=!1,this.fpsTime=0,this.engineTime=0,this.fps=0,this.frameNumber=0,this.canvas.width=this.engine.width*this.pixelsPerCell,this.canvas.height=this.engine.height*this.pixelsPerCell}var t,n,r;return t=e,(n=[{key:"togglePlay",value:function(){this.play=!this.play}},{key:"draw",value:function(e){window.requestAnimationFrame(this.draw.bind(this)),this.context.clearRect(0,0,this.canvas.width,this.canvas.height),this.context.strokeStyle=this.strokeStyle,this.context.fillStyle=this.fillStyle;for(var t=this.pixelsPerCell>1,n=0;n1e3/this.desiredFPS&&this.play&&(this.engine.computeNextState(),this.frameNumber+=1,this.engineTime=e-s%(1e3/this.desiredFPS)),this.fpsNode){var a=e-this.fpsTime;a>500&&(this.fps=1e3/a*this.frameNumber,this.fpsNode.textContent="".concat(this.fps.toFixed(2)," FPS"),this.fpsTime=e,this.frameNumber=0)}}},{key:"start",value:function(){this.engine.computeNextState(),this.play=!0,window.requestAnimationFrame(this.draw.bind(this))}}])&&c(t.prototype,n),r&&c(t,r),e}();function u(e,t){for(var n=0;n0&&void 0!==arguments[0]?arguments[0]:[];t.forEach((function(t){e.listeners.push(t);var n=document;t.selector&&(n=document.querySelector(t.selector)),n&&n.addEventListener(t.eventType,t.callback)}))}},{key:"addCells",value:function(e){var t=arguments.length>1&&void 0!==arguments[1]&&arguments[1],n=this.canvas.getBoundingClientRect(),r={x:(e.clientX-n.left)/(n.right-n.left)*this.canvas.clientWidth,y:(e.clientY-n.top)/(n.bottom-n.top)*this.canvas.clientHeight},i={i:~~(r.y/this.renderer.pixelsPerCell),j:~~(r.x/this.renderer.pixelsPerCell)};(this.mouseDown||t)&&(e.ctrlKey?a(this.engine,i.i,i.j):s(this.engine,i.i,i.j))}},{key:"mouseIsDown",value:function(e){0===e.button&&(this.mouseDown=!0,this.addCells(e))}},{key:"mouseIsUp",value:function(e){this.mouseDown=!1}}])&&u(t.prototype,n),r&&u(t,r),e}(),f=n(0),d=n.n(f),p=(n(4),{canvasSelector:"#universe",fpsNodeSelector:"#fps-info",playButtonSelector:"#ctrl-play-pause",hideButtonSelector:"#ctrl-hide-show",switchEngineSelector:"#ctrl-engine",desiredFPS:30,pixelsPerCell:5,strokeStyle:"rgba(255,118,5,0.5)",fillStyle:"rgba(222,122,39,0.5)",showText:!0,useWasm:!1}),m=d.a.parse(window.location.search);(m.desiredFPS||m.pixelsperCell)&&(p.showText=!1);var v=Object.assign(p,m);v.desiredFPS=parseInt(v.desiredFPS,10),v.pixelsperCell=parseInt(v.pixelsperCell,10);window.onload=function(){var e,t=document.querySelector(v.canvasSelector),n=~~(t.clientWidth/v.pixelsPerCell),r=~~(t.clientHeight/v.pixelsPerCell),s=new i(n,r),a=new i(n,r);e=!0===v.useWasm?s:a,window.engine=e;var c=new l(t,e,{desiredFPS:v.desiredFPS,pixelsPerCell:v.pixelsPerCell,fpsNode:document.querySelector(v.fpsNodeSelector),strokeStyle:v.strokeStyle,fillStyle:v.fillStyle});!1===v.showText&&(document.querySelector(".text-content").classList.add("hidden"),document.querySelector(v.hideButtonSelector).textContent="Show text");var u=new h(t,e,c);u.addEvents([{selector:v.playButtonSelector,eventType:"click",callback:function(e){c.togglePlay(),e.target.textContent="Pause"===e.target.textContent?"Play":"Pause"}},{selector:v.hideButtonSelector,eventType:"click",callback:function(e){document.querySelector(".text-content").classList.toggle("hidden"),e.target.textContent="Hide text"===e.target.textContent?"Show text":"Hide text"}},{selector:v.switchEngineSelector,eventType:"click",callback:function(t){e=e instanceof i?a:s,c.engine=e,u.engine=e,t.target.textContent="Use js engine"===t.target.textContent?"Use wasm engine":"Use js engine"}}]);!function t(){!0!==e.module.calledRun?window.setTimeout(t.bind(void 0),100):(s.init(),a.init(),o(s,~~(r/2),~~(n/2)),o(s,0,0),o(a,~~(r/2),~~(n/2)),o(a,0,0),c.start())}()}}]); 2 | //# sourceMappingURL=lab-bundle.js.map?594b8cb4 -------------------------------------------------------------------------------- /docs/js/demo-styles.js: -------------------------------------------------------------------------------- 1 | !function(n){function e(o){if(t[o])return t[o].exports;var r=t[o]={i:o,l:!1,exports:{}};return n[o].call(r.exports,r,r.exports,e),r.l=!0,r.exports}var t={};e.m=n,e.c=t,e.d=function(n,t,o){e.o(n,t)||Object.defineProperty(n,t,{configurable:!1,enumerable:!0,get:o})},e.n=function(n){var t=n&&n.__esModule?function(){return n.default}:function(){return n};return e.d(t,"a",t),t},e.o=function(n,e){return Object.prototype.hasOwnProperty.call(n,e)},e.p="",e(e.s=15)}([,,function(n,e,t){"use strict";function o(n,e){var t=n[1]||"",o=n[3];if(!o)return t;if(e&&"function"==typeof btoa){var i=r(o);return[t].concat(o.sources.map(function(n){return"/*# sourceURL="+o.sourceRoot+n+" */"})).concat([i]).join("\n")}return[t].join("\n")}function r(n){return"/*# sourceMappingURL=data:application/json;charset=utf-8;base64,"+btoa(unescape(encodeURIComponent(JSON.stringify(n))))+" */"}n.exports=function(n){var e=[];return e.toString=function(){return this.map(function(e){var t=o(e,n);return e[2]?"@media "+e[2]+"{"+t+"}":t}).join("")},e.i=function(n,t){"string"==typeof n&&(n=[[null,n,""]]);for(var o={},r=0;rfooter{margin-top:4em;margin-bottom:2em;border-top:1px solid #eee;display:none}article>h1:first-child{display:none}article h1,article h2,article h3,article h4,article h5{font-weight:400;margin-top:2em;color:#000}article h1,article h2{font-size:1.5em}article h3{font-family:Alegreya SC,Palatino,Georgia,serif;font-weight:400;text-transform:lowercase}article h4{font-size:1em;font-style:italic}article h5{font-style:normal}article ol,article ul{list-style-position:outside;padding:0}article blockquote{margin:0;padding-left:1em;border-left:1px solid #aaa}article audio,article figure,article iframe,article img,article object,article video{max-width:100%}@media (min-width:33.236em){.content{padding:1.618em;margin:0 auto;max-width:45rem;font-size:125%}}@media (min-width:33.236em) and screen and (min-width:30em) and (max-width:63.236em){.content,.text-content{width:30em}}","",{version:3,sources:["/home/maxime/git/way-of-life/src/styles/screen.css"],names:[],mappings:"AAAA,EACI,2BAA4B,AAC5B,8BAA+B,AAC/B,qBAAsB,CACzB,AAED,KACI,sBAAuB,AACvB,SAAW,CAGd,AAED,UAJI,SAAU,AACV,UAAW,CAWd,AARD,KAEI,YAAa,AACb,YAAa,AAEb,4CAAkD,AAClD,eAAgB,AAChB,UAAW,CACd,AAED,YACI,WAAY,AACZ,yBAA4B,CAC/B,AAED,QACI,UAAW,CACd,AAED,SACI,eAAiB,AACjB,eAAgB,AAChB,WAAY,CACf,AAED,gBACI,eAAgB,AAChB,kBAAmB,AACnB,0BAA2B,AAC3B,YAAa,CAChB,AAED,uBACI,YAAa,CAChB,AAED,uDAKI,gBAAoB,AACpB,eAAgB,AAChB,UAAY,CACf,AAED,sBAEI,eAAgB,CACnB,AAED,WACI,+CAAqD,AACrD,gBAAoB,AACpB,wBAAyB,CAC5B,AAED,WACI,cAAe,AACf,iBAAkB,CACrB,AAED,WACI,iBAAkB,CACrB,AAED,sBAEI,4BAA6B,AAC7B,SAAU,CACb,AAED,mBACI,SAAU,AACV,iBAAkB,AAClB,0BAA2B,CAC9B,AAED,qFAMI,cAAe,CAClB,AAED,4BACI,SACI,gBAAiB,AACjB,cAAe,AACf,gBAAiB,AACjB,cAAgB,CACnB,CACJ,AAED,qFACI,uBACI,UAAW,CACd,CACJ",file:"screen.css",sourcesContent:['* {\n -moz-box-sizing: border-box;\n -webkit-box-sizing: border-box;\n box-sizing: border-box\n}\n\nhtml {\n background-color: #fff;\n padding: 0;\n margin: 0;\n width: 100%\n}\n\nbody {\n width: 100%;\n height: 100%;\n padding: 1em;\n margin: 0;\n font-family: "Alegreya", Palatino, Georgia, serif;\n font-size: 100%;\n color: #444\n}\n\na, a:visited {\n color: #444;\n transition: color 0.3s ease;\n}\n\na:hover {\n color: #000\n}\n\n.content {\n padding: 0.809em;\n max-width: 30em;\n margin: auto\n}\n\n.content>footer {\n margin-top: 4em;\n margin-bottom: 2em;\n border-top: 1px solid #eee;\n display: none\n}\n\narticle>h1:first-child {\n display: none\n}\n\narticle h1,\narticle h2,\narticle h3,\narticle h4,\narticle h5 {\n font-weight: normal;\n margin-top: 2em;\n color: black\n}\n\narticle h1,\narticle h2 {\n font-size: 1.5em\n}\n\narticle h3 {\n font-family: "Alegreya SC", Palatino, Georgia, serif;\n font-weight: normal;\n text-transform: lowercase\n}\n\narticle h4 {\n font-size: 1em;\n font-style: italic\n}\n\narticle h5 {\n font-style: normal\n}\n\narticle ul,\narticle ol {\n list-style-position: outside;\n padding: 0\n}\n\narticle blockquote {\n margin: 0;\n padding-left: 1em;\n border-left: 1px solid #aaa\n}\n\narticle img,\narticle object,\narticle video,\narticle audio,\narticle figure,\narticle iframe {\n max-width: 100%\n}\n\n@media all and (min-width: 33.236em) {\n .content {\n padding: 1.618em;\n margin: 0 auto;\n max-width: 45rem;\n font-size: 125%;\n }\n}\n\n@media all and (min-width: 33.236em) and screen and (min-width: 30em) and (max-width: 63.236em) {\n .content, .text-content {\n width: 30em\n }\n}\n'],sourceRoot:""}])},function(n,e,t){function o(n,e){for(var t=0;t=0&&C.splice(e,1)}function c(n){var e=document.createElement("style");return n.attrs.type="text/css",l(e,n.attrs),i(n,e),e}function s(n){var e=document.createElement("link");return n.attrs.type="text/css",n.attrs.rel="stylesheet",l(e,n.attrs),i(n,e),e}function l(n,e){Object.keys(e).forEach(function(t){n.setAttribute(t,e[t])})}function d(n,e){var t,o,r,i;if(e.transform&&n.css){if(!(i=e.transform(n.css)))return function(){};n.css=i}if(e.singleton){var l=h++;t=m||(m=c(e)),o=p.bind(null,t,l,!1),r=p.bind(null,t,l,!0)}else n.sourceMap&&"function"==typeof URL&&"function"==typeof URL.createObjectURL&&"function"==typeof URL.revokeObjectURL&&"function"==typeof Blob&&"function"==typeof btoa?(t=s(e),o=u.bind(null,t,e),r=function(){a(t),t.href&&URL.revokeObjectURL(t.href)}):(t=c(e),o=b.bind(null,t),r=function(){a(t)});return o(n),function(e){if(e){if(e.css===n.css&&e.media===n.media&&e.sourceMap===n.sourceMap)return;o(n=e)}else r()}}function p(n,e,t,o){var r=t?"":o.css;if(n.styleSheet)n.styleSheet.cssText=k(e,r);else{var i=document.createTextNode(r),a=n.childNodes;a[e]&&n.removeChild(a[e]),a.length?n.insertBefore(i,a[e]):n.appendChild(i)}}function b(n,e){var t=e.css,o=e.media;if(o&&n.setAttribute("media",o),n.styleSheet)n.styleSheet.cssText=t;else{for(;n.firstChild;)n.removeChild(n.firstChild);n.appendChild(document.createTextNode(t))}}function u(n,e,t){var o=t.css,r=t.sourceMap,i=void 0===e.convertToAbsoluteUrls&&r;(e.convertToAbsoluteUrls||i)&&(o=x(o)),r&&(o+="\n/*# sourceMappingURL=data:application/json;base64,"+btoa(unescape(encodeURIComponent(JSON.stringify(r))))+" */");var a=new Blob([o],{type:"text/css"}),c=n.href;n.href=URL.createObjectURL(a),c&&URL.revokeObjectURL(c)}var A={},g=function(n){var e;return function(){return void 0===e&&(e=n.apply(this,arguments)),e}}(function(){return window&&document&&document.all&&!window.atob}),f=function(n){var e={};return function(t){return void 0===e[t]&&(e[t]=n.call(this,t)),e[t]}}(function(n){return document.querySelector(n)}),m=null,h=0,C=[],x=t(19);n.exports=function(n,e){if("undefined"!=typeof DEBUG&&DEBUG&&"object"!=typeof document)throw new Error("The style-loader cannot be used in a non-browser environment");e=e||{},e.attrs="object"==typeof e.attrs?e.attrs:{},e.singleton||(e.singleton=g()),e.insertInto||(e.insertInto="head"),e.insertAt||(e.insertAt="bottom");var t=r(n,e);return o(t,e),function(n){for(var i=[],a=0;a1)for(var t=1;t1&&n.argv[1].replace(/\\/g,"/"),c=n.argv.slice(2),n.on("uncaughtException",(function(n){if(!(n instanceof xn))throw n})),n.on("unhandledRejection",tn),l=function(e){n.exit(e)},e.inspect=function(){return"[Emscripten Module object]"}):p?("undefined"!=typeof read&&(f=function(n){return read(n)}),h=function(n){var e;return"function"==typeof readbuffer?new Uint8Array(readbuffer(n)):($("object"===a(e=read(n,"binary"))),e)},"undefined"!=typeof scriptArgs?c=scriptArgs:void 0!==arguments&&(c=arguments),"function"==typeof quit&&(l=function(n){quit(n)}),"undefined"!=typeof print&&("undefined"==typeof console&&(console={}),console.log=print,console.warn=console.error="undefined"!=typeof printErr?printErr:print)):(u||A)&&(A?g=self.location.href:document.currentScript&&(g=document.currentScript.src),s&&(g=s),g=0!==g.indexOf("blob:")?g.substr(0,g.lastIndexOf("/")+1):"",f=function(n){var e=new XMLHttpRequest;return e.open("GET",n,!1),e.send(null),e.responseText},A&&(h=function(n){var e=new XMLHttpRequest;return e.open("GET",n,!1),e.responseType="arraybuffer",e.send(null),new Uint8Array(e.response)}));var y=e.print||console.log.bind(console),x=e.printErr||console.warn.bind(console);for(i in o)o.hasOwnProperty(i)&&(e[i]=o[i]);o=null,e.arguments&&(c=e.arguments),e.thisProgram&&e.thisProgram,e.quit&&(l=e.quit);var B,w,v,k=16;function I(n,e){return e||(e=k),Math.ceil(n/e)*e}e.wasmBinary&&(B=e.wasmBinary),e.noExitRuntime&&(w=e.noExitRuntime),"object"!==("undefined"==typeof WebAssembly?"undefined":a(WebAssembly))&&x("no native wasm support detected");var E=new WebAssembly.Table({initial:1,maximum:1,element:"anyfunc"}),S=!1;function $(n,e){n||tn("Assertion failed: "+e)}"undefined"!=typeof TextDecoder&&new TextDecoder("utf8"),"undefined"!=typeof TextDecoder&&new TextDecoder("utf-16le");var z,O,F,_,U=65536;function j(n){z=n,e.HEAP8=new Int8Array(n),e.HEAP16=new Int16Array(n),e.HEAP32=F=new Int32Array(n),e.HEAPU8=O=new Uint8Array(n),e.HEAPU16=new Uint16Array(n),e.HEAPU32=new Uint32Array(n),e.HEAPF32=new Float32Array(n),e.HEAPF64=_=new Float64Array(n)}var G=5244624,P=1584,R=e.TOTAL_MEMORY||16777216;function T(n){for(;n.length>0;){var t=n.shift();if("function"!=typeof t){var r=t.func;"number"==typeof r?void 0===t.arg?e.dynCall_v(r):e.dynCall_vi(r,t.arg):r(void 0===t.arg?null:t.arg)}else t()}}(v=e.wasmMemory?e.wasmMemory:new WebAssembly.Memory({initial:R/U,maximum:R/U}))&&(z=v.buffer),R=z.byteLength,j(z),F[P>>2]=G;var D=[],L=[],H=[],M=[];function W(){if(e.preRun)for("function"==typeof e.preRun&&(e.preRun=[e.preRun]);e.preRun.length;)K(e.preRun.shift());T(D)}function N(){T(L)}function q(){T(H)}function Q(){}function Y(){if(e.postRun)for("function"==typeof e.postRun&&(e.postRun=[e.postRun]);e.postRun.length;)J(e.postRun.shift());T(M)}function K(n){D.unshift(n)}function J(n){M.unshift(n)}var V=0,X=null,Z=null;function nn(n){V++,e.monitorRunDependencies&&e.monitorRunDependencies(V)}function en(n){if(V--,e.monitorRunDependencies&&e.monitorRunDependencies(V),0==V&&(null!==X&&(clearInterval(X),X=null),Z)){var t=Z;Z=null,t()}}function tn(n){throw e.onAbort&&e.onAbort(n),y(n+=""),x(n),S=!0,n="abort("+n+"). Build with -s ASSERTIONS=1 for more info.",new WebAssembly.RuntimeError(n)}e.preloadedImages={},e.preloadedAudios={};var rn="data:application/octet-stream;base64,";function on(n){return String.prototype.startsWith?n.startsWith(rn):0===n.indexOf(rn)}var an="engine.wasm";function sn(){try{if(B)return new Uint8Array(B);if(h)return h(an);throw"both async and sync fetching of the wasm failed"}catch(n){tn(n)}}function cn(){return B||!u&&!A||"function"!=typeof fetch?new Promise((function(n,e){n(sn())})):fetch(an,{credentials:"same-origin"}).then((function(n){if(!n.ok)throw"failed to load wasm binary file at '"+an+"'";return n.arrayBuffer()})).catch((function(){return sn()}))}function ln(){var n={env:Cn,wasi_unstable:Cn};function t(n,t){var r=n.exports;e.asm=r,en()}function r(n){t(n.instance)}function i(e){return cn().then((function(e){return WebAssembly.instantiate(e,n)})).then(e,(function(n){x("failed to asynchronously prepare wasm: "+n),tn(n)}))}if(nn(),e.instantiateWasm)try{return e.instantiateWasm(n,t)}catch(n){return x("Module.instantiateWasm callback failed with error: "+n),!1}return function(){if(B||"function"!=typeof WebAssembly.instantiateStreaming||on(an)||"function"!=typeof fetch)return i(r);fetch(an,{credentials:"same-origin"}).then((function(e){return WebAssembly.instantiateStreaming(e,n).then(r,(function(n){x("wasm streaming compile failed: "+n),x("falling back to ArrayBuffer instantiation"),i(r)}))}))}(),{}}on(an)||(an=m(an));var un={1024:function(){console.log("ok, run")}},An=[];function dn(n,e){var t=An;for(t.length=0;;){var r=O[n++];if(!r)return t;r==="d".charCodeAt(0)||r==="f".charCodeAt(0)?(e=I(e,8),t.push(_[e>>3]),e+=8):r==="i".charCodeAt(0)&&(e=I(e,4),t.push(F[e>>2]),e+=4)}}function pn(n,e,t){var r=dn(e,t);return un[n].apply(null,r)}function fn(n,e,t){O.set(O.subarray(e,e+t),n)}function hn(n){tn("OOM")}function bn(n){hn()}L.push({func:function(){yn()}});var Cn={a:pn,b:fn,c:bn,memory:v,table:E},gn=ln();e.asm=gn;var mn,yn=e.___wasm_call_ctors=function(){return e.asm.d.apply(null,arguments)};function xn(n){this.name="ExitStatus",this.message="Program terminated with exit("+n+")",this.status=n}function Bn(n){var t=e._main;try{vn(t(0,0),!0)}catch(n){if(n instanceof xn)return;if("SimulateInfiniteLoop"==n)return void(w=!0);var r=n;n&&"object"===a(n)&&n.stack&&(r=[n,n.stack]),x("exception thrown: "+r),l(1,n)}}function wn(n){function t(){mn||(mn=!0,S||(N(),q(),e.onRuntimeInitialized&&e.onRuntimeInitialized(),kn&&Bn(),Y()))}n=n||c,V>0||(W(),V>0||(e.setStatus?(e.setStatus("Running..."),setTimeout((function(){setTimeout((function(){e.setStatus("")}),1),t()}),1)):t()))}function vn(n,t){t&&w&&0===n||(w||(S=!0,Q(),e.onExit&&e.onExit(n)),l(n,new xn(n)))}if(e._init=function(){return e.asm.e.apply(null,arguments)},e._cell=function(){return e.asm.f.apply(null,arguments)},e._cellSafe=function(){return e.asm.g.apply(null,arguments)},e._getNext=function(){return e.asm.h.apply(null,arguments)},e._computeNextState=function(){return e.asm.i.apply(null,arguments)},e._set=function(){return e.asm.j.apply(null,arguments)},e._setNext=function(){return e.asm.k.apply(null,arguments)},e.___original_main=function(){return e.asm.l.apply(null,arguments)},e._main=function(){return e.asm.m.apply(null,arguments)},e.asm=gn,e.then=function(n){if(mn)n(e);else{var t=e.onRuntimeInitialized;e.onRuntimeInitialized=function(){t&&t(),n(e)}}return e},Z=function n(){mn||wn(),mn||(Z=n)},e.run=wn,e.preInit)for("function"==typeof e.preInit&&(e.preInit=[e.preInit]);e.preInit.length>0;)e.preInit.pop()();var kn=!0;return e.noInitialRun&&(kn=!1),w=!0,wn(),e});"object"===a(e)&&"object"===a(i)?i.exports=c:void 0===(o=function(){return c}.apply(e,[]))||(i.exports=o)}).call(this,t(0),"/",t(4)(n))},function(n,e,t){"use strict";const r=t(7),i=t(8),o=t(9);function a(n,e){return e.encode?e.strict?r(n):encodeURIComponent(n):n}function s(n,e){return e.decode?i(n):n}function c(n){const e=n.indexOf("#");return-1!==e&&(n=n.slice(0,e)),n}function l(n){const e=(n=c(n)).indexOf("?");return-1===e?"":n.slice(e+1)}function u(n,e){return e.parseNumbers&&!Number.isNaN(Number(n))&&"string"==typeof n&&""!==n.trim()?n=Number(n):!e.parseBooleans||null===n||"true"!==n.toLowerCase()&&"false"!==n.toLowerCase()||(n="true"===n.toLowerCase()),n}function A(n,e){const t=function(n){let e;switch(n.arrayFormat){case"index":return(n,t,r)=>{e=/\[(\d*)\]$/.exec(n),n=n.replace(/\[\d*\]$/,""),e?(void 0===r[n]&&(r[n]={}),r[n][e[1]]=t):r[n]=t};case"bracket":return(n,t,r)=>{e=/(\[\])$/.exec(n),n=n.replace(/\[\]$/,""),e?void 0!==r[n]?r[n]=[].concat(r[n],t):r[n]=[t]:r[n]=t};case"comma":return(n,e,t)=>{const r="string"==typeof e&&e.split("").indexOf(",")>-1?e.split(","):e;t[n]=r};default:return(n,e,t)=>{void 0!==t[n]?t[n]=[].concat(t[n],e):t[n]=e}}}(e=Object.assign({decode:!0,sort:!0,arrayFormat:"none",parseNumbers:!1,parseBooleans:!1},e)),r=Object.create(null);if("string"!=typeof n)return r;if(!(n=n.trim().replace(/^[?#&]/,"")))return r;for(const i of n.split("&")){let[n,a]=o(i.replace(/\+/g," "),"=");a=void 0===a?null:s(a,e),t(s(n,e),a,r)}for(const n of Object.keys(r)){const t=r[n];if("object"==typeof t&&null!==t)for(const n of Object.keys(t))t[n]=u(t[n],e);else r[n]=u(t,e)}return!1===e.sort?r:(!0===e.sort?Object.keys(r).sort():Object.keys(r).sort(e.sort)).reduce((n,e)=>{const t=r[e];return Boolean(t)&&"object"==typeof t&&!Array.isArray(t)?n[e]=function n(e){return Array.isArray(e)?e.sort():"object"==typeof e?n(Object.keys(e)).sort((n,e)=>Number(n)-Number(e)).map(n=>e[n]):e}(t):n[e]=t,n},Object.create(null))}e.extract=l,e.parse=A,e.stringify=(n,e)=>{if(!n)return"";const t=function(n){switch(n.arrayFormat){case"index":return e=>(t,r)=>{const i=t.length;return void 0===r?t:null===r?[...t,[a(e,n),"[",i,"]"].join("")]:[...t,[a(e,n),"[",a(i,n),"]=",a(r,n)].join("")]};case"bracket":return e=>(t,r)=>void 0===r?t:null===r?[...t,[a(e,n),"[]"].join("")]:[...t,[a(e,n),"[]=",a(r,n)].join("")];case"comma":return e=>(t,r,i)=>null==r||0===r.length?t:0===i?[[a(e,n),"=",a(r,n)].join("")]:[[t,a(r,n)].join(",")];default:return e=>(t,r)=>void 0===r?t:null===r?[...t,a(e,n)]:[...t,[a(e,n),"=",a(r,n)].join("")]}}(e=Object.assign({encode:!0,strict:!0,arrayFormat:"none"},e)),r=Object.keys(n);return!1!==e.sort&&r.sort(e.sort),r.map(r=>{const i=n[r];return void 0===i?"":null===i?a(r,e):Array.isArray(i)?i.reduce(t(r),[]).join("&"):a(r,e)+"="+a(i,e)}).filter(n=>n.length>0).join("&")},e.parseUrl=(n,e)=>({url:c(n).split("?")[0]||"",query:A(l(n),e)})},function(n,e){n.exports=function(n){return n.webpackPolyfill||(n.deprecate=function(){},n.paths=[],n.children||(n.children=[]),Object.defineProperty(n,"loaded",{enumerable:!0,get:function(){return n.l}}),Object.defineProperty(n,"id",{enumerable:!0,get:function(){return n.i}}),n.webpackPolyfill=1),n}},function(n,e){},function(n,e,t){(function(n){function t(n,e){for(var t=0,r=n.length-1;r>=0;r--){var i=n[r];"."===i?n.splice(r,1):".."===i?(n.splice(r,1),t++):t&&(n.splice(r,1),t--)}if(e)for(;t--;t)n.unshift("..");return n}function r(n,e){if(n.filter)return n.filter(e);for(var t=[],r=0;r=-1&&!i;o--){var a=o>=0?arguments[o]:n.cwd();if("string"!=typeof a)throw new TypeError("Arguments to path.resolve must be strings");a&&(e=a+"/"+e,i="/"===a.charAt(0))}return(i?"/":"")+(e=t(r(e.split("/"),(function(n){return!!n})),!i).join("/"))||"."},e.normalize=function(n){var o=e.isAbsolute(n),a="/"===i(n,-1);return(n=t(r(n.split("/"),(function(n){return!!n})),!o).join("/"))||o||(n="."),n&&a&&(n+="/"),(o?"/":"")+n},e.isAbsolute=function(n){return"/"===n.charAt(0)},e.join=function(){var n=Array.prototype.slice.call(arguments,0);return e.normalize(r(n,(function(n,e){if("string"!=typeof n)throw new TypeError("Arguments to path.join must be strings");return n})).join("/"))},e.relative=function(n,t){function r(n){for(var e=0;e=0&&""===n[t];t--);return e>t?[]:n.slice(e,t-e+1)}n=e.resolve(n).substr(1),t=e.resolve(t).substr(1);for(var i=r(n.split("/")),o=r(t.split("/")),a=Math.min(i.length,o.length),s=a,c=0;c=1;--o)if(47===(e=n.charCodeAt(o))){if(!i){r=o;break}}else i=!1;return-1===r?t?"/":".":t&&1===r?"/":n.slice(0,r)},e.basename=function(n,e){var t=function(n){"string"!=typeof n&&(n+="");var e,t=0,r=-1,i=!0;for(e=n.length-1;e>=0;--e)if(47===n.charCodeAt(e)){if(!i){t=e+1;break}}else-1===r&&(i=!1,r=e+1);return-1===r?"":n.slice(t,r)}(n);return e&&t.substr(-1*e.length)===e&&(t=t.substr(0,t.length-e.length)),t},e.extname=function(n){"string"!=typeof n&&(n+="");for(var e=-1,t=0,r=-1,i=!0,o=0,a=n.length-1;a>=0;--a){var s=n.charCodeAt(a);if(47!==s)-1===r&&(i=!1,r=a+1),46===s?-1===e?e=a:1!==o&&(o=1):-1!==e&&(o=-1);else if(!i){t=a+1;break}}return-1===e||-1===r||0===o||1===o&&e===r-1&&e===t+1?"":n.slice(e,r)};var i="b"==="ab".substr(-1)?function(n,e,t){return n.substr(e,t)}:function(n,e,t){return e<0&&(e=n.length+e),n.substr(e,t)}}).call(this,t(0))},function(n,e,t){"use strict";n.exports=n=>encodeURIComponent(n).replace(/[!'()*]/g,n=>`%${n.charCodeAt(0).toString(16).toUpperCase()}`)},function(n,e,t){"use strict";var r=new RegExp("%[a-f0-9]{2}","gi"),i=new RegExp("(%[a-f0-9]{2})+","gi");function o(n,e){try{return decodeURIComponent(n.join(""))}catch(n){}if(1===n.length)return n;e=e||1;var t=n.slice(0,e),r=n.slice(e);return Array.prototype.concat.call([],o(t),o(r))}function a(n){try{return decodeURIComponent(n)}catch(i){for(var e=n.match(r),t=1;t{if("string"!=typeof n||"string"!=typeof e)throw new TypeError("Expected the arguments to be of type `string`");if(""===e)return[n];const t=n.indexOf(e);return-1===t?[n]:[n.slice(0,t),n.slice(t+e.length)]}},function(n,e,t){var r=t(11);"string"==typeof r&&(r=[[n.i,r,""]]);var i={sourceMap:!0,hmr:!0,transform:void 0,insertInto:void 0};t(13)(r,i);r.locals&&(n.exports=r.locals)},function(n,e,t){(e=n.exports=t(1)(!0)).i(t(12),""),e.push([n.i,'/*! normalize.css v8.0.1 | MIT License | github.com/necolas/normalize.css */html{line-height:1.15;-webkit-text-size-adjust:100%}body{margin:0}main{display:block}h1{font-size:2em;margin:0.67em 0}hr{box-sizing:content-box;height:0;overflow:visible}pre{font-family:monospace, monospace;font-size:1em}a{background-color:transparent}abbr[title]{border-bottom:none;text-decoration:underline;text-decoration:underline dotted}b,strong{font-weight:bolder}code,kbd,samp{font-family:monospace, monospace;font-size:1em}small{font-size:80%}sub,sup{font-size:75%;line-height:0;position:relative;vertical-align:baseline}sub{bottom:-0.25em}sup{top:-0.5em}img{border-style:none}button,input,optgroup,select,textarea{font-family:inherit;font-size:100%;line-height:1.15;margin:0}button,input{overflow:visible}button,select{text-transform:none}button,[type="button"],[type="reset"],[type="submit"]{-webkit-appearance:button}button::-moz-focus-inner,[type="button"]::-moz-focus-inner,[type="reset"]::-moz-focus-inner,[type="submit"]::-moz-focus-inner{border-style:none;padding:0}button:-moz-focusring,[type="button"]:-moz-focusring,[type="reset"]:-moz-focusring,[type="submit"]:-moz-focusring{outline:1px dotted ButtonText}fieldset{padding:0.35em 0.75em 0.625em}legend{box-sizing:border-box;color:inherit;display:table;max-width:100%;padding:0;white-space:normal}progress{vertical-align:baseline}textarea{overflow:auto}[type="checkbox"],[type="radio"]{box-sizing:border-box;padding:0}[type="number"]::-webkit-inner-spin-button,[type="number"]::-webkit-outer-spin-button{height:auto}[type="search"]{-webkit-appearance:textfield;outline-offset:-2px}[type="search"]::-webkit-search-decoration{-webkit-appearance:none}::-webkit-file-upload-button{-webkit-appearance:button;font:inherit}details{display:block}summary{display:list-item}template{display:none}[hidden]{display:none}[class^="btn"]{transition:all 0.3s cubic-bezier(0.64, 0.09, 0.08, 1);box-shadow:0 2px 5px 0 rgba(0,0,0,0.14),0 2px 10px 0 rgba(0,0,0,0.1);position:relative;cursor:pointer;text-transform:uppercase;margin-bottom:10px;background-image:none;background-size:0;background-repeat:no-repeat;background-position:50% 50%;transition:background-color 0.3s cubic-bezier(0.64, 0.09, 0.08, 1),box-shadow 0.3s cubic-bezier(0.64, 0.09, 0.08, 1);will-change:background-size, background-image;padding:10px 20px;display:inline-block;font-family:Roboto;border:0}[class^="btn"]:after{position:absolute;content:\'\';transition:none;background:radial-gradient(circle, white 95%, rgba(0,0,0,0) 95%);background-size:0.7%;background-position:50% 50%;background-repeat:no-repeat}[class^="btn"]:focus{outline:none;background-size:1000%;transition:all 1s cubic-bezier(0.64, 0.09, 0.08, 1)}.btn--float{border-radius:50%;width:50px;height:50px;background-image:radial-gradient(circle, #ff5722 1%, #ff916f 15%, rgba(0,0,0,0) 30%);background-color:#ff5722;will-change:box-shadow, background-color;font-size:22px;padding:0}.btn--float:hover{box-shadow:0 5px 11px 0 rgba(0,0,0,0.16),0 4px 15px 0 rgba(0,0,0,0.13);background-color:#ff6a3c}.btn--raised{border-radius:1.98px;box-shadow:0 2px 5px 0 rgba(0,0,0,0.14),0 2px 10px 0 rgba(0,0,0,0.1);background-image:radial-gradient(circle, #3498db 1%, #75b9e7 15%, rgba(0,0,0,0) 30%);background-color:#3498db;will-change:box-shadow, background-color;color:white}.btn--raised:hover{box-shadow:0 5px 11px 0 rgba(0,0,0,0.16),0 4px 15px 0 rgba(0,0,0,0.13);background-color:#4aa3df}.btn--flat{background-image:radial-gradient(circle, #7f8c8d 1%, #a7b0b1 15%, rgba(0,0,0,0) 30%);background-color:#7f8c8d;background-color:#fcfcfc;box-shadow:none}.btn--primary{background-image:radial-gradient(circle, #e67e22 1%, #eea667 15%, rgba(0,0,0,0) 30%);background-color:#e67e22}.btn--primary:hover{background-color:#e98b39}.btn--secondary{background-image:radial-gradient(circle, #7f8c8d 1%, #a7b0b1 15%, rgba(0,0,0,0) 30%);background-color:#7f8c8d}.btn--secondary:hover{background-color:#8c9899}.btn--accent{background-image:radial-gradient(circle, #ff5722 1%, #ff916f 15%, rgba(0,0,0,0) 30%);background-color:#ff5722}.btn--accent:hover{background-color:#ff6a3c}.btn--red{background-image:radial-gradient(circle, #e74c3c 1%, #ef8b80 15%, rgba(0,0,0,0) 30%);background-color:#e74c3c}.btn--red:hover{background-color:#ea6153}.btn--yellow{background-image:radial-gradient(circle, #f1c40f 1%, #f5d657 15%, rgba(0,0,0,0) 30%);background-color:#f1c40f}.btn--yellow:hover{background-color:#f2ca27}.btn--green{background-image:radial-gradient(circle, #4caf50 1%, #80c883 15%, rgba(0,0,0,0) 30%);background-color:#4caf50}.btn--green:hover{background-color:#5cb860}.btn--blue{background-image:radial-gradient(circle, #2196f3 1%, #6ab8f7 15%, rgba(0,0,0,0) 30%);background-color:#2196f3}.btn--blue:hover{background-color:#39a1f4}label{color:#2e2e2e}input[type="email"],input[type="text"],input[type="password"]{margin-bottom:40px;width:200px;display:block;border:none;padding:10px 0;border-bottom:solid 1px #e67e22;will-change:background-position;transition:all 0.3s cubic-bezier(0.64, 0.09, 0.08, 1);background:linear-gradient(to bottom, rgba(255,255,255,0) 96%, #e67e22 96%);background-position:-200px 0;background-size:200px 100%;background-repeat:no-repeat;color:#924d10}input[type="email"]:focus,input[type="email"]:valid,input[type="text"]:focus,input[type="text"]:valid,input[type="password"]:focus,input[type="password"]:valid{box-shadow:none;outline:none;background-position:0 0}input[type="email"]:focus::-webkit-input-placeholder,input[type="email"]:valid::-webkit-input-placeholder,input[type="text"]:focus::-webkit-input-placeholder,input[type="text"]:valid::-webkit-input-placeholder,input[type="password"]:focus::-webkit-input-placeholder,input[type="password"]:valid::-webkit-input-placeholder{color:#e67e22;font-size:11px;transform:translateY(-20px);visibility:visible !important}input[type="email"]::-webkit-input-placeholder,input[type="text"]::-webkit-input-placeholder,input[type="password"]::-webkit-input-placeholder{transition:all 0.2s cubic-bezier(0.64, 0.09, 0.08, 1)}input[type="checkbox"]:not(.modal-trigger):not(.toggle):not(#alert-check):not([id*="lightbox-"]){position:relative;cursor:pointer}input[type="checkbox"]:not(.modal-trigger):not(.toggle):not(#alert-check):not([id*="lightbox-"]):before{content:\'\';width:16px;height:16px;border:solid 2px #e67e22;border-radius:3px;background:white;position:absolute}input[type="checkbox"]:not(.modal-trigger):not(.toggle):not(#alert-check):not([id*="lightbox-"]):after{content:\'✓\';color:white;background:#e67e22;position:absolute;top:0;left:0;width:16px;height:16px;border:solid 2px #e67e22;border-radius:3px;display:inline-flex;align-items:center;justify-content:center;opacity:0;transition:opacity 0.3s cubic-bezier(0.64, 0.09, 0.08, 1);will-change:opacity}input[type="checkbox"]:not(.modal-trigger):not(.toggle):not(#alert-check):not([id*="lightbox-"]):checked:after{opacity:1}input[type="checkbox"]:not(.modal-trigger):not(.toggle):not(#alert-check):not([id*="lightbox-"]):focus{outline:none}input[type="radio"]{position:relative;top:2px;left:2px;margin:0 8px;cursor:pointer}input[type="radio"]:before{content:\'\';background-color:white;width:16px;height:16px;border-radius:50%;border:solid 2px #e67e22;display:inline-block;position:absolute;top:-2px;left:-2px;background-image:radial-gradient(circle, #e67e22 40%, white 50%);background-size:0;background-position:50% 50%;background-repeat:no-repeat;transition:all 0.3s cubic-bezier(0.64, 0.09, 0.08, 1);will-change:background-size;z-index:2}input[type="radio"]:after{content:\'\';width:16px;height:16px;background:white;border-radius:50%;position:absolute;transition:all 0.3s cubic-bezier(0.64, 0.09, 0.08, 1)}input[type="radio"]:checked:before{background-size:14px 14px}input[type="radio"]:focus{outline:none}select{border:none;border-bottom:solid 1px #9e9e9e;color:#212121;padding:6px;cursor:pointer}input[type=range]{-webkit-appearance:none}input[type=range]:focus{outline:none}input[type=range]::-webkit-slider-runnable-track{height:3px;cursor:pointer;background:#9e9e9e}input[type=range]::-webkit-slider-thumb{height:16px;width:16px;border-radius:50%;background:#2ecc71;cursor:pointer;-webkit-appearance:none;margin-top:-6px;box-shadow:0 2px 5px 0 rgba(0,0,0,0.1),0 2px 10px 0 rgba(0,0,0,0.07)}input[type=range]::-moz-range-track{height:3px;cursor:pointer;background:#9e9e9e}input[type=range]::-moz-range-thumb{height:16px;width:16px;border-radius:50%;border:none;background:#2ecc71;cursor:pointer}input[type=range]::-ms-track{cursor:pointer;background:transparent;border-color:transparent;color:transparent}input[type=range]::-ms-fill-lower{background:#2ecc71}input[type=range]::-ms-fill-upper{background:#9e9e9e}input[type=range]::-ms-thumb{background:#2ecc71}.toggle{display:none}.toggle:checked+label{background:#93e7b6}.toggle:checked+label:after{background:#2ecc71;margin-left:18px}.toggle+label{position:absolute;width:30px;height:6px;margin-top:12px;background:#9e9e9e;transition:background 0.3s cubic-bezier(0.64, 0.09, 0.08, 1);will-change:background}.toggle+label:after{position:absolute;content:\'\';width:14px;height:14px;border-radius:50%;background:#dedede;display:inline-block;cursor:pointer;margin-top:-4px;margin-left:-1px;transition:all 0.3s ease;will-change:background, margin-left;box-shadow:0 2px 5px 0 rgba(0,0,0,0.1),0 2px 10px 0 rgba(0,0,0,0.07)}textarea{border:solid 1px #9e9e9e;transition:border 0.3s cubic-bezier(0.64, 0.09, 0.08, 1)}textarea:focus{border:solid 1px #e67e22;outline:none}.github-corner svg{fill:rgba(222,122,39,0.5);color:#fff;position:fixed;top:0;border:0;right:0}.github-corner svg:hover{fill:rgba(255,118,5,0.5)}.github-corner:hover .octo-arm{animation:octocat-wave 560ms ease-in-out}@keyframes octocat-wave{0%,100%{transform:rotate(0)}20%,60%{transform:rotate(-25deg)}40%,80%{transform:rotate(10deg)}}@media (max-width: 500px){.github-corner:hover .octo-arm{animation:none}.github-corner .octo-arm{animation:octocat-wave 560ms ease-in-out}}.github-button,iframe{position:fixed;top:20px;left:20px}::-moz-selection{background:#fff !important;color:#444 !important}::selection{background:rgba(255,255,255,0) !important;color:#444 !important}#universe{border:0;position:fixed;top:0;left:0;width:100%;height:100%;z-index:-1;cursor:crosshair}.APL{background:rgba(243,243,243,0.5);font-family:"APL385", "APL385 Unicode", "APLX Upright", "Courier APL2 Unicode", "SImPL", "SiMPL medium", monospace;text-align:center;padding:0.5em 0;border-radius:0.5em}.APL a{text-decoration:none}.controls{text-align:center;position:fixed;bottom:0;margin-left:auto;margin-right:auto;left:0;right:0}.controls button{margin:0.5rem 1rem}.hidden{display:none}.github-link{position:fixed;top:24px;left:125px;font-size:1rem}.github-link a{text-decoration:none}.github-link .octicon{height:20px;vertical-align:sub}#fps-info{min-width:10rem;position:fixed;top:26px;left:0;right:80px;text-align:right}.text-content{position:fixed;top:55px;bottom:115px;left:0;right:0;max-width:45rem;overflow:scroll;overflow-x:hidden;margin:auto}small{font-size:0.9rem}\n',"",{version:3,sources:["/home/maxime/github/way-of-life/node_modules/normalize.css/normalize.css","/home/maxime/github/way-of-life/node_modules/surface/src/scss/buttons.scss","/home/maxime/github/way-of-life/node_modules/surface/src/scss/_imports/_variables.scss","/home/maxime/github/way-of-life/src/styles/main.scss","/home/maxime/github/way-of-life/node_modules/surface/src/scss/_imports/_colors.scss","/home/maxime/github/way-of-life/node_modules/surface/src/scss/form.scss","/home/maxime/github/way-of-life/src/styles/github-corner.scss"],names:[],mappings:"AAAA,2EAAA,CAUA,KACE,gBAAiB,CACjB,6BAA8B,CAC/B,KAUC,QAAS,CACV,KAOC,aAAc,CACf,GAQC,aAAc,CACd,eAAgB,CACjB,GAWC,sBAAuB,CACvB,QAAS,CACT,gBAAiB,CAClB,IAQC,gCAAiC,CACjC,aAAc,CACf,EAUC,4BAA6B,CAC9B,YAQC,kBAAmB,CACnB,yBAA0B,CAC1B,gCAAiC,CAClC,SAQC,kBAAmB,CACpB,cAUC,gCAAiC,CACjC,aAAc,CACf,MAOC,aAAc,CACf,QASC,aAAc,CACd,aAAc,CACd,iBAAkB,CAClB,uBAAwB,CACzB,IAGC,cAAe,CAChB,IAGC,UAAW,CACZ,IAUC,iBAAkB,CACnB,sCAeC,mBAAoB,CACpB,cAAe,CACf,gBAAiB,CACjB,QAAS,CACV,aASC,gBAAiB,CAClB,cASC,mBAAoB,CACrB,sDAUC,yBAA0B,CAC3B,8HAUC,iBAAkB,CAClB,SAAU,CACX,kHAUC,6BAA8B,CAC/B,SAOC,6BAA8B,CAC/B,OAUC,qBAAsB,CACtB,aAAc,CACd,aAAc,CACd,cAAe,CACf,SAAU,CACV,kBAAmB,CACpB,SAOC,uBAAwB,CACzB,SAOC,aAAc,CACf,iCASC,qBAAsB,CACtB,SAAU,CACX,sFAQC,WAAY,CACb,gBAQC,4BAA6B,CAC7B,mBAAoB,CACrB,2CAOC,uBAAwB,CACzB,6BAQC,yBAA0B,CAC1B,YAAa,CACd,QAUC,aAAc,CACf,QAOC,iBAAkB,CACnB,SAUC,YAAa,CACd,SAOC,YAAa,CACd,eCzVA,qDCyBkC,CDxBlC,oECmBkF,CDlBlF,iBAAkB,CAClB,cAAe,CACf,wBAAyB,CACzB,kBC6BiB,CD5BjB,qBAAsB,CACtB,iBAAkB,CAClB,2BAA4B,CAC5B,2BAA4B,CAC5B,oHCekC,CDdlC,6CAA8C,CAC9C,iBCqBe,CDpBf,oBAAqB,CACrB,kBAAmB,CACnB,QAAS,CAeT,qBAbC,iBAAkB,CAClB,UAAW,CACX,eAAgB,CAChB,gEAAiE,CACjE,oBAAqB,CACrB,2BAA4B,CAC5B,2BAA4B,CAC5B,qBAEA,YAAa,CACb,qBAAsB,CACnB,mDCH8B,CDIjC,YASD,iBAAkB,CAClB,UAAW,CACX,WAAY,CAPZ,oFAAyF,CACzF,wBE7Be,CFqCf,wCAAyC,CACzC,cCvCc,CDwCd,SAAU,CAPX,kBASE,sECzByF,CD0BzF,wBAAsC,CACtC,aAID,oBAAwC,CACxC,oEC/BmF,CDWnF,oFAAyF,CACzF,wBG5Bc,CHiDd,wCAAyC,CACzC,WAAY,CALb,mBAOE,sECnC0F,CDoC1F,wBAAqC,CACrC,WA3BD,oFAAyF,CACzF,wBGMiB,CHyBjB,wBGnBkB,CHoBlB,eAAgB,CAChB,cAlCA,oFAAyF,CACzF,wBE/BgB,CFkEjB,oBAGE,wBAAuC,CACvC,gBAxCD,oFAAyF,CACzF,wBGMiB,CHoClB,sBAGE,wBAAyC,CACzC,aA/CD,oFAAyF,CACzF,wBE7Be,CF8EhB,mBAGE,wBAAsC,CACtC,UAtDD,oFAAyF,CACzF,wBGnBiB,CH2ElB,gBAGE,wBAAwC,CACxC,aA7DD,oFAAyF,CACzF,wBGRkB,CHuEnB,mBAGE,wBAAyC,CACzC,YApED,oFAAyF,CACzF,wBGhCc,CHsGf,kBAGE,wBAAqC,CACrC,WA3ED,oFAAyF,CACzF,wBGxBa,CHqGd,iBAGE,wBAAoC,CACpC,MInHD,aAA0B,CAC1B,8DAKE,kBH0Ba,CGzBb,WAAY,CACZ,aAAc,CACd,WAAY,CACZ,cAAe,CACf,+BFRc,CESd,+BAAgC,CAChC,qDAAgD,CAChD,2EAA6E,CAC7E,4BAA6B,CAC7B,0BAA2B,CAC3B,2BAA4B,CAC5B,aAA4B,CAf/B,gKAiBK,eAAgB,CAChB,YAAa,CACb,uBAAwB,CAnB7B,kUAqBO,aFrBU,CEsBV,cAAe,CACf,2BAA4B,CAC5B,6BAA8B,CAxBrC,+IA4BI,qDHN+B,CGO/B,iGAKH,iBAAkB,CAClB,cAAe,CAFhB,wGAIE,UAAW,CACX,UAAW,CACX,WAAY,CACZ,wBFxCe,CEyCf,iBHjBuB,CGkBvB,gBAAiB,CACjB,iBAAkB,CAVpB,uGAaE,WAAS,CAAI,WACN,CAAK,kBF/CJ,CAAO,iBEiDL,CAAQ,KACb,CAAC,MACA,CAAC,UACA,CAAI,WACH,CAAI,wBFrDJ,CAAO,iBDwBK,CAAG,mBGgCd,CAAW,kBACP,CAAM,sBACF,CAAM,SACd,CAAC,yDHrCJ,CAA2B,mBGuCpB,CAAO,+GAGb,SACG,CAAC,uGAGL,YACG,CAAI,oBAIK,iBACT,CAAQ,OACb,CAAG,QACF,CAAG,YACD,CAAK,cACL,CAAO,2BACP,UACE,CAAE,sBACO,CAAK,UAChB,CAAI,WACH,CAAI,iBACG,CAAG,wBFpFV,CAAO,oBEsFN,CAAY,iBACX,CAAQ,QACb,CAAI,SACH,CAAI,gEACQ,CAAgD,iBACjD,CAAC,2BACG,CAAO,2BACT,CAAS,qDHvEtB,CAA2B,2BGyEpB,CAAe,SACnB,CAAC,0BAEJ,UACG,CAAE,UACJ,CAAI,WACH,CAAI,gBACA,CAAK,iBACF,CAAG,iBACR,CAAQ,qDHlFZ,CAA2B,mCGsFzB,yBACU,CAAS,0BAGrB,YACG,CAAI,OAIT,WACG,CAAI,+BDnFN,CAAO,aAUN,CAAO,WC4EL,CAAG,cACJ,CAAO,kBAGE,uBACI,CAAI,wBACjB,YACG,CAAI,iDAIgC,UACtC,CAAG,cACH,CAAO,kBDnGV,CAAO,wCCuGyB,WAC7B,CAAI,UACL,CAAI,iBACI,CAAG,kBDhJV,CAAO,cCkJP,CAAO,uBACK,CAAI,eACZ,CAAI,oEHjI6C,CAAmB,oCGqI/C,UACzB,CAAG,cACH,CAAO,kBDpHV,CAAO,oCCwHqB,WACzB,CAAI,UACL,CAAI,iBACI,CAAG,WACV,CAAI,kBDlKJ,CAAO,cCoKP,CAAO,6BAGW,cAClB,CAAO,sBACH,CAAW,wBACT,CAAW,iBAClB,CAAW,kCAGa,kBD9KvB,CAAO,kCCkLgB,kBD5I1B,CAAO,6BCgJc,kBDtLlB,CAAO,QC0LV,YACI,CAAI,sBAEJ,kBACO,CAAsB,4BAC3B,kBD/LH,CAAO,gBCiMG,CAAI,cAMT,iBACJ,CAAQ,UACX,CAAI,UACH,CAAG,eACC,CAAI,kBDrKV,CAAO,4DFbN,CAA2B,sBGqLrB,CAAU,oBAChB,iBACI,CAAQ,UACT,CAAE,UACJ,CAAI,WACH,CAAI,iBACG,CAAG,kBACN,CAAmB,oBACtB,CAAY,cACb,CAAO,eACH,CAAI,gBACH,CAAI,wBACL,CAAa,mCACZ,CAAuB,oEHxMyB,CAAmB,SG6M1E,wBD1LD,CAAO,wDFbN,CAA2B,eG0M3B,wBFhOE,CAAO,YEkON,CAAI,mBCxOG,yBACR,CAAoB,UACnB,CAAI,cACD,CAAK,KACV,CAAC,QACE,CAAC,OACF,CAAC,yBAGY,wBACd,CAAmB,+BAGC,wCACf,CACf,wBAEU,QAEF,mBACW,CAAS,QAGrB,wBACY,CAAc,QAG1B,uBACY,CAAa,CAAA,0BAIR,+BACU,cACf,CACf,yBACwB,wCACT,CACf,CAAC,sBAIiB,cACR,CAAK,QACV,CAAI,SACH,CAAI,iBH5BE,0BACF,CAAe,qBACpB,CAAe,YAGb,yCAC0B,CAAU,qBACtC,CAAe,UAGf,QACG,CAAC,cACC,CAAK,KACV,CAAC,MACA,CAAC,UACA,CAAI,WACH,CAAI,UACH,CAAE,gBACH,CAAS,KAGjB,gCACY,CAAwB,kHACvB,CAAsG,iBACvG,CAAM,eACT,CAAO,mBACD,CAAK,OAEnB,oBACoB,CAAI,UAIpB,iBACO,CAAM,cACR,CAAK,QACP,CAAC,gBACI,CAAI,iBACH,CAAI,MACZ,CAAC,OACA,CAAC,iBAEF,kBACM,CAAW,QAIpB,YACM,CAAI,aAGL,cACE,CAAK,QACV,CAAI,UACH,CAAK,cACA,CAAI,eACd,oBACoB,CAAI,sBAEjB,WACI,CAAI,kBACI,CAAG,UAIlB,eACM,CAAK,cACN,CAAK,QACV,CAAI,MACH,CAAC,UACA,CAAI,gBACC,CAAK,cAGR,cACC,CAAK,QACV,CAAI,YACD,CAAK,MACP,CAAC,OACA,CAAC,eACG,CAAK,eACN,CAAM,iBACJ,CAAM,WACV,CAAI,MASX,gBACU",file:"main.scss",sourcesContent:['/*! normalize.css v8.0.1 | MIT License | github.com/necolas/normalize.css */\n\n/* Document\n ========================================================================== */\n\n/**\n * 1. Correct the line height in all browsers.\n * 2. Prevent adjustments of font size after orientation changes in iOS.\n */\n\nhtml {\n line-height: 1.15; /* 1 */\n -webkit-text-size-adjust: 100%; /* 2 */\n}\n\n/* Sections\n ========================================================================== */\n\n/**\n * Remove the margin in all browsers.\n */\n\nbody {\n margin: 0;\n}\n\n/**\n * Render the `main` element consistently in IE.\n */\n\nmain {\n display: block;\n}\n\n/**\n * Correct the font size and margin on `h1` elements within `section` and\n * `article` contexts in Chrome, Firefox, and Safari.\n */\n\nh1 {\n font-size: 2em;\n margin: 0.67em 0;\n}\n\n/* Grouping content\n ========================================================================== */\n\n/**\n * 1. Add the correct box sizing in Firefox.\n * 2. Show the overflow in Edge and IE.\n */\n\nhr {\n box-sizing: content-box; /* 1 */\n height: 0; /* 1 */\n overflow: visible; /* 2 */\n}\n\n/**\n * 1. Correct the inheritance and scaling of font size in all browsers.\n * 2. Correct the odd `em` font sizing in all browsers.\n */\n\npre {\n font-family: monospace, monospace; /* 1 */\n font-size: 1em; /* 2 */\n}\n\n/* Text-level semantics\n ========================================================================== */\n\n/**\n * Remove the gray background on active links in IE 10.\n */\n\na {\n background-color: transparent;\n}\n\n/**\n * 1. Remove the bottom border in Chrome 57-\n * 2. Add the correct text decoration in Chrome, Edge, IE, Opera, and Safari.\n */\n\nabbr[title] {\n border-bottom: none; /* 1 */\n text-decoration: underline; /* 2 */\n text-decoration: underline dotted; /* 2 */\n}\n\n/**\n * Add the correct font weight in Chrome, Edge, and Safari.\n */\n\nb,\nstrong {\n font-weight: bolder;\n}\n\n/**\n * 1. Correct the inheritance and scaling of font size in all browsers.\n * 2. Correct the odd `em` font sizing in all browsers.\n */\n\ncode,\nkbd,\nsamp {\n font-family: monospace, monospace; /* 1 */\n font-size: 1em; /* 2 */\n}\n\n/**\n * Add the correct font size in all browsers.\n */\n\nsmall {\n font-size: 80%;\n}\n\n/**\n * Prevent `sub` and `sup` elements from affecting the line height in\n * all browsers.\n */\n\nsub,\nsup {\n font-size: 75%;\n line-height: 0;\n position: relative;\n vertical-align: baseline;\n}\n\nsub {\n bottom: -0.25em;\n}\n\nsup {\n top: -0.5em;\n}\n\n/* Embedded content\n ========================================================================== */\n\n/**\n * Remove the border on images inside links in IE 10.\n */\n\nimg {\n border-style: none;\n}\n\n/* Forms\n ========================================================================== */\n\n/**\n * 1. Change the font styles in all browsers.\n * 2. Remove the margin in Firefox and Safari.\n */\n\nbutton,\ninput,\noptgroup,\nselect,\ntextarea {\n font-family: inherit; /* 1 */\n font-size: 100%; /* 1 */\n line-height: 1.15; /* 1 */\n margin: 0; /* 2 */\n}\n\n/**\n * Show the overflow in IE.\n * 1. Show the overflow in Edge.\n */\n\nbutton,\ninput { /* 1 */\n overflow: visible;\n}\n\n/**\n * Remove the inheritance of text transform in Edge, Firefox, and IE.\n * 1. Remove the inheritance of text transform in Firefox.\n */\n\nbutton,\nselect { /* 1 */\n text-transform: none;\n}\n\n/**\n * Correct the inability to style clickable types in iOS and Safari.\n */\n\nbutton,\n[type="button"],\n[type="reset"],\n[type="submit"] {\n -webkit-appearance: button;\n}\n\n/**\n * Remove the inner border and padding in Firefox.\n */\n\nbutton::-moz-focus-inner,\n[type="button"]::-moz-focus-inner,\n[type="reset"]::-moz-focus-inner,\n[type="submit"]::-moz-focus-inner {\n border-style: none;\n padding: 0;\n}\n\n/**\n * Restore the focus styles unset by the previous rule.\n */\n\nbutton:-moz-focusring,\n[type="button"]:-moz-focusring,\n[type="reset"]:-moz-focusring,\n[type="submit"]:-moz-focusring {\n outline: 1px dotted ButtonText;\n}\n\n/**\n * Correct the padding in Firefox.\n */\n\nfieldset {\n padding: 0.35em 0.75em 0.625em;\n}\n\n/**\n * 1. Correct the text wrapping in Edge and IE.\n * 2. Correct the color inheritance from `fieldset` elements in IE.\n * 3. Remove the padding so developers are not caught out when they zero out\n * `fieldset` elements in all browsers.\n */\n\nlegend {\n box-sizing: border-box; /* 1 */\n color: inherit; /* 2 */\n display: table; /* 1 */\n max-width: 100%; /* 1 */\n padding: 0; /* 3 */\n white-space: normal; /* 1 */\n}\n\n/**\n * Add the correct vertical alignment in Chrome, Firefox, and Opera.\n */\n\nprogress {\n vertical-align: baseline;\n}\n\n/**\n * Remove the default vertical scrollbar in IE 10+.\n */\n\ntextarea {\n overflow: auto;\n}\n\n/**\n * 1. Add the correct box sizing in IE 10.\n * 2. Remove the padding in IE 10.\n */\n\n[type="checkbox"],\n[type="radio"] {\n box-sizing: border-box; /* 1 */\n padding: 0; /* 2 */\n}\n\n/**\n * Correct the cursor style of increment and decrement buttons in Chrome.\n */\n\n[type="number"]::-webkit-inner-spin-button,\n[type="number"]::-webkit-outer-spin-button {\n height: auto;\n}\n\n/**\n * 1. Correct the odd appearance in Chrome and Safari.\n * 2. Correct the outline style in Safari.\n */\n\n[type="search"] {\n -webkit-appearance: textfield; /* 1 */\n outline-offset: -2px; /* 2 */\n}\n\n/**\n * Remove the inner padding in Chrome and Safari on macOS.\n */\n\n[type="search"]::-webkit-search-decoration {\n -webkit-appearance: none;\n}\n\n/**\n * 1. Correct the inability to style clickable types in iOS and Safari.\n * 2. Change font properties to `inherit` in Safari.\n */\n\n::-webkit-file-upload-button {\n -webkit-appearance: button; /* 1 */\n font: inherit; /* 2 */\n}\n\n/* Interactive\n ========================================================================== */\n\n/*\n * Add the correct display in Edge, IE 10+, and Firefox.\n */\n\ndetails {\n display: block;\n}\n\n/*\n * Add the correct display in all browsers.\n */\n\nsummary {\n display: list-item;\n}\n\n/* Misc\n ========================================================================== */\n\n/**\n * Add the correct display in IE 10+.\n */\n\ntemplate {\n display: none;\n}\n\n/**\n * Add the correct display in IE 10.\n */\n\n[hidden] {\n display: none;\n}\n',"@import './_imports/_variables';\n\n[class^=\"btn\"] {\n\ttransition: all 0.3s $cubic;\n\tbox-shadow: $box-shadow-float;\n\tposition: relative;\n\tcursor: pointer;\n\ttext-transform: uppercase;\n\tmargin-bottom: $space-small;\n\tbackground-image: none;\n\tbackground-size: 0;\n\tbackground-repeat: no-repeat;\n\tbackground-position: 50% 50%;\n\ttransition: background-color 0.3s $cubic, box-shadow 0.3s $cubic;\n\twill-change: background-size, background-image;\n\tpadding: $space-small $space-med;\n\tdisplay: inline-block;\n\tfont-family: Roboto;\n\tborder: 0;\n\t&:after {\n\t\tposition: absolute;\n\t\tcontent: '';\n\t\ttransition: none;\n\t\tbackground: radial-gradient(circle, white 95%, rgba(0,0,0,0) 95%);\n\t\tbackground-size: 0.7%;\n\t\tbackground-position: 50% 50%;\n\t\tbackground-repeat: no-repeat;\n\t}\n\t&:focus {\n\t\toutline: none;\n\t\tbackground-size: 1000%;\n \ttransition: all 1s $cubic;\n\t}\n}\n\n@mixin btn-gradient($c) {\n\tbackground-image: radial-gradient(circle, $c 1%, lighten($c, 15%) 15%, rgba(0,0,0,0) 30%);\n\tbackground-color: $c;\n}\n\n.btn--float {\n\tborder-radius: 50%;\n\twidth: 50px;\n\theight: 50px;\n\t@include btn-gradient($accent);\n\twill-change: box-shadow, background-color;\n\tfont-size: $font-big;\n\tpadding: 0;\n\t&:hover {\n\t\tbox-shadow: $box-shadow-float-hover;\n\t\tbackground-color: lighten($accent, 5%);\n\t}\n}\n\n.btn--raised {\n\tborder-radius: $border-radius-small*0.66;\n\tbox-shadow: $box-shadow-raised;\n\t@include btn-gradient($river);\n\twill-change: box-shadow, background-color;\n\tcolor: white;\n\t&:hover {\n\t\tbox-shadow: $box-shadow-raised-hover;\n\t\tbackground-color: lighten($river, 5%);\n\t}\n}\n\n.btn--flat {\n\t@include btn-gradient($secondary);\n\tbackground-color: $body-background;\n\tbox-shadow: none;\n}\n\n.btn--primary {\n\t@include btn-gradient($primary);\n\t&:hover {\n\t\tbackground-color: lighten($primary, 5%);\n\t}\n}\n\n.btn--secondary {\n\t@include btn-gradient($secondary);\n\t&:hover {\n\t\tbackground-color: lighten($secondary, 5%);\n\t}\n}\n\n.btn--accent {\n\t@include btn-gradient($accent);\n\t&:hover {\n\t\tbackground-color: lighten($accent, 5%);\n\t}\n}\n\n.btn--red {\n\t@include btn-gradient($alizarin);\n\t&:hover {\n\t\tbackground-color: lighten($alizarin, 5%);\n\t}\n}\n\n.btn--yellow {\n\t@include btn-gradient($sunflower);\n\t&:hover {\n\t\tbackground-color: lighten($sunflower, 5%);\n\t}\n}\n\n.btn--green {\n\t@include btn-gradient($green);\n\t&:hover {\n\t\tbackground-color: lighten($green, 5%);\n\t}\n}\n\n.btn--blue {\n\t@include btn-gradient($blue);\n\t&:hover {\n\t\tbackground-color: lighten($blue, 5%);\n\t}\n}\n\n","@import './colors';\n\n// Type\n$serif: 'Gentium Book Basic', serif !default;\n$sans: Roboto, sans-serif !default;\n$font-small: 12px !default;\n$font-med: 16px !default;\n$font-big: 22px !default;\n$font-huge: 40px !default;\n$line-height: 130% !default;\n\n// Colors\n$primary: $turqoise !default;\n$secondary: lighten($turqoise, 10%) !default;\n$accent: $yellow !default;\n\n$body-background: $off-white !default;\n\n// Grid\n$grid-columns: 12 !default;\n\n// Decorative\n$box-shadow-card: 0 2px 5px 0 rgba(0, 0, 0, 0.1), 0 2px 10px 0 rgba(0, 0, 0, 0.07) !default;\n$box-shadow-float: 0 2px 5px 0 rgba(0, 0, 0, 0.14), 0 2px 10px 0 rgba(0, 0, 0, 0.1) !default;\n$box-shadow-float-hover: 0 5px 11px 0 rgba(0, 0, 0, 0.16), 0 4px 15px 0 rgba(0, 0, 0, 0.13) !default;\n$box-shadow-raised: 0 2px 5px 0 rgba(0, 0, 0, 0.14), 0 2px 10px 0 rgba(0, 0, 0, 0.1) !default;\n$box-shadow-raised-hover: 0 5px 11px 0 rgba(0, 0, 0, 0.16), 0 4px 15px 0 rgba(0, 0, 0, 0.13) !default;\n\n$cubic: cubic-bezier(.64,.09,.08,1) !default;\n\n$border-radius-small: 3px !default;\n$border-radius-med: 6px !default;\n$border-radius-big: 10px !default;\n\n// Spacing\n$space-big: 40px !default;\n$space-med: 20px !default;\n$space-small: 10px !default;\n\n// Media Queries\n$media-med: 1200px !default;\n$media-small: 900px !default;\n$media-tiny: 520px !default;\n",'// normalize.css\n@import "~normalize.css";\n\n// surface imports and customization\n// Colors\n@import "~surface/src/scss/_imports/_colors.scss";\n$primary: #e67e22;\n$secondary: $asbestos;\n$accent: #ff5722;\n\n@import "~surface/src/scss/buttons.scss";\n@import "~surface/src/scss/form.scss";\n\n@import \'screen.css\';\n\n@import \'github-corner.scss\';\n\n::-moz-selection {\n background: #fff !important;\n color: #444 !important;\n}\n\n::selection {\n background: rgba(255, 255, 255, 0) !important;\n color: #444 !important;\n}\n\n#universe {\n border: 0;\n position: fixed;\n top: 0;\n left: 0;\n width: 100%;\n height: 100%;\n z-index: -1;\n cursor: crosshair;\n}\n\n.APL {\n background: rgba(243, 243, 243, 0.5);\n font-family: "APL385", "APL385 Unicode", "APLX Upright", "Courier APL2 Unicode", "SImPL", "SiMPL medium", monospace;\n text-align: center;\n padding: 0.5em 0;\n border-radius: 0.5em;\n // -webkit-text-fill-color: #444;\n a {\n text-decoration: none;\n }\n}\n\n.controls {\n text-align: center;\n position: fixed;\n bottom: 0;\n margin-left: auto;\n margin-right: auto;\n left: 0;\n right: 0;\n\n button {\n margin: 0.5rem 1rem;\n }\n}\n\n.hidden {\n display: none;\n}\n\n.github-link {\n position: fixed;\n top: 24px;\n left: 125px;\n font-size: 1rem;\n a {\n text-decoration: none;\n }\n .octicon {\n height: 20px;\n vertical-align: sub;\n }\n}\n\n#fps-info {\n min-width: 10rem;\n position: fixed;\n top: 26px;\n left: 0;\n right: 80px;\n text-align: right;\n}\n\n.text-content {\n position: fixed;\n top: 55px;\n bottom: 115px;\n left: 0;\n right: 0;\n max-width: 45rem;\n overflow: scroll;\n overflow-x: hidden;\n margin: auto;\n\n // nice in FF, broken in chrome\n // -webkit-text-fill-color: transparent;\n // background: -webkit-linear-gradient(transparent, #444 5%, #444 95%, transparent);\n // background: -o-linear-gradient(transparent, #444 5%, #444 95%, transparent);\n // -webkit-background-clip: text;\n}\n\nsmall {\n font-size: 0.9rem;\n}',"//Greens\n$turqoise: #1abc9c;\n$green-sea: #16a085;\n$emerald: #2ecc71;\n$nephritis: #27ae60;\n$green: #4caf50;\n$light-green: #8bc34a;\n$lime: #cddc39;\n//Blues\n$river: #3498db;\n$belize: #2980b9;\n$asphalt: #34495e;\n$midnight-blue: #2c3e50;\n$blue: #2196f3;\n$light-blue: #03a9f4;\n$cyan: #00bcd4;\n$teal: #009688;\n//Reds\n$alizarin: #e74c3c;\n$pomegranate: #c0392b;\n$red: #f44336;\n//Oranges\n$carrot: #e67e22;\n$pumpkin: #d35400;\n$dull-orange: #f39c12;\n$orange: #ff9800;\n$blood-orange: #ff5722;\n$amber: #ffc107;\n//Yellows\n$sunflower: #f1c40f;\n$yellow: #ffeb3b;\n//Purples + Pinks\n$amethyst: #9b59b6;\n$plum: #8e44ad;\n$purple: #9c27b0;\n$deep-purple: #673ab7;\n$pink: #e91e63;\n$indigo: #3f51b5;\n//Browns\n$brown: #795548;\n//Greys\n$grey: #9e9e9e;\n$gun-metal: #607d8b;\n$asbestos: #7f8c8d;\n$concrete: #95a5a6;\n$silver: #bdc3c7;\n//Whites\n$clouds: #dde4e6;\n$paper: #efefef;\n$off-white: #fcfcfc;\n//Blacks\n$black: #212121;\n","@import './_imports/_variables';\n\nlabel {\n\tcolor: lighten($black, 5%);\n}\n\ninput {\n\t&[type=\"email\"], &[type=\"text\"], \n\t&[type=\"password\"] {\n\t margin-bottom: $space-big;\n\t width: 200px;\n\t display: block;\n\t border: none;\n\t padding: 10px 0;\n\t border-bottom: solid 1px $primary;\n\t will-change: background-position;\n\t transition: all 0.3s cubic-bezier(.64,.09,.08,1);\n\t background: linear-gradient(to bottom, rgba(255,255,255,0) 96%, $primary 96%);\n\t background-position: -200px 0;\n\t background-size: 200px 100%;\n\t background-repeat: no-repeat;\n\t color: darken($primary, 20%);\n\t &:focus, &:valid {\n\t box-shadow: none;\n\t outline: none;\n\t background-position: 0 0;\n\t &::-webkit-input-placeholder {\n\t color: $primary;\n\t font-size: 11px;\n\t transform: translateY(-20px);\n\t visibility: visible !important;\n\t }\n\t }\n\t &::-webkit-input-placeholder {\n\t \ttransition: all 0.2s $cubic;\n\t }\n\t}\n}\n\ninput[type=\"checkbox\"]:not(.modal-trigger):not(.toggle):not(#alert-check):not([id*=\"lightbox-\"]) {\n\tposition: relative;\n\tcursor: pointer;\n\t&:before {\n\t\tcontent: '';\n\t\twidth: 16px;\n\t\theight: 16px;\n\t\tborder: solid 2px $primary;\n\t\tborder-radius: $border-radius-small;\n\t\tbackground: white;\n\t\tposition: absolute;\n\t}\n\t&:after {\n\t\tcontent: '✓';\n\t\tcolor: white;\n\t\tbackground: $primary;\n\t\tposition: absolute;\n\t\ttop: 0;\n\t\tleft: 0;\n\t\twidth: 16px;\n\t\theight: 16px;\n\t\tborder: solid 2px $primary;\n\t\tborder-radius: $border-radius-small;\n\t\tdisplay: inline-flex;\n\t\talign-items: center;\n\t\tjustify-content: center;\n\t\topacity: 0;\n\t\ttransition: opacity 0.3s $cubic;\n\t\twill-change: opacity;\n\t}\n\t&:checked {\n\t\t&:after {\n\t\t\topacity: 1;\n\t\t}\n\t}\n\t&:focus {\n\t\toutline: none;\n\t}\n}\n\ninput[type=\"radio\"] {\n\tposition: relative;\n\ttop: 2px;\n\tleft: 2px;\n\tmargin: 0 8px;\n\tcursor: pointer;\n\t&:before {\n\t\tcontent: '';\n\t\tbackground-color: white;\n\t\twidth: 16px;\n\t\theight: 16px;\n\t\tborder-radius: 50%;\n\t\tborder: solid 2px $primary;\n\t\tdisplay: inline-block;\n\t\tposition: absolute;\n\t\ttop: -2px;\n\t\tleft: -2px;\n\t\tbackground-image: radial-gradient(circle, $primary 40%, white 50%);\n\t\tbackground-size: 0;\n\t\tbackground-position: 50% 50%;\n\t\tbackground-repeat: no-repeat;\n\t\ttransition: all 0.3s $cubic;\n\t\twill-change: background-size;\n\t\tz-index: 2;\n\t}\n\t&:after {\n\t\tcontent: '';\n\t\twidth: 16px;\n\t\theight: 16px;\n\t\tbackground: white;\n\t\tborder-radius: 50%;\n\t\tposition: absolute;\n\t\ttransition: all 0.3s $cubic;\n\t}\n\t&:checked {\n\t\t&:before {\n\t\t\tbackground-size: 14px 14px;\n\t\t}\n\t}\n\t&:focus {\n\t\toutline: none;\n\t}\n}\n\nselect {\n\tborder: none;\n\tborder-bottom: solid 1px $grey;\n\tcolor: $black;\n\tpadding: 6px;\n\tcursor: pointer;\n}\n\ninput[type=range] {\n -webkit-appearance: none;\n &:focus {\n \toutline: none;\n }\n}\n\ninput[type=range]::-webkit-slider-runnable-track {\n height: 3px;\n cursor: pointer;\n background: $grey;\n}\n\ninput[type=range]::-webkit-slider-thumb {\n height: 16px;\n width: 16px;\n border-radius: 50%;\n background: $emerald;\n cursor: pointer;\n -webkit-appearance: none;\n margin-top: -6px;\n box-shadow: $box-shadow-card;\n}\n\ninput[type=range]::-moz-range-track {\n height: 3px;\n cursor: pointer;\n background: $grey;\n}\n\ninput[type=range]::-moz-range-thumb {\n height: 16px;\n width: 16px;\n border-radius: 50%;\n border: none;\n background: $emerald;\n cursor: pointer;\n}\n\ninput[type=range]::-ms-track {\n cursor: pointer;\n background: transparent;\n border-color: transparent;\n color: transparent;\n}\n\ninput[type=range]::-ms-fill-lower {\n background: $emerald;\n}\n\ninput[type=range]::-ms-fill-upper {\n background: $grey;\n}\n\ninput[type=range]::-ms-thumb {\n background: $emerald;\n}\n\n.toggle {\n display: none;\n &:checked {\n + label {\n background: lighten($emerald, 25%);\n &:after {\n background: $emerald;\n \tmargin-left: 18px;\n }\n }\n }\n}\n\n.toggle + label {\n\tposition: absolute;\n\twidth: 30px;\n\theight: 6px;\n\tmargin-top: 12px;\n\tbackground: $grey;\n\ttransition: background 0.3s $cubic;\n\twill-change: background;\n\t&:after {\n\t\tposition: absolute;\n\t\tcontent: '';\n\t\twidth: 14px;\n\t\theight: 14px;\n\t\tborder-radius: 50%;\n\t\tbackground: lighten($grey, 25%);\n\t\tdisplay: inline-block;\n\t\tcursor: pointer;\n\t\tmargin-top: -4px;\n\t\tmargin-left: -1px;\n\t\ttransition: all 0.3s ease;\n\t\twill-change: background, margin-left;\n\t\tbox-shadow: $box-shadow-card;\n\t}\n}\n\ntextarea {\n\tborder: solid 1px $grey;\n\ttransition: border 0.3s $cubic;\n\t&:focus {\n\t\tborder: solid 1px $primary;\n\t\toutline: none;\n\t}\n}",".github-corner svg {\n fill: rgba(222,122,39,0.5);\n color: #fff;\n position: fixed;\n top: 0;\n border: 0;\n right: 0;\n}\n\n.github-corner svg:hover {\n fill: rgba(255,118,5,0.5);\n}\n\n.github-corner:hover .octo-arm {\n animation: octocat-wave 560ms ease-in-out\n}\n\n@keyframes octocat-wave {\n 0%,\n 100% {\n transform: rotate(0)\n }\n 20%,\n 60% {\n transform: rotate(-25deg)\n }\n 40%,\n 80% {\n transform: rotate(10deg)\n }\n}\n\n@media (max-width:500px) {\n .github-corner:hover .octo-arm {\n animation: none\n }\n .github-corner .octo-arm {\n animation: octocat-wave 560ms ease-in-out\n }\n}\n\n\n.github-button, iframe {\n position: fixed;\n top: 20px;\n left: 20px;\n}"]}])},function(n,e,t){(n.exports=t(1)(!0)).push([n.i,'* {\n -moz-box-sizing: border-box;\n -webkit-box-sizing: border-box;\n box-sizing: border-box\n}\n\nhtml {\n background-color: #fff;\n padding: 0;\n margin: 0;\n width: 100%\n}\n\nbody {\n width: 100%;\n height: 100%;\n padding: 1em;\n margin: 0;\n font-family: "Alegreya", Palatino, Georgia, serif;\n font-size: 100%;\n color: #444\n}\n\na, a:visited {\n color: #444;\n transition: color 0.3s ease;\n}\n\na:hover {\n color: #000\n}\n\n.content {\n padding: 0.809em;\n max-width: 30em;\n margin: auto\n}\n\n.content>footer {\n margin-top: 4em;\n margin-bottom: 2em;\n border-top: 1px solid #eee;\n display: none\n}\n\narticle>h1:first-child {\n display: none\n}\n\narticle h1,\narticle h2,\narticle h3,\narticle h4,\narticle h5 {\n font-weight: normal;\n margin-top: 2em;\n color: black\n}\n\narticle h1,\narticle h2 {\n font-size: 1.5em\n}\n\narticle h3 {\n font-family: "Alegreya SC", Palatino, Georgia, serif;\n font-weight: normal;\n text-transform: lowercase\n}\n\narticle h4 {\n font-size: 1em;\n font-style: italic\n}\n\narticle h5 {\n font-style: normal\n}\n\narticle ul,\narticle ol {\n list-style-position: outside;\n padding: 0\n}\n\narticle blockquote {\n margin: 0;\n padding-left: 1em;\n border-left: 1px solid #aaa\n}\n\narticle img,\narticle object,\narticle video,\narticle audio,\narticle figure,\narticle iframe {\n max-width: 100%\n}\n\n@media all and (min-width: 33.236em) {\n .content {\n padding: 1.618em;\n margin: 0 auto;\n max-width: 45rem;\n font-size: 125%;\n }\n}\n\n@media all and (min-width: 33.236em) and screen and (min-width: 30em) and (max-width: 63.236em) {\n .content, .text-content {\n width: 30em\n }\n}\n',"",{version:3,sources:["screen.css"],names:[],mappings:"AAAA;IACI,2BAA2B;IAC3B,8BAA8B;IAC9B;AACJ;;AAEA;IACI,sBAAsB;IACtB,UAAU;IACV,SAAS;IACT;AACJ;;AAEA;IACI,WAAW;IACX,YAAY;IACZ,YAAY;IACZ,SAAS;IACT,iDAAiD;IACjD,eAAe;IACf;AACJ;;AAEA;IACI,WAAW;IACX,2BAA2B;AAC/B;;AAEA;IACI;AACJ;;AAEA;IACI,gBAAgB;IAChB,eAAe;IACf;AACJ;;AAEA;IACI,eAAe;IACf,kBAAkB;IAClB,0BAA0B;IAC1B;AACJ;;AAEA;IACI;AACJ;;AAEA;;;;;IAKI,mBAAmB;IACnB,eAAe;IACf;AACJ;;AAEA;;IAEI;AACJ;;AAEA;IACI,oDAAoD;IACpD,mBAAmB;IACnB;AACJ;;AAEA;IACI,cAAc;IACd;AACJ;;AAEA;IACI;AACJ;;AAEA;;IAEI,4BAA4B;IAC5B;AACJ;;AAEA;IACI,SAAS;IACT,iBAAiB;IACjB;AACJ;;AAEA;;;;;;IAMI;AACJ;;AAEA;IACI;QACI,gBAAgB;QAChB,cAAc;QACd,gBAAgB;QAChB,eAAe;IACnB;AACJ;;AAEA;IACI;QACI;IACJ;AACJ",file:"screen.css",sourcesContent:['* {\n -moz-box-sizing: border-box;\n -webkit-box-sizing: border-box;\n box-sizing: border-box\n}\n\nhtml {\n background-color: #fff;\n padding: 0;\n margin: 0;\n width: 100%\n}\n\nbody {\n width: 100%;\n height: 100%;\n padding: 1em;\n margin: 0;\n font-family: "Alegreya", Palatino, Georgia, serif;\n font-size: 100%;\n color: #444\n}\n\na, a:visited {\n color: #444;\n transition: color 0.3s ease;\n}\n\na:hover {\n color: #000\n}\n\n.content {\n padding: 0.809em;\n max-width: 30em;\n margin: auto\n}\n\n.content>footer {\n margin-top: 4em;\n margin-bottom: 2em;\n border-top: 1px solid #eee;\n display: none\n}\n\narticle>h1:first-child {\n display: none\n}\n\narticle h1,\narticle h2,\narticle h3,\narticle h4,\narticle h5 {\n font-weight: normal;\n margin-top: 2em;\n color: black\n}\n\narticle h1,\narticle h2 {\n font-size: 1.5em\n}\n\narticle h3 {\n font-family: "Alegreya SC", Palatino, Georgia, serif;\n font-weight: normal;\n text-transform: lowercase\n}\n\narticle h4 {\n font-size: 1em;\n font-style: italic\n}\n\narticle h5 {\n font-style: normal\n}\n\narticle ul,\narticle ol {\n list-style-position: outside;\n padding: 0\n}\n\narticle blockquote {\n margin: 0;\n padding-left: 1em;\n border-left: 1px solid #aaa\n}\n\narticle img,\narticle object,\narticle video,\narticle audio,\narticle figure,\narticle iframe {\n max-width: 100%\n}\n\n@media all and (min-width: 33.236em) {\n .content {\n padding: 1.618em;\n margin: 0 auto;\n max-width: 45rem;\n font-size: 125%;\n }\n}\n\n@media all and (min-width: 33.236em) and screen and (min-width: 30em) and (max-width: 63.236em) {\n .content, .text-content {\n width: 30em\n }\n}\n']}])},function(n,e,t){var r,i,o={},a=(r=function(){return window&&document&&document.all&&!window.atob},function(){return void 0===i&&(i=r.apply(this,arguments)),i}),s=function(n,e){return e?e.querySelector(n):document.querySelector(n)},c=function(n){var e={};return function(n,t){if("function"==typeof n)return n();if(void 0===e[n]){var r=s.call(this,n,t);if(window.HTMLIFrameElement&&r instanceof window.HTMLIFrameElement)try{r=r.contentDocument.head}catch(n){r=null}e[n]=r}return e[n]}}(),l=null,u=0,A=[],d=t(14);function p(n,e){for(var t=0;t=0&&A.splice(e,1)}function C(n){var e=document.createElement("style");if(void 0===n.attrs.type&&(n.attrs.type="text/css"),void 0===n.attrs.nonce){var r=function(){0;return t.nc}();r&&(n.attrs.nonce=r)}return g(e,n.attrs),h(n,e),e}function g(n,e){Object.keys(e).forEach((function(t){n.setAttribute(t,e[t])}))}function m(n,e){var t,r,i,o;if(e.transform&&n.css){if(!(o="function"==typeof e.transform?e.transform(n.css):e.transform.default(n.css)))return function(){};n.css=o}if(e.singleton){var a=u++;t=l||(l=C(e)),r=B.bind(null,t,a,!1),i=B.bind(null,t,a,!0)}else n.sourceMap&&"function"==typeof URL&&"function"==typeof URL.createObjectURL&&"function"==typeof URL.revokeObjectURL&&"function"==typeof Blob&&"function"==typeof btoa?(t=function(n){var e=document.createElement("link");return void 0===n.attrs.type&&(n.attrs.type="text/css"),n.attrs.rel="stylesheet",g(e,n.attrs),h(n,e),e}(e),r=v.bind(null,t,e),i=function(){b(t),t.href&&URL.revokeObjectURL(t.href)}):(t=C(e),r=w.bind(null,t),i=function(){b(t)});return r(n),function(e){if(e){if(e.css===n.css&&e.media===n.media&&e.sourceMap===n.sourceMap)return;r(n=e)}else i()}}n.exports=function(n,e){if("undefined"!=typeof DEBUG&&DEBUG&&"object"!=typeof document)throw new Error("The style-loader cannot be used in a non-browser environment");(e=e||{}).attrs="object"==typeof e.attrs?e.attrs:{},e.singleton||"boolean"==typeof e.singleton||(e.singleton=a()),e.insertInto||(e.insertInto="head"),e.insertAt||(e.insertAt="bottom");var t=f(n,e);return p(t,e),function(n){for(var r=[],i=0;i2&&void 0!==arguments[2]?arguments[2]:1;this._current[this.index(n,e)]=t}},{key:"setNext",value:function(n,e){var t=arguments.length>2&&void 0!==arguments[2]?arguments[2]:1;this._next[this.index(n,e)]=t}}])&&r(e.prototype,t),i&&r(e,i),n}(),o=t(2),a=t.n(o);function s(n,e){for(var t=0;t2&&void 0!==arguments[2]?arguments[2]:1;this.module._set(n,e,t)}}])&&s(e.prototype,t),r&&s(e,r),n}();function l(n,e,t){n.set(e-1,t),n.set(e,t+2),n.set(e+1,t-1),n.set(e+1,t),n.set(e+1,t+3),n.set(e+1,t+4),n.set(e+1,t+5)}function u(n,e,t){n.set(e-1,t),n.set(e,t-1),n.set(e,t),n.set(e,t+1),n.set(e+1,t)}function A(n,e,t){n.set(e-1,t-1,0),n.set(e-1,t,0),n.set(e-1,t+1,0),n.set(e,t-1,0),n.set(e,t,0),n.set(e,t+1,0),n.set(e+1,t-1,0),n.set(e+1,t,0),n.set(e+1,t+1,0)}function d(n,e){for(var t=0;t2&&void 0!==arguments[2]?arguments[2]:{};!function(n,e){if(!(n instanceof e))throw new TypeError("Cannot call a class as a function")}(this,n),this.canvas=e,this.context=e.getContext("2d"),this.engine=t,this.pixelsPerCell=r.pixelsPerCell||5,this.desiredFPS=r.desiredFPS||30,this.fpsNode=r.fpsNode||!1,this.strokeStyle=r.strokeStyle||"rgba(255,118,5,0.5)",this.fillStyle=r.fillStyle||"rgba(222,122,39,0.5)",this.play=!1,this.fpsTime=0,this.engineTime=0,this.fps=0,this.frameNumber=0,this.canvas.width=this.engine.width*this.pixelsPerCell,this.canvas.height=this.engine.height*this.pixelsPerCell}var e,t,r;return e=n,(t=[{key:"togglePlay",value:function(){this.play=!this.play}},{key:"draw",value:function(n){window.requestAnimationFrame(this.draw.bind(this)),this.context.clearRect(0,0,this.canvas.width,this.canvas.height),this.context.strokeStyle=this.strokeStyle,this.context.fillStyle=this.fillStyle;for(var e=this.pixelsPerCell>1,t=0;t1e3/this.desiredFPS&&this.play&&(this.engine.computeNextState(),this.frameNumber+=1,this.engineTime=n-a%(1e3/this.desiredFPS)),this.fpsNode){var s=n-this.fpsTime;s>500&&(this.fps=1e3/s*this.frameNumber,this.fpsNode.textContent="".concat(this.fps.toFixed(2)," FPS"),this.fpsTime=n,this.frameNumber=0)}}},{key:"start",value:function(){this.engine.computeNextState(),this.play=!0,window.requestAnimationFrame(this.draw.bind(this))}}])&&d(e.prototype,t),r&&d(e,r),n}();function f(n,e){for(var t=0;t0&&void 0!==arguments[0]?arguments[0]:[];e.forEach((function(e){n.listeners.push(e);var t=document;e.selector&&(t=document.querySelector(e.selector)),t&&t.addEventListener(e.eventType,e.callback)}))}},{key:"addCells",value:function(n){var e=arguments.length>1&&void 0!==arguments[1]&&arguments[1],t=this.canvas.getBoundingClientRect(),r={x:(n.clientX-t.left)/(t.right-t.left)*this.canvas.clientWidth,y:(n.clientY-t.top)/(t.bottom-t.top)*this.canvas.clientHeight},i={i:~~(r.y/this.renderer.pixelsPerCell),j:~~(r.x/this.renderer.pixelsPerCell)};(this.mouseDown||e)&&(n.ctrlKey?A(this.engine,i.i,i.j):u(this.engine,i.i,i.j))}},{key:"mouseIsDown",value:function(n){0===n.button&&(this.mouseDown=!0,this.addCells(n))}},{key:"mouseIsUp",value:function(n){this.mouseDown=!1}}])&&f(e.prototype,t),r&&f(e,r),n}(),b=t(3),C=t.n(b),g=(t(10),{canvasSelector:"#universe",fpsNodeSelector:"#fps-info",playButtonSelector:"#ctrl-play-pause",hideButtonSelector:"#ctrl-hide-show",switchEngineSelector:"#ctrl-engine",desiredFPS:30,pixelsPerCell:5,strokeStyle:"rgba(255,118,5,0.5)",fillStyle:"rgba(222,122,39,0.5)",showText:!0,useWasm:!0}),m=C.a.parse(window.location.search);(m.desiredFPS||m.pixelsperCell)&&(g.showText=!1);var y=Object.assign(g,m);y.desiredFPS=parseInt(y.desiredFPS,10),y.pixelsperCell=parseInt(y.pixelsperCell,10);window.onload=function(){var n,e=document.querySelector(y.canvasSelector),t=~~(e.clientWidth/y.pixelsPerCell),r=~~(e.clientHeight/y.pixelsPerCell),o=new c(t,r),a=new i(t,r);n=!0===y.useWasm?o:a,window.engine=n;var s=new p(e,n,{desiredFPS:y.desiredFPS,pixelsPerCell:y.pixelsPerCell,fpsNode:document.querySelector(y.fpsNodeSelector),strokeStyle:y.strokeStyle,fillStyle:y.fillStyle});!1===y.showText&&(document.querySelector(".text-content").classList.add("hidden"),document.querySelector(y.hideButtonSelector).textContent="Show text");var u=new h(e,n,s);u.addEvents([{selector:y.playButtonSelector,eventType:"click",callback:function(n){s.togglePlay(),n.target.textContent="Pause"===n.target.textContent?"Play":"Pause"}},{selector:y.hideButtonSelector,eventType:"click",callback:function(n){document.querySelector(".text-content").classList.toggle("hidden"),n.target.textContent="Hide text"===n.target.textContent?"Show text":"Hide text"}},{selector:y.switchEngineSelector,eventType:"click",callback:function(e){n=n instanceof c?a:o,s.engine=n,u.engine=n,e.target.textContent="Use js engine"===e.target.textContent?"Use wasm engine":"Use js engine"}}]);!function e(){!0!==n.module.calledRun?window.setTimeout(e.bind(void 0),100):(o.init(),a.init(),l(o,~~(r/2),~~(t/2)),l(o,0,0),l(a,~~(r/2),~~(t/2)),l(a,0,0),s.start())}()}}]); 2 | //# sourceMappingURL=demo-bundle.js.map?9001efd7 --------------------------------------------------------------------------------