├── .gitignore
├── backend
├── .gitignore
├── .upignore
├── Makefile
├── test
│ ├── routeUser
│ │ ├── email_verification.js
│ │ ├── password_reset.js
│ │ ├── index.js
│ │ ├── needingToken.js
│ │ ├── signup.js
│ │ ├── needingTokenAndEmailVerified.js
│ │ └── login.js
│ └── testConfig.js
├── scripts
│ ├── dynamodb.js
│ ├── deleteTable.js
│ └── createTables.js
├── daos
│ ├── index.js
│ ├── db.js
│ ├── UserEmail.js
│ ├── Song.js
│ └── User.js
├── utils
│ ├── createToken.js
│ ├── test
│ │ └── crypts.js
│ ├── errorCatcher.js
│ ├── crypts.js
│ └── sendMail.js
├── up.json
├── routeGraphql
│ ├── index.js
│ ├── resolvers
│ │ └── index.js
│ └── typeDefs.graphql
├── routeUser
│ ├── index.js
│ ├── email_verification.js
│ ├── password_reset.js
│ ├── login.js
│ └── signup.js
├── package.json
├── README.md
├── middleware
│ └── verifyToken.js
├── config.js
├── app.js
└── package-lock.json
├── frontend
├── src
│ ├── components
│ │ ├── Song.css
│ │ ├── Strap
│ │ │ ├── style.css
│ │ │ ├── Row
│ │ │ │ ├── style.css
│ │ │ │ └── index.js
│ │ │ └── index.js
│ │ ├── Song.js
│ │ └── Keyboard
│ │ │ ├── style.css
│ │ │ └── index.js
│ ├── App.test.js
│ ├── index.css
│ ├── App.css
│ ├── utils
│ │ └── notePlayer.js
│ ├── config
│ │ └── index.js
│ ├── App.js
│ ├── index.js
│ ├── store.js
│ ├── registerServiceWorker.js
│ ├── store2.js
│ └── bttn.css
├── resources
│ ├── C4.mp3
│ └── a4.ogg
├── public
│ ├── favicon.ico
│ ├── manifest.json
│ └── index.html
├── .gitignore
├── scripts
│ └── testTone.js
└── package.json
├── favicon.ico
├── screenShot.png
├── asset-manifest.json
├── manifest.json
├── README.md
├── index.html
├── service-worker.js
└── static
└── css
└── main.9c2864c1.css
/.gitignore:
--------------------------------------------------------------------------------
1 | node_modules
2 | .DS_Store
--------------------------------------------------------------------------------
/backend/.gitignore:
--------------------------------------------------------------------------------
1 | node-v8.4.0-linux-x64
--------------------------------------------------------------------------------
/backend/.upignore:
--------------------------------------------------------------------------------
1 | !node-v8.4.0-linux-x64
--------------------------------------------------------------------------------
/frontend/src/components/Song.css:
--------------------------------------------------------------------------------
1 | .Song {
2 | width: 100px;
3 | }
--------------------------------------------------------------------------------
/favicon.ico:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/timqian/music-lab/HEAD/favicon.ico
--------------------------------------------------------------------------------
/screenShot.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/timqian/music-lab/HEAD/screenShot.png
--------------------------------------------------------------------------------
/frontend/resources/C4.mp3:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/timqian/music-lab/HEAD/frontend/resources/C4.mp3
--------------------------------------------------------------------------------
/frontend/resources/a4.ogg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/timqian/music-lab/HEAD/frontend/resources/a4.ogg
--------------------------------------------------------------------------------
/frontend/public/favicon.ico:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/timqian/music-lab/HEAD/frontend/public/favicon.ico
--------------------------------------------------------------------------------
/backend/Makefile:
--------------------------------------------------------------------------------
1 | node-v8.4.0-linux-x64:
2 | @curl -sL https://nodejs.org/dist/v8.4.0/node-v8.4.0-linux-x64.tar.xz | tar x
--------------------------------------------------------------------------------
/backend/test/routeUser/email_verification.js:
--------------------------------------------------------------------------------
1 | // go to your mail address and click the link.
2 | // Should return {
3 | // success: ture
4 | // }
5 |
--------------------------------------------------------------------------------
/backend/scripts/dynamodb.js:
--------------------------------------------------------------------------------
1 | var AWS = require("aws-sdk");
2 |
3 | AWS.config.update({
4 | region: "us-west-2",
5 | endpoint: "http://localhost:8000"
6 | });
7 |
8 | module.exports = new AWS.DynamoDB();
--------------------------------------------------------------------------------
/backend/daos/index.js:
--------------------------------------------------------------------------------
1 | const User = require('./User');
2 | const UserEmail = require('./UserEmail');
3 | const Song = require('./Song')
4 |
5 | module.exports = {
6 | User,
7 | UserEmail,
8 | Song,
9 | }
10 |
--------------------------------------------------------------------------------
/asset-manifest.json:
--------------------------------------------------------------------------------
1 | {
2 | "main.css": "static/css/main.9c2864c1.css",
3 | "main.css.map": "static/css/main.9c2864c1.css.map",
4 | "main.js": "static/js/main.12f54783.js",
5 | "main.js.map": "static/js/main.12f54783.js.map"
6 | }
--------------------------------------------------------------------------------
/backend/utils/createToken.js:
--------------------------------------------------------------------------------
1 | const jwt= require('jsonwebtoken');
2 | const config= require('../config');
3 |
4 | module.exports = function(payload, expiresIn) {
5 | return jwt.sign(payload, config.SECRET, { expiresIn });
6 | }
7 |
--------------------------------------------------------------------------------
/backend/utils/test/crypts.js:
--------------------------------------------------------------------------------
1 | const { hashPassword, checkPassword}= require('../crypts');
2 |
3 | (async function() {
4 | const passHash = await hashPassword('123');
5 | const res = await checkPassword('123', passHash);
6 | console.log(res);
7 | })();
8 |
--------------------------------------------------------------------------------
/backend/up.json:
--------------------------------------------------------------------------------
1 | {
2 | "profile": "timqian'sUp",
3 | "environment": {
4 | "ON_LAMBDA": "true"
5 | },
6 | "hooks": {
7 | "build": "make"
8 | },
9 | "proxy": {
10 | "command": "./node-v8.4.0-linux-x64/bin/node app.js"
11 | }
12 | }
13 |
--------------------------------------------------------------------------------
/frontend/src/App.test.js:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import ReactDOM from 'react-dom';
3 | import App from './App';
4 |
5 | it('renders without crashing', () => {
6 | const div = document.createElement('div');
7 | ReactDOM.render(, div);
8 | });
9 |
--------------------------------------------------------------------------------
/backend/test/testConfig.js:
--------------------------------------------------------------------------------
1 | // used to store config
2 |
3 | const testConfig = {
4 | BASEURL: process.env.ON_LAMBDA ? 'https://0txotn5h18.execute-api.us-west-2.amazonaws.com/development' : 'http://localhost:3000', // api url
5 | EMAIL_RECEIVING_VERIFICATION: 'timqian92@qq.com',
6 | };
7 |
8 | module.exports = testConfig;
9 |
--------------------------------------------------------------------------------
/backend/utils/errorCatcher.js:
--------------------------------------------------------------------------------
1 | const errorCatcher = fn =>
2 | (req, res, next) => {
3 | return Promise.resolve(fn(req, res, next))
4 | .catch(err => {
5 | console.log('Un handled err: ', err);
6 | res.status(500).json('internal err')
7 | });
8 | };
9 |
10 | module.exports = errorCatcher;
--------------------------------------------------------------------------------
/manifest.json:
--------------------------------------------------------------------------------
1 | {
2 | "short_name": "React App",
3 | "name": "Create React App Sample",
4 | "icons": [
5 | {
6 | "src": "favicon.ico",
7 | "sizes": "192x192",
8 | "type": "image/png"
9 | }
10 | ],
11 | "start_url": "./index.html",
12 | "display": "standalone",
13 | "theme_color": "#000000",
14 | "background_color": "#ffffff"
15 | }
16 |
--------------------------------------------------------------------------------
/frontend/public/manifest.json:
--------------------------------------------------------------------------------
1 | {
2 | "short_name": "React App",
3 | "name": "Create React App Sample",
4 | "icons": [
5 | {
6 | "src": "favicon.ico",
7 | "sizes": "192x192",
8 | "type": "image/png"
9 | }
10 | ],
11 | "start_url": "./index.html",
12 | "display": "standalone",
13 | "theme_color": "#000000",
14 | "background_color": "#ffffff"
15 | }
16 |
--------------------------------------------------------------------------------
/frontend/.gitignore:
--------------------------------------------------------------------------------
1 | # See https://help.github.com/ignore-files/ for more about ignoring files.
2 |
3 | # dependencies
4 | /node_modules
5 |
6 | # testing
7 | /coverage
8 |
9 | # production
10 | /build
11 |
12 | # misc
13 | .DS_Store
14 | .env.local
15 | .env.development.local
16 | .env.test.local
17 | .env.production.local
18 |
19 | npm-debug.log*
20 | yarn-debug.log*
21 | yarn-error.log*
22 |
--------------------------------------------------------------------------------
/backend/daos/db.js:
--------------------------------------------------------------------------------
1 | const AWS= require('aws-sdk');
2 | const {AWS_ENDPOINT, AWS_ACCESS, AWS_SECRET} = require('../config');
3 |
4 | AWS.config.update({
5 | accessKeyId: AWS_ACCESS,
6 | secretAccessKey: AWS_SECRET,
7 | region: "us-west-2",
8 | endpoint: AWS_ENDPOINT,
9 | signatureVersion: 'v4',
10 | });
11 |
12 | module.exports = {
13 | docClient: new AWS.DynamoDB.DocumentClient(),
14 | dynamoDb: new AWS.DynamoDB(),
15 | }
--------------------------------------------------------------------------------
/frontend/scripts/testTone.js:
--------------------------------------------------------------------------------
1 | // const Tone = require('tone');
2 | const fs = require('fs');
3 | const MidiConvert = require('midiconvert');
4 |
5 | var midi = MidiConvert.create()
6 | // add a track
7 | midi.track()
8 | // select an instrument by its MIDI patch number
9 | .patch(32)
10 | // chain note events: note, time, duration
11 | .note(60, 0, 2)
12 | .note(63, 1, 2)
13 | .note(60, 2, 2)
14 |
15 | // write the output
16 | fs.writeFileSync("output.mid", midi.encode(), "binary")
--------------------------------------------------------------------------------
/frontend/src/index.css:
--------------------------------------------------------------------------------
1 | body {
2 | margin: 0;
3 | padding: 0;
4 | font-family: sans-serif;
5 | height: 100vh;
6 | }
7 |
8 | /*
9 | * Bring back scrollbar for OS X
10 | * http://simurai.com/blog/2011/07/26/webkit-scrollbar
11 | */
12 | /* ::-webkit-scrollbar {
13 | -webkit-appearance: none;
14 | width: 10px;
15 | }
16 |
17 | ::-webkit-scrollbar-thumb {
18 | border-radius: 6px;
19 | background-color: rgba(0,0,0,.5);
20 | box-shadow: 0 0 1px rgba(255,255,255,.5);
21 | } */
22 |
23 |
--------------------------------------------------------------------------------
/frontend/src/components/Strap/style.css:
--------------------------------------------------------------------------------
1 | .strap {
2 | /* white-space: nowrap; */
3 | /* height: 120 */
4 | /*
5 | remove space between inline block elements (as inline blocks regarded as )
6 | https://stackoverflow.com/questions/5078239/how-to-remove-the-space-between-inline-block-elements
7 | */
8 | font-size: 0;
9 | height: 85vh;
10 | overflow-y: scroll;
11 | /* padding-bottom: 85vh; */
12 | }
13 |
14 | .whiteSpace {
15 | height: 85vh;
16 | }
--------------------------------------------------------------------------------
/backend/test/routeUser/password_reset.js:
--------------------------------------------------------------------------------
1 | const axios = require('axios');
2 | const assert = require('assert');
3 | const config = require('../../config');
4 |
5 | module.exports = function password_reset() {
6 |
7 | describe('POST /password_reset', function () {
8 |
9 | it('reset success', () => {
10 | return axios.post(`${config.API_URL}/user/password_reset`, {
11 | email: `${config.EMAIL_RECEIVING_VERIFICATION}`,
12 | password: '123',
13 | }).then((res) => {
14 | assert.equal(res.status, 200);
15 | });
16 | });
17 | });
18 | }
19 |
--------------------------------------------------------------------------------
/backend/scripts/deleteTable.js:
--------------------------------------------------------------------------------
1 | var { dynamoDb } = require('../daos/db');
2 | (async () => {
3 |
4 | await dynamoDb.deleteTable({
5 | TableName: "User"
6 | }).promise().catch(err => {
7 | console.log(err);
8 | });
9 |
10 |
11 | await dynamoDb.deleteTable({
12 | TableName: "UserEmail"
13 | }).promise().catch(err => {
14 | console.log(err);
15 | });
16 |
17 | await dynamoDb.deleteTable({
18 | TableName: "Song"
19 | }).promise().catch(err => {
20 | console.log(err);
21 | });
22 |
23 | console.log('tabels deleted');
24 | })();
25 |
26 |
--------------------------------------------------------------------------------
/backend/utils/crypts.js:
--------------------------------------------------------------------------------
1 | const { hash, compare }= require('bcrypt-nodejs');
2 | const promisify= require('es6-promisify');
3 |
4 | // const genSaltAsync = promisify(genSalt);
5 | const hashAsync = promisify(hash);
6 | const compareAsync = promisify(compare);
7 |
8 | async function hashPassword(password) {
9 | // const salt = await genSaltAsync(10);
10 | const hash = await hashAsync(password,null,null);
11 | return hash;
12 | }
13 |
14 | async function checkPassword(password, hash) {
15 | const res = await compareAsync(password, hash);
16 | return res;
17 | }
18 |
19 | module.exports = {
20 | hashPassword,
21 | checkPassword,
22 | }
--------------------------------------------------------------------------------
/frontend/src/App.css:
--------------------------------------------------------------------------------
1 | .App {
2 | /* text-align: center; */
3 | user-select: none;
4 | height:100%;
5 | --border-width: 1px;
6 | display: flex;
7 | background-color: #08131b;
8 | }
9 |
10 | .App-header {
11 | width: 200px;
12 | min-width: 150px;
13 | color: whitesmoke;
14 | top: 0;
15 | font-size: 30px;
16 | margin-left: 10px;
17 | margin-right: 10px;
18 | height: 100vh;
19 | }
20 |
21 | .Control-panel {
22 | /* border-top: 4px solid snow; */
23 | /* border-bottom: 2px solid snow; */
24 | padding-top: 5px;
25 | padding-bottom: 20px;
26 | margin-top: 70px;
27 | }
28 |
29 | #Song {
30 | flex: 1;
31 | font-size: 0;
32 | }
33 |
34 |
35 |
--------------------------------------------------------------------------------
/backend/daos/UserEmail.js:
--------------------------------------------------------------------------------
1 | // Used to make sure uniqueness of user email
2 | // TODO: 1. change dao name to UserEmail
3 | // 2. move emailVerified to userDao
4 | const {docClient} = require('./db');
5 |
6 | async function get(email) {
7 | const obj = await docClient.get({
8 | TableName: 'UserEmail',
9 | Key: { email },
10 | }).promise();
11 |
12 | return obj.Item;
13 | }
14 |
15 | function put({ email, name }) {
16 | return docClient.put({
17 | TableName: 'UserEmail',
18 | Item: {
19 | email,
20 | name,
21 | },
22 | }).promise();
23 | }
24 |
25 | module.exports = {
26 | get,
27 | put
28 | }
29 |
--------------------------------------------------------------------------------
/backend/routeGraphql/index.js:
--------------------------------------------------------------------------------
1 | const fs = require('fs');
2 | const path = require('path');
3 | const graphqlHTTP = require('express-graphql');
4 | const { makeExecutableSchema, addMockFunctionsToSchema } = require('graphql-tools');
5 | const { maskErrors } = require('graphql-errors');
6 |
7 | const typeDefs = fs.readFileSync(path.join(__dirname, "typeDefs.graphql"), "utf8");
8 | const resolvers = require('./resolvers');
9 |
10 | const schema = makeExecutableSchema({
11 | typeDefs,
12 | resolvers,
13 | });
14 |
15 | // Used to mock api
16 | // addMockFunctionsToSchema({ schema });
17 | maskErrors(schema);
18 |
19 | module.exports = graphqlHTTP({
20 | schema,
21 | graphiql: true,
22 | });
23 |
--------------------------------------------------------------------------------
/backend/routeUser/index.js:
--------------------------------------------------------------------------------
1 | const { Router }= require('express');
2 | const verifyToken= require('../middleware/verifyToken');
3 | const errorCatcher = require('../utils/errorCatcher');
4 | const signup= require('./signup');
5 | const login= require('./login');
6 | const email_verification= require('./email_verification');
7 | const password_reset= require('./password_reset');
8 |
9 | const router = new Router();
10 |
11 | router.post('/signup', errorCatcher(signup));
12 | router.post('/login', errorCatcher(login));
13 | router.get('/email_verification', errorCatcher(verifyToken), errorCatcher(email_verification));
14 | router.post('/password_reset', errorCatcher(password_reset));
15 |
16 | module.exports = router;
17 |
--------------------------------------------------------------------------------
/frontend/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "frontend",
3 | "version": "0.1.0",
4 | "private": true,
5 | "dependencies": {
6 | "apollo-client": "^1.9.2",
7 | "babel-plugin-transform-decorators-legacy": "^1.3.4",
8 | "graphql-tag": "^2.4.2",
9 | "midiconvert": "^0.4.1",
10 | "mobx": "^3.2.2",
11 | "mobx-react": "^4.2.2",
12 | "mobx-react-devtools": "^4.2.15",
13 | "react": "^15.6.1",
14 | "react-dom": "^15.6.1",
15 | "react-scripts": "1.0.10",
16 | "tone": "^0.10.0"
17 | },
18 | "scripts": {
19 | "start": "react-scripts start",
20 | "build": "react-scripts build && rm build/static/js/*.map && rm build/static/css/*.map && rm -r ../static && mv build/* ../",
21 | "test": "react-scripts test --env=jsdom",
22 | "eject": "react-scripts eject"
23 | }
24 | }
25 |
--------------------------------------------------------------------------------
/backend/routeGraphql/resolvers/index.js:
--------------------------------------------------------------------------------
1 | const daos = require('../../daos');
2 |
3 | const resolvers = {
4 | Query: {
5 | async user(_, { name }, req) {
6 | return await daos.User.get(name);
7 | },
8 | },
9 |
10 | Mutation: {
11 | async putSong(_, { author, name, notes2D }, req) {
12 | await daos.Song.put({
13 | author,
14 | name,
15 | notes2D,
16 | });
17 |
18 | return 'OK';
19 | },
20 |
21 | async deleteSong(_, { author, name }, req) {
22 | await daos.Song.del({
23 | author,
24 | name,
25 | });
26 |
27 | return 'OK';
28 | }
29 | },
30 |
31 | User: {
32 | songs(user, args, req) {
33 | return daos.Song.getAll(user.name);
34 | },
35 | }
36 | };
37 |
38 | module.exports = resolvers;
--------------------------------------------------------------------------------
/backend/routeUser/email_verification.js:
--------------------------------------------------------------------------------
1 | const daos = require('../daos');
2 |
3 | // set email verified
4 | module.exports = async function (req, res) {
5 | const { email, hashedPassword } = req.decoded;
6 | const userEmail = await daos.UserEmail.get(email);
7 | if (!userEmail) {
8 | res.status(400).json('No user find for this email');
9 | return;
10 | }
11 |
12 | // this API is used for both verify email and change password
13 | if (!hashedPassword) {
14 |
15 | const updateRes = await daos.User.update({
16 | name: userEmail.name,
17 | emailVerified: true,
18 | })
19 |
20 | res.status(200).json('Your email account is now verified. Congratulations!');
21 | } else {
22 | const user = await daos.User.get(userEmail.name);
23 |
24 | const updateRes = await daos.User.update({
25 | name: userEmail.name,
26 | hashedPassword,
27 | emailVerified: true,
28 | });
29 |
30 |
31 | res.status(200).json('Your password is updated. Congratulations! ');
32 | }
33 | }
34 |
--------------------------------------------------------------------------------
/backend/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "yiin-cloud-backend",
3 | "main": "app.js",
4 | "dependencies": {
5 | "aws-sdk": "^2.108.0",
6 | "bcrypt-nodejs": "0.0.3",
7 | "body-parser": "^1.17.2",
8 | "es6-promisify": "^5.0.0",
9 | "express": "^4.15.4",
10 | "express-graphql": "^0.6.11",
11 | "graphql": "^0.11.3",
12 | "graphql-errors": "^2.1.0",
13 | "graphql-tools": "^1.2.2",
14 | "jsonwebtoken": "^7.4.3",
15 | "method-override": "^2.3.9",
16 | "morgan": "^1.8.2",
17 | "nodemailer": "^4.1.0"
18 | },
19 | "devDependencies": {
20 | "axios": "^0.16.2"
21 | },
22 | "scripts": {
23 | "test": "export ON_LAMBDA='' && node scripts/deleteTable.js && node scripts/createTables.js && mocha --recursive --timeout 30000",
24 | "testProductionNoteAllTheTablesWillBeCleared": "export ON_LAMBDA='true' && node scripts/deleteTable.js && node scripts/createTables.js && mocha --recursive --timeout 30000",
25 | "start": "nodemon app.js -e js,graphql,json"
26 | },
27 | "author": "timqian",
28 | "license": "ISC"
29 | }
30 |
--------------------------------------------------------------------------------
/backend/test/routeUser/index.js:
--------------------------------------------------------------------------------
1 | const config = require('../../config');
2 | const axios = require('axios');
3 | const login = require('./login');
4 | const signup = require('./signup');
5 | const passwordReset = require('./password_reset');
6 | const needingToken = require('./needingToken');
7 | const needingTokenAndEmailVerified = require('./needingTokenAndEmailVerified');
8 |
9 | describe('Test starts', function () {
10 |
11 | // signup initial user
12 | before(async function() {
13 |
14 | console.log('Base URL: ', config.API_URL);
15 |
16 | await axios.post(`${config.API_URL}/user/signup`, {
17 | email: `${config.EMAIL_RECEIVING_VERIFICATION}`,
18 | name: `tim`,
19 | password: '123',
20 | }).then((res) => {
21 | console.log('before message:', res.data);
22 | }).catch((err) => {
23 | console.log('before message:', err.response.data);
24 | // throw res;
25 | });
26 | });
27 |
28 | login();
29 | signup();
30 | passwordReset();
31 | needingToken();
32 | needingTokenAndEmailVerified();
33 | });
34 |
--------------------------------------------------------------------------------
/backend/utils/sendMail.js:
--------------------------------------------------------------------------------
1 | const { createTransport }= require('nodemailer');
2 | const config= require('../config');
3 | // create reusable transporter object using SMTP transport
4 |
5 | /**
6 | * to: mail
7 | * verifyAddress:
8 | */
9 | module.exports = function sendMail(targetAddress, content) {
10 | // NB! No need to recreate the transporter object. You can use
11 | // the same transporter object for all e-mails
12 | const transporter = createTransport(config.EMAIL_SENDER, {
13 | // default values for sendMail method
14 | from: `${config.APP_NAME} <${config.EMAIL_SENDER.auth.user}>`,
15 | // headers: {
16 | // 'My-Awesome-Header': '123'
17 | // }
18 | });
19 |
20 | // setup e-mail data with unicode symbols
21 | let mailOptions = {
22 | to: `${targetAddress}`, // list of receivers
23 | subject: 'Email verification', // Subject line
24 | html: `${content}`,
25 | };
26 |
27 | // console.log('sendMail', mailOptions);
28 | // send mail with defined transport object
29 | return transporter.sendMail(mailOptions);
30 | }
31 |
--------------------------------------------------------------------------------
/backend/test/routeUser/needingToken.js:
--------------------------------------------------------------------------------
1 | const axios = require('axios');
2 | const assert = require('assert');
3 | const config = require('../../config');
4 |
5 | module.exports = function password_reset() {
6 | describe('GET /needingToken', function () {
7 |
8 | let token = '';
9 |
10 | before(async function() {
11 | const loginRes = await axios.post(`${config.API_URL}/user/login`, {
12 | name: 'tim',
13 | password: '123',
14 | }).catch((err) => { throw err.response.data;});
15 |
16 | token = loginRes.data.token;
17 | });
18 |
19 | it('should need token', function () {
20 | return axios.get(`${config.API_URL}/needingToken`)
21 | .then((res) => { throw res; })
22 | .catch((err) => {
23 | assert.equal(err.response.status, 403);
24 | });
25 | });
26 |
27 | it('should success', function () {
28 | return axios.get(`${config.API_URL}/needingToken`, {params: {token}})
29 | .then((res) => {
30 | assert.equal(res.status, 200);
31 | });
32 | });
33 |
34 | });
35 | }
36 |
--------------------------------------------------------------------------------
/backend/README.md:
--------------------------------------------------------------------------------
1 | > up url: https://0txotn5h18.execute-api.us-west-2.amazonaws.com/development/
2 |
3 | ## Development
4 | ```bash
5 | # start dynamodb locally
6 | cd ~/dynamodb_local_latest
7 | java -Djava.library.path=./DynamoDBLocal_lib -jar DynamoDBLocal.jar -sharedDb -inMemory
8 | ```
9 |
10 | ## Tools used
11 |
12 | - [Up](https://github.com/apex/up/blob/master/docs/getting-started.md): For deploy backend to aws lambda
13 |
14 | ## use node 8 on lambda
15 |
16 | https://github.com/apex/up-examples/tree/master/oss/node-8
17 |
18 |
19 | ## TODO:
20 |
21 | - [x] replace mongo with dynamo
22 | - [x] use node8 on lambda
23 | - [x] write config for lambda (danamodb url etc.)
24 | - [x] move email verified attribute to user dao
25 | - [x] add update method for user
26 | - [ ] add location of user
27 | - [ ] try catch in router so server can always respond
28 | - [ ] return message shorter(`{success: false, message: 'abc'}` => `'abc'`)
29 | - [ ] add song!
30 | - [ ] send mail block event loop
31 | - [ ] update test flow
32 | - [ ] How to do follow?: One may want to see his followers and who are he following.
33 |
34 |
35 | ## Good pratise
36 |
37 | - add a uuid for error
38 |
39 |
40 |
--------------------------------------------------------------------------------
/backend/routeGraphql/typeDefs.graphql:
--------------------------------------------------------------------------------
1 |
2 | type User {
3 | name: String!
4 | # type: userType # user dao
5 | email: String!
6 | emailVerified: Boolean # email dao
7 | songs: [Song] # store Song ID or not is a question
8 | stared: [Song]
9 | following: [User]
10 | # setting: UserSetting
11 | }
12 |
13 | enum userType {
14 | FREE
15 | PRO
16 | }
17 |
18 | type Song {
19 | author: String!
20 | name: String!
21 | notes2D: String!
22 | # speed: Float
23 | # starNum: Float
24 | # isPersonal: Boolean
25 | }
26 |
27 | # input SongInput {
28 | # author: String!
29 | # name: String!
30 | # notes2D: String!
31 | # }
32 |
33 | type Query {
34 | user(name: String!): User!
35 | }
36 |
37 | # http://graphql.org/graphql-js/mutations-and-input-types/
38 | type Mutation {
39 | putSong(author: String!, name: String!, notes2D: String!): String
40 | deleteSong(author: String!, name: String!): String
41 | }
42 |
43 | # we need to tell the server which types represent the root query
44 | # and root mutation types. We call them RootQuery and RootMutation by convention.
45 | schema {
46 | query: Query
47 | mutation: Mutation
48 | }
--------------------------------------------------------------------------------
/backend/middleware/verifyToken.js:
--------------------------------------------------------------------------------
1 | /**
2 | * middleware used to verify token of client
3 | * NOTE: only with jsonParser,
4 | */
5 |
6 | const jwt= require('jsonwebtoken');
7 | const config= require('../config');
8 |
9 | module.exports = function (req, res, next) {
10 |
11 | // check header or url parameters or post parameters for token
12 | const token = req.body.token || req.query.token ||
13 | req.headers['x-access-token'];
14 |
15 | // decode token
16 | if (token) {
17 |
18 | // verifies secret and checks exp
19 | jwt.verify(token, config.SECRET, (err, decoded) => {
20 | if (err) {
21 | return res.status(403).json({
22 | success: false,
23 | message: 'Failed to authenticate token.'
24 | });
25 | } else {
26 | // if everything is good, save to request for use in other routes
27 | req.decoded = decoded;
28 | next();
29 | }
30 | });
31 |
32 | } else {
33 |
34 | // if there is no token
35 | // return an error
36 | // return res.status(403).send({
37 | // success: false,
38 | // message: 'No token provided.'
39 | // });
40 |
41 | next();
42 | }
43 | }
44 |
--------------------------------------------------------------------------------
/backend/daos/Song.js:
--------------------------------------------------------------------------------
1 |
2 | const { docClient } = require('./db');
3 |
4 | async function get(author, name) {
5 | const obj = await docClient.get({
6 | TableName: 'Song',
7 | Key: { author, name },
8 | }).promise();
9 |
10 | return obj.Item;
11 | }
12 |
13 | async function getAll(author) {
14 | const obj = await docClient.query({
15 | TableName: 'Song',
16 | KeyConditionExpression: '#author = :author',
17 | ExpressionAttributeNames: {
18 | '#author': 'author'
19 | },
20 | ExpressionAttributeValues: {
21 | ':author': author
22 | }
23 | }).promise();
24 |
25 | return obj.Items;
26 | }
27 |
28 | function put({ author, name, notes2D }) {
29 | return docClient.put({
30 | TableName: 'Song',
31 | Item: {
32 | author,
33 | name,
34 | notes2D,
35 | },
36 | }).promise();
37 | }
38 |
39 | function del({ author, name }) {
40 | return docClient.delete({
41 | TableName: 'Song',
42 | Key: {author, name},
43 | }).promise();
44 | }
45 |
46 | module.exports = {
47 | get,
48 | put,
49 | getAll,
50 | del,
51 | }
--------------------------------------------------------------------------------
/frontend/src/utils/notePlayer.js:
--------------------------------------------------------------------------------
1 | import Tone from 'tone';
2 | import { NUM_KEY_MAP } from '../config';
3 |
4 | // const synth = new Tone.Synth().toMaster();
5 | const polySynth = new Tone.PolySynth(61, Tone.Synth).toMaster();
6 |
7 | export function attack(i) {
8 | // console.log('goint to attack', i, NUM_KEY_MAP[i]);
9 | polySynth.triggerAttackRelease([NUM_KEY_MAP[i]], '4n');
10 | }
11 |
12 | export function release(i) {
13 | // console.log('going to release ', i, NUM_KEY_MAP[i]);
14 | polySynth.triggerRelease([NUM_KEY_MAP[i]]);
15 | }
16 |
17 | export function attackRow (currentRow, lastRow) {
18 | if(currentRow) {
19 | const attackArr = [];
20 | const releaseArr = [];
21 | for (let i = 0; i < currentRow.length; i++) {
22 | if(currentRow[i] !== 0 && (!lastRow || currentRow[i] !== lastRow[i])) attackArr.push(NUM_KEY_MAP[i]);
23 | if(currentRow[i] === 0) releaseArr.push(NUM_KEY_MAP[i]);
24 | }
25 | polySynth.triggerAttackRelease(attackArr, '3');
26 | polySynth.triggerRelease(releaseArr);
27 | }
28 | }
29 | // attack(30);
30 | // attack(30);
31 |
32 | // setTimeout(function() {
33 | // release(30);
34 | // // release(30)
35 | // }, 1000);
36 |
37 | // release(30);
38 | // release(30);
--------------------------------------------------------------------------------
/frontend/src/components/Song.js:
--------------------------------------------------------------------------------
1 | import React, { Component } from 'react';
2 |
3 | import Keyboard from './Keyboard';
4 | import Strap from './Strap';
5 | import { observer } from 'mobx-react';
6 |
7 | @observer
8 | class Song extends Component {
9 |
10 | render() {
11 | const store = this.props.store;
12 |
13 | let pressedArr;
14 | if( store.currentRow === -1) {
15 | pressedArr = store.pressedNotes;
16 | } else {
17 | // console.log(store.currentRow,'song');
18 | const currentNotes = store.notes2D[store.currentRow]
19 | pressedArr = currentNotes ? currentNotes.map((note, i) => note || store.pressedNotes[i]) : [];
20 | }
21 | return (
22 |
store.isMouseDown = true}
23 | onMouseUp={() => store.isMouseDown = false}
24 | className="Song"
25 | >
26 | {!store.isKeyboardUp ?
:
}
27 |
28 | {store.isKeyboardUp ?
:
}
29 |
30 | )
31 | }
32 | }
33 |
34 | export default Song;
35 |
--------------------------------------------------------------------------------
/backend/test/routeUser/signup.js:
--------------------------------------------------------------------------------
1 | const axios = require('axios');
2 | const assert = require('assert');
3 | const config = require('../../config');
4 |
5 |
6 | module.exports = function login() {
7 |
8 | describe('POST /signup', function () {
9 |
10 | // it('signup success', function () {
11 | // return axios.post(`${config.API_URL}/user/signup`, {
12 | // email: `${Date.now()}@qq.com`,
13 | // name: `tim${Date.now()}`,
14 | // password: '123',
15 | // }).then((res) => {
16 | // assert.equal(res.status, 200);
17 | // });
18 | // });
19 |
20 | it('email taken', function () {
21 | return axios.post(`${config.API_URL}/user/signup`, {
22 | email: `${config.EMAIL_RECEIVING_VERIFICATION}`,
23 | name: `timq`,
24 | password: '123',
25 | }).then((res) => {
26 | throw res;
27 | // console.log(res);
28 | }).catch((err) => {
29 | assert.equal(err.response.status, 400);
30 | });
31 | });
32 |
33 | it('name taken', function () {
34 | return axios.post(`${config.API_URL}/user/signup`, {
35 | email: 't92@qq.com',
36 | name: `tim`,
37 | password: '123',
38 | }).then((res) => {
39 | throw res;
40 | // console.log(res);
41 | }).catch((err) => {
42 | assert.equal(err.response.status, 400);
43 | });
44 | });
45 |
46 | });
47 | }
48 |
--------------------------------------------------------------------------------
/backend/daos/User.js:
--------------------------------------------------------------------------------
1 |
2 | const { docClient } = require('./db');
3 |
4 | async function get(name) {
5 | const obj = await docClient.get({
6 | TableName: 'User',
7 | Key: { name },
8 | }).promise();
9 |
10 | return obj.Item;
11 | }
12 |
13 | function put({ name, email, emailVerified, hashedPassword }) {
14 | return docClient.put({
15 | TableName: 'User',
16 | Item: {
17 | name,
18 | email,
19 | emailVerified,
20 | hashedPassword,
21 | },
22 |
23 | }).promise();
24 | }
25 |
26 | function update({ name, email, emailVerified, hashedPassword }) {
27 |
28 | const emailExp = email ? 'email = :email,' : '';
29 | const verifiedExp = emailVerified ? 'emailVerified = :emailVerified,' : '';
30 | const passExp = hashedPassword ? 'hashedPassword = :hashedPassword,' : '';
31 |
32 | const UpdateExpression = `set ${emailExp}${verifiedExp}${passExp}`.slice(0, -1);
33 |
34 | const ExpressionAttributeValues = {
35 | ":email": email,
36 | ":emailVerified": emailVerified,
37 | ":hashedPassword": hashedPassword,
38 | };
39 |
40 | return docClient.update({
41 | TableName: 'User',
42 | Key: { name },
43 | UpdateExpression,
44 | ExpressionAttributeValues,
45 | ReturnValues: "UPDATED_NEW",
46 | }).promise();
47 | }
48 |
49 | module.exports = {
50 | get,
51 | put,
52 | update,
53 | }
--------------------------------------------------------------------------------
/backend/config.js:
--------------------------------------------------------------------------------
1 | // used to store config
2 |
3 | const config = {
4 | APP_NAME: 'Yiin Cloud',
5 | SECRET: 'ilovemusic', // jwt secret
6 | API_URL: process.env.ON_LAMBDA ? 'https://0txotn5h18.execute-api.us-west-2.amazonaws.com/development' : 'http://localhost:3000',
7 | AWS_ENDPOINT: process.env.ON_LAMBDA ? 'https://dynamodb.us-west-2.amazonaws.com' : "http://localhost:8000",
8 | AWS_ACCESS: 'AKIAIFS5D6E2GKOPIOIA', // removed after open source
9 | AWS_SECRET: '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',
10 | CLIENT_TOKEN_EXPIRES_IN: 24 * 60 * 60 * 30, // client token expires time TODO: test expiring
11 | EMAIL_TOKEN_EXPIRES_IN: 24 * 60 * 60, // email token expires time
12 |
13 | EMAIL_RECEIVING_VERIFICATION: 'timqian92@qq.com', // used for test
14 |
15 | EMAIL_SENDER: { // used to send mail by nodemailer
16 | service: 'Gmail',
17 | auth: {
18 | user: 'yiin.cloud@gmail.com',
19 | pass: 'yiinCloud931127',
20 | }
21 | },
22 |
23 | USER_MESSAGE: { // message sent to client
24 | MAIL_WILL_SEND: 'mail will be send soon',
25 | SIGNUP_SUCCESS: 'signup success',
26 | NAME_TAKEN: 'Name or email has been taken',
27 | USER_NOT_FOUND: 'User not found',
28 | EMAIL_NOT_FOUND: 'Email not found',
29 | WRONG_PASSWORD: 'wrong password',
30 | LOGIN_SUCCESS: 'Enjoy your token!',
31 | NEED_EMAIL_VERIFICATION: 'You need to verify your email first',
32 | },
33 |
34 | };
35 |
36 | module.exports = config;
37 |
--------------------------------------------------------------------------------
/backend/test/routeUser/needingTokenAndEmailVerified.js:
--------------------------------------------------------------------------------
1 | const axios = require('axios');
2 | const assert = require('assert');
3 | const config = require('../../config');
4 |
5 | module.exports = function password_reset() {
6 |
7 | let token = '';
8 |
9 | describe('GET /needingTokenAndEmailVerified', function () {
10 |
11 | before(async function() {
12 | const loginRes = await axios.post(`${config.API_URL}/user/login`, {
13 | name: 'tim',
14 | password: '123',
15 | }).catch((res) => { throw res.data; });
16 |
17 | token = loginRes.data.token;
18 | });
19 |
20 | it('should need token', function () {
21 | return axios.get(`${config.API_URL}/needingTokenAndEmailVerified`)
22 | .then((res) => {
23 | throw res;
24 | }).catch((err) => {
25 | // console.log(res.data);
26 | assert.equal(err.response.status, 403);
27 | });
28 | });
29 |
30 | it('should need email verified', function () {
31 | return axios.get(`${config.API_URL}/needingTokenAndEmailVerified`, {params: {token}})
32 | .then((res) => { throw res; })
33 | .catch((err) => {
34 | assert.equal(err.response.status, 400);
35 | });
36 | });
37 |
38 | // it('should success', function () {
39 | // return User.findOne({name: 'tim'}, (err, user) => {
40 | // user.verified = true;
41 | // user.save((err) => {
42 | // if (err) throw err;
43 | //
44 | // })
45 | // })
46 | // });
47 |
48 | });
49 | }
50 |
--------------------------------------------------------------------------------
/backend/routeUser/password_reset.js:
--------------------------------------------------------------------------------
1 | const createToken= require('../utils/createToken');
2 | const { hashPassword }= require('../utils/crypts');
3 | const sendMail= require('../utils/sendMail');
4 | const config= require('../config');
5 | const daos = require('../daos');
6 |
7 | module.exports = async function(req, res) {
8 |
9 | const { email, password } = req.body;
10 | if(!email || !password) {
11 | res.status(400).json('email and pass is needed');
12 | return;
13 | }
14 | // const user = await User.findOne( { email } );
15 | const userEmail = await daos.UserEmail.get(email);
16 | const user = await daos.User.get(userEmail.name);
17 |
18 | if (!user) {
19 | res.status(400).json({ success: false, message: config.USER_MESSAGE.USER_NOT_FOUND });
20 | } else {
21 | const hashedPassword = await hashPassword(password);
22 | const token = createToken({ email:userEmail.email, hashedPassword }, config.EMAIL_TOKEN_EXPIRES_IN);
23 | const verifyAddress =
24 | `${config.API_URL}/user/email_verification/?token=${token}`;
25 | const content = `
26 | Click to change your password.
27 |
NOTE: don't click it if you did not want to change password`;
28 |
29 | // send mail asyncly
30 | sendMail(email, content).then(info => {
31 | console.log(`Email to ${email} sent: ` + info.response);
32 | }).catch((err) => {
33 | console.error(`Email to ${email} err:` + err);
34 | });
35 |
36 | res.status(200).json({ success: true, message: config.USER_MESSAGE.MAIL_WILL_SEND });
37 |
38 | }
39 | }
40 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | ##
2 |
3 | Press a button
4 | 1. play mode: play the sound
5 | 2. record mode: play and record
6 |
7 |
8 | ## Refs
9 |
10 | - [midi file conversion to any working, audible Tone.js object](https://github.com/Tonejs/Tone.js/issues/137)
11 |
12 | ## Questions
13 |
14 | pass down props or import store directly?
15 |
16 |
17 | props in the next level is not updated,
18 |
19 |
20 | ## good examples
21 |
22 | examples in the readme of https://github.com/mudcube/MIDI.js
23 |
24 |
25 |
26 | ## How to deal with continuity:
27 |
28 | 1. 1px gap if clicked; remove 1px gap if continuly
29 | 2. better way: triple state of a cell
30 |
31 | ## let the user know sound strap scrollable: 半透明
32 | example: https://www.graph.cool/docs/reference/auth/permission-queries-iox3aqu0ee/
33 |
34 | ## TODO
35 | - [x] soundStrap 到底
36 | - [x] 第一行问题(没有 last row 的 Row unplayable)
37 | - [x] hit 时变色
38 | - [x] 布局改成pannel形式
39 | - [x] add more rows
40 | - [x] key binding!!!
41 | - [ ] 如果 share 到手机,就弄一个css播放的效果
42 | - [ ] login; signup; save song using graphql: https://github.com/apollographql/apollo-client/tree/lates
43 | - [ ] 反向 play
44 | - [ ] option to hide black keys
45 | - [ ] convert midi
46 | - [ ] login
47 | - [ ] save notes
48 | - [ ] share
49 | - [ ] more instruments!!!
50 | - [ ] add voice
51 | - [ ] repeat rows
52 | - [ ] user center
53 | - [ ] 广场(other people's work
54 | - [ ] scroll bar 移过来
55 | - [ ] change the way scrolling (currently speed not right)
56 | - [ ] Intro with https://github.com/HubSpot/shepherd
57 |
58 | ## UI ref
59 |
60 | github
61 | graphcool
62 |
63 | ## To learn
64 |
65 | flex box 布局
66 |
67 | ## add build script
68 |
69 | 1. build
70 | 2. remove source map(both css and js)
71 | 3. move to root
--------------------------------------------------------------------------------
/frontend/src/config/index.js:
--------------------------------------------------------------------------------
1 | export const KEY_NUM = 61;
2 |
3 | export const NUM_KEY_MAP = [
4 | // 'C-1', 'C#-1', 'D-1', 'D#-1', 'E-1', 'F-1', 'F#-1', 'G-1', 'G#-1', 'A-1', 'A#-1', 'B-1',
5 | // 'C0', 'C#0', 'D0', 'D#0', 'E0', 'F0', 'F#0', 'G0', 'G#0', 'A0', 'A#0', 'B0',
6 | // 'C1', 'C#1', 'D1', 'D#1', 'E1', 'F1', 'F#1', 'G1', 'G#1', 'A1', 'A#1', 'B1',
7 | 'C2', 'C#2', 'D2', 'D#2', 'E2', 'F2', 'F#2', 'G2', 'G#2', 'A2', 'A#2', 'B2',
8 | 'C3', 'C#3', 'D3', 'D#3', 'E3', 'F3', 'F#3', 'G3', 'G#3', 'A3', 'A#3', 'B3',
9 | 'C4', 'C#4', 'D4', 'D#4', 'E4', 'F4', 'F#4', 'G4', 'G#4', 'A4', 'A#4', 'B4',
10 | 'C5', 'C#5', 'D5', 'D#5', 'E5', 'F5', 'F#5', 'G5', 'G#5', 'A5', 'A#5', 'B5',
11 | 'C6', 'C#6', 'D6', 'D#6', 'E6', 'F6', 'F#6', 'G6', 'G#6', 'A6', 'A#6', 'B6',
12 | 'C7',// 'C#7', 'D7', 'D#7', 'E7', 'F7', 'F#7', 'G7', 'G#7', 'A7', 'A#7', 'B7',
13 | // 'C8', 'C#8', 'D8', 'D#8', 'E8', 'F8', 'F#8', 'G8', 'G#8', 'A8', 'A#8', 'B8',
14 | // 'C9', 'C#9', 'D9', 'D#9', 'E9', 'F9', 'F#9', 'G9', 'G#9', 'A9', 'A#9', 'B9',
15 | // 'C10', 'C#10', 'D10', 'D#10', 'E10', 'F10', 'F#10', 'G10',
16 | ];
17 |
18 | // pressed key and
19 | export const NUM_KEY_CODE_MAP = [
20 | 122, 90, 120, 88, 99, 118, 86, 98, 66, 110, 78, 109,
21 | 97, 65, 115, 83, 100, 102, 70, 103, 71, 104, 72, 106,
22 | 113, 81, 119, 87, 101, 114, 82, 116, 84, 121, 89, 117,
23 | 49, 33, 50, 64, 51, 52, 36, 53, 37, 54, 94, 55,
24 | 56
25 | ];
26 |
27 | export const NUM_UP_KEY_CODE_MAP = [
28 | 90, 90, 88, 88, 67, 86, 86, 66, 66, 78, 78, 77,
29 | 65, 65, 83, 83, 68, 70, 70, 71, 71, 72, 72, 74,
30 | 81, 81, 87, 87, 69, 82, 82, 84, 84, 89, 89, 85,
31 | 49, 49, 50, 50, 51, 52, 52, 53, 53, 54, 54, 55,
32 | 56
33 | ];
34 |
--------------------------------------------------------------------------------
/backend/app.js:
--------------------------------------------------------------------------------
1 | const userRouter = require('./routeUser');
2 | const graphqlRoute = require('./routeGraphql');
3 | const verifyToken = require('./middleware/verifyToken');
4 | const express = require('express');
5 | const bodyParser = require('body-parser');
6 | const methodOverride = require('method-override');
7 | const morgan = require('morgan');
8 | const { PORT = 3000 } = process.env;
9 |
10 | const app = express();
11 | app.use(bodyParser.urlencoded({ extended: false }));
12 | app.use(bodyParser.json());
13 | app.use(methodOverride());
14 | app.use(morgan('dev'));
15 | app.use('/user', userRouter);
16 |
17 | // protecting api
18 | app.get('/needingToken', verifyToken, (req, res) => {
19 |
20 | // send back the jwt claim directly
21 | const claim = req.decoded;
22 | if (!claim) {
23 | res.status(403).json('should needing token');
24 | return;
25 | }
26 | res.status(200).json(claim);
27 | });
28 |
29 | app.get('/needingTokenAndEmailVerified', verifyToken, (req, res) => {
30 | if (!req.decoded) {
31 | res.status(403).json('should needing token');
32 | } else if (req.decoded.emailVerified) {
33 | res.status(200).json(req.decoded);
34 | } else {
35 | res.status(400).json({
36 | success: false,
37 | message: 'Please verify your email before doing this!'
38 | });
39 | }
40 | });
41 |
42 | app.use('/graphql', verifyToken, graphqlRoute);
43 |
44 | app.listen(PORT);
45 | console.log(`API magic happens at http://localhost:${PORT}`);
46 |
47 | // handle unhandled promise rejection
48 | // https://nodejs.org/api/process.html#process_event_unhandledrejection
49 | process.on('unhandledRejection', function (reason, p) {
50 | console.log('Unhandled Rejection at: Promise ', p, ' reason: ', reason);
51 | // application specific logging, throwing an error, or other logic here
52 | });
53 |
--------------------------------------------------------------------------------
/backend/routeUser/login.js:
--------------------------------------------------------------------------------
1 | const createToken = require('../utils/createToken');
2 | const { checkPassword } = require('../utils/crypts');
3 | const config = require('../config');
4 | const daos = require('../daos');
5 | /**
6 | * {
7 | * email, // optional: provide email or name but not both
8 | * name, // optional
9 | * password,
10 | * }
11 | */
12 | module.exports = async function (req, res) {
13 | const { email, password } = req.body;
14 | let { name } = req.body;
15 | // const user = await User.findOne({ $or: [ { name }, { email } ] });
16 |
17 | if (email) {
18 | const userEmail = await daos.UserEmail.get(email);
19 | if (!userEmail) {
20 | res.status(400).json({ success: false, message: config.USER_MESSAGE.EMAIL_NOT_FOUND });
21 | return;
22 | }
23 | name = userEmail.name;
24 | }
25 |
26 | const user = await daos.User.get(name);
27 |
28 | if (!user) {
29 | res.status(400).json({ success: false, message: config.USER_MESSAGE.USER_NOT_FOUND });
30 | return;
31 | } else if (user) {
32 |
33 | // check if password matches
34 | const result = await checkPassword(password, user.hashedPassword);
35 | if (!result) {
36 | res.status(400).json({ success: false, message: config.USER_MESSAGE.WRONG_PASSWORD });
37 | return;
38 | } else {
39 | // if user is found and password is right
40 | // create a token
41 | const payload = { name: user.name, emailVerified: user.emailVerified };
42 | const token = createToken(payload, config.CLIENT_TOKEN_EXPIRES_IN);
43 |
44 | // return the information including token as JSON
45 | res.status(200).json({
46 | success: true,
47 | message: `${config.USER_MESSAGE.LOGIN_SUCCESS} ${user.name}`,
48 | name: user.name,
49 | token: token,
50 | });
51 | }
52 | }
53 | }
54 |
--------------------------------------------------------------------------------
/backend/routeUser/signup.js:
--------------------------------------------------------------------------------
1 | const createToken = require('../utils/createToken');
2 | const sendMail = require('../utils/sendMail');
3 | const { hashPassword } = require('../utils/crypts');
4 | const config = require('../config');
5 | const daos = require('../daos');
6 |
7 | module.exports = async function (req, res) {
8 | const { email, name, password } = req.body;
9 |
10 | if (!name || !password || !email) {
11 | res.status(400).json({ success: false, message: 'Should provide email, name and password', });
12 | return;
13 | }
14 |
15 | // see if name or email is occupied
16 | const user = await daos.User.get(name);
17 | const userEmail = await daos.UserEmail.get(email);
18 |
19 | if (!user && !userEmail) {
20 | const hashedPassword = await hashPassword(password);
21 | await daos.User.put({
22 | name,
23 | email,
24 | hashedPassword,
25 | emailVerified: false,
26 | });
27 |
28 | await daos.UserEmail.put({
29 | email,
30 | name,
31 | });
32 |
33 | console.log('____User saved');
34 |
35 | // send verification email
36 | const token = createToken({ name, email });
37 | const verifyAddress =
38 | `${config.API_URL}/user/email_verification/?token=${token}`;
39 | const content =
40 | `
41 | Click to verify your email address.
42 | `;
43 |
44 | // send mail asyncly
45 | sendMail(email, content).then(info => {
46 | console.log(`Email to ${email} sent: ` + info.response);
47 | }).catch((err) => {
48 | console.error(`Email to ${email} err:` + err);
49 | });
50 |
51 | res.status(200).json({ success: true, message: config.USER_MESSAGE.SIGNUP_SUCCESS });
52 |
53 | } else {
54 | console.log('____User not saved, name or email has been taken');
55 | res.status(400).json({ success: false, message: config.USER_MESSAGE.NAME_TAKEN, });
56 | }
57 | }
58 |
--------------------------------------------------------------------------------
/frontend/src/components/Strap/Row/style.css:
--------------------------------------------------------------------------------
1 | .Row {
2 | --border-width: 1px;
3 | display: inline-block;
4 | border-right: var(--border-width) solid gray;
5 | /*
6 | prevent line to be breaked by white space
7 | https://developer.mozilla.org/en-US/docs/Web/CSS/white-space
8 | */
9 | white-space: nowrap;
10 | }
11 |
12 | .cellWrapper {
13 | position: relative;
14 | display: inline-block;
15 | /* user-select: none; */
16 | }
17 |
18 | .writeCell {
19 | display: inline-block;
20 | width: 26px;
21 | height: 20px;
22 | background-color: white;
23 | border-left: var(--border-width) solid gray;
24 |
25 | }
26 |
27 | .blackCell {
28 | position: absolute;
29 | top:0;
30 | z-index: 1;
31 | left: 20px;
32 | width: 13px;
33 | height: 20px;
34 | border-left: var(--border-width) solid #ddd;
35 | border-right: var(--border-width) solid #ddd;
36 | /* background-color: white; */
37 | }
38 |
39 | .cell {
40 | border-top: var(--border-width) solid gray;
41 | box-sizing: border-box;
42 | }
43 |
44 | .cell:hover {
45 | cursor:Pointer;
46 | background-color: gray;
47 | }
48 |
49 | .cell.onPressContinue {
50 | /* background-color: red; */
51 | border-top: 0;
52 | }
53 |
54 | .C.onPress {
55 | background-color: red;
56 | }
57 |
58 | .Csharp.onPress {
59 | background-color: orangered;
60 | }
61 |
62 | .D.onPress {
63 | background-color: orange;
64 | }
65 |
66 | .Dsharp.onPress {
67 | background-color: #ffcc00;
68 | }
69 |
70 | .E.onPress {
71 | background-color: yellow;
72 | }
73 |
74 | .F.onPress {
75 | background-color: greenyellow;
76 | }
77 |
78 | .Fsharp.onPress {
79 | background-color: green;
80 | }
81 |
82 | .G.onPress {
83 | background-color: cyan;
84 | }
85 |
86 | .Gsharp.onPress {
87 | background-color: blue;
88 | }
89 |
90 | .A.onPress {
91 | background-color: blueviolet;
92 | }
93 |
94 | .Asharp.onPress {
95 | background-color: violet;
96 | }
97 |
98 | .B.onPress {
99 | background-color: magenta;
100 | }
--------------------------------------------------------------------------------
/frontend/src/App.js:
--------------------------------------------------------------------------------
1 | import React, { Component } from 'react';
2 | import './App.css';
3 | import './bttn.css'
4 | import { observer } from 'mobx-react';
5 | import Song from './components/Song';
6 |
7 | // const mobxDevtools = require('mobx-react-devtools');
8 |
9 | @observer // inform componenet when store updates
10 | class App extends Component {
11 |
12 | render() {
13 | const store = this.props.store;
14 |
15 | return (
16 |
17 |
18 |
19 |
20 |
21 |
24 |
27 |
30 |
33 |
34 | {/*
*/}
37 |
38 |
39 |
40 | {/* Name of the song will be here */}
41 | Hi there~ this is an unfinished product waiting for your suggestion.
42 | {/* */}
45 |
46 |
47 |
48 | {/*
*/}
49 |
50 | );
51 | }
52 | }
53 |
54 | export default App;
55 |
--------------------------------------------------------------------------------
/frontend/src/index.js:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import ReactDOM from 'react-dom';
3 | import './index.css';
4 | import App from './App';
5 | import registerServiceWorker from './registerServiceWorker';
6 | import store from './store2.js';
7 | // import Tone from 'tone';
8 | // import MidiConvert from 'midiconvert'
9 |
10 | ReactDOM.render(, document.getElementById('root'));
11 | registerServiceWorker();
12 |
13 |
14 | // var synth = new Tone.PolySynth(8).toMaster()
15 |
16 | // const midi = {
17 | // "header": {
18 | // "PPQ": 480,
19 | // "bpm": 120, // beats per minute
20 | // "name": ""
21 | // },
22 | // "startTime": 0,
23 | // "duration": 4,
24 | // "tracks": [
25 | // {
26 | // "startTime": 0,
27 | // "duration": 4,
28 | // "length": 3,
29 | // "notes": [
30 | // {
31 | // "name": "C4",
32 | // // "midi": 60,
33 | // "time": 0,
34 | // // "velocity": 1,
35 | // "duration": 1
36 | // },
37 | // {
38 | // "name": "D#4",
39 | // // "midi": 63,
40 | // "time": 0,
41 | // "velocity": 1,
42 | // "duration": 2
43 | // },
44 | // {
45 | // "name": "C4",
46 | // // "midi": 60,
47 | // "time": 3,
48 | // // "velocity": 1,
49 | // "duration": 2.576
50 | // }
51 | // ],
52 | // "controlChanges": {},
53 | // "id": 0,
54 | // "instrumentNumber": 32,
55 | // "instrument": "acoustic bass",
56 | // "instrumentFamily": "bass",
57 | // "channelNumber": 0,
58 | // "isPercussion": false
59 | // }
60 | // ]
61 | // };
62 |
63 | // // make sure you set the tempo before you schedule the events
64 | // Tone.Transport.bpm.value = midi.header.bpm
65 |
66 | // // pass in the note events from one of the tracks as the second argument to Tone.Part
67 | // new Tone.Part(function(time, note) {
68 |
69 | // //use the events to play the synth
70 | // synth.triggerAttackRelease(note.name, note.duration, time, note.velocity)
71 |
72 | // }, midi.tracks[0].notes).start()
73 |
74 | // // start the transport to hear the events
75 | // Tone.Transport.start()
--------------------------------------------------------------------------------
/backend/test/routeUser/login.js:
--------------------------------------------------------------------------------
1 | const axios = require('axios');
2 | const assert = require('assert');
3 | const config = require('../../config');
4 |
5 | module.exports = function login() {
6 | describe('POST /login', function () {
7 |
8 | it('name login', function () {
9 | return axios.post(`${config.API_URL}/user/login`, {
10 | name: 'tim',
11 | password: '123',
12 | }).then((res) => {
13 | assert.equal(res.status, 200);
14 | });
15 | });
16 |
17 | it('email login', function () {
18 | return axios.post(`${config.API_URL}/user/login`, {
19 | email: `${config.EMAIL_RECEIVING_VERIFICATION}`,
20 | password: '123',
21 | }).then((res) => {
22 | assert.equal(res.status, 200);
23 | });
24 | });
25 |
26 | it('wrong user name', function () {
27 | return axios.post(`${config.API_URL}/user/login`, {
28 | name: `tim${Date.now()}`,
29 | password: '123',
30 | }).then((res) => {
31 | console.log('should fail');
32 | throw res;
33 | }).catch((err) => {
34 | assert.equal(err.response.status, 400);
35 | });
36 | });
37 |
38 | it('wrong email', function () {
39 | return axios.post(`${config.API_URL}/user/login`, {
40 | email: `tim${Date.now()}@qq.com`,
41 | password: '123',
42 | }).then((res) => {
43 | console.log('should fail');
44 | throw res;
45 | }).catch((err) => {
46 | assert.equal(err.response.status, 400);
47 | });
48 | });
49 |
50 | it('wrong password with name', function () {
51 | return axios.post(`${config.API_URL}/user/login`, {
52 | name: `tim`,
53 | password: '1234',
54 | }).then((res) => {
55 | console.log('should fail');
56 | throw res;
57 | }).catch((err) => {
58 | assert.equal(err.response.status, 400);
59 | });
60 | });
61 |
62 | it('wrong password with email', function () {
63 | return axios.post(`${config.API_URL}/user/login`, {
64 | email: `${config.EMAIL_RECEIVING_VERIFICATION}`,
65 | password: '1234',
66 | }).then((res) => {
67 | console.log('should fail');
68 | throw res;
69 | }).catch((err) => {
70 | assert.equal(err.response.status, 400);
71 | });
72 | });
73 | });
74 | }
75 |
--------------------------------------------------------------------------------
/frontend/src/store.js:
--------------------------------------------------------------------------------
1 | import Tone from 'tone';
2 | import { observable, autorun } from 'mobx';
3 |
4 | let pressedArr = [];
5 | const synthArr = [];
6 |
7 | // TODO: maybe memary problem: 128 keys make the sound weird
8 | for (let i = 0; i < 61; i++) {
9 | pressedArr[i] = 0;
10 | synthArr[i] = new Tone.Synth().toMaster();
11 | }
12 |
13 | pressedArr = observable(pressedArr);
14 |
15 | const store = {
16 | pressedArr: pressedArr,
17 | notes2D: [], // 2 D notes arr
18 | bpm: 80, // beats per minites (scroll spead)
19 | time: 0,
20 | isPlaying: false,
21 | addNote(i) {
22 | lastKeyArr = this.pressedArr.slice();
23 | this.pressedArr[i] = 1;
24 | },
25 | deleteNote(i) {
26 | lastKeyArr = this.pressedArr.slice();
27 | this.pressedArr[i] = 0;
28 | }
29 | }
30 |
31 | let lastKeyArr = [];
32 |
33 | autorun(() => {
34 | pressedArr.forEach((key, i) => {
35 | if (key === 1 && lastKeyArr[i] === 0) {
36 | console.log('attacking', i, numberNameMap[i]);
37 | synthArr[i].triggerAttack(numberNameMap[i]);
38 | } else if (key === 0 && lastKeyArr[i] === 1) {
39 | // console.log('release', i);
40 | synthArr[i].triggerRelease();
41 | }
42 | })
43 | }).onError(e => {
44 | console.log(e)
45 | })
46 |
47 | const numberNameMap = [
48 | // 'C-1', 'C#-1', 'D-1', 'D#-1', 'E-1', 'F-1', 'F#-1', 'G-1', 'G#-1', 'A-1', 'A#-1', 'B-1',
49 | // 'C0', 'C#0', 'D0', 'D#0', 'E0', 'F0', 'F#0', 'G0', 'G#0', 'A0', 'A#0', 'B0',
50 | // 'C1', 'C#1', 'D1', 'D#1', 'E1', 'F1', 'F#1', 'G1', 'G#1', 'A1', 'A#1', 'B1',
51 | 'C2', 'C#2', 'D2', 'D#2', 'E2', 'F2', 'F#2', 'G2', 'G#2', 'A2', 'A#2', 'B2',
52 | 'C3', 'C#3', 'D3', 'D#3', 'E3', 'F3', 'F#3', 'G3', 'G#3', 'A3', 'A#3', 'B3',
53 | 'C4', 'C#4', 'D4', 'D#4', 'E4', 'F4', 'F#4', 'G4', 'G#4', 'A4', 'A#4', 'B4',
54 | 'C5', 'C#5', 'D5', 'D#5', 'E5', 'F5', 'F#5', 'G5', 'G#5', 'A5', 'A#5', 'B5',
55 | 'C6', 'C#6', 'D6', 'D#6', 'E6', 'F6', 'F#6', 'G6', 'G#6', 'A6', 'A#6', 'B6',
56 | 'C7', 'C#7', 'D7', 'D#7', 'E7', 'F7', 'F#7', 'G7', 'G#7', 'A7', 'A#7', 'B7',
57 | 'C8', 'C#8', 'D8', 'D#8', 'E8', 'F8', 'F#8', 'G8', 'G#8', 'A8', 'A#8', 'B8',
58 | 'C9', 'C#9', 'D9', 'D#9', 'E9', 'F9', 'F#9', 'G9', 'G#9', 'A9', 'A#9', 'B9',
59 | 'C10', 'C#10', 'D10', 'D#10', 'E10', 'F10', 'F#10', 'G10',
60 | ];
61 |
62 | export default store;
63 |
64 |
--------------------------------------------------------------------------------
/frontend/src/components/Strap/index.js:
--------------------------------------------------------------------------------
1 | import React, { Component } from 'react';
2 | import './style.css';
3 | import Row from './Row';
4 | import { observer } from 'mobx-react';
5 | import { KEY_NUM } from '../../config';
6 |
7 | @observer
8 | export default class Strap extends Component {
9 | render() {
10 | const store = this.props.store;
11 | const notes2D = store.notes2D;
12 | const rowArr = notes2D.map((notes, i, notes2D) => {
13 | const emptyRow = [];
14 | for (let i = 0; i < KEY_NUM; i++) {
15 | emptyRow.push(0);
16 | }
17 | const lastRow = notes2D[i - 1] ? notes2D[i - 1] : emptyRow;
18 |
19 | // TODO: add add button
20 | return (
21 |
28 |
29 | );
30 | });
31 |
32 | function handleScroll() {
33 | const currentPosition = document.querySelector('.strap').scrollTop;
34 | // FIXME: 20 is the height of cell defined both here and in css; need to find a better way to find the currentRow
35 | const currentRow = Math.floor((currentPosition - 1) / 20);
36 | store.currentRow = currentRow;
37 | }
38 | return (
39 | handleScroll()}
41 | >
42 | {/** whiteSpace div is used to control the position of rowArr*/}
43 | {store.isKeyboardUp ? (
) : (
)}
44 | {rowArr}
45 |
46 |
57 | {!store.isKeyboardUp ?
: (
)}
58 |
59 | )
60 | }
61 | }
--------------------------------------------------------------------------------
/frontend/public/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
11 |
12 |
13 |
14 |
23 | Yiin Cloud
24 |
25 |
47 |
48 |
49 |
50 |
53 |
54 |
64 |
65 |
66 |
--------------------------------------------------------------------------------
/frontend/src/components/Keyboard/style.css:
--------------------------------------------------------------------------------
1 | .Keyboard {
2 | --border-color: black;
3 | --border-width: 1px;
4 | display: inline-block;
5 | border-right: var(--border-width) solid var(--border-color);
6 | border-bottom: var(--border-width) solid black;
7 | white-space: nowrap;
8 | }
9 |
10 | .keyWrapper {
11 | white-space: nowrap;
12 | position: relative;
13 | display: inline-block;
14 | border-bottom: 1px solid var(--border-color);
15 | }
16 |
17 | .writeKey {
18 | display: inline-block;
19 | width: 26px;
20 | height: 10vh;
21 | background-color: white;
22 | border-left: var(--border-width) solid var(--border-color);
23 | }
24 |
25 | .blackKey {
26 | position: absolute;
27 | z-index: 1;
28 | top: 0;
29 | left: 20px;
30 | width: 13px;
31 | height: 6vh;
32 | background-color: black;
33 | border-left: 1px solid black;
34 | border-right: var(--border-width) solid var(--border-color);
35 | border-bottom: var(--border-width) solid var(--border-color);
36 | }
37 |
38 | .key {
39 | box-sizing: border-box;
40 | }
41 |
42 | .key.C {
43 | border-top: 5px solid red;
44 | }
45 |
46 | .key.Csharp {
47 | border-top: 5px solid orangered;
48 | }
49 |
50 | .key.D {
51 | border-top: 5px solid orange;
52 | }
53 |
54 | .key.Dsharp {
55 | border-top: 5px solid #ffcc00;
56 | }
57 |
58 | .key.E {
59 | border-top: 5px solid yellow;
60 | }
61 |
62 | .key.F {
63 | border-top: 5px solid greenyellow;
64 | }
65 |
66 | .key.Fsharp {
67 | border-top: 5px solid green;
68 | }
69 |
70 | .key.G {
71 | border-top: 5px solid cyan;
72 | }
73 |
74 | .key.Gsharp {
75 | border-top: 5px solid blue;
76 | }
77 |
78 | .key.A {
79 | border-top: 5px solid blueviolet;
80 | }
81 |
82 | .key.Asharp {
83 | border-top: 5px solid violet;
84 | }
85 |
86 | .key.B {
87 | border-top: 5px solid magenta;
88 | }
89 |
90 | .key.C:hover {
91 | background-color: red; cursor:Pointer;
92 | }
93 |
94 | .key.Csharp:hover {
95 | background-color: orangered; cursor:Pointer;
96 | }
97 |
98 | .key.D:hover {
99 | background-color: orange; cursor:Pointer;
100 | }
101 |
102 | .key.Dsharp:hover {
103 | background-color: #ffcc00; cursor:Pointer;
104 | }
105 |
106 | .key.E:hover {
107 | background-color: yellow; cursor:Pointer;
108 | }
109 |
110 | .key.F:hover {
111 | background-color: greenyellow; cursor:Pointer;
112 | }
113 |
114 | .key.Fsharp:hover {
115 | background-color: green; cursor:Pointer;
116 | }
117 |
118 | .key.G:hover {
119 | background-color: cyan; cursor:Pointer;
120 | }
121 |
122 | .key.Gsharp:hover {
123 | background-color: blue; cursor:Pointer;
124 | }
125 |
126 | .key.A:hover {
127 | background-color: blueviolet; cursor:Pointer;
128 | }
129 |
130 | .key.Asharp:hover {
131 | background-color: violet; cursor:Pointer;
132 | }
133 |
134 | .key.B:hover {
135 | background-color: magenta; cursor:Pointer;
136 | }
137 |
138 |
--------------------------------------------------------------------------------
/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 | Yiin Cloud
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
--------------------------------------------------------------------------------
/service-worker.js:
--------------------------------------------------------------------------------
1 | "use strict";function setOfCachedUrls(e){return e.keys().then(function(e){return e.map(function(e){return e.url})}).then(function(e){return new Set(e)})}var precacheConfig=[["/index.html","a0e0bdd71c8201eb268d58b12154836c"],["/static/css/main.9c2864c1.css","48697f567e000a4c31db40ac7352d6e8"],["/static/js/main.12f54783.js","712c21e01695d4b7ecf7b220f478ecde"]],cacheName="sw-precache-v3-sw-precache-webpack-plugin-"+(self.registration?self.registration.scope:""),ignoreUrlParametersMatching=[/^utm_/],addDirectoryIndex=function(e,t){var n=new URL(e);return"/"===n.pathname.slice(-1)&&(n.pathname+=t),n.toString()},cleanResponse=function(e){return e.redirected?("body"in e?Promise.resolve(e.body):e.blob()).then(function(t){return new Response(t,{headers:e.headers,status:e.status,statusText:e.statusText})}):Promise.resolve(e)},createCacheKey=function(e,t,n,r){var a=new URL(e);return r&&a.pathname.match(r)||(a.search+=(a.search?"&":"")+encodeURIComponent(t)+"="+encodeURIComponent(n)),a.toString()},isPathWhitelisted=function(e,t){if(0===e.length)return!0;var n=new URL(t).pathname;return e.some(function(e){return n.match(e)})},stripIgnoredUrlParameters=function(e,t){var n=new URL(e);return n.hash="",n.search=n.search.slice(1).split("&").map(function(e){return e.split("=")}).filter(function(e){return t.every(function(t){return!t.test(e[0])})}).map(function(e){return e.join("=")}).join("&"),n.toString()},hashParamName="_sw-precache",urlsToCacheKeys=new Map(precacheConfig.map(function(e){var t=e[0],n=e[1],r=new URL(t,self.location),a=createCacheKey(r,hashParamName,n,/\.\w{8}\./);return[r.toString(),a]}));self.addEventListener("install",function(e){e.waitUntil(caches.open(cacheName).then(function(e){return setOfCachedUrls(e).then(function(t){return Promise.all(Array.from(urlsToCacheKeys.values()).map(function(n){if(!t.has(n)){var r=new Request(n,{credentials:"same-origin"});return fetch(r).then(function(t){if(!t.ok)throw new Error("Request for "+n+" returned a response with status "+t.status);return cleanResponse(t).then(function(t){return e.put(n,t)})})}}))})}).then(function(){return self.skipWaiting()}))}),self.addEventListener("activate",function(e){var t=new Set(urlsToCacheKeys.values());e.waitUntil(caches.open(cacheName).then(function(e){return e.keys().then(function(n){return Promise.all(n.map(function(n){if(!t.has(n.url))return e.delete(n)}))})}).then(function(){return self.clients.claim()}))}),self.addEventListener("fetch",function(e){if("GET"===e.request.method){var t,n=stripIgnoredUrlParameters(e.request.url,ignoreUrlParametersMatching);(t=urlsToCacheKeys.has(n))||(n=addDirectoryIndex(n,"index.html"),t=urlsToCacheKeys.has(n));!t&&"navigate"===e.request.mode&&isPathWhitelisted(["^(?!\\/__).*"],e.request.url)&&(n=new URL("/index.html",self.location).toString(),t=urlsToCacheKeys.has(n)),t&&e.respondWith(caches.open(cacheName).then(function(e){return e.match(urlsToCacheKeys.get(n)).then(function(e){if(e)return e;throw Error("The cached response that was expected is missing.")})}).catch(function(t){return console.warn('Couldn\'t serve response for "%s" from cache: %O',e.request.url,t),fetch(e.request)}))}});
--------------------------------------------------------------------------------
/backend/scripts/createTables.js:
--------------------------------------------------------------------------------
1 | var {dynamoDb} = require('../daos/db');
2 |
3 | (async () =>{
4 | await dynamoDb.createTable({
5 | TableName: "User",
6 | KeySchema: [{AttributeName: 'name', KeyType: 'HASH'}],
7 | AttributeDefinitions: [
8 | {AttributeName: 'name', AttributeType: 'S'},
9 | // {AttributeName: 'password', AttributeType: 'S'},
10 | ],
11 | ProvisionedThroughput: {
12 | ReadCapacityUnits: 5,
13 | WriteCapacityUnits:5,
14 | }
15 | }).promise().catch(err => {
16 | console.log(err);
17 | });
18 |
19 | // ref: https://stackoverflow.com/questions/12920884/is-there-a-way-to-enforce-unique-constraint-on-a-property-field-other-than-the
20 | await dynamoDb.createTable({
21 | TableName: 'UserEmail',
22 | KeySchema: [{AttributeName:'email', KeyType:'HASH'}],
23 | AttributeDefinitions: [
24 | {AttributeName: 'email', AttributeType: 'S'},
25 | // {AttributeName: 'name', AttributeType: 'S'},
26 | ],
27 | ProvisionedThroughput: {
28 | ReadCapacityUnits: 5,
29 | WriteCapacityUnits:5,
30 | }
31 | }).promise().catch(err => {
32 | console.log(err);
33 | });
34 |
35 | await dynamoDb.createTable({
36 | TableName: 'Song',
37 | KeySchema: [
38 | {AttributeName: 'author', KeyType:'HASH'},
39 | {AttributeName: 'name', KeyType: 'RANGE'},
40 | ],
41 | AttributeDefinitions: [
42 | {AttributeName: 'author', AttributeType: 'S'},
43 | {AttributeName: 'name', AttributeType: 'S'},
44 | ],
45 | ProvisionedThroughput: {
46 | ReadCapacityUnits: 5,
47 | WriteCapacityUnits: 5,
48 | }
49 | }).promise().catch(err => {
50 | console.log(err);
51 | });
52 |
53 | console.log('tabels created');
54 | })();
55 |
56 | // dynamodb.createTable({
57 | // TableName: "Users",
58 | // KeySchema: [{ AttributeName: "name", KeyType: "HASH" }],
59 | // GlobalSecondaryIndexes: [ // optional (list of GlobalSecondaryIndex)
60 | // {
61 | // IndexName: 'email',
62 | // KeySchema: [
63 | // { AttributeName: 'email', KeyType: 'HASH',},
64 | // ],
65 | // Projection: { // attributes to project into the index
66 | // ProjectionType: 'INCLUDE', // (ALL | KEYS_ONLY | INCLUDE)
67 | // NonKeyAttributes: [ // required / allowed only for INCLUDE
68 | // 'email',
69 | // // ... more attribute names ...
70 | // ],
71 | // },
72 | // ProvisionedThroughput: { // throughput to provision to the index
73 | // ReadCapacityUnits: 1,
74 | // WriteCapacityUnits: 1,
75 | // },
76 | // },
77 | // // ... more global secondary indexes ...
78 | // ],
79 | // AttributeDefinitions: [
80 | // { AttributeName: "name", AttributeType: "S" },
81 | // { AttributeName: "email", AttributeType: "S" }
82 | // ],
83 | // ProvisionedThroughput: {
84 | // ReadCapacityUnits: 10,
85 | // WriteCapacityUnits: 10
86 | // }
87 | // }).promise().then(result => {
88 | // console.log(result);
89 | // }).catch(err => {
90 | // console.log(err);
91 | // });
--------------------------------------------------------------------------------
/frontend/src/registerServiceWorker.js:
--------------------------------------------------------------------------------
1 | // In production, we register a service worker to serve assets from local cache.
2 |
3 | // This lets the app load faster on subsequent visits in production, and gives
4 | // it offline capabilities. However, it also means that developers (and users)
5 | // will only see deployed updates on the "N+1" visit to a page, since previously
6 | // cached resources are updated in the background.
7 |
8 | // To learn more about the benefits of this model, read https://goo.gl/KwvDNy.
9 | // This link also includes instructions on opting out of this behavior.
10 |
11 | const isLocalhost = Boolean(
12 | window.location.hostname === 'localhost' ||
13 | // [::1] is the IPv6 localhost address.
14 | window.location.hostname === '[::1]' ||
15 | // 127.0.0.1/8 is considered localhost for IPv4.
16 | window.location.hostname.match(
17 | /^127(?:\.(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)){3}$/
18 | )
19 | );
20 |
21 | export default function register() {
22 | if (process.env.NODE_ENV === 'production' && 'serviceWorker' in navigator) {
23 | // The URL constructor is available in all browsers that support SW.
24 | const publicUrl = new URL(process.env.PUBLIC_URL, window.location);
25 | if (publicUrl.origin !== window.location.origin) {
26 | // Our service worker won't work if PUBLIC_URL is on a different origin
27 | // from what our page is served on. This might happen if a CDN is used to
28 | // serve assets; see https://github.com/facebookincubator/create-react-app/issues/2374
29 | return;
30 | }
31 |
32 | window.addEventListener('load', () => {
33 | const swUrl = `${process.env.PUBLIC_URL}/service-worker.js`;
34 |
35 | if (!isLocalhost) {
36 | // Is not local host. Just register service worker
37 | registerValidSW(swUrl);
38 | } else {
39 | // This is running on localhost. Lets check if a service worker still exists or not.
40 | checkValidServiceWorker(swUrl);
41 | }
42 | });
43 | }
44 | }
45 |
46 | function registerValidSW(swUrl) {
47 | navigator.serviceWorker
48 | .register(swUrl)
49 | .then(registration => {
50 | registration.onupdatefound = () => {
51 | const installingWorker = registration.installing;
52 | installingWorker.onstatechange = () => {
53 | if (installingWorker.state === 'installed') {
54 | if (navigator.serviceWorker.controller) {
55 | // At this point, the old content will have been purged and
56 | // the fresh content will have been added to the cache.
57 | // It's the perfect time to display a "New content is
58 | // available; please refresh." message in your web app.
59 | console.log('New content is available; please refresh.');
60 | } else {
61 | // At this point, everything has been precached.
62 | // It's the perfect time to display a
63 | // "Content is cached for offline use." message.
64 | console.log('Content is cached for offline use.');
65 | }
66 | }
67 | };
68 | };
69 | })
70 | .catch(error => {
71 | console.error('Error during service worker registration:', error);
72 | });
73 | }
74 |
75 | function checkValidServiceWorker(swUrl) {
76 | // Check if the service worker can be found. If it can't reload the page.
77 | fetch(swUrl)
78 | .then(response => {
79 | // Ensure service worker exists, and that we really are getting a JS file.
80 | if (
81 | response.status === 404 ||
82 | response.headers.get('content-type').indexOf('javascript') === -1
83 | ) {
84 | // No service worker found. Probably a different app. Reload the page.
85 | navigator.serviceWorker.ready.then(registration => {
86 | registration.unregister().then(() => {
87 | window.location.reload();
88 | });
89 | });
90 | } else {
91 | // Service worker found. Proceed as normal.
92 | registerValidSW(swUrl);
93 | }
94 | })
95 | .catch(() => {
96 | console.log(
97 | 'No internet connection found. App is running in offline mode.'
98 | );
99 | });
100 | }
101 |
102 | export function unregister() {
103 | if ('serviceWorker' in navigator) {
104 | navigator.serviceWorker.ready.then(registration => {
105 | registration.unregister();
106 | });
107 | }
108 | }
109 |
--------------------------------------------------------------------------------
/frontend/src/components/Keyboard/index.js:
--------------------------------------------------------------------------------
1 | // keyboard with keys to be pressed down
2 |
3 | import React, { Component } from 'react';
4 | import './style.css';
5 | import { attack, release } from '../../utils/notePlayer.js';
6 | import { observer } from 'mobx-react';
7 |
8 | @observer
9 | export default class Board extends Component {
10 | componentDidMount() {
11 | document.querySelectorAll(`.key`)
12 | .forEach((key, i) => {
13 | key.addEventListener('mousedown', () => {
14 | attack(i);
15 | });
16 | key.addEventListener('mouseup', () => {
17 | release(i);
18 | });
19 |
20 | key.addEventListener('mouseleave', () => {
21 | release(i);
22 | })
23 | })
24 | }
25 |
26 | render() {
27 | const pressedArr = this.props.pressedArr;
28 | const pressedKeys = pressedArr ? pressedArr.map(isPress => isPress ? 'onPress' : '') : [];
29 | const isMouseDown = this.props.isMouseDown;
30 |
31 | function handleMouseOver(i) {
32 | if (isMouseDown) {
33 | attack(i);
34 | }
35 | }
36 |
37 | return (
38 |
39 |
40 |
handleMouseOver(0)}>
41 |
handleMouseOver(1)}>
42 |
43 |
44 |
handleMouseOver(2)}>
45 |
handleMouseOver(3)}>
46 |
47 |
48 |
handleMouseOver(4)}>
49 |
50 |
51 |
handleMouseOver(5)}>
52 |
handleMouseOver(6)}>
53 |
54 |
55 |
handleMouseOver(7)}>
56 |
handleMouseOver(8)}>
57 |
58 |
59 |
handleMouseOver(9)}>
60 |
handleMouseOver(10)}>
61 |
62 |
63 |
handleMouseOver(11)}>
64 |
65 |
66 |
67 |
handleMouseOver(12)}>
68 |
handleMouseOver(13)}>
69 |
70 |
71 |
handleMouseOver(14)}>
72 |
handleMouseOver(15)}>
73 |
74 |
75 |
handleMouseOver(16)}>
76 |
77 |
78 |
handleMouseOver(17)}>
79 |
handleMouseOver(18)}>
80 |
81 |
82 |
handleMouseOver(19)}>
83 |
handleMouseOver(20)}>
84 |
85 |
86 |
handleMouseOver(21)}>
87 |
handleMouseOver(22)}>
88 |
89 |
90 |
handleMouseOver(23)}>
91 |
92 |
93 |
94 |
handleMouseOver(24)}>
95 |
handleMouseOver(25)}>
96 |
97 |
98 |
handleMouseOver(26)}>
99 |
handleMouseOver(27)}>
100 |
101 |
102 |
handleMouseOver(28)}>
103 |
104 |
105 |
handleMouseOver(29)}>
106 |
handleMouseOver(30)}>
107 |
108 |
109 |
handleMouseOver(31)}>
110 |
handleMouseOver(32)}>
111 |
112 |
113 |
handleMouseOver(33)}>
114 |
handleMouseOver(34)}>
115 |
116 |
117 |
handleMouseOver(35)}>
118 |
119 |
120 |
121 |
handleMouseOver(36)}>
122 |
handleMouseOver(37)}>
123 |
124 |
125 |
handleMouseOver(38)}>
126 |
handleMouseOver(39)}>
127 |
128 |
129 |
handleMouseOver(40)}>
130 |
131 |
132 |
handleMouseOver(41)}>
133 |
handleMouseOver(42)}>
134 |
135 |
136 |
handleMouseOver(43)}>
137 |
handleMouseOver(44)}>
138 |
139 |
140 |
handleMouseOver(45)}>
141 |
handleMouseOver(46)}>
142 |
143 |
144 |
handleMouseOver(47)}>
145 |
146 |
147 |
148 |
handleMouseOver(48)}>
149 |
handleMouseOver(49)}>
150 |
151 |
152 |
handleMouseOver(50)}>
153 |
handleMouseOver(51)}>
154 |
155 |
156 |
handleMouseOver(52)}>
157 |
158 |
159 |
handleMouseOver(53)}>
160 |
handleMouseOver(54)}>
161 |
162 |
163 |
handleMouseOver(55)}>
164 |
handleMouseOver(56)}>
165 |
166 |
167 |
handleMouseOver(57)}>
168 |
handleMouseOver(58)}>
169 |
170 |
171 |
handleMouseOver(59)}>
172 |
173 |
174 |
175 |
handleMouseOver(60)} >
176 |
177 |
178 | );
179 | }
180 | }
--------------------------------------------------------------------------------
/frontend/src/components/Strap/Row/index.js:
--------------------------------------------------------------------------------
1 | import React, { Component } from 'react';
2 | import './style.css';
3 | import { attack, release } from '../../../utils/notePlayer.js';
4 | import { observer } from 'mobx-react';
5 |
6 | let noteMark = 1; // the notes recored with mousedown and move have the same pressType;
7 |
8 | @observer
9 | export default class Row extends Component {
10 | componentDidMount() {
11 | const rowNum = this.props.rowNum;
12 | const rowClassName = `Row-${rowNum}`;
13 | const notes = this.props.notes;
14 | document.querySelectorAll(`.${rowClassName} .cell`)
15 | .forEach((cell, i) => {
16 | cell.addEventListener('mousedown', () => {
17 | if (notes[i]) {
18 | notes[i] = 0;
19 | } else {
20 | notes[i] = noteMark;
21 | attack(i);
22 | }
23 | });
24 | cell.addEventListener('mouseup', () => {
25 | release(i);
26 | noteMark++;
27 | });
28 |
29 | cell.addEventListener('mouseleave', () => {
30 | release(i);
31 | })
32 | })
33 | }
34 |
35 | render() {
36 | // FIXME: duplicated definitions with componentDidMount
37 | const notes = this.props.notes;
38 | const lastRow = this.props.lastRow;
39 |
40 | const noteKeys = [];
41 | for (let i = 0; i < notes.length; i++) {
42 | if (notes[i] !== 0 && lastRow) {
43 | notes[i] === lastRow[i] ? noteKeys.push('onPressContinue onPress') : noteKeys.push('onPress');
44 | } else {
45 | noteKeys.push('');
46 | }
47 | }
48 | const rowNum = this.props.rowNum;
49 | const rowClassName = `Row-${rowNum}`;
50 | const isMouseDown = this.props.isMouseDown;
51 |
52 | function handleMouseOver(i) {
53 | if (isMouseDown) {
54 | if (notes[i] !== 0) {
55 | notes[i] = 0;
56 | } else {
57 | notes[i] = noteMark;
58 | attack(i);
59 | }
60 | }
61 | }
62 |
63 | return (
64 |
65 |
66 |
handleMouseOver(0)}>
67 |
handleMouseOver(1)}>
68 |
69 |
70 |
handleMouseOver(2)}>
71 |
handleMouseOver(3)}>
72 |
73 |
74 |
handleMouseOver(4)}>
75 |
76 |
77 |
handleMouseOver(5)}>
78 |
handleMouseOver(6)}>
79 |
80 |
81 |
handleMouseOver(7)}>
82 |
handleMouseOver(8)}>
83 |
84 |
85 |
handleMouseOver(9)}>
86 |
handleMouseOver(10)}>
87 |
88 |
89 |
handleMouseOver(11)}>
90 |
91 |
92 |
93 |
handleMouseOver(12)}>
94 |
handleMouseOver(13)}>
95 |
96 |
97 |
handleMouseOver(14)}>
98 |
handleMouseOver(15)}>
99 |
100 |
101 |
handleMouseOver(16)}>
102 |
103 |
104 |
handleMouseOver(17)}>
105 |
handleMouseOver(18)}>
106 |
107 |
108 |
handleMouseOver(19)}>
109 |
handleMouseOver(20)}>
110 |
111 |
112 |
handleMouseOver(21)}>
113 |
handleMouseOver(22)}>
114 |
115 |
116 |
handleMouseOver(23)}>
117 |
118 |
119 |
120 |
handleMouseOver(24)}>
121 |
handleMouseOver(25)}>
122 |
123 |
124 |
handleMouseOver(26)}>
125 |
handleMouseOver(27)}>
126 |
127 |
128 |
handleMouseOver(28)}>
129 |
130 |
131 |
handleMouseOver(29)}>
132 |
handleMouseOver(30)}>
133 |
134 |
135 |
handleMouseOver(31)}>
136 |
handleMouseOver(32)}>
137 |
138 |
139 |
handleMouseOver(33)}>
140 |
handleMouseOver(34)}>
141 |
142 |
143 |
handleMouseOver(35)}>
144 |
145 |
146 |
147 |
handleMouseOver(36)}>
148 |
handleMouseOver(37)}>
149 |
150 |
151 |
handleMouseOver(38)}>
152 |
handleMouseOver(39)}>
153 |
154 |
155 |
handleMouseOver(40)}>
156 |
157 |
158 |
handleMouseOver(41)}>
159 |
handleMouseOver(42)}>
160 |
161 |
162 |
handleMouseOver(43)}>
163 |
handleMouseOver(44)}>
164 |
165 |
166 |
handleMouseOver(45)}>
167 |
handleMouseOver(46)}>
168 |
169 |
170 |
handleMouseOver(47)}>
171 |
172 |
173 |
174 |
handleMouseOver(48)}>
175 |
handleMouseOver(49)}>
176 |
177 |
178 |
handleMouseOver(50)}>
179 |
handleMouseOver(51)}>
180 |
181 |
182 |
handleMouseOver(52)}>
183 |
184 |
185 |
handleMouseOver(53)}>
186 |
handleMouseOver(54)}>
187 |
188 |
189 |
handleMouseOver(55)}>
190 |
handleMouseOver(56)}>
191 |
192 |
193 |
handleMouseOver(57)}>
194 |
handleMouseOver(58)}>
195 |
196 |
197 |
handleMouseOver(59)}>
198 |
199 |
200 |
201 |
handleMouseOver(60)} >
202 |
203 |
204 | );
205 | }
206 | }
--------------------------------------------------------------------------------
/frontend/src/store2.js:
--------------------------------------------------------------------------------
1 | import { observable, autorun } from 'mobx';
2 | import { KEY_NUM , NUM_KEY_CODE_MAP, NUM_UP_KEY_CODE_MAP} from './config';
3 | import { attackRow } from './utils/notePlayer.js'
4 |
5 | const tmpPressedNotes = []
6 | for (let i = 0; i < KEY_NUM; i++) {
7 | tmpPressedNotes.push(0);
8 | }
9 |
10 | // init notes2D
11 | const notes2D = [[0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0],[0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0],[0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,1,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0],[0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,1,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0],[0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0],[0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,1,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0],[0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,1,0,0,1,0,0,0,0,0,0,0,0,0,0,0,4,0,0,0,4,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0],[0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,4,0,0,0,4,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0],[0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,4,0,0,0,4,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0],[0,0,0,0,0,0,0,0,0,0,0,0,6,0,0,0,6,0,0,6,0,0,0,0,0,0,0,0,0,0,0,4,0,0,0,4,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0],[0,0,0,0,0,0,0,0,0,0,0,0,6,0,0,0,6,0,0,6,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0],[0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,9,0,0,0,9,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0],[0,0,0,0,0,0,0,0,0,0,0,0,26,0,0,0,25,0,0,24,0,0,0,0,0,0,0,0,0,9,0,0,0,9,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0],[0,0,0,0,0,0,0,0,0,0,0,0,26,0,0,0,25,0,0,24,0,0,0,0,0,0,0,0,0,9,0,0,0,9,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0],[0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,9,0,0,0,9,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0],[11,0,0,0,11,0,0,11,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0],[11,0,0,0,11,0,0,11,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0],[11,0,0,0,11,0,0,11,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0],[11,0,0,0,11,0,0,11,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0],[11,0,0,0,11,0,0,11,0,0,0,0,0,0,0,0,0,11,0,0,0,11,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0],[0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,11,0,0,0,11,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0],[0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,14,0,0,0,11,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0],[0,0,0,0,0,0,0,0,0,0,0,0,16,0,0,0,16,0,0,16,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0],[0,0,0,0,0,0,0,0,0,0,0,0,16,0,0,0,16,0,0,16,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0],[0,0,0,0,0,0,0,0,0,0,0,0,16,0,0,0,16,0,0,16,0,0,0,0,0,0,0,0,0,16,0,0,0,16,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0],[0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,16,0,0,0,16,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0],[0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,19,0,0,0,16,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0],[0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0],[0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,21,0,0,0,21,0,0,21,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0],[0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,21,0,0,0,21,0,0,21,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0],[0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,21,0,0,0,21,0,0,21,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0],[0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,21,0,0,0,21,0,0,21,0,0,0,0,0,0,0,0,0,0,0,22,0,0,0,21,0,0,0,0,0,0,0,0,0,0,0,0,0],[0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,22,0,0,0,21,0,0,0,0,0,0,0,0,0,0,0,0,0],[0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,24,0,0,0,21,0,0,0,0,0,0,0,0,0,0,0,0,0],[0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,26,0,0,0,26,0,0,26,0,0,0,0,0,0,0,0,0,0,0,24,0,0,0,21,0,0,0,0,0,0,0,0,0,0,0,0,0],[0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,26,0,0,0,26,0,0,26,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0],[0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,26,0,0,0,26,0,0,26,0,0,0,0,0,0,0,0,29,29,0,28,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0],[0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,29,29,0,28,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0],[0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,29,0,0,28,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0],[0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,29,0,0,28,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0],[0,0,0,0,0,0,0,0,0,0,0,0,0,0,32,0,32,0,0,32,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0],[0,0,0,0,0,0,0,0,0,0,0,0,0,0,32,0,32,0,0,32,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0],[0,0,0,0,0,0,0,0,0,0,0,0,0,0,32,0,32,0,0,32,0,0,0,0,0,0,0,0,0,32,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0],[0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,32,0,0,0,0,0,0,0,0,0,32,0,0,0,35,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0],[0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,32,0,0,0,35,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0],[0,0,0,0,0,0,0,0,0,0,0,0,0,0,37,0,0,0,0,37,0,0,0,0,0,0,0,0,0,32,0,0,0,35,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0],[0,0,0,0,0,0,0,0,0,0,0,0,0,0,37,0,0,0,0,37,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0],[0,0,0,0,0,0,0,0,0,0,0,0,0,0,37,0,0,0,0,37,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,37,0,0,0,39,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0],[0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,37,0,0,0,39,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0],[0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,37,0,0,0,39,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0],[0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,41,0,0,0,41,0,0,0,0,0,0,0,0,0,0,0,0,0],[0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,41,0,0,0,41,0,0,0,0,0,0,0,0,0,0,0,0,0],[0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,41,0,0,0,41,0,0,0,0,0,0,0,0,0,0,0,0,0],[0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,43,0,0,0,0,43,0,0,0,0,0,0,0,0,0,0,0,0,0,41,0,0,0,42,0,0,0,0,0,0,0,0,0,0,0,0,0],[0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,43,0,43,0,0,43,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0],[0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,43,0,43,0,0,43,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0],[0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,43,0,43,0,0,43,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0],[0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,43,0,43,0,0,43,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0],[0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,43,0,43,0,0,43,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0],[0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,43,0,43,0,0,43,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0],[0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,46,0,0,0,0,43,0,43,0,0,43,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,51,0,0,0,0,0,0,0,0,0,0,0,0,0],[0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,46,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,33,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0],[0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,46,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,54,0,0,0,0,0,0,0,0,0,0,0,0,0],[0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,47,0,46,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,34,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0],[0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,47,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,56,0,0,0,0,0,0,0,0,0,0,0,0,0],[0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,47,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,38,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0],[0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,47,47,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,58,0,0,0,0,0,0,0,0,0,0,0,0,0],[0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,47,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,59,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0],[0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,48,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,60,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0],[0,0,0,0,0,0,0,0,0,0,0,0,0,0,48,0,48,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,61,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0],[0,0,0,0,0,0,0,0,0,0,0,0,0,0,48,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,62,0,0,63,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0],[0,0,0,0,0,0,0,0,0,0,0,0,0,0,49,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,64,0,0,63,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0],[0,0,0,0,0,0,0,0,0,0,0,0,12,0,49,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,64,0,0,65,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0],[0,0,0,0,0,0,0,0,0,0,0,0,12,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,66,0,0,0,0,65,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0],[0,0,0,0,0,0,0,0,0,0,0,0,12,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,66,0,0,67,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0],[0,0,0,0,0,0,0,0,0,0,0,0,12,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,68,0,0,0,0,67,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0],[0,0,0,0,0,0,0,0,0,0,0,0,12,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,68,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0],[0,0,0,0,0,0,0,0,0,0,0,0,12,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,68,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0],[0,0,0,0,0,0,0,0,0,0,0,0,12,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,68,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0],[0,0,0,0,0,0,0,0,0,0,0,0,12,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,68,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0],[0,0,0,0,0,0,0,0,0,0,0,0,12,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,68,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0],[0,0,0,0,0,0,0,0,0,0,0,0,12,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,68,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0],[0,0,0,0,0,0,0,0,0,0,0,0,12,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0],[0,0,0,0,0,0,0,0,0,0,0,0,12,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0],[0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0],[0,0,0,0,0,0,0,0,0,0,0,0,4,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0],[0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0],[0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0],[0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0],[0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0],[0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0],[0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0],[0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0],[0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0],[0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0],[0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0],[0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0],[0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0],[0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0],[0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0]];
12 | // for (let j = -1; j < 100; j++) {
13 | // notes2D[j] = [];
14 | // for (let i = 0; i < KEY_NUM; i++) {
15 | // notes2D[j].push(0);
16 | // }
17 | // }
18 |
19 | const speedMap = [80, 60, 40, 20];
20 |
21 | class Store {
22 | @observable notes2D;// 2D number array storing all the notes of a song
23 | @observable pressedNotes;
24 | @observable currentRow = -1; // current time
25 | @observable speedIndex = 1; // scroll 5 px
26 | @observable isPlaying = false;// update time if isPlaying is true
27 | @observable isRecording = false;// record to notes in respond to playing notes
28 | @observable isMouseDown = false;
29 | @observable isWriteKeyMode = false; // disable black key on Strap
30 | @observable isKeyboardUp = false; // position of the keyboard
31 |
32 | clearNotes() {
33 | // console.log(JSON.stringify(this.notes2D));
34 | for(let i = 0; i < this.notes2D.length; i++) {
35 | for(let j = 0; j < this.notes2D[0].length; j++) {
36 | this.notes2D[i][j] = 0;
37 | }
38 | }
39 | }
40 |
41 | changeSpeed() {
42 | this.speedIndex = (this.speedIndex + 1) % 4;
43 | }
44 |
45 | constructor(notes2D, tmpPressedNotes) {
46 | this.notes2D = notes2D;
47 | this.pressedNotes = tmpPressedNotes;
48 | // autorun(() => console.log('isMouseDown in store', this.isMouseDown));
49 |
50 | // play the Row
51 | let lastRow;
52 | autorun(() => {
53 | // console.log('current row in store', this.currentRow);
54 | if(this.currentRow !== lastRow) {
55 | attackRow(this.notes2D[this.currentRow], this.notes2D[lastRow]);
56 | lastRow = this.currentRow;
57 | }
58 | });
59 |
60 | // scroll the strap
61 | let handeler;
62 | autorun(() => {
63 | if (this.isPlaying) {
64 | clearInterval(handeler);
65 | const element = document.querySelector('.strap');
66 | handeler = setInterval(()=>{
67 | element.scrollTop += 5;
68 | }, speedMap[this.speedIndex]);
69 | } else {
70 | clearInterval(handeler);
71 | }
72 | });
73 |
74 | //record from keyboard
75 | autorun (() => {
76 | if (this.currentRow !== -1) {
77 | this.pressedNotes.forEach((note, i) => {
78 | if(note !== 0) this.notes2D[this.currentRow][i] = note;
79 | });
80 | }
81 | });
82 |
83 | autorun(()=>{
84 | if (this.currentRow >= this.notes2D.length && this.isKeyboardUp === false) this.isPlaying = false;
85 | if (this.currentRow >= this.notes2D.length && this.isKeyboardUp === true) {
86 | this.isPlaying = false;
87 | document.querySelector('.strap').scrollTop += 100;
88 | }
89 | });
90 |
91 | let lastPressedNotes;
92 | autorun (()=>{
93 | attackRow(this.pressedNotes, lastPressedNotes);
94 | lastPressedNotes = JSON.parse(JSON.stringify(this.pressedNotes));
95 | });
96 |
97 | let noteMark = 1;
98 | autorun(() => {
99 | // if (this.isKeyboardUp) {
100 | window.addEventListener('keyup', e => {
101 | const possibleUpKeyNum1 = NUM_UP_KEY_CODE_MAP.indexOf(e.keyCode);
102 | const possibleUpKeyNum2 = NUM_UP_KEY_CODE_MAP.lastIndexOf(e.keyCode);
103 | this.pressedNotes[possibleUpKeyNum1] = 0;
104 | this.pressedNotes[possibleUpKeyNum2] = 0;
105 | noteMark++;
106 | });
107 | window.addEventListener('keypress', e => {
108 | const keyNum = NUM_KEY_CODE_MAP.indexOf(e.keyCode);
109 | this.pressedNotes[keyNum] = noteMark;
110 | })
111 | // }
112 | });
113 | }
114 | }
115 |
116 | const store = new Store(notes2D, tmpPressedNotes);
117 |
118 | export default store;
--------------------------------------------------------------------------------
/backend/package-lock.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "yiin-cloud-backend",
3 | "requires": true,
4 | "lockfileVersion": 1,
5 | "dependencies": {
6 | "@types/graphql": {
7 | "version": "0.9.4",
8 | "resolved": "http://registry.npm.taobao.org/@types/graphql/download/@types/graphql-0.9.4.tgz",
9 | "integrity": "sha1-zetry++bbFhDdLgap/SOzz2kBPo=",
10 | "optional": true
11 | },
12 | "accepts": {
13 | "version": "1.3.4",
14 | "resolved": "http://registry.npm.taobao.org/accepts/download/accepts-1.3.4.tgz",
15 | "integrity": "sha1-hiRnWMfdbSGmR0/whKR0DsBesh8=",
16 | "requires": {
17 | "mime-types": "2.1.16",
18 | "negotiator": "0.6.1"
19 | }
20 | },
21 | "array-flatten": {
22 | "version": "1.1.1",
23 | "resolved": "http://registry.npm.taobao.org/array-flatten/download/array-flatten-1.1.1.tgz",
24 | "integrity": "sha1-ml9pkFGx5wczKPKgCJaLZOopVdI="
25 | },
26 | "aws-sdk": {
27 | "version": "2.108.0",
28 | "resolved": "http://registry.npm.taobao.org/aws-sdk/download/aws-sdk-2.108.0.tgz",
29 | "integrity": "sha1-i29u2Gkr/GysF4LQQk+YZ9G6pb0=",
30 | "requires": {
31 | "buffer": "4.9.1",
32 | "crypto-browserify": "1.0.9",
33 | "events": "1.1.1",
34 | "jmespath": "0.15.0",
35 | "querystring": "0.2.0",
36 | "sax": "1.2.1",
37 | "url": "0.10.3",
38 | "uuid": "3.0.1",
39 | "xml2js": "0.4.17",
40 | "xmlbuilder": "4.2.1"
41 | },
42 | "dependencies": {
43 | "uuid": {
44 | "version": "3.0.1",
45 | "resolved": "http://registry.npm.taobao.org/uuid/download/uuid-3.0.1.tgz",
46 | "integrity": "sha1-ZUS7ot/ajBzxfmKaOjBeK7H+5sE="
47 | }
48 | }
49 | },
50 | "axios": {
51 | "version": "0.16.2",
52 | "resolved": "http://registry.npm.taobao.org/axios/download/axios-0.16.2.tgz",
53 | "integrity": "sha1-uk+S8XFn37q0CYN4VFS5rBScPG0=",
54 | "dev": true,
55 | "requires": {
56 | "follow-redirects": "1.2.4",
57 | "is-buffer": "1.1.5"
58 | }
59 | },
60 | "babel-runtime": {
61 | "version": "6.26.0",
62 | "resolved": "http://registry.npm.taobao.org/babel-runtime/download/babel-runtime-6.26.0.tgz",
63 | "integrity": "sha1-llxwWGaOgrVde/4E/yM3vItWR/4=",
64 | "requires": {
65 | "core-js": "2.5.1",
66 | "regenerator-runtime": "0.11.0"
67 | }
68 | },
69 | "base64-js": {
70 | "version": "1.2.1",
71 | "resolved": "http://registry.npm.taobao.org/base64-js/download/base64-js-1.2.1.tgz",
72 | "integrity": "sha1-qRlH2h9KUW6jjltOwOw3c2deCIY="
73 | },
74 | "base64url": {
75 | "version": "2.0.0",
76 | "resolved": "http://registry.npm.taobao.org/base64url/download/base64url-2.0.0.tgz",
77 | "integrity": "sha1-6sFuA+oUOO/5Qj1puqNiYu0fcLs="
78 | },
79 | "basic-auth": {
80 | "version": "1.1.0",
81 | "resolved": "http://registry.npm.taobao.org/basic-auth/download/basic-auth-1.1.0.tgz",
82 | "integrity": "sha1-RSIe5Cn37h5QNb4/UVM/HN/SmIQ="
83 | },
84 | "bcrypt-nodejs": {
85 | "version": "0.0.3",
86 | "resolved": "http://registry.npm.taobao.org/bcrypt-nodejs/download/bcrypt-nodejs-0.0.3.tgz",
87 | "integrity": "sha1-xgkX8m3CNWYVZsaBBhwwPCsohCs="
88 | },
89 | "body-parser": {
90 | "version": "1.17.2",
91 | "resolved": "http://registry.npm.taobao.org/body-parser/download/body-parser-1.17.2.tgz",
92 | "integrity": "sha1-+IkqvI+eYn1Crtr7yma/WrmRBO4=",
93 | "requires": {
94 | "bytes": "2.4.0",
95 | "content-type": "1.0.2",
96 | "debug": "2.6.7",
97 | "depd": "1.1.1",
98 | "http-errors": "1.6.2",
99 | "iconv-lite": "0.4.15",
100 | "on-finished": "2.3.0",
101 | "qs": "6.4.0",
102 | "raw-body": "2.2.0",
103 | "type-is": "1.6.15"
104 | },
105 | "dependencies": {
106 | "bytes": {
107 | "version": "2.4.0",
108 | "resolved": "http://registry.npm.taobao.org/bytes/download/bytes-2.4.0.tgz",
109 | "integrity": "sha1-fZcZb51br39pNeJZhVSe3SpsIzk="
110 | },
111 | "debug": {
112 | "version": "2.6.7",
113 | "resolved": "http://registry.npm.taobao.org/debug/download/debug-2.6.7.tgz",
114 | "integrity": "sha1-krrR9tBbu2u6Isyoi80OyJTChh4=",
115 | "requires": {
116 | "ms": "2.0.0"
117 | }
118 | },
119 | "iconv-lite": {
120 | "version": "0.4.15",
121 | "resolved": "http://registry.npm.taobao.org/iconv-lite/download/iconv-lite-0.4.15.tgz",
122 | "integrity": "sha1-/iZaIYrGpXz+hUkn6dBMGYJe3es="
123 | },
124 | "qs": {
125 | "version": "6.4.0",
126 | "resolved": "http://registry.npm.taobao.org/qs/download/qs-6.4.0.tgz",
127 | "integrity": "sha1-E+JtKK1rD/qpExLNO/cI7TUecjM="
128 | },
129 | "raw-body": {
130 | "version": "2.2.0",
131 | "resolved": "http://registry.npm.taobao.org/raw-body/download/raw-body-2.2.0.tgz",
132 | "integrity": "sha1-mUl2z2pQlqQRYoQEkvC9xdbn+5Y=",
133 | "requires": {
134 | "bytes": "2.4.0",
135 | "iconv-lite": "0.4.15",
136 | "unpipe": "1.0.0"
137 | }
138 | }
139 | }
140 | },
141 | "buffer": {
142 | "version": "4.9.1",
143 | "resolved": "http://registry.npm.taobao.org/buffer/download/buffer-4.9.1.tgz",
144 | "integrity": "sha1-bRu2AbB6TvztlwlBMgkwJ8lbwpg=",
145 | "requires": {
146 | "base64-js": "1.2.1",
147 | "ieee754": "1.1.8",
148 | "isarray": "1.0.0"
149 | }
150 | },
151 | "buffer-equal-constant-time": {
152 | "version": "1.0.1",
153 | "resolved": "http://registry.npm.taobao.org/buffer-equal-constant-time/download/buffer-equal-constant-time-1.0.1.tgz",
154 | "integrity": "sha1-+OcRMvf/5uAaXJaXpMbz5I1cyBk="
155 | },
156 | "bytes": {
157 | "version": "3.0.0",
158 | "resolved": "http://registry.npm.taobao.org/bytes/download/bytes-3.0.0.tgz",
159 | "integrity": "sha1-0ygVQE1olpn4Wk6k+odV3ROpYEg="
160 | },
161 | "content-disposition": {
162 | "version": "0.5.2",
163 | "resolved": "http://registry.npm.taobao.org/content-disposition/download/content-disposition-0.5.2.tgz",
164 | "integrity": "sha1-DPaLud318r55YcOoUXjLhdunjLQ="
165 | },
166 | "content-type": {
167 | "version": "1.0.2",
168 | "resolved": "http://registry.npm.taobao.org/content-type/download/content-type-1.0.2.tgz",
169 | "integrity": "sha1-t9ETrueo3Se9IRM8TcJSnfFyHu0="
170 | },
171 | "cookie": {
172 | "version": "0.3.1",
173 | "resolved": "http://registry.npm.taobao.org/cookie/download/cookie-0.3.1.tgz",
174 | "integrity": "sha1-5+Ch+e9DtMi6klxcWpboBtFoc7s="
175 | },
176 | "cookie-signature": {
177 | "version": "1.0.6",
178 | "resolved": "http://registry.npm.taobao.org/cookie-signature/download/cookie-signature-1.0.6.tgz",
179 | "integrity": "sha1-4wOogrNCzD7oylE6eZmXNNqzriw="
180 | },
181 | "core-js": {
182 | "version": "2.5.1",
183 | "resolved": "http://registry.npm.taobao.org/core-js/download/core-js-2.5.1.tgz",
184 | "integrity": "sha1-rmh03GaTd4m4B1T/VCjfZoGcpQs="
185 | },
186 | "crypto-browserify": {
187 | "version": "1.0.9",
188 | "resolved": "http://registry.npm.taobao.org/crypto-browserify/download/crypto-browserify-1.0.9.tgz",
189 | "integrity": "sha1-zFRJaF37hesRyYKKzHy4erW7/MA="
190 | },
191 | "debug": {
192 | "version": "2.6.8",
193 | "resolved": "http://registry.npm.taobao.org/debug/download/debug-2.6.8.tgz",
194 | "integrity": "sha1-5zFTHKLt4n0YgiJCfaF4IdaP9Pw=",
195 | "requires": {
196 | "ms": "2.0.0"
197 | }
198 | },
199 | "depd": {
200 | "version": "1.1.1",
201 | "resolved": "http://registry.npm.taobao.org/depd/download/depd-1.1.1.tgz",
202 | "integrity": "sha1-V4O04cRZ8G+lyif5kfPQbnoxA1k="
203 | },
204 | "deprecated-decorator": {
205 | "version": "0.1.6",
206 | "resolved": "http://registry.npm.taobao.org/deprecated-decorator/download/deprecated-decorator-0.1.6.tgz",
207 | "integrity": "sha1-AJZjF7ehL+kvPMgx91g68ym4bDc="
208 | },
209 | "destroy": {
210 | "version": "1.0.4",
211 | "resolved": "http://registry.npm.taobao.org/destroy/download/destroy-1.0.4.tgz",
212 | "integrity": "sha1-l4hXRCxEdJ5CBmE+N5RiBYJqvYA="
213 | },
214 | "ecdsa-sig-formatter": {
215 | "version": "1.0.9",
216 | "resolved": "http://registry.npm.taobao.org/ecdsa-sig-formatter/download/ecdsa-sig-formatter-1.0.9.tgz",
217 | "integrity": "sha1-S8kmJ07Dtau1AW5+HWCSGsJisqE=",
218 | "requires": {
219 | "base64url": "2.0.0",
220 | "safe-buffer": "5.1.1"
221 | }
222 | },
223 | "ee-first": {
224 | "version": "1.1.1",
225 | "resolved": "http://registry.npm.taobao.org/ee-first/download/ee-first-1.1.1.tgz",
226 | "integrity": "sha1-WQxhFWsK4vTwJVcyoViyZrxWsh0="
227 | },
228 | "encodeurl": {
229 | "version": "1.0.1",
230 | "resolved": "http://registry.npm.taobao.org/encodeurl/download/encodeurl-1.0.1.tgz",
231 | "integrity": "sha1-eePVhlU0aQn+bw9Fpd5oEDspTSA="
232 | },
233 | "es6-promisify": {
234 | "version": "5.0.0",
235 | "resolved": "http://registry.npm.taobao.org/es6-promisify/download/es6-promisify-5.0.0.tgz",
236 | "integrity": "sha1-UQnWLz5W6pZ8S2NQWu8IKRyKUgM=",
237 | "requires": {
238 | "es6-promise": "4.1.1"
239 | },
240 | "dependencies": {
241 | "es6-promise": {
242 | "version": "4.1.1",
243 | "resolved": "http://registry.npm.taobao.org/es6-promise/download/es6-promise-4.1.1.tgz",
244 | "integrity": "sha1-iBHpCRXZoNujYnTwskLb2nj5ySo="
245 | }
246 | }
247 | },
248 | "escape-html": {
249 | "version": "1.0.3",
250 | "resolved": "http://registry.npm.taobao.org/escape-html/download/escape-html-1.0.3.tgz",
251 | "integrity": "sha1-Aljq5NPQwJdN4cFpGI7wBR0dGYg="
252 | },
253 | "etag": {
254 | "version": "1.8.0",
255 | "resolved": "http://registry.npm.taobao.org/etag/download/etag-1.8.0.tgz",
256 | "integrity": "sha1-b2Ma7zNtbEY2K1F2QETOIWvjwFE="
257 | },
258 | "events": {
259 | "version": "1.1.1",
260 | "resolved": "http://registry.npm.taobao.org/events/download/events-1.1.1.tgz",
261 | "integrity": "sha1-nr23Y1rQmccNzEwqH1AEKI6L2SQ="
262 | },
263 | "express": {
264 | "version": "4.15.4",
265 | "resolved": "http://registry.npm.taobao.org/express/download/express-4.15.4.tgz",
266 | "integrity": "sha1-Ay4iU0ic+PzgJma+yj0R7XotrtE=",
267 | "requires": {
268 | "accepts": "1.3.4",
269 | "array-flatten": "1.1.1",
270 | "content-disposition": "0.5.2",
271 | "content-type": "1.0.2",
272 | "cookie": "0.3.1",
273 | "cookie-signature": "1.0.6",
274 | "debug": "2.6.8",
275 | "depd": "1.1.1",
276 | "encodeurl": "1.0.1",
277 | "escape-html": "1.0.3",
278 | "etag": "1.8.0",
279 | "finalhandler": "1.0.4",
280 | "fresh": "0.5.0",
281 | "merge-descriptors": "1.0.1",
282 | "methods": "1.1.2",
283 | "on-finished": "2.3.0",
284 | "parseurl": "1.3.1",
285 | "path-to-regexp": "0.1.7",
286 | "proxy-addr": "1.1.5",
287 | "qs": "6.5.0",
288 | "range-parser": "1.2.0",
289 | "send": "0.15.4",
290 | "serve-static": "1.12.4",
291 | "setprototypeof": "1.0.3",
292 | "statuses": "1.3.1",
293 | "type-is": "1.6.15",
294 | "utils-merge": "1.0.0",
295 | "vary": "1.1.1"
296 | }
297 | },
298 | "express-graphql": {
299 | "version": "0.6.11",
300 | "resolved": "http://registry.npm.taobao.org/express-graphql/download/express-graphql-0.6.11.tgz",
301 | "integrity": "sha1-Pc540GQ+eOfjYGZGzhYgJboFhas=",
302 | "requires": {
303 | "accepts": "1.3.4",
304 | "content-type": "1.0.2",
305 | "http-errors": "1.6.2",
306 | "raw-body": "2.3.2"
307 | }
308 | },
309 | "finalhandler": {
310 | "version": "1.0.4",
311 | "resolved": "http://registry.npm.taobao.org/finalhandler/download/finalhandler-1.0.4.tgz",
312 | "integrity": "sha1-GFdPLnxLmLiuOyMMIfIB8xvbP7c=",
313 | "requires": {
314 | "debug": "2.6.8",
315 | "encodeurl": "1.0.1",
316 | "escape-html": "1.0.3",
317 | "on-finished": "2.3.0",
318 | "parseurl": "1.3.1",
319 | "statuses": "1.3.1",
320 | "unpipe": "1.0.0"
321 | }
322 | },
323 | "follow-redirects": {
324 | "version": "1.2.4",
325 | "resolved": "http://registry.npm.taobao.org/follow-redirects/download/follow-redirects-1.2.4.tgz",
326 | "integrity": "sha1-NV6PTRaHa0P1d7DVziZouXIyFOo=",
327 | "dev": true,
328 | "requires": {
329 | "debug": "2.6.8"
330 | }
331 | },
332 | "forwarded": {
333 | "version": "0.1.0",
334 | "resolved": "http://registry.npm.taobao.org/forwarded/download/forwarded-0.1.0.tgz",
335 | "integrity": "sha1-Ge+YdMSuHCl7zweP3mOgm2aoQ2M="
336 | },
337 | "fresh": {
338 | "version": "0.5.0",
339 | "resolved": "http://registry.npm.taobao.org/fresh/download/fresh-0.5.0.tgz",
340 | "integrity": "sha1-9HTKXmqSRtb9jglTz6m5yAWvp44="
341 | },
342 | "graphql": {
343 | "version": "0.11.3",
344 | "resolved": "http://registry.npm.taobao.org/graphql/download/graphql-0.11.3.tgz",
345 | "integrity": "sha1-mTTi3yjxfTl6hfg8s50dF5v/70c=",
346 | "requires": {
347 | "iterall": "1.1.1"
348 | }
349 | },
350 | "graphql-errors": {
351 | "version": "2.1.0",
352 | "resolved": "http://registry.npm.taobao.org/graphql-errors/download/graphql-errors-2.1.0.tgz",
353 | "integrity": "sha1-gxyMSRs1SFnuegwHv/EBr2RzEZU=",
354 | "requires": {
355 | "babel-runtime": "6.26.0",
356 | "uuid": "2.0.3"
357 | },
358 | "dependencies": {
359 | "uuid": {
360 | "version": "2.0.3",
361 | "resolved": "http://registry.npm.taobao.org/uuid/download/uuid-2.0.3.tgz",
362 | "integrity": "sha1-Z+LoY3lyFVMN/zGOW/nc6/1Hsho="
363 | }
364 | }
365 | },
366 | "graphql-tools": {
367 | "version": "1.2.2",
368 | "resolved": "http://registry.npm.taobao.org/graphql-tools/download/graphql-tools-1.2.2.tgz",
369 | "integrity": "sha1-/3kekbeOBe7BijJxancyvHv1y00=",
370 | "requires": {
371 | "@types/graphql": "0.9.4",
372 | "deprecated-decorator": "0.1.6",
373 | "uuid": "3.1.0"
374 | }
375 | },
376 | "hoek": {
377 | "version": "2.16.3",
378 | "resolved": "http://registry.npm.taobao.org/hoek/download/hoek-2.16.3.tgz",
379 | "integrity": "sha1-ILt0A9POo5jpHcRxCo/xuCdKJe0="
380 | },
381 | "http-errors": {
382 | "version": "1.6.2",
383 | "resolved": "http://registry.npm.taobao.org/http-errors/download/http-errors-1.6.2.tgz",
384 | "integrity": "sha1-CgAsyFcHGSp+eUbO7cERVfYOxzY=",
385 | "requires": {
386 | "depd": "1.1.1",
387 | "inherits": "2.0.3",
388 | "setprototypeof": "1.0.3",
389 | "statuses": "1.3.1"
390 | }
391 | },
392 | "iconv-lite": {
393 | "version": "0.4.19",
394 | "resolved": "http://registry.npm.taobao.org/iconv-lite/download/iconv-lite-0.4.19.tgz",
395 | "integrity": "sha1-90aPYBNfXl2tM5nAqBvpoWA6CCs="
396 | },
397 | "ieee754": {
398 | "version": "1.1.8",
399 | "resolved": "http://registry.npm.taobao.org/ieee754/download/ieee754-1.1.8.tgz",
400 | "integrity": "sha1-vjPUCsEO8ZJnAfbwii2G+/0a0+Q="
401 | },
402 | "inherits": {
403 | "version": "2.0.3",
404 | "resolved": "http://registry.npm.taobao.org/inherits/download/inherits-2.0.3.tgz",
405 | "integrity": "sha1-Yzwsg+PaQqUC9SRmAiSA9CCCYd4="
406 | },
407 | "ipaddr.js": {
408 | "version": "1.4.0",
409 | "resolved": "http://registry.npm.taobao.org/ipaddr.js/download/ipaddr.js-1.4.0.tgz",
410 | "integrity": "sha1-KWrKh4qCGBbluF0KKFqZvP9FgvA="
411 | },
412 | "is-buffer": {
413 | "version": "1.1.5",
414 | "resolved": "http://registry.npm.taobao.org/is-buffer/download/is-buffer-1.1.5.tgz",
415 | "integrity": "sha1-Hzsm72E7IUuIy8ojzGwB2Hlh7sw=",
416 | "dev": true
417 | },
418 | "isarray": {
419 | "version": "1.0.0",
420 | "resolved": "http://registry.npm.taobao.org/isarray/download/isarray-1.0.0.tgz",
421 | "integrity": "sha1-u5NdSFgsuhaMBoNJV6VKPgcSTxE="
422 | },
423 | "isemail": {
424 | "version": "1.2.0",
425 | "resolved": "http://registry.npm.taobao.org/isemail/download/isemail-1.2.0.tgz",
426 | "integrity": "sha1-vgPfjMPineTSxd9lASY/H6RZXpo="
427 | },
428 | "iterall": {
429 | "version": "1.1.1",
430 | "resolved": "http://registry.npm.taobao.org/iterall/download/iterall-1.1.1.tgz",
431 | "integrity": "sha1-9/CvEemgTsZCYmD1AZ2fzKTVAhQ="
432 | },
433 | "jmespath": {
434 | "version": "0.15.0",
435 | "resolved": "http://registry.npm.taobao.org/jmespath/download/jmespath-0.15.0.tgz",
436 | "integrity": "sha1-o/Iiqarp+Wb10nx5ZRDigJF2Qhc="
437 | },
438 | "joi": {
439 | "version": "6.10.1",
440 | "resolved": "http://registry.npm.taobao.org/joi/download/joi-6.10.1.tgz",
441 | "integrity": "sha1-TVDDGAeRIgAP5fFq8f+OGRe3fgY=",
442 | "requires": {
443 | "hoek": "2.16.3",
444 | "isemail": "1.2.0",
445 | "moment": "2.18.1",
446 | "topo": "1.1.0"
447 | }
448 | },
449 | "jsonwebtoken": {
450 | "version": "7.4.3",
451 | "resolved": "http://registry.npm.taobao.org/jsonwebtoken/download/jsonwebtoken-7.4.3.tgz",
452 | "integrity": "sha1-d/UCHeBYtgWheD+hKD6ZgS5kVjg=",
453 | "requires": {
454 | "joi": "6.10.1",
455 | "jws": "3.1.4",
456 | "lodash.once": "4.1.1",
457 | "ms": "2.0.0",
458 | "xtend": "4.0.1"
459 | }
460 | },
461 | "jwa": {
462 | "version": "1.1.5",
463 | "resolved": "http://registry.npm.taobao.org/jwa/download/jwa-1.1.5.tgz",
464 | "integrity": "sha1-oFUs4CIHQs1S4VN3SjKQXDDnVuU=",
465 | "requires": {
466 | "base64url": "2.0.0",
467 | "buffer-equal-constant-time": "1.0.1",
468 | "ecdsa-sig-formatter": "1.0.9",
469 | "safe-buffer": "5.1.1"
470 | }
471 | },
472 | "jws": {
473 | "version": "3.1.4",
474 | "resolved": "http://registry.npm.taobao.org/jws/download/jws-3.1.4.tgz",
475 | "integrity": "sha1-+ei5M46KhHJ31kRLFGT2GIDgUKI=",
476 | "requires": {
477 | "base64url": "2.0.0",
478 | "jwa": "1.1.5",
479 | "safe-buffer": "5.1.1"
480 | }
481 | },
482 | "lodash": {
483 | "version": "4.17.4",
484 | "resolved": "http://registry.npm.taobao.org/lodash/download/lodash-4.17.4.tgz",
485 | "integrity": "sha1-eCA6TRwyiuHYbcpkYONptX9AVa4="
486 | },
487 | "lodash.once": {
488 | "version": "4.1.1",
489 | "resolved": "http://registry.npm.taobao.org/lodash.once/download/lodash.once-4.1.1.tgz",
490 | "integrity": "sha1-DdOXEhPHxW34gJd9UEyI+0cal6w="
491 | },
492 | "media-typer": {
493 | "version": "0.3.0",
494 | "resolved": "http://registry.npm.taobao.org/media-typer/download/media-typer-0.3.0.tgz",
495 | "integrity": "sha1-hxDXrwqmJvj/+hzgAWhUUmMlV0g="
496 | },
497 | "merge-descriptors": {
498 | "version": "1.0.1",
499 | "resolved": "http://registry.npm.taobao.org/merge-descriptors/download/merge-descriptors-1.0.1.tgz",
500 | "integrity": "sha1-sAqqVW3YtEVoFQ7J0blT8/kMu2E="
501 | },
502 | "method-override": {
503 | "version": "2.3.9",
504 | "resolved": "http://registry.npm.taobao.org/method-override/download/method-override-2.3.9.tgz",
505 | "integrity": "sha1-vRUfLONM8Bp2ykAKuVwBKxAtj3E=",
506 | "requires": {
507 | "debug": "2.6.8",
508 | "methods": "1.1.2",
509 | "parseurl": "1.3.1",
510 | "vary": "1.1.1"
511 | }
512 | },
513 | "methods": {
514 | "version": "1.1.2",
515 | "resolved": "http://registry.npm.taobao.org/methods/download/methods-1.1.2.tgz",
516 | "integrity": "sha1-VSmk1nZUE07cxSZmVoNbD4Ua/O4="
517 | },
518 | "mime": {
519 | "version": "1.3.4",
520 | "resolved": "http://registry.npm.taobao.org/mime/download/mime-1.3.4.tgz",
521 | "integrity": "sha1-EV+eO2s9rylZmDyzjxSaLUDrXVM="
522 | },
523 | "mime-db": {
524 | "version": "1.29.0",
525 | "resolved": "http://registry.npm.taobao.org/mime-db/download/mime-db-1.29.0.tgz",
526 | "integrity": "sha1-SNJtI1WJZRcErFkWygYAGRQmaHg="
527 | },
528 | "mime-types": {
529 | "version": "2.1.16",
530 | "resolved": "http://registry.npm.taobao.org/mime-types/download/mime-types-2.1.16.tgz",
531 | "integrity": "sha1-K4WKUuXs1RbbiXrCvodIeDBpjiM=",
532 | "requires": {
533 | "mime-db": "1.29.0"
534 | }
535 | },
536 | "moment": {
537 | "version": "2.18.1",
538 | "resolved": "http://registry.npm.taobao.org/moment/download/moment-2.18.1.tgz",
539 | "integrity": "sha1-w2GT3Tzhwu7SrbfIAtu8d6gbHA8="
540 | },
541 | "morgan": {
542 | "version": "1.8.2",
543 | "resolved": "http://registry.npm.taobao.org/morgan/download/morgan-1.8.2.tgz",
544 | "integrity": "sha1-eErHc05KRTqcbm6GgKkyknXItoc=",
545 | "requires": {
546 | "basic-auth": "1.1.0",
547 | "debug": "2.6.8",
548 | "depd": "1.1.1",
549 | "on-finished": "2.3.0",
550 | "on-headers": "1.0.1"
551 | }
552 | },
553 | "ms": {
554 | "version": "2.0.0",
555 | "resolved": "http://registry.npm.taobao.org/ms/download/ms-2.0.0.tgz",
556 | "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g="
557 | },
558 | "negotiator": {
559 | "version": "0.6.1",
560 | "resolved": "http://registry.npm.taobao.org/negotiator/download/negotiator-0.6.1.tgz",
561 | "integrity": "sha1-KzJxhOiZIQEXeyhWP7XnECrNDKk="
562 | },
563 | "nodemailer": {
564 | "version": "4.1.0",
565 | "resolved": "http://registry.npm.taobao.org/nodemailer/download/nodemailer-4.1.0.tgz",
566 | "integrity": "sha1-4/drytc3a65EcUVSVx9bBnT+Rp8="
567 | },
568 | "on-finished": {
569 | "version": "2.3.0",
570 | "resolved": "http://registry.npm.taobao.org/on-finished/download/on-finished-2.3.0.tgz",
571 | "integrity": "sha1-IPEzZIGwg811M3mSoWlxqi2QaUc=",
572 | "requires": {
573 | "ee-first": "1.1.1"
574 | }
575 | },
576 | "on-headers": {
577 | "version": "1.0.1",
578 | "resolved": "http://registry.npm.taobao.org/on-headers/download/on-headers-1.0.1.tgz",
579 | "integrity": "sha1-ko9dD0cNSTQmUepnlLCFfBAGk/c="
580 | },
581 | "parseurl": {
582 | "version": "1.3.1",
583 | "resolved": "http://registry.npm.taobao.org/parseurl/download/parseurl-1.3.1.tgz",
584 | "integrity": "sha1-yKuMkiO6NIiKpkopeyiFO+wY2lY="
585 | },
586 | "path-to-regexp": {
587 | "version": "0.1.7",
588 | "resolved": "http://registry.npm.taobao.org/path-to-regexp/download/path-to-regexp-0.1.7.tgz",
589 | "integrity": "sha1-32BBeABfUi8V60SQ5yR6G/qmf4w="
590 | },
591 | "proxy-addr": {
592 | "version": "1.1.5",
593 | "resolved": "http://registry.npm.taobao.org/proxy-addr/download/proxy-addr-1.1.5.tgz",
594 | "integrity": "sha1-ccDuOxAt4/IC87ZPYI0XP8uhqRg=",
595 | "requires": {
596 | "forwarded": "0.1.0",
597 | "ipaddr.js": "1.4.0"
598 | }
599 | },
600 | "qs": {
601 | "version": "6.5.0",
602 | "resolved": "http://registry.npm.taobao.org/qs/download/qs-6.5.0.tgz",
603 | "integrity": "sha1-jQSVTTZN7z78VbWgeT4eLIsebkk="
604 | },
605 | "querystring": {
606 | "version": "0.2.0",
607 | "resolved": "http://registry.npm.taobao.org/querystring/download/querystring-0.2.0.tgz",
608 | "integrity": "sha1-sgmEkgO7Jd+CDadW50cAWHhSFiA="
609 | },
610 | "range-parser": {
611 | "version": "1.2.0",
612 | "resolved": "http://registry.npm.taobao.org/range-parser/download/range-parser-1.2.0.tgz",
613 | "integrity": "sha1-9JvmtIeJTdxA3MlKMi9hEJLgDV4="
614 | },
615 | "raw-body": {
616 | "version": "2.3.2",
617 | "resolved": "http://registry.npm.taobao.org/raw-body/download/raw-body-2.3.2.tgz",
618 | "integrity": "sha1-vNYMd9Prk83gBQKVw/N5OJvIj4k=",
619 | "requires": {
620 | "bytes": "3.0.0",
621 | "http-errors": "1.6.2",
622 | "iconv-lite": "0.4.19",
623 | "unpipe": "1.0.0"
624 | }
625 | },
626 | "regenerator-runtime": {
627 | "version": "0.11.0",
628 | "resolved": "http://registry.npm.taobao.org/regenerator-runtime/download/regenerator-runtime-0.11.0.tgz",
629 | "integrity": "sha1-flT+W1zNXWYk6mJVw0c74JC4AuE="
630 | },
631 | "safe-buffer": {
632 | "version": "5.1.1",
633 | "resolved": "http://registry.npm.taobao.org/safe-buffer/download/safe-buffer-5.1.1.tgz",
634 | "integrity": "sha1-iTMSr2myEj3vcfV4iQAWce6yyFM="
635 | },
636 | "sax": {
637 | "version": "1.2.1",
638 | "resolved": "http://registry.npm.taobao.org/sax/download/sax-1.2.1.tgz",
639 | "integrity": "sha1-e45lYZCyKOgaZq6nSEgNgozS03o="
640 | },
641 | "send": {
642 | "version": "0.15.4",
643 | "resolved": "http://registry.npm.taobao.org/send/download/send-0.15.4.tgz",
644 | "integrity": "sha1-mF+qPihLAnPHkzZKNcZze9k5Bbk=",
645 | "requires": {
646 | "debug": "2.6.8",
647 | "depd": "1.1.1",
648 | "destroy": "1.0.4",
649 | "encodeurl": "1.0.1",
650 | "escape-html": "1.0.3",
651 | "etag": "1.8.0",
652 | "fresh": "0.5.0",
653 | "http-errors": "1.6.2",
654 | "mime": "1.3.4",
655 | "ms": "2.0.0",
656 | "on-finished": "2.3.0",
657 | "range-parser": "1.2.0",
658 | "statuses": "1.3.1"
659 | }
660 | },
661 | "serve-static": {
662 | "version": "1.12.4",
663 | "resolved": "http://registry.npm.taobao.org/serve-static/download/serve-static-1.12.4.tgz",
664 | "integrity": "sha1-m2qpjutyU8Tu3Ewfb9vKYJkBqWE=",
665 | "requires": {
666 | "encodeurl": "1.0.1",
667 | "escape-html": "1.0.3",
668 | "parseurl": "1.3.1",
669 | "send": "0.15.4"
670 | }
671 | },
672 | "setprototypeof": {
673 | "version": "1.0.3",
674 | "resolved": "http://registry.npm.taobao.org/setprototypeof/download/setprototypeof-1.0.3.tgz",
675 | "integrity": "sha1-ZlZ+NwQ+608E2RvWWMDL77VbjgQ="
676 | },
677 | "statuses": {
678 | "version": "1.3.1",
679 | "resolved": "http://registry.npm.taobao.org/statuses/download/statuses-1.3.1.tgz",
680 | "integrity": "sha1-+vUbnrdKrvOzrPStX2Gr8ky3uT4="
681 | },
682 | "topo": {
683 | "version": "1.1.0",
684 | "resolved": "http://registry.npm.taobao.org/topo/download/topo-1.1.0.tgz",
685 | "integrity": "sha1-6ddRYV0buH3IZdsYL6HKCl71NtU=",
686 | "requires": {
687 | "hoek": "2.16.3"
688 | }
689 | },
690 | "type-is": {
691 | "version": "1.6.15",
692 | "resolved": "http://registry.npm.taobao.org/type-is/download/type-is-1.6.15.tgz",
693 | "integrity": "sha1-yrEPtJCeRByChC6v4a1kbIGARBA=",
694 | "requires": {
695 | "media-typer": "0.3.0",
696 | "mime-types": "2.1.16"
697 | }
698 | },
699 | "unpipe": {
700 | "version": "1.0.0",
701 | "resolved": "http://registry.npm.taobao.org/unpipe/download/unpipe-1.0.0.tgz",
702 | "integrity": "sha1-sr9O6FFKrmFltIF4KdIbLvSZBOw="
703 | },
704 | "url": {
705 | "version": "0.10.3",
706 | "resolved": "http://registry.npm.taobao.org/url/download/url-0.10.3.tgz",
707 | "integrity": "sha1-Ah5NnHcF8hu/N9A861h2dAJ3TGQ=",
708 | "requires": {
709 | "punycode": "1.3.2",
710 | "querystring": "0.2.0"
711 | },
712 | "dependencies": {
713 | "punycode": {
714 | "version": "1.3.2",
715 | "resolved": "http://registry.npm.taobao.org/punycode/download/punycode-1.3.2.tgz",
716 | "integrity": "sha1-llOgNvt8HuQjQvIyXM7v6jkmxI0="
717 | }
718 | }
719 | },
720 | "utils-merge": {
721 | "version": "1.0.0",
722 | "resolved": "http://registry.npm.taobao.org/utils-merge/download/utils-merge-1.0.0.tgz",
723 | "integrity": "sha1-ApT7kiu5N1FTVBxPcJYjHyh8ivg="
724 | },
725 | "uuid": {
726 | "version": "3.1.0",
727 | "resolved": "http://registry.npm.taobao.org/uuid/download/uuid-3.1.0.tgz",
728 | "integrity": "sha1-PdPT55Crwk17DToDT/q6vijrvAQ="
729 | },
730 | "vary": {
731 | "version": "1.1.1",
732 | "resolved": "http://registry.npm.taobao.org/vary/download/vary-1.1.1.tgz",
733 | "integrity": "sha1-Z1Neu2lMHVIldFeYRmUyP1h+jTc="
734 | },
735 | "xml2js": {
736 | "version": "0.4.17",
737 | "resolved": "http://registry.npm.taobao.org/xml2js/download/xml2js-0.4.17.tgz",
738 | "integrity": "sha1-F76T6q4/O3eTWceVtBlwWogX6Gg=",
739 | "requires": {
740 | "sax": "1.2.1",
741 | "xmlbuilder": "4.2.1"
742 | }
743 | },
744 | "xmlbuilder": {
745 | "version": "4.2.1",
746 | "resolved": "http://registry.npm.taobao.org/xmlbuilder/download/xmlbuilder-4.2.1.tgz",
747 | "integrity": "sha1-qlijBBoGb5DqoWwvU4n/GfP0YaU=",
748 | "requires": {
749 | "lodash": "4.17.4"
750 | }
751 | },
752 | "xtend": {
753 | "version": "4.0.1",
754 | "resolved": "http://registry.npm.taobao.org/xtend/download/xtend-4.0.1.tgz",
755 | "integrity": "sha1-pcbVMr5lbiPbgg77lDofBJmNY68="
756 | }
757 | }
758 | }
759 |
--------------------------------------------------------------------------------
/frontend/src/bttn.css:
--------------------------------------------------------------------------------
1 | /*!
2 | *
3 | * bttn.css - https://ganapativs.github.io/bttn.css
4 | * Version - 0.2.4
5 | * Demo: https://bttn.surge.sh
6 | *
7 | * Licensed under the MIT license - http://opensource.org/licenses/MIT
8 | *
9 | * Copyright (c) 2016 Ganapati V S (@ganapativs)
10 | *
11 | */.bttn-default{color:#fff}.bttn,.bttn-lg,.bttn-md,.bttn-primary,.bttn-sm,.bttn-xs{color:#1d89ff}.bttn-warning{color:#feab3a}.bttn-danger{color:#ff5964}.bttn-success{color:#28b78d}.bttn-royal{color:#bd2df5}.bttn,.bttn-lg,.bttn-md,.bttn-sm,.bttn-xs{margin:0;padding:0;border-width:0;border-color:transparent;background:transparent;font-weight:400;cursor:pointer;position:relative}.bttn-lg{padding:8px 15px;font-size:24px}.bttn-lg,.bttn-md{font-family:inherit}.bttn-md{font-size:20px;padding:5px 12px}.bttn-sm{padding:4px 10px;font-size:16px}.bttn-sm,.bttn-xs{font-family:inherit}.bttn-xs{padding:3px 8px;font-size:12px}.bttn-gradient,.bttn-simple{margin:0;padding:0;border-color:transparent;background:transparent;font-weight:400;cursor:pointer;position:relative;font-size:20px;font-family:inherit;padding:5px 12px;overflow:hidden;border-width:0;border-radius:4px;background:hsla(0,0%,100%,.4);color:#fff;-webkit-transition:all .3s cubic-bezier(.02,.01,.47,1);transition:all .3s cubic-bezier(.02,.01,.47,1)}.bttn-gradient:focus,.bttn-gradient:hover,.bttn-simple:focus,.bttn-simple:hover{opacity:.75}.bttn-gradient.bttn-xs,.bttn-simple.bttn-xs{padding:3px 8px;font-size:12px;font-family:inherit}.bttn-gradient.bttn-sm,.bttn-simple.bttn-sm{padding:4px 10px;font-size:16px;font-family:inherit}.bttn-gradient.bttn-md,.bttn-simple.bttn-md{font-size:20px;font-family:inherit;padding:5px 12px}.bttn-gradient.bttn-lg,.bttn-simple.bttn-lg{padding:8px 15px;font-size:24px;font-family:inherit}.bttn-gradient.bttn-default,.bttn-simple.bttn-default{background:hsla(0,0%,100%,.4)}.bttn-gradient.bttn-primary,.bttn-simple.bttn-primary{background:#1d89ff}.bttn-gradient.bttn-warning,.bttn-simple.bttn-warning{background:#feab3a}.bttn-gradient.bttn-danger,.bttn-simple.bttn-danger{background:#ff5964}.bttn-gradient.bttn-success,.bttn-simple.bttn-success{background:#28b78d}.bttn-gradient.bttn-royal,.bttn-simple.bttn-royal{background:#bd2df5}.bttn-bordered{margin:0;padding:0;border-width:0;border-color:transparent;font-weight:400;cursor:pointer;position:relative;font-size:20px;font-family:inherit;padding:5px 12px;overflow:hidden;border:1px solid hsla(0,0%,100%,.4);border-radius:4px;background:transparent;color:#fff;-webkit-transition:all .3s cubic-bezier(.02,.01,.47,1);transition:all .3s cubic-bezier(.02,.01,.47,1)}.bttn-bordered:focus,.bttn-bordered:hover{border-color:hsla(0,0%,100%,.7)}.bttn-bordered.bttn-xs{padding:3px 8px;font-size:12px;font-family:inherit}.bttn-bordered.bttn-sm{padding:4px 10px;font-size:16px;font-family:inherit}.bttn-bordered.bttn-md{font-size:20px;font-family:inherit;padding:5px 12px}.bttn-bordered.bttn-lg{padding:8px 15px;font-size:24px;font-family:inherit}.bttn-bordered.bttn-default{border-color:hsla(0,0%,100%,.4);color:#fff}.bttn-bordered.bttn-default:focus,.bttn-bordered.bttn-default:hover{border-color:hsla(0,0%,100%,.7)}.bttn-bordered.bttn-primary{border-color:rgba(29,137,255,.4);color:#1d89ff}.bttn-bordered.bttn-primary:focus,.bttn-bordered.bttn-primary:hover{border-color:rgba(29,137,255,.7)}.bttn-bordered.bttn-warning{border-color:rgba(254,171,58,.4);color:#feab3a}.bttn-bordered.bttn-warning:focus,.bttn-bordered.bttn-warning:hover{border-color:rgba(254,171,58,.7)}.bttn-bordered.bttn-danger{border-color:rgba(255,89,100,.4);color:#ff5964}.bttn-bordered.bttn-danger:focus,.bttn-bordered.bttn-danger:hover{border-color:rgba(255,89,100,.7)}.bttn-bordered.bttn-success{border-color:rgba(40,183,141,.4);color:#28b78d}.bttn-bordered.bttn-success:focus,.bttn-bordered.bttn-success:hover{border-color:rgba(40,183,141,.7)}.bttn-bordered.bttn-royal{border-color:rgba(189,45,245,.4);color:#bd2df5}.bttn-bordered.bttn-royal:focus,.bttn-bordered.bttn-royal:hover{border-color:rgba(189,45,245,.7)}.bttn-gradient{border-radius:100px;box-shadow:0 1px 2px rgba(0,0,0,.25);text-shadow:0 1px 0 hsla(0,0%,100%,.25)}.bttn-gradient,.bttn-gradient.bttn-default{background-image:-webkit-gradient(linear,left top,left bottom,color-stop(0,#fff),color-stop(1,#d6e3ff));background-image:-webkit-linear-gradient(top,#fff,#d6e3ff);background-image:linear-gradient(180deg,#fff 0,#d6e3ff);background-image:-webkit-linear-gradient(93deg,#d6e3ff,#fff);color:#1d89ff}.bttn-gradient.bttn-primary{background-image:-webkit-gradient(linear,left top,left bottom,color-stop(0,#00bbd4),color-stop(1,#3f51b5));background-image:-webkit-linear-gradient(top,#00bbd4,#3f51b5);background-image:linear-gradient(180deg,#00bbd4 0,#3f51b5);background-image:-webkit-linear-gradient(93deg,#3f51b5,#00bbd4);color:#fff}.bttn-gradient.bttn-warning{background-image:-webkit-gradient(linear,left top,left bottom,color-stop(0,#feab3a),color-stop(1,#f35626));background-image:-webkit-linear-gradient(top,#feab3a,#f35626);background-image:linear-gradient(180deg,#feab3a 0,#f35626);background-image:-webkit-linear-gradient(93deg,#f35626,#feab3a);color:#fff}.bttn-gradient.bttn-danger{background-image:-webkit-gradient(linear,left top,left bottom,color-stop(0,#ff97c2),color-stop(1,#e91e63));background-image:-webkit-linear-gradient(top,#ff97c2,#e91e63);background-image:linear-gradient(180deg,#ff97c2 0,#e91e63);background-image:-webkit-linear-gradient(93deg,#e91e63,#ff97c2);color:#fff}.bttn-gradient.bttn-success{background-image:-webkit-gradient(linear,left top,left bottom,color-stop(0,#9ccc65),color-stop(1,#009688));background-image:-webkit-linear-gradient(top,#9ccc65,#009688);background-image:linear-gradient(180deg,#9ccc65 0,#009688);background-image:-webkit-linear-gradient(93deg,#009688,#9ccc65);color:#fff}.bttn-gradient.bttn-royal{background-image:-webkit-gradient(linear,left top,left bottom,color-stop(0,#9c27b0),color-stop(1,#512da8));background-image:-webkit-linear-gradient(top,#9c27b0,#512da8);background-image:linear-gradient(180deg,#9c27b0 0,#512da8);background-image:-webkit-linear-gradient(93deg,#512da8,#9c27b0);color:#fff}.bttn-minimal{margin:0;padding:0;border-color:transparent;font-weight:400;cursor:pointer;position:relative;font-size:20px;font-family:inherit;padding:5px 12px;overflow:hidden;border-width:0;border-radius:4px;background:transparent;color:#fff;-webkit-transition:all .5s cubic-bezier(.02,.01,.47,1);transition:all .5s cubic-bezier(.02,.01,.47,1)}.bttn-minimal:after,.bttn-minimal:before{position:absolute;bottom:0;left:10px;width:calc(100% - 20px);height:1px;background:currentColor;content:'';opacity:.65;-webkit-transition:opacity .5s cubic-bezier(.02,.01,.47,1),-webkit-transform .5s cubic-bezier(.02,.01,.47,1);transition:opacity .5s cubic-bezier(.02,.01,.47,1),-webkit-transform .5s cubic-bezier(.02,.01,.47,1);transition:transform .5s cubic-bezier(.02,.01,.47,1),opacity .5s cubic-bezier(.02,.01,.47,1);transition:transform .5s cubic-bezier(.02,.01,.47,1),opacity .5s cubic-bezier(.02,.01,.47,1),-webkit-transform .5s cubic-bezier(.02,.01,.47,1)}.bttn-minimal:focus,.bttn-minimal:hover{opacity:.9}.bttn-minimal:focus:after,.bttn-minimal:hover:after{opacity:1;-webkit-transform:translateX(-10px) rotate(.001deg);transform:translateX(-10px) rotate(.001deg)}.bttn-minimal:focus:before,.bttn-minimal:hover:before{opacity:1;-webkit-transform:translateX(10px) rotate(.001deg);transform:translateX(10px) rotate(.001deg)}.bttn-minimal.bttn-xs{padding:3px 8px;font-size:12px;font-family:inherit}.bttn-minimal.bttn-sm{padding:4px 10px;font-size:16px;font-family:inherit}.bttn-minimal.bttn-md{font-size:20px;font-family:inherit;padding:5px 12px}.bttn-minimal.bttn-lg{padding:8px 15px;font-size:24px;font-family:inherit}.bttn-minimal.bttn-default{color:#fff}.bttn-minimal.bttn-primary{color:#1d89ff}.bttn-minimal.bttn-warning{color:#feab3a}.bttn-minimal.bttn-danger{color:#ff5964}.bttn-minimal.bttn-success{color:#28b78d}.bttn-minimal.bttn-royal{color:#bd2df5}.bttn-stretch{margin:0;padding:0;border-color:transparent;font-weight:400;cursor:pointer;position:relative;font-size:20px;font-family:inherit;padding:5px 12px;overflow:hidden;border-width:0;border-radius:0;background:transparent;color:#fff;letter-spacing:0}.bttn-stretch,.bttn-stretch:after,.bttn-stretch:before{-webkit-transition:all .2s cubic-bezier(.02,.01,.47,1);transition:all .2s cubic-bezier(.02,.01,.47,1)}.bttn-stretch:after,.bttn-stretch:before{position:absolute;left:0;width:100%;height:1px;background:currentColor;content:'';opacity:.65;-webkit-transform:scaleX(0);transform:scaleX(0)}.bttn-stretch:after{top:0}.bttn-stretch:before{bottom:0}.bttn-stretch:focus,.bttn-stretch:hover{letter-spacing:2px;opacity:.9;-webkit-transition:all .3s cubic-bezier(.02,.01,.47,1);transition:all .3s cubic-bezier(.02,.01,.47,1)}.bttn-stretch:focus:after,.bttn-stretch:focus:before,.bttn-stretch:hover:after,.bttn-stretch:hover:before{opacity:1;-webkit-transition:all .3s cubic-bezier(.02,.01,.47,1);transition:all .3s cubic-bezier(.02,.01,.47,1);-webkit-transform:scaleX(1);transform:scaleX(1)}.bttn-stretch.bttn-xs{padding:3px 8px;font-size:12px;font-family:inherit}.bttn-stretch.bttn-sm{padding:4px 10px;font-size:16px;font-family:inherit}.bttn-stretch.bttn-md{font-size:20px;font-family:inherit;padding:5px 12px}.bttn-stretch.bttn-lg{padding:8px 15px;font-size:24px;font-family:inherit}.bttn-stretch.bttn-default{color:#fff}.bttn-stretch.bttn-primary{color:#1d89ff}.bttn-stretch.bttn-warning{color:#feab3a}.bttn-stretch.bttn-danger{color:#ff5964}.bttn-stretch.bttn-success{color:#28b78d}.bttn-stretch.bttn-royal{color:#bd2df5}.bttn-jelly{margin:0;padding:0;border-width:0;border-color:transparent;background:transparent;font-weight:400;cursor:pointer;position:relative;font-size:20px;font-family:inherit;padding:5px 12px;overflow:hidden;background:#fff;color:#1d89ff}.bttn-jelly,.bttn-jelly:before{border-radius:50px;-webkit-transition:all .2s cubic-bezier(.02,.01,.47,1);transition:all .2s cubic-bezier(.02,.01,.47,1)}.bttn-jelly:before{position:absolute;top:0;left:0;width:100%;height:100%;background:currentColor;content:'';z-index:-1;opacity:0;-webkit-transform:scale(.2);transform:scale(.2)}.bttn-jelly:focus,.bttn-jelly:hover{box-shadow:0 1px 8px rgba(58,51,53,.4);-webkit-transform:scale(1.1);transform:scale(1.1)}.bttn-jelly:focus,.bttn-jelly:focus:before,.bttn-jelly:hover,.bttn-jelly:hover:before{-webkit-transition:all .3s cubic-bezier(.02,.01,.47,1);transition:all .3s cubic-bezier(.02,.01,.47,1)}.bttn-jelly:focus:before,.bttn-jelly:hover:before{opacity:.15;-webkit-transform:scale(1);transform:scale(1)}.bttn-jelly.bttn-xs{padding:3px 8px;font-size:12px;font-family:inherit}.bttn-jelly.bttn-xs:focus,.bttn-jelly.bttn-xs:hover{box-shadow:0 1px 4px rgba(58,51,53,.4)}.bttn-jelly.bttn-sm{padding:4px 10px;font-size:16px;font-family:inherit}.bttn-jelly.bttn-sm:focus,.bttn-jelly.bttn-sm:hover{box-shadow:0 1px 6px rgba(58,51,53,.4)}.bttn-jelly.bttn-md{font-size:20px;font-family:inherit;padding:5px 12px}.bttn-jelly.bttn-md:focus,.bttn-jelly.bttn-md:hover{box-shadow:0 1px 8px rgba(58,51,53,.4)}.bttn-jelly.bttn-lg{padding:8px 15px;font-size:24px;font-family:inherit}.bttn-jelly.bttn-lg:focus,.bttn-jelly.bttn-lg:hover{box-shadow:0 1px 10px rgba(58,51,53,.4)}.bttn-jelly.bttn-default{background:#fff;color:#1d89ff}.bttn-jelly.bttn-primary{background:#1d89ff;color:#fff}.bttn-jelly.bttn-warning{background:#feab3a;color:#fff}.bttn-jelly.bttn-danger{background:#ff5964;color:#fff}.bttn-jelly.bttn-success{background:#28b78d;color:#fff}.bttn-jelly.bttn-royal{background:#bd2df5;color:#fff}.bttn-fill{margin:0;padding:0;border-width:0;border-color:transparent;background:transparent;font-weight:400;cursor:pointer;position:relative;font-size:20px;font-family:inherit;padding:5px 12px;z-index:0;border:none;background:#fff;color:#1d89ff;-webkit-transition:all .3s cubic-bezier(.02,.01,.47,1);transition:all .3s cubic-bezier(.02,.01,.47,1)}.bttn-fill:before{position:absolute;bottom:0;left:0;width:100%;height:100%;background:#1d89ff;content:'';opacity:0;-webkit-transition:opacity .15s ease-out,-webkit-transform .15s ease-out;transition:opacity .15s ease-out,-webkit-transform .15s ease-out;transition:transform .15s ease-out,opacity .15s ease-out;transition:transform .15s ease-out,opacity .15s ease-out,-webkit-transform .15s ease-out;z-index:-1;-webkit-transform:scaleX(0);transform:scaleX(0)}.bttn-fill:focus,.bttn-fill:hover{box-shadow:0 1px 8px rgba(58,51,53,.3);color:#fff;-webkit-transition:all .5s cubic-bezier(.02,.01,.47,1);transition:all .5s cubic-bezier(.02,.01,.47,1)}.bttn-fill:focus:before,.bttn-fill:hover:before{opacity:1;-webkit-transition:opacity .2s ease-in,-webkit-transform .2s ease-in;transition:opacity .2s ease-in,-webkit-transform .2s ease-in;transition:transform .2s ease-in,opacity .2s ease-in;transition:transform .2s ease-in,opacity .2s ease-in,-webkit-transform .2s ease-in;-webkit-transform:scaleX(1);transform:scaleX(1)}.bttn-fill.bttn-xs{padding:3px 8px;font-size:12px;font-family:inherit}.bttn-fill.bttn-xs:focus,.bttn-fill.bttn-xs:hover{box-shadow:0 1px 4px rgba(58,51,53,.3)}.bttn-fill.bttn-sm{padding:4px 10px;font-size:16px;font-family:inherit}.bttn-fill.bttn-sm:focus,.bttn-fill.bttn-sm:hover{box-shadow:0 1px 6px rgba(58,51,53,.3)}.bttn-fill.bttn-md{font-size:20px;font-family:inherit;padding:5px 12px}.bttn-fill.bttn-md:focus,.bttn-fill.bttn-md:hover{box-shadow:0 1px 8px rgba(58,51,53,.3)}.bttn-fill.bttn-lg{padding:8px 15px;font-size:24px;font-family:inherit}.bttn-fill.bttn-lg:focus,.bttn-fill.bttn-lg:hover{box-shadow:0 1px 10px rgba(58,51,53,.3)}.bttn-fill.bttn-default{background:#fff;color:#1d89ff}.bttn-fill.bttn-default:focus,.bttn-fill.bttn-default:hover{color:#fff}.bttn-fill.bttn-default:before{background:#1d89ff}.bttn-fill.bttn-primary{background:#1d89ff;color:#fff}.bttn-fill.bttn-primary:focus,.bttn-fill.bttn-primary:hover{color:#1d89ff}.bttn-fill.bttn-primary:before{background:#fff}.bttn-fill.bttn-warning{background:#feab3a;color:#fff}.bttn-fill.bttn-warning:focus,.bttn-fill.bttn-warning:hover{color:#feab3a}.bttn-fill.bttn-warning:before{background:#fff}.bttn-fill.bttn-danger{background:#ff5964;color:#fff}.bttn-fill.bttn-danger:focus,.bttn-fill.bttn-danger:hover{color:#ff5964}.bttn-fill.bttn-danger:before{background:#fff}.bttn-fill.bttn-success{background:#28b78d;color:#fff}.bttn-fill.bttn-success:focus,.bttn-fill.bttn-success:hover{color:#28b78d}.bttn-fill.bttn-success:before{background:#fff}.bttn-fill.bttn-royal{background:#bd2df5;color:#fff}.bttn-fill.bttn-royal:focus,.bttn-fill.bttn-royal:hover{color:#bd2df5}.bttn-fill.bttn-royal:before{background:#fff}.bttn-material-circle{margin:0;padding:0;border-color:transparent;background:transparent;font-weight:400;cursor:pointer;position:relative;font-size:20px;font-family:inherit;padding:5px 12px;overflow:hidden;border-width:0;border-radius:50%;background:#fff;box-shadow:0 2px 5px 0 rgba(0,0,0,.18),0 1px 5px 0 rgba(0,0,0,.15);color:#1d89ff;-webkit-transition:all .25s cubic-bezier(.02,.01,.47,1);transition:all .25s cubic-bezier(.02,.01,.47,1);-webkit-transform:translateZ(0);transform:translateZ(0)}.bttn-material-circle:focus,.bttn-material-circle:hover{box-shadow:0 5px 11px 0 rgba(0,0,0,.18),0 4px 15px 0 rgba(0,0,0,.15);-webkit-transition:box-shadow .4s ease-out;transition:box-shadow .4s ease-out}.bttn-material-circle.bttn-xs{padding:3px 8px;font-size:12px;font-family:inherit;width:28px;height:28px;line-height:24px}.bttn-material-circle.bttn-sm{padding:4px 10px;font-size:16px;font-family:inherit;width:36px;height:36px;line-height:30px}.bttn-material-circle.bttn-md{font-size:20px;font-family:inherit;padding:5px 12px;width:44px;height:44px;line-height:38px}.bttn-material-circle.bttn-lg{padding:8px 15px;font-size:24px;font-family:inherit;width:54px;height:54px;line-height:44px}.bttn-material-circle.bttn-default{background:#fff;color:#1d89ff}.bttn-material-circle.bttn-primary{background:#1d89ff;color:#fff}.bttn-material-circle.bttn-warning{background:#feab3a;color:#fff}.bttn-material-circle.bttn-danger{background:#ff5964;color:#fff}.bttn-material-circle.bttn-success{background:#28b78d;color:#fff}.bttn-material-circle.bttn-royal{background:#bd2df5;color:#fff}.bttn-material-flat{margin:0;padding:0;border-color:transparent;background:transparent;font-weight:400;cursor:pointer;position:relative;font-size:20px;font-family:inherit;padding:5px 12px;overflow:hidden;border-width:0;border-radius:2px;background:#fff;box-shadow:0 2px 5px 0 rgba(0,0,0,.18),0 1px 5px 0 rgba(0,0,0,.15);color:#1d89ff;text-transform:uppercase;-webkit-transition:all .25s cubic-bezier(.02,.01,.47,1);transition:all .25s cubic-bezier(.02,.01,.47,1);-webkit-transform:translateZ(0);transform:translateZ(0)}.bttn-material-flat:focus,.bttn-material-flat:hover{box-shadow:0 5px 11px 0 rgba(0,0,0,.18),0 4px 15px 0 rgba(0,0,0,.15);-webkit-transition:box-shadow .4s ease-out;transition:box-shadow .4s ease-out}.bttn-material-flat.bttn-xs{padding:3px 8px;font-size:12px;font-family:inherit}.bttn-material-flat.bttn-sm{padding:4px 10px;font-size:16px;font-family:inherit}.bttn-material-flat.bttn-md{font-size:20px;font-family:inherit;padding:5px 12px}.bttn-material-flat.bttn-lg{padding:8px 15px;font-size:24px;font-family:inherit}.bttn-material-flat.bttn-default{background:#fff;color:#1d89ff}.bttn-material-flat.bttn-primary{background:#1d89ff;color:#fff}.bttn-material-flat.bttn-warning{background:#feab3a;color:#fff}.bttn-material-flat.bttn-danger{background:#ff5964;color:#fff}.bttn-material-flat.bttn-success{background:#28b78d;color:#fff}.bttn-material-flat.bttn-royal{background:#bd2df5;color:#fff}.bttn-pill{margin:0;padding:0;border-width:0;border-color:transparent;background:transparent;font-weight:400;cursor:pointer;position:relative;font-size:20px;font-family:inherit;padding:5px 12px;z-index:0;overflow:hidden;border:none;border-radius:100px;background:#fff;color:#1d89ff;-webkit-transition:all .3s cubic-bezier(.02,.01,.47,1);transition:all .3s cubic-bezier(.02,.01,.47,1)}.bttn-pill:after,.bttn-pill:before{position:absolute;right:0;bottom:0;width:100px;height:100px;border-radius:50%;background:#1d89ff;content:'';opacity:0;-webkit-transition:opacity .15s cubic-bezier(.02,.01,.47,1),-webkit-transform .15s cubic-bezier(.02,.01,.47,1);transition:opacity .15s cubic-bezier(.02,.01,.47,1),-webkit-transform .15s cubic-bezier(.02,.01,.47,1);transition:transform .15s cubic-bezier(.02,.01,.47,1),opacity .15s cubic-bezier(.02,.01,.47,1);transition:transform .15s cubic-bezier(.02,.01,.47,1),opacity .15s cubic-bezier(.02,.01,.47,1),-webkit-transform .15s cubic-bezier(.02,.01,.47,1);z-index:-1;-webkit-transform:translate(100%,-25%) translateZ(0);transform:translate(100%,-25%) translateZ(0)}.bttn-pill:focus,.bttn-pill:hover{box-shadow:0 1px 8px rgba(58,51,53,.3);color:#fff;-webkit-transition:all .5s cubic-bezier(.02,.01,.47,1);transition:all .5s cubic-bezier(.02,.01,.47,1);-webkit-transform:scale(1.1) translateZ(0);transform:scale(1.1) translateZ(0)}.bttn-pill:focus:before,.bttn-pill:hover:before{opacity:.15;-webkit-transition:opacity .2s cubic-bezier(.02,.01,.47,1),-webkit-transform .2s cubic-bezier(.02,.01,.47,1);transition:opacity .2s cubic-bezier(.02,.01,.47,1),-webkit-transform .2s cubic-bezier(.02,.01,.47,1);transition:transform .2s cubic-bezier(.02,.01,.47,1),opacity .2s cubic-bezier(.02,.01,.47,1);transition:transform .2s cubic-bezier(.02,.01,.47,1),opacity .2s cubic-bezier(.02,.01,.47,1),-webkit-transform .2s cubic-bezier(.02,.01,.47,1);-webkit-transform:translate3d(50%,0,0) scale(.9);transform:translate3d(50%,0,0) scale(.9)}.bttn-pill:focus:after,.bttn-pill:hover:after{opacity:.25;-webkit-transition:opacity .2s cubic-bezier(.02,.01,.47,1) .05s,-webkit-transform .2s cubic-bezier(.02,.01,.47,1) .05s;transition:opacity .2s cubic-bezier(.02,.01,.47,1) .05s,-webkit-transform .2s cubic-bezier(.02,.01,.47,1) .05s;transition:transform .2s cubic-bezier(.02,.01,.47,1) .05s,opacity .2s cubic-bezier(.02,.01,.47,1) .05s;transition:transform .2s cubic-bezier(.02,.01,.47,1) .05s,opacity .2s cubic-bezier(.02,.01,.47,1) .05s,-webkit-transform .2s cubic-bezier(.02,.01,.47,1) .05s;-webkit-transform:translate(50%) scale(1.1);transform:translate(50%) scale(1.1)}.bttn-pill.bttn-xs{padding:3px 8px;font-size:12px;font-family:inherit}.bttn-pill.bttn-xs:focus,.bttn-pill.bttn-xs:hover{box-shadow:0 1px 4px rgba(58,51,53,.3)}.bttn-pill.bttn-sm{padding:4px 10px;font-size:16px;font-family:inherit}.bttn-pill.bttn-sm:focus,.bttn-pill.bttn-sm:hover{box-shadow:0 1px 6px rgba(58,51,53,.3)}.bttn-pill.bttn-md{font-size:20px;font-family:inherit;padding:5px 12px}.bttn-pill.bttn-md:focus,.bttn-pill.bttn-md:hover{box-shadow:0 1px 8px rgba(58,51,53,.3)}.bttn-pill.bttn-lg{padding:8px 15px;font-size:24px;font-family:inherit}.bttn-pill.bttn-lg:focus,.bttn-pill.bttn-lg:hover{box-shadow:0 1px 10px rgba(58,51,53,.3)}.bttn-pill.bttn-default{background:#fff;color:#1d89ff}.bttn-pill.bttn-default:focus,.bttn-pill.bttn-default:hover{color:#1d89ff}.bttn-pill.bttn-default:after,.bttn-pill.bttn-default:before{background:#1d89ff}.bttn-pill.bttn-primary{background:#1d89ff;color:#fff}.bttn-pill.bttn-primary:focus,.bttn-pill.bttn-primary:hover{color:#fff}.bttn-pill.bttn-primary:after,.bttn-pill.bttn-primary:before{background:#fff}.bttn-pill.bttn-warning{background:#feab3a;color:#fff}.bttn-pill.bttn-warning:focus,.bttn-pill.bttn-warning:hover{color:#fff}.bttn-pill.bttn-warning:after,.bttn-pill.bttn-warning:before{background:#fff}.bttn-pill.bttn-danger{background:#ff5964;color:#fff}.bttn-pill.bttn-danger:focus,.bttn-pill.bttn-danger:hover{color:#fff}.bttn-pill.bttn-danger:after,.bttn-pill.bttn-danger:before{background:#fff}.bttn-pill.bttn-success{background:#28b78d;color:#fff}.bttn-pill.bttn-success:focus,.bttn-pill.bttn-success:hover{color:#fff}.bttn-pill.bttn-success:after,.bttn-pill.bttn-success:before{background:#fff}.bttn-pill.bttn-royal{background:#bd2df5;color:#fff}.bttn-pill.bttn-royal:focus,.bttn-pill.bttn-royal:hover{color:#fff}.bttn-pill.bttn-royal:after,.bttn-pill.bttn-royal:before{background:#fff}.bttn-float{margin:0;padding:0;border-width:0;border-color:transparent;background:transparent;font-weight:400;cursor:pointer;position:relative;font-size:20px;font-family:inherit;padding:5px 12px;overflow:hidden;border:1px dotted #fff;border-radius:4px;background:hsla(0,0%,100%,.4);color:#fff;-webkit-transition:opacity .3s cubic-bezier(.02,.01,.47,1),box-shadow .2s cubic-bezier(.02,.01,.47,1),-webkit-transform .3s cubic-bezier(.02,.01,.47,1);transition:opacity .3s cubic-bezier(.02,.01,.47,1),box-shadow .2s cubic-bezier(.02,.01,.47,1),-webkit-transform .3s cubic-bezier(.02,.01,.47,1);transition:transform .3s cubic-bezier(.02,.01,.47,1),opacity .3s cubic-bezier(.02,.01,.47,1),box-shadow .2s cubic-bezier(.02,.01,.47,1);transition:transform .3s cubic-bezier(.02,.01,.47,1),opacity .3s cubic-bezier(.02,.01,.47,1),box-shadow .2s cubic-bezier(.02,.01,.47,1),-webkit-transform .3s cubic-bezier(.02,.01,.47,1)}.bttn-float:focus,.bttn-float:hover{box-shadow:0 30px 30px rgba(0,0,0,.16);opacity:.85;-webkit-transition:opacity .2s cubic-bezier(.02,.01,.47,1),box-shadow .4s cubic-bezier(.02,.01,.47,1),-webkit-transform .2s cubic-bezier(.02,.01,.47,1);transition:opacity .2s cubic-bezier(.02,.01,.47,1),box-shadow .4s cubic-bezier(.02,.01,.47,1),-webkit-transform .2s cubic-bezier(.02,.01,.47,1);transition:transform .2s cubic-bezier(.02,.01,.47,1),opacity .2s cubic-bezier(.02,.01,.47,1),box-shadow .4s cubic-bezier(.02,.01,.47,1);transition:transform .2s cubic-bezier(.02,.01,.47,1),opacity .2s cubic-bezier(.02,.01,.47,1),box-shadow .4s cubic-bezier(.02,.01,.47,1),-webkit-transform .2s cubic-bezier(.02,.01,.47,1)}.bttn-float.bttn-xs{padding:3px 8px;font-size:12px;font-family:inherit}.bttn-float.bttn-xs:focus,.bttn-float.bttn-xs:hover{-webkit-transform:translateY(-6px);transform:translateY(-6px)}.bttn-float.bttn-sm{padding:4px 10px;font-size:16px;font-family:inherit}.bttn-float.bttn-sm:focus,.bttn-float.bttn-sm:hover{-webkit-transform:translateY(-8px);transform:translateY(-8px)}.bttn-float.bttn-md{font-size:20px;font-family:inherit;padding:5px 12px}.bttn-float.bttn-md:focus,.bttn-float.bttn-md:hover{-webkit-transform:translateY(-10px);transform:translateY(-10px)}.bttn-float.bttn-lg{padding:8px 15px;font-size:24px;font-family:inherit}.bttn-float.bttn-lg:focus,.bttn-float.bttn-lg:hover{-webkit-transform:translateY(-12px);transform:translateY(-12px)}.bttn-float.bttn-default{border-color:#fff;background:hsla(0,0%,100%,.4);color:#fff}.bttn-float.bttn-primary{border-color:#1d89ff;background:rgba(29,137,255,.4);color:#1d89ff}.bttn-float.bttn-warning{border-color:#feab3a;background:rgba(254,171,58,.4);color:#feab3a}.bttn-float.bttn-danger{border-color:#ff5964;background:rgba(255,89,100,.4);color:#ff5964}.bttn-float.bttn-success{border-color:#28b78d;background:rgba(40,183,141,.4);color:#28b78d}.bttn-float.bttn-royal{border-color:#bd2df5;background:rgba(189,45,245,.4);color:#bd2df5}.bttn-unite{margin:0;padding:0;border-width:0;border-color:transparent;background:transparent;font-weight:400;cursor:pointer;position:relative;font-size:20px;font-family:inherit;padding:5px 12px;z-index:0;overflow:hidden;border:1px solid #1d89ff;border-radius:100px;background:#fff;color:#1d89ff;-webkit-transition:color .3s cubic-bezier(.02,.01,.47,1),border-color .3s cubic-bezier(.02,.01,.47,1);transition:color .3s cubic-bezier(.02,.01,.47,1),border-color .3s cubic-bezier(.02,.01,.47,1)}.bttn-unite:before{background:#d6e3ff;-webkit-transform:translate3d(-110%,-10%,0) skewX(-20deg);transform:translate3d(-110%,-10%,0) skewX(-20deg)}.bttn-unite:after,.bttn-unite:before{position:absolute;top:0;left:0;width:100%;height:120%;content:'';opacity:0;z-index:-1;-webkit-transition:opacity .15s cubic-bezier(.02,.01,.47,1),-webkit-transform .15s cubic-bezier(.02,.01,.47,1);transition:opacity .15s cubic-bezier(.02,.01,.47,1),-webkit-transform .15s cubic-bezier(.02,.01,.47,1);transition:transform .15s cubic-bezier(.02,.01,.47,1),opacity .15s cubic-bezier(.02,.01,.47,1);transition:transform .15s cubic-bezier(.02,.01,.47,1),opacity .15s cubic-bezier(.02,.01,.47,1),-webkit-transform .15s cubic-bezier(.02,.01,.47,1)}.bttn-unite:after{background:rgba(214,227,255,.7);-webkit-transform:translate3d(110%,-10%,0) skewX(-20deg);transform:translate3d(110%,-10%,0) skewX(-20deg)}.bttn-unite:focus,.bttn-unite:hover{box-shadow:0 1px 8px rgba(58,51,53,.3);color:#1d89ff;-webkit-transition:all .5s cubic-bezier(.02,.01,.47,1);transition:all .5s cubic-bezier(.02,.01,.47,1)}.bttn-unite:focus:before,.bttn-unite:hover:before{-webkit-transform:translate3d(-50%,-10%,0) skewX(-20deg);transform:translate3d(-50%,-10%,0) skewX(-20deg)}.bttn-unite:focus:after,.bttn-unite:focus:before,.bttn-unite:hover:after,.bttn-unite:hover:before{opacity:1;-webkit-transition:opacity .25s cubic-bezier(.02,.01,.47,1),-webkit-transform .25s cubic-bezier(.02,.01,.47,1);transition:opacity .25s cubic-bezier(.02,.01,.47,1),-webkit-transform .25s cubic-bezier(.02,.01,.47,1);transition:transform .25s cubic-bezier(.02,.01,.47,1),opacity .25s cubic-bezier(.02,.01,.47,1);transition:transform .25s cubic-bezier(.02,.01,.47,1),opacity .25s cubic-bezier(.02,.01,.47,1),-webkit-transform .25s cubic-bezier(.02,.01,.47,1)}.bttn-unite:focus:after,.bttn-unite:hover:after{-webkit-transform:translate3d(50%,-10%,0) skewX(-20deg);transform:translate3d(50%,-10%,0) skewX(-20deg)}.bttn-unite.bttn-xs{padding:3px 8px;font-size:12px;font-family:inherit}.bttn-unite.bttn-xs:focus,.bttn-unite.bttn-xs:hover{box-shadow:0 1px 4px rgba(58,51,53,.3)}.bttn-unite.bttn-sm{padding:4px 10px;font-size:16px;font-family:inherit}.bttn-unite.bttn-sm:focus,.bttn-unite.bttn-sm:hover{box-shadow:0 1px 6px rgba(58,51,53,.3)}.bttn-unite.bttn-md{font-size:20px;font-family:inherit;padding:5px 12px}.bttn-unite.bttn-md:focus,.bttn-unite.bttn-md:hover{box-shadow:0 1px 8px rgba(58,51,53,.3)}.bttn-unite.bttn-lg{padding:8px 15px;font-size:24px;font-family:inherit}.bttn-unite.bttn-lg:focus,.bttn-unite.bttn-lg:hover{box-shadow:0 1px 10px rgba(58,51,53,.3)}.bttn-unite.bttn-default{border-color:#1d89ff;color:#1d89ff}.bttn-unite.bttn-default:focus,.bttn-unite.bttn-default:hover{background:#d6e3ff;color:#1d89ff}.bttn-unite.bttn-default:before{background:#a7c3ff}.bttn-unite.bttn-default:after{background:#d6e3ff}.bttn-unite.bttn-primary{border-color:#1d89ff;color:#1d89ff}.bttn-unite.bttn-primary:focus,.bttn-unite.bttn-primary:hover{background:#1d89ff;color:#fff}.bttn-unite.bttn-primary:before{background:#006de3}.bttn-unite.bttn-primary:after{background:#1d89ff}.bttn-unite.bttn-warning{border-color:#feab3a;color:#feab3a}.bttn-unite.bttn-warning:focus,.bttn-unite.bttn-warning:hover{background:#feab3a;color:#fff}.bttn-unite.bttn-warning:before{background:#f89001}.bttn-unite.bttn-warning:after{background:#feab3a}.bttn-unite.bttn-danger{border-color:#ff5964;color:#ff5964}.bttn-unite.bttn-danger:focus,.bttn-unite.bttn-danger:hover{background:#ff5964;color:#fff}.bttn-unite.bttn-danger:before{background:#ff1424}.bttn-unite.bttn-danger:after{background:#ff5964}.bttn-unite.bttn-success{border-color:#28b78d;color:#28b78d}.bttn-unite.bttn-success:focus,.bttn-unite.bttn-success:hover{background:#28b78d;color:#fff}.bttn-unite.bttn-success:before{background:#209271}.bttn-unite.bttn-success:after{background:#28b78d}.bttn-unite.bttn-royal{border-color:#bd2df5;color:#bd2df5}.bttn-unite.bttn-royal:focus,.bttn-unite.bttn-royal:hover{background:#bd2df5;color:#fff}.bttn-unite.bttn-royal:before{background:#a20bdd}.bttn-unite.bttn-royal:after{background:#bd2df5}.bttn-slant{margin:0;padding:0;border-width:0;border-color:transparent;font-weight:400;cursor:pointer;position:relative;font-size:20px;font-family:inherit;padding:5px 12px;z-index:0;border:none;border-radius:0;background:transparent;color:#1d89ff;-webkit-transition:color .3s cubic-bezier(.02,.01,.47,1),-webkit-transform .3s cubic-bezier(.02,.01,.47,1);transition:color .3s cubic-bezier(.02,.01,.47,1),-webkit-transform .3s cubic-bezier(.02,.01,.47,1);transition:color .3s cubic-bezier(.02,.01,.47,1),transform .3s cubic-bezier(.02,.01,.47,1);transition:color .3s cubic-bezier(.02,.01,.47,1),transform .3s cubic-bezier(.02,.01,.47,1),-webkit-transform .3s cubic-bezier(.02,.01,.47,1)}.bttn-slant:before{width:100%;background:#fafafa;-webkit-transition:box-shadow .2s cubic-bezier(.02,.01,.47,1);transition:box-shadow .2s cubic-bezier(.02,.01,.47,1)}.bttn-slant:after,.bttn-slant:before{position:absolute;top:0;left:0;z-index:-1;height:100%;content:'';-webkit-transform:skewX(20deg);transform:skewX(20deg)}.bttn-slant:after{width:0;background:hsla(0,0%,98%,.3);opacity:0;-webkit-transition:opacity .2s cubic-bezier(.02,.01,.47,1),width .15s cubic-bezier(.02,.01,.47,1);transition:opacity .2s cubic-bezier(.02,.01,.47,1),width .15s cubic-bezier(.02,.01,.47,1)}.bttn-slant:focus,.bttn-slant:hover{-webkit-transform:translateX(5px);transform:translateX(5px)}.bttn-slant:focus:after,.bttn-slant:hover:after{width:5px;opacity:1}.bttn-slant:focus:before,.bttn-slant:hover:before{box-shadow:inset 0 -1px 0 #a7c3ff,inset 0 1px 0 #a7c3ff,inset -1px 0 0 #a7c3ff}.bttn-slant.bttn-xs{padding:3px 8px;font-size:12px;font-family:inherit}.bttn-slant.bttn-sm{padding:4px 10px;font-size:16px;font-family:inherit}.bttn-slant.bttn-md{font-size:20px;font-family:inherit;padding:5px 12px}.bttn-slant.bttn-lg{padding:8px 15px;font-size:24px;font-family:inherit}.bttn-slant.bttn-default{color:#1d89ff}.bttn-slant.bttn-default:focus:before,.bttn-slant.bttn-default:hover:before{box-shadow:inset 0 -1px 0 #a7c3ff,inset 0 1px 0 #a7c3ff,inset -1px 0 0 #a7c3ff}.bttn-slant.bttn-default:before{background:#fff}.bttn-slant.bttn-default:after{background:#a7c3ff}.bttn-slant.bttn-primary{color:#fff}.bttn-slant.bttn-primary:focus:before,.bttn-slant.bttn-primary:hover:before{box-shadow:inset 0 -1px 0 #006de3,inset 0 1px 0 #006de3,inset -1px 0 0 #006de3}.bttn-slant.bttn-primary:before{background:#1d89ff}.bttn-slant.bttn-primary:after{background:#006de3}.bttn-slant.bttn-warning{color:#fff}.bttn-slant.bttn-warning:focus:before,.bttn-slant.bttn-warning:hover:before{box-shadow:inset 0 -1px 0 #f89001,inset 0 1px 0 #f89001,inset -1px 0 0 #f89001}.bttn-slant.bttn-warning:before{background:#feab3a}.bttn-slant.bttn-warning:after{background:#f89001}.bttn-slant.bttn-danger{color:#fff}.bttn-slant.bttn-danger:focus:before,.bttn-slant.bttn-danger:hover:before{box-shadow:inset 0 -1px 0 #ff1424,inset 0 1px 0 #ff1424,inset -1px 0 0 #ff1424}.bttn-slant.bttn-danger:before{background:#ff5964}.bttn-slant.bttn-danger:after{background:#ff1424}.bttn-slant.bttn-success{color:#fff}.bttn-slant.bttn-success:focus:before,.bttn-slant.bttn-success:hover:before{box-shadow:inset 0 -1px 0 #209271,inset 0 1px 0 #209271,inset -1px 0 0 #209271}.bttn-slant.bttn-success:before{background:#28b78d}.bttn-slant.bttn-success:after{background:#209271}.bttn-slant.bttn-royal{color:#fff}.bttn-slant.bttn-royal:focus:before,.bttn-slant.bttn-royal:hover:before{box-shadow:inset 0 -1px 0 #a20bdd,inset 0 1px 0 #a20bdd,inset -1px 0 0 #a20bdd}.bttn-slant.bttn-royal:before{background:#bd2df5}.bttn-slant.bttn-royal:after{background:#a20bdd}.bttn-block{display:block;width:100%}.bttn-no-outline,.bttn-no-outline:active,.bttn-no-outline:focus,.bttn-no-outline:hover{outline:none}
--------------------------------------------------------------------------------
/static/css/main.9c2864c1.css:
--------------------------------------------------------------------------------
1 | body{margin:0;padding:0;font-family:sans-serif;height:100vh}.App{-webkit-user-select:none;-moz-user-select:none;-ms-user-select:none;user-select:none;height:100%;--border-width:1px;display:-ms-flexbox;display:flex;background-color:#08131b}.App-header{width:200px;min-width:150px;color:#f5f5f5;top:0;font-size:30px;margin-left:10px;margin-right:10px;height:100vh}.Control-panel{padding-top:5px;padding-bottom:20px;margin-top:70px}#Song{-ms-flex:1 1 0%;flex:1 1 0%;font-size:0}/*!
2 | *
3 | * bttn.css - https://ganapativs.github.io/bttn.css
4 | * Version - 0.2.4
5 | * Demo: https://bttn.surge.sh
6 | *
7 | * Licensed under the MIT license - http://opensource.org/licenses/MIT
8 | *
9 | * Copyright (c) 2016 Ganapati V S (@ganapativs)
10 | *
11 | */.bttn-default{color:#fff}.bttn,.bttn-lg,.bttn-md,.bttn-primary,.bttn-sm,.bttn-xs{color:#1d89ff}.bttn-warning{color:#feab3a}.bttn-danger{color:#ff5964}.bttn-success{color:#28b78d}.bttn-royal{color:#bd2df5}.bttn,.bttn-lg,.bttn-md,.bttn-sm,.bttn-xs{margin:0;padding:0;border-width:0;border-color:transparent;background:transparent;font-weight:400;cursor:pointer;position:relative}.bttn-lg{padding:8px 15px;font-size:24px}.bttn-lg,.bttn-md{font-family:inherit}.bttn-md{font-size:20px;padding:5px 12px}.bttn-sm{padding:4px 10px;font-size:16px}.bttn-sm,.bttn-xs{font-family:inherit}.bttn-xs{padding:3px 8px;font-size:12px}.bttn-gradient,.bttn-simple{margin:0;padding:0;border-color:transparent;background:transparent;font-weight:400;cursor:pointer;position:relative;font-size:20px;font-family:inherit;padding:5px 12px;overflow:hidden;border-width:0;border-radius:4px;background:hsla(0,0%,100%,.4);color:#fff;-webkit-transition:all .3s cubic-bezier(.02,.01,.47,1);-o-transition:all .3s cubic-bezier(.02,.01,.47,1);transition:all .3s cubic-bezier(.02,.01,.47,1)}.bttn-gradient:focus,.bttn-gradient:hover,.bttn-simple:focus,.bttn-simple:hover{opacity:.75}.bttn-gradient.bttn-xs,.bttn-simple.bttn-xs{padding:3px 8px;font-size:12px;font-family:inherit}.bttn-gradient.bttn-sm,.bttn-simple.bttn-sm{padding:4px 10px;font-size:16px;font-family:inherit}.bttn-gradient.bttn-md,.bttn-simple.bttn-md{font-size:20px;font-family:inherit;padding:5px 12px}.bttn-gradient.bttn-lg,.bttn-simple.bttn-lg{padding:8px 15px;font-size:24px;font-family:inherit}.bttn-gradient.bttn-default,.bttn-simple.bttn-default{background:hsla(0,0%,100%,.4)}.bttn-gradient.bttn-primary,.bttn-simple.bttn-primary{background:#1d89ff}.bttn-gradient.bttn-warning,.bttn-simple.bttn-warning{background:#feab3a}.bttn-gradient.bttn-danger,.bttn-simple.bttn-danger{background:#ff5964}.bttn-gradient.bttn-success,.bttn-simple.bttn-success{background:#28b78d}.bttn-gradient.bttn-royal,.bttn-simple.bttn-royal{background:#bd2df5}.bttn-bordered{margin:0;padding:0;border-width:0;border-color:transparent;font-weight:400;cursor:pointer;position:relative;font-size:20px;font-family:inherit;padding:5px 12px;overflow:hidden;border:1px solid hsla(0,0%,100%,.4);border-radius:4px;background:transparent;color:#fff;-webkit-transition:all .3s cubic-bezier(.02,.01,.47,1);-o-transition:all .3s cubic-bezier(.02,.01,.47,1);transition:all .3s cubic-bezier(.02,.01,.47,1)}.bttn-bordered:focus,.bttn-bordered:hover{border-color:hsla(0,0%,100%,.7)}.bttn-bordered.bttn-xs{padding:3px 8px;font-size:12px;font-family:inherit}.bttn-bordered.bttn-sm{padding:4px 10px;font-size:16px;font-family:inherit}.bttn-bordered.bttn-md{font-size:20px;font-family:inherit;padding:5px 12px}.bttn-bordered.bttn-lg{padding:8px 15px;font-size:24px;font-family:inherit}.bttn-bordered.bttn-default{border-color:hsla(0,0%,100%,.4);color:#fff}.bttn-bordered.bttn-default:focus,.bttn-bordered.bttn-default:hover{border-color:hsla(0,0%,100%,.7)}.bttn-bordered.bttn-primary{border-color:rgba(29,137,255,.4);color:#1d89ff}.bttn-bordered.bttn-primary:focus,.bttn-bordered.bttn-primary:hover{border-color:rgba(29,137,255,.7)}.bttn-bordered.bttn-warning{border-color:rgba(254,171,58,.4);color:#feab3a}.bttn-bordered.bttn-warning:focus,.bttn-bordered.bttn-warning:hover{border-color:rgba(254,171,58,.7)}.bttn-bordered.bttn-danger{border-color:rgba(255,89,100,.4);color:#ff5964}.bttn-bordered.bttn-danger:focus,.bttn-bordered.bttn-danger:hover{border-color:rgba(255,89,100,.7)}.bttn-bordered.bttn-success{border-color:rgba(40,183,141,.4);color:#28b78d}.bttn-bordered.bttn-success:focus,.bttn-bordered.bttn-success:hover{border-color:rgba(40,183,141,.7)}.bttn-bordered.bttn-royal{border-color:rgba(189,45,245,.4);color:#bd2df5}.bttn-bordered.bttn-royal:focus,.bttn-bordered.bttn-royal:hover{border-color:rgba(189,45,245,.7)}.bttn-gradient{border-radius:100px;-webkit-box-shadow:0 1px 2px rgba(0,0,0,.25);box-shadow:0 1px 2px rgba(0,0,0,.25);text-shadow:0 1px 0 hsla(0,0%,100%,.25)}.bttn-gradient,.bttn-gradient.bttn-default{background-image:-webkit-gradient(linear,left top,left bottom,color-stop(0,#fff),color-stop(1,#d6e3ff));background-image:-webkit-gradient(linear,left top,left bottom,color-stop(0,#fff),to(#d6e3ff));background-image:-webkit-linear-gradient(top,#fff,#d6e3ff);background-image:-o-linear-gradient(top,#fff 0,#d6e3ff);background-image:linear-gradient(180deg,#fff,#d6e3ff);background-image:-webkit-linear-gradient(93deg,#d6e3ff,#fff);color:#1d89ff}.bttn-gradient.bttn-primary{background-image:-webkit-gradient(linear,left top,left bottom,color-stop(0,#00bbd4),color-stop(1,#3f51b5));background-image:-webkit-gradient(linear,left top,left bottom,color-stop(0,#00bbd4),to(#3f51b5));background-image:-webkit-linear-gradient(top,#00bbd4,#3f51b5);background-image:-o-linear-gradient(top,#00bbd4 0,#3f51b5);background-image:linear-gradient(180deg,#00bbd4,#3f51b5);background-image:-webkit-linear-gradient(93deg,#3f51b5,#00bbd4);color:#fff}.bttn-gradient.bttn-warning{background-image:-webkit-gradient(linear,left top,left bottom,color-stop(0,#feab3a),color-stop(1,#f35626));background-image:-webkit-gradient(linear,left top,left bottom,color-stop(0,#feab3a),to(#f35626));background-image:-webkit-linear-gradient(top,#feab3a,#f35626);background-image:-o-linear-gradient(top,#feab3a 0,#f35626);background-image:linear-gradient(180deg,#feab3a,#f35626);background-image:-webkit-linear-gradient(93deg,#f35626,#feab3a);color:#fff}.bttn-gradient.bttn-danger{background-image:-webkit-gradient(linear,left top,left bottom,color-stop(0,#ff97c2),color-stop(1,#e91e63));background-image:-webkit-gradient(linear,left top,left bottom,color-stop(0,#ff97c2),to(#e91e63));background-image:-webkit-linear-gradient(top,#ff97c2,#e91e63);background-image:-o-linear-gradient(top,#ff97c2 0,#e91e63);background-image:linear-gradient(180deg,#ff97c2,#e91e63);background-image:-webkit-linear-gradient(93deg,#e91e63,#ff97c2);color:#fff}.bttn-gradient.bttn-success{background-image:-webkit-gradient(linear,left top,left bottom,color-stop(0,#9ccc65),color-stop(1,#009688));background-image:-webkit-gradient(linear,left top,left bottom,color-stop(0,#9ccc65),to(#009688));background-image:-webkit-linear-gradient(top,#9ccc65,#009688);background-image:-o-linear-gradient(top,#9ccc65 0,#009688);background-image:linear-gradient(180deg,#9ccc65,#009688);background-image:-webkit-linear-gradient(93deg,#009688,#9ccc65);color:#fff}.bttn-gradient.bttn-royal{background-image:-webkit-gradient(linear,left top,left bottom,color-stop(0,#9c27b0),color-stop(1,#512da8));background-image:-webkit-gradient(linear,left top,left bottom,color-stop(0,#9c27b0),to(#512da8));background-image:-webkit-linear-gradient(top,#9c27b0,#512da8);background-image:-o-linear-gradient(top,#9c27b0 0,#512da8);background-image:linear-gradient(180deg,#9c27b0,#512da8);background-image:-webkit-linear-gradient(93deg,#512da8,#9c27b0);color:#fff}.bttn-minimal{margin:0;padding:0;border-color:transparent;font-weight:400;cursor:pointer;position:relative;font-size:20px;font-family:inherit;padding:5px 12px;overflow:hidden;border-width:0;border-radius:4px;background:transparent;color:#fff;-webkit-transition:all .5s cubic-bezier(.02,.01,.47,1);-o-transition:all .5s cubic-bezier(.02,.01,.47,1);transition:all .5s cubic-bezier(.02,.01,.47,1)}.bttn-minimal:after,.bttn-minimal:before{position:absolute;bottom:0;left:10px;width:calc(100% - 20px);height:1px;background:currentColor;content:"";opacity:.65;-webkit-transition:opacity .5s cubic-bezier(.02,.01,.47,1),-webkit-transform .5s cubic-bezier(.02,.01,.47,1);transition:opacity .5s cubic-bezier(.02,.01,.47,1),-webkit-transform .5s cubic-bezier(.02,.01,.47,1);-o-transition:transform .5s cubic-bezier(.02,.01,.47,1),opacity .5s cubic-bezier(.02,.01,.47,1);transition:transform .5s cubic-bezier(.02,.01,.47,1),opacity .5s cubic-bezier(.02,.01,.47,1);transition:transform .5s cubic-bezier(.02,.01,.47,1),opacity .5s cubic-bezier(.02,.01,.47,1),-webkit-transform .5s cubic-bezier(.02,.01,.47,1)}.bttn-minimal:focus,.bttn-minimal:hover{opacity:.9}.bttn-minimal:focus:after,.bttn-minimal:hover:after{opacity:1;-webkit-transform:translateX(-10px) rotate(.001deg);-ms-transform:translateX(-10px) rotate(.001deg);transform:translateX(-10px) rotate(.001deg)}.bttn-minimal:focus:before,.bttn-minimal:hover:before{opacity:1;-webkit-transform:translateX(10px) rotate(.001deg);-ms-transform:translateX(10px) rotate(.001deg);transform:translateX(10px) rotate(.001deg)}.bttn-minimal.bttn-xs{padding:3px 8px;font-size:12px;font-family:inherit}.bttn-minimal.bttn-sm{padding:4px 10px;font-size:16px;font-family:inherit}.bttn-minimal.bttn-md{font-size:20px;font-family:inherit;padding:5px 12px}.bttn-minimal.bttn-lg{padding:8px 15px;font-size:24px;font-family:inherit}.bttn-minimal.bttn-default{color:#fff}.bttn-minimal.bttn-primary{color:#1d89ff}.bttn-minimal.bttn-warning{color:#feab3a}.bttn-minimal.bttn-danger{color:#ff5964}.bttn-minimal.bttn-success{color:#28b78d}.bttn-minimal.bttn-royal{color:#bd2df5}.bttn-stretch{margin:0;padding:0;border-color:transparent;font-weight:400;cursor:pointer;position:relative;font-size:20px;font-family:inherit;padding:5px 12px;overflow:hidden;border-width:0;border-radius:0;background:transparent;color:#fff;letter-spacing:0}.bttn-stretch,.bttn-stretch:after,.bttn-stretch:before{-webkit-transition:all .2s cubic-bezier(.02,.01,.47,1);-o-transition:all .2s cubic-bezier(.02,.01,.47,1);transition:all .2s cubic-bezier(.02,.01,.47,1)}.bttn-stretch:after,.bttn-stretch:before{position:absolute;left:0;width:100%;height:1px;background:currentColor;content:"";opacity:.65;-webkit-transform:scaleX(0);-ms-transform:scaleX(0);transform:scaleX(0)}.bttn-stretch:after{top:0}.bttn-stretch:before{bottom:0}.bttn-stretch:focus,.bttn-stretch:hover{letter-spacing:2px;opacity:.9}.bttn-stretch:focus,.bttn-stretch:focus:after,.bttn-stretch:focus:before,.bttn-stretch:hover,.bttn-stretch:hover:after,.bttn-stretch:hover:before{-webkit-transition:all .3s cubic-bezier(.02,.01,.47,1);-o-transition:all .3s cubic-bezier(.02,.01,.47,1);transition:all .3s cubic-bezier(.02,.01,.47,1)}.bttn-stretch:focus:after,.bttn-stretch:focus:before,.bttn-stretch:hover:after,.bttn-stretch:hover:before{opacity:1;-webkit-transform:scaleX(1);-ms-transform:scaleX(1);transform:scaleX(1)}.bttn-stretch.bttn-xs{padding:3px 8px;font-size:12px;font-family:inherit}.bttn-stretch.bttn-sm{padding:4px 10px;font-size:16px;font-family:inherit}.bttn-stretch.bttn-md{font-size:20px;font-family:inherit;padding:5px 12px}.bttn-stretch.bttn-lg{padding:8px 15px;font-size:24px;font-family:inherit}.bttn-stretch.bttn-default{color:#fff}.bttn-stretch.bttn-primary{color:#1d89ff}.bttn-stretch.bttn-warning{color:#feab3a}.bttn-stretch.bttn-danger{color:#ff5964}.bttn-stretch.bttn-success{color:#28b78d}.bttn-stretch.bttn-royal{color:#bd2df5}.bttn-jelly{margin:0;padding:0;border-width:0;border-color:transparent;background:transparent;font-weight:400;cursor:pointer;position:relative;font-size:20px;font-family:inherit;padding:5px 12px;overflow:hidden;background:#fff;color:#1d89ff}.bttn-jelly,.bttn-jelly:before{border-radius:50px;-webkit-transition:all .2s cubic-bezier(.02,.01,.47,1);-o-transition:all .2s cubic-bezier(.02,.01,.47,1);transition:all .2s cubic-bezier(.02,.01,.47,1)}.bttn-jelly:before{position:absolute;top:0;left:0;width:100%;height:100%;background:currentColor;content:"";z-index:-1;opacity:0;-webkit-transform:scale(.2);-ms-transform:scale(.2);transform:scale(.2)}.bttn-jelly:focus,.bttn-jelly:hover{-webkit-box-shadow:0 1px 8px rgba(58,51,53,.4);box-shadow:0 1px 8px rgba(58,51,53,.4);-webkit-transform:scale(1.1);-ms-transform:scale(1.1);transform:scale(1.1)}.bttn-jelly:focus,.bttn-jelly:focus:before,.bttn-jelly:hover,.bttn-jelly:hover:before{-webkit-transition:all .3s cubic-bezier(.02,.01,.47,1);-o-transition:all .3s cubic-bezier(.02,.01,.47,1);transition:all .3s cubic-bezier(.02,.01,.47,1)}.bttn-jelly:focus:before,.bttn-jelly:hover:before{opacity:.15;-webkit-transform:scale(1);-ms-transform:scale(1);transform:scale(1)}.bttn-jelly.bttn-xs{padding:3px 8px;font-size:12px;font-family:inherit}.bttn-jelly.bttn-xs:focus,.bttn-jelly.bttn-xs:hover{-webkit-box-shadow:0 1px 4px rgba(58,51,53,.4);box-shadow:0 1px 4px rgba(58,51,53,.4)}.bttn-jelly.bttn-sm{padding:4px 10px;font-size:16px;font-family:inherit}.bttn-jelly.bttn-sm:focus,.bttn-jelly.bttn-sm:hover{-webkit-box-shadow:0 1px 6px rgba(58,51,53,.4);box-shadow:0 1px 6px rgba(58,51,53,.4)}.bttn-jelly.bttn-md{font-size:20px;font-family:inherit;padding:5px 12px}.bttn-jelly.bttn-md:focus,.bttn-jelly.bttn-md:hover{-webkit-box-shadow:0 1px 8px rgba(58,51,53,.4);box-shadow:0 1px 8px rgba(58,51,53,.4)}.bttn-jelly.bttn-lg{padding:8px 15px;font-size:24px;font-family:inherit}.bttn-jelly.bttn-lg:focus,.bttn-jelly.bttn-lg:hover{-webkit-box-shadow:0 1px 10px rgba(58,51,53,.4);box-shadow:0 1px 10px rgba(58,51,53,.4)}.bttn-jelly.bttn-default{background:#fff;color:#1d89ff}.bttn-jelly.bttn-primary{background:#1d89ff;color:#fff}.bttn-jelly.bttn-warning{background:#feab3a;color:#fff}.bttn-jelly.bttn-danger{background:#ff5964;color:#fff}.bttn-jelly.bttn-success{background:#28b78d;color:#fff}.bttn-jelly.bttn-royal{background:#bd2df5;color:#fff}.bttn-fill{margin:0;padding:0;border-width:0;border-color:transparent;background:transparent;font-weight:400;cursor:pointer;position:relative;font-size:20px;font-family:inherit;padding:5px 12px;z-index:0;border:none;background:#fff;color:#1d89ff;-webkit-transition:all .3s cubic-bezier(.02,.01,.47,1);-o-transition:all .3s cubic-bezier(.02,.01,.47,1);transition:all .3s cubic-bezier(.02,.01,.47,1)}.bttn-fill:before{position:absolute;bottom:0;left:0;width:100%;height:100%;background:#1d89ff;content:"";opacity:0;-webkit-transition:opacity .15s ease-out,-webkit-transform .15s ease-out;transition:opacity .15s ease-out,-webkit-transform .15s ease-out;-o-transition:transform .15s ease-out,opacity .15s ease-out;transition:transform .15s ease-out,opacity .15s ease-out;transition:transform .15s ease-out,opacity .15s ease-out,-webkit-transform .15s ease-out;z-index:-1;-webkit-transform:scaleX(0);-ms-transform:scaleX(0);transform:scaleX(0)}.bttn-fill:focus,.bttn-fill:hover{-webkit-box-shadow:0 1px 8px rgba(58,51,53,.3);box-shadow:0 1px 8px rgba(58,51,53,.3);color:#fff;-webkit-transition:all .5s cubic-bezier(.02,.01,.47,1);-o-transition:all .5s cubic-bezier(.02,.01,.47,1);transition:all .5s cubic-bezier(.02,.01,.47,1)}.bttn-fill:focus:before,.bttn-fill:hover:before{opacity:1;-webkit-transition:opacity .2s ease-in,-webkit-transform .2s ease-in;transition:opacity .2s ease-in,-webkit-transform .2s ease-in;-o-transition:transform .2s ease-in,opacity .2s ease-in;transition:transform .2s ease-in,opacity .2s ease-in;transition:transform .2s ease-in,opacity .2s ease-in,-webkit-transform .2s ease-in;-webkit-transform:scaleX(1);-ms-transform:scaleX(1);transform:scaleX(1)}.bttn-fill.bttn-xs{padding:3px 8px;font-size:12px;font-family:inherit}.bttn-fill.bttn-xs:focus,.bttn-fill.bttn-xs:hover{-webkit-box-shadow:0 1px 4px rgba(58,51,53,.3);box-shadow:0 1px 4px rgba(58,51,53,.3)}.bttn-fill.bttn-sm{padding:4px 10px;font-size:16px;font-family:inherit}.bttn-fill.bttn-sm:focus,.bttn-fill.bttn-sm:hover{-webkit-box-shadow:0 1px 6px rgba(58,51,53,.3);box-shadow:0 1px 6px rgba(58,51,53,.3)}.bttn-fill.bttn-md{font-size:20px;font-family:inherit;padding:5px 12px}.bttn-fill.bttn-md:focus,.bttn-fill.bttn-md:hover{-webkit-box-shadow:0 1px 8px rgba(58,51,53,.3);box-shadow:0 1px 8px rgba(58,51,53,.3)}.bttn-fill.bttn-lg{padding:8px 15px;font-size:24px;font-family:inherit}.bttn-fill.bttn-lg:focus,.bttn-fill.bttn-lg:hover{-webkit-box-shadow:0 1px 10px rgba(58,51,53,.3);box-shadow:0 1px 10px rgba(58,51,53,.3)}.bttn-fill.bttn-default{background:#fff;color:#1d89ff}.bttn-fill.bttn-default:focus,.bttn-fill.bttn-default:hover{color:#fff}.bttn-fill.bttn-default:before{background:#1d89ff}.bttn-fill.bttn-primary{background:#1d89ff;color:#fff}.bttn-fill.bttn-primary:focus,.bttn-fill.bttn-primary:hover{color:#1d89ff}.bttn-fill.bttn-primary:before{background:#fff}.bttn-fill.bttn-warning{background:#feab3a;color:#fff}.bttn-fill.bttn-warning:focus,.bttn-fill.bttn-warning:hover{color:#feab3a}.bttn-fill.bttn-warning:before{background:#fff}.bttn-fill.bttn-danger{background:#ff5964;color:#fff}.bttn-fill.bttn-danger:focus,.bttn-fill.bttn-danger:hover{color:#ff5964}.bttn-fill.bttn-danger:before{background:#fff}.bttn-fill.bttn-success{background:#28b78d;color:#fff}.bttn-fill.bttn-success:focus,.bttn-fill.bttn-success:hover{color:#28b78d}.bttn-fill.bttn-success:before{background:#fff}.bttn-fill.bttn-royal{background:#bd2df5;color:#fff}.bttn-fill.bttn-royal:focus,.bttn-fill.bttn-royal:hover{color:#bd2df5}.bttn-fill.bttn-royal:before{background:#fff}.bttn-material-circle{margin:0;padding:0;border-color:transparent;background:transparent;font-weight:400;cursor:pointer;position:relative;font-size:20px;font-family:inherit;padding:5px 12px;overflow:hidden;border-width:0;border-radius:50%;background:#fff;-webkit-box-shadow:0 2px 5px 0 rgba(0,0,0,.18),0 1px 5px 0 rgba(0,0,0,.15);box-shadow:0 2px 5px 0 rgba(0,0,0,.18),0 1px 5px 0 rgba(0,0,0,.15);color:#1d89ff;-webkit-transition:all .25s cubic-bezier(.02,.01,.47,1);-o-transition:all .25s cubic-bezier(.02,.01,.47,1);transition:all .25s cubic-bezier(.02,.01,.47,1);-webkit-transform:translateZ(0);transform:translateZ(0)}.bttn-material-circle:focus,.bttn-material-circle:hover{-webkit-box-shadow:0 5px 11px 0 rgba(0,0,0,.18),0 4px 15px 0 rgba(0,0,0,.15);box-shadow:0 5px 11px 0 rgba(0,0,0,.18),0 4px 15px 0 rgba(0,0,0,.15);-webkit-transition:box-shadow .4s ease-out;-webkit-transition:-webkit-box-shadow .4s ease-out;transition:-webkit-box-shadow .4s ease-out;-o-transition:box-shadow .4s ease-out;transition:box-shadow .4s ease-out;transition:box-shadow .4s ease-out,-webkit-box-shadow .4s ease-out}.bttn-material-circle.bttn-xs{padding:3px 8px;font-size:12px;font-family:inherit;width:28px;height:28px;line-height:24px}.bttn-material-circle.bttn-sm{padding:4px 10px;font-size:16px;font-family:inherit;width:36px;height:36px;line-height:30px}.bttn-material-circle.bttn-md{font-size:20px;font-family:inherit;padding:5px 12px;width:44px;height:44px;line-height:38px}.bttn-material-circle.bttn-lg{padding:8px 15px;font-size:24px;font-family:inherit;width:54px;height:54px;line-height:44px}.bttn-material-circle.bttn-default{background:#fff;color:#1d89ff}.bttn-material-circle.bttn-primary{background:#1d89ff;color:#fff}.bttn-material-circle.bttn-warning{background:#feab3a;color:#fff}.bttn-material-circle.bttn-danger{background:#ff5964;color:#fff}.bttn-material-circle.bttn-success{background:#28b78d;color:#fff}.bttn-material-circle.bttn-royal{background:#bd2df5;color:#fff}.bttn-material-flat{margin:0;padding:0;border-color:transparent;background:transparent;font-weight:400;cursor:pointer;position:relative;font-size:20px;font-family:inherit;padding:5px 12px;overflow:hidden;border-width:0;border-radius:2px;background:#fff;-webkit-box-shadow:0 2px 5px 0 rgba(0,0,0,.18),0 1px 5px 0 rgba(0,0,0,.15);box-shadow:0 2px 5px 0 rgba(0,0,0,.18),0 1px 5px 0 rgba(0,0,0,.15);color:#1d89ff;text-transform:uppercase;-webkit-transition:all .25s cubic-bezier(.02,.01,.47,1);-o-transition:all .25s cubic-bezier(.02,.01,.47,1);transition:all .25s cubic-bezier(.02,.01,.47,1);-webkit-transform:translateZ(0);transform:translateZ(0)}.bttn-material-flat:focus,.bttn-material-flat:hover{-webkit-box-shadow:0 5px 11px 0 rgba(0,0,0,.18),0 4px 15px 0 rgba(0,0,0,.15);box-shadow:0 5px 11px 0 rgba(0,0,0,.18),0 4px 15px 0 rgba(0,0,0,.15);-webkit-transition:box-shadow .4s ease-out;-webkit-transition:-webkit-box-shadow .4s ease-out;transition:-webkit-box-shadow .4s ease-out;-o-transition:box-shadow .4s ease-out;transition:box-shadow .4s ease-out;transition:box-shadow .4s ease-out,-webkit-box-shadow .4s ease-out}.bttn-material-flat.bttn-xs{padding:3px 8px;font-size:12px;font-family:inherit}.bttn-material-flat.bttn-sm{padding:4px 10px;font-size:16px;font-family:inherit}.bttn-material-flat.bttn-md{font-size:20px;font-family:inherit;padding:5px 12px}.bttn-material-flat.bttn-lg{padding:8px 15px;font-size:24px;font-family:inherit}.bttn-material-flat.bttn-default{background:#fff;color:#1d89ff}.bttn-material-flat.bttn-primary{background:#1d89ff;color:#fff}.bttn-material-flat.bttn-warning{background:#feab3a;color:#fff}.bttn-material-flat.bttn-danger{background:#ff5964;color:#fff}.bttn-material-flat.bttn-success{background:#28b78d;color:#fff}.bttn-material-flat.bttn-royal{background:#bd2df5;color:#fff}.bttn-pill{margin:0;padding:0;border-width:0;border-color:transparent;background:transparent;font-weight:400;cursor:pointer;position:relative;font-size:20px;font-family:inherit;padding:5px 12px;z-index:0;overflow:hidden;border:none;border-radius:100px;background:#fff;color:#1d89ff;-webkit-transition:all .3s cubic-bezier(.02,.01,.47,1);-o-transition:all .3s cubic-bezier(.02,.01,.47,1);transition:all .3s cubic-bezier(.02,.01,.47,1)}.bttn-pill:after,.bttn-pill:before{position:absolute;right:0;bottom:0;width:100px;height:100px;border-radius:50%;background:#1d89ff;content:"";opacity:0;-webkit-transition:opacity .15s cubic-bezier(.02,.01,.47,1),-webkit-transform .15s cubic-bezier(.02,.01,.47,1);transition:opacity .15s cubic-bezier(.02,.01,.47,1),-webkit-transform .15s cubic-bezier(.02,.01,.47,1);-o-transition:transform .15s cubic-bezier(.02,.01,.47,1),opacity .15s cubic-bezier(.02,.01,.47,1);transition:transform .15s cubic-bezier(.02,.01,.47,1),opacity .15s cubic-bezier(.02,.01,.47,1);transition:transform .15s cubic-bezier(.02,.01,.47,1),opacity .15s cubic-bezier(.02,.01,.47,1),-webkit-transform .15s cubic-bezier(.02,.01,.47,1);z-index:-1;-webkit-transform:translate(100%,-25%) translateZ(0);transform:translate(100%,-25%) translateZ(0)}.bttn-pill:focus,.bttn-pill:hover{-webkit-box-shadow:0 1px 8px rgba(58,51,53,.3);box-shadow:0 1px 8px rgba(58,51,53,.3);color:#fff;-webkit-transition:all .5s cubic-bezier(.02,.01,.47,1);-o-transition:all .5s cubic-bezier(.02,.01,.47,1);transition:all .5s cubic-bezier(.02,.01,.47,1);-webkit-transform:scale(1.1) translateZ(0);transform:scale(1.1) translateZ(0)}.bttn-pill:focus:before,.bttn-pill:hover:before{opacity:.15;-webkit-transition:opacity .2s cubic-bezier(.02,.01,.47,1),-webkit-transform .2s cubic-bezier(.02,.01,.47,1);transition:opacity .2s cubic-bezier(.02,.01,.47,1),-webkit-transform .2s cubic-bezier(.02,.01,.47,1);-o-transition:transform .2s cubic-bezier(.02,.01,.47,1),opacity .2s cubic-bezier(.02,.01,.47,1);transition:transform .2s cubic-bezier(.02,.01,.47,1),opacity .2s cubic-bezier(.02,.01,.47,1);transition:transform .2s cubic-bezier(.02,.01,.47,1),opacity .2s cubic-bezier(.02,.01,.47,1),-webkit-transform .2s cubic-bezier(.02,.01,.47,1);-webkit-transform:translate3d(50%,0,0) scale(.9);transform:translate3d(50%,0,0) scale(.9)}.bttn-pill:focus:after,.bttn-pill:hover:after{opacity:.25;-webkit-transition:opacity .2s cubic-bezier(.02,.01,.47,1) .05s,-webkit-transform .2s cubic-bezier(.02,.01,.47,1) .05s;transition:opacity .2s cubic-bezier(.02,.01,.47,1) .05s,-webkit-transform .2s cubic-bezier(.02,.01,.47,1) .05s;-o-transition:transform .2s cubic-bezier(.02,.01,.47,1) .05s,opacity .2s cubic-bezier(.02,.01,.47,1) .05s;transition:transform .2s cubic-bezier(.02,.01,.47,1) .05s,opacity .2s cubic-bezier(.02,.01,.47,1) .05s;transition:transform .2s cubic-bezier(.02,.01,.47,1) .05s,opacity .2s cubic-bezier(.02,.01,.47,1) .05s,-webkit-transform .2s cubic-bezier(.02,.01,.47,1) .05s;-webkit-transform:translate(50%) scale(1.1);-ms-transform:translate(50%) scale(1.1);transform:translate(50%) scale(1.1)}.bttn-pill.bttn-xs{padding:3px 8px;font-size:12px;font-family:inherit}.bttn-pill.bttn-xs:focus,.bttn-pill.bttn-xs:hover{-webkit-box-shadow:0 1px 4px rgba(58,51,53,.3);box-shadow:0 1px 4px rgba(58,51,53,.3)}.bttn-pill.bttn-sm{padding:4px 10px;font-size:16px;font-family:inherit}.bttn-pill.bttn-sm:focus,.bttn-pill.bttn-sm:hover{-webkit-box-shadow:0 1px 6px rgba(58,51,53,.3);box-shadow:0 1px 6px rgba(58,51,53,.3)}.bttn-pill.bttn-md{font-size:20px;font-family:inherit;padding:5px 12px}.bttn-pill.bttn-md:focus,.bttn-pill.bttn-md:hover{-webkit-box-shadow:0 1px 8px rgba(58,51,53,.3);box-shadow:0 1px 8px rgba(58,51,53,.3)}.bttn-pill.bttn-lg{padding:8px 15px;font-size:24px;font-family:inherit}.bttn-pill.bttn-lg:focus,.bttn-pill.bttn-lg:hover{-webkit-box-shadow:0 1px 10px rgba(58,51,53,.3);box-shadow:0 1px 10px rgba(58,51,53,.3)}.bttn-pill.bttn-default{background:#fff;color:#1d89ff}.bttn-pill.bttn-default:focus,.bttn-pill.bttn-default:hover{color:#1d89ff}.bttn-pill.bttn-default:after,.bttn-pill.bttn-default:before{background:#1d89ff}.bttn-pill.bttn-primary{background:#1d89ff;color:#fff}.bttn-pill.bttn-primary:focus,.bttn-pill.bttn-primary:hover{color:#fff}.bttn-pill.bttn-primary:after,.bttn-pill.bttn-primary:before{background:#fff}.bttn-pill.bttn-warning{background:#feab3a;color:#fff}.bttn-pill.bttn-warning:focus,.bttn-pill.bttn-warning:hover{color:#fff}.bttn-pill.bttn-warning:after,.bttn-pill.bttn-warning:before{background:#fff}.bttn-pill.bttn-danger{background:#ff5964;color:#fff}.bttn-pill.bttn-danger:focus,.bttn-pill.bttn-danger:hover{color:#fff}.bttn-pill.bttn-danger:after,.bttn-pill.bttn-danger:before{background:#fff}.bttn-pill.bttn-success{background:#28b78d;color:#fff}.bttn-pill.bttn-success:focus,.bttn-pill.bttn-success:hover{color:#fff}.bttn-pill.bttn-success:after,.bttn-pill.bttn-success:before{background:#fff}.bttn-pill.bttn-royal{background:#bd2df5;color:#fff}.bttn-pill.bttn-royal:focus,.bttn-pill.bttn-royal:hover{color:#fff}.bttn-pill.bttn-royal:after,.bttn-pill.bttn-royal:before{background:#fff}.bttn-float{margin:0;padding:0;border-width:0;border-color:transparent;background:transparent;font-weight:400;cursor:pointer;position:relative;font-size:20px;font-family:inherit;padding:5px 12px;overflow:hidden;border:1px dotted #fff;border-radius:4px;background:hsla(0,0%,100%,.4);color:#fff;-webkit-transition:opacity .3s cubic-bezier(.02,.01,.47,1),box-shadow .2s cubic-bezier(.02,.01,.47,1),-webkit-transform .3s cubic-bezier(.02,.01,.47,1);transition:opacity .3s cubic-bezier(.02,.01,.47,1),box-shadow .2s cubic-bezier(.02,.01,.47,1),-webkit-transform .3s cubic-bezier(.02,.01,.47,1);-webkit-transition:opacity .3s cubic-bezier(.02,.01,.47,1),-webkit-transform .3s cubic-bezier(.02,.01,.47,1),-webkit-box-shadow .2s cubic-bezier(.02,.01,.47,1);transition:opacity .3s cubic-bezier(.02,.01,.47,1),-webkit-transform .3s cubic-bezier(.02,.01,.47,1),-webkit-box-shadow .2s cubic-bezier(.02,.01,.47,1);-o-transition:transform .3s cubic-bezier(.02,.01,.47,1),opacity .3s cubic-bezier(.02,.01,.47,1),box-shadow .2s cubic-bezier(.02,.01,.47,1);transition:transform .3s cubic-bezier(.02,.01,.47,1),opacity .3s cubic-bezier(.02,.01,.47,1),box-shadow .2s cubic-bezier(.02,.01,.47,1);transition:transform .3s cubic-bezier(.02,.01,.47,1),opacity .3s cubic-bezier(.02,.01,.47,1),box-shadow .2s cubic-bezier(.02,.01,.47,1),-webkit-transform .3s cubic-bezier(.02,.01,.47,1),-webkit-box-shadow .2s cubic-bezier(.02,.01,.47,1);transition:transform .3s cubic-bezier(.02,.01,.47,1),opacity .3s cubic-bezier(.02,.01,.47,1),box-shadow .2s cubic-bezier(.02,.01,.47,1),-webkit-transform .3s cubic-bezier(.02,.01,.47,1)}.bttn-float:focus,.bttn-float:hover{-webkit-box-shadow:0 30px 30px rgba(0,0,0,.16);box-shadow:0 30px 30px rgba(0,0,0,.16);opacity:.85;-webkit-transition:opacity .2s cubic-bezier(.02,.01,.47,1),box-shadow .4s cubic-bezier(.02,.01,.47,1),-webkit-transform .2s cubic-bezier(.02,.01,.47,1);transition:opacity .2s cubic-bezier(.02,.01,.47,1),box-shadow .4s cubic-bezier(.02,.01,.47,1),-webkit-transform .2s cubic-bezier(.02,.01,.47,1);-webkit-transition:opacity .2s cubic-bezier(.02,.01,.47,1),-webkit-transform .2s cubic-bezier(.02,.01,.47,1),-webkit-box-shadow .4s cubic-bezier(.02,.01,.47,1);transition:opacity .2s cubic-bezier(.02,.01,.47,1),-webkit-transform .2s cubic-bezier(.02,.01,.47,1),-webkit-box-shadow .4s cubic-bezier(.02,.01,.47,1);-o-transition:transform .2s cubic-bezier(.02,.01,.47,1),opacity .2s cubic-bezier(.02,.01,.47,1),box-shadow .4s cubic-bezier(.02,.01,.47,1);transition:transform .2s cubic-bezier(.02,.01,.47,1),opacity .2s cubic-bezier(.02,.01,.47,1),box-shadow .4s cubic-bezier(.02,.01,.47,1);transition:transform .2s cubic-bezier(.02,.01,.47,1),opacity .2s cubic-bezier(.02,.01,.47,1),box-shadow .4s cubic-bezier(.02,.01,.47,1),-webkit-transform .2s cubic-bezier(.02,.01,.47,1),-webkit-box-shadow .4s cubic-bezier(.02,.01,.47,1);transition:transform .2s cubic-bezier(.02,.01,.47,1),opacity .2s cubic-bezier(.02,.01,.47,1),box-shadow .4s cubic-bezier(.02,.01,.47,1),-webkit-transform .2s cubic-bezier(.02,.01,.47,1)}.bttn-float.bttn-xs{padding:3px 8px;font-size:12px;font-family:inherit}.bttn-float.bttn-xs:focus,.bttn-float.bttn-xs:hover{-webkit-transform:translateY(-6px);-ms-transform:translateY(-6px);transform:translateY(-6px)}.bttn-float.bttn-sm{padding:4px 10px;font-size:16px;font-family:inherit}.bttn-float.bttn-sm:focus,.bttn-float.bttn-sm:hover{-webkit-transform:translateY(-8px);-ms-transform:translateY(-8px);transform:translateY(-8px)}.bttn-float.bttn-md{font-size:20px;font-family:inherit;padding:5px 12px}.bttn-float.bttn-md:focus,.bttn-float.bttn-md:hover{-webkit-transform:translateY(-10px);-ms-transform:translateY(-10px);transform:translateY(-10px)}.bttn-float.bttn-lg{padding:8px 15px;font-size:24px;font-family:inherit}.bttn-float.bttn-lg:focus,.bttn-float.bttn-lg:hover{-webkit-transform:translateY(-12px);-ms-transform:translateY(-12px);transform:translateY(-12px)}.bttn-float.bttn-default{border-color:#fff;background:hsla(0,0%,100%,.4);color:#fff}.bttn-float.bttn-primary{border-color:#1d89ff;background:rgba(29,137,255,.4);color:#1d89ff}.bttn-float.bttn-warning{border-color:#feab3a;background:rgba(254,171,58,.4);color:#feab3a}.bttn-float.bttn-danger{border-color:#ff5964;background:rgba(255,89,100,.4);color:#ff5964}.bttn-float.bttn-success{border-color:#28b78d;background:rgba(40,183,141,.4);color:#28b78d}.bttn-float.bttn-royal{border-color:#bd2df5;background:rgba(189,45,245,.4);color:#bd2df5}.bttn-unite{margin:0;padding:0;border-width:0;border-color:transparent;background:transparent;font-weight:400;cursor:pointer;position:relative;font-size:20px;font-family:inherit;padding:5px 12px;z-index:0;overflow:hidden;border:1px solid #1d89ff;border-radius:100px;background:#fff;color:#1d89ff;-webkit-transition:color .3s cubic-bezier(.02,.01,.47,1),border-color .3s cubic-bezier(.02,.01,.47,1);-o-transition:color .3s cubic-bezier(.02,.01,.47,1),border-color .3s cubic-bezier(.02,.01,.47,1);transition:color .3s cubic-bezier(.02,.01,.47,1),border-color .3s cubic-bezier(.02,.01,.47,1)}.bttn-unite:before{background:#d6e3ff;-webkit-transform:translate3d(-110%,-10%,0) skewX(-20deg);transform:translate3d(-110%,-10%,0) skewX(-20deg)}.bttn-unite:after,.bttn-unite:before{position:absolute;top:0;left:0;width:100%;height:120%;content:"";opacity:0;z-index:-1;-webkit-transition:opacity .15s cubic-bezier(.02,.01,.47,1),-webkit-transform .15s cubic-bezier(.02,.01,.47,1);transition:opacity .15s cubic-bezier(.02,.01,.47,1),-webkit-transform .15s cubic-bezier(.02,.01,.47,1);-o-transition:transform .15s cubic-bezier(.02,.01,.47,1),opacity .15s cubic-bezier(.02,.01,.47,1);transition:transform .15s cubic-bezier(.02,.01,.47,1),opacity .15s cubic-bezier(.02,.01,.47,1);transition:transform .15s cubic-bezier(.02,.01,.47,1),opacity .15s cubic-bezier(.02,.01,.47,1),-webkit-transform .15s cubic-bezier(.02,.01,.47,1)}.bttn-unite:after{background:rgba(214,227,255,.7);-webkit-transform:translate3d(110%,-10%,0) skewX(-20deg);transform:translate3d(110%,-10%,0) skewX(-20deg)}.bttn-unite:focus,.bttn-unite:hover{-webkit-box-shadow:0 1px 8px rgba(58,51,53,.3);box-shadow:0 1px 8px rgba(58,51,53,.3);color:#1d89ff;-webkit-transition:all .5s cubic-bezier(.02,.01,.47,1);-o-transition:all .5s cubic-bezier(.02,.01,.47,1);transition:all .5s cubic-bezier(.02,.01,.47,1)}.bttn-unite:focus:before,.bttn-unite:hover:before{-webkit-transform:translate3d(-50%,-10%,0) skewX(-20deg);transform:translate3d(-50%,-10%,0) skewX(-20deg)}.bttn-unite:focus:after,.bttn-unite:focus:before,.bttn-unite:hover:after,.bttn-unite:hover:before{opacity:1;-webkit-transition:opacity .25s cubic-bezier(.02,.01,.47,1),-webkit-transform .25s cubic-bezier(.02,.01,.47,1);transition:opacity .25s cubic-bezier(.02,.01,.47,1),-webkit-transform .25s cubic-bezier(.02,.01,.47,1);-o-transition:transform .25s cubic-bezier(.02,.01,.47,1),opacity .25s cubic-bezier(.02,.01,.47,1);transition:transform .25s cubic-bezier(.02,.01,.47,1),opacity .25s cubic-bezier(.02,.01,.47,1);transition:transform .25s cubic-bezier(.02,.01,.47,1),opacity .25s cubic-bezier(.02,.01,.47,1),-webkit-transform .25s cubic-bezier(.02,.01,.47,1)}.bttn-unite:focus:after,.bttn-unite:hover:after{-webkit-transform:translate3d(50%,-10%,0) skewX(-20deg);transform:translate3d(50%,-10%,0) skewX(-20deg)}.bttn-unite.bttn-xs{padding:3px 8px;font-size:12px;font-family:inherit}.bttn-unite.bttn-xs:focus,.bttn-unite.bttn-xs:hover{-webkit-box-shadow:0 1px 4px rgba(58,51,53,.3);box-shadow:0 1px 4px rgba(58,51,53,.3)}.bttn-unite.bttn-sm{padding:4px 10px;font-size:16px;font-family:inherit}.bttn-unite.bttn-sm:focus,.bttn-unite.bttn-sm:hover{-webkit-box-shadow:0 1px 6px rgba(58,51,53,.3);box-shadow:0 1px 6px rgba(58,51,53,.3)}.bttn-unite.bttn-md{font-size:20px;font-family:inherit;padding:5px 12px}.bttn-unite.bttn-md:focus,.bttn-unite.bttn-md:hover{-webkit-box-shadow:0 1px 8px rgba(58,51,53,.3);box-shadow:0 1px 8px rgba(58,51,53,.3)}.bttn-unite.bttn-lg{padding:8px 15px;font-size:24px;font-family:inherit}.bttn-unite.bttn-lg:focus,.bttn-unite.bttn-lg:hover{-webkit-box-shadow:0 1px 10px rgba(58,51,53,.3);box-shadow:0 1px 10px rgba(58,51,53,.3)}.bttn-unite.bttn-default{border-color:#1d89ff;color:#1d89ff}.bttn-unite.bttn-default:focus,.bttn-unite.bttn-default:hover{background:#d6e3ff;color:#1d89ff}.bttn-unite.bttn-default:before{background:#a7c3ff}.bttn-unite.bttn-default:after{background:#d6e3ff}.bttn-unite.bttn-primary{border-color:#1d89ff;color:#1d89ff}.bttn-unite.bttn-primary:focus,.bttn-unite.bttn-primary:hover{background:#1d89ff;color:#fff}.bttn-unite.bttn-primary:before{background:#006de3}.bttn-unite.bttn-primary:after{background:#1d89ff}.bttn-unite.bttn-warning{border-color:#feab3a;color:#feab3a}.bttn-unite.bttn-warning:focus,.bttn-unite.bttn-warning:hover{background:#feab3a;color:#fff}.bttn-unite.bttn-warning:before{background:#f89001}.bttn-unite.bttn-warning:after{background:#feab3a}.bttn-unite.bttn-danger{border-color:#ff5964;color:#ff5964}.bttn-unite.bttn-danger:focus,.bttn-unite.bttn-danger:hover{background:#ff5964;color:#fff}.bttn-unite.bttn-danger:before{background:#ff1424}.bttn-unite.bttn-danger:after{background:#ff5964}.bttn-unite.bttn-success{border-color:#28b78d;color:#28b78d}.bttn-unite.bttn-success:focus,.bttn-unite.bttn-success:hover{background:#28b78d;color:#fff}.bttn-unite.bttn-success:before{background:#209271}.bttn-unite.bttn-success:after{background:#28b78d}.bttn-unite.bttn-royal{border-color:#bd2df5;color:#bd2df5}.bttn-unite.bttn-royal:focus,.bttn-unite.bttn-royal:hover{background:#bd2df5;color:#fff}.bttn-unite.bttn-royal:before{background:#a20bdd}.bttn-unite.bttn-royal:after{background:#bd2df5}.bttn-slant{margin:0;padding:0;border-width:0;border-color:transparent;font-weight:400;cursor:pointer;position:relative;font-size:20px;font-family:inherit;padding:5px 12px;z-index:0;border:none;border-radius:0;background:transparent;color:#1d89ff;-webkit-transition:color .3s cubic-bezier(.02,.01,.47,1),-webkit-transform .3s cubic-bezier(.02,.01,.47,1);transition:color .3s cubic-bezier(.02,.01,.47,1),-webkit-transform .3s cubic-bezier(.02,.01,.47,1);-o-transition:color .3s cubic-bezier(.02,.01,.47,1),transform .3s cubic-bezier(.02,.01,.47,1);transition:color .3s cubic-bezier(.02,.01,.47,1),transform .3s cubic-bezier(.02,.01,.47,1);transition:color .3s cubic-bezier(.02,.01,.47,1),transform .3s cubic-bezier(.02,.01,.47,1),-webkit-transform .3s cubic-bezier(.02,.01,.47,1)}.bttn-slant:before{width:100%;background:#fafafa;-webkit-transition:box-shadow .2s cubic-bezier(.02,.01,.47,1);-webkit-transition:-webkit-box-shadow .2s cubic-bezier(.02,.01,.47,1);transition:-webkit-box-shadow .2s cubic-bezier(.02,.01,.47,1);-o-transition:box-shadow .2s cubic-bezier(.02,.01,.47,1);transition:box-shadow .2s cubic-bezier(.02,.01,.47,1);transition:box-shadow .2s cubic-bezier(.02,.01,.47,1),-webkit-box-shadow .2s cubic-bezier(.02,.01,.47,1)}.bttn-slant:after,.bttn-slant:before{position:absolute;top:0;left:0;z-index:-1;height:100%;content:"";-webkit-transform:skewX(20deg);-ms-transform:skewX(20deg);transform:skewX(20deg)}.bttn-slant:after{width:0;background:hsla(0,0%,98%,.3);opacity:0;-webkit-transition:opacity .2s cubic-bezier(.02,.01,.47,1),width .15s cubic-bezier(.02,.01,.47,1);-o-transition:opacity .2s cubic-bezier(.02,.01,.47,1),width .15s cubic-bezier(.02,.01,.47,1);transition:opacity .2s cubic-bezier(.02,.01,.47,1),width .15s cubic-bezier(.02,.01,.47,1)}.bttn-slant:focus,.bttn-slant:hover{-webkit-transform:translateX(5px);-ms-transform:translateX(5px);transform:translateX(5px)}.bttn-slant:focus:after,.bttn-slant:hover:after{width:5px;opacity:1}.bttn-slant:focus:before,.bttn-slant:hover:before{-webkit-box-shadow:inset 0 -1px 0 #a7c3ff,inset 0 1px 0 #a7c3ff,inset -1px 0 0 #a7c3ff;box-shadow:inset 0 -1px 0 #a7c3ff,inset 0 1px 0 #a7c3ff,inset -1px 0 0 #a7c3ff}.bttn-slant.bttn-xs{padding:3px 8px;font-size:12px;font-family:inherit}.bttn-slant.bttn-sm{padding:4px 10px;font-size:16px;font-family:inherit}.bttn-slant.bttn-md{font-size:20px;font-family:inherit;padding:5px 12px}.bttn-slant.bttn-lg{padding:8px 15px;font-size:24px;font-family:inherit}.bttn-slant.bttn-default{color:#1d89ff}.bttn-slant.bttn-default:focus:before,.bttn-slant.bttn-default:hover:before{-webkit-box-shadow:inset 0 -1px 0 #a7c3ff,inset 0 1px 0 #a7c3ff,inset -1px 0 0 #a7c3ff;box-shadow:inset 0 -1px 0 #a7c3ff,inset 0 1px 0 #a7c3ff,inset -1px 0 0 #a7c3ff}.bttn-slant.bttn-default:before{background:#fff}.bttn-slant.bttn-default:after{background:#a7c3ff}.bttn-slant.bttn-primary{color:#fff}.bttn-slant.bttn-primary:focus:before,.bttn-slant.bttn-primary:hover:before{-webkit-box-shadow:inset 0 -1px 0 #006de3,inset 0 1px 0 #006de3,inset -1px 0 0 #006de3;box-shadow:inset 0 -1px 0 #006de3,inset 0 1px 0 #006de3,inset -1px 0 0 #006de3}.bttn-slant.bttn-primary:before{background:#1d89ff}.bttn-slant.bttn-primary:after{background:#006de3}.bttn-slant.bttn-warning{color:#fff}.bttn-slant.bttn-warning:focus:before,.bttn-slant.bttn-warning:hover:before{-webkit-box-shadow:inset 0 -1px 0 #f89001,inset 0 1px 0 #f89001,inset -1px 0 0 #f89001;box-shadow:inset 0 -1px 0 #f89001,inset 0 1px 0 #f89001,inset -1px 0 0 #f89001}.bttn-slant.bttn-warning:before{background:#feab3a}.bttn-slant.bttn-warning:after{background:#f89001}.bttn-slant.bttn-danger{color:#fff}.bttn-slant.bttn-danger:focus:before,.bttn-slant.bttn-danger:hover:before{-webkit-box-shadow:inset 0 -1px 0 #ff1424,inset 0 1px 0 #ff1424,inset -1px 0 0 #ff1424;box-shadow:inset 0 -1px 0 #ff1424,inset 0 1px 0 #ff1424,inset -1px 0 0 #ff1424}.bttn-slant.bttn-danger:before{background:#ff5964}.bttn-slant.bttn-danger:after{background:#ff1424}.bttn-slant.bttn-success{color:#fff}.bttn-slant.bttn-success:focus:before,.bttn-slant.bttn-success:hover:before{-webkit-box-shadow:inset 0 -1px 0 #209271,inset 0 1px 0 #209271,inset -1px 0 0 #209271;box-shadow:inset 0 -1px 0 #209271,inset 0 1px 0 #209271,inset -1px 0 0 #209271}.bttn-slant.bttn-success:before{background:#28b78d}.bttn-slant.bttn-success:after{background:#209271}.bttn-slant.bttn-royal{color:#fff}.bttn-slant.bttn-royal:focus:before,.bttn-slant.bttn-royal:hover:before{-webkit-box-shadow:inset 0 -1px 0 #a20bdd,inset 0 1px 0 #a20bdd,inset -1px 0 0 #a20bdd;box-shadow:inset 0 -1px 0 #a20bdd,inset 0 1px 0 #a20bdd,inset -1px 0 0 #a20bdd}.bttn-slant.bttn-royal:before{background:#bd2df5}.bttn-slant.bttn-royal:after{background:#a20bdd}.bttn-block{display:block;width:100%}.bttn-no-outline,.bttn-no-outline:active,.bttn-no-outline:focus,.bttn-no-outline:hover{outline:none}.Keyboard{--border-color:#000;--border-width:1px;border-right:var(--border-width) solid var(--border-color);border-bottom:var(--border-width) solid #000}.Keyboard,.keyWrapper{display:inline-block;white-space:nowrap}.keyWrapper{position:relative;border-bottom:1px solid var(--border-color)}.writeKey{display:inline-block;width:26px;height:10vh;background-color:#fff;border-left:var(--border-width) solid var(--border-color)}.blackKey{position:absolute;z-index:1;top:0;left:20px;width:13px;height:6vh;background-color:#000;border-left:1px solid #000;border-right:var(--border-width) solid var(--border-color);border-bottom:var(--border-width) solid var(--border-color)}.key{-webkit-box-sizing:border-box;box-sizing:border-box}.key.C{border-top:5px solid red}.key.Csharp{border-top:5px solid #ff4500}.key.D{border-top:5px solid orange}.key.Dsharp{border-top:5px solid #fc0}.key.E{border-top:5px solid #ff0}.key.F{border-top:5px solid #adff2f}.key.Fsharp{border-top:5px solid green}.key.G{border-top:5px solid cyan}.key.Gsharp{border-top:5px solid blue}.key.A{border-top:5px solid #8a2be2}.key.Asharp{border-top:5px solid violet}.key.B{border-top:5px solid #f0f}.key.C:hover{background-color:red;cursor:Pointer}.key.Csharp:hover{background-color:#ff4500;cursor:Pointer}.key.D:hover{background-color:orange;cursor:Pointer}.key.Dsharp:hover{background-color:#fc0;cursor:Pointer}.key.E:hover{background-color:#ff0;cursor:Pointer}.key.F:hover{background-color:#adff2f;cursor:Pointer}.key.Fsharp:hover{background-color:green;cursor:Pointer}.key.G:hover{background-color:cyan;cursor:Pointer}.key.Gsharp:hover{background-color:blue;cursor:Pointer}.key.A:hover{background-color:#8a2be2;cursor:Pointer}.key.Asharp:hover{background-color:violet;cursor:Pointer}.key.B:hover{background-color:#f0f;cursor:Pointer}.strap{font-size:0;overflow-y:scroll}.strap,.whiteSpace{height:85vh}.Row{--border-width:1px;border-right:var(--border-width) solid gray;white-space:nowrap}.cellWrapper,.Row{display:inline-block}.cellWrapper{position:relative}.writeCell{display:inline-block;width:26px;height:20px;background-color:#fff;border-left:var(--border-width) solid gray}.blackCell{position:absolute;top:0;z-index:1;left:20px;width:13px;height:20px;border-left:var(--border-width) solid #ddd;border-right:var(--border-width) solid #ddd}.cell{border-top:var(--border-width) solid gray;-webkit-box-sizing:border-box;box-sizing:border-box}.cell:hover{cursor:Pointer;background-color:gray}.cell.onPressContinue{border-top:0}.C.onPress{background-color:red}.Csharp.onPress{background-color:#ff4500}.D.onPress{background-color:orange}.Dsharp.onPress{background-color:#fc0}.E.onPress{background-color:#ff0}.F.onPress{background-color:#adff2f}.Fsharp.onPress{background-color:green}.G.onPress{background-color:cyan}.Gsharp.onPress{background-color:blue}.A.onPress{background-color:#8a2be2}.Asharp.onPress{background-color:violet}.B.onPress{background-color:#f0f}
12 | /*# sourceMappingURL=main.9c2864c1.css.map*/
--------------------------------------------------------------------------------