├── .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 | [](https://travis-ci.org/thepracticaldev/1pr) [](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 |
58 |
59 |
60 |
61 |
62 |
Trigger database migrations here
63 |
64 |
65 |
66 |
67 |
68 |
Migrations Loading
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 | Swap Engine
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 |
58 |
59 |
60 |
61 |
62 |
?
63 |
open pull requests
64 |
65 |
66 |
67 |
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 |
89 |
90 |
98 |
99 |
106 |
107 |
108 |
109 |
110 |
111 |
112 |
116 |
120 |
124 |
125 |
Contributors
126 |
127 |
128 |
129 |
130 |
131 |
Check out what pull requests are up for selection
132 |
133 |
134 |
135 |
136 |
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;e 2?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