├── .csslintrc ├── .editorconfig ├── .gitignore ├── .htmlhintrc ├── .jshintignore ├── .jshintrc ├── .travis.yml ├── LICENSE.txt ├── README.md ├── admin.html ├── ci ├── Gemfile ├── all.sh ├── install.sh └── requirements.txt ├── favicon.ico ├── game.html ├── images └── grey-brick-texture-seamless-glassy-dirty-modern-concrete-wall-squared-pattern-smooth-surface-256x182.jpg ├── index.html ├── scripts ├── api.js ├── asset-loader.js ├── contributors.js ├── database │ ├── auth.js │ ├── config.js │ ├── firebase-rules.json │ └── migrations.js ├── game-engine-2d.js ├── game-engine-3d.js ├── game-engine-example.js ├── game.js ├── main.js ├── pr-counter.js ├── pr.js ├── stats.js ├── utils.js └── vote.js ├── stylesheets ├── game.css ├── gitter-override.css └── styles.css └── vendor ├── gitter.css ├── progressbar.min.js └── sidecar.v1.js /.csslintrc: -------------------------------------------------------------------------------- 1 | --errors=empty-rules,duplicate-properties,regex-selectors 2 | --warnings=floats,font-sizes,important,known-properties,outline-none,shorthand,overqualified-elements 3 | -------------------------------------------------------------------------------- /.editorconfig: -------------------------------------------------------------------------------- 1 | # EditorConfig is awesome: http://EditorConfig.org 2 | 3 | root = true 4 | 5 | # One config to rule them all 6 | [*] 7 | 8 | # Always use UTF-8 9 | charset = utf-8 10 | 11 | # Unix style line breaks and end files with a newline 12 | end_of_line = lf 13 | insert_final_newline = true 14 | trim_trailing_whitespace = true 15 | 16 | # Tab indentation without specified width 17 | # This allows each dev to set the tab width in their IDE to whatever they like best 18 | indent_style = tab 19 | 20 | 21 | # Special markup for yml files 22 | [*.yml] 23 | indent_style = space 24 | indent_size = 2 25 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | _site/* 2 | *.DS_Store 3 | /.vs 4 | -------------------------------------------------------------------------------- /.htmlhintrc: -------------------------------------------------------------------------------- 1 | { 2 | "tagname-lowercase": true, 3 | "attr-value-double-quotes": true, 4 | "attr-no-duplication": true, 5 | "doctype-first": true, 6 | "doctype-html5": true, 7 | "title-require": true, 8 | "id-unique": true, 9 | "src-not-empty": true, 10 | "alt-require": true, 11 | "head-script-disabled": true, 12 | "inline-style-disabled": true, 13 | "inline-script-disabled": true 14 | } 15 | -------------------------------------------------------------------------------- /.jshintignore: -------------------------------------------------------------------------------- 1 | vendor/ 2 | -------------------------------------------------------------------------------- /.jshintrc: -------------------------------------------------------------------------------- 1 | { 2 | "esversion": 6, 3 | "bitwise": true, 4 | "curly": true, 5 | "eqeqeq": true, 6 | "freeze": true, 7 | "nocomma": true, 8 | "nonbsp": true, 9 | "notypeof": true, 10 | "shadow": true, 11 | "strict": "global", 12 | "undef": true, 13 | "unused": true, 14 | "browser": true 15 | } 16 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | dist: trusty 2 | sudo: false 3 | language: ruby 4 | python: 5 | - "3.6" 6 | install: ci/install.sh 7 | script: ci/all.sh 8 | gemfile: ci/Gemfile 9 | addons: 10 | apt: 11 | packages: 12 | # install Java 8 as required by vnu.jar 13 | - oracle-java8-installer 14 | - oracle-java8-set-default 15 | cache: 16 | bundler: true 17 | pip: true 18 | -------------------------------------------------------------------------------- /LICENSE.txt: -------------------------------------------------------------------------------- 1 | Copyright 2017 1pr Contributors 2 | 3 | Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: 4 | 5 | 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. 6 | 7 | 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. 8 | 9 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 10 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # 1pr 2 | 3 | I will be accepting up to one pull request per day on this project. 4 | 5 | View the result at [thepracticaldev.github.io/1pr](https://thepracticaldev.github.io/1pr/). 6 | 7 | [![Build Status](https://travis-ci.org/thepracticaldev/1pr.svg?branch=master)](https://travis-ci.org/thepracticaldev/1pr) [![Gitter](https://badges.gitter.im/dev-1pr/1pr.svg)](https://gitter.im/dev-1pr/1pr?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=body_badge) 8 | 9 | ## Project Journal 10 | 11 | - [As one does at three o'clock in the morning, I started a funky side project.](https://dev.to/ben/as-one-does-at-three-oclock-in-the-morning-i-started-a-funky-side-project) 12 | - [1pr Day 3: Added styles and a project contributor list](https://dev.to/ben/1pr-day-3-added-styles-and-a-project-contributor-list) 13 | - [Having trouble integrating gitter into 1pr](https://dev.to/ben/having-trouble-integrating-gitter-into-1pr) 14 | 15 | ## How to contribute 16 | 17 | - Fork the repository 18 | - Add something awesome 19 | - Create a pull request 20 | - Hope you get picked 21 | 22 | ## Project structure 23 | 24 | The project's structure is pretty simple. 25 | 26 | - HTML files go in the root directory 27 | - CSS goes in the `stylesheets/` directory 28 | - Javascript goes in the `scripts/` directory 29 | - Third-party libraries (CSS and JS) go in the `vendor/` directory, which is ignored by the linters (see **Testing** below). 30 | 31 | ## Coding standards 32 | 33 | When contributing, please try to follow the coding standards so we have nice looking code that's easy to follow for everyone. 34 | 35 | ### Editorconfig 36 | 37 | Where possible, use an editor (or a plugin for your editor) that supports [editorconfig](http://editorconfig.org/). 38 | 39 | The editorconfig file should set your editor to the following settings automatically: 40 | 41 | - UTF-8 charset 42 | - Unix-style line breaks 43 | - End file with a new line 44 | - No trailing whitespace before a line break 45 | - Use tabs for indentation, apart from in Markdown files where spaces are preferred 46 | 47 | Tab width is not defined in the editorconfig, so each deveveloper can set their editor's tab width to what they're most comfortable with. 48 | 49 | ### Furthermore 50 | 51 | - Add comments to your code where necessary. The project should be accessible for devs of all experience and skill levels. Better to have too many comments, than none at all. 52 | - Whitespace is not the enemy! A couple of empty lines between blocks of code can really improve readability. 53 | 54 | ### For Javascript specifically 55 | 56 | - Use semicolons (even through they're not strictly necessary). It's good practice! 57 | - Use `let` and `const` where applicable, to keep the scope of your variables specific. Don't know what scope is or what `let` does? Check out [this article](https://medium.com/@MentallyFriendly/es6-an-idiots-guide-to-let-and-const-70be9691c389). 58 | - Use `lowerCamelCase` for variable names (not `snake_case`) 59 | 60 | ## Testing 61 | 62 | The project contains the files `.htmlhintrc`, `.csslintrc` and `.jshintrc` with configuration for the respective testing utilities. 63 | 64 | To install the testing utilities locally, simply install [Node.js](https://nodejs.org/en/) and then use npm (bundled with Node.js) to install the utilities: 65 | 66 | ``` 67 | 68 | npm install --global htmlhint csslint jshint 69 | 70 | ``` 71 | 72 | ### HTML validation 73 | 74 | Run the HTML validator with: 75 | 76 | ``` 77 | 78 | htmlhint 79 | 80 | ``` 81 | 82 | - All tags should be lowercase 83 | - Use double quotes for attributes 84 | - No duplicate attributes 85 | - HTML5 doctype on the first line 86 | - Must have a title tag 87 | - IDs must be unique 88 | - Src and alt attribute required on images 89 | - No scripts in the head (place them at the bottom of the body) 90 | - No inline style attributes or javascript event handlers (e.g. `onclick=""`) 91 | 92 | ### CSS validation 93 | 94 | Run the CSS validator with: 95 | 96 | ``` 97 | 98 | csslint stylesheets 99 | 100 | ``` 101 | 102 | - No empty rules 103 | - No duplicate properties with the same value 104 | - Limit the amount of floats used 105 | - Limit the amount of different font sizes used 106 | - Don't use `!important` 107 | - Don't use `outline: none` unless you have a `:focus` rule on the same element to replace the outline 108 | - Don't use elements in the css when only a class name will suffice 109 | - Don't use regex selectors 110 | - Encourage the use of shorthand notation 111 | 112 | ### JS validation 113 | 114 | Run the Javascript validator with: 115 | 116 | ``` 117 | 118 | jshint scripts 119 | 120 | ``` 121 | 122 | - Always use strict mode 123 | - Avoid using bitwise operators 124 | - Always use curly brackets, even for a single line 125 | - Compare values with `===` and `!==` for type safety 126 | - Don't extend prototypes of native objects (e.g. `Array` or `Date`) 127 | - Don't use the comma operator 128 | - Avoid declaring variables that are already declared in a higher scope 129 | - Avoid declaring variables and not using them 130 | 131 | ## Storing Data 132 | 133 | 1pr is backed by a Firebase database. This allows you to save and share data armed with just the knowledge of JSON rather than having to understand structuring a database or sql etc.. 134 | 135 | To set up a test database and add yourself as administrator: 136 | 137 | 1. create a Firebase account and project 138 | 1. replace the config details in `scripts\database\config.js` with the config details shown in the Firebase console 139 | 1. copy the contents of `scripts\database\firebase-rules.json` into the database rules section of the console 140 | 141 | (Re-do this every time you pull new changes into your fork that change the rules file) 142 | 143 | 1. under the authentication section of the console, enable GitHub authentication (follow the instructions there) 144 | 1. (optionally add your github.io subdomain or other domains where you can access your site as an authorised OAuth redirect domain) 145 | 1. serve your files 146 | 1. navigate to your 1pr homepage 147 | 1. click the sign in button in the sidebar to sign in with GitHub and generate a database account for yourself 148 | 1. go to the users panel of the authentication section of the Firebase console and copy the `uid` for your newly-generated account 149 | 1. go to the data panel of the database section and: 150 | 1. add a node named `admins` as a child of the root node 151 | 1. click the plus to add a sub-node 152 | 1. copy your `uid` into the name of that sub-node and add a value of true 153 | 1. click add to save the changes 154 | 1. go back to your 1pr page and refresh. 155 | 1. click the newly-visible Manage Web App link 156 | 1. Click run migrations to apply all database changes to your Firebase database 157 | 158 | (Re-run this every time you pull new changes into your fork that change the database) 159 | 160 | To develop with the database: 161 | 162 | - Use the documentation to find out how to save and load data 163 | - By default the rules only allow administrators to edit the database, so make sure you've given yourself that role 164 | 165 | This is important - Firebase allows connections from localhost so (given the connection details are public) anyone could serve their own script that reads and writes to the database maliciously 166 | 167 | - Make a new top-level node for each feature (unless it particularly makes sense not to) 168 | - Remember to update the rules to allow non-admins to use your feature, though be restrictive rather than permissive 169 | - Remember to test those rules using the simulator build into the rules interface 170 | - Record the steps you take modifying the structure and data within the database in a migration: 171 | 1. Open up `scripts\database\migrations.js` 172 | 1. Find the end of the `migrations` array 173 | 1. Add a new object, following the pattern of the existing migrations, i.e. 174 | 175 | ``` 176 | 177 | { 178 | //unique identifier for the migration 179 | name: "doNothing", 180 | 181 | //talk about what your changes do 182 | description: "Does nothing of substance. Does add to the migration history.", 183 | 184 | //function that returns a promise 185 | // (or other thenable object) that 186 | // enacts the data manipulation to 187 | // achieve what you want to do 188 | doMigration: function(){return Promise.Resolve();} 189 | } 190 | 191 | ``` 192 | 193 | 1. Test your migration by using the button on the admin page 194 | 195 | There's no mechanism to roll back migrations yet, so testing multiple times requires deleting all but the `admins` node from the database and rerunning all migrations again 196 | 197 | - Add the class `signed-out` to any elements you want to be visible when the user isn't signed in 198 | - Add the classes `signed-in` and `hidden` to the elements you want to show when the user is signed in 199 | - Add the classes `signed-in-admin` and `hidden` on top of the regular `signed-in hidden` to the elements you want to show when the user is signed in as an admin 200 | -------------------------------------------------------------------------------- /admin.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | One PR A Day 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 32 | 33 | 34 |
35 |
36 |
37 | 40 | 41 | 50 | 51 | 52 | 53 | 54 | 55 | 56 |
57 |
58 | 59 |
60 |
61 |
62 |

Trigger database migrations here

63 |
64 |
65 | 66 |
67 |
68 | 69 |

Migrations to be applied:

70 |
Loading
71 |

Applied Migrations:

72 |
Loading
73 |
74 |
75 |
76 | 77 | 96 | 97 | 98 | 99 | 100 | 101 | -------------------------------------------------------------------------------- /ci/Gemfile: -------------------------------------------------------------------------------- 1 | source "https://rubygems.org" 2 | source "http://gems.github.com" 3 | 4 | gem 'mdl' 5 | -------------------------------------------------------------------------------- /ci/all.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | set -ex 3 | cd "$(git rev-parse --show-toplevel)" 4 | mdl -g -r '~MD013' . 5 | html5validator --root . 6 | htmlhint . 7 | jshint --config=.jshintrc . 8 | csslint --exclude-list=vendor . 9 | -------------------------------------------------------------------------------- /ci/install.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | set -ex 3 | cd "$(git rev-parse --show-toplevel)" || exit 1 4 | bundle install --retry 3 --gemfile="$BUNDLE_GEMFILE" 5 | pip install -r ci/requirements.txt 6 | npm install -g jshint htmlhint csslint 7 | -------------------------------------------------------------------------------- /ci/requirements.txt: -------------------------------------------------------------------------------- 1 | html5validator 2 | -------------------------------------------------------------------------------- /favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/thepracticaldev/1pr/b7009e80bc906a0e2eba0d10e754067a6e92818f/favicon.ico -------------------------------------------------------------------------------- /game.html: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | 6 | Game - One PR A Day 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 |
16 |
17 | 18 |
19 | 20 | Player Score: 0 21 |
22 | 23 |
24 |
25 |
26 | 27 | 35 | 36 | 45 | 46 | 47 | 48 | 49 | 50 | -------------------------------------------------------------------------------- /images/grey-brick-texture-seamless-glassy-dirty-modern-concrete-wall-squared-pattern-smooth-surface-256x182.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/thepracticaldev/1pr/b7009e80bc906a0e2eba0d10e754067a6e92818f/images/grey-brick-texture-seamless-glassy-dirty-modern-concrete-wall-squared-pattern-smooth-surface-256x182.jpg -------------------------------------------------------------------------------- /index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | One PR A Day 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 32 | 33 | 34 |
35 |
36 |
37 | 40 | 41 | 50 | 51 | 52 | 53 | 54 | 55 | 56 |
57 |
58 | 59 |
60 |
61 |
62 |
?
63 |
open pull requests
64 |
65 |
66 | 67 |
68 |
69 |

70 |
71 |
72 | 73 |
74 |
75 |

I will be accepting up to one pull request per day on this project

76 | 77 |
78 | 79 |

Interested in contributing, but not sure where to start?

80 | 81 |
82 | 83 | 84 | 88 |
89 | 90 |
91 | 92 | 93 | 97 |
98 | 99 |
100 | 101 | 102 | 105 |
106 | 107 |
108 |
109 |
110 | 111 |
112 |
113 | 114 | 115 |
116 |
117 | 118 | 119 |
120 |
121 | 122 | 123 |
124 | 125 |

Contributors

126 |
127 |
128 | 129 |
130 |
131 |

Check out what pull requests are up for selection

132 |
133 |
134 |
135 | 136 |
137 |

The 1PR Game

138 | Play Now 139 |
140 |
141 | 142 | 143 | 167 | 168 | 169 | 170 | 171 | 172 | -------------------------------------------------------------------------------- /scripts/api.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | /* Shared API "get" function 4 | window.api(requestUrl, callback) 5 | requestUrl: The URL that you want to request 6 | callback: A function that takes two parameters: (err, responseData) 7 | `err` will be a JS error, or a number in case of a non-200 response code 8 | If err is null, `responseData` will contain the parsed JSON response 9 | */ 10 | (function defineAPI() { 11 | 12 | // This function uses "node.js style" error-first callback 13 | window.api = function(requestUrl, callback) { 14 | 15 | 16 | // Create HTTP request 17 | let request = new XMLHttpRequest(); 18 | 19 | // Attach a handler for when the request is completed 20 | request.onreadystatechange = function onStateChange() { 21 | 22 | // Initialise variable 23 | let responseData = {}; 24 | 25 | /* Instead of checking if the request is ready and putting everything below inside the if 26 | I simply return out of the handler whenever the request isn't ready yet. 27 | This makes for cleaner code and less nesting imo. */ 28 | if(request.readyState !== 4){ 29 | return; 30 | 31 | }else if(request.status !== 200){ 32 | callback(request.status); 33 | return; 34 | 35 | } 36 | 37 | // Attempt to parse the JSON response data (in a try/catch in case the JSON is malformed) 38 | try { 39 | responseData = JSON.parse(request.responseText); 40 | 41 | } catch (err) { 42 | callback(err); 43 | return; 44 | 45 | } 46 | 47 | // If we've made it this far, the request was successful and we can just pass the response data to the callback 48 | callback(null, responseData); 49 | 50 | }; 51 | 52 | // Send the request 53 | request.open('GET', requestUrl, true); 54 | request.send(null); 55 | 56 | // If you hit the API rate limit, add the following before sending the request (add in your own username and api token) 57 | // request.setRequestHeader('Authorization', 'Basic ' + btoa('username:personal api token')); 58 | }; 59 | 60 | })(); 61 | -------------------------------------------------------------------------------- /scripts/asset-loader.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | (function() { 4 | 5 | // CONFIG - How long the loading animation should run for 6 | let animationDelay = 320; 7 | 8 | // This function will load the assets for the page 9 | // Scripts are defined in window.loadScripts 10 | let loadAssets = function(loadComplete) { 11 | 12 | 13 | // Create a circular progress bar 14 | let loading = new window.ProgressBar.Circle( 15 | document.getElementById('loader'), 16 | { 17 | strokeWidth: 2, 18 | easing: 'easeInOut', 19 | duration: animationDelay, 20 | color: 'rgba(255, 255, 255, 0.6)', 21 | trailColor: 'rgba(255, 255, 255, 0.25)', 22 | trailWidth: 1, 23 | svgStyle: null 24 | } 25 | ); 26 | 27 | 28 | 29 | // Allow asynchronous scripts (i.e. scripts making API calls) to register on the progress bar as well 30 | let asyncScripts = []; 31 | let registerAsyncScript = function(scriptName){ 32 | asyncScripts.push(scriptName); 33 | let done = function() { 34 | asyncScripts.splice(asyncScripts.indexOf(scriptName), 1); 35 | checkIfLoaded(); 36 | }; 37 | return done; 38 | }; 39 | window.registerAsyncScript = registerAsyncScript; 40 | 41 | 42 | // Check if everything has been loaded 43 | let checkIfLoaded = function() { 44 | 45 | // Stop the currently running animation (if any) and animate towards the new progress value 46 | loading.stop(); 47 | loading.animate(loadIndex / (window.assets.length + asyncScripts.length)); 48 | 49 | // Check if we're done loading 50 | if(loadIndex >= window.assets.length && asyncScripts.length === 0){ 51 | loadComplete(); 52 | 53 | // Otherwise just return false 54 | }else{ 55 | return false; 56 | } 57 | }; 58 | 59 | 60 | 61 | // Asynchronously loads a script and calls callback when the script is loaded 62 | let loadScript = function(src, callback) { 63 | 64 | // Create a new script element 65 | let script = document.createElement('script'); 66 | 67 | // Set the src attribute 68 | script.src = src; 69 | 70 | // Attach an 'onload' handler 71 | script.onload = callback; 72 | 73 | // Append the script to the body so it actually starts loading 74 | document.body.appendChild(script); 75 | }; 76 | 77 | // Asynchronously loads a stylesheet and calls callback when the script is loaded 78 | let loadStylesheet = function(src, callback) { 79 | 80 | // Create a new link element 81 | let stylesheet = document.createElement('link'); 82 | 83 | // Set the attributes 84 | stylesheet.rel = 'stylesheet'; 85 | stylesheet.href = src; 86 | 87 | // Attach an 'onload' handler 88 | stylesheet.onload = callback; 89 | 90 | // Append the stylesheet to the body so it actually starts loading 91 | document.body.appendChild(stylesheet); 92 | }; 93 | 94 | 95 | 96 | // Keep track of where we are in the loading process 97 | let loadIndex = 0; 98 | 99 | // This function will load all scripts from the scrips array asynchronously one after the other and, once they're loaded, call the provided callback function. 100 | let load = function() { 101 | 102 | // Get asset 103 | let asset = window.assets[loadIndex]; 104 | 105 | // This function will be called when the asset is loaded 106 | let done = function() { 107 | 108 | // Increase the loading index 109 | loadIndex++; 110 | 111 | // If we've loaded all the scripts, call the callback function 112 | if(loadIndex >= window.assets.length){ 113 | checkIfLoaded(); 114 | return; 115 | } 116 | 117 | // Otherwise, continue loading (and pass the callback through so it can be called from the last iteration) 118 | load(); 119 | }; 120 | 121 | // Figure out if it's a script or a stylesheet 122 | if(asset.substr(-3) === '.js'){ 123 | loadScript(asset, done); 124 | 125 | }else if(asset.substr(-4) === '.css'){ 126 | loadStylesheet(asset, done); 127 | 128 | }else if(asset.substr(0, 3) === 'js:'){ 129 | loadScript(asset.substr(3), done); 130 | 131 | }else if(asset.substr(0, 4) === 'css:'){ 132 | loadStylesheet(asset.substr(4), done); 133 | 134 | }else{ 135 | if(window.console){ window.console.error('Unknown asset type:', asset); } 136 | done(); 137 | 138 | } 139 | 140 | }; 141 | 142 | 143 | // Start loading 144 | load(); 145 | 146 | }; 147 | 148 | // Start by loading the progress bar lib, which we need to show a pretty loading bar while we're loading the other stuff 149 | let progressBarScript = document.createElement('script'); 150 | progressBarScript.src = 'vendor/progressbar.min.js'; 151 | progressBarScript.onload = loadAssets.bind(null, function() { 152 | setTimeout(function() { 153 | document.body.className = 'loaded'; 154 | }, animationDelay); 155 | }); 156 | document.body.appendChild(progressBarScript); 157 | 158 | })(); 159 | -------------------------------------------------------------------------------- /scripts/contributors.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | (function loadContributors() { 4 | 5 | window.api('https://api.github.com/repos/thepracticaldev/1pr/contributors', function(err, contributors){ 6 | 7 | // Log and display any errors 8 | if(err !== null){ 9 | if(window.console){ window.console.error(err); } 10 | document.getElementById('contributors').className = 'error'; 11 | document.getElementById('contributors').textContent = 'Could not load contributors'; 12 | return; 13 | } 14 | 15 | 16 | // Append each contributor to the contributors element 17 | for(let i = 0; i < contributors.length; i++){ 18 | 19 | // Link to contributor's profile 20 | let contributor = document.createElement('a'); 21 | contributor.href = contributors[i].html_url; // URL of profile page 22 | contributor.title = contributors[i].login; // Username 23 | contributor.target = '_blank'; 24 | 25 | // Contributor's avatar 26 | let avatar = document.createElement('img'); 27 | avatar.src = contributors[i].avatar_url; 28 | 29 | // Append to contributors element 30 | contributor.appendChild(avatar); 31 | document.getElementById('contributors').appendChild(contributor); 32 | 33 | } 34 | 35 | }); 36 | 37 | })(); 38 | -------------------------------------------------------------------------------- /scripts/database/auth.js: -------------------------------------------------------------------------------- 1 | /* globals firebase, utils */ 2 | "use strict"; 3 | 4 | // Wrap everything inside a scoped function 5 | (function firebaseAuth() { 6 | 7 | var signInButton = document.getElementById("button-sign-in"); 8 | var signOutButton = document.getElementById("button-sign-out"); 9 | var userSpan = document.getElementById("firebase-user"); 10 | 11 | var signedInControls = document.querySelectorAll(".signed-in"); 12 | var signedOutControls = document.querySelectorAll(".signed-out"); 13 | var signedInAdminControls = document.querySelectorAll(".signed-in-admin"); 14 | 15 | //Set up response to log in and log out 16 | firebase.auth().onAuthStateChanged(function(user) { 17 | if (user) { 18 | // User is signed in. 19 | firebase.database().ref("admins/" + user.uid).once('value').then(function(adminSnapshot){ 20 | var admins = adminSnapshot.val(); 21 | if (admins !== null){ 22 | utils.showManyElements(signedInAdminControls); 23 | } 24 | }); 25 | var displayName = user.displayName; 26 | userSpan.innerHTML = "Logged in as " + displayName; 27 | utils.showManyElements(signedInControls); 28 | utils.hideManyElements(signedOutControls); 29 | } else { 30 | userSpan.innerHTML = ""; 31 | utils.showManyElements(signedOutControls); 32 | utils.hideManyElements(signedInControls); 33 | utils.hideManyElements(signedInAdminControls); 34 | } 35 | }); 36 | 37 | signInButton.onclick = function(){ 38 | var provider = new firebase.auth.GithubAuthProvider(); 39 | firebase.auth().signInWithRedirect(provider); 40 | }; 41 | 42 | signOutButton.onclick = function(){ 43 | firebase.auth().signOut(); 44 | }; 45 | 46 | })(); 47 | -------------------------------------------------------------------------------- /scripts/database/config.js: -------------------------------------------------------------------------------- 1 | /* globals firebase */ 2 | "use strict"; 3 | 4 | //Please overide this with the config for your test firebase project 5 | var config = { 6 | apiKey: "", 7 | authDomain: "", 8 | databaseURL: "", 9 | storageBucket: "", 10 | messagingSenderId: "" 11 | }; 12 | firebase.initializeApp(config); 13 | -------------------------------------------------------------------------------- /scripts/database/firebase-rules.json: -------------------------------------------------------------------------------- 1 | { 2 | "rules": { 3 | //Default to administrator-access only 4 | ".read": "auth != null && root.child('admins').hasChild(auth.uid)", 5 | ".write": "auth != null && root.child('admins').hasChild(auth.uid)", 6 | //Allow access to individual db sections as required 7 | //admins records uids of administrator users 8 | "admins":{ 9 | //Need to be able to read the admin data when logged in to check if 10 | //the admin interface should be displayed or not 11 | ".read": "auth != null" 12 | }, 13 | //migrationHistory records structual changes to the database 14 | "migrationHistory":{ 15 | //admin-only feature so can inherit the above access condiions 16 | "$migrationId":{ 17 | ".validate":"newData.hasChildren(['name','description','timestamp'])", 18 | "name":{ 19 | ".validate":"newData.isString()" 20 | }, 21 | "description":{ 22 | ".validate":"newData.isString()" 23 | } 24 | } 25 | } 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /scripts/database/migrations.js: -------------------------------------------------------------------------------- 1 | /* globals firebase, console */ 2 | "use strict"; 3 | 4 | (function prepareMigrations(){ 5 | 6 | var migrationButton = document.getElementById("button-migrate"); 7 | var appliedMigrationsList = document.getElementById("ul-migrations-applied"); 8 | var availableMigrationsList = document.getElementById("ul-migrations-available"); 9 | 10 | var self = {}; 11 | self.appliedMigrations = []; 12 | self.availableMigrations = []; 13 | const migrationHistoryRefName = "migrationHistory"; 14 | var migrationHistoryRef = firebase.database().ref(migrationHistoryRefName); 15 | 16 | var migrations = [ 17 | //Define migrations here, in the order they should be applied. 18 | //migrations are objects with the following properties: 19 | // name - a short unique identifying string 20 | // description - go into more detail about what it does for reference 21 | // doMigration - a function that adds, updates or deletes nodes from the database 22 | // (assuming that the database has had all previous migrations applied 23 | // and has been used by regular users since). 24 | // Takes no input 25 | // Returns a promise to act upon after the migration 26 | //Don't forget to also adjust the firebaserules.json as required. 27 | { 28 | name: "Initial migration", 29 | description: "Added migration system. Requires 'admins' node defined in database populated with an array of user ids", 30 | doMigration: function(){ 31 | //Don't need to do anything explicitly. 32 | // migrationHistoryRef gets created when the first history item (this one) gets pushed 33 | //Still need to return a thenable object 34 | return Promise.resolve(); 35 | } 36 | } 37 | ]; 38 | 39 | var applyMigration = function(migration){ 40 | console.log("Applying migration", migration.name); 41 | return migration.doMigration().then(function(){ 42 | console.log("Applied migration", migration.name); 43 | //Strip out function definition when pushing to database but add timestamp for reference 44 | migrationHistoryRef.push().set({ 45 | name: migration.name, 46 | description: migration.description, 47 | timestamp: new Date().toUTCString() 48 | }); 49 | }); 50 | }; 51 | 52 | var populateMigrationList = function(containerElement, migrations){ 53 | var migration = {}; 54 | var row = null; 55 | var nameSpan = null; 56 | var descriptionSpan = null; 57 | var timestampSpan = null; 58 | containerElement.innerHTML = ""; 59 | 60 | if (migrations.length === 0){ 61 | row = document.createElement("div"); 62 | descriptionSpan = document.createElement("span"); 63 | descriptionSpan.appendChild(document.createTextNode("No Migrations")); 64 | row.appendChild(descriptionSpan); 65 | containerElement.appendChild(row); 66 | } 67 | else{ 68 | for (var i = 0; i < migrations.length ; i++){ 69 | migration = migrations[i]; 70 | row = document.createElement("div"); 71 | nameSpan = document.createElement("span"); 72 | nameSpan.appendChild(document.createTextNode(migration.name)); 73 | row.appendChild(nameSpan); 74 | if(migration.timestamp !== undefined){ 75 | timestampSpan = document.createElement("span"); 76 | timestampSpan.appendChild(document.createTextNode(migration.timestamp)); 77 | row.appendChild(timestampSpan); 78 | } 79 | descriptionSpan = document.createElement("span"); 80 | descriptionSpan.appendChild(document.createTextNode(migration.description)); 81 | row.appendChild(descriptionSpan); 82 | containerElement.appendChild(row); 83 | } 84 | } 85 | }; 86 | 87 | migrationHistoryRef.on("value", function(migrationHistorySnapshot){ 88 | var migration = {}; 89 | var migrationHistoryItem = {}; 90 | var found = false; 91 | //Handle no migration history node 92 | var migrationHistory = migrationHistorySnapshot.val(); 93 | if (migrationHistory === null){ 94 | migrationHistory = {}; 95 | } 96 | 97 | //Work out which of the above migrations are applied or waiting to be applied 98 | self.appliedMigrations = []; 99 | self.availableMigrations = []; 100 | 101 | for (var i = 0; i < migrations.length ; i++){ 102 | migration = migrations[i]; 103 | found = false; 104 | for(var k in migrationHistory) 105 | { 106 | migrationHistoryItem = migrationHistory[k]; 107 | if (migrationHistoryItem.name === migration.name) 108 | { 109 | self.appliedMigrations.push(migrationHistoryItem); 110 | found = true; 111 | break; 112 | } 113 | } 114 | if (!found){ 115 | self.availableMigrations.push(migration); 116 | } 117 | } 118 | 119 | if (self.availableMigrations.length > 0) 120 | { 121 | migrationButton.disabled = false; 122 | migrationButton.textContent = "Apply migrations"; 123 | } 124 | else 125 | { 126 | migrationButton.disabled = true; 127 | migrationButton.textContent = "No migrations to apply"; 128 | } 129 | 130 | populateMigrationList(appliedMigrationsList, self.appliedMigrations); 131 | populateMigrationList(availableMigrationsList, self.availableMigrations); 132 | 133 | //Refresh the apply migration click handler so it applys only the required migrations 134 | migrationButton.onclick = function(){ 135 | //Empty promise to begin promise chain 136 | var promise = Promise.resolve(); 137 | for (var i = 0; i < self.availableMigrations.length ; i++){ 138 | migration = self.availableMigrations[i]; 139 | promise = promise.then(applyMigration(migration)); 140 | } 141 | //promise.error((error) => console.log(error)); 142 | }; 143 | }, function(error){ 144 | console.log(error); 145 | }); 146 | })(); 147 | -------------------------------------------------------------------------------- /scripts/game-engine-2d.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | if (typeof OnePRGame === 'undefined') { 4 | var OnePRGame = {}; 5 | } 6 | 7 | OnePRGame.Engine2D = function (context, gameBoardClassName) { 8 | 9 | this.GameBoardClassName = gameBoardClassName; 10 | this.Context = context; 11 | this.ScreenWidth = 0; 12 | this.ScreenHeight = 0; 13 | this.HalfWidth = 0; 14 | this.HalfHeight = 0; 15 | this.MinTop = 0; 16 | this.MaxTop = 0; 17 | this.MinLeft = 0; 18 | this.MaxLeft = 0; 19 | let that = this; 20 | 21 | this.Run = function () { 22 | 23 | let gameBoardWindow = document.getElementsByClassName('game-board-window')[0]; 24 | that.ScreenWidth = gameBoardWindow.clientWidth; 25 | that.ScreenHeight = gameBoardWindow.clientHeight; 26 | that.HalfWidth = that.ScreenWidth / 2; 27 | that.HalfHeight = that.ScreenHeight / 2; 28 | that.MinTop = that.ScreenHeight - (that.Context.Map.length * 100); 29 | that.MaxTop = 0; 30 | that.MinLeft = that.ScreenWidth - (that.Context.Map[0].length * 100); 31 | that.MaxLeft = 0; 32 | 33 | let gameBoard = document.getElementsByClassName(that.GameBoardClassName)[0]; 34 | // Remove existing maps 35 | let existingGameBoardMaps = gameBoard.getElementsByClassName('game-board-2d-map'); 36 | for (let i = 0; i < existingGameBoardMaps.length; i++) { 37 | gameBoard.removeChild(existingGameBoardMaps[i]); 38 | } 39 | 40 | // Remove existing controls 41 | let existingGameBoardControls = gameBoard.getElementsByClassName('game-board-2d-controls'); 42 | if (existingGameBoardControls.length > 0) { 43 | gameBoard.removeChild(existingGameBoardControls[0]); 44 | } 45 | 46 | // Draw Map 47 | let map = document.createElement('div'); 48 | map.className = 'game-board-2d-map'; 49 | map.style.width = (that.Context.Map[0].length * 100) + 'px'; 50 | 51 | for (let i = 0; i < that.Context.Map.length; i++) { 52 | for (let j = 0; j < that.Context.Map[i].length; j++) { 53 | let tile = document.createElement('div'); 54 | tile.className = 'game-board-2d-tile'; 55 | tile.style.backgroundColor = that.Context.Tileset[that.Context.Map[i][j]].Color; 56 | tile.textContent = that.Context.Tileset[that.Context.Map[i][j]].Text; 57 | map.appendChild(tile); 58 | } 59 | 60 | let rowBreak = document.createElement('div'); 61 | rowBreak.className = 'game-board-2d-row-break'; 62 | map.appendChild(rowBreak); 63 | } 64 | 65 | // Draw Player 66 | let player = document.createElement('div'); 67 | player.className = 'game-board-2d-player'; 68 | player.textContent = 'DEV'; 69 | player.style.top = (100 * that.Context.Player.Position[0]) + 'px'; 70 | player.style.left = (100 * that.Context.Player.Position[1]) + 'px'; 71 | 72 | map.appendChild(player); 73 | 74 | // Append new map 75 | gameBoard.appendChild(map); 76 | 77 | // Translate Map 78 | that.TranslateMap(); 79 | 80 | // Setup controls elements 81 | let controls2d = document.createElement('div'); 82 | controls2d.className = 'game-board-2d-controls'; 83 | controls2d.style.position = 'absolute'; 84 | controls2d.style.bottom = '0px'; 85 | controls2d.style.left = '0px'; 86 | 87 | let upButton = document.createElement('button'); 88 | upButton.type = 'button'; 89 | upButton.className = 'game-board-2d-controls-up'; 90 | upButton.textContent = 'Up'; 91 | controls2d.appendChild(upButton); 92 | 93 | let lineBreak1 = document.createElement('br'); 94 | controls2d.appendChild(lineBreak1); 95 | 96 | let leftButton = document.createElement('button'); 97 | leftButton.type = 'button'; 98 | leftButton.className = 'game-board-2d-controls-left'; 99 | leftButton.textContent = 'Left'; 100 | controls2d.appendChild(leftButton); 101 | 102 | let rightButton = document.createElement('button'); 103 | rightButton.type = 'button'; 104 | rightButton.className = 'game-board-2d-controls-right'; 105 | rightButton.textContent = 'Right'; 106 | controls2d.appendChild(rightButton); 107 | 108 | let lineBreak2 = document.createElement('br'); 109 | controls2d.appendChild(lineBreak2); 110 | 111 | let downButton = document.createElement('button'); 112 | downButton.type = 'button'; 113 | downButton.className = 'game-board-2d-controls-down'; 114 | downButton.textContent = 'Down'; 115 | controls2d.appendChild(downButton); 116 | 117 | gameBoard.appendChild(controls2d); 118 | 119 | // Controls event listeners 120 | gameBoard.getElementsByClassName('game-board-2d-controls-up')[0].addEventListener('click', that.Up, false); 121 | 122 | gameBoard.getElementsByClassName('game-board-2d-controls-down')[0].addEventListener('click', that.Down, false); 123 | 124 | gameBoard.getElementsByClassName('game-board-2d-controls-left')[0].addEventListener('click', that.Left, false); 125 | 126 | gameBoard.getElementsByClassName('game-board-2d-controls-right')[0].addEventListener('click', that.Right, false); 127 | 128 | }; 129 | 130 | this.Stop = function (callback) { 131 | 132 | let gameBoard = document.getElementsByClassName(that.GameBoardClassName)[0]; 133 | 134 | gameBoard.getElementsByClassName('game-board-2d-controls-up')[0].removeEventListener('click', that.Up, false); 135 | 136 | gameBoard.getElementsByClassName('game-board-2d-controls-down')[0].addEventListener('click', that.Down, false); 137 | 138 | gameBoard.getElementsByClassName('game-board-2d-controls-left')[0].addEventListener('click', that.Left, false); 139 | 140 | gameBoard.getElementsByClassName('game-board-2d-controls-right')[0].addEventListener('click', that.Right, false); 141 | 142 | if (typeof callback !== 'undefined') { 143 | callback(); 144 | } 145 | }; 146 | 147 | this.Up = function () { 148 | // Face upwards regardless of the ability to move there. 149 | that.Context.Player.Direction = Math.PI * 3 / 2; 150 | 151 | // Where am I going? 152 | let targetTile = OnePRGame.Tileset[OnePRGame.Map[that.Context.Player.Position[0] - 1][that.Context.Player.Position[1]]]; 153 | 154 | // Can I get there? 155 | if (targetTile !== null && !targetTile.Impassable) { 156 | that.Context.Player.Position[0]--; 157 | that.UpdatePlayerPosition(); 158 | that.TranslateMap(); 159 | if (typeof targetTile.Event === 'function') { 160 | targetTile.Event(); 161 | } 162 | } 163 | else { 164 | // Maybe later add an audio queue to let the player know they can't move in this direction. 165 | } 166 | }; 167 | 168 | this.Down = function () { 169 | // Face downwards regardless of the ability to move there. 170 | that.Context.Player.Direction = Math.PI / 2; 171 | 172 | // Where am I going? 173 | let targetTile = OnePRGame.Tileset[OnePRGame.Map[that.Context.Player.Position[0] + 1][that.Context.Player.Position[1]]]; 174 | 175 | // Can I get there? 176 | if (targetTile !== null && !targetTile.Impassable) { 177 | that.Context.Player.Position[0]++; 178 | that.UpdatePlayerPosition(); 179 | that.TranslateMap(); 180 | if (typeof targetTile.Event === 'function') { 181 | targetTile.Event(); 182 | } 183 | } 184 | else { 185 | // Maybe later add an audio queue to let the player know they can't move in this direction. 186 | } 187 | }; 188 | 189 | this.Left = function () { 190 | // Face left regardless of the ability to move there. 191 | that.Context.Player.Direction = Math.PI; 192 | 193 | // Where am I going? 194 | let targetTile = OnePRGame.Tileset[OnePRGame.Map[that.Context.Player.Position[0]][that.Context.Player.Position[1] - 1]]; 195 | 196 | // Can I get there? 197 | if (targetTile !== null && !targetTile.Impassable) { 198 | that.Context.Player.Position[1]--; 199 | that.UpdatePlayerPosition(); 200 | that.TranslateMap(); 201 | if (typeof targetTile.Event === 'function') { 202 | targetTile.Event(); 203 | } 204 | } 205 | else { 206 | // Maybe later add an audio queue to let the player know they can't move in this direction. 207 | } 208 | }; 209 | 210 | this.Right = function () { 211 | // Face right regardless of the ability to move there. 212 | that.Context.Player.Direction = 0; 213 | 214 | // Where am I going? 215 | let targetTile = OnePRGame.Tileset[OnePRGame.Map[that.Context.Player.Position[0]][that.Context.Player.Position[1] + 1]]; 216 | 217 | // Can I get there? 218 | if (targetTile !== null && !targetTile.Impassable) { 219 | that.Context.Player.Position[1]++; 220 | that.UpdatePlayerPosition(); 221 | that.TranslateMap(); 222 | if (typeof targetTile.Event === 'function') { 223 | targetTile.Event(); 224 | } 225 | } 226 | else { 227 | // Maybe later add an audio queue to let the player know they can't move in this direction. 228 | } 229 | }; 230 | 231 | this.UpdatePlayerPosition = function () { 232 | let player = document.getElementsByClassName(that.GameBoardClassName)[0].getElementsByClassName('game-board-2d-player')[0]; 233 | player.style.top = (100 * that.Context.Player.Position[0]) + 'px'; 234 | player.style.left = (100 * that.Context.Player.Position[1]) + 'px'; 235 | }; 236 | 237 | this.PlayerAction = function (text) { 238 | 239 | let gameBoard = document.getElementsByClassName(that.GameBoardClassName)[0]; 240 | let gameBoardPlayer = gameBoard.getElementsByClassName('game-board-2d-player')[0]; 241 | let gameBoardPlayerActions = gameBoardPlayer.getElementsByClassName('game-board-2d-player-action'); 242 | if (gameBoardPlayerActions.length > 0) { 243 | gameBoardPlayer.removeChild(gameBoardPlayerActions[0]); 244 | } 245 | 246 | let newPlayerAction = document.createElement('div'); 247 | newPlayerAction.className = 'game-board-2d-player-action'; 248 | newPlayerAction.textContent = text; 249 | gameBoardPlayer.appendChild(newPlayerAction); 250 | 251 | }; 252 | 253 | this.TranslateMap = function () { 254 | 255 | let gameBoard = document.getElementsByClassName(that.GameBoardClassName)[0]; 256 | let gameBoardMap = gameBoard.getElementsByClassName('game-board-2d-map')[0]; 257 | 258 | gameBoardMap.style.top = Math.min(Math.max((-100 * that.Context.Player.Position[0]) + that.HalfHeight - 50, that.MinTop), that.MaxTop) + 'px'; 259 | gameBoardMap.style.left = Math.min(Math.max((-100 * that.Context.Player.Position[1]) + that.HalfWidth - 50, that.MinLeft), that.MaxLeft) + 'px'; 260 | 261 | }; 262 | 263 | }; 264 | -------------------------------------------------------------------------------- /scripts/game-engine-3d.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | if (typeof OnePRGame === 'undefined') { 4 | var OnePRGame = {}; 5 | } 6 | 7 | OnePRGame.Engine3D = function (context, gameBoardClassName) { 8 | 9 | this.GameBoardClassName = gameBoardClassName; 10 | this.Context = context; 11 | this.ScreenWidth = 0; 12 | this.ScreenHeight = 0; 13 | this.HalfWidth = 0; 14 | this.HalfHeight = 0; 15 | this.CanvasContext = null; 16 | this.Camera = { 17 | DrawDistance: 4, 18 | XFieldOfView: 100 * Math.PI / 180, 19 | YFieldOfView: 100 * Math.PI / 180 20 | }; 21 | this.MoveQueue = []; 22 | this.RenderInterval = null; 23 | this.MoveQueueInterval = null; 24 | this.Planes = []; 25 | let that = this; 26 | 27 | // Applying patterns to the 2D image doesn't work right. 28 | ////this.StoneBrick = that.CanvasContext.createPattern(document.getElementsByClassName('stone-bricks')[0], 'repeat'); 29 | ////for (let i = 2; i < that.Planes.length; i++) { 30 | //// that.Planes[i].Color = that.StoneBrick; 31 | ////} 32 | 33 | this.Run = function () { 34 | 35 | let gameBoardWindow = document.getElementsByClassName('game-board-window')[0]; 36 | that.ScreenWidth = gameBoardWindow.clientWidth; 37 | that.ScreenHeight = gameBoardWindow.clientHeight; 38 | 39 | that.HalfWidth = that.ScreenWidth / 2; 40 | that.HalfHeight = that.ScreenHeight / 2; 41 | 42 | that.Camera.XFocalLength = that.HalfWidth / Math.tan(that.Camera.XFieldOfView / 2); 43 | that.Camera.YFocalLength = that.HalfHeight / Math.tan(that.Camera.YFieldOfView / 2); 44 | 45 | let gameBoard = document.getElementsByClassName(that.GameBoardClassName)[0]; 46 | 47 | // Remove old elements 48 | let existingGameBoardCanvases = gameBoard.getElementsByClassName('game-board-3d-canvas'); 49 | if (existingGameBoardCanvases.length > 0) { 50 | gameBoard.removeChild(existingGameBoardCanvases[0]); 51 | } 52 | 53 | let existingGameBoardPlayerActions = gameBoard.getElementsByClassName('game-board-3d-player-action'); 54 | for (let i = existingGameBoardPlayerActions.length - 1; i >= 0; i--) { 55 | gameBoard.removeChild(existingGameBoardPlayerActions[i]); 56 | } 57 | 58 | let existingGameBoardControls = gameBoard.getElementsByClassName('game-board-3d-controls'); 59 | if (existingGameBoardControls.length > 0) { 60 | gameBoard.removeChild(existingGameBoardControls[0]); 61 | } 62 | 63 | // Add canvas element 64 | let canvas = document.createElement('canvas'); 65 | canvas.className = 'game-board-3d-canvas'; 66 | canvas.width = that.ScreenWidth; 67 | canvas.height = that.ScreenHeight; 68 | gameBoard.appendChild(canvas); 69 | that.CanvasContext = canvas.getContext('2d'); 70 | 71 | // Convert map to planes. 72 | for (let i = 0; i < that.Context.Map.length; i++) { 73 | for (let j = 0; j < that.Context.Map[i].length; j++) { 74 | that.Planes.push({ 75 | Vertices: [ 76 | [j, 0, i], 77 | [j, 0, i + 1], 78 | [j + 1, 0, i + 1], 79 | [j + 1, 0, i] 80 | ], 81 | Color: that.Context.Tileset[that.Context.Map[i][j]].Color 82 | }); 83 | } 84 | } 85 | 86 | // This is the frame rate (1000/50 = 20 frames/sec) 87 | // Mainstream games: 60 FPS is good, 30 FPS low. 88 | that.RenderInterval = setInterval(function () { that.Render(); }, 50); 89 | 90 | that.MoveQueueInterval = setInterval(function () { that.ExecuteMoveQueue(); }, 25); 91 | 92 | // Setup controls elements 93 | let controls3d = document.createElement('div'); 94 | controls3d.className = 'game-board-3d-controls'; 95 | controls3d.style.position = 'absolute'; 96 | controls3d.style.bottom = '0px'; 97 | controls3d.style.left = '0px'; 98 | 99 | let leftButton = document.createElement('button'); 100 | leftButton.type = 'button'; 101 | leftButton.className = 'game-board-3d-controls-left'; 102 | leftButton.textContent = 'Left'; 103 | controls3d.appendChild(leftButton); 104 | 105 | let forwardButton = document.createElement('button'); 106 | forwardButton.type = 'button'; 107 | forwardButton.className = 'game-board-3d-controls-forward'; 108 | forwardButton.textContent = 'Forward'; 109 | controls3d.appendChild(forwardButton); 110 | 111 | let rightButton = document.createElement('button'); 112 | rightButton.type = 'button'; 113 | rightButton.className = 'game-board-3d-controls-right'; 114 | rightButton.textContent = 'Right'; 115 | controls3d.appendChild(rightButton); 116 | 117 | gameBoard.appendChild(controls3d); 118 | 119 | // Controls event listeners 120 | gameBoard.getElementsByClassName('game-board-3d-controls-left')[0].addEventListener('click', that.Left, false); 121 | 122 | gameBoard.getElementsByClassName('game-board-3d-controls-forward')[0].addEventListener('click', that.Forward, false); 123 | 124 | gameBoard.getElementsByClassName('game-board-3d-controls-right')[0].addEventListener('click', that.Right, false); 125 | 126 | }; 127 | 128 | this.Stop = function (callback) { 129 | 130 | clearInterval(that.RenderInterval); 131 | clearInterval(that.MoveQueueInterval); 132 | 133 | let gameBoard = document.getElementsByClassName(that.GameBoardClassName)[0]; 134 | 135 | gameBoard.getElementsByClassName('game-board-3d-controls-left')[0].removeEventListener('click', that.Left, false); 136 | 137 | gameBoard.getElementsByClassName('game-board-3d-controls-forward')[0].removeEventListener('click', that.Forward, false); 138 | 139 | gameBoard.getElementsByClassName('game-board-3d-controls-right')[0].removeEventListener('click', that.Right, false); 140 | 141 | let stopInterval = setInterval(function () { 142 | if (that.MoveQueue.length === 0 || that.MoveQueue[0].Status !== 'Executing') { 143 | 144 | clearInterval(stopInterval); 145 | 146 | if (typeof callback !== 'undefined') { 147 | callback(); 148 | } 149 | } 150 | }, 50); 151 | }; 152 | 153 | this.Forward = function () { 154 | if (that.MoveQueue.length === 0 || that.MoveQueue[0].Status !== 'Executing') { 155 | that.MoveQueue.push({ 156 | Status: 'Not Started', 157 | Move: function () { 158 | let isX = that.Context.Player.Direction !== Math.PI / 2 && that.Context.Player.Direction !== Math.PI * 3 / 2; 159 | let isAdd = that.Context.Player.Direction !== Math.PI && that.Context.Player.Direction !== Math.PI * 3 / 2; 160 | 161 | let startPosition = parseFloat((that.Context.Player.Position[isX ? 1 : 0] + 0.5)); 162 | let endPosition = startPosition + (isAdd ? 1 : -1); 163 | 164 | // Where am I going? 165 | let targetTile = OnePRGame.Tileset[OnePRGame.Map[that.Context.Player.Position[0] + (isX ? 0 : isAdd ? 1 : -1)][that.Context.Player.Position[1] + (!isX ? 0 : isAdd ? 1 : -1)]]; 166 | 167 | // Can I get there? 168 | if (targetTile !== null && !targetTile.Impassable) { 169 | 170 | let intervalId = setInterval(function () { 171 | 172 | if ((isAdd && (that.Context.Player.Position[isX ? 1 : 0] + 0.5) >= endPosition) || (!isAdd && (that.Context.Player.Position[isX ? 1 : 0] + 0.5) <= endPosition)) { 173 | that.Context.Player.Position[isX ? 1 : 0] = endPosition - 0.5; 174 | clearInterval(intervalId); 175 | that.MoveQueue[0].Status = 'Complete'; 176 | if (typeof targetTile.Event === 'function') { 177 | targetTile.Event(); 178 | } 179 | } 180 | else { 181 | that.Context.Player.Position[isX ? 1 : 0] = (parseFloat((that.Context.Player.Position[isX ? 1 : 0] + 0.5)) + (isAdd ? 0.05 : -0.05)).toPrecision(3) - 0.5; 182 | } 183 | 184 | }, 50); 185 | 186 | } 187 | else { 188 | that.MoveQueue[0].Status = 'Complete'; 189 | } 190 | } 191 | }); 192 | } 193 | }; 194 | 195 | this.Left = function () { 196 | if (that.MoveQueue.length === 0 || that.MoveQueue[0].Status !== 'Executing') { 197 | that.MoveQueue.push({ 198 | Status: 'Not Started', 199 | Move: function () { 200 | let startPosition = parseFloat(that.Context.Player.Direction); 201 | let endPosition = startPosition - (Math.PI / 2); 202 | 203 | let intervalId = setInterval(function () { 204 | 205 | if (that.Context.Player.Direction <= endPosition) { 206 | that.Context.Player.Direction = endPosition; 207 | clearInterval(intervalId); 208 | if (that.Context.Player.Direction < 0) { 209 | that.Context.Player.Direction += Math.PI * 2; 210 | } 211 | 212 | that.MoveQueue[0].Status = 'Complete'; 213 | } 214 | else { 215 | that.Context.Player.Direction = parseFloat(that.Context.Player.Direction) - (Math.PI / 20); 216 | } 217 | }, 50); 218 | } 219 | }); 220 | } 221 | }; 222 | 223 | this.Right = function () { 224 | if (that.MoveQueue.length === 0 || that.MoveQueue[0].Status !== 'Executing') { 225 | that.MoveQueue.push({ 226 | Status: 'Not Started', 227 | Move: function () { 228 | let startPosition = parseFloat(that.Context.Player.Direction); 229 | let endPosition = startPosition + (Math.PI / 2); 230 | 231 | let intervalId = setInterval(function () { 232 | 233 | if (that.Context.Player.Direction >= endPosition) { 234 | that.Context.Player.Direction = endPosition; 235 | clearInterval(intervalId); 236 | if (that.Context.Player.Direction > Math.PI * 2) { 237 | that.Context.Player.Direction -= Math.PI * 2; 238 | } 239 | 240 | that.MoveQueue[0].Status = 'Complete'; 241 | } 242 | else { 243 | that.Context.Player.Direction = parseFloat(that.Context.Player.Direction) + (Math.PI / 20); 244 | } 245 | }, 50); 246 | } 247 | }); 248 | } 249 | }; 250 | 251 | this.PlayerAction = function (text) { 252 | 253 | let playerAction = document.createElement('div'); 254 | playerAction.className = 'game-board-3d-player-action'; 255 | playerAction.textContent = text; 256 | document.getElementsByClassName(that.GameBoardClassName)[0].appendChild(playerAction); 257 | 258 | }; 259 | 260 | this.RotatePointAroundCameraDirection = function (point3d) { 261 | let x = point3d[0]; 262 | let z = point3d[2]; 263 | 264 | let cosRY = Math.cos(that.Context.Player.Direction); 265 | let sinRY = Math.sin(that.Context.Player.Direction); 266 | let tempz = z; 267 | let tempx = x; 268 | 269 | x = Math.round(((tempx * cosRY) + (tempz * sinRY)) * 100) / 100; 270 | z = Math.round(((tempx * -sinRY) + (tempz * cosRY)) * 100) / 100; 271 | 272 | return [z, point3d[1], x]; 273 | }; 274 | 275 | this.GetLocalCoordinates = function (plane3d) { 276 | 277 | let localPlane = { 278 | Vertices: [], 279 | Color: plane3d.Color 280 | }; 281 | 282 | for (let i = 0; i < plane3d.Vertices.length; i++) { 283 | 284 | // Camera position modification 285 | let vertex = [ 286 | plane3d.Vertices[i][0] - (that.Context.Player.Position[1] + 0.5), 287 | plane3d.Vertices[i][1] - 0.5, 288 | plane3d.Vertices[i][2] - (that.Context.Player.Position[0] + 0.5) 289 | ]; 290 | 291 | // Camera direction modification 292 | vertex = that.RotatePointAroundCameraDirection(vertex); 293 | 294 | localPlane.Vertices.push(vertex); 295 | } 296 | 297 | return localPlane; 298 | }; 299 | 300 | this.Calc3DPointIn2D = function (point3d) { 301 | 302 | // Using projection, not using ray casting. Maybe some other time. 303 | 304 | let x3d = point3d[0]; 305 | let y3d = point3d[1]; 306 | let z3d = point3d[2]; 307 | 308 | // Projection 309 | let x2d = z3d === 0 && x3d > 0 ? that.HalfWidth : z3d === 0 && x3d < 0 ? -that.HalfWidth : (x3d * (that.Camera.XFocalLength / z3d)); 310 | 311 | if (z3d < 0) { 312 | // Using this as a surrogate for proper 3D clipping. 313 | // See: www.cubic.org/docs/3dclip.htm 314 | x2d = -6 * x2d; 315 | } 316 | 317 | let y2d = z3d === 0 && y3d > 0 ? that.HalfHeight : z3d === 0 && y3d < 0 ? -that.HalfHeight : (y3d * (that.Camera.YFocalLength / z3d)); 318 | 319 | if (z3d < 0) { 320 | // Using this as a surrogate for proper 3D clipping. 321 | // See: www.cubic.org/docs/3dclip.htm 322 | y2d = -6 * y2d; 323 | } 324 | 325 | // So, point (0, 0) on the canvas is the upper left corner and positive is in 326 | // the direction of the bottom of the screen. 327 | // That means everything is mirrored over the Y, and the code here adjusts for that. 328 | // And we want (0, 0) in the middle, so we move it to the center of the screen. 329 | return [x2d + that.HalfWidth, -y2d + that.HalfHeight]; 330 | 331 | }; 332 | 333 | this.DrawPlaneIn2D = function (plane2d) { 334 | 335 | that.CanvasContext.beginPath(); 336 | 337 | for (let i = 0; i < plane2d.Vertices.length; i++) { 338 | 339 | if (i === 0) { 340 | that.CanvasContext.moveTo.apply(that.CanvasContext, plane2d.Vertices[i]); 341 | } 342 | else { 343 | that.CanvasContext.lineTo.apply(that.CanvasContext, plane2d.Vertices[i]); 344 | } 345 | 346 | } 347 | 348 | that.CanvasContext.closePath(); 349 | that.CanvasContext.fillStyle = plane2d.Color; 350 | that.CanvasContext.fill(); 351 | 352 | }; 353 | 354 | this.RegionContains = function (region, location) { 355 | var lastPoint = region.Vertices[region.Vertices.length - 1]; 356 | var isInside = false; 357 | var x = location[2]; 358 | for (var i = 0; i < region.Vertices.length; i++) { 359 | var x1 = lastPoint[2]; 360 | var x2 = region.Vertices[i][2]; 361 | var dx = x2 - x1; 362 | 363 | if ((x1 <= x && x2 > x) || (x1 >= x && x2 < x)) { 364 | var grad = (region.Vertices[i][0] - lastPoint[0]) / dx; 365 | var intersectAtLat = lastPoint[0] + ((x - x1) * grad); 366 | 367 | if (intersectAtLat > location[0]) { 368 | isInside = !isInside; 369 | } 370 | } 371 | 372 | lastPoint = region.Vertices[i]; 373 | } 374 | 375 | return isInside; 376 | }; 377 | 378 | this.SortByLeastZ = function (a, b) { 379 | 380 | let aX = null, bX = null, aZ = null, bZ = null; 381 | for (let i = 0; i < a.Vertices.length; i++) { 382 | if (aX === null || Math.abs(a.Vertices[i][0]) < aX) { 383 | aX = Math.abs(a.Vertices[i][0]); 384 | } 385 | 386 | if (a.Vertices[i][2] >= 0 && (aZ === null || a.Vertices[i][2] < aZ)) { 387 | aZ = a.Vertices[i][2]; 388 | } 389 | } 390 | 391 | for (let i = 0; i < b.Vertices.length; i++) { 392 | if (bX === null || Math.abs(b.Vertices[i][0]) < bX) { 393 | bX = Math.abs(b.Vertices[i][0]); 394 | } 395 | 396 | if (b.Vertices[i][2] >= 0 && (bZ === null || b.Vertices[i][2] < bZ)) { 397 | bZ = b.Vertices[i][2]; 398 | } 399 | } 400 | 401 | return aZ === null && bZ === null ? 0 402 | : aZ === null && bZ !== null ? 1 403 | : aZ !== null && bZ === null ? -1 404 | : aZ < bZ ? 1 405 | : aZ === bZ && aX < bX ? 1 406 | : aZ === bZ && aX > bX ? 1 407 | : aZ === bZ && aX === bX ? 0 408 | : -1; 409 | }; 410 | 411 | this.Render = function () { 412 | 413 | // If we don't clear out the canvas, old frames will still show. 414 | that.CanvasContext.fillStyle = "rgb(0,0,0)"; 415 | that.CanvasContext.fillRect(0, 0, that.ScreenWidth, that.ScreenHeight); 416 | 417 | // Get camera relative 3D coordinates. 418 | let cameraRelative3DPlanes = []; 419 | for (let i = 0; i < that.Planes.length; i++) { 420 | 421 | // Because planes are stored in global coordinates. 422 | let plane3d = that.GetLocalCoordinates(that.Planes[i]); 423 | 424 | // Get the camera's field of view. Ignoring Y. 425 | let cameraFieldOfView = { 426 | Vertices: [ 427 | [0, null, 0], 428 | [-1 * that.Camera.DrawDistance * Math.tan(that.Camera.XFieldOfView), null, that.Camera.DrawDistance], 429 | [that.Camera.DrawDistance * Math.tan(that.Camera.XFieldOfView), null, that.Camera.DrawDistance] 430 | ] 431 | }; 432 | 433 | // Does plane have at least one vertex in the camera's field of view? 434 | let hasVertexInFront = false; 435 | for (let j = 0; j < plane3d.Vertices.length; j++) { 436 | if (that.RegionContains(cameraFieldOfView, plane3d.Vertices[j])) { 437 | hasVertexInFront = true; 438 | break; 439 | } 440 | } 441 | 442 | // Plane is visible. Store it. 443 | if (hasVertexInFront) { 444 | cameraRelative3DPlanes.push(plane3d); 445 | } 446 | 447 | } 448 | 449 | // Sort them by lowest distance positive vertex. 450 | cameraRelative3DPlanes.sort(that.SortByLeastZ); 451 | 452 | // Get camera relative 2D coordinates. 453 | let cameraRelative2DPlanes = []; 454 | for (let i = 0; i < cameraRelative3DPlanes.length; i++) { 455 | 456 | let plane2d = { 457 | Vertices: [], 458 | Color: cameraRelative3DPlanes[i].Color 459 | }; 460 | 461 | // Calculate 2D vertices 462 | for (let j = 0; j < cameraRelative3DPlanes[i].Vertices.length; j++) { 463 | 464 | let vertex2d = that.Calc3DPointIn2D(cameraRelative3DPlanes[i].Vertices[j]); 465 | plane2d.Vertices.push(vertex2d); 466 | 467 | } 468 | 469 | cameraRelative2DPlanes.push(plane2d); 470 | 471 | } 472 | 473 | // Draw 2D planes 474 | for (let i = 0; i < cameraRelative2DPlanes.length; i++) { 475 | 476 | that.DrawPlaneIn2D(cameraRelative2DPlanes[i]); 477 | 478 | } 479 | 480 | }; 481 | 482 | this.ExecuteMoveQueue = function () { 483 | 484 | if (that.MoveQueue.length > 0) { 485 | if (that.MoveQueue[0].Status === 'Complete') { 486 | that.MoveQueue.shift(); 487 | } 488 | else if (that.MoveQueue[0].Status === 'Not Started') { 489 | that.MoveQueue[0].Status = 'Executing'; 490 | that.MoveQueue[0].Move(); 491 | } 492 | 493 | // If status is 'Executing' don't do anything. 494 | } 495 | }; 496 | 497 | }; -------------------------------------------------------------------------------- /scripts/game-engine-example.js: -------------------------------------------------------------------------------- 1 | /* 2 | 3 | Example engine interface. 4 | 5 | if (typeof OnePRGame === 'undefined') { 6 | var OnePRGame = {}; 7 | } 8 | 9 | // An example game engine. Defines what needs to be supported. 10 | // Could make, say, a text-based game or an isometric perspective, 11 | // in addition to the 2D and 3D currently available. 12 | OnePRGame.EngineExample = function (context, gameBoardClassName) { 13 | 14 | // The map placeholder element's class name. 15 | this.GameBoardClassName = gameBoardClassName; 16 | 17 | // The map, tileset, player objects. 18 | this.Context = context; 19 | 20 | // Used for instancing. 21 | let that = this; 22 | 23 | // Start up engine. May already be started. Draw map, player, setup controls. 24 | this.Run = function () { }; 25 | 26 | // Stop engine. May or may not have intervals to cancel. 27 | // Callback for when stop is complete. 28 | this.Stop = function (callback) { }; 29 | 30 | // Handle the player action control. 31 | // Should probably be named something different. Just using it currently to display text. 32 | this.PlayerAction = function (text) { }; 33 | }; 34 | 35 | */ -------------------------------------------------------------------------------- /scripts/game.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | if (typeof OnePRGame === 'undefined') { 4 | var OnePRGame = {}; 5 | } 6 | 7 | OnePRGame.Engines = []; 8 | 9 | OnePRGame.CurrentEngine = 0; 10 | 11 | OnePRGame.Player = { 12 | // [Z, X] 13 | Position: [0, 0], 14 | // +X = 0, +Z = Math.PI / 2, -X = Math.PI, -Z = Math.PI * 3 / 2 15 | Direction: 0 16 | }; 17 | 18 | // Map is a set of rows, 19 | // each row has a set of tile IDs. 20 | // Y------------------------------> +X 21 | /* | */OnePRGame.Map = [ 22 | /* | */ [0, 0, 0, 0, 0, 0, 0, 0, 0, 0], 23 | /* | */ [0, 0, 0, 0, 0, 0, 0, 9, 0, 0], 24 | /* | */ [0, 1, 2, 3, 4, 5, 6, 7, 8, 0], 25 | /* | */ [0, 0, 0, 0, 0, 6, 0, 0, 0, 0], 26 | /* | */ [0, 0, 0, 0, 0, 7, 0, 0, 0, 0], 27 | /* | */ [0, 0, 0, 0, 0, 8, 0, 0, 0, 0], 28 | /* | */ [0, 0, 0, 0, 0, 0, 0, 0, 0, 0] 29 | /* | */]; 30 | /* V */ 31 | /* */ 32 | /*+Z */ 33 | 34 | // Tileset is the collection of tiles used in the game. 35 | // I just added the 'Id' property for convenience, map refers to array index. 36 | OnePRGame.Tileset = [{ 37 | Id: 0, 38 | Color: '#000', 39 | Impassable: true 40 | }, 41 | { 42 | Id: 1, 43 | Color: '#888', 44 | Text: 'GO', 45 | IsStart: true 46 | }, 47 | { 48 | Id: 2, 49 | Color: '#0FF', 50 | Event: function () { 51 | OnePRGame.Engines[OnePRGame.CurrentEngine].PlayerAction('COMMIT!'); 52 | } 53 | }, 54 | { 55 | Id: 3, 56 | Color: '#0F0', 57 | Event: function () { 58 | OnePRGame.Engines[OnePRGame.CurrentEngine].PlayerAction('COMMIT!'); 59 | } 60 | }, 61 | { 62 | Id: 4, 63 | Color: '#FFF', 64 | Event: function () { 65 | OnePRGame.Engines[OnePRGame.CurrentEngine].PlayerAction('COMMIT!'); 66 | } 67 | }, 68 | { 69 | Id: 5, 70 | Color: '#F0F', 71 | Event: function () { 72 | OnePRGame.Engines[OnePRGame.CurrentEngine].PlayerAction('COMMIT!'); 73 | } 74 | }, 75 | { 76 | Id: 6, 77 | Color: '#F00', 78 | Event: function () { 79 | OnePRGame.Engines[OnePRGame.CurrentEngine].PlayerAction('COMMIT!'); 80 | } 81 | }, 82 | { 83 | Id: 7, 84 | Color: '#00F', 85 | Event: function () { 86 | OnePRGame.Engines[OnePRGame.CurrentEngine].PlayerAction('COMMIT!'); 87 | } 88 | }, 89 | { 90 | Id: 8, 91 | Color: '#FF0', 92 | Event: function () { 93 | OnePRGame.Player.Position = [2, 1]; 94 | OnePRGame.Player.Direction = 0; 95 | OnePRGame.Engines[OnePRGame.CurrentEngine].Stop(function () { 96 | OnePRGame.Engines[OnePRGame.CurrentEngine].Run(); 97 | OnePRGame.Engines[OnePRGame.CurrentEngine].PlayerAction('PULL REQUEST!'); 98 | document.getElementsByClassName('controls-score')[0].textContent = 99 | parseInt(document.getElementsByClassName('controls-score')[0].textContent) + 200; 100 | }); 101 | } 102 | }, 103 | { 104 | Id: 9, 105 | Color: '#000', 106 | Event: function () { 107 | OnePRGame.Engines[OnePRGame.CurrentEngine].PlayerAction('EASTER EGG! +1000 POINTS!'); 108 | document.getElementsByClassName('controls-score')[0].textContent = 109 | parseInt(document.getElementsByClassName('controls-score')[0].textContent) + 1000; 110 | } 111 | }]; 112 | 113 | OnePRGame.LoadControls = function () { 114 | 115 | if (OnePRGame.Engines.length > 1) { 116 | 117 | document.getElementsByClassName('controls-swapengine')[0].addEventListener('click', function () { 118 | 119 | if (OnePRGame.CurrentEngine === 0) { 120 | OnePRGame.Engines[OnePRGame.CurrentEngine].Stop(function () { 121 | document.getElementsByClassName('game-board-2d')[0].style.display = 'none'; 122 | document.getElementsByClassName('game-board-3d')[0].style.display = 'block'; 123 | OnePRGame.CurrentEngine = 1; 124 | OnePRGame.Engines[OnePRGame.CurrentEngine].Run(); 125 | }); 126 | } 127 | else if (OnePRGame.CurrentEngine === 1) { 128 | OnePRGame.Engines[OnePRGame.CurrentEngine].Stop(function () { 129 | document.getElementsByClassName('game-board-3d')[0].style.display = 'none'; 130 | document.getElementsByClassName('game-board-2d')[0].style.display = 'block'; 131 | OnePRGame.CurrentEngine = 0; 132 | OnePRGame.Engines[OnePRGame.CurrentEngine].Run(); 133 | }); 134 | } 135 | 136 | }, false); 137 | 138 | } 139 | else { 140 | 141 | document.getElementsByClassName('controls-swapengine')[0].style.display = 'none'; 142 | 143 | } 144 | 145 | }; 146 | 147 | OnePRGame.Load = function () { 148 | 149 | // Get player start position 150 | for (let i = 0; i < OnePRGame.Map.length; i++) { 151 | for (let j = 0; j < OnePRGame.Map[i].length; j++) { 152 | if (OnePRGame.Tileset[OnePRGame.Map[i][j]].IsStart) { 153 | OnePRGame.Player.Position = [i, j]; 154 | } 155 | } 156 | } 157 | 158 | // Add supported engines 159 | OnePRGame.Engines.push(new OnePRGame.Engine2D(OnePRGame, 'game-board-2d')); 160 | OnePRGame.Engines.push(new OnePRGame.Engine3D(OnePRGame, 'game-board-3d')); 161 | 162 | OnePRGame.Engines[OnePRGame.CurrentEngine].Run(); 163 | 164 | OnePRGame.LoadControls(); 165 | 166 | OnePRGame.Engines[OnePRGame.CurrentEngine].PlayerAction('INITIAL CHECKIN!'); 167 | 168 | document.getElementById('loader').style.display = 'none'; 169 | }; 170 | 171 | (function loadGame() { 172 | 173 | OnePRGame.Load(); 174 | 175 | })(); -------------------------------------------------------------------------------- /scripts/main.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | 4 | ((window.gitter = {}).chat = {}).options = { 5 | room: 'thepracticaldev/1pr', 6 | useStyles: false 7 | }; 8 | 9 | 10 | (function() { 11 | 12 | let sidebar = { 13 | isOpen: false, 14 | toggle: function() { 15 | if(sidebar.isOpen){ 16 | sidebar.close(); 17 | }else{ 18 | sidebar.open(); 19 | } 20 | }, 21 | open: function() { 22 | sidebar.isOpen = true; 23 | document.getElementById('sidebar').className = 'open'; 24 | document.getElementById('sidebar-overlay').className = 'open'; 25 | document.getElementById('body-container').className = 'sidebar-open'; 26 | }, 27 | close: function() { 28 | sidebar.isOpen = false; 29 | document.getElementById('sidebar').className = ''; 30 | document.getElementById('sidebar-overlay').className = ''; 31 | document.getElementById('body-container').className = ''; 32 | } 33 | }; 34 | 35 | document.getElementById('sidebar-toggle').addEventListener('click', sidebar.toggle, false); 36 | document.getElementById('sidebar-close').addEventListener('click', sidebar.close, false); 37 | document.getElementById('sidebar-overlay').addEventListener('click', sidebar.close, false); 38 | 39 | document.getElementById('sidebar-toggle').style.display = 'block'; 40 | 41 | })(); 42 | -------------------------------------------------------------------------------- /scripts/pr-counter.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | (function() { 4 | 5 | let done = window.registerAsyncScript('pr-counter'); 6 | window.api('https://api.github.com/search/issues?q=type:pr%20state:open%20repo:thepracticaldev/1pr', function(err, openPRs){ 7 | 8 | document.getElementById('pr-count').textContent = openPRs.total_count; 9 | done(); 10 | 11 | }); 12 | 13 | })(); 14 | -------------------------------------------------------------------------------- /scripts/pr.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | (function loadLastPr() { 4 | 5 | // Helper function to calculate difference between date and return the date in appropiate format. 6 | let getTime = function(merge_date) { 7 | 8 | let diff = new Date() - new Date(merge_date); 9 | 10 | let seconds = Math.round(diff/1000); 11 | if (seconds < 60) { 12 | return seconds + ' seconds ago'; 13 | } 14 | 15 | let minutes = Math.round(seconds/60); 16 | if (minutes < 60) { 17 | return minutes + ' minutes ago'; 18 | } 19 | 20 | let hours = Math.round(minutes/60); 21 | if (hours < 24) { 22 | return hours + ' hours ago'; 23 | } 24 | 25 | let days = Math.round(hours/24); 26 | return days + ' days ago'; 27 | }; 28 | 29 | // Helper function to limit the maximum number of characters of a string but not break up words 30 | let maxLength = function(str, maxLength) { 31 | 32 | // Don't do anything if the string is shorter 33 | if(str.length < maxLength){ 34 | return str; 35 | } 36 | 37 | let words = str.split(' '); 38 | str = ''; 39 | 40 | for(let i = 0; i < words.length; i++){ 41 | 42 | str += words[i]; 43 | 44 | if(str.length >= maxLength){ 45 | str += '...'; 46 | break; 47 | } 48 | 49 | str += ' '; 50 | } 51 | 52 | return str; 53 | 54 | }; 55 | 56 | 57 | // Load last merged PR 58 | let done = window.registerAsyncScript('pr'); 59 | window.api('https://api.github.com/repos/thepracticaldev/1pr/pulls?state=closed&sort=updated&direction=desc', function(err, pr_list) { 60 | done(); 61 | 62 | if(err) { 63 | if(window.console){ window.console.error(err); } 64 | document.getElementById('last-pr').textContent = 'Could not load latest PR'; 65 | document.getElementById('last-pr').className = 'error'; 66 | return; 67 | } 68 | 69 | for(let i = 0; i < pr_list.length; i++) { 70 | 71 | if(pr_list[i].merged_at !== null) { 72 | let pr = pr_list[i]; 73 | 74 | document.getElementById('last-pr').getElementsByClassName('pr-title')[0].innerHTML = '' + maxLength(pr.title, 32) + ' by ' + pr.user.login + ' merged ' + getTime(pr.merged_at) + ''; 75 | break; 76 | } 77 | 78 | } 79 | 80 | }); 81 | 82 | })(); 83 | -------------------------------------------------------------------------------- /scripts/stats.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | (function loadRepoIssues() { 4 | 5 | let done = window.registerAsyncScript('stats/issues'); 6 | window.api('https://api.github.com/search/issues?q=type:issue%20state:open%20repo:thepracticaldev/1pr', function(err, issues) { 7 | done(); 8 | 9 | document.getElementById('issues').getElementsByClassName('data')[0].textContent = issues.total_count; 10 | 11 | }); 12 | 13 | })(); 14 | 15 | 16 | (function loadRepoStats() { 17 | 18 | let done = window.registerAsyncScript('stats'); 19 | window.api('https://api.github.com/repos/thepracticaldev/1pr', function(err, stats) { 20 | done(); 21 | 22 | document.getElementById('forks').getElementsByClassName('data')[0].textContent = stats.forks_count; 23 | document.getElementById('stars').getElementsByClassName('data')[0].textContent = stats.stargazers_count; 24 | 25 | }); 26 | 27 | })(); 28 | -------------------------------------------------------------------------------- /scripts/utils.js: -------------------------------------------------------------------------------- 1 | /* exported utils */ 2 | "use strict"; 3 | 4 | //Useful side-wide utilities 5 | var utils = { 6 | showElement: function (element){ 7 | //Removes first instance of hidden in className 8 | // so element can be hidden multiple times and still 9 | // stay hidden when shown fewer times. 10 | var hiddenClassName = /\b(hidden)\b/; 11 | element.className = element.className.replace(hiddenClassName, ""); 12 | }, 13 | 14 | hideElement: function(element){ 15 | //Extend the className rather than use classList so that 16 | //element can be hidden multiple times 17 | element.className = element.className + " hidden"; 18 | }, 19 | 20 | showManyElements: function(arrayOfElements){ 21 | arrayOfElements.forEach(el => this.showElement(el)); 22 | }, 23 | 24 | hideManyElements: function(arrayOfElements){ 25 | arrayOfElements.forEach(el => this.hideElement(el)); 26 | } 27 | }; 28 | -------------------------------------------------------------------------------- /scripts/vote.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | 3 | // Wrap everything inside a scoped function 4 | (function loadVoting() { 5 | 6 | // Helper function; shorthand for document.getElementById (because that gets pretty annoying if you have to write it a bunch) 7 | let el = function(id) { return document.getElementById(id); }; 8 | 9 | window.api('https://api.github.com/repos/thepracticaldev/1pr/pulls', function(err, pullRequests){ 10 | 11 | // Log and display any errors 12 | if(err !== null){ 13 | if(window.console){ window.console.error(err); } 14 | el('pull-requests').className = 'error'; 15 | el('pull-requests').textContent = 'Could not load pull requests'; 16 | return; 17 | } 18 | 19 | // Append each pull request to the pull requests element 20 | for(let i = 0; i < pullRequests.length; i++){ 21 | 22 | /* 23 | 24 |

25 | {{title}} 26 |
27 | submitted by: {{user.login}} 28 |
29 | 30 |

31 | 32 | */ 33 | let pullRequest = document.createElement('p'); 34 | let pullRequestHeading = document.createElement('strong'); 35 | let pullRequestLink = document.createElement('a'); 36 | pullRequestLink.href = pullRequests[i].html_url; 37 | pullRequestLink.textContent = pullRequests[i].title; 38 | 39 | pullRequestHeading.appendChild(pullRequestLink); 40 | pullRequest.appendChild(pullRequestHeading); 41 | 42 | let pullRequestLineBreak1 = document.createElement('br'); 43 | pullRequest.appendChild(pullRequestLineBreak1); 44 | 45 | let pullRequestSubmittedBy = document.createElement('small'); 46 | pullRequestSubmittedBy.textContent = 'submitted by: ' + pullRequests[i].user.login; 47 | pullRequest.appendChild(pullRequestSubmittedBy); 48 | 49 | let pullRequestLineBreak2 = document.createElement('br'); 50 | pullRequest.appendChild(pullRequestLineBreak2); 51 | 52 | let pullRequestGooglePlus = document.createElement('span'); 53 | pullRequestGooglePlus.className = 'g-plusone'; 54 | pullRequestGooglePlus.setAttribute('data-href', pullRequests[i].html_url); 55 | pullRequest.appendChild(pullRequestGooglePlus); 56 | 57 | el('pull-requests-container').appendChild(pullRequest); 58 | 59 | window.gapi.plusone.go(); 60 | } 61 | 62 | }); 63 | 64 | })(); 65 | -------------------------------------------------------------------------------- /stylesheets/game.css: -------------------------------------------------------------------------------- 1 | body { 2 | background: #000; 3 | margin: 0; 4 | text-align: center; 5 | } 6 | 7 | .game-board-window { 8 | background-color: #000; 9 | position: absolute; 10 | top: 0; 11 | left: 0; 12 | bottom: 0; 13 | right: 0; 14 | max-width: 800px; 15 | max-height: 600px; 16 | margin: auto auto; 17 | overflow: hidden; 18 | color: #000; 19 | } 20 | 21 | .controls { 22 | color: #FFF; 23 | position: absolute; 24 | z-index: 1 25 | } 26 | 27 | .game-board-2d, .game-board-3d { 28 | position: relative; 29 | width: 100%; 30 | height: 100%; 31 | } 32 | 33 | @keyframes fadein { 34 | 0% { 35 | opacity: 0; 36 | margin-top: 40px; 37 | } 38 | 39 | 25% { 40 | opacity: 1; 41 | } 42 | 43 | 75% { 44 | opacity: 1; 45 | margin-top: 0px; 46 | } 47 | 48 | 100% { 49 | opacity: 0; 50 | } 51 | } 52 | 53 | /* 2D ENGINE STYLES */ 54 | 55 | .game-board-2d-map { 56 | position: absolute; 57 | } 58 | 59 | .game-board-2d-tile { 60 | width: 100px; 61 | height: 100px; 62 | float: left 63 | } 64 | 65 | .game-board-2d-row-break { 66 | visibility: hidden; 67 | height: 0; 68 | line-height: 0; 69 | overflow: hidden; 70 | clear: both; 71 | } 72 | 73 | .game-board-2d-player { 74 | position: absolute; 75 | top: 100px; 76 | left: 100px; 77 | font-size: 45px; 78 | width: 100px; 79 | text-align: center; 80 | height: 100px; 81 | line-height: 100px 82 | } 83 | 84 | .game-board-2d-player-action { 85 | color: #fff; 86 | font-size: 21px; 87 | text-align: center; 88 | animation: fadein 1s; 89 | opacity: 0; 90 | text-shadow: 3px 3px 2px rgba(0,0,0,0.8), -3px 3px 2px rgba(0,0,0,0.8), 3px -3px 2px rgba(0,0,0,0.8), -3px -3px 2px rgba(0,0,0,0.8); 91 | position: absolute; 92 | top: 0; 93 | line-height: normal; 94 | } 95 | 96 | /* 3D ENGINE STYLES */ 97 | 98 | .game-board-3d-player-action { 99 | position: absolute; 100 | top: 50%; 101 | width: 100%; 102 | color: #fff; 103 | font-size: 21px; 104 | text-align: center; 105 | animation: fadein 1s; 106 | opacity: 0; 107 | text-shadow: 3px 3px 2px rgba(0,0,0,0.8), -3px 3px 2px rgba(0,0,0,0.8), 3px -3px 2px rgba(0,0,0,0.8), -3px -3px 2px rgba(0,0,0,0.8); 108 | line-height: normal; 109 | } 110 | -------------------------------------------------------------------------------- /stylesheets/gitter-override.css: -------------------------------------------------------------------------------- 1 | /* Hide gitter button until the page is loaded */ 2 | body .gitter-open-chat-button { 3 | bottom: -50px; 4 | right: 25px; 5 | z-index: 80; 6 | transition: bottom 120ms ease-out; 7 | } 8 | .loaded .gitter-open-chat-button { 9 | bottom: 0; 10 | } 11 | -------------------------------------------------------------------------------- /stylesheets/styles.css: -------------------------------------------------------------------------------- 1 | /* Use border-box sizing instead of the default (content-box) 2 | This makes it so the width of an element includes its padding, instead of adding the padding on top of the element. 3 | More information on box-sizing here: https://developer.mozilla.org/en-US/docs/Web/CSS/box-sizing */ 4 | * { 5 | box-sizing: border-box; 6 | } 7 | 8 | /* Basic html elements styling */ 9 | body { 10 | margin: 0; 11 | padding-bottom: 60px; 12 | 13 | background: #1F2539; 14 | color: white; 15 | 16 | font-size: 16px; 17 | font-weight: 400; 18 | font-family: 'Open Sans', sans-serif; 19 | } 20 | 21 | strong { 22 | font-weight: 600; 23 | } 24 | 25 | h1, 26 | h2, 27 | h3, 28 | h4 { 29 | text-align: center; 30 | font-family: 'Patua One', sans-serif; 31 | font-weight: 400; 32 | } 33 | 34 | h1 { 35 | margin-bottom: 30px; 36 | } 37 | 38 | a { 39 | display: inline-block; 40 | color: #2980b9; 41 | text-decoration: none; 42 | } 43 | 44 | a:hover, 45 | a:focus { 46 | color: #3498db; 47 | } 48 | 49 | 50 | 51 | /* Basic elements */ 52 | #body-container { 53 | position: fixed; 54 | height: 100%; 55 | width: 100%; 56 | overflow: auto; 57 | } 58 | 59 | .container { 60 | width: 100%; 61 | max-width: 960px; 62 | margin: auto; 63 | } 64 | 65 | .panel { 66 | display: block; 67 | padding: 50px 10px; 68 | 69 | background: white; 70 | color: #333; 71 | 72 | opacity: 0; 73 | transform: translateY(-20px); 74 | transition: all 320ms ease-out; 75 | } 76 | body.loaded .panel { 77 | opacity: 1; 78 | transform: translateY(0); 79 | } 80 | 81 | 82 | 83 | /* Header */ 84 | header { 85 | height: 100px; 86 | background: linear-gradient(to bottom, rgba(0,0,0,0.2) 0%,rgba(0,0,0,0) 100%); 87 | } 88 | 89 | header .container { 90 | display: flex; 91 | align-items: flex-start; 92 | } 93 | 94 | header .fill { 95 | flex-grow: 1; 96 | } 97 | 98 | header nav { 99 | display: flex; 100 | align-items: center; 101 | } 102 | 103 | header a { 104 | display: block; 105 | padding: 15px 10px; 106 | color: rgba(255, 255, 255, 0.75); 107 | } 108 | header a:hover, 109 | header a:focus { 110 | color: white; 111 | } 112 | 113 | header a svg { 114 | display: block; 115 | height: 24px; 116 | width: 24px; 117 | } 118 | header a path { 119 | fill: rgba(255, 255, 255, 0.75); 120 | transition: all 80ms ease-in-out; 121 | } 122 | header a:hover path { 123 | fill: white; 124 | } 125 | header .logo path { 126 | fill: white; 127 | } 128 | 129 | 130 | 131 | /* Sidebar menu */ 132 | #sidebar-toggle { 133 | display: none; 134 | } 135 | #sidebar { 136 | position: fixed; 137 | right: -400px; 138 | top: 0; 139 | z-index: 0; 140 | transition: right 320ms ease-out, z-index 320ms step-end; 141 | 142 | width: 100%; 143 | max-width: 400px; 144 | height: 100%; 145 | height: 100vh; 146 | overflow-y: auto; 147 | 148 | background: #141825; 149 | } 150 | #sidebar.open { 151 | right: 0; 152 | z-index: 100; 153 | transition: right 320ms ease-out, z-index 320ms step-start; 154 | } 155 | 156 | #sidebar-close { 157 | display: block; 158 | float: right; 159 | padding: 15px; 160 | } 161 | #sidebar-close svg { 162 | display: block; 163 | width: 24px; 164 | height: 24px; 165 | } 166 | #sidebar-close path { 167 | fill: rgba(255, 255, 255, 0.75); 168 | } 169 | #sidebar-close:hover path, #sidebar-close:active path { 170 | fill: white; 171 | } 172 | 173 | #sidebar nav, 174 | #sidebar .info { 175 | width: 90%; 176 | margin: auto; 177 | margin-top: 72px; 178 | margin-bottom: 24px; 179 | 180 | } 181 | 182 | #sidebar nav{ 183 | border-top: 1px solid rgba(255, 255, 255, 0.1); 184 | } 185 | 186 | #sidebar .info{ 187 | padding-left:0px; 188 | -webkit-padding-start: 0px; 189 | } 190 | 191 | #sidebar nav a, 192 | #sidebar .info li{ 193 | display: block; 194 | padding: 12px; 195 | color: rgba(255, 255, 255, 0.6); 196 | text-align: center; 197 | } 198 | 199 | #sidebar nav a{ 200 | border-bottom: 1px solid rgba(255, 255, 255, 0.1); 201 | } 202 | 203 | 204 | #sidebar nav a:hover, #sidebar nav a:focus { 205 | color: white; 206 | } 207 | 208 | #sidebar-overlay { 209 | position: fixed; 210 | top: 0; 211 | left: 0; 212 | z-index: 0; 213 | opacity: 0; 214 | transition: opacity 320ms ease-out, z-index 320ms step-end; 215 | 216 | width: 100%; 217 | height: 100%; 218 | background: rgba(31, 37, 57, 0.6); 219 | } 220 | #sidebar-overlay.open { 221 | opacity: 1; 222 | z-index: 90; 223 | transition: opacity 320ms ease-out, z-index 320ms step-start; 224 | } 225 | 226 | #body-container { 227 | transition: all 280ms ease-out; 228 | } 229 | #body-container.sidebar-open { 230 | transform: translateX(-72px); 231 | overflow-y: hidden; 232 | } 233 | 234 | 235 | /* PR counter */ 236 | #pr-counter { 237 | position: relative; 238 | 239 | display: flex; 240 | flex-direction: column; 241 | justify-content: center; 242 | 243 | width: 220px; 244 | height: 220px; 245 | margin: auto; 246 | margin-bottom: 50px; 247 | padding-bottom: 20px; 248 | } 249 | 250 | #pr-counter h1 { 251 | text-align: center; 252 | } 253 | 254 | #loader { 255 | position: absolute; 256 | top: 0; 257 | left: 0; 258 | 259 | width: 100%; 260 | height: 100%; 261 | } 262 | 263 | #pr-counter .info { 264 | opacity: 0; 265 | transform: scale(0.8); 266 | transition: all 240ms ease-out; 267 | } 268 | body.loaded #pr-counter .info { 269 | opacity: 1; 270 | transform: scale(1); 271 | } 272 | 273 | #pr-count { 274 | font-size: 4em; 275 | text-align: center; 276 | } 277 | #pr-counter .label { 278 | margin-top: -12px; 279 | 280 | color: rgba(255, 255, 255, 0.8); 281 | 282 | font-size: 0.8em; 283 | text-align: center; 284 | } 285 | 286 | /* Last PR */ 287 | #last-pr { 288 | opacity: 0; 289 | transition: all 320ms ease-out; 290 | 291 | min-height: 100px; 292 | padding: 10px; 293 | padding-top: 40px; 294 | 295 | background: linear-gradient(to top, rgba(0,0,0,0.1) 0%,rgba(0,0,0,0) 100%); 296 | text-align: center; 297 | } 298 | body.loaded #last-pr { 299 | opacity: 1; 300 | } 301 | 302 | #last-pr.error { 303 | color: rgba(255, 255, 255, 0.5); 304 | font-size: 0.9em; 305 | font-style: italic; 306 | } 307 | 308 | #last-pr p { 309 | margin: 5px; 310 | color: rgba(255, 255, 255, 0.6); 311 | } 312 | 313 | #last-pr span { 314 | white-space: nowrap; 315 | } 316 | 317 | /* Intro */ 318 | #intro { 319 | display: flex; 320 | flex-wrap: wrap; 321 | justify-content: space-between; 322 | align-items: stretch; 323 | 324 | padding: 10px; 325 | padding-bottom: 25px; 326 | text-align: center; 327 | } 328 | 329 | #intro p { 330 | width: 100%; 331 | } 332 | 333 | #intro .block { 334 | flex-basis: 250px; 335 | flex-grow: 1; 336 | 337 | margin: 0 10px; 338 | margin-top: 28px; 339 | padding: 0 10px; 340 | 341 | border: 2px solid rgba(128, 128, 128, 0.2); 342 | border-radius: 6px; 343 | 344 | text-align: left; 345 | } 346 | 347 | #intro ul { 348 | padding-left: 25px; 349 | } 350 | 351 | #intro .icon { 352 | display: block; 353 | width: 60px; 354 | height: 38px; 355 | margin: auto; 356 | margin-bottom: -20px; 357 | 358 | position: relative; 359 | top: -20px; 360 | 361 | padding: 0 8px; 362 | background: white; 363 | } 364 | 365 | #intro .icon path { 366 | fill: rgb(128, 128, 128); 367 | } 368 | 369 | 370 | 371 | 372 | /* Repository info & stats */ 373 | #repo-info { 374 | opacity: 0; 375 | transition: all 480ms ease-out; 376 | 377 | display: flex; 378 | justify-content: center; 379 | flex-wrap: wrap; 380 | 381 | padding: 40px 10px; 382 | } 383 | body.loaded #repo-info { 384 | opacity: 1; 385 | } 386 | 387 | #repo-info h2 { 388 | width: 80%; 389 | margin-top: 30px; 390 | padding-top: 40px; 391 | border-top: 1px solid rgba(255, 255, 255, 0.2); 392 | } 393 | 394 | #contributors { 395 | display: flex; 396 | justify-content: center; 397 | flex-wrap: wrap; 398 | 399 | width: 100%; 400 | } 401 | 402 | #contributors a { 403 | display: inline-block; 404 | margin: 2px 0; 405 | } 406 | 407 | #contributors img { 408 | height: 42px; 409 | width: 42px; 410 | margin: 5px; 411 | border-radius: 50%; 412 | transition: all 80ms ease-in-out; 413 | } 414 | 415 | #contributors a:hover img, #contributors a:focus img { 416 | transform: scale(1.1); 417 | } 418 | 419 | 420 | #repo-info .block { 421 | flex-basis: 100%; 422 | flex-grow: 1; 423 | 424 | display: flex; 425 | align-items: center; 426 | justify-content: center; 427 | 428 | padding: 5px; 429 | } 430 | 431 | #repo-info svg { 432 | display: inline-block; 433 | width: 24px; 434 | height: 24px; 435 | margin-right: 8px; 436 | } 437 | #repo-info path { 438 | fill: rgba(255, 255, 255, 0.6); 439 | } 440 | 441 | #stars .data::after { 442 | content: ' stars'; 443 | } 444 | #issues .data::after { 445 | content: ' issues'; 446 | } 447 | #forks .data::after { 448 | content: ' forks'; 449 | } 450 | 451 | 452 | /* Pull Request Voting elements */ 453 | #pull-requests-container { 454 | display: flex; 455 | flex-wrap: wrap; 456 | justify-content: space-between; 457 | align-items: stretch; 458 | 459 | padding: 10px; 460 | padding-top: 15px; 461 | } 462 | 463 | #pull-requests-container p { 464 | width: 100%; 465 | margin: 0; 466 | margin-bottom: 15px; 467 | padding-bottom: 15px; 468 | border-bottom: 1px solid rgba(128, 128, 128, 0.15); 469 | } 470 | #pull-requests-container p:last-child { 471 | border-bottom: none; 472 | margin-bottom: 0; 473 | padding-bottom: 0; 474 | } 475 | 476 | 477 | 478 | /* Games list */ 479 | #games { 480 | opacity: 0; 481 | transition: all 480ms ease-out; 482 | 483 | padding: 40px 0; 484 | text-align: center; 485 | } 486 | body.loaded #games { 487 | opacity: 1; 488 | } 489 | 490 | #games a { 491 | display: inline-block; 492 | padding: 12px 22px; 493 | margin: 0 4px; 494 | 495 | background: rgba(255, 255, 255, 0.8); 496 | border-radius: 6px; 497 | color: #333; 498 | } 499 | #games a:hover, #games a:focus { 500 | background: white; 501 | color: #333; 502 | } 503 | 504 | #migration-container>div>div{ 505 | margin-bottom: 25px; 506 | } 507 | 508 | #migration-container div span{ 509 | margin: 10px; 510 | display: block; 511 | } 512 | 513 | #migration-container div span:first-child{ 514 | font-weight: bold; 515 | } 516 | 517 | /* It is recommended to always work mobile-first (i.e. design the base css for mobile), and use 'min-width' media queries to adapt the display for larger monitors */ 518 | @media all and (min-width: 480px) { 519 | 520 | .container { 521 | width: 90%; 522 | } 523 | 524 | header { 525 | padding-top: 10px; 526 | } 527 | 528 | #pr-counter { 529 | margin-top: 25px; 530 | margin-bottom: 75px; 531 | } 532 | 533 | #repo-info .block { 534 | flex-basis: 30%; 535 | } 536 | 537 | #sidebar { 538 | width: 90%; 539 | } 540 | #sidebar nav { 541 | width: 80%; 542 | } 543 | #sidebar-close { 544 | margin: 5px; 545 | } 546 | 547 | 548 | #migration-container div span:not(:last-child){ 549 | display: inline; 550 | } 551 | 552 | } 553 | 554 | @media all and (min-width: 720px) { 555 | 556 | .container { 557 | width: 85%; 558 | } 559 | 560 | #pr-counter { 561 | margin-top: 50px; 562 | margin-bottom: 100px; 563 | } 564 | 565 | #pull-requests-container p { 566 | width: 46%; 567 | } 568 | 569 | } 570 | 571 | /* 572 | Utility for quickly showing and hiding via javascript 573 | Last in the document and !important so it can override 574 | existing styles regardless of specificity 575 | */ 576 | .hidden{ 577 | /* 578 | Temporarily allow !important because this is a valid 579 | use case (unless someone can suggest a better approach) 580 | */ 581 | /* csslint ignore:start */ 582 | display:none!important; 583 | /* csslint ignore:end */ 584 | } 585 | 586 | -------------------------------------------------------------------------------- /vendor/gitter.css: -------------------------------------------------------------------------------- 1 | /*gitter*/ 2 | 3 | .gitter-hidden { 4 | box-sizing: border-box; 5 | display: none 6 | } 7 | 8 | .gitter-icon { 9 | box-sizing: border-box; 10 | width: 22px; 11 | height: 22px; 12 | fill: currentColor 13 | } 14 | 15 | .gitter-chat-embed { 16 | box-sizing: border-box; 17 | z-index: 100; 18 | position: fixed; 19 | top: 0; 20 | left: 60%; 21 | bottom: 0; 22 | right: 0; 23 | display: -webkit-box; 24 | display: -ms-flexbox; 25 | display: flex; 26 | -webkit-box-orient: horizontal; 27 | -webkit-box-direction: normal; 28 | -ms-flex-direction: row; 29 | flex-direction: row; 30 | background-color: #fff; 31 | border-left: 1px solid #333; 32 | box-shadow: -12px 0 18px 0 rgba(50, 50, 50, .3); 33 | -webkit-transition: -webkit-transform .3s cubic-bezier(.16, .22, .22, 1.7); 34 | transition: -webkit-transform .3s cubic-bezier(.16, .22, .22, 1.7); 35 | transition: transform .3s cubic-bezier(.16, .22, .22, 1.7); 36 | transition: transform .3s cubic-bezier(.16, .22, .22, 1.7), -webkit-transform .3s cubic-bezier(.16, .22, .22, 1.7) 37 | } 38 | 39 | @context border-box { 40 | .gitter-chat-embed { 41 | box-sizing: border-box; 42 | background-color: #fff 43 | } 44 | } 45 | 46 | .gitter-chat-embed.is-collapsed:not(.is-loading) { 47 | box-sizing: border-box; 48 | -webkit-transform: translateX(110%); 49 | transform: translateX(110%) 50 | } 51 | 52 | .gitter-chat-embed:after { 53 | box-sizing: border-box; 54 | content: ''; 55 | z-index: -1; 56 | position: absolute; 57 | top: 0; 58 | left: 100%; 59 | bottom: 0; 60 | right: -100%; 61 | background-color: #fff 62 | } 63 | 64 | @context border-box { 65 | .gitter-chat-embed:after { 66 | box-sizing: border-box; 67 | background-color: #fff 68 | } 69 | } 70 | 71 | @media(max-width:1150px) { 72 | .gitter-chat-embed { 73 | box-sizing: border-box; 74 | left: 45% 75 | } 76 | } 77 | 78 | @media(max-width:944px) { 79 | .gitter-chat-embed { 80 | box-sizing: border-box; 81 | left: 30% 82 | } 83 | } 84 | 85 | @media(max-width:600px) { 86 | .gitter-chat-embed { 87 | box-sizing: border-box; 88 | left: 15% 89 | } 90 | } 91 | 92 | @media(max-width:500px) { 93 | .gitter-chat-embed { 94 | box-sizing: border-box; 95 | left: 0; 96 | border-left: none 97 | } 98 | } 99 | 100 | .gitter-chat-embed>iframe { 101 | box-sizing: border-box; 102 | -webkit-box-flex: 1; 103 | -ms-flex: 1; 104 | flex: 1; 105 | width: 100%; 106 | height: 100%; 107 | border: 0 108 | } 109 | 110 | .gitter-chat-embed-loading-wrapper { 111 | box-sizing: border-box; 112 | position: absolute; 113 | top: 0; 114 | left: 0; 115 | bottom: 0; 116 | right: 0; 117 | display: none; 118 | -webkit-box-pack: center; 119 | -ms-flex-pack: center; 120 | justify-content: center; 121 | -webkit-box-align: center; 122 | -ms-flex-align: center; 123 | -ms-grid-row-align: center; 124 | align-items: center 125 | } 126 | 127 | .is-loading .gitter-chat-embed-loading-wrapper { 128 | box-sizing: border-box; 129 | display: -webkit-box; 130 | display: -ms-flexbox; 131 | display: flex 132 | } 133 | 134 | .gitter-chat-embed-loading-indicator { 135 | box-sizing: border-box; 136 | opacity: .75; 137 | background-image: url(data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHZpZXdCb3g9IjAgMCAxNzkyIDE3OTIiIGZpbGw9IiMzYTMxMzMiPjxwYXRoIGQ9Ik01MjYgMTM5NHEwIDUzLTM3LjUgOTAuNXQtOTAuNSAzNy41cS01MiAwLTkwLTM4dC0zOC05MHEwLTUzIDM3LjUtOTAuNXQ5MC41LTM3LjUgOTAuNSAzNy41IDM3LjUgOTAuNXptNDk4IDIwNnEwIDUzLTM3LjUgOTAuNXQtOTAuNSAzNy41LTkwLjUtMzcuNS0zNy41LTkwLjUgMzcuNS05MC41IDkwLjUtMzcuNSA5MC41IDM3LjUgMzcuNSA5MC41em0tNzA0LTcwNHEwIDUzLTM3LjUgOTAuNXQtOTAuNSAzNy41LTkwLjUtMzcuNS0zNy41LTkwLjUgMzcuNS05MC41IDkwLjUtMzcuNSA5MC41IDM3LjUgMzcuNSA5MC41em0xMjAyIDQ5OHEwIDUyLTM4IDkwdC05MCAzOHEtNTMgMC05MC41LTM3LjV0LTM3LjUtOTAuNSAzNy41LTkwLjUgOTAuNS0zNy41IDkwLjUgMzcuNSAzNy41IDkwLjV6bS05NjQtOTk2cTAgNjYtNDcgMTEzdC0xMTMgNDctMTEzLTQ3LTQ3LTExMyA0Ny0xMTMgMTEzLTQ3IDExMyA0NyA0NyAxMTN6bTExNzAgNDk4cTAgNTMtMzcuNSA5MC41dC05MC41IDM3LjUtOTAuNS0zNy41LTM3LjUtOTAuNSAzNy41LTkwLjUgOTAuNS0zNy41IDkwLjUgMzcuNSAzNy41IDkwLjV6bS02NDAtNzA0cTAgODAtNTYgMTM2dC0xMzYgNTYtMTM2LTU2LTU2LTEzNiA1Ni0xMzYgMTM2LTU2IDEzNiA1NiA1NiAxMzZ6bTUzMCAyMDZxMCA5My02NiAxNTguNXQtMTU4IDY1LjVxLTkzIDAtMTU4LjUtNjUuNXQtNjUuNS0xNTguNXEwLTkyIDY1LjUtMTU4dDE1OC41LTY2cTkyIDAgMTU4IDY2dDY2IDE1OHoiLz48L3N2Zz4=); 138 | -webkit-animation: spin 2s infinite linear; 139 | animation: spin 2s infinite linear 140 | } 141 | 142 | @-webkit-keyframes spin { 143 | 0% { 144 | box-sizing: border-box; 145 | -webkit-transform: rotate(0deg); 146 | transform: rotate(0deg) 147 | } 148 | to { 149 | box-sizing: border-box; 150 | -webkit-transform: rotate(359.9deg); 151 | transform: rotate(359.9deg) 152 | } 153 | } 154 | 155 | @keyframes spin { 156 | 0% { 157 | box-sizing: border-box; 158 | -webkit-transform: rotate(0deg); 159 | transform: rotate(0deg) 160 | } 161 | to { 162 | box-sizing: border-box; 163 | -webkit-transform: rotate(359.9deg); 164 | transform: rotate(359.9deg) 165 | } 166 | } 167 | 168 | .gitter-chat-embed-action-bar { 169 | position: absolute; 170 | top: 0; 171 | left: 0; 172 | right: 0; 173 | -webkit-box-pack: end; 174 | -ms-flex-pack: end; 175 | justify-content: flex-end; 176 | padding-bottom: .7em; 177 | background: -webkit-linear-gradient(top, #fff, #fff 50%, hsla(0, 0%, 100%, 0)); 178 | background: linear-gradient(180deg, #fff 0, #fff 50%, hsla(0, 0%, 100%, 0)) 179 | } 180 | 181 | .gitter-chat-embed-action-bar, 182 | .gitter-chat-embed-action-bar-item { 183 | box-sizing: border-box; 184 | display: -webkit-box; 185 | display: -ms-flexbox; 186 | display: flex 187 | } 188 | 189 | .gitter-chat-embed-action-bar-item { 190 | -webkit-box-pack: center; 191 | -ms-flex-pack: center; 192 | justify-content: center; 193 | -webkit-box-align: center; 194 | -ms-flex-align: center; 195 | align-items: center; 196 | width: 40px; 197 | height: 40px; 198 | padding-left: 0; 199 | padding-right: 0; 200 | opacity: .65; 201 | background: none; 202 | background-position: 50%; 203 | background-repeat: no-repeat; 204 | background-size: 22px 22px; 205 | border: 0; 206 | outline: none; 207 | cursor: pointer; 208 | cursor: hand; 209 | -webkit-transition: all .2s ease; 210 | transition: all .2s ease 211 | } 212 | 213 | .gitter-chat-embed-action-bar-item:focus, 214 | .gitter-chat-embed-action-bar-item:hover { 215 | box-sizing: border-box; 216 | opacity: 1 217 | } 218 | 219 | .gitter-chat-embed-action-bar-item:active { 220 | box-sizing: border-box; 221 | -webkit-filter: hue-rotate(80deg) saturate(150); 222 | filter: hue-rotate(80deg) saturate(150) 223 | } 224 | 225 | .gitter-chat-embed-action-bar-item-pop-out { 226 | box-sizing: border-box; 227 | margin-right: -4px; 228 | background-image: url(data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHZpZXdCb3g9IjAgMCAyMDAgMTcxLjQyOSIgZmlsbD0iIzNhMzEzMyI+PHBhdGggZD0iTTE1Ny4xNDMsMTAzLjU3MXYzNS43MTRjMCw4Ljg1NC0zLjE0NCwxNi40MjYtOS40MzEsMjIuNzEzcy0xMy44NTgsOS40MzEtMjIuNzEyLDkuNDMxSDMyLjE0MyBjLTguODU0LDAtMTYuNDI1LTMuMTQ0LTIyLjcxMi05LjQzMVMwLDE0OC4xNCwwLDEzOS4yODVWNDYuNDI5YzAtOC44NTQsMy4xNDQtMTYuNDI1LDkuNDMxLTIyLjcxMiBjNi4yODctNi4yODcsMTMuODU4LTkuNDMxLDIyLjcxMi05LjQzMWg3OC41NzJjMS4wNDEsMCwxLjg5NiwwLjMzNSwyLjU2NiwxLjAwNGMwLjY3LDAuNjcsMS4wMDQsMS41MjUsMS4wMDQsMi41NjdWMjUgYzAsMS4wNDItMC4zMzQsMS44OTctMS4wMDQsMi41NjdjLTAuNjcsMC42Ny0xLjUyNSwxLjAwNC0yLjU2NiwxLjAwNEgzMi4xNDNjLTQuOTExLDAtOS4xMTUsMS43NDktMTIuNjEyLDUuMjQ2IHMtNS4yNDYsNy43MDEtNS4yNDYsMTIuNjEydjkyLjg1NmMwLDQuOTExLDEuNzQ5LDkuMTE1LDUuMjQ2LDEyLjYxMnM3LjcwMSw1LjI0NSwxMi42MTIsNS4yNDVIMTI1YzQuOTEsMCw5LjExNS0xLjc0OCwxMi42MTEtNS4yNDUgYzMuNDk3LTMuNDk3LDUuMjQ2LTcuNzAxLDUuMjQ2LTEyLjYxMnYtMzUuNzE0YzAtMS4wNDIsMC4zMzQtMS44OTcsMS4wMDQtMi41NjdjMC42Ny0wLjY2OSwxLjUyNS0xLjAwNCwyLjU2Ny0xLjAwNGg3LjE0MyBjMS4wNDIsMCwxLjg5NywwLjMzNSwyLjU2NywxLjAwNEMxNTYuODA5LDEwMS42NzQsMTU3LjE0MywxMDIuNTI5LDE1Ny4xNDMsMTAzLjU3MXogTTIwMCw3LjE0M3Y1Ny4xNDMgYzAsMS45MzUtMC43MDcsMy42MDktMi4xMjEsNS4wMjJjLTEuNDEzLDEuNDE0LTMuMDg4LDIuMTIxLTUuMDIxLDIuMTIxYy0xLjkzNSwwLTMuNjA5LTAuNzA3LTUuMDIyLTIuMTIxbC0xOS42NDQtMTkuNjQzIGwtNzIuNzY3LDcyLjc2OWMtMC43NDQsMC43NDQtMS42LDEuMTE1LTIuNTY3LDEuMTE1cy0xLjgyMy0wLjM3MS0yLjU2Ny0xLjExNUw3Ny41NjcsMTA5LjcxYy0wLjc0NC0wLjc0NC0xLjExNi0xLjYtMS4xMTYtMi41NjcgYzAtMC45NjcsMC4zNzItMS44MjIsMS4xMTYtMi41NjZsNzIuNzY4LTcyLjc2OGwtMTkuNjQ0LTE5LjY0M2MtMS40MTMtMS40MTQtMi4xMi0zLjA4OC0yLjEyLTUuMDIyYzAtMS45MzUsMC43MDctMy42MDksMi4xMi01LjAyMiBDMTMyLjEwNSwwLjcwNywxMzMuNzc5LDAsMTM1LjcxNSwwaDU3LjE0M2MxLjkzNCwwLDMuNjA4LDAuNzA3LDUuMDIxLDIuMTIxQzE5OS4yOTMsMy41MzQsMjAwLDUuMjA4LDIwMCw3LjE0M3oiLz48L3N2Zz4=) 229 | } 230 | 231 | .gitter-chat-embed-action-bar-item-collapse-chat { 232 | box-sizing: border-box; 233 | background-image: url(data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHZpZXdCb3g9IjAgMCAxNzEuNDI5IDE3MS40MjkiIGZpbGw9IiMzYTMxMzMiPjxwYXRoIGQ9Ik0xMjIuNDMzLDEwNi4xMzhsLTE2LjI5NSwxNi4yOTVjLTAuNzQ0LDAuNzQ0LTEuNiwxLjExNi0yLjU2NiwxLjExNmMtMC45NjgsMC0xLjgyMy0wLjM3Mi0yLjU2Ny0xLjExNmwtMTUuMjktMTUuMjkgbC0xNS4yOSwxNS4yOWMtMC43NDQsMC43NDQtMS42LDEuMTE2LTIuNTY3LDEuMTE2cy0xLjgyMy0wLjM3Mi0yLjU2Ny0xLjExNmwtMTYuMjk0LTE2LjI5NWMtMC43NDQtMC43NDQtMS4xMTYtMS42LTEuMTE2LTIuNTY2IGMwLTAuOTY4LDAuMzcyLTEuODIzLDEuMTE2LTIuNTY3bDE1LjI5LTE1LjI5bC0xNS4yOS0xNS4yOWMtMC43NDQtMC43NDQtMS4xMTYtMS42LTEuMTE2LTIuNTY3czAuMzcyLTEuODIzLDEuMTE2LTIuNTY3IEw2NS4yOSw0OC45OTZjMC43NDQtMC43NDQsMS42LTEuMTE2LDIuNTY3LTEuMTE2czEuODIzLDAuMzcyLDIuNTY3LDEuMTE2bDE1LjI5LDE1LjI5bDE1LjI5LTE1LjI5IGMwLjc0NC0wLjc0NCwxLjYtMS4xMTYsMi41NjctMS4xMTZjMC45NjcsMCwxLjgyMiwwLjM3MiwyLjU2NiwxLjExNmwxNi4yOTUsMTYuMjk0YzAuNzQ0LDAuNzQ0LDEuMTE2LDEuNiwxLjExNiwyLjU2NyBzLTAuMzcyLDEuODIzLTEuMTE2LDIuNTY3bC0xNS4yOSwxNS4yOWwxNS4yOSwxNS4yOWMwLjc0NCwwLjc0NCwxLjExNiwxLjYsMS4xMTYsMi41NjcgQzEyMy41NDksMTA0LjUzOSwxMjMuMTc3LDEwNS4zOTQsMTIyLjQzMywxMDYuMTM4eiBNMTQ2LjQyOSw4NS43MTRjMC0xMS4wMTItMi43MTctMjEuMTY4LTguMTQ4LTMwLjQ2OSBzLTEyLjc5Ny0xNi42NjctMjIuMDk4LTIyLjA5OFM5Ni43MjYsMjUsODUuNzE0LDI1cy0yMS4xNjgsMi43MTYtMzAuNDY5LDguMTQ3UzM4LjU3OSw0NS45NDUsMzMuMTQ3LDU1LjI0NlMyNSw3NC43MDMsMjUsODUuNzE0IHMyLjcxNiwyMS4xNjgsOC4xNDcsMzAuNDY5czEyLjc5NywxNi42NjYsMjIuMDk4LDIyLjA5OHMxOS40NTcsOC4xNDgsMzAuNDY5LDguMTQ4czIxLjE2OC0yLjcxNywzMC40NjktOC4xNDggczE2LjY2Ni0xMi43OTcsMjIuMDk4LTIyLjA5OFMxNDYuNDI5LDk2LjcyNiwxNDYuNDI5LDg1LjcxNHogTTE3MS40MjksODUuNzE0YzAsMTUuNTUxLTMuODMyLDI5Ljg5My0xMS40OTYsNDMuMDI0IGMtNy42NjQsMTMuMTMzLTE4LjA2MiwyMy41My0zMS4xOTQsMzEuMTk0Yy0xMy4xMzIsNy42NjQtMjcuNDc0LDExLjQ5Ni00My4wMjQsMTEuNDk2cy0yOS44OTItMy44MzItNDMuMDI0LTExLjQ5NiBjLTEzLjEzMy03LjY2NC0yMy41MzEtMTguMDYyLTMxLjE5NC0zMS4xOTRDMy44MzIsMTE1LjYwNywwLDEwMS4yNjUsMCw4NS43MTRTMy44MzIsNTUuODIyLDExLjQ5Niw0Mi42OSBjNy42NjQtMTMuMTMzLDE4LjA2Mi0yMy41MzEsMzEuMTk0LTMxLjE5NEM1NS44MjIsMy44MzIsNzAuMTY0LDAsODUuNzE0LDBzMjkuODkzLDMuODMyLDQzLjAyNCwxMS40OTYgYzEzLjEzMyw3LjY2NCwyMy41MywxOC4wNjIsMzEuMTk0LDMxLjE5NEMxNjcuNTk3LDU1LjgyMiwxNzEuNDI5LDcwLjE2NCwxNzEuNDI5LDg1LjcxNHoiLz48L3N2Zz4=) 234 | } 235 | 236 | .gitter-open-chat-button { 237 | color: #fff !important; 238 | z-index: 100; 239 | position: fixed; 240 | bottom: 0; 241 | right: 10px; 242 | padding: 1em 3em; 243 | background-color: #36bc98; 244 | border: 0; 245 | border-top-left-radius: .5em; 246 | border-top-right-radius: .5em; 247 | font-family: sans-serif; 248 | font-size: 12px; 249 | letter-spacing: 1px; 250 | text-transform: uppercase; 251 | text-align: center; 252 | text-decoration: none; 253 | cursor: pointer; 254 | cursor: hand; 255 | -webkit-transition: all .3s ease; 256 | transition: all .3s ease 257 | } 258 | 259 | .gitter-open-chat-button::after, 260 | .gitter-open-chat-button:visited::after { 261 | display: none; 262 | } 263 | 264 | .gitter-open-chat-button, 265 | .gitter-open-chat-button:visited { 266 | box-sizing: border-box; 267 | color: #fff 268 | } 269 | 270 | .gitter-open-chat-button:focus, 271 | .gitter-open-chat-button:hover { 272 | box-sizing: border-box; 273 | background-color: #3ea07f; 274 | color: #fff 275 | } 276 | 277 | .gitter-open-chat-button:focus { 278 | box-sizing: border-box; 279 | box-shadow: 0 0 8px rgba(62, 160, 127, .6); 280 | outline: none 281 | } 282 | 283 | .gitter-open-chat-button:active { 284 | box-sizing: border-box; 285 | color: #eee 286 | } 287 | 288 | .gitter-open-chat-button.is-collapsed { 289 | box-sizing: border-box; 290 | -webkit-transform: translateY(120%); 291 | transform: translateY(120%) 292 | } 293 | -------------------------------------------------------------------------------- /vendor/progressbar.min.js: -------------------------------------------------------------------------------- 1 | // ProgressBar.js 1.0.1 2 | // https://kimmobrunfeldt.github.io/progressbar.js 3 | // License: MIT 4 | 5 | !function(a){if("object"==typeof exports&&"undefined"!=typeof module)module.exports=a();else if("function"==typeof define&&define.amd)define([],a);else{var b;b="undefined"!=typeof window?window:"undefined"!=typeof global?global:"undefined"!=typeof self?self:this,b.ProgressBar=a()}}(function(){var a;return function b(a,c,d){function e(g,h){if(!c[g]){if(!a[g]){var i="function"==typeof require&&require;if(!h&&i)return i(g,!0);if(f)return f(g,!0);var j=new Error("Cannot find module '"+g+"'");throw j.code="MODULE_NOT_FOUND",j}var k=c[g]={exports:{}};a[g][0].call(k.exports,function(b){var c=a[g][1][b];return e(c?c:b)},k,k.exports,b,a,c,d)}return c[g].exports}for(var f="function"==typeof require&&require,g=0;ga?0:(a-f)/e;for(h in b)b.hasOwnProperty(h)&&(i=g[h],k="function"==typeof i?i:o[i],b[h]=j(c[h],d[h],k,l));return b}function j(a,b,c,d){return a+(b-a)*c(d)}function k(a,b){var c=n.prototype.filter,d=a._filterArgs;f(c,function(e){"undefined"!=typeof c[e][b]&&c[e][b].apply(a,d)})}function l(a,b,c,d,e,f,g,h,j,l,m){v=b+c+d,w=Math.min(m||u(),v),x=w>=v,y=d-(v-w),a.isPlaying()&&(x?(j(g,a._attachment,y),a.stop(!0)):(a._scheduleId=l(a._timeoutHandler,s),k(a,"beforeTween"),b+c>w?i(1,e,f,g,1,1,h):i(w,e,f,g,d,b+c,h),k(a,"afterTween"),j(e,a._attachment,y)))}function m(a,b){var c={},d=typeof b;return"string"===d||"function"===d?f(a,function(a){c[a]=b}):f(a,function(a){c[a]||(c[a]=b[a]||q)}),c}function n(a,b){this._currentState=a||{},this._configured=!1,this._scheduleFunction=p,"undefined"!=typeof b&&this.setConfig(b)}var o,p,q="linear",r=500,s=1e3/60,t=Date.now?Date.now:function(){return+new Date},u="undefined"!=typeof SHIFTY_DEBUG_NOW?SHIFTY_DEBUG_NOW:t;p="undefined"!=typeof window?window.requestAnimationFrame||window.webkitRequestAnimationFrame||window.oRequestAnimationFrame||window.msRequestAnimationFrame||window.mozCancelRequestAnimationFrame&&window.mozRequestAnimationFrame||setTimeout:setTimeout;var v,w,x,y;return n.prototype.tween=function(a){return this._isTweening?this:(void 0===a&&this._configured||this.setConfig(a),this._timestamp=u(),this._start(this.get(),this._attachment),this.resume())},n.prototype.setConfig=function(a){a=a||{},this._configured=!0,this._attachment=a.attachment,this._pausedAtTime=null,this._scheduleId=null,this._delay=a.delay||0,this._start=a.start||e,this._step=a.step||e,this._finish=a.finish||e,this._duration=a.duration||r,this._currentState=g({},a.from)||this.get(),this._originalState=this.get(),this._targetState=g({},a.to)||this.get();var b=this;this._timeoutHandler=function(){l(b,b._timestamp,b._delay,b._duration,b._currentState,b._originalState,b._targetState,b._easing,b._step,b._scheduleFunction)};var c=this._currentState,d=this._targetState;return h(d,c),this._easing=m(c,a.easing||q),this._filterArgs=[c,this._originalState,d,this._easing],k(this,"tweenCreated"),this},n.prototype.get=function(){return g({},this._currentState)},n.prototype.set=function(a){this._currentState=a},n.prototype.pause=function(){return this._pausedAtTime=u(),this._isPaused=!0,this},n.prototype.resume=function(){return this._isPaused&&(this._timestamp+=u()-this._pausedAtTime),this._isPaused=!1,this._isTweening=!0,this._timeoutHandler(),this},n.prototype.seek=function(a){a=Math.max(a,0);var b=u();return this._timestamp+a===0?this:(this._timestamp=b-a,this.isPlaying()||(this._isTweening=!0,this._isPaused=!1,l(this,this._timestamp,this._delay,this._duration,this._currentState,this._originalState,this._targetState,this._easing,this._step,this._scheduleFunction,b),this.pause()),this)},n.prototype.stop=function(a){return this._isTweening=!1,this._isPaused=!1,this._timeoutHandler=e,(b.cancelAnimationFrame||b.webkitCancelAnimationFrame||b.oCancelAnimationFrame||b.msCancelAnimationFrame||b.mozCancelRequestAnimationFrame||b.clearTimeout)(this._scheduleId),a&&(k(this,"beforeTween"),i(1,this._currentState,this._originalState,this._targetState,1,0,this._easing),k(this,"afterTween"),k(this,"afterTweenEnd"),this._finish.call(this,this._currentState,this._attachment)),this},n.prototype.isPlaying=function(){return this._isTweening&&!this._isPaused},n.prototype.setScheduleFunction=function(a){this._scheduleFunction=a},n.prototype.dispose=function(){var a;for(a in this)this.hasOwnProperty(a)&&delete this[a]},n.prototype.filter={},n.prototype.formula={linear:function(a){return a}},o=n.prototype.formula,g(n,{now:u,each:f,tweenProps:i,tweenProp:j,applyFilter:k,shallowCopy:g,defaults:h,composeEasingObject:m}),"function"==typeof SHIFTY_DEBUG_NOW&&(b.timeoutHandler=l),"object"==typeof d?c.exports=n:"function"==typeof a&&a.amd?a(function(){return n}):"undefined"==typeof b.Tweenable&&(b.Tweenable=n),n}();!function(){e.shallowCopy(e.prototype.formula,{easeInQuad:function(a){return Math.pow(a,2)},easeOutQuad:function(a){return-(Math.pow(a-1,2)-1)},easeInOutQuad:function(a){return(a/=.5)<1?.5*Math.pow(a,2):-.5*((a-=2)*a-2)},easeInCubic:function(a){return Math.pow(a,3)},easeOutCubic:function(a){return Math.pow(a-1,3)+1},easeInOutCubic:function(a){return(a/=.5)<1?.5*Math.pow(a,3):.5*(Math.pow(a-2,3)+2)},easeInQuart:function(a){return Math.pow(a,4)},easeOutQuart:function(a){return-(Math.pow(a-1,4)-1)},easeInOutQuart:function(a){return(a/=.5)<1?.5*Math.pow(a,4):-.5*((a-=2)*Math.pow(a,3)-2)},easeInQuint:function(a){return Math.pow(a,5)},easeOutQuint:function(a){return Math.pow(a-1,5)+1},easeInOutQuint:function(a){return(a/=.5)<1?.5*Math.pow(a,5):.5*(Math.pow(a-2,5)+2)},easeInSine:function(a){return-Math.cos(a*(Math.PI/2))+1},easeOutSine:function(a){return Math.sin(a*(Math.PI/2))},easeInOutSine:function(a){return-.5*(Math.cos(Math.PI*a)-1)},easeInExpo:function(a){return 0===a?0:Math.pow(2,10*(a-1))},easeOutExpo:function(a){return 1===a?1:-Math.pow(2,-10*a)+1},easeInOutExpo:function(a){return 0===a?0:1===a?1:(a/=.5)<1?.5*Math.pow(2,10*(a-1)):.5*(-Math.pow(2,-10*--a)+2)},easeInCirc:function(a){return-(Math.sqrt(1-a*a)-1)},easeOutCirc:function(a){return Math.sqrt(1-Math.pow(a-1,2))},easeInOutCirc:function(a){return(a/=.5)<1?-.5*(Math.sqrt(1-a*a)-1):.5*(Math.sqrt(1-(a-=2)*a)+1)},easeOutBounce:function(a){return 1/2.75>a?7.5625*a*a:2/2.75>a?7.5625*(a-=1.5/2.75)*a+.75:2.5/2.75>a?7.5625*(a-=2.25/2.75)*a+.9375:7.5625*(a-=2.625/2.75)*a+.984375},easeInBack:function(a){var b=1.70158;return a*a*((b+1)*a-b)},easeOutBack:function(a){var b=1.70158;return(a-=1)*a*((b+1)*a+b)+1},easeInOutBack:function(a){var b=1.70158;return(a/=.5)<1?.5*(a*a*(((b*=1.525)+1)*a-b)):.5*((a-=2)*a*(((b*=1.525)+1)*a+b)+2)},elastic:function(a){return-1*Math.pow(4,-8*a)*Math.sin((6*a-1)*(2*Math.PI)/2)+1},swingFromTo:function(a){var b=1.70158;return(a/=.5)<1?.5*(a*a*(((b*=1.525)+1)*a-b)):.5*((a-=2)*a*(((b*=1.525)+1)*a+b)+2)},swingFrom:function(a){var b=1.70158;return a*a*((b+1)*a-b)},swingTo:function(a){var b=1.70158;return(a-=1)*a*((b+1)*a+b)+1},bounce:function(a){return 1/2.75>a?7.5625*a*a:2/2.75>a?7.5625*(a-=1.5/2.75)*a+.75:2.5/2.75>a?7.5625*(a-=2.25/2.75)*a+.9375:7.5625*(a-=2.625/2.75)*a+.984375},bouncePast:function(a){return 1/2.75>a?7.5625*a*a:2/2.75>a?2-(7.5625*(a-=1.5/2.75)*a+.75):2.5/2.75>a?2-(7.5625*(a-=2.25/2.75)*a+.9375):2-(7.5625*(a-=2.625/2.75)*a+.984375)},easeFromTo:function(a){return(a/=.5)<1?.5*Math.pow(a,4):-.5*((a-=2)*Math.pow(a,3)-2)},easeFrom:function(a){return Math.pow(a,4)},easeTo:function(a){return Math.pow(a,.25)}})}(),function(){function a(a,b,c,d,e,f){function g(a){return((n*a+o)*a+p)*a}function h(a){return((q*a+r)*a+s)*a}function i(a){return(3*n*a+2*o)*a+p}function j(a){return 1/(200*a)}function k(a,b){return h(m(a,b))}function l(a){return a>=0?a:0-a}function m(a,b){var c,d,e,f,h,j;for(e=a,j=0;8>j;j++){if(f=g(e)-a,l(f)e)return c;if(e>d)return d;for(;d>c;){if(f=g(e),l(f-a)f?c=e:d=e,e=.5*(d-c)+c}return e}var n=0,o=0,p=0,q=0,r=0,s=0;return p=3*b,o=3*(d-b)-p,n=1-p-o,s=3*c,r=3*(e-c)-s,q=1-s-r,k(a,j(f))}function b(b,c,d,e){return function(f){return a(f,b,c,d,e,1)}}e.setBezierFunction=function(a,c,d,f,g){var h=b(c,d,f,g);return h.displayName=a,h.x1=c,h.y1=d,h.x2=f,h.y2=g,e.prototype.formula[a]=h},e.unsetBezierFunction=function(a){delete e.prototype.formula[a]}}(),function(){function a(a,b,c,d,f,g){return e.tweenProps(d,b,a,c,1,g,f)}var b=new e;b._filterArgs=[],e.interpolate=function(c,d,f,g,h){var i=e.shallowCopy({},c),j=h||0,k=e.composeEasingObject(c,g||"linear");b.set({});var l=b._filterArgs;l.length=0,l[0]=i,l[1]=c,l[2]=d,l[3]=k,e.applyFilter(b,"tweenCreated"),e.applyFilter(b,"beforeTween");var m=a(c,i,d,f,k,j);return e.applyFilter(b,"afterTween"),m}}(),function(a){function b(a,b){var c,d=[],e=a.length;for(c=0;e>c;c++)d.push("_"+b+"_"+c);return d}function c(a){var b=a.match(v);return b?(1===b.length||a[0].match(u))&&b.unshift(""):b=["",""],b.join(A)}function d(b){a.each(b,function(a){var c=b[a];"string"==typeof c&&c.match(z)&&(b[a]=e(c))})}function e(a){return i(z,a,f)}function f(a){var b=g(a);return"rgb("+b[0]+","+b[1]+","+b[2]+")"}function g(a){return a=a.replace(/#/,""),3===a.length&&(a=a.split(""),a=a[0]+a[0]+a[1]+a[1]+a[2]+a[2]),B[0]=h(a.substr(0,2)),B[1]=h(a.substr(2,2)),B[2]=h(a.substr(4,2)),B}function h(a){return parseInt(a,16)}function i(a,b,c){var d=b.match(a),e=b.replace(a,A);if(d)for(var f,g=d.length,h=0;g>h;h++)f=d.shift(),e=e.replace(A,c(f));return e}function j(a){return i(x,a,k)}function k(a){for(var b=a.match(w),c=b.length,d=a.match(y)[0],e=0;c>e;e++)d+=parseInt(b[e],10)+",";return d=d.slice(0,-1)+")"}function l(d){var e={};return a.each(d,function(a){var f=d[a];if("string"==typeof f){var g=r(f);e[a]={formatString:c(f),chunkNames:b(g,a)}}}),e}function m(b,c){a.each(c,function(a){for(var d=b[a],e=r(d),f=e.length,g=0;f>g;g++)b[c[a].chunkNames[g]]=+e[g];delete b[a]})}function n(b,c){a.each(c,function(a){var d=b[a],e=o(b,c[a].chunkNames),f=p(e,c[a].chunkNames);d=q(c[a].formatString,f),b[a]=j(d)})}function o(a,b){for(var c,d={},e=b.length,f=0;e>f;f++)c=b[f],d[c]=a[c],delete a[c];return d}function p(a,b){C.length=0;for(var c=b.length,d=0;c>d;d++)C.push(a[b[d]]);return C}function q(a,b){for(var c=a,d=b.length,e=0;d>e;e++)c=c.replace(A,+b[e].toFixed(4));return c}function r(a){return a.match(w)}function s(b,c){a.each(c,function(a){var d,e=c[a],f=e.chunkNames,g=f.length,h=b[a];if("string"==typeof h){var i=h.split(" "),j=i[i.length-1];for(d=0;g>d;d++)b[f[d]]=i[d]||j}else for(d=0;g>d;d++)b[f[d]]=h;delete b[a]})}function t(b,c){a.each(c,function(a){var d=c[a],e=d.chunkNames,f=e.length,g=b[e[0]],h=typeof g;if("string"===h){for(var i="",j=0;f>j;j++)i+=" "+b[e[j]],delete b[e[j]];b[a]=i.substr(1)}else b[a]=g})}var u=/(\d|\-|\.)/,v=/([^\-0-9\.]+)/g,w=/[0-9.\-]+/g,x=new RegExp("rgb\\("+w.source+/,\s*/.source+w.source+/,\s*/.source+w.source+"\\)","g"),y=/^.*\(/,z=/#([0-9]|[a-f]){3,6}/gi,A="VAL",B=[],C=[];a.prototype.filter.token={tweenCreated:function(a,b,c,e){d(a),d(b),d(c),this._tokenData=l(a)},beforeTween:function(a,b,c,d){s(d,this._tokenData),m(a,this._tokenData),m(b,this._tokenData),m(c,this._tokenData)},afterTween:function(a,b,c,d){n(a,this._tokenData),n(b,this._tokenData),n(c,this._tokenData),t(d,this._tokenData)}}}(e)}).call(null)},{}],2:[function(a,b,c){var d=a("./shape"),e=a("./utils"),f=function(a,b){this._pathTemplate="M 50,50 m 0,-{radius} a {radius},{radius} 0 1 1 0,{2radius} a {radius},{radius} 0 1 1 0,-{2radius}",this.containerAspectRatio=1,d.apply(this,arguments)};f.prototype=new d,f.prototype.constructor=f,f.prototype._pathString=function(a){var b=a.strokeWidth;a.trailWidth&&a.trailWidth>a.strokeWidth&&(b=a.trailWidth);var c=50-b/2;return e.render(this._pathTemplate,{radius:c,"2radius":2*c})},f.prototype._trailString=function(a){return this._pathString(a)},b.exports=f},{"./shape":7,"./utils":8}],3:[function(a,b,c){var d=a("./shape"),e=a("./utils"),f=function(a,b){this._pathTemplate="M 0,{center} L 100,{center}",d.apply(this,arguments)};f.prototype=new d,f.prototype.constructor=f,f.prototype._initializeSvg=function(a,b){a.setAttribute("viewBox","0 0 100 "+b.strokeWidth),a.setAttribute("preserveAspectRatio","none")},f.prototype._pathString=function(a){return e.render(this._pathTemplate,{center:a.strokeWidth/2})},f.prototype._trailString=function(a){return this._pathString(a)},b.exports=f},{"./shape":7,"./utils":8}],4:[function(a,b,c){b.exports={Line:a("./line"),Circle:a("./circle"),SemiCircle:a("./semicircle"),Path:a("./path"),Shape:a("./shape"),utils:a("./utils")}},{"./circle":2,"./line":3,"./path":5,"./semicircle":6,"./shape":7,"./utils":8}],5:[function(a,b,c){var d=a("shifty"),e=a("./utils"),f={easeIn:"easeInCubic",easeOut:"easeOutCubic",easeInOut:"easeInOutCubic"},g=function h(a,b){if(!(this instanceof h))throw new Error("Constructor was called without new keyword");b=e.extend({duration:800,easing:"linear",from:{},to:{},step:function(){}},b);var c;c=e.isString(a)?document.querySelector(a):a,this.path=c,this._opts=b,this._tweenable=null;var d=this.path.getTotalLength();this.path.style.strokeDasharray=d+" "+d,this.set(0)};g.prototype.value=function(){var a=this._getComputedDashOffset(),b=this.path.getTotalLength(),c=1-a/b;return parseFloat(c.toFixed(6),10)},g.prototype.set=function(a){this.stop(),this.path.style.strokeDashoffset=this._progressToOffset(a);var b=this._opts.step;if(e.isFunction(b)){var c=this._easing(this._opts.easing),d=this._calculateTo(a,c),f=this._opts.shape||this;b(d,f,this._opts.attachment)}},g.prototype.stop=function(){this._stopTween(),this.path.style.strokeDashoffset=this._getComputedDashOffset()},g.prototype.animate=function(a,b,c){b=b||{},e.isFunction(b)&&(c=b,b={});var f=e.extend({},b),g=e.extend({},this._opts);b=e.extend(g,b);var h=this._easing(b.easing),i=this._resolveFromAndTo(a,h,f);this.stop(),this.path.getBoundingClientRect();var j=this._getComputedDashOffset(),k=this._progressToOffset(a),l=this;this._tweenable=new d,this._tweenable.tween({from:e.extend({offset:j},i.from),to:e.extend({offset:k},i.to),duration:b.duration,easing:h,step:function(a){l.path.style.strokeDashoffset=a.offset;var c=b.shape||l;b.step(a,c,b.attachment)},finish:function(a){e.isFunction(c)&&c()}})},g.prototype._getComputedDashOffset=function(){var a=window.getComputedStyle(this.path,null);return parseFloat(a.getPropertyValue("stroke-dashoffset"),10)},g.prototype._progressToOffset=function(a){var b=this.path.getTotalLength();return b-a*b},g.prototype._resolveFromAndTo=function(a,b,c){return c.from&&c.to?{from:c.from,to:c.to}:{from:this._calculateFrom(b),to:this._calculateTo(a,b)}},g.prototype._calculateFrom=function(a){return d.interpolate(this._opts.from,this._opts.to,this.value(),a)},g.prototype._calculateTo=function(a,b){return d.interpolate(this._opts.from,this._opts.to,a,b)},g.prototype._stopTween=function(){null!==this._tweenable&&(this._tweenable.stop(),this._tweenable=null)},g.prototype._easing=function(a){return f.hasOwnProperty(a)?f[a]:a},b.exports=g},{"./utils":8,shifty:1}],6:[function(a,b,c){var d=a("./shape"),e=a("./circle"),f=a("./utils"),g=function(a,b){this._pathTemplate="M 50,50 m -{radius},0 a {radius},{radius} 0 1 1 {2radius},0",this.containerAspectRatio=2,d.apply(this,arguments)};g.prototype=new d,g.prototype.constructor=g,g.prototype._initializeSvg=function(a,b){a.setAttribute("viewBox","0 0 100 50")},g.prototype._initializeTextContainer=function(a,b,c){a.text.style&&(c.style.top="auto",c.style.bottom="0",a.text.alignToBottom?f.setStyle(c,"transform","translate(-50%, 0)"):f.setStyle(c,"transform","translate(-50%, 50%)"))},g.prototype._pathString=e.prototype._pathString,g.prototype._trailString=e.prototype._trailString,b.exports=g},{"./circle":2,"./shape":7,"./utils":8}],7:[function(a,b,c){var d=a("./path"),e=a("./utils"),f="Object is destroyed",g=function h(a,b){if(!(this instanceof h))throw new Error("Constructor was called without new keyword");if(0!==arguments.length){this._opts=e.extend({color:"#555",strokeWidth:1,trailColor:null,trailWidth:null,fill:null,text:{style:{color:null,position:"absolute",left:"50%",top:"50%",padding:0,margin:0,transform:{prefix:!0,value:"translate(-50%, -50%)"}},autoStyleContainer:!0,alignToBottom:!0,value:null,className:"progressbar-text"},svgStyle:{display:"block",width:"100%"},warnings:!1},b,!0),e.isObject(b)&&void 0!==b.svgStyle&&(this._opts.svgStyle=b.svgStyle),e.isObject(b)&&e.isObject(b.text)&&void 0!==b.text.style&&(this._opts.text.style=b.text.style);var c,f=this._createSvgView(this._opts);if(c=e.isString(a)?document.querySelector(a):a,!c)throw new Error("Container does not exist: "+a);this._container=c,this._container.appendChild(f.svg),this._opts.warnings&&this._warnContainerAspectRatio(this._container),this._opts.svgStyle&&e.setStyles(f.svg,this._opts.svgStyle),this.svg=f.svg,this.path=f.path,this.trail=f.trail,this.text=null;var g=e.extend({attachment:void 0,shape:this},this._opts);this._progressPath=new d(f.path,g),e.isObject(this._opts.text)&&null!==this._opts.text.value&&this.setText(this._opts.text.value)}};g.prototype.animate=function(a,b,c){if(null===this._progressPath)throw new Error(f);this._progressPath.animate(a,b,c)},g.prototype.stop=function(){if(null===this._progressPath)throw new Error(f);void 0!==this._progressPath&&this._progressPath.stop()},g.prototype.destroy=function(){if(null===this._progressPath)throw new Error(f);this.stop(),this.svg.parentNode.removeChild(this.svg),this.svg=null,this.path=null,this.trail=null,this._progressPath=null,null!==this.text&&(this.text.parentNode.removeChild(this.text),this.text=null)},g.prototype.set=function(a){if(null===this._progressPath)throw new Error(f);this._progressPath.set(a)},g.prototype.value=function(){if(null===this._progressPath)throw new Error(f);return void 0===this._progressPath?0:this._progressPath.value()},g.prototype.setText=function(a){if(null===this._progressPath)throw new Error(f);null===this.text&&(this.text=this._createTextContainer(this._opts,this._container),this._container.appendChild(this.text)),e.isObject(a)?(e.removeChildren(this.text),this.text.appendChild(a)):this.text.innerHTML=a},g.prototype._createSvgView=function(a){var b=document.createElementNS("http://www.w3.org/2000/svg","svg");this._initializeSvg(b,a);var c=null;(a.trailColor||a.trailWidth)&&(c=this._createTrail(a),b.appendChild(c));var d=this._createPath(a);return b.appendChild(d),{svg:b,path:d,trail:c}},g.prototype._initializeSvg=function(a,b){a.setAttribute("viewBox","0 0 100 100")},g.prototype._createPath=function(a){var b=this._pathString(a);return this._createPathElement(b,a)},g.prototype._createTrail=function(a){var b=this._trailString(a),c=e.extend({},a);return c.trailColor||(c.trailColor="#eee"),c.trailWidth||(c.trailWidth=c.strokeWidth),c.color=c.trailColor,c.strokeWidth=c.trailWidth,c.fill=null,this._createPathElement(b,c)},g.prototype._createPathElement=function(a,b){var c=document.createElementNS("http://www.w3.org/2000/svg","path");return c.setAttribute("d",a),c.setAttribute("stroke",b.color),c.setAttribute("stroke-width",b.strokeWidth),b.fill?c.setAttribute("fill",b.fill):c.setAttribute("fill-opacity","0"),c},g.prototype._createTextContainer=function(a,b){var c=document.createElement("div");c.className=a.text.className;var d=a.text.style;return d&&(a.text.autoStyleContainer&&(b.style.position="relative"),e.setStyles(c,d),d.color||(c.style.color=a.color)),this._initializeTextContainer(a,b,c),c},g.prototype._initializeTextContainer=function(a,b,c){},g.prototype._pathString=function(a){throw new Error("Override this function for each progress bar")},g.prototype._trailString=function(a){throw new Error("Override this function for each progress bar")},g.prototype._warnContainerAspectRatio=function(a){if(this.containerAspectRatio){var b=window.getComputedStyle(a,null),c=parseFloat(b.getPropertyValue("width"),10),d=parseFloat(b.getPropertyValue("height"),10);e.floatEquals(this.containerAspectRatio,c/d)||(console.warn("Incorrect aspect ratio of container","#"+a.id,"detected:",b.getPropertyValue("width")+"(width)","/",b.getPropertyValue("height")+"(height)","=",c/d),console.warn("Aspect ratio of should be",this.containerAspectRatio))}},b.exports=g},{"./path":5,"./utils":8}],8:[function(a,b,c){function d(a,b,c){a=a||{},b=b||{},c=c||!1;for(var e in b)if(b.hasOwnProperty(e)){var f=a[e],g=b[e];c&&l(f)&&l(g)?a[e]=d(f,g,c):a[e]=g}return a}function e(a,b){var c=a;for(var d in b)if(b.hasOwnProperty(d)){var e=b[d],f="\\{"+d+"\\}",g=new RegExp(f,"g");c=c.replace(g,e)}return c}function f(a,b,c){for(var d=a.style,e=0;e2?arguments[2]:{},a=n(e);i&&(a=a.concat(Object.getOwnPropertySymbols(e))),o(a,function(n){l(t,n,e[n],r[n])})};f.supportsDescriptors=!!s,t.exports=f},function(t,e){"use strict";var r=Function.prototype.toString,n=/^\s*class /,o=function(t){try{var e=r.call(t),o=e.replace(/\/\/.*\n/g,""),i=o.replace(/\/\*[.\s\S]*\*\//g,""),a=i.replace(/\n/gm," ").replace(/ {2}/g," ");return n.test(a)}catch(u){return!1}},i=function(t){try{return o(t)?!1:(r.call(t),!0)}catch(e){return!1}},a=Object.prototype.toString,u="[object Function]",c="[object GeneratorFunction]",s="function"==typeof Symbol&&"symbol"==typeof Symbol.toStringTag;t.exports=function(t){if(!t)return!1;if("function"!=typeof t&&"object"!=typeof t)return!1;if(s)return i(t);if(o(t))return!1;var e=a.call(t);return e===u||e===c}},function(t,e){"use strict";function r(t){if(null===t||void 0===t)throw new TypeError("Object.assign cannot be called with null or undefined");return Object(t)}function n(){try{if(!Object.assign)return!1;var t=new String("abc");if(t[5]="de","5"===Object.getOwnPropertyNames(t)[0])return!1;for(var e={},r=0;10>r;r++)e["_"+String.fromCharCode(r)]=r;var n=Object.getOwnPropertyNames(e).map(function(t){return e[t]});if("0123456789"!==n.join(""))return!1;var o={};return"abcdefghijklmnopqrst".split("").forEach(function(t){o[t]=t}),"abcdefghijklmnopqrst"===Object.keys(Object.assign({},o)).join("")}catch(i){return!1}}var o=Object.prototype.hasOwnProperty,i=Object.prototype.propertyIsEnumerable;t.exports=n()?Object.assign:function(t,e){for(var n,a,u=r(t),c=1;c1){if(i=arguments[1],!n.IsCallable(i))throw new TypeError("When provided, the second argument to `Array.from` must be a function");arguments.length>2&&(a=arguments[2])}for(var c,s,l=n.ToLength(u.length),f=n.IsCallable(r)?n.ToObject(new r(l)):new Array(l),b=0;l>b;)c=u[b],s=i?"undefined"==typeof a?i(c,b):n.Call(i,a,[c,b]):c,e(f,b,{value:s,configurable:!0,enumerable:!0,writable:!0}),b+=1;return f.length=l,f}},function(t,e,r){"use strict";var n=Object.prototype.toString,o="function"==typeof Symbol&&"symbol"==typeof Symbol.iterator,i=o?Symbol.prototype.toString:n,a=r(7),u=r(6),c=Number.MAX_SAFE_INTEGER||Math.pow(2,53)-1,s=r(18),l=r(9),f=r(8),b=r(19),y=r(21),p=parseInt,d=r(25),M=d.call(Function.call,String.prototype.slice),g=d.call(Function.call,RegExp.prototype.test,/^0b[01]+$/i),h=d.call(Function.call,RegExp.prototype.test,/^0o[0-7]+$/i),m=["…","​","￾"].join(""),N=new RegExp("["+m+"]","g"),j=d.call(Function.call,RegExp.prototype.test,N),w=/^[\-\+]0x[0-9a-f]+$/i,x=d.call(Function.call,RegExp.prototype.test,w),L=[" \n\x0B\f\r  ᠎    ","          \u2028","\u2029\ufeff"].join(""),T=new RegExp("(^["+L+"]+)|(["+L+"]+$)","g"),v=d.call(Function.call,String.prototype.replace),E=function(t){return v(t,T,"")},D=r(17),S=r(26),z=s(s({},D),{Call:function(t,e){var r=arguments.length>2?arguments[2]:[];if(!this.IsCallable(t))throw new TypeError(t+" is not a function");return t.apply(e,r)},ToPrimitive:y,ToNumber:function(t){var e=b(t)?t:y(t,"number");if("symbol"==typeof e)throw new TypeError("Cannot convert a Symbol value to a number");if("string"==typeof e){if(g(e))return this.ToNumber(p(M(e,2),2));if(h(e))return this.ToNumber(p(M(e,2),8));if(j(e)||x(e))return NaN;var r=E(e);if(r!==e)return this.ToNumber(r)}return Number(e)},ToInt16:function(t){var e=this.ToUint16(t);return e>=32768?e-65536:e},ToInt8:function(t){var e=this.ToUint8(t);return e>=128?e-256:e},ToUint8:function(t){var e=this.ToNumber(t);if(a(e)||0===e||!u(e))return 0;var r=l(e)*Math.floor(Math.abs(e));return f(r,256)},ToUint8Clamp:function(t){var e=this.ToNumber(t);if(a(e)||0>=e)return 0;if(e>=255)return 255;var r=Math.floor(t);return e>r+.5?r+1:r+.5>e?r:r%2!==0?r+1:r},ToString:function(t){if("symbol"==typeof t)throw new TypeError("Cannot convert a Symbol value to a string");return String(t)},ToObject:function(t){return this.RequireObjectCoercible(t),Object(t)},ToPropertyKey:function(t){var e=this.ToPrimitive(t,String);return"symbol"==typeof e?i.call(e):this.ToString(e)},ToLength:function(t){var e=this.ToInteger(t);return 0>=e?0:e>c?c:e},CanonicalNumericIndexString:function(t){if("[object String]"!==n.call(t))throw new TypeError("must be a string");if("-0"===t)return-0;var e=this.ToNumber(t);return this.SameValue(this.ToString(e),t)?e:void 0},RequireObjectCoercible:D.CheckObjectCoercible,IsArray:Array.isArray||function(t){return"[object Array]"===n.call(t)},IsConstructor:function(t){return this.IsCallable(t)},IsExtensible:function(t){return Object.preventExtensions?b(t)?!1:Object.isExtensible(t):!0},IsInteger:function(t){if("number"!=typeof t||a(t)||!u(t))return!1;var e=Math.abs(t);return Math.floor(e)===e},IsPropertyKey:function(t){return"string"==typeof t||"symbol"==typeof t},IsRegExp:function(t){if(!t||"object"!=typeof t)return!1;if(o){var e=t[Symbol.match];if("undefined"!=typeof e)return D.ToBoolean(e)}return S(t)},SameValueZero:function(t,e){return t===e||a(t)&&a(e)}});delete z.CheckObjectCoercible,t.exports=z},function(t,e){var r=Number.isNaN||function(t){return t!==t};t.exports=Number.isFinite||function(t){return"number"==typeof t&&!r(t)&&t!==1/0&&t!==-(1/0)}},function(t,e){t.exports=Number.isNaN||function(t){return t!==t}},function(t,e){t.exports=function(t,e){var r=t%e;return Math.floor(r>=0?r:r+e)}},function(t,e){t.exports=function(t){return t>=0?1:-1}},function(t,e){t.exports=function(t){return null===t||"function"!=typeof t&&"object"!=typeof t}},function(t,e,r){"use strict";var n=r(5),o=r(4),i=function(t){try{return t(),!0}catch(e){return!1}};t.exports=function(){var t=n.IsCallable(Array.from)&&i(function(){Array.from({length:-(1/0)})})&&!i(function(){Array.from([],void 0)});return t?Array.from:o}},function(t,e){"use strict";function r(t,e){var r=e.bubbles,n=void 0===r?!1:r,o=e.cancelable,i=void 0===o?!1:o,a=e.detail,u=void 0===a?void 0:a,c=void 0;try{c=new window.CustomEvent(t,{bubbles:n,cancelable:i,detail:u})}catch(s){c=document.createEvent("CustomEvent"),c.initCustomEvent(t,n,i,u)}return c}Object.defineProperty(e,"__esModule",{value:!0}),e["default"]=r,r.prototype=window.Event.prototype},function(t,e,r){"use strict";var n=r(1),o=r(4),i=r(11),a=r(27),u=function(t){return o.call(Array,arguments)};n(u,{implementation:o,getPolyfill:i,shim:a}),t.exports=u},function(t,e){var r=Object.prototype.hasOwnProperty,n=Object.prototype.toString;t.exports=function(t,e,o){if("[object Function]"!==n.call(e))throw new TypeError("iterator must be a function");var i=t.length;if(i===+i)for(var a=0;i>a;a++)e.call(o,t[a],a,t);else for(var u in t)r.call(t,u)&&e.call(o,t[u],u,t)}},function(t,e,r){"use strict";var n=Object.prototype.hasOwnProperty,o=Object.prototype.toString,i=Array.prototype.slice,a=r(16),u=!{toString:null}.propertyIsEnumerable("toString"),c=function(){}.propertyIsEnumerable("prototype"),s=["toString","toLocaleString","valueOf","hasOwnProperty","isPrototypeOf","propertyIsEnumerable","constructor"],l=function(t){var e=t.constructor;return e&&e.prototype===t},f={$console:!0,$frame:!0,$frameElement:!0,$frames:!0,$parent:!0,$self:!0,$webkitIndexedDB:!0,$webkitStorageInfo:!0,$window:!0},b=function(){if("undefined"==typeof window)return!1;for(var t in window)try{if(!f["$"+t]&&n.call(window,t)&&null!==window[t]&&"object"==typeof window[t])try{l(window[t])}catch(e){return!0}}catch(e){return!0}return!1}(),y=function(t){if("undefined"==typeof window||!b)return l(t);try{return l(t)}catch(e){return!1}},p=function(t){var e=null!==t&&"object"==typeof t,r="[object Function]"===o.call(t),i=a(t),l=e&&"[object String]"===o.call(t),f=[];if(!e&&!r&&!i)throw new TypeError("Object.keys called on a non-object");var b=c&&r;if(l&&t.length>0&&!n.call(t,0))for(var p=0;p0)for(var d=0;d=0&&"[object Function]"===r.call(t.callee)),n}},function(t,e,r){"use strict";var n=r(7),o=r(6),i=r(9),a=r(8),u=r(2),c=r(20),s={ToPrimitive:c,ToBoolean:function(t){return Boolean(t)},ToNumber:function(t){return Number(t)},ToInteger:function(t){var e=this.ToNumber(t);return n(e)?0:0!==e&&o(e)?i(e)*Math.floor(Math.abs(e)):e},ToInt32:function(t){return this.ToNumber(t)>>0},ToUint32:function(t){return this.ToNumber(t)>>>0},ToUint16:function(t){var e=this.ToNumber(t);if(n(e)||0===e||!o(e))return 0;var r=i(e)*Math.floor(Math.abs(e));return a(r,65536)},ToString:function(t){return String(t)},ToObject:function(t){return this.CheckObjectCoercible(t),Object(t)},CheckObjectCoercible:function(t,e){if(null==t)throw new TypeError(e||"Cannot call method on "+t);return t},IsCallable:u,SameValue:function(t,e){return t===e?0===t?1/t===1/e:!0:n(t)&&n(e)}};t.exports=s},function(t,e){var r=Object.prototype.hasOwnProperty;t.exports=Object.assign||function(t,e){for(var n in e)r.call(e,n)&&(t[n]=e[n]);return t}},function(t,e){t.exports=function(t){return null===t||"function"!=typeof t&&"object"!=typeof t}},function(t,e,r){"use strict";var n=Object.prototype.toString,o=r(10),i=r(2),a={"[[DefaultValue]]":function(t,e){var r=e||("[object Date]"===n.call(t)?String:Number);if(r===String||r===Number){var a,u,c=r===String?["toString","valueOf"]:["valueOf","toString"];for(u=0;u1&&(e===String?r="string":e===Number&&(r="number"));var i;if(n&&(Symbol.toPrimitive?i=s(t,Symbol.toPrimitive):u(t)&&(i=Symbol.prototype.valueOf)),"undefined"!=typeof i){var l=i.call(t,r);if(o(l))return l;throw new TypeError("unable to convert exotic object to primitive")}return"default"===r&&(a(t)||u(t))&&(r="string"),c(t,"default"===r?"number":r)}},function(t,e){"use strict";var r=Date.prototype.getDay,n=function(t){try{return r.call(t),!0}catch(e){return!1}},o=Object.prototype.toString,i="[object Date]",a="function"==typeof Symbol&&"symbol"==typeof Symbol.toStringTag;t.exports=function(t){return"object"!=typeof t||null===t?!1:a?n(t):o.call(t)===i}},function(t,e){"use strict";var r=Object.prototype.toString,n="function"==typeof Symbol&&"symbol"==typeof Symbol();if(n){var o=Symbol.prototype.toString,i=/^Symbol\(.*\)$/,a=function(t){return"symbol"!=typeof t.valueOf()?!1:i.test(o.call(t))};t.exports=function(t){if("symbol"==typeof t)return!0;if("[object Symbol]"!==r.call(t))return!1;try{return a(t)}catch(e){return!1}}}else t.exports=function(t){return!1}},function(t,e){var r="Function.prototype.bind called on incompatible ",n=Array.prototype.slice,o=Object.prototype.toString,i="[object Function]";t.exports=function(t){var e=this;if("function"!=typeof e||o.call(e)!==i)throw new TypeError(r+e);for(var a,u=n.call(arguments,1),c=function(){if(this instanceof a){var r=e.apply(this,u.concat(n.call(arguments)));return Object(r)===r?r:this}return e.apply(t,u.concat(n.call(arguments)))},s=Math.max(0,e.length-u.length),l=[],f=0;s>f;f++)l.push("$"+f);if(a=Function("binder","return function ("+l.join(",")+"){ return binder.apply(this,arguments); }")(c),e.prototype){var b=function(){};b.prototype=e.prototype,a.prototype=new b,b.prototype=null}return a}},function(t,e,r){var n=r(24);t.exports=Function.prototype.bind||n},function(t,e){"use strict";var r=RegExp.prototype.exec,n=function(t){try{return r.call(t),!0}catch(e){return!1}},o=Object.prototype.toString,i="[object RegExp]",a="function"==typeof Symbol&&"symbol"==typeof Symbol.toStringTag;t.exports=function(t){return"object"!=typeof t?!1:a?n(t):o.call(t)===i}},function(t,e,r){"use strict";var n=r(1),o=r(11);t.exports=function(){var t=o();return n(Array,{from:t},{from:function(){return Array.from!==t}}),t}},function(t,e){"use strict";function r(t){if("function"==typeof Symbol)return Symbol(t);var e=Math.random().toString(36).substr(2,8);return t+e}Object.defineProperty(e,"__esModule",{value:!0}),e["default"]=r},function(t,e,r){"use strict";function n(t){if(t&&t.__esModule)return t;var e={};if(null!=t)for(var r in t)Object.prototype.hasOwnProperty.call(t,r)&&(e[r]=t[r]);return e["default"]=t,e}function o(t){return t&&t.__esModule?t:{"default":t}}function i(t,e){if(!(t instanceof e))throw new TypeError("Cannot call a class as a function")}Object.defineProperty(e,"__esModule",{value:!0});var a=function(){function t(t,e){for(var r=0;r1?r-1:0),o=1;r>o;o++)n[o-1]=arguments[o];e.call.apply(e,[this,t].concat(n))}};return m.on(t,"click keydown",r),function(){m.off(t,"click keydown",r)}},E=function(){var t=new d["default"],e=t.createElement("style");return e.innerHTML=g["default"],m.prependElementTo(e,(0,m["default"])("head")[0]),t},D=function(t){var e=new d["default"],r=t.targetElement;return r.forEach(function(r){var n=x(t,r);if(n.room){var o=e.createElement("iframe");o.setAttribute("frameborder","0"),o.src=""+t.host+n.room+"/~embed",r.appendChild(o)}else console.error("Gitter Sidecar: No room specified for targetElement",r)}),e},S=function(t){var e=t.options,r=new d["default"];return e.targetElement.forEach(function(n){var o=r.createElement("div");o.classList.add("gitter-chat-embed-action-bar"),n.insertBefore(o,n.firstChild);var i=r.createElement("a");i.classList.add("gitter-chat-embed-action-bar-item"),i.classList.add("gitter-chat-embed-action-bar-item-pop-out"),i.setAttribute("aria-label","Open Chat in Gitter.im"),i.setAttribute("href",""+e.host+e.room),i.setAttribute("target","_blank"),o.appendChild(i);var a=r.createElement("button");a.classList.add("gitter-chat-embed-action-bar-item"),a.classList.add("gitter-chat-embed-action-bar-item-collapse-chat"),a.setAttribute("aria-label","Collapse Gitter Chat"),v(a,function(e){t.toggleChat(!1),e.preventDefault()}),o.appendChild(a)}),r},z=document.body||document.documentElement,O={room:void 0,targetElement:void 0,activationElement:void 0,showChatByDefault:!1,preload:!1,useStyles:!0,layout:"fixed",host:"https://gitter.im/"},A=(0,f["default"])("DEFAULTS"),I=(0,f["default"])("OPTIONS"),C=(0,f["default"])("ELEMENTSTORE"),k=(0,f["default"])("EVENTHANDLESTORE"),U=(0,f["default"])("INIT"),_=(0,f["default"])("ISEMBEDDED"),Y=(0,f["default"])("EMBEDCHATONCE"),Q=(0,f["default"])("TOGGLETARGETELEMENTS"),P=function(){function t(){var e=arguments.length<=0||void 0===arguments[0]?{}:arguments[0];i(this,t),this[C]=new d["default"],this[k]=[],this[A]=(0,s["default"])({},O),this[I]=(0,s["default"])({},this[A],e),this[U]()}return a(t,[{key:U,value:function(){var t=this,e=this[I];e.useStyles&&this[C].add(E()),e.targetElement=(0,m["default"])(e.targetElement||function(){var e=t[C].createElement("aside");return e.classList.add("gitter-chat-embed"),e.classList.add("is-collapsed"),z.appendChild(e),e}()),e.targetElement.forEach(function(e){var r=t[C].createElement("div");r.classList.add("gitter-chat-embed-loading-wrapper"),r.innerHTML='\n
\n ',e.insertBefore(r,e.firstChild)}),S(this),e.preload&&this.toggleChat(!1),e.showChatByDefault?this.toggleChat(!0):(void 0===e.activationElement||e.activationElement===!0?e.activationElement=(0,m["default"])(function(){var r=t[C].createElement("a");return r.href=""+e.host+e.room,r.innerHTML="Open Chat",r.classList.add("gitter-open-chat-button"),z.appendChild(r),r}()):e.activationElement&&(e.activationElement=(0,m["default"])(e.activationElement)),e.activationElement&&(v(e.activationElement,function(e){t.toggleChat(!0),e.preventDefault()}),e.targetElement.forEach(function(t){m.on(t,"gitter-chat-toggle",function(t){var r=t.detail.state;e.activationElement.forEach(function(t){m.toggleClass(t,"is-collapsed",r)})})})));var r=v((0,m["default"])(".js-gitter-toggle-chat-button"),function(e){var r=w(e.target.getAttribute("data-gitter-toggle-chat-state"));t.toggleChat(null!==r?r:"toggle"),e.preventDefault()});this[k].push(r),e.targetElement.forEach(function(e){var r=new y["default"]("gitter-chat-started",{detail:{chat:t}});e.dispatchEvent(r)});var n=new y["default"]("gitter-sidecar-instance-started",{detail:{chat:this}});document.dispatchEvent(n)}},{key:Y,value:function(){if(!this[_]){var t=this[I],e=D(t);this[C].add(e)}this[_]=!0}},{key:Q,value:function(t){var e=this[I];e.targetElement||console.warn("Gitter Sidecar: No chat embed elements to toggle visibility on");var r=e.targetElement;r.forEach(function(e){"toggle"===t?m.toggleClass(e,"is-collapsed"):m.toggleClass(e,"is-collapsed",!t);var r=new y["default"]("gitter-chat-toggle",{detail:{state:t}});e.dispatchEvent(r)})}},{key:"toggleChat",value:function(t){var e=this,r=this[I];t&&!this[_]?!function(){var n=r.targetElement;n.forEach(function(t){t.classList.add("is-loading")}),setTimeout(function(){e[Y](),e[Q](t),n.forEach(function(t){t.classList.remove("is-loading")})},300)}():(this[Y](),this[Q](t))}},{key:"destroy",value:function(){this[k].forEach(function(t){t()}),this[C].destroy()}},{key:"options",get:function(){return(0,j["default"])(this[I])}}]),t}();e["default"]=P},function(t,e){"use strict";function r(t){if(Array.isArray(t)){for(var e=0,r=Array(t.length);er;r++)e[r]=arguments[r];return e.reduce(function(t,e){return!e||void 0===e.length||Array.isArray(e)||window&&(!window||e instanceof window.constructor)||(e=Array.prototype.slice.call(e)),t.concat(e)},[])},s=function(){for(var t=arguments.length,e=Array(t),n=0;t>n;n++)e[n]=arguments[n];var o=e;if("string"==typeof e[0]){var i;o=(i=document.querySelectorAll).call.apply(i,[document].concat(e))}return c.apply(void 0,r(o))},l=function(){return s.apply(void 0,arguments)};e["default"]=l},function(t,e,r){"use strict";function n(t){return t&&t.__esModule?t:{"default":t}}function o(t,e){if(!(t instanceof e))throw new TypeError("Cannot call a class as a function")}Object.defineProperty(e,"__esModule",{value:!0});var i=function(){function t(t,e){for(var r=0;rr;r++)e[r]=arguments[r];var n=document.createElement.apply(document,e);return this.add(n),n}},{key:"add",value:function(){for(var e=arguments.length,r=Array(e),n=0;e>n;n++)r[n]=arguments[n];var o=[].concat(r).reduce(function(e,r){return r?r instanceof t?e.concat(r.elements):e.concat(r):e},[]);this.elements=this.elements.concat(o)}},{key:"destroy",value:function(){this.elements.forEach(function(t){return u(t)}),this.elements=[]}}]),t}();e["default"]=c},function(t,e){"use strict";function r(t){var e={};return Object.keys(t).forEach(function(r){Object.defineProperty(e,r,{value:t[r],writable:!1,configurable:!1})}),e}Object.defineProperty(e,"__esModule",{value:!0}),e["default"]=r},function(t,e,r){e=t.exports=r(34)(),e.push([t.id,".gitter-hidden{box-sizing:border-box;display:none}.gitter-icon{box-sizing:border-box;width:22px;height:22px;fill:currentColor}.gitter-chat-embed{box-sizing:border-box;z-index:100;position:fixed;top:0;left:60%;bottom:0;right:0;display:-webkit-box;display:-ms-flexbox;display:flex;-webkit-box-orient:horizontal;-webkit-box-direction:normal;-ms-flex-direction:row;flex-direction:row;background-color:#fff;border-left:1px solid #333;box-shadow:-12px 0 18px 0 rgba(50,50,50,.3);-webkit-transition:-webkit-transform .3s cubic-bezier(.16,.22,.22,1.7);transition:-webkit-transform .3s cubic-bezier(.16,.22,.22,1.7);transition:transform .3s cubic-bezier(.16,.22,.22,1.7);transition:transform .3s cubic-bezier(.16,.22,.22,1.7),-webkit-transform .3s cubic-bezier(.16,.22,.22,1.7)}@context border-box{.gitter-chat-embed{box-sizing:border-box;background-color:#fff}}.gitter-chat-embed.is-collapsed:not(.is-loading){box-sizing:border-box;-webkit-transform:translateX(110%);transform:translateX(110%)}.gitter-chat-embed:after{box-sizing:border-box;content:'';z-index:-1;position:absolute;top:0;left:100%;bottom:0;right:-100%;background-color:#fff}@context border-box{.gitter-chat-embed:after{box-sizing:border-box;background-color:#fff}}@media(max-width:1150px){.gitter-chat-embed{box-sizing:border-box;left:45%}}@media(max-width:944px){.gitter-chat-embed{box-sizing:border-box;left:30%}}@media(max-width:600px){.gitter-chat-embed{box-sizing:border-box;left:15%}}@media(max-width:500px){.gitter-chat-embed{box-sizing:border-box;left:0;border-left:none}}.gitter-chat-embed>iframe{box-sizing:border-box;-webkit-box-flex:1;-ms-flex:1;flex:1;width:100%;height:100%;border:0}.gitter-chat-embed-loading-wrapper{box-sizing:border-box;position:absolute;top:0;left:0;bottom:0;right:0;display:none;-webkit-box-pack:center;-ms-flex-pack:center;justify-content:center;-webkit-box-align:center;-ms-flex-align:center;-ms-grid-row-align:center;align-items:center}.is-loading .gitter-chat-embed-loading-wrapper{box-sizing:border-box;display:-webkit-box;display:-ms-flexbox;display:flex}.gitter-chat-embed-loading-indicator{box-sizing:border-box;opacity:.75;background-image:url(data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHZpZXdCb3g9IjAgMCAxNzkyIDE3OTIiIGZpbGw9IiMzYTMxMzMiPjxwYXRoIGQ9Ik01MjYgMTM5NHEwIDUzLTM3LjUgOTAuNXQtOTAuNSAzNy41cS01MiAwLTkwLTM4dC0zOC05MHEwLTUzIDM3LjUtOTAuNXQ5MC41LTM3LjUgOTAuNSAzNy41IDM3LjUgOTAuNXptNDk4IDIwNnEwIDUzLTM3LjUgOTAuNXQtOTAuNSAzNy41LTkwLjUtMzcuNS0zNy41LTkwLjUgMzcuNS05MC41IDkwLjUtMzcuNSA5MC41IDM3LjUgMzcuNSA5MC41em0tNzA0LTcwNHEwIDUzLTM3LjUgOTAuNXQtOTAuNSAzNy41LTkwLjUtMzcuNS0zNy41LTkwLjUgMzcuNS05MC41IDkwLjUtMzcuNSA5MC41IDM3LjUgMzcuNSA5MC41em0xMjAyIDQ5OHEwIDUyLTM4IDkwdC05MCAzOHEtNTMgMC05MC41LTM3LjV0LTM3LjUtOTAuNSAzNy41LTkwLjUgOTAuNS0zNy41IDkwLjUgMzcuNSAzNy41IDkwLjV6bS05NjQtOTk2cTAgNjYtNDcgMTEzdC0xMTMgNDctMTEzLTQ3LTQ3LTExMyA0Ny0xMTMgMTEzLTQ3IDExMyA0NyA0NyAxMTN6bTExNzAgNDk4cTAgNTMtMzcuNSA5MC41dC05MC41IDM3LjUtOTAuNS0zNy41LTM3LjUtOTAuNSAzNy41LTkwLjUgOTAuNS0zNy41IDkwLjUgMzcuNSAzNy41IDkwLjV6bS02NDAtNzA0cTAgODAtNTYgMTM2dC0xMzYgNTYtMTM2LTU2LTU2LTEzNiA1Ni0xMzYgMTM2LTU2IDEzNiA1NiA1NiAxMzZ6bTUzMCAyMDZxMCA5My02NiAxNTguNXQtMTU4IDY1LjVxLTkzIDAtMTU4LjUtNjUuNXQtNjUuNS0xNTguNXEwLTkyIDY1LjUtMTU4dDE1OC41LTY2cTkyIDAgMTU4IDY2dDY2IDE1OHoiLz48L3N2Zz4=);-webkit-animation:spin 2s infinite linear;animation:spin 2s infinite linear}@-webkit-keyframes spin{0%{box-sizing:border-box;-webkit-transform:rotate(0deg);transform:rotate(0deg)}to{box-sizing:border-box;-webkit-transform:rotate(359.9deg);transform:rotate(359.9deg)}}@keyframes spin{0%{box-sizing:border-box;-webkit-transform:rotate(0deg);transform:rotate(0deg)}to{box-sizing:border-box;-webkit-transform:rotate(359.9deg);transform:rotate(359.9deg)}}.gitter-chat-embed-action-bar{position:absolute;top:0;left:0;right:0;-webkit-box-pack:end;-ms-flex-pack:end;justify-content:flex-end;padding-bottom:.7em;background:-webkit-linear-gradient(top,#fff,#fff 50%,hsla(0,0%,100%,0));background:linear-gradient(180deg,#fff 0,#fff 50%,hsla(0,0%,100%,0))}.gitter-chat-embed-action-bar,.gitter-chat-embed-action-bar-item{box-sizing:border-box;display:-webkit-box;display:-ms-flexbox;display:flex}.gitter-chat-embed-action-bar-item{-webkit-box-pack:center;-ms-flex-pack:center;justify-content:center;-webkit-box-align:center;-ms-flex-align:center;align-items:center;width:40px;height:40px;padding-left:0;padding-right:0;opacity:.65;background:none;background-position:50%;background-repeat:no-repeat;background-size:22px 22px;border:0;outline:none;cursor:pointer;cursor:hand;-webkit-transition:all .2s ease;transition:all .2s ease}.gitter-chat-embed-action-bar-item:focus,.gitter-chat-embed-action-bar-item:hover{box-sizing:border-box;opacity:1}.gitter-chat-embed-action-bar-item:active{box-sizing:border-box;-webkit-filter:hue-rotate(80deg) saturate(150);filter:hue-rotate(80deg) saturate(150)}.gitter-chat-embed-action-bar-item-pop-out{box-sizing:border-box;margin-right:-4px;background-image:url(data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHZpZXdCb3g9IjAgMCAyMDAgMTcxLjQyOSIgZmlsbD0iIzNhMzEzMyI+PHBhdGggZD0iTTE1Ny4xNDMsMTAzLjU3MXYzNS43MTRjMCw4Ljg1NC0zLjE0NCwxNi40MjYtOS40MzEsMjIuNzEzcy0xMy44NTgsOS40MzEtMjIuNzEyLDkuNDMxSDMyLjE0MyBjLTguODU0LDAtMTYuNDI1LTMuMTQ0LTIyLjcxMi05LjQzMVMwLDE0OC4xNCwwLDEzOS4yODVWNDYuNDI5YzAtOC44NTQsMy4xNDQtMTYuNDI1LDkuNDMxLTIyLjcxMiBjNi4yODctNi4yODcsMTMuODU4LTkuNDMxLDIyLjcxMi05LjQzMWg3OC41NzJjMS4wNDEsMCwxLjg5NiwwLjMzNSwyLjU2NiwxLjAwNGMwLjY3LDAuNjcsMS4wMDQsMS41MjUsMS4wMDQsMi41NjdWMjUgYzAsMS4wNDItMC4zMzQsMS44OTctMS4wMDQsMi41NjdjLTAuNjcsMC42Ny0xLjUyNSwxLjAwNC0yLjU2NiwxLjAwNEgzMi4xNDNjLTQuOTExLDAtOS4xMTUsMS43NDktMTIuNjEyLDUuMjQ2IHMtNS4yNDYsNy43MDEtNS4yNDYsMTIuNjEydjkyLjg1NmMwLDQuOTExLDEuNzQ5LDkuMTE1LDUuMjQ2LDEyLjYxMnM3LjcwMSw1LjI0NSwxMi42MTIsNS4yNDVIMTI1YzQuOTEsMCw5LjExNS0xLjc0OCwxMi42MTEtNS4yNDUgYzMuNDk3LTMuNDk3LDUuMjQ2LTcuNzAxLDUuMjQ2LTEyLjYxMnYtMzUuNzE0YzAtMS4wNDIsMC4zMzQtMS44OTcsMS4wMDQtMi41NjdjMC42Ny0wLjY2OSwxLjUyNS0xLjAwNCwyLjU2Ny0xLjAwNGg3LjE0MyBjMS4wNDIsMCwxLjg5NywwLjMzNSwyLjU2NywxLjAwNEMxNTYuODA5LDEwMS42NzQsMTU3LjE0MywxMDIuNTI5LDE1Ny4xNDMsMTAzLjU3MXogTTIwMCw3LjE0M3Y1Ny4xNDMgYzAsMS45MzUtMC43MDcsMy42MDktMi4xMjEsNS4wMjJjLTEuNDEzLDEuNDE0LTMuMDg4LDIuMTIxLTUuMDIxLDIuMTIxYy0xLjkzNSwwLTMuNjA5LTAuNzA3LTUuMDIyLTIuMTIxbC0xOS42NDQtMTkuNjQzIGwtNzIuNzY3LDcyLjc2OWMtMC43NDQsMC43NDQtMS42LDEuMTE1LTIuNTY3LDEuMTE1cy0xLjgyMy0wLjM3MS0yLjU2Ny0xLjExNUw3Ny41NjcsMTA5LjcxYy0wLjc0NC0wLjc0NC0xLjExNi0xLjYtMS4xMTYtMi41NjcgYzAtMC45NjcsMC4zNzItMS44MjIsMS4xMTYtMi41NjZsNzIuNzY4LTcyLjc2OGwtMTkuNjQ0LTE5LjY0M2MtMS40MTMtMS40MTQtMi4xMi0zLjA4OC0yLjEyLTUuMDIyYzAtMS45MzUsMC43MDctMy42MDksMi4xMi01LjAyMiBDMTMyLjEwNSwwLjcwNywxMzMuNzc5LDAsMTM1LjcxNSwwaDU3LjE0M2MxLjkzNCwwLDMuNjA4LDAuNzA3LDUuMDIxLDIuMTIxQzE5OS4yOTMsMy41MzQsMjAwLDUuMjA4LDIwMCw3LjE0M3oiLz48L3N2Zz4=)}.gitter-chat-embed-action-bar-item-collapse-chat{box-sizing:border-box;background-image:url(data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHZpZXdCb3g9IjAgMCAxNzEuNDI5IDE3MS40MjkiIGZpbGw9IiMzYTMxMzMiPjxwYXRoIGQ9Ik0xMjIuNDMzLDEwNi4xMzhsLTE2LjI5NSwxNi4yOTVjLTAuNzQ0LDAuNzQ0LTEuNiwxLjExNi0yLjU2NiwxLjExNmMtMC45NjgsMC0xLjgyMy0wLjM3Mi0yLjU2Ny0xLjExNmwtMTUuMjktMTUuMjkgbC0xNS4yOSwxNS4yOWMtMC43NDQsMC43NDQtMS42LDEuMTE2LTIuNTY3LDEuMTE2cy0xLjgyMy0wLjM3Mi0yLjU2Ny0xLjExNmwtMTYuMjk0LTE2LjI5NWMtMC43NDQtMC43NDQtMS4xMTYtMS42LTEuMTE2LTIuNTY2IGMwLTAuOTY4LDAuMzcyLTEuODIzLDEuMTE2LTIuNTY3bDE1LjI5LTE1LjI5bC0xNS4yOS0xNS4yOWMtMC43NDQtMC43NDQtMS4xMTYtMS42LTEuMTE2LTIuNTY3czAuMzcyLTEuODIzLDEuMTE2LTIuNTY3IEw2NS4yOSw0OC45OTZjMC43NDQtMC43NDQsMS42LTEuMTE2LDIuNTY3LTEuMTE2czEuODIzLDAuMzcyLDIuNTY3LDEuMTE2bDE1LjI5LDE1LjI5bDE1LjI5LTE1LjI5IGMwLjc0NC0wLjc0NCwxLjYtMS4xMTYsMi41NjctMS4xMTZjMC45NjcsMCwxLjgyMiwwLjM3MiwyLjU2NiwxLjExNmwxNi4yOTUsMTYuMjk0YzAuNzQ0LDAuNzQ0LDEuMTE2LDEuNiwxLjExNiwyLjU2NyBzLTAuMzcyLDEuODIzLTEuMTE2LDIuNTY3bC0xNS4yOSwxNS4yOWwxNS4yOSwxNS4yOWMwLjc0NCwwLjc0NCwxLjExNiwxLjYsMS4xMTYsMi41NjcgQzEyMy41NDksMTA0LjUzOSwxMjMuMTc3LDEwNS4zOTQsMTIyLjQzMywxMDYuMTM4eiBNMTQ2LjQyOSw4NS43MTRjMC0xMS4wMTItMi43MTctMjEuMTY4LTguMTQ4LTMwLjQ2OSBzLTEyLjc5Ny0xNi42NjctMjIuMDk4LTIyLjA5OFM5Ni43MjYsMjUsODUuNzE0LDI1cy0yMS4xNjgsMi43MTYtMzAuNDY5LDguMTQ3UzM4LjU3OSw0NS45NDUsMzMuMTQ3LDU1LjI0NlMyNSw3NC43MDMsMjUsODUuNzE0IHMyLjcxNiwyMS4xNjgsOC4xNDcsMzAuNDY5czEyLjc5NywxNi42NjYsMjIuMDk4LDIyLjA5OHMxOS40NTcsOC4xNDgsMzAuNDY5LDguMTQ4czIxLjE2OC0yLjcxNywzMC40NjktOC4xNDggczE2LjY2Ni0xMi43OTcsMjIuMDk4LTIyLjA5OFMxNDYuNDI5LDk2LjcyNiwxNDYuNDI5LDg1LjcxNHogTTE3MS40MjksODUuNzE0YzAsMTUuNTUxLTMuODMyLDI5Ljg5My0xMS40OTYsNDMuMDI0IGMtNy42NjQsMTMuMTMzLTE4LjA2MiwyMy41My0zMS4xOTQsMzEuMTk0Yy0xMy4xMzIsNy42NjQtMjcuNDc0LDExLjQ5Ni00My4wMjQsMTEuNDk2cy0yOS44OTItMy44MzItNDMuMDI0LTExLjQ5NiBjLTEzLjEzMy03LjY2NC0yMy41MzEtMTguMDYyLTMxLjE5NC0zMS4xOTRDMy44MzIsMTE1LjYwNywwLDEwMS4yNjUsMCw4NS43MTRTMy44MzIsNTUuODIyLDExLjQ5Niw0Mi42OSBjNy42NjQtMTMuMTMzLDE4LjA2Mi0yMy41MzEsMzEuMTk0LTMxLjE5NEM1NS44MjIsMy44MzIsNzAuMTY0LDAsODUuNzE0LDBzMjkuODkzLDMuODMyLDQzLjAyNCwxMS40OTYgYzEzLjEzMyw3LjY2NCwyMy41MywxOC4wNjIsMzEuMTk0LDMxLjE5NEMxNjcuNTk3LDU1LjgyMiwxNzEuNDI5LDcwLjE2NCwxNzEuNDI5LDg1LjcxNHoiLz48L3N2Zz4=)}.gitter-open-chat-button{z-index:100;position:fixed;bottom:0;right:10px;padding:1em 3em;background-color:#36bc98;border:0;border-top-left-radius:.5em;border-top-right-radius:.5em;font-family:sans-serif;font-size:12px;letter-spacing:1px;text-transform:uppercase;text-align:center;text-decoration:none;cursor:pointer;cursor:hand;-webkit-transition:all .3s ease;transition:all .3s ease}.gitter-open-chat-button,.gitter-open-chat-button:visited{box-sizing:border-box;color:#fff}.gitter-open-chat-button:focus,.gitter-open-chat-button:hover{box-sizing:border-box;background-color:#3ea07f;color:#fff}.gitter-open-chat-button:focus{box-sizing:border-box;box-shadow:0 0 8px rgba(62,160,127,.6);outline:none}.gitter-open-chat-button:active{box-sizing:border-box;color:#eee}.gitter-open-chat-button.is-collapsed{box-sizing:border-box;-webkit-transform:translateY(120%);transform:translateY(120%)}",""])},function(t,e){t.exports=function(){var t=[];return t.toString=function(){for(var t=[],e=0;et;t+=2){var e=at[t],r=at[t+1];e(r),at[t]=void 0,at[t+1]=void 0}J=0}function M(){try{var t=r(39);return V=t.runOnLoop||t.runOnContext,f()}catch(e){return p()}}function g(t,e){var r=this,n=new this.constructor(m);void 0===n[st]&&Y(n);var o=r._state;if(o){var i=arguments[o-1];tt(function(){k(o,n,i,r._result)})}else O(r,n,t,e);return n}function h(t){var e=this;if(t&&"object"==typeof t&&t.constructor===e)return t;var r=new e(m);return E(r,t),r}function m(){}function N(){return new TypeError("You cannot resolve a promise with itself")}function j(){return new TypeError("A promises callback cannot return that same promise.")}function w(t){try{return t.then}catch(e){return yt.error=e,yt}}function x(t,e,r,n){try{t.call(e,r,n)}catch(o){return o}}function L(t,e,r){tt(function(t){var n=!1,o=x(r,e,function(r){n||(n=!0,e!==r?E(t,r):S(t,r))},function(e){n||(n=!0,z(t,e))},"Settle: "+(t._label||" unknown promise"));!n&&o&&(n=!0,z(t,o))},t)}function T(t,e){e._state===ft?S(t,e._result):e._state===bt?z(t,e._result):O(e,void 0,function(e){E(t,e)},function(e){z(t,e)})}function v(t,e,r){e.constructor===t.constructor&&r===ut&&constructor.resolve===ct?T(t,e):r===yt?z(t,yt.error):void 0===r?S(t,e):u(r)?L(t,e,r):S(t,e)}function E(t,e){t===e?z(t,N()):a(e)?v(t,e,w(e)):S(t,e)}function D(t){t._onerror&&t._onerror(t._result),A(t)}function S(t,e){t._state===lt&&(t._result=e,t._state=ft,0!==t._subscribers.length&&tt(A,t))}function z(t,e){t._state===lt&&(t._state=bt,t._result=e,tt(D,t))}function O(t,e,r,n){var o=t._subscribers,i=o.length;t._onerror=null,o[i]=e,o[i+ft]=r,o[i+bt]=n,0===i&&t._state&&tt(A,t)}function A(t){var e=t._subscribers,r=t._state;if(0!==e.length){for(var n,o,i=t._result,a=0;ai;i++)e.resolve(t[i]).then(r,n)}:function(t,e){e(new TypeError("You must pass an array to race."))})}function B(t){var e=this,r=new e(m);return z(r,t),r}function H(){throw new TypeError("You must pass a resolver function as the first argument to the promise constructor")}function F(){throw new TypeError("Failed to construct 'Promise': Please use the 'new' operator, this object constructor cannot be called as a function.")}function R(t){this[st]=_(),this._result=this._state=void 0,this._subscribers=[],m!==t&&("function"!=typeof t&&H(),this instanceof R?U(this,t):F())}function G(t,e){this._instanceConstructor=t,this.promise=new t(m),this.promise[st]||Y(this.promise),K(e)?(this._input=e,this.length=e.length,this._remaining=e.length,this._result=new Array(this.length),0===this.length?S(this.promise,this._result):(this.length=this.length||0,this._enumerate(),0===this._remaining&&S(this.promise,this._result))):z(this.promise,Z())}function Z(){return new Error("Array Methods must be provided an Array")}function X(){var t;if("undefined"!=typeof o)t=o;else if("undefined"!=typeof self)t=self;else try{t=Function("return this")()}catch(e){throw new Error("polyfill failed because global object is unavailable in this environment")}var r=t.Promise;r&&"[object Promise]"===Object.prototype.toString.call(r.resolve())&&!r.cast||(t.Promise=mt)}var $;$=Array.isArray?Array.isArray:function(t){return"[object Array]"===Object.prototype.toString.call(t)};var V,W,q,K=$,J=0,tt=function(t,e){at[J]=t,at[J+1]=e,J+=2,2===J&&(W?W(d):q())},et="undefined"!=typeof window?window:void 0,rt=et||{},nt=rt.MutationObserver||rt.WebKitMutationObserver,ot="undefined"==typeof self&&"undefined"!=typeof t&&"[object process]"==={}.toString.call(t),it="undefined"!=typeof Uint8ClampedArray&&"undefined"!=typeof importScripts&&"undefined"!=typeof MessageChannel,at=new Array(1e3);q=ot?l():nt?b():it?y():void 0===et?M():p();var ut=g,ct=h,st=Math.random().toString(36).substring(16),lt=void 0,ft=1,bt=2,yt=new I,pt=new I,dt=0,Mt=Q,gt=P,ht=B,mt=R;R.all=Mt,R.race=gt,R.resolve=ct,R.reject=ht,R._setScheduler=c,R._setAsap=s,R._asap=tt,R.prototype={constructor:R,then:ut,"catch":function(t){return this.then(null,t)}};var Nt=G;G.prototype._enumerate=function(){for(var t=this.length,e=this._input,r=0;this._state===lt&&t>r;r++)this._eachEntry(e[r],r)},G.prototype._eachEntry=function(t,e){var r=this._instanceConstructor,n=r.resolve;if(n===ct){var o=w(t);if(o===ut&&t._state!==lt)this._settledAt(t._state,e,t._result);else if("function"!=typeof o)this._remaining--,this._result[e]=t;else if(r===mt){var i=new r(m);v(i,t,o),this._willSettleAt(i,e)}else this._willSettleAt(new r(function(e){e(t)}),e)}else this._willSettleAt(n(t),e)},G.prototype._settledAt=function(t,e,r){var n=this.promise;n._state===lt&&(this._remaining--,t===bt?z(n,r):this._result[e]=r),0===this._remaining&&S(n,this._result)},G.prototype._willSettleAt=function(t,e){var r=this;O(t,void 0,function(t){r._settledAt(ft,e,t)},function(t){r._settledAt(bt,e,t)})};var jt=X,wt={Promise:mt,polyfill:jt};r(36).amd?(n=function(){return wt}.call(e,r,e,i),!(void 0!==n&&(i.exports=n))):"undefined"!=typeof i&&i.exports?i.exports=wt:"undefined"!=typeof this&&(this.ES6Promise=wt),jt()}).call(this)}).call(e,r(38),function(){return this}(),r(37)(t))},function(t,e){t.exports=function(){throw new Error("define cannot be used indirect")}},function(t,e){t.exports=function(t){return t.webpackPolyfill||(t.deprecate=function(){},t.paths=[],t.children=[],t.webpackPolyfill=1),t}},function(t,e){function r(){s&&a&&(s=!1,a.length?c=a.concat(c):l=-1,c.length&&n())}function n(){if(!s){var t=setTimeout(r);s=!0;for(var e=c.length;e;){for(a=c,c=[];++l1)for(var r=1;r