diff --git a/conduit-backend/.dockerignore b/conduit-backend/.dockerignore index 93f13619916123cf5434dab2ffcc8263c7420af1..64b56b0e24f5a835ed0a022459c06b4ce329c13f 100644 --- a/conduit-backend/.dockerignore +++ b/conduit-backend/.dockerignore @@ -1,2 +1,8 @@ +.git +.gitignore +.gitlab-ci.yml +README.md node_modules -npm-debug.log +project-logo.png +back-up-system + diff --git a/conduit-backend/.gitignore b/conduit-backend/.gitignore index a8124030a5ddc94ee4af6d833fb7a7209f1d8139..9adbb849f5e613acd86231b98fb68ecbec983fdb 100644 --- a/conduit-backend/.gitignore +++ b/conduit-backend/.gitignore @@ -35,3 +35,7 @@ node_modules .node_repl_history .idea + +# used only in local development +Dockerfile-dev +docker-compose-dev.yml diff --git a/te-backend/.gitlab-ci.yml b/conduit-backend/.gitlab-ci.yml similarity index 100% rename from te-backend/.gitlab-ci.yml rename to conduit-backend/.gitlab-ci.yml diff --git a/conduit-backend/Dockerfile b/conduit-backend/Dockerfile index 10f4416cf0dfd919a0807f47c2bb2052032a23dc..5dc91e55714afe6d9aeefed4a34e111e5282f27c 100644 --- a/conduit-backend/Dockerfile +++ b/conduit-backend/Dockerfile @@ -1,13 +1,18 @@ -# Node/express -FROM node:14 +# Choose base image (maybe use node 14 with fewer warnings??) (changed from node:16-alpine) +FROM node:16 +# Create app directory WORKDIR /usr/src/app +# Install app dependencies COPY package*.json ./ - RUN npm install +# Bundle app source COPY . . +# Expose port EXPOSE 3000 -CMD [ "npm", "start" ] + +# Run app with node.js +CMD [ "node", "app.js" ] diff --git a/conduit-backend/app.js b/conduit-backend/app.js index 7435933ba300ceeff5e7cb11e66fe93e66fbef96..460baa408e848adf0b10d8c2812596fed38cc7e1 100644 --- a/conduit-backend/app.js +++ b/conduit-backend/app.js @@ -33,7 +33,7 @@ if (!isProduction) { if(isProduction){ mongoose.connect(process.env.MONGODB_URI); } else { - mongoose.connect('mongodb://10.152.183.200/conduit'); + mongoose.connect('mongodb://localhost/conduit'); mongoose.set('debug', true); } diff --git a/conduit-backend/models/User.js b/conduit-backend/models/User.js index 482613b4245c416989f8d6722b48eaf0fc1f60c8..acb2bcfd0af396c108908011cef4b1f851e3063b 100644 --- a/conduit-backend/models/User.js +++ b/conduit-backend/models/User.js @@ -5,8 +5,8 @@ var jwt = require('jsonwebtoken'); var secret = require('../config').secret; var UserSchema = new mongoose.Schema({ - username: {type: String, lowercase: true, unique: true, required: [true, "can't be blank"], match: [/^[a-zA-Z0-9]+$/, 'is invalid'], index: true}, - email: {type: String, lowercase: true, unique: true, required: [true, "can't be blank"], match: [/\S+@\S+\.\S+/, 'is invalid'], index: true}, + username: {type: String, unique: true, uniqueCaseInsensitive: true, required: [true, "can't be blank"], match: [/^[a-zA-Z0-9]+$/, 'is invalid'], index: true}, + email: {type: String, unique: true, uniqueCaseInsensitive: true, required: [true, "can't be blank"], match: [/\S+@\S+\.\S+/, 'is invalid'], index: true}, bio: String, image: String, favorites: [{ type: mongoose.Schema.Types.ObjectId, ref: 'Article' }], @@ -45,7 +45,7 @@ UserSchema.methods.toAuthJSON = function(){ email: this.email, token: this.generateJWT(), bio: this.bio, - image: this.image + image: this.image || 'https://static.productionready.io/images/smiley-cyrus.jpg' }; }; diff --git a/te-backend/package-lock.json b/conduit-backend/package-lock.json similarity index 100% rename from te-backend/package-lock.json rename to conduit-backend/package-lock.json diff --git a/conduit-backend/package.json b/conduit-backend/package.json index 8facb2e192d8ef0861567af8c04480af31e551d4..0b71f5bde85a58970601fff69c3d1ff76ded72b2 100644 --- a/conduit-backend/package.json +++ b/conduit-backend/package.json @@ -1,6 +1,6 @@ { "name": "conduit-node", - "version": "1.0.0", + "version": "2.0.0", "description": "conduit on node", "main": "app.js", "scripts": { @@ -27,7 +27,7 @@ "jsonwebtoken": "7.1.9", "method-override": "2.3.5", "methods": "1.1.2", - "mongoose": "4.6.4", + "mongoose": "^4.6.4", "mongoose-unique-validator": "1.0.2", "morgan": "1.7.0", "passport": "0.3.2", diff --git a/conduit-backend/routes/api/articles.js b/conduit-backend/routes/api/articles.js index e0f1074ba317a6235b3242f40c0e90664753fc66..a8ffe736c2dbe63d1ae2fb3b873157748f486d98 100644 --- a/conduit-backend/routes/api/articles.js +++ b/conduit-backend/routes/api/articles.js @@ -104,6 +104,7 @@ router.get('/feed', auth.required, function(req, res, next) { Promise.all([ Article.find({ author: {$in: user.following}}) .limit(Number(limit)) + .sort({createdAt: 'desc'}) .skip(Number(offset)) .populate('author') .exec(), diff --git a/conduit-backend/routes/api/users.js b/conduit-backend/routes/api/users.js index cc2e444c5452d6f018090fcd423a3f53c0c57bb8..2fc15476259a2eb850610339b4d9be5d6c85281b 100644 --- a/conduit-backend/routes/api/users.js +++ b/conduit-backend/routes/api/users.js @@ -1,6 +1,8 @@ var mongoose = require('mongoose'); var router = require('express').Router(); var passport = require('passport'); +var Article = mongoose.model('Article'); +var Comment = mongoose.model('Comment'); var User = mongoose.model('User'); var auth = require('../auth'); @@ -72,4 +74,18 @@ router.post('/users', function(req, res, next){ }).catch(next); }); +router.delete('/user', auth.required, function(req, res, next){ + //console.log('TESTI', req); // prints to backend container log + User.findByIdAndRemove(req.payload.id).then(function(user){ + if(!user){ return res.sendStatus(401); } + + Promise.all([ + Article.find({ author: user._id }).remove(), + Comment.find({ author: user._id }).remove() + ]).then(function(){ + return res.sendStatus(204); + }).catch(next); + }); +}); + module.exports = router; diff --git a/conduit-front/.dockerignore b/conduit-front/.dockerignore index 93f13619916123cf5434dab2ffcc8263c7420af1..009becc2ccd296ed11e3d799adc91ef03fd4e2cb 100644 --- a/conduit-front/.dockerignore +++ b/conduit-front/.dockerignore @@ -1,2 +1,5 @@ +.gitignore +.gitlab-ci.yml +README.md node_modules -npm-debug.log +project-logo.png diff --git a/conduit-front/.gitignore b/conduit-front/.gitignore index d09ce83155cf7e9ccf70b1a5878f97730480fda2..22b3f47901f6368b76636f8dcfae1c2fd8370b47 100644 --- a/conduit-front/.gitignore +++ b/conduit-front/.gitignore @@ -14,3 +14,7 @@ build .env npm-debug.log .idea + +# used only in local development +Dockerfile-dev +docker-compose-dev.yml diff --git a/te-frontend/.gitlab-ci.yml b/conduit-front/.gitlab-ci.yml similarity index 100% rename from te-frontend/.gitlab-ci.yml rename to conduit-front/.gitlab-ci.yml diff --git a/conduit-front/Dockerfile b/conduit-front/Dockerfile index 4b168c0a7c82365f48fe868a13bea9fca0494dd5..6d4d9e88a44114cd7ff4591843df56c886905b87 100644 --- a/conduit-front/Dockerfile +++ b/conduit-front/Dockerfile @@ -1,22 +1,15 @@ -#React/Redux +# Choose base image (maybe use node 14 for fewer warnings??) (changed from node:16) FROM node:14 -### -#FROM ubuntu:latest -### - +# Create app directory WORKDIR /usr/src/app +# Install app dependecies COPY package*.json ./ - RUN npm install -COPY . . +# Bundle app source +COPY . . EXPOSE 4100 - -### -#CMD apt update && apt install dnsutils -y && public_ip=$(dig +short myip.opendns.com @resolver1.opendns.com) && sed -i "s/localhost/$public_ip/g" -###src/agent.js && npm start - CMD [ "npm", "start" ] diff --git a/conduit-front/README.md b/conduit-front/README.md index 559f82861046b15c712bf96550783ff6597271f7..776ea0a6038364a08c92913053b53b2689961f7f 100644 --- a/conduit-front/README.md +++ b/conduit-front/README.md @@ -65,7 +65,7 @@ The example application is a social blogging site (i.e. a Medium.com clone) call - Render markdown from server client side - Comments section at bottom of page - Delete comment button (only shown to comment's author) -- Profile page (URL: /#/@username, /#/@username/favorites/lömps ) +- Profile page (URL: /#/@username, /#/@username/favorites ) - Show basic user info - List of articles populated from author's created articles or author's favorited articles diff --git a/te-frontend/package-lock.json b/conduit-front/package-lock.json similarity index 100% rename from te-frontend/package-lock.json rename to conduit-front/package-lock.json diff --git a/conduit-front/package.json b/conduit-front/package.json index 43b813643457e05a684362013f44fb42006aa5cf..8d7f2c4692aa43d3ef52c67783740226b0ec9b87 100644 --- a/conduit-front/package.json +++ b/conduit-front/package.json @@ -1,12 +1,17 @@ { "name": "react-redux-realworld-example-app", - "version": "0.1.0", + "version": "2.0.0", "private": true, "devDependencies": { "cross-env": "^5.1.4", - "react-scripts": "0.9.5" + "react-scripts": "^4.0.3" }, "dependencies": { + "@emotion/react": "^11.5.0", + "@emotion/styled": "^11.3.0", + "@material-ui/core": "^4.12.3", + "@material-ui/icons": "^4.11.2", + "@mui/material": "^5.1.0", "history": "^4.6.3", "marked": "^0.3.6", "prop-types": "^15.5.10", @@ -16,18 +21,31 @@ "react-router": "^4.1.2", "react-router-dom": "^4.1.2", "react-router-redux": "^5.0.0-alpha.6", + "react-share": "^4.4.0", "redux": "^3.6.0", "redux-devtools-extension": "^2.13.2", "redux-logger": "^3.0.1", "superagent": "^3.8.2", "superagent-promise": "^1.1.0", - "isexe": "", - "which": "" + "sweetalert": "^2.1.2", + "sweetalert2": "^11.1.9" }, "scripts": { "start": "cross-env PORT=4100 react-scripts start", "build": "react-scripts build", "test": "cross-env PORT=4100 react-scripts test --env=jsdom", "eject": "react-scripts eject" + }, + "browserslist": { + "production": [ + ">0.2%", + "not dead", + "not op_mini all" + ], + "development": [ + "last 1 chrome version", + "last 1 firefox version", + "last 1 safari version" + ] } } diff --git a/conduit-front/public/favicon.ico b/conduit-front/public/favicon.ico index 768ef78d709e0d54cf1734cb3e975924661b63b9..5c125de5d897c1ff5692a656485b3216123dcd89 100644 Binary files a/conduit-front/public/favicon.ico and b/conduit-front/public/favicon.ico differ diff --git a/conduit-front/public/index.html b/conduit-front/public/index.html index 977b223fb1bd7965f94da42a09730d3e64582832..21d61d53eb117d3e45ba74ea18f4ed210ab20753 100644 --- a/conduit-front/public/index.html +++ b/conduit-front/public/index.html @@ -1,10 +1,40 @@ <!doctype html> <html lang="en"> <head> + <style> + #doorbell{ + text-align: center; + } + #doorbell-submit-button{ + background-color: #5ED9C3; + color: rgb(255, 255, 255); + font-size: 1.1rem; + border-color: #5ED9C3; + } + #doorbell-submit-button:hover{ + background-color: #20c2a4; + color: rgb(255, 255, 255); + font-size: 1.1rem; + border-color: #219c86; + } + #doorbell-powered-by{ + color: #5ED9C3 + } + </style> + <!-- Global site tag (gtag.js) - Google Analytics --> + <script async src="https://www.googletagmanager.com/gtag/js?id=G-9FGSY8NE0L"></script> + <script> + window.dataLayer = window.dataLayer || []; + function gtag(){dataLayer.push(arguments);} + gtag('js', new Date()); + + gtag('config', 'G-9FGSY8NE0L'); + </script> + <meta charset="utf-8"> <meta name="viewport" content="width=device-width, initial-scale=1"> - <link rel="shortcut icon" href="%PUBLIC_URL%/favicon.ico"> - <link rel="stylesheet" href="main.css"> + <link rel="shortcut icon" href="%PUBLIC_URL%/logo_wimma.png"> + <link rel="stylesheet" href="../main.css"> <link href="//code.ionicframework.com/ionicons/2.0.1/css/ionicons.min.css" rel="stylesheet" type="text/css"> <link href="//fonts.googleapis.com/css?family=Titillium+Web:700|Source+Serif+Pro:400,700|Merriweather+Sans:400,700|Source+Sans+Pro:400,300,600,700,300italic,400italic,600italic,700italic" rel="stylesheet" type="text/css"> <!-- @@ -16,7 +46,7 @@ work correctly both with client-side routing and a non-root public URL. Learn how to configure a non-root public URL by running `npm run build`. --> - <title>ConduitMiau</title> + <title>WIMMA Lab Forum</title> </head> <body> <div id="root"></div> @@ -31,4 +61,23 @@ To create a production bundle, use `npm run build`. --> </body> + <script type="text/javascript"> + window.doorbellOptions = { + "id": "12727", + "appKey": "gcS5JEM6B1yEf3mRntjR2cKutewNl36UvthiBAEAv0K7o1kVkYIDi0CIVMpSIX5A", + strings: { + "feedback-button-text": "Feedback", + title: "Wimma Forum Feedback", + "feedback-textarea-placeholder": "Help Wimma Forum to improve, letting us know what you think.", + "email-input-placeholder": "Your email address"}, + email:"", + }; + (function(w, d, t) { + var hasLoaded = false; + function l() { if (hasLoaded) { return; } hasLoaded = true; window.doorbellOptions.windowLoaded = true; var g = d.createElement(t);g.id = 'doorbellScript';g.type = 'text/javascript';g.async = true;g.src = 'https://embed.doorbell.io/button/'+ + window.doorbellOptions['id']+'?t='+(new Date().getTime());(d.getElementsByTagName('head')[0]||d.getElementsByTagName('body')[0]).appendChild(g); } + if (w.attachEvent) { w.attachEvent('onload', l); } else if (w.addEventListener) { w.addEventListener('load', l, false); } else { l(); } + if (d.readyState == 'complete') { l(); } + }(window, document, 'script')); +</script> </html> diff --git a/te-frontend/public/logo_wimma.png b/conduit-front/public/logo_wimma.png similarity index 100% rename from te-frontend/public/logo_wimma.png rename to conduit-front/public/logo_wimma.png diff --git a/conduit-front/public/main.css b/conduit-front/public/main.css index a76a6f3afdecd7e02885a8199ce75fba584cab0a..0d6dd8f1795efbf51a5493e5d1ec140e359f1475 100644 --- a/conduit-front/public/main.css +++ b/conduit-front/public/main.css @@ -1,3 +1,3 @@ .logo-font{font-family:titillium web,sans-serif}html{position:relative;min-height:100vh;padding-bottom:100px}/*!* Bootstrap v4.0.0-alpha.2 (http://getbootstrap.com) * Copyright 2011-2016 Twitter, Inc. -* Licensed under MIT (https://github.com/twbs/bootstrap/blob/master/LICENSE)*//*!normalize.css commit fe56763 | MIT License | github.com/necolas/normalize.css*/html{font-family:sans-serif;-ms-text-size-adjust:100%;-webkit-text-size-adjust:100%}body{margin:0}article,aside,details,figcaption,figure,footer,header,main,menu,nav,section,summary{display:block}audio,canvas,progress,video{display:inline-block;vertical-align:baseline}audio:not([controls]){display:none;height:0}[hidden],template{display:none}a{background-color:transparent}a:active{outline:0}a:hover{outline:0}abbr[title]{border-bottom:1px dotted}b,strong{font-weight:700}dfn{font-style:italic}h1{font-size:2em;margin:.67em 0}mark{background:#ff0;color:#000}small{font-size:80%}sub,sup{font-size:75%;line-height:0;position:relative;vertical-align:baseline}sup{top:-.5em}sub{bottom:-.25em}img{border:0}svg:not(:root){overflow:hidden}figure{margin:1em 40px}hr{box-sizing:content-box;height:0}pre{overflow:auto}code,kbd,pre,samp{font-family:monospace,monospace;font-size:1em}button,input,optgroup,select,textarea{color:inherit;font:inherit;margin:0}button{overflow:visible}button,select{text-transform:none}button,html input[type=button],input[type=reset],input[type=submit]{-webkit-appearance:button;cursor:pointer}button[disabled],html input[disabled]{cursor:default}button::-moz-focus-inner,input::-moz-focus-inner{border:0;padding:0}input{line-height:normal}input[type=checkbox],input[type=radio]{box-sizing:border-box;padding:0}input[type=number]::-webkit-inner-spin-button,input[type=number]::-webkit-outer-spin-button{height:auto}input[type=search]{-webkit-appearance:textfield}input[type=search]::-webkit-search-cancel-button,input[type=search]::-webkit-search-decoration{-webkit-appearance:none}fieldset{border:1px solid silver;margin:0 2px;padding:.35em .625em .75em}legend{border:0;padding:0}textarea{overflow:auto}optgroup{font-weight:700}table{border-collapse:collapse;border-spacing:0}td,th{padding:0}@media print{*,*::before,*::after,*::first-letter,*::first-line{text-shadow:none!important;box-shadow:none!important}a,a:visited{text-decoration:underline}abbr[title]::after{content:" (" attr(title) ")"}pre,blockquote{border:1px solid #999;page-break-inside:avoid}thead{display:table-header-group}tr,img{page-break-inside:avoid}p,h2,h3{orphans:3;widows:3}h2,h3{page-break-after:avoid}.navbar{display:none}.btn>.caret,.dropup>.btn>.caret{border-top-color:#000!important}.tag{border:1px solid #000}.table{border-collapse:collapse!important}.table td,.table th{background-color:#fff!important}.table-bordered th,.table-bordered td{border:1px solid #ddd!important}}html{box-sizing:border-box}*,*::before,*::after{box-sizing:inherit}@-ms-viewport{width:device-width;}html{font-size:16px;-ms-overflow-style:scrollbar;-webkit-tap-highlight-color:transparent}body{font-family:source sans pro,sans-serif;font-size:1rem;line-height:1.5;color:#373a3c;background-color:rgb(255, 255, 255)}[tabindex="-1"]:focus{outline:none!important}h1,h2,h3,h4,h5,h6{margin-top:0;margin-bottom:.5rem}p{margin-top:0;margin-bottom:1rem}abbr[title],abbr[data-original-title]{cursor:help;border-bottom:1px dotted #818a91}address{margin-bottom:1rem;font-style:normal;line-height:inherit}ol,ul,dl{margin-top:0;margin-bottom:1rem}ol ol,ul ul,ol ul,ul ol{margin-bottom:0}dt{font-weight:700}dd{margin-bottom:.5rem;margin-left:0}blockquote{margin:0 0 1rem}a{color:#5cb85c;text-decoration:none}a:focus,a:hover{color:#3d8b3d;text-decoration:underline}a:focus{outline:thin dotted;outline:5px auto -webkit-focus-ring-color;outline-offset:-2px}a:not([href]){color:inherit;text-decoration:none}a:not([href]):focus,a:not([href]):hover{color:inherit;text-decoration:none}a:not([href]):focus{outline:none}pre{margin-top:0;margin-bottom:1rem}figure{margin:0 0 1rem}img{vertical-align:middle}[role=button]{cursor:pointer}a,area,button,[role=button],input,label,select,summary,textarea{touch-action:manipulation}table{background-color:transparent}caption{padding-top:.75rem;padding-bottom:.75rem;color:#818a91;text-align:left;caption-side:bottom}th{text-align:left}label{display:inline-block;margin-bottom:.5rem}button:focus{outline:1px dotted;outline:5px auto -webkit-focus-ring-color}input,button,select,textarea{margin:0;line-height:inherit;border-radius:0}textarea{resize:vertical}fieldset{min-width:0;padding:0;margin:0;border:0}legend{display:block;width:100%;padding:0;margin-bottom:.5rem;font-size:1.5rem;line-height:inherit}input[type=search]{-webkit-appearance:none}output{display:inline-block}[hidden]{display:none!important}h1,h2,h3,h4,h5,h6,.h1,.h2,.h3,.h4,.h5,.h6{margin-bottom:.5rem;font-family:inherit;font-weight:500;line-height:1.1;color:inherit}h1,.h1{font-size:2.5rem}h2,.h2{font-size:2rem}h3,.h3{font-size:1.75rem}h4,.h4{font-size:1.5rem}h5,.h5{font-size:1.25rem}h6,.h6{font-size:1rem}.lead{font-size:1.25rem;font-weight:300}.display-1{font-size:6rem;font-weight:300}.display-2{font-size:5.5rem;font-weight:300}.display-3{font-size:4.5rem;font-weight:300}.display-4{font-size:3.5rem;font-weight:300}hr{margin-top:1rem;margin-bottom:1rem;border:0;border-top:1px solid rgba(0,0,0,.1)}small,.small{font-size:80%;font-weight:400}mark,.mark{padding:.2em;background-color:#fcf8e3}.list-unstyled{padding-left:0;list-style:none}.list-inline{padding-left:0;list-style:none}.list-inline-item{display:inline-block}.list-inline-item:not(:last-child){margin-right:5px}.initialism{font-size:90%;text-transform:uppercase}.blockquote{padding:.5rem 1rem;margin-bottom:1rem;font-size:1.25rem;border-left:.25rem solid #eceeef}.blockquote-footer{display:block;font-size:80%;color:#818a91}.blockquote-footer::before{content:"\2014 \00A0"}.blockquote-reverse{padding-right:1rem;padding-left:0;text-align:right;border-right:.25rem solid #eceeef;border-left:0}.blockquote-reverse .blockquote-footer::before{content:""}.blockquote-reverse .blockquote-footer::after{content:"\00A0 \2014"}.img-fluid,.carousel-inner>.carousel-item>img,.carousel-inner>.carousel-item>a>img{display:block;max-width:100%;height:auto}.img-rounded{border-radius:.3rem}.img-thumbnail{padding:.25rem;background-color:#fff;border:1px solid #ddd;border-radius:.25rem;transition:all .2s ease-in-out;display:inline-block;max-width:100%;height:auto}.img-circle{border-radius:50%}.figure{display:inline-block}.figure-img{margin-bottom:.5rem;line-height:1}.figure-caption{font-size:90%;color:#818a91}code,kbd,pre,samp{font-family:Menlo,Monaco,Consolas,liberation mono,courier new,monospace}code{padding:.2rem .4rem;font-size:90%;color:#bd4147;background-color:#f7f7f9;border-radius:.25rem}kbd{padding:.2rem .4rem;font-size:90%;color:#fff;background-color:#333;border-radius:.2rem}kbd kbd{padding:0;font-size:100%;font-weight:700}pre{display:block;margin-top:0;margin-bottom:1rem;font-size:90%;color:#373a3c}pre code{padding:0;font-size:inherit;color:inherit;background-color:transparent;border-radius:0}.pre-scrollable{max-height:340px;overflow-y:scroll}.container{margin-left:auto;margin-right:auto;padding-left:15px;padding-right:15px}@media(min-width:544px){.container{max-width:576px}}@media(min-width:768px){.container{max-width:720px}}@media(min-width:992px){.container{max-width:940px}}@media(min-width:1200px){.container{max-width:1140px}}.container-fluid{margin-left:auto;margin-right:auto;padding-left:15px;padding-right:15px}.row{display:flex;flex-wrap:wrap;margin-left:-15px;margin-right:-15px}.col-xs{position:relative;flex-basis:0;flex-grow:1;max-width:100%;min-height:1px;padding-right:15px;padding-left:15px}.col-xs-1{position:relative;min-height:1px;padding-right:15px;padding-left:15px;flex:0 0 8.33333%;max-width:8.33333%}.col-xs-2{position:relative;min-height:1px;padding-right:15px;padding-left:15px;flex:0 0 16.66667%;max-width:16.66667%}.col-xs-3{position:relative;min-height:1px;padding-right:15px;padding-left:15px;flex:0 0 25%;max-width:25%}.col-xs-4{position:relative;min-height:1px;padding-right:15px;padding-left:15px;flex:0 0 33.33333%;max-width:33.33333%}.col-xs-5{position:relative;min-height:1px;padding-right:15px;padding-left:15px;flex:0 0 41.66667%;max-width:41.66667%}.col-xs-6{position:relative;min-height:1px;padding-right:15px;padding-left:15px;flex:0 0 50%;max-width:50%}.col-xs-7{position:relative;min-height:1px;padding-right:15px;padding-left:15px;flex:0 0 58.33333%;max-width:58.33333%}.col-xs-8{position:relative;min-height:1px;padding-right:15px;padding-left:15px;flex:0 0 66.66667%;max-width:66.66667%}.col-xs-9{position:relative;min-height:1px;padding-right:15px;padding-left:15px;flex:0 0 75%;max-width:75%}.col-xs-10{position:relative;min-height:1px;padding-right:15px;padding-left:15px;flex:0 0 83.33333%;max-width:83.33333%}.col-xs-11{position:relative;min-height:1px;padding-right:15px;padding-left:15px;flex:0 0 91.66667%;max-width:91.66667%}.col-xs-12{position:relative;min-height:1px;padding-right:15px;padding-left:15px;flex:0 0 100%;max-width:100%}.pull-xs-0{right:auto}.pull-xs-1{right:8.33333%}.pull-xs-2{right:16.66667%}.pull-xs-3{right:25%}.pull-xs-4{right:33.33333%}.pull-xs-5{right:41.66667%}.pull-xs-6{right:50%}.pull-xs-7{right:58.33333%}.pull-xs-8{right:66.66667%}.pull-xs-9{right:75%}.pull-xs-10{right:83.33333%}.pull-xs-11{right:91.66667%}.pull-xs-12{right:100%}.push-xs-0{left:auto}.push-xs-1{left:8.33333%}.push-xs-2{left:16.66667%}.push-xs-3{left:25%}.push-xs-4{left:33.33333%}.push-xs-5{left:41.66667%}.push-xs-6{left:50%}.push-xs-7{left:58.33333%}.push-xs-8{left:66.66667%}.push-xs-9{left:75%}.push-xs-10{left:83.33333%}.push-xs-11{left:91.66667%}.push-xs-12{left:100%}.offset-xs-1{margin-left:8.33333%}.offset-xs-2{margin-left:16.66667%}.offset-xs-3{margin-left:25%}.offset-xs-4{margin-left:33.33333%}.offset-xs-5{margin-left:41.66667%}.offset-xs-6{margin-left:50%}.offset-xs-7{margin-left:58.33333%}.offset-xs-8{margin-left:66.66667%}.offset-xs-9{margin-left:75%}.offset-xs-10{margin-left:83.33333%}.offset-xs-11{margin-left:91.66667%}@media(min-width:544px){.col-sm{position:relative;flex-basis:0;flex-grow:1;max-width:100%;min-height:1px;padding-right:15px;padding-left:15px}.col-sm-1{position:relative;min-height:1px;padding-right:15px;padding-left:15px;flex:0 0 8.33333%;max-width:8.33333%}.col-sm-2{position:relative;min-height:1px;padding-right:15px;padding-left:15px;flex:0 0 16.66667%;max-width:16.66667%}.col-sm-3{position:relative;min-height:1px;padding-right:15px;padding-left:15px;flex:0 0 25%;max-width:25%}.col-sm-4{position:relative;min-height:1px;padding-right:15px;padding-left:15px;flex:0 0 33.33333%;max-width:33.33333%}.col-sm-5{position:relative;min-height:1px;padding-right:15px;padding-left:15px;flex:0 0 41.66667%;max-width:41.66667%}.col-sm-6{position:relative;min-height:1px;padding-right:15px;padding-left:15px;flex:0 0 50%;max-width:50%}.col-sm-7{position:relative;min-height:1px;padding-right:15px;padding-left:15px;flex:0 0 58.33333%;max-width:58.33333%}.col-sm-8{position:relative;min-height:1px;padding-right:15px;padding-left:15px;flex:0 0 66.66667%;max-width:66.66667%}.col-sm-9{position:relative;min-height:1px;padding-right:15px;padding-left:15px;flex:0 0 75%;max-width:75%}.col-sm-10{position:relative;min-height:1px;padding-right:15px;padding-left:15px;flex:0 0 83.33333%;max-width:83.33333%}.col-sm-11{position:relative;min-height:1px;padding-right:15px;padding-left:15px;flex:0 0 91.66667%;max-width:91.66667%}.col-sm-12{position:relative;min-height:1px;padding-right:15px;padding-left:15px;flex:0 0 100%;max-width:100%}.pull-sm-0{right:auto}.pull-sm-1{right:8.33333%}.pull-sm-2{right:16.66667%}.pull-sm-3{right:25%}.pull-sm-4{right:33.33333%}.pull-sm-5{right:41.66667%}.pull-sm-6{right:50%}.pull-sm-7{right:58.33333%}.pull-sm-8{right:66.66667%}.pull-sm-9{right:75%}.pull-sm-10{right:83.33333%}.pull-sm-11{right:91.66667%}.pull-sm-12{right:100%}.push-sm-0{left:auto}.push-sm-1{left:8.33333%}.push-sm-2{left:16.66667%}.push-sm-3{left:25%}.push-sm-4{left:33.33333%}.push-sm-5{left:41.66667%}.push-sm-6{left:50%}.push-sm-7{left:58.33333%}.push-sm-8{left:66.66667%}.push-sm-9{left:75%}.push-sm-10{left:83.33333%}.push-sm-11{left:91.66667%}.push-sm-12{left:100%}.offset-sm-0{margin-left:0%}.offset-sm-1{margin-left:8.33333%}.offset-sm-2{margin-left:16.66667%}.offset-sm-3{margin-left:25%}.offset-sm-4{margin-left:33.33333%}.offset-sm-5{margin-left:41.66667%}.offset-sm-6{margin-left:50%}.offset-sm-7{margin-left:58.33333%}.offset-sm-8{margin-left:66.66667%}.offset-sm-9{margin-left:75%}.offset-sm-10{margin-left:83.33333%}.offset-sm-11{margin-left:91.66667%}}@media(min-width:768px){.col-md{position:relative;flex-basis:0;flex-grow:1;max-width:100%;min-height:1px;padding-right:15px;padding-left:15px}.col-md-1{position:relative;min-height:1px;padding-right:15px;padding-left:15px;flex:0 0 8.33333%;max-width:8.33333%}.col-md-2{position:relative;min-height:1px;padding-right:15px;padding-left:15px;flex:0 0 16.66667%;max-width:16.66667%}.col-md-3{position:relative;min-height:1px;padding-right:15px;padding-left:15px;flex:0 0 25%;max-width:25%}.col-md-4{position:relative;min-height:1px;padding-right:15px;padding-left:15px;flex:0 0 33.33333%;max-width:33.33333%}.col-md-5{position:relative;min-height:1px;padding-right:15px;padding-left:15px;flex:0 0 41.66667%;max-width:41.66667%}.col-md-6{position:relative;min-height:1px;padding-right:15px;padding-left:15px;flex:0 0 50%;max-width:50%}.col-md-7{position:relative;min-height:1px;padding-right:15px;padding-left:15px;flex:0 0 58.33333%;max-width:58.33333%}.col-md-8{position:relative;min-height:1px;padding-right:15px;padding-left:15px;flex:0 0 66.66667%;max-width:66.66667%}.col-md-9{position:relative;min-height:1px;padding-right:15px;padding-left:15px;flex:0 0 75%;max-width:75%}.col-md-10{position:relative;min-height:1px;padding-right:15px;padding-left:15px;flex:0 0 83.33333%;max-width:83.33333%}.col-md-11{position:relative;min-height:1px;padding-right:15px;padding-left:15px;flex:0 0 91.66667%;max-width:91.66667%}.col-md-12{position:relative;min-height:1px;padding-right:15px;padding-left:15px;flex:0 0 100%;max-width:100%}.pull-md-0{right:auto}.pull-md-1{right:8.33333%}.pull-md-2{right:16.66667%}.pull-md-3{right:25%}.pull-md-4{right:33.33333%}.pull-md-5{right:41.66667%}.pull-md-6{right:50%}.pull-md-7{right:58.33333%}.pull-md-8{right:66.66667%}.pull-md-9{right:75%}.pull-md-10{right:83.33333%}.pull-md-11{right:91.66667%}.pull-md-12{right:100%}.push-md-0{left:auto}.push-md-1{left:8.33333%}.push-md-2{left:16.66667%}.push-md-3{left:25%}.push-md-4{left:33.33333%}.push-md-5{left:41.66667%}.push-md-6{left:50%}.push-md-7{left:58.33333%}.push-md-8{left:66.66667%}.push-md-9{left:75%}.push-md-10{left:83.33333%}.push-md-11{left:91.66667%}.push-md-12{left:100%}.offset-md-0{margin-left:0%}.offset-md-1{margin-left:8.33333%}.offset-md-2{margin-left:16.66667%}.offset-md-3{margin-left:25%}.offset-md-4{margin-left:33.33333%}.offset-md-5{margin-left:41.66667%}.offset-md-6{margin-left:50%}.offset-md-7{margin-left:58.33333%}.offset-md-8{margin-left:66.66667%}.offset-md-9{margin-left:75%}.offset-md-10{margin-left:83.33333%}.offset-md-11{margin-left:91.66667%}}@media(min-width:992px){.col-lg{position:relative;flex-basis:0;flex-grow:1;max-width:100%;min-height:1px;padding-right:15px;padding-left:15px}.col-lg-1{position:relative;min-height:1px;padding-right:15px;padding-left:15px;flex:0 0 8.33333%;max-width:8.33333%}.col-lg-2{position:relative;min-height:1px;padding-right:15px;padding-left:15px;flex:0 0 16.66667%;max-width:16.66667%}.col-lg-3{position:relative;min-height:1px;padding-right:15px;padding-left:15px;flex:0 0 25%;max-width:25%}.col-lg-4{position:relative;min-height:1px;padding-right:15px;padding-left:15px;flex:0 0 33.33333%;max-width:33.33333%}.col-lg-5{position:relative;min-height:1px;padding-right:15px;padding-left:15px;flex:0 0 41.66667%;max-width:41.66667%}.col-lg-6{position:relative;min-height:1px;padding-right:15px;padding-left:15px;flex:0 0 50%;max-width:50%}.col-lg-7{position:relative;min-height:1px;padding-right:15px;padding-left:15px;flex:0 0 58.33333%;max-width:58.33333%}.col-lg-8{position:relative;min-height:1px;padding-right:15px;padding-left:15px;flex:0 0 66.66667%;max-width:66.66667%}.col-lg-9{position:relative;min-height:1px;padding-right:15px;padding-left:15px;flex:0 0 75%;max-width:75%}.col-lg-10{position:relative;min-height:1px;padding-right:15px;padding-left:15px;flex:0 0 83.33333%;max-width:83.33333%}.col-lg-11{position:relative;min-height:1px;padding-right:15px;padding-left:15px;flex:0 0 91.66667%;max-width:91.66667%}.col-lg-12{position:relative;min-height:1px;padding-right:15px;padding-left:15px;flex:0 0 100%;max-width:100%}.pull-lg-0{right:auto}.pull-lg-1{right:8.33333%}.pull-lg-2{right:16.66667%}.pull-lg-3{right:25%}.pull-lg-4{right:33.33333%}.pull-lg-5{right:41.66667%}.pull-lg-6{right:50%}.pull-lg-7{right:58.33333%}.pull-lg-8{right:66.66667%}.pull-lg-9{right:75%}.pull-lg-10{right:83.33333%}.pull-lg-11{right:91.66667%}.pull-lg-12{right:100%}.push-lg-0{left:auto}.push-lg-1{left:8.33333%}.push-lg-2{left:16.66667%}.push-lg-3{left:25%}.push-lg-4{left:33.33333%}.push-lg-5{left:41.66667%}.push-lg-6{left:50%}.push-lg-7{left:58.33333%}.push-lg-8{left:66.66667%}.push-lg-9{left:75%}.push-lg-10{left:83.33333%}.push-lg-11{left:91.66667%}.push-lg-12{left:100%}.offset-lg-0{margin-left:0%}.offset-lg-1{margin-left:8.33333%}.offset-lg-2{margin-left:16.66667%}.offset-lg-3{margin-left:25%}.offset-lg-4{margin-left:33.33333%}.offset-lg-5{margin-left:41.66667%}.offset-lg-6{margin-left:50%}.offset-lg-7{margin-left:58.33333%}.offset-lg-8{margin-left:66.66667%}.offset-lg-9{margin-left:75%}.offset-lg-10{margin-left:83.33333%}.offset-lg-11{margin-left:91.66667%}}@media(min-width:1200px){.col-xl{position:relative;flex-basis:0;flex-grow:1;max-width:100%;min-height:1px;padding-right:15px;padding-left:15px}.col-xl-1{position:relative;min-height:1px;padding-right:15px;padding-left:15px;flex:0 0 8.33333%;max-width:8.33333%}.col-xl-2{position:relative;min-height:1px;padding-right:15px;padding-left:15px;flex:0 0 16.66667%;max-width:16.66667%}.col-xl-3{position:relative;min-height:1px;padding-right:15px;padding-left:15px;flex:0 0 25%;max-width:25%}.col-xl-4{position:relative;min-height:1px;padding-right:15px;padding-left:15px;flex:0 0 33.33333%;max-width:33.33333%}.col-xl-5{position:relative;min-height:1px;padding-right:15px;padding-left:15px;flex:0 0 41.66667%;max-width:41.66667%}.col-xl-6{position:relative;min-height:1px;padding-right:15px;padding-left:15px;flex:0 0 50%;max-width:50%}.col-xl-7{position:relative;min-height:1px;padding-right:15px;padding-left:15px;flex:0 0 58.33333%;max-width:58.33333%}.col-xl-8{position:relative;min-height:1px;padding-right:15px;padding-left:15px;flex:0 0 66.66667%;max-width:66.66667%}.col-xl-9{position:relative;min-height:1px;padding-right:15px;padding-left:15px;flex:0 0 75%;max-width:75%}.col-xl-10{position:relative;min-height:1px;padding-right:15px;padding-left:15px;flex:0 0 83.33333%;max-width:83.33333%}.col-xl-11{position:relative;min-height:1px;padding-right:15px;padding-left:15px;flex:0 0 91.66667%;max-width:91.66667%}.col-xl-12{position:relative;min-height:1px;padding-right:15px;padding-left:15px;flex:0 0 100%;max-width:100%}.pull-xl-0{right:auto}.pull-xl-1{right:8.33333%}.pull-xl-2{right:16.66667%}.pull-xl-3{right:25%}.pull-xl-4{right:33.33333%}.pull-xl-5{right:41.66667%}.pull-xl-6{right:50%}.pull-xl-7{right:58.33333%}.pull-xl-8{right:66.66667%}.pull-xl-9{right:75%}.pull-xl-10{right:83.33333%}.pull-xl-11{right:91.66667%}.pull-xl-12{right:100%}.push-xl-0{left:auto}.push-xl-1{left:8.33333%}.push-xl-2{left:16.66667%}.push-xl-3{left:25%}.push-xl-4{left:33.33333%}.push-xl-5{left:41.66667%}.push-xl-6{left:50%}.push-xl-7{left:58.33333%}.push-xl-8{left:66.66667%}.push-xl-9{left:75%}.push-xl-10{left:83.33333%}.push-xl-11{left:91.66667%}.push-xl-12{left:100%}.offset-xl-0{margin-left:0%}.offset-xl-1{margin-left:8.33333%}.offset-xl-2{margin-left:16.66667%}.offset-xl-3{margin-left:25%}.offset-xl-4{margin-left:33.33333%}.offset-xl-5{margin-left:41.66667%}.offset-xl-6{margin-left:50%}.offset-xl-7{margin-left:58.33333%}.offset-xl-8{margin-left:66.66667%}.offset-xl-9{margin-left:75%}.offset-xl-10{margin-left:83.33333%}.offset-xl-11{margin-left:91.66667%}}.table{width:100%;max-width:100%;margin-bottom:1rem}.table th,.table td{padding:.75rem;vertical-align:top;border-top:1px solid #eceeef}.table thead th{vertical-align:bottom;border-bottom:2px solid #eceeef}.table tbody+tbody{border-top:2px solid #eceeef}.table .table{background-color:#fff}.table-sm th,.table-sm td{padding:.3rem}.table-bordered{border:1px solid #eceeef}.table-bordered th,.table-bordered td{border:1px solid #eceeef}.table-bordered thead th,.table-bordered thead td{border-bottom-width:2px}.table-striped tbody tr:nth-of-type(odd){background-color:#f9f9f9}.table-hover tbody tr:hover{background-color:#f5f5f5}.table-active,.table-active>th,.table-active>td{background-color:#f5f5f5}.table-hover .table-active:hover{background-color:#e8e8e8}.table-hover .table-active:hover>td,.table-hover .table-active:hover>th{background-color:#e8e8e8}.table-success,.table-success>th,.table-success>td{background-color:#dff0d8}.table-hover .table-success:hover{background-color:#d0e9c6}.table-hover .table-success:hover>td,.table-hover .table-success:hover>th{background-color:#d0e9c6}.table-info,.table-info>th,.table-info>td{background-color:#d9edf7}.table-hover .table-info:hover{background-color:#c4e3f3}.table-hover .table-info:hover>td,.table-hover .table-info:hover>th{background-color:#c4e3f3}.table-warning,.table-warning>th,.table-warning>td{background-color:#fcf8e3}.table-hover .table-warning:hover{background-color:#faf2cc}.table-hover .table-warning:hover>td,.table-hover .table-warning:hover>th{background-color:#faf2cc}.table-danger,.table-danger>th,.table-danger>td{background-color:#f2dede}.table-hover .table-danger:hover{background-color:#ebcccc}.table-hover .table-danger:hover>td,.table-hover .table-danger:hover>th{background-color:#ebcccc}.table-responsive{display:block;width:100%;min-height:.01%;overflow-x:auto}.thead-inverse th{color:#fff;background-color:#373a3c}.thead-default th{color:#55595c;background-color:#eceeef}.table-inverse{color:#eceeef;background-color:#373a3c}.table-inverse.table-bordered{border:0}.table-inverse th,.table-inverse td,.table-inverse thead th{border-color:#55595c}.table-reflow thead{float:left}.table-reflow tbody{display:block;white-space:nowrap}.table-reflow th,.table-reflow td{border-top:1px solid #eceeef;border-left:1px solid #eceeef}.table-reflow th:last-child,.table-reflow td:last-child{border-right:1px solid #eceeef}.table-reflow thead:last-child tr:last-child th,.table-reflow thead:last-child tr:last-child td,.table-reflow tbody:last-child tr:last-child th,.table-reflow tbody:last-child tr:last-child td,.table-reflow tfoot:last-child tr:last-child th,.table-reflow tfoot:last-child tr:last-child td{border-bottom:1px solid #eceeef}.table-reflow tr{float:left}.table-reflow tr th,.table-reflow tr td{display:block!important;border:1px solid #eceeef}.form-control{display:block;width:100%;padding:.5rem .75rem;font-size:1rem;line-height:1.25;color:#55595c;background-color:#fff;background-image:none;background-clip:padding-box;border:1px solid rgba(0,0,0,.15);border-radius:.25rem}.form-control::-ms-expand{background-color:transparent;border:0}.form-control:focus{border-color:#66afe9;outline:none}.form-control::placeholder{color:#999;opacity:1}.form-control:disabled,.form-control[readonly]{background-color:#eceeef;opacity:1}.form-control:disabled{cursor:not-allowed}select.form-control:not([size]):not([multiple]){height:2.5rem}.form-control-file,.form-control-range{display:block}.form-control-label{padding:.5rem .75rem;margin-bottom:0}.form-control-legend{padding:.5rem .75rem;margin-bottom:0;font-size:1rem}_::-webkit-full-page-media.form-control,input[type=date].form-control,input[type=time].form-control,input[type=datetime-local].form-control,input[type=month].form-control{line-height:2.5rem}_::-webkit-full-page-media.input-sm,.input-group-sm _::-webkit-full-page-media.form-control,input[type=date].input-sm,.input-group-sm input[type=date].form-control,input[type=time].input-sm,.input-group-sm input[type=time].form-control,input[type=datetime-local].input-sm,.input-group-sm input[type=datetime-local].form-control,input[type=month].input-sm,.input-group-sm input[type=month].form-control{line-height:1.8125rem}_::-webkit-full-page-media.input-lg,.input-group-lg _::-webkit-full-page-media.form-control,input[type=date].input-lg,.input-group-lg input[type=date].form-control,input[type=time].input-lg,.input-group-lg input[type=time].form-control,input[type=datetime-local].input-lg,.input-group-lg input[type=datetime-local].form-control,input[type=month].input-lg,.input-group-lg input[type=month].form-control{line-height:3.16667rem}.form-control-static{min-height:2.5rem;padding-top:.5rem;padding-bottom:.5rem;margin-bottom:0}.form-control-static.form-control-sm,.input-group-sm>.form-control-static.form-control,.input-group-sm>.form-control-static.input-group-addon,.input-group-sm>.input-group-btn>.form-control-static.btn,.form-control-static.form-control-lg,.input-group-lg>.form-control-static.form-control,.input-group-lg>.form-control-static.input-group-addon,.input-group-lg>.input-group-btn>.form-control-static.btn{padding-right:0;padding-left:0}.form-control-sm,.input-group-sm>.form-control,.input-group-sm>.input-group-addon,.input-group-sm>.input-group-btn>.btn{padding:.25rem .5rem;font-size:.875rem;border-radius:.2rem}.form-control-lg,.input-group-lg>.form-control,.input-group-lg>.input-group-addon,.input-group-lg>.input-group-btn>.btn{padding:.75rem 1.5rem;font-size:1.25rem;border-radius:.3rem}.form-group{margin-bottom:1rem}.radio,.checkbox{position:relative;display:block;margin-bottom:.75rem}.radio label,.checkbox label{padding-left:1.25rem;margin-bottom:0;cursor:pointer}.radio label input:only-child,.checkbox label input:only-child{position:static}.radio input[type=radio],.radio-inline input[type=radio],.checkbox input[type=checkbox],.checkbox-inline input[type=checkbox]{position:absolute;margin-top:.25rem;margin-left:-1.25rem}.radio+.radio,.checkbox+.checkbox{margin-top:-.25rem}.radio-inline,.checkbox-inline{position:relative;display:inline-block;padding-left:1.25rem;margin-bottom:0;vertical-align:middle;cursor:pointer}.radio-inline+.radio-inline,.checkbox-inline+.checkbox-inline{margin-top:0;margin-left:.75rem}input[type=radio]:disabled,input[type=radio].disabled,input[type=checkbox]:disabled,input[type=checkbox].disabled{cursor:not-allowed}.radio-inline.disabled,.checkbox-inline.disabled{cursor:not-allowed}.radio.disabled label,.checkbox.disabled label{cursor:not-allowed}.form-control-success,.form-control-warning,.form-control-danger{padding-right:2.25rem;background-repeat:no-repeat;background-position:center right .625rem;background-size:1.25rem 1.25rem}.has-success .text-help,.has-success .form-control-label,.has-success .radio,.has-success .checkbox,.has-success .radio-inline,.has-success .checkbox-inline,.has-success.radio label,.has-success.checkbox label,.has-success.radio-inline label,.has-success.checkbox-inline label,.has-success .custom-control{color:#5cb85c}.has-success .form-control{border-color:#5cb85c}.has-success .input-group-addon{color:#5cb85c;border-color:#5cb85c;background-color:#eaf6ea}.has-success .form-control-feedback{color:#5cb85c}.has-success .form-control-success{background-image:url(data:image/svg+xml;charset=utf8;base64,PHN2ZyB4bWxucz0naHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmcnIHZpZXdCb3g9JzAgMCA4IDgnPjxwYXRoIGZpbGw9JyM1Y2I4NWMnIGQ9J00yLjMgNi43M0wuNiA0LjUzYy0uNC0xLjA0LjQ2LTEuNCAxLjEtLjhsMS4xIDEuNCAzLjQtMy44Yy42LS42MyAxLjYtLjI3IDEuMi43bC00IDQuNmMtLjQzLjUtLjguNC0xLjEuMXonLz48L3N2Zz4=)}.has-warning .text-help,.has-warning .form-control-label,.has-warning .radio,.has-warning .checkbox,.has-warning .radio-inline,.has-warning .checkbox-inline,.has-warning.radio label,.has-warning.checkbox label,.has-warning.radio-inline label,.has-warning.checkbox-inline label,.has-warning .custom-control{color:#f0ad4e}.has-warning .form-control{border-color:#f0ad4e}.has-warning .input-group-addon{color:#f0ad4e;border-color:#f0ad4e;background-color:#fff}.has-warning .form-control-feedback{color:#f0ad4e}.has-warning .form-control-warning{background-image:url(data:image/svg+xml;charset=utf8;base64,PHN2ZyB4bWxucz0naHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmcnIHZpZXdCb3g9JzAgMCA4IDgnPjxwYXRoIGZpbGw9JyNmMGFkNGUnIGQ9J000LjQgNS4zMjRoLS44di0yLjQ2aC44em0wIDEuNDJoLS44VjUuODloLjh6TTMuNzYuNjNMLjA0IDcuMDc1Yy0uMTE1LjIuMDE2LjQyNS4yNi40MjZoNy4zOTdjLjI0MiAwIC4zNzItLjIyNi4yNTgtLjQyNkM2LjcyNiA0LjkyNCA1LjQ3IDIuNzkgNC4yNTMuNjNjLS4xMTMtLjE3NC0uMzktLjE3NC0uNDk0IDB6Jy8+PC9zdmc+)}.has-danger .text-help,.has-danger .form-control-label,.has-danger .radio,.has-danger .checkbox,.has-danger .radio-inline,.has-danger .checkbox-inline,.has-danger.radio label,.has-danger.checkbox label,.has-danger.radio-inline label,.has-danger.checkbox-inline label,.has-danger .custom-control{color:#b85c5c}.has-danger .form-control{border-color:#b85c5c}.has-danger .input-group-addon{color:#b85c5c;border-color:#b85c5c;background-color:#f6eaea}.has-danger .form-control-feedback{color:#b85c5c}.has-danger .form-control-danger{background-image:url(data:image/svg+xml;charset=utf8;base64,PHN2ZyB4bWxucz0naHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmcnIGZpbGw9JyNkOTUzNGYnIHZpZXdCb3g9Jy0yIC0yIDcgNyc+PHBhdGggc3Ryb2tlPScjZDk1MzRmJyBkPSdNMCAwbDMgM20wLTNMMCAzJy8+PGNpcmNsZSByPScuNScvPjxjaXJjbGUgY3g9JzMnIHI9Jy41Jy8+PGNpcmNsZSBjeT0nMycgcj0nLjUnLz48Y2lyY2xlIGN4PSczJyBjeT0nMycgcj0nLjUnLz48L3N2Zz4=)}@media(min-width:544px){.form-inline .form-group{display:inline-block;margin-bottom:0;vertical-align:middle}.form-inline .form-control{display:inline-block;width:auto;vertical-align:middle}.form-inline .form-control-static{display:inline-block}.form-inline .input-group{display:inline-table;vertical-align:middle}.form-inline .input-group .input-group-addon,.form-inline .input-group .input-group-btn,.form-inline .input-group .form-control{width:auto}.form-inline .input-group>.form-control{width:100%}.form-inline .form-control-label{margin-bottom:0;vertical-align:middle}.form-inline .radio,.form-inline .checkbox{display:inline-block;margin-top:0;margin-bottom:0;vertical-align:middle}.form-inline .radio label,.form-inline .checkbox label{padding-left:0}.form-inline .radio input[type=radio],.form-inline .checkbox input[type=checkbox]{position:relative;margin-left:0}.form-inline .has-feedback .form-control-feedback{top:0}}.btn{display:inline-block;font-weight:400;line-height:1.25;text-align:center;white-space:nowrap;vertical-align:middle;cursor:pointer;user-select:none;border:1px solid transparent;padding:.5rem 1rem;font-size:1rem;border-radius:.25rem}.btn:focus,.btn.focus,.btn:active:focus,.btn:active.focus,.btn.active:focus,.btn.active.focus{outline:thin dotted;outline:5px auto -webkit-focus-ring-color;outline-offset:-2px}.btn:focus,.btn:hover{text-decoration:none}.btn.focus{text-decoration:none}.btn:active,.btn.active{background-image:none;outline:0}.btn.disabled,.btn:disabled{cursor:not-allowed;opacity:.65}a.btn.disabled,fieldset[disabled] a.btn{pointer-events:none}.btn-primary{color:#fff;background-color:#5cb85c;border-color:#5cb85c}.btn-primary:hover{color:#fff;background-color:#449d44;border-color:#419641}.btn-primary:focus,.btn-primary.focus{color:#fff;background-color:#449d44;border-color:#419641}.btn-primary:active,.btn-primary.active,.open>.btn-primary.dropdown-toggle{color:#fff;background-color:#449d44;border-color:#419641;background-image:none}.btn-primary:active:hover,.btn-primary:active:focus,.btn-primary:active.focus,.btn-primary.active:hover,.btn-primary.active:focus,.btn-primary.active.focus,.open>.btn-primary.dropdown-toggle:hover,.open>.btn-primary.dropdown-toggle:focus,.open>.btn-primary.dropdown-toggle.focus{color:#fff;background-color:#398439;border-color:#2d672d}.btn-primary.disabled:focus,.btn-primary.disabled.focus,.btn-primary:disabled:focus,.btn-primary:disabled.focus{background-color:#5cb85c;border-color:#5cb85c}.btn-primary.disabled:hover,.btn-primary:disabled:hover{background-color:#5cb85c;border-color:#5cb85c}.btn-secondary{color:#373a3c;background-color:#fff;border-color:#ccc}.btn-secondary:hover{color:#373a3c;background-color:#e6e6e6;border-color:#adadad}.btn-secondary:focus,.btn-secondary.focus{color:#373a3c;background-color:#e6e6e6;border-color:#adadad}.btn-secondary:active,.btn-secondary.active,.open>.btn-secondary.dropdown-toggle{color:#373a3c;background-color:#e6e6e6;border-color:#adadad;background-image:none}.btn-secondary:active:hover,.btn-secondary:active:focus,.btn-secondary:active.focus,.btn-secondary.active:hover,.btn-secondary.active:focus,.btn-secondary.active.focus,.open>.btn-secondary.dropdown-toggle:hover,.open>.btn-secondary.dropdown-toggle:focus,.open>.btn-secondary.dropdown-toggle.focus{color:#373a3c;background-color:#d4d4d4;border-color:#8c8c8c}.btn-secondary.disabled:focus,.btn-secondary.disabled.focus,.btn-secondary:disabled:focus,.btn-secondary:disabled.focus{background-color:#fff;border-color:#ccc}.btn-secondary.disabled:hover,.btn-secondary:disabled:hover{background-color:#fff;border-color:#ccc}.btn-info{color:#fff;background-color:#5bc0de;border-color:#5bc0de}.btn-info:hover{color:#fff;background-color:#31b0d5;border-color:#2aabd2}.btn-info:focus,.btn-info.focus{color:#fff;background-color:#31b0d5;border-color:#2aabd2}.btn-info:active,.btn-info.active,.open>.btn-info.dropdown-toggle{color:#fff;background-color:#31b0d5;border-color:#2aabd2;background-image:none}.btn-info:active:hover,.btn-info:active:focus,.btn-info:active.focus,.btn-info.active:hover,.btn-info.active:focus,.btn-info.active.focus,.open>.btn-info.dropdown-toggle:hover,.open>.btn-info.dropdown-toggle:focus,.open>.btn-info.dropdown-toggle.focus{color:#fff;background-color:#269abc;border-color:#1f7e9a}.btn-info.disabled:focus,.btn-info.disabled.focus,.btn-info:disabled:focus,.btn-info:disabled.focus{background-color:#5bc0de;border-color:#5bc0de}.btn-info.disabled:hover,.btn-info:disabled:hover{background-color:#5bc0de;border-color:#5bc0de}.btn-success{color:#fff;background-color:#5cb85c;border-color:#5cb85c}.btn-success:hover{color:#fff;background-color:#449d44;border-color:#419641}.btn-success:focus,.btn-success.focus{color:#fff;background-color:#449d44;border-color:#419641}.btn-success:active,.btn-success.active,.open>.btn-success.dropdown-toggle{color:#fff;background-color:#449d44;border-color:#419641;background-image:none}.btn-success:active:hover,.btn-success:active:focus,.btn-success:active.focus,.btn-success.active:hover,.btn-success.active:focus,.btn-success.active.focus,.open>.btn-success.dropdown-toggle:hover,.open>.btn-success.dropdown-toggle:focus,.open>.btn-success.dropdown-toggle.focus{color:#fff;background-color:#398439;border-color:#2d672d}.btn-success.disabled:focus,.btn-success.disabled.focus,.btn-success:disabled:focus,.btn-success:disabled.focus{background-color:#5cb85c;border-color:#5cb85c}.btn-success.disabled:hover,.btn-success:disabled:hover{background-color:#5cb85c;border-color:#5cb85c}.btn-warning{color:#fff;background-color:#f0ad4e;border-color:#f0ad4e}.btn-warning:hover{color:#fff;background-color:#ec971f;border-color:#eb9316}.btn-warning:focus,.btn-warning.focus{color:#fff;background-color:#ec971f;border-color:#eb9316}.btn-warning:active,.btn-warning.active,.open>.btn-warning.dropdown-toggle{color:#fff;background-color:#ec971f;border-color:#eb9316;background-image:none}.btn-warning:active:hover,.btn-warning:active:focus,.btn-warning:active.focus,.btn-warning.active:hover,.btn-warning.active:focus,.btn-warning.active.focus,.open>.btn-warning.dropdown-toggle:hover,.open>.btn-warning.dropdown-toggle:focus,.open>.btn-warning.dropdown-toggle.focus{color:#fff;background-color:#d58512;border-color:#b06d0f}.btn-warning.disabled:focus,.btn-warning.disabled.focus,.btn-warning:disabled:focus,.btn-warning:disabled.focus{background-color:#f0ad4e;border-color:#f0ad4e}.btn-warning.disabled:hover,.btn-warning:disabled:hover{background-color:#f0ad4e;border-color:#f0ad4e}.btn-danger{color:#fff;background-color:#b85c5c;border-color:#b85c5c}.btn-danger:hover{color:#fff;background-color:#9d4444;border-color:#964141}.btn-danger:focus,.btn-danger.focus{color:#fff;background-color:#9d4444;border-color:#964141}.btn-danger:active,.btn-danger.active,.open>.btn-danger.dropdown-toggle{color:#fff;background-color:#9d4444;border-color:#964141;background-image:none}.btn-danger:active:hover,.btn-danger:active:focus,.btn-danger:active.focus,.btn-danger.active:hover,.btn-danger.active:focus,.btn-danger.active.focus,.open>.btn-danger.dropdown-toggle:hover,.open>.btn-danger.dropdown-toggle:focus,.open>.btn-danger.dropdown-toggle.focus{color:#fff;background-color:#843939;border-color:#672d2d}.btn-danger.disabled:focus,.btn-danger.disabled.focus,.btn-danger:disabled:focus,.btn-danger:disabled.focus{background-color:#b85c5c;border-color:#b85c5c}.btn-danger.disabled:hover,.btn-danger:disabled:hover{background-color:#b85c5c;border-color:#b85c5c}.btn-outline-primary{color:#5cb85c;background-image:none;background-color:transparent;border-color:#5cb85c}.btn-outline-primary:hover{color:#fff;background-color:#5cb85c;border-color:#5cb85c}.btn-outline-primary:focus,.btn-outline-primary.focus{color:#fff;background-color:#5cb85c;border-color:#5cb85c}.btn-outline-primary:active,.btn-outline-primary.active,.open>.btn-outline-primary.dropdown-toggle{color:#fff;background-color:#5cb85c;border-color:#5cb85c}.btn-outline-primary:active:hover,.btn-outline-primary:active:focus,.btn-outline-primary:active.focus,.btn-outline-primary.active:hover,.btn-outline-primary.active:focus,.btn-outline-primary.active.focus,.open>.btn-outline-primary.dropdown-toggle:hover,.open>.btn-outline-primary.dropdown-toggle:focus,.open>.btn-outline-primary.dropdown-toggle.focus{color:#fff;background-color:#398439;border-color:#2d672d}.btn-outline-primary.disabled:focus,.btn-outline-primary.disabled.focus,.btn-outline-primary:disabled:focus,.btn-outline-primary:disabled.focus{border-color:#a3d7a3}.btn-outline-primary.disabled:hover,.btn-outline-primary:disabled:hover{border-color:#a3d7a3}.btn-outline-secondary{color:#ccc;background-image:none;background-color:transparent;border-color:#ccc}.btn-outline-secondary:hover{color:#fff;background-color:#ccc;border-color:#ccc}.btn-outline-secondary:focus,.btn-outline-secondary.focus{color:#fff;background-color:#ccc;border-color:#ccc}.btn-outline-secondary:active,.btn-outline-secondary.active,.open>.btn-outline-secondary.dropdown-toggle{color:#fff;background-color:#ccc;border-color:#ccc}.btn-outline-secondary:active:hover,.btn-outline-secondary:active:focus,.btn-outline-secondary:active.focus,.btn-outline-secondary.active:hover,.btn-outline-secondary.active:focus,.btn-outline-secondary.active.focus,.open>.btn-outline-secondary.dropdown-toggle:hover,.open>.btn-outline-secondary.dropdown-toggle:focus,.open>.btn-outline-secondary.dropdown-toggle.focus{color:#fff;background-color:#a1a1a1;border-color:#8c8c8c}.btn-outline-secondary.disabled:focus,.btn-outline-secondary.disabled.focus,.btn-outline-secondary:disabled:focus,.btn-outline-secondary:disabled.focus{border-color:#fff}.btn-outline-secondary.disabled:hover,.btn-outline-secondary:disabled:hover{border-color:#fff}.btn-outline-info{color:#5bc0de;background-image:none;background-color:transparent;border-color:#5bc0de}.btn-outline-info:hover{color:#fff;background-color:#5bc0de;border-color:#5bc0de}.btn-outline-info:focus,.btn-outline-info.focus{color:#fff;background-color:#5bc0de;border-color:#5bc0de}.btn-outline-info:active,.btn-outline-info.active,.open>.btn-outline-info.dropdown-toggle{color:#fff;background-color:#5bc0de;border-color:#5bc0de}.btn-outline-info:active:hover,.btn-outline-info:active:focus,.btn-outline-info:active.focus,.btn-outline-info.active:hover,.btn-outline-info.active:focus,.btn-outline-info.active.focus,.open>.btn-outline-info.dropdown-toggle:hover,.open>.btn-outline-info.dropdown-toggle:focus,.open>.btn-outline-info.dropdown-toggle.focus{color:#fff;background-color:#269abc;border-color:#1f7e9a}.btn-outline-info.disabled:focus,.btn-outline-info.disabled.focus,.btn-outline-info:disabled:focus,.btn-outline-info:disabled.focus{border-color:#b0e1ef}.btn-outline-info.disabled:hover,.btn-outline-info:disabled:hover{border-color:#b0e1ef}.btn-outline-success{color:#5cb85c;background-image:none;background-color:transparent;border-color:#5cb85c}.btn-outline-success:hover{color:#fff;background-color:#5cb85c;border-color:#5cb85c}.btn-outline-success:focus,.btn-outline-success.focus{color:#fff;background-color:#5cb85c;border-color:#5cb85c}.btn-outline-success:active,.btn-outline-success.active,.open>.btn-outline-success.dropdown-toggle{color:#fff;background-color:#5cb85c;border-color:#5cb85c}.btn-outline-success:active:hover,.btn-outline-success:active:focus,.btn-outline-success:active.focus,.btn-outline-success.active:hover,.btn-outline-success.active:focus,.btn-outline-success.active.focus,.open>.btn-outline-success.dropdown-toggle:hover,.open>.btn-outline-success.dropdown-toggle:focus,.open>.btn-outline-success.dropdown-toggle.focus{color:#fff;background-color:#398439;border-color:#2d672d}.btn-outline-success.disabled:focus,.btn-outline-success.disabled.focus,.btn-outline-success:disabled:focus,.btn-outline-success:disabled.focus{border-color:#a3d7a3}.btn-outline-success.disabled:hover,.btn-outline-success:disabled:hover{border-color:#a3d7a3}.btn-outline-warning{color:#f0ad4e;background-image:none;background-color:transparent;border-color:#f0ad4e}.btn-outline-warning:hover{color:#fff;background-color:#f0ad4e;border-color:#f0ad4e}.btn-outline-warning:focus,.btn-outline-warning.focus{color:#fff;background-color:#f0ad4e;border-color:#f0ad4e}.btn-outline-warning:active,.btn-outline-warning.active,.open>.btn-outline-warning.dropdown-toggle{color:#fff;background-color:#f0ad4e;border-color:#f0ad4e}.btn-outline-warning:active:hover,.btn-outline-warning:active:focus,.btn-outline-warning:active.focus,.btn-outline-warning.active:hover,.btn-outline-warning.active:focus,.btn-outline-warning.active.focus,.open>.btn-outline-warning.dropdown-toggle:hover,.open>.btn-outline-warning.dropdown-toggle:focus,.open>.btn-outline-warning.dropdown-toggle.focus{color:#fff;background-color:#d58512;border-color:#b06d0f}.btn-outline-warning.disabled:focus,.btn-outline-warning.disabled.focus,.btn-outline-warning:disabled:focus,.btn-outline-warning:disabled.focus{border-color:#f8d9ac}.btn-outline-warning.disabled:hover,.btn-outline-warning:disabled:hover{border-color:#f8d9ac}.btn-outline-danger{color:#b85c5c;background-image:none;background-color:transparent;border-color:#b85c5c}.btn-outline-danger:hover{color:#fff;background-color:#b85c5c;border-color:#b85c5c}.btn-outline-danger:focus,.btn-outline-danger.focus{color:#fff;background-color:#b85c5c;border-color:#b85c5c}.btn-outline-danger:active,.btn-outline-danger.active,.open>.btn-outline-danger.dropdown-toggle{color:#fff;background-color:#b85c5c;border-color:#b85c5c}.btn-outline-danger:active:hover,.btn-outline-danger:active:focus,.btn-outline-danger:active.focus,.btn-outline-danger.active:hover,.btn-outline-danger.active:focus,.btn-outline-danger.active.focus,.open>.btn-outline-danger.dropdown-toggle:hover,.open>.btn-outline-danger.dropdown-toggle:focus,.open>.btn-outline-danger.dropdown-toggle.focus{color:#fff;background-color:#843939;border-color:#672d2d}.btn-outline-danger.disabled:focus,.btn-outline-danger.disabled.focus,.btn-outline-danger:disabled:focus,.btn-outline-danger:disabled.focus{border-color:#d7a3a3}.btn-outline-danger.disabled:hover,.btn-outline-danger:disabled:hover{border-color:#d7a3a3}.btn-link{font-weight:400;color:#5cb85c;border-radius:0}.btn-link,.btn-link:active,.btn-link.active,.btn-link:disabled{background-color:transparent}.btn-link,.btn-link:focus,.btn-link:active{border-color:transparent}.btn-link:hover{border-color:transparent}.btn-link:focus,.btn-link:hover{color:#3d8b3d;text-decoration:underline;background-color:transparent}.btn-link:disabled:focus,.btn-link:disabled:hover{color:#818a91;text-decoration:none}.btn-lg,.btn-group-lg>.btn{padding:.75rem 1.5rem;font-size:1.25rem;border-radius:.3rem}.btn-sm,.btn-group-sm>.btn{padding:.25rem .5rem;font-size:.875rem;border-radius:.2rem}.btn-block{display:block;width:100%}.btn-block+.btn-block{margin-top:5px}input[type=submit].btn-block,input[type=reset].btn-block,input[type=button].btn-block{width:100%}.fade{opacity:0;transition:opacity .15s linear}.fade.in{opacity:1}.collapse{display:none}.collapse.in{display:block}.collapsing{position:relative;height:0;overflow:hidden;transition-timing-function:ease;transition-duration:.35s;transition-property:height}.dropup,.dropdown{position:relative}.dropdown-toggle::after{display:inline-block;width:0;height:0;margin-right:.25rem;margin-left:.25rem;vertical-align:middle;content:"";border-top:.3em solid;border-right:.3em solid transparent;border-left:.3em solid transparent}.dropdown-toggle:focus{outline:0}.dropup .dropdown-toggle::after{border-top:0;border-bottom:.3em solid}.dropdown-menu{position:absolute;top:100%;left:0;z-index:1000;display:none;float:left;min-width:160px;padding:5px 0;margin:2px 0 0;font-size:1rem;color:#373a3c;text-align:left;list-style:none;background-color:#fff;background-clip:padding-box;border:1px solid rgba(0,0,0,.15);border-radius:.25rem}.dropdown-divider{height:1px;margin:.5rem 0;overflow:hidden;background-color:#e5e5e5}.dropdown-item{display:block;width:100%;padding:3px 20px;clear:both;font-weight:400;color:#373a3c;text-align:inherit;white-space:nowrap;background:0 0;border:0}.dropdown-item:focus,.dropdown-item:hover{color:#2b2d2f;text-decoration:none;background-color:#f5f5f5}.dropdown-item.active,.dropdown-item.active:focus,.dropdown-item.active:hover{color:#fff;text-decoration:none;background-color:#5cb85c;outline:0}.dropdown-item.disabled,.dropdown-item.disabled:focus,.dropdown-item.disabled:hover{color:#818a91}.dropdown-item.disabled:focus,.dropdown-item.disabled:hover{text-decoration:none;cursor:not-allowed;background-color:transparent;background-image:none;filter:"progid:DXImageTransform.Microsoft.gradient(enabled = false)"}.open>.dropdown-menu{display:block}.open>a{outline:0}.dropdown-menu-right{right:0;left:auto}.dropdown-menu-left{right:auto;left:0}.dropdown-header{display:block;padding:5px 20px;font-size:.875rem;color:#818a91;white-space:nowrap}.dropdown-backdrop{position:fixed;top:0;right:0;bottom:0;left:0;z-index:990}.dropup .caret,.navbar-fixed-bottom .dropdown .caret{content:"";border-top:0;border-bottom:.3em solid}.dropup .dropdown-menu,.navbar-fixed-bottom .dropdown .dropdown-menu{top:auto;bottom:100%;margin-bottom:2px}.btn-group,.btn-group-vertical{position:relative;display:inline-block;vertical-align:middle}.btn-group>.btn,.btn-group-vertical>.btn{position:relative;float:left}.btn-group>.btn:focus,.btn-group>.btn:active,.btn-group>.btn.active,.btn-group-vertical>.btn:focus,.btn-group-vertical>.btn:active,.btn-group-vertical>.btn.active{z-index:2}.btn-group>.btn:hover,.btn-group-vertical>.btn:hover{z-index:2}.btn-group .btn+.btn,.btn-group .btn+.btn-group,.btn-group .btn-group+.btn,.btn-group .btn-group+.btn-group{margin-left:-1px}.btn-toolbar{margin-left:-5px}.btn-toolbar::after{content:"";display:table;clear:both}.btn-toolbar .btn-group,.btn-toolbar .input-group{float:left}.btn-toolbar>.btn,.btn-toolbar>.btn-group,.btn-toolbar>.input-group{margin-left:5px}.btn-group>.btn:not(:first-child):not(:last-child):not(.dropdown-toggle){border-radius:0}.btn-group>.btn:first-child{margin-left:0}.btn-group>.btn:first-child:not(:last-child):not(.dropdown-toggle){border-bottom-right-radius:0;border-top-right-radius:0}.btn-group>.btn:last-child:not(:first-child),.btn-group>.dropdown-toggle:not(:first-child){border-bottom-left-radius:0;border-top-left-radius:0}.btn-group>.btn-group{float:left}.btn-group>.btn-group:not(:first-child):not(:last-child)>.btn{border-radius:0}.btn-group>.btn-group:first-child:not(:last-child)>.btn:last-child,.btn-group>.btn-group:first-child:not(:last-child)>.dropdown-toggle{border-bottom-right-radius:0;border-top-right-radius:0}.btn-group>.btn-group:last-child:not(:first-child)>.btn:first-child{border-bottom-left-radius:0;border-top-left-radius:0}.btn-group .dropdown-toggle:active,.btn-group.open .dropdown-toggle{outline:0}.btn-group>.btn+.dropdown-toggle{padding-right:8px;padding-left:8px}.btn-group>.btn-lg+.dropdown-toggle,.btn-group-lg.btn-group>.btn+.dropdown-toggle{padding-right:12px;padding-left:12px}.btn .caret{margin-left:0}.btn-lg .caret,.btn-group-lg>.btn .caret{border-width:.3em .3em 0;border-bottom-width:0}.dropup .btn-lg .caret,.dropup .btn-group-lg>.btn .caret{border-width:0 .3em .3em}.btn-group-vertical>.btn,.btn-group-vertical>.btn-group,.btn-group-vertical>.btn-group>.btn{display:block;float:none;width:100%;max-width:100%}.btn-group-vertical>.btn-group::after{content:"";display:table;clear:both}.btn-group-vertical>.btn-group>.btn{float:none}.btn-group-vertical>.btn+.btn,.btn-group-vertical>.btn+.btn-group,.btn-group-vertical>.btn-group+.btn,.btn-group-vertical>.btn-group+.btn-group{margin-top:-1px;margin-left:0}.btn-group-vertical>.btn:not(:first-child):not(:last-child){border-radius:0}.btn-group-vertical>.btn:first-child:not(:last-child){border-bottom-right-radius:0;border-bottom-left-radius:0}.btn-group-vertical>.btn:last-child:not(:first-child){border-top-right-radius:0;border-top-left-radius:0}.btn-group-vertical>.btn-group:not(:first-child):not(:last-child)>.btn{border-radius:0}.btn-group-vertical>.btn-group:first-child:not(:last-child)>.btn:last-child,.btn-group-vertical>.btn-group:first-child:not(:last-child)>.dropdown-toggle{border-bottom-right-radius:0;border-bottom-left-radius:0}.btn-group-vertical>.btn-group:last-child:not(:first-child)>.btn:first-child{border-top-right-radius:0;border-top-left-radius:0}[data-toggle=buttons]>.btn input[type=radio],[data-toggle=buttons]>.btn input[type=checkbox],[data-toggle=buttons]>.btn-group>.btn input[type=radio],[data-toggle=buttons]>.btn-group>.btn input[type=checkbox]{position:absolute;clip:rect(0,0,0,0);pointer-events:none}.input-group{position:relative;width:100%;display:flex}.input-group .form-control{position:relative;z-index:2;flex:1;margin-bottom:0}.input-group .form-control:focus,.input-group .form-control:active,.input-group .form-control:hover{z-index:3}.input-group-addon:not(:first-child):not(:last-child),.input-group-btn:not(:first-child):not(:last-child),.input-group .form-control:not(:first-child):not(:last-child){border-radius:0}.input-group-addon,.input-group-btn{white-space:nowrap;vertical-align:middle}.input-group-addon{padding:.5rem .75rem;margin-bottom:0;font-size:1rem;font-weight:400;line-height:1.25;color:#55595c;text-align:center;background-color:#eceeef;border:1px solid rgba(0,0,0,.15);border-radius:.25rem}.input-group-addon.form-control-sm,.input-group-sm>.input-group-addon,.input-group-sm>.input-group-btn>.input-group-addon.btn{padding:.25rem .5rem;font-size:.875rem;border-radius:.2rem}.input-group-addon.form-control-lg,.input-group-lg>.input-group-addon,.input-group-lg>.input-group-btn>.input-group-addon.btn{padding:.75rem 1.5rem;font-size:1.25rem;border-radius:.3rem}.input-group-addon input[type=radio],.input-group-addon input[type=checkbox]{margin-top:0}.input-group .form-control:not(:last-child),.input-group-addon:not(:last-child),.input-group-btn:not(:last-child)>.btn,.input-group-btn:not(:last-child)>.btn-group>.btn,.input-group-btn:not(:last-child)>.dropdown-toggle,.input-group-btn:not(:first-child)>.btn:not(:last-child):not(.dropdown-toggle),.input-group-btn:not(:first-child)>.btn-group:not(:last-child)>.btn{border-bottom-right-radius:0;border-top-right-radius:0}.input-group-addon:not(:last-child){border-right:0}.input-group .form-control:not(:first-child),.input-group-addon:not(:first-child),.input-group-btn:not(:first-child)>.btn,.input-group-btn:not(:first-child)>.btn-group>.btn,.input-group-btn:not(:first-child)>.dropdown-toggle,.input-group-btn:not(:last-child)>.btn:not(:first-child),.input-group-btn:not(:last-child)>.btn-group:not(:first-child)>.btn{border-bottom-left-radius:0;border-top-left-radius:0}.form-control+.input-group-addon:not(:first-child){border-left:0}.input-group-btn{position:relative;font-size:0;white-space:nowrap}.input-group-btn>.btn{position:relative}.input-group-btn>.btn+.btn{margin-left:-1px}.input-group-btn>.btn:focus,.input-group-btn>.btn:active,.input-group-btn>.btn:hover{z-index:3}.input-group-btn:not(:last-child)>.btn,.input-group-btn:not(:last-child)>.btn-group{margin-right:-1px}.input-group-btn:not(:first-child)>.btn,.input-group-btn:not(:first-child)>.btn-group{z-index:2;margin-left:-1px}.input-group-btn:not(:first-child)>.btn:focus,.input-group-btn:not(:first-child)>.btn:active,.input-group-btn:not(:first-child)>.btn:hover,.input-group-btn:not(:first-child)>.btn-group:focus,.input-group-btn:not(:first-child)>.btn-group:active,.input-group-btn:not(:first-child)>.btn-group:hover{z-index:3}.custom-control{position:relative;display:inline;padding-left:1.5rem;cursor:pointer}.custom-control+.custom-control{margin-left:1rem}.custom-control-input{position:absolute;z-index:-1;opacity:0}.custom-control-input:checked~.custom-control-indicator{color:#fff;background-color:#0074d9}.custom-control-input:focus~.custom-control-indicator{box-shadow:0 0 0 .075rem #fff,0 0 0 .2rem #0074d9}.custom-control-input:active~.custom-control-indicator{color:#fff;background-color:#84c6ff}.custom-control-input:disabled~.custom-control-indicator{cursor:not-allowed;background-color:#eee}.custom-control-input:disabled~.custom-control-description{color:#767676;cursor:not-allowed}.custom-control-indicator{position:absolute;top:.0625rem;left:0;display:block;width:1rem;height:1rem;pointer-events:none;user-select:none;background-color:#ddd;background-repeat:no-repeat;background-position:center center;background-size:50% 50%}.custom-checkbox .custom-control-indicator{border-radius:.25rem}.custom-checkbox .custom-control-input:checked~.custom-control-indicator{background-image:url(data:image/svg+xml;charset=utf8;base64,PHN2ZyB4bWxucz0naHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmcnIHZpZXdCb3g9JzAgMCA4IDgnPjxwYXRoIGZpbGw9JyNmZmYnIGQ9J002LjU2NC43NWwtMy41OSAzLjYxMi0xLjUzOC0xLjU1TDAgNC4yNiAyLjk3NCA3LjI1IDggMi4xOTN6Jy8+PC9zdmc+)}.custom-checkbox .custom-control-input:indeterminate~.custom-control-indicator{background-color:#0074d9;background-image:url(data:image/svg+xml;charset=utf8;base64,PHN2ZyB4bWxucz0naHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmcnIHZpZXdCb3g9JzAgMCA0IDQnPjxwYXRoIHN0cm9rZT0nI2ZmZicgZD0nTTAgMmg0Jy8+PC9zdmc+)}.custom-radio .custom-control-indicator{border-radius:50%}.custom-radio .custom-control-input:checked~.custom-control-indicator{background-image:url(data:image/svg+xml;charset=utf8;base64,PHN2ZyB4bWxucz0naHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmcnIHZpZXdCb3g9Jy00IC00IDggOCc+PGNpcmNsZSByPSczJyBmaWxsPScjZmZmJy8+PC9zdmc+)}.custom-controls-stacked .custom-control{display:inline}.custom-controls-stacked .custom-control::after{display:block;margin-bottom:.25rem;content:""}.custom-controls-stacked .custom-control+.custom-control{margin-left:0}.custom-select{display:inline-block;max-width:100%;padding:.375rem 1.75rem .375rem .75rem;padding-right:.75rem \9;color:#55595c;vertical-align:middle;background:#fff url(data:image/svg+xml;charset=utf8;base64,PHN2ZyB4bWxucz0naHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmcnIHZpZXdCb3g9JzAgMCA0IDUnPjxwYXRoIGZpbGw9JyMzMzMnIGQ9J00yIDBMMCAyaDR6bTAgNUwwIDNoNHonLz48L3N2Zz4=) no-repeat right .75rem center;background-image:none \9;background-size:8px 10px;border:1px solid rgba(0,0,0,.15);border-radius:.25rem;-moz-appearance:none;-webkit-appearance:none}.custom-select:focus{border-color:#51a7e8;outline:none}.custom-select::-ms-expand{opacity:0}.custom-select-sm{padding-top:.375rem;padding-bottom:.375rem;font-size:75%}.custom-file{position:relative;display:inline-block;max-width:100%;height:2.5rem;cursor:pointer}.custom-file-input{min-width:14rem;max-width:100%;margin:0;filter:alpha(opacity=0);opacity:0}.custom-file-control{position:absolute;top:0;right:0;left:0;z-index:5;height:2.5rem;padding:.5rem 1rem;line-height:1.5;color:#555;user-select:none;background-color:#fff;border:1px solid #ddd;border-radius:.25rem}.custom-file-control:lang(en)::after{content:"Choose file..."}.custom-file-control::before{position:absolute;top:-1px;right:-1px;bottom:-1px;z-index:6;display:block;height:2.5rem;padding:.5rem 1rem;line-height:1.5;color:#555;background-color:#eee;border:1px solid #ddd;border-radius:0 .25rem .25rem 0}.custom-file-control:lang(en)::before{content:"Browse"}.nav{padding-left:0;margin-bottom:0;list-style:none}.nav-link{display:inline-block}.nav-link:focus,.nav-link:hover{text-decoration:none}.nav-link.disabled{color:#818a91}.nav-link.disabled,.nav-link.disabled:focus,.nav-link.disabled:hover{color:#818a91;cursor:not-allowed;background-color:transparent}.nav-inline .nav-item{display:inline-block}.nav-inline .nav-item+.nav-item,.nav-inline .nav-link+.nav-link{margin-left:1rem}.nav-tabs{border-bottom:1px solid #ddd}.nav-tabs::after{content:"";display:table;clear:both}.nav-tabs .nav-item{float:left;margin-bottom:-1px}.nav-tabs .nav-item+.nav-item{margin-left:.2rem}.nav-tabs .nav-link{display:block;padding:.5em 1em;border:1px solid transparent;border-radius:.25rem .25rem 0 0}.nav-tabs .nav-link:focus,.nav-tabs .nav-link:hover{border-color:#eceeef #eceeef #ddd}.nav-tabs .nav-link.disabled,.nav-tabs .nav-link.disabled:focus,.nav-tabs .nav-link.disabled:hover{color:#818a91;background-color:transparent;border-color:transparent}.nav-tabs .nav-link.active,.nav-tabs .nav-link.active:focus,.nav-tabs .nav-link.active:hover,.nav-tabs .nav-item.open .nav-link,.nav-tabs .nav-item.open .nav-link:focus,.nav-tabs .nav-item.open .nav-link:hover{color:#55595c;background-color:#fff;border-color:#ddd #ddd transparent}.nav-tabs .dropdown-menu{margin-top:-1px;border-top-right-radius:0;border-top-left-radius:0}.nav-pills::after{content:"";display:table;clear:both}.nav-pills .nav-item{float:left}.nav-pills .nav-item+.nav-item{margin-left:.2rem}.nav-pills .nav-link{display:block;padding:.5em 1em;border-radius:.25rem}.nav-pills .nav-link.active,.nav-pills .nav-link.active:focus,.nav-pills .nav-link.active:hover,.nav-pills .nav-item.open .nav-link,.nav-pills .nav-item.open .nav-link:focus,.nav-pills .nav-item.open .nav-link:hover{color:#fff;cursor:default;background-color:#5cb85c}.nav-stacked .nav-item{display:block;float:none}.nav-stacked .nav-item+.nav-item{margin-top:.2rem;margin-left:0}.tab-content>.tab-pane{display:none}.tab-content>.active{display:block}.navbar{position:relative;padding:.5rem 1rem}.navbar::after{content:"";display:table;clear:both}@media(min-width:544px){.navbar{border-radius:.25rem}}.navbar-full{z-index:1000}@media(min-width:544px){.navbar-full{border-radius:0}}.navbar-fixed-top,.navbar-fixed-bottom{position:fixed;right:0;left:0;z-index:1030}@media(min-width:544px){.navbar-fixed-top,.navbar-fixed-bottom{border-radius:0}}.navbar-fixed-top{top:0}.navbar-fixed-bottom{bottom:0}.navbar-sticky-top{position:sticky;top:0;z-index:1030;width:100%}@media(min-width:544px){.navbar-sticky-top{border-radius:0}}.navbar-brand{float:left;padding-top:.25rem;padding-bottom:.25rem;margin-right:1rem;font-size:1.25rem}.navbar-brand:focus,.navbar-brand:hover{text-decoration:none}.navbar-brand>img{display:block}.navbar-divider{float:left;width:1px;padding-top:.425rem;padding-bottom:.425rem;margin-right:1rem;margin-left:1rem;overflow:hidden}.navbar-divider::before{content:"\00a0"}.navbar-toggler{padding:.5rem .75rem;font-size:1.25rem;line-height:1;background:0 0;border:1px solid transparent;border-radius:.25rem}.navbar-toggler:focus,.navbar-toggler:hover{text-decoration:none}@media(min-width:544px){.navbar-toggleable-xs{display:block!important}}@media(min-width:768px){.navbar-toggleable-sm{display:block!important}}@media(min-width:992px){.navbar-toggleable-md{display:block!important}}.navbar-nav .nav-item{float:left}.navbar-nav .nav-link{display:block;padding-top:.425rem;padding-bottom:.425rem}.navbar-nav .nav-link+.nav-link{margin-left:1rem}.navbar-nav .nav-item+.nav-item{margin-left:1rem}.navbar-light .navbar-brand{color:rgba(0,0,0,.8)}.navbar-light .navbar-brand:focus,.navbar-light .navbar-brand:hover{color:rgba(0,0,0,.8)}.navbar-light .navbar-nav .nav-link{color:rgba(0,0,0,.3)}.navbar-light .navbar-nav .nav-link:focus,.navbar-light .navbar-nav .nav-link:hover{color:rgba(0,0,0,.6)}.navbar-light .navbar-nav .open>.nav-link,.navbar-light .navbar-nav .open>.nav-link:focus,.navbar-light .navbar-nav .open>.nav-link:hover,.navbar-light .navbar-nav .active>.nav-link,.navbar-light .navbar-nav .active>.nav-link:focus,.navbar-light .navbar-nav .active>.nav-link:hover,.navbar-light .navbar-nav .nav-link.open,.navbar-light .navbar-nav .nav-link.open:focus,.navbar-light .navbar-nav .nav-link.open:hover,.navbar-light .navbar-nav .nav-link.active,.navbar-light .navbar-nav .nav-link.active:focus,.navbar-light .navbar-nav .nav-link.active:hover{color:rgba(0,0,0,.8)}.navbar-light .navbar-divider{background-color:rgba(0,0,0,.075)}.navbar-dark .navbar-brand{color:#fff}.navbar-dark .navbar-brand:focus,.navbar-dark .navbar-brand:hover{color:#fff}.navbar-dark .navbar-nav .nav-link{color:rgba(255,255,255,.5)}.navbar-dark .navbar-nav .nav-link:focus,.navbar-dark .navbar-nav .nav-link:hover{color:rgba(255,255,255,.75)}.navbar-dark .navbar-nav .open>.nav-link,.navbar-dark .navbar-nav .open>.nav-link:focus,.navbar-dark .navbar-nav .open>.nav-link:hover,.navbar-dark .navbar-nav .active>.nav-link,.navbar-dark .navbar-nav .active>.nav-link:focus,.navbar-dark .navbar-nav .active>.nav-link:hover,.navbar-dark .navbar-nav .nav-link.open,.navbar-dark .navbar-nav .nav-link.open:focus,.navbar-dark .navbar-nav .nav-link.open:hover,.navbar-dark .navbar-nav .nav-link.active,.navbar-dark .navbar-nav .nav-link.active:focus,.navbar-dark .navbar-nav .nav-link.active:hover{color:#fff}.navbar-dark .navbar-divider{background-color:rgba(255,255,255,.075)}.card{position:relative;display:block;margin-bottom:.75rem;background-color:#fff;border-radius:.25rem;box-shadow:inset 0 0 0 1px rgba(0,0,0,.125)}.card-block{padding:1.25rem}.card-block::after{content:"";display:table;clear:both}.card-title{margin-bottom:.75rem}.card-subtitle{margin-top:-.375rem;margin-bottom:0}.card-text:last-child{margin-bottom:0}.card-link:hover{text-decoration:none}.card-link+.card-link{margin-left:1.25rem}.card>.list-group:first-child .list-group-item:first-child{border-top-left-radius:.25rem;border-top-right-radius:.25rem}.card>.list-group:last-child .list-group-item:last-child{border-bottom-right-radius:.25rem;border-bottom-left-radius:.25rem}.card-header{padding:.75rem 1.25rem;background-color:#f5f5f5;box-shadow:inset 0 0 0 1px rgba(0,0,0,.125)}.card-header::after{content:"";display:table;clear:both}.card-header:first-child{border-radius:.25rem .25rem 0 0}.card-footer{padding:.75rem 1.25rem;background-color:#f5f5f5;box-shadow:inset 0 0 0 1px rgba(0,0,0,.125)}.card-footer::after{content:"";display:table;clear:both}.card-footer:last-child{border-radius:0 0 .25rem .25rem}.card-header-tabs{margin-right:-.625rem;margin-bottom:-.75rem;margin-left:-.625rem;border-bottom:0}.card-header-tabs .nav-item{margin-bottom:0}.card-header-pills{margin-right:-.625rem;margin-left:-.625rem}.card-primary{background-color:#5cb85c;border-color:#5cb85c}.card-success{background-color:#5cb85c;border-color:#5cb85c}.card-info{background-color:#5bc0de;border-color:#5bc0de}.card-warning{background-color:#f0ad4e;border-color:#f0ad4e}.card-danger{background-color:#b85c5c;border-color:#b85c5c}.card-outline-primary{background-color:transparent;border-color:#5cb85c}.card-outline-secondary{background-color:transparent;border-color:#ccc}.card-outline-info{background-color:transparent;border-color:#5bc0de}.card-outline-success{background-color:transparent;border-color:#5cb85c}.card-outline-warning{background-color:transparent;border-color:#f0ad4e}.card-outline-danger{background-color:transparent;border-color:#b85c5c}.card-inverse .card-header,.card-inverse .card-footer{border-bottom:1px solid rgba(255,255,255,.2)}.card-inverse .card-header,.card-inverse .card-footer,.card-inverse .card-title,.card-inverse .card-blockquote{color:#fff}.card-inverse .card-link,.card-inverse .card-text,.card-inverse .card-blockquote>footer{color:rgba(255,255,255,.65)}.card-inverse .card-link:focus,.card-inverse .card-link:hover{color:#fff}.card-blockquote{padding:0;margin-bottom:0;border-left:0}.card-img{border-radius:.25rem}.card-img-overlay{position:absolute;top:0;right:0;bottom:0;left:0;padding:1.25rem}.card-img-top{border-radius:.25rem .25rem 0 0}.card-img-bottom{border-radius:0 0 .25rem .25rem}@media(min-width:544px){.card-deck{display:flex;flex-flow:row wrap;margin-right:-.625rem;margin-left:-.625rem}.card-deck .card{flex:1 0 0;margin-right:.625rem;margin-left:.625rem}}@media(min-width:544px){.card-group{display:flex;flex-flow:row wrap}.card-group .card{flex:1 0 0}.card-group .card+.card{margin-left:0;border-left:0}.card-group .card:first-child{border-bottom-right-radius:0;border-top-right-radius:0}.card-group .card:first-child .card-img-top{border-top-right-radius:0}.card-group .card:first-child .card-img-bottom{border-bottom-right-radius:0}.card-group .card:last-child{border-bottom-left-radius:0;border-top-left-radius:0}.card-group .card:last-child .card-img-top{border-top-left-radius:0}.card-group .card:last-child .card-img-bottom{border-bottom-left-radius:0}.card-group .card:not(:first-child):not(:last-child){border-radius:0}.card-group .card:not(:first-child):not(:last-child) .card-img-top,.card-group .card:not(:first-child):not(:last-child) .card-img-bottom{border-radius:0}}@media(min-width:544px){.card-columns{column-count:3;column-gap:1.25rem}.card-columns .card{display:inline-block;width:100%}}.breadcrumb{padding:.75rem 1rem;margin-bottom:1rem;list-style:none;background-color:#eceeef;border-radius:.25rem}.breadcrumb::after{content:"";display:table;clear:both}.breadcrumb-item{float:left}.breadcrumb-item+.breadcrumb-item::before{display:inline-block;padding-right:.5rem;padding-left:.5rem;color:#818a91;content:"/"}.breadcrumb-item+.breadcrumb-item:hover::before{text-decoration:underline}.breadcrumb-item+.breadcrumb-item:hover::before{text-decoration:none}.breadcrumb-item.active{color:#818a91}.pagination{display:inline-block;padding-left:0;margin-top:1rem;margin-bottom:1rem;border-radius:.25rem}.page-item{display:inline}.page-item:first-child .page-link{margin-left:0;border-bottom-left-radius:.25rem;border-top-left-radius:.25rem}.page-item:last-child .page-link{border-bottom-right-radius:.25rem;border-top-right-radius:.25rem}.page-item.active .page-link,.page-item.active .page-link:focus,.page-item.active .page-link:hover{z-index:2;color:#fff;cursor:default;background-color:#5cb85c;border-color:#5cb85c}.page-item.disabled .page-link,.page-item.disabled .page-link:focus,.page-item.disabled .page-link:hover{color:#818a91;pointer-events:none;cursor:not-allowed;background-color:#fff;border-color:#ddd}.page-link{position:relative;float:left;padding:.5rem .75rem;margin-left:-1px;color:#5cb85c;text-decoration:none;background-color:#fff;border:1px solid #ddd}.page-link:focus,.page-link:hover{color:#3d8b3d;background-color:#eceeef;border-color:#ddd}.pagination-lg .page-link{padding:.75rem 1.5rem;font-size:1.25rem}.pagination-lg .page-item:first-child .page-link{border-bottom-left-radius:.3rem;border-top-left-radius:.3rem}.pagination-lg .page-item:last-child .page-link{border-bottom-right-radius:.3rem;border-top-right-radius:.3rem}.pagination-sm .page-link{padding:.275rem .75rem;font-size:.875rem}.pagination-sm .page-item:first-child .page-link{border-bottom-left-radius:.2rem;border-top-left-radius:.2rem}.pagination-sm .page-item:last-child .page-link{border-bottom-right-radius:.2rem;border-top-right-radius:.2rem}.tag{display:inline-block;padding:.25em .4em;font-size:75%;font-weight:700;line-height:1;color:#fff;text-align:center;white-space:nowrap;vertical-align:baseline;border-radius:.25rem}.tag:empty{display:none}.btn .tag{position:relative;top:-1px}a.tag:focus,a.tag:hover{color:#fff;text-decoration:none;cursor:pointer}.tag-pill{padding-right:.6em;padding-left:.6em;border-radius:10rem}.tag-default{background-color:#818a91}.tag-default[href]:focus,.tag-default[href]:hover{background-color:#687077}.tag-primary{background-color:#5cb85c}.tag-primary[href]:focus,.tag-primary[href]:hover{background-color:#449d44}.tag-success{background-color:#5cb85c}.tag-success[href]:focus,.tag-success[href]:hover{background-color:#449d44}.tag-info{background-color:#5bc0de}.tag-info[href]:focus,.tag-info[href]:hover{background-color:#31b0d5}.tag-warning{background-color:#f0ad4e}.tag-warning[href]:focus,.tag-warning[href]:hover{background-color:#ec971f}.tag-danger{background-color:#b85c5c}.tag-danger[href]:focus,.tag-danger[href]:hover{background-color:#9d4444}.jumbotron{padding:2rem 1rem;margin-bottom:2rem;background-color:#eceeef;border-radius:.3rem}@media(min-width:544px){.jumbotron{padding:4rem 2rem}}.jumbotron-hr{border-top-color:#d0d5d8}.jumbotron-fluid{padding-right:0;padding-left:0;border-radius:0}.alert{padding:15px;margin-bottom:1rem;border:1px solid transparent;border-radius:.25rem}.alert-heading{color:inherit}.alert-link{font-weight:700}.alert-dismissible{padding-right:35px}.alert-dismissible .close{position:relative;top:-2px;right:-21px;color:inherit}.alert-success{background-color:#dff0d8;border-color:#d0e9c6;color:#3c763d}.alert-success hr{border-top-color:#c1e2b3}.alert-success .alert-link{color:#2b542c}.alert-info{background-color:#d9edf7;border-color:#bcdff1;color:#31708f}.alert-info hr{border-top-color:#a6d5ec}.alert-info .alert-link{color:#245269}.alert-warning{background-color:#fcf8e3;border-color:#faf2cc;color:#8a6d3b}.alert-warning hr{border-top-color:#f7ecb5}.alert-warning .alert-link{color:#66512c}.alert-danger{background-color:#f2dede;border-color:#ebcccc;color:#a94442}.alert-danger hr{border-top-color:#e4b9b9}.alert-danger .alert-link{color:#843534}@keyframes progress-bar-stripes{from{background-position:1rem 0}to{background-position:0 0}}.progress{display:block;width:100%;height:1rem;margin-bottom:1rem}.progress[value]{background-color:#eee;border:0;appearance:none;border-radius:.25rem}.progress[value]::-ms-fill{background-color:#0074d9;border:0}.progress[value]::-moz-progress-bar{background-color:#0074d9;border-bottom-left-radius:.25rem;border-top-left-radius:.25rem}.progress[value]::-webkit-progress-value{background-color:#0074d9;border-bottom-left-radius:.25rem;border-top-left-radius:.25rem}.progress[value="100"]::-moz-progress-bar{border-bottom-right-radius:.25rem;border-top-right-radius:.25rem}.progress[value="100"]::-webkit-progress-value{border-bottom-right-radius:.25rem;border-top-right-radius:.25rem}.progress[value]::-webkit-progress-bar{background-color:#eee;border-radius:.25rem}base::-moz-progress-bar,.progress[value]{background-color:#eee;border-radius:.25rem}@media screen and (min-width:0\0){.progress{background-color:#eee;border-radius:.25rem}.progress-bar{display:inline-block;height:1rem;text-indent:-999rem;background-color:#0074d9;border-bottom-left-radius:.25rem;border-top-left-radius:.25rem}.progress[width="100%"]{border-bottom-right-radius:.25rem;border-top-right-radius:.25rem}}.progress-striped[value]::-webkit-progress-value{background-image:linear-gradient(45deg,rgba(255,255,255,0.15) 25%,transparent 25%,transparent 50%,rgba(255,255,255,0.15) 50%,rgba(255,255,255,0.15) 75%,transparent 75%,transparent);background-size:1rem 1rem}.progress-striped[value]::-moz-progress-bar{background-image:linear-gradient(45deg,rgba(255,255,255,0.15) 25%,transparent 25%,transparent 50%,rgba(255,255,255,0.15) 50%,rgba(255,255,255,0.15) 75%,transparent 75%,transparent);background-size:1rem 1rem}.progress-striped[value]::-ms-fill{background-image:linear-gradient(45deg,rgba(255,255,255,0.15) 25%,transparent 25%,transparent 50%,rgba(255,255,255,0.15) 50%,rgba(255,255,255,0.15) 75%,transparent 75%,transparent);background-size:1rem 1rem}@media screen and (min-width:0\0){.progress-bar-striped{background-image:linear-gradient(45deg,rgba(255,255,255,0.15) 25%,transparent 25%,transparent 50%,rgba(255,255,255,0.15) 50%,rgba(255,255,255,0.15) 75%,transparent 75%,transparent);background-size:1rem 1rem}}.progress-animated[value]::-webkit-progress-value{animation:progress-bar-stripes 2s linear infinite}.progress-animated[value]::-moz-progress-bar{animation:progress-bar-stripes 2s linear infinite}@media screen and (min-width:0\0){.progress-animated .progress-bar-striped{animation:progress-bar-stripes 2s linear infinite}}.progress-success[value]::-webkit-progress-value{background-color:#5cb85c}.progress-success[value]::-moz-progress-bar{background-color:#5cb85c}.progress-success[value]::-ms-fill{background-color:#5cb85c}@media screen and (min-width:0\0){.progress-success .progress-bar{background-color:#5cb85c}}.progress-info[value]::-webkit-progress-value{background-color:#5bc0de}.progress-info[value]::-moz-progress-bar{background-color:#5bc0de}.progress-info[value]::-ms-fill{background-color:#5bc0de}@media screen and (min-width:0\0){.progress-info .progress-bar{background-color:#5bc0de}}.progress-warning[value]::-webkit-progress-value{background-color:#f0ad4e}.progress-warning[value]::-moz-progress-bar{background-color:#f0ad4e}.progress-warning[value]::-ms-fill{background-color:#f0ad4e}@media screen and (min-width:0\0){.progress-warning .progress-bar{background-color:#f0ad4e}}.progress-danger[value]::-webkit-progress-value{background-color:#b85c5c}.progress-danger[value]::-moz-progress-bar{background-color:#b85c5c}.progress-danger[value]::-ms-fill{background-color:#b85c5c}@media screen and (min-width:0\0){.progress-danger .progress-bar{background-color:#b85c5c}}.media{display:flex;margin-bottom:1rem}.media-body{flex:1}.media-middle{align-self:center}.media-bottom{align-self:flex-end}.media-object{display:block}.media-object.img-thumbnail{max-width:none}.media-right{padding-left:10px}.media-left{padding-right:10px}.media-heading{margin-top:0;margin-bottom:5px}.media-list{padding-left:0;list-style:none}.list-group{padding-left:0;margin-bottom:0}.list-group-item{position:relative;display:block;padding:.75rem 1.25rem;margin-bottom:-1px;background-color:#fff;border:1px solid #ddd}.list-group-item:first-child{border-top-right-radius:.25rem;border-top-left-radius:.25rem}.list-group-item:last-child{margin-bottom:0;border-bottom-right-radius:.25rem;border-bottom-left-radius:.25rem}.list-group-item.disabled,.list-group-item.disabled:focus,.list-group-item.disabled:hover{color:#818a91;cursor:not-allowed;background-color:#eceeef}.list-group-item.disabled .list-group-item-heading,.list-group-item.disabled:focus .list-group-item-heading,.list-group-item.disabled:hover .list-group-item-heading{color:inherit}.list-group-item.disabled .list-group-item-text,.list-group-item.disabled:focus .list-group-item-text,.list-group-item.disabled:hover .list-group-item-text{color:#818a91}.list-group-item.active,.list-group-item.active:focus,.list-group-item.active:hover{z-index:2;color:#fff;text-decoration:none;background-color:#5cb85c;border-color:#5cb85c}.list-group-item.active .list-group-item-heading,.list-group-item.active .list-group-item-heading>small,.list-group-item.active .list-group-item-heading>.small,.list-group-item.active:focus .list-group-item-heading,.list-group-item.active:focus .list-group-item-heading>small,.list-group-item.active:focus .list-group-item-heading>.small,.list-group-item.active:hover .list-group-item-heading,.list-group-item.active:hover .list-group-item-heading>small,.list-group-item.active:hover .list-group-item-heading>.small{color:inherit}.list-group-item.active .list-group-item-text,.list-group-item.active:focus .list-group-item-text,.list-group-item.active:hover .list-group-item-text{color:#eaf6ea}.list-group-flush .list-group-item{border-radius:0}.list-group-item-action{width:100%;color:#555;text-align:inherit}.list-group-item-action .list-group-item-heading{color:#333}.list-group-item-action:focus,.list-group-item-action:hover{color:#555;text-decoration:none;background-color:#f5f5f5}.list-group-item-success{color:#3c763d;background-color:#dff0d8}a.list-group-item-success,button.list-group-item-success{color:#3c763d}a.list-group-item-success .list-group-item-heading,button.list-group-item-success .list-group-item-heading{color:inherit}a.list-group-item-success:focus,a.list-group-item-success:hover,button.list-group-item-success:focus,button.list-group-item-success:hover{color:#3c763d;background-color:#d0e9c6}a.list-group-item-success.active,a.list-group-item-success.active:focus,a.list-group-item-success.active:hover,button.list-group-item-success.active,button.list-group-item-success.active:focus,button.list-group-item-success.active:hover{color:#fff;background-color:#3c763d;border-color:#3c763d}.list-group-item-info{color:#31708f;background-color:#d9edf7}a.list-group-item-info,button.list-group-item-info{color:#31708f}a.list-group-item-info .list-group-item-heading,button.list-group-item-info .list-group-item-heading{color:inherit}a.list-group-item-info:focus,a.list-group-item-info:hover,button.list-group-item-info:focus,button.list-group-item-info:hover{color:#31708f;background-color:#c4e3f3}a.list-group-item-info.active,a.list-group-item-info.active:focus,a.list-group-item-info.active:hover,button.list-group-item-info.active,button.list-group-item-info.active:focus,button.list-group-item-info.active:hover{color:#fff;background-color:#31708f;border-color:#31708f}.list-group-item-warning{color:#8a6d3b;background-color:#fcf8e3}a.list-group-item-warning,button.list-group-item-warning{color:#8a6d3b}a.list-group-item-warning .list-group-item-heading,button.list-group-item-warning .list-group-item-heading{color:inherit}a.list-group-item-warning:focus,a.list-group-item-warning:hover,button.list-group-item-warning:focus,button.list-group-item-warning:hover{color:#8a6d3b;background-color:#faf2cc}a.list-group-item-warning.active,a.list-group-item-warning.active:focus,a.list-group-item-warning.active:hover,button.list-group-item-warning.active,button.list-group-item-warning.active:focus,button.list-group-item-warning.active:hover{color:#fff;background-color:#8a6d3b;border-color:#8a6d3b}.list-group-item-danger{color:#a94442;background-color:#f2dede}a.list-group-item-danger,button.list-group-item-danger{color:#a94442}a.list-group-item-danger .list-group-item-heading,button.list-group-item-danger .list-group-item-heading{color:inherit}a.list-group-item-danger:focus,a.list-group-item-danger:hover,button.list-group-item-danger:focus,button.list-group-item-danger:hover{color:#a94442;background-color:#ebcccc}a.list-group-item-danger.active,a.list-group-item-danger.active:focus,a.list-group-item-danger.active:hover,button.list-group-item-danger.active,button.list-group-item-danger.active:focus,button.list-group-item-danger.active:hover{color:#fff;background-color:#a94442;border-color:#a94442}.list-group-item-heading{margin-top:0;margin-bottom:5px}.list-group-item-text{margin-bottom:0;line-height:1.3}.embed-responsive{position:relative;display:block;height:0;padding:0;overflow:hidden}.embed-responsive .embed-responsive-item,.embed-responsive iframe,.embed-responsive embed,.embed-responsive object,.embed-responsive video{position:absolute;top:0;bottom:0;left:0;width:100%;height:100%;border:0}.embed-responsive-21by9{padding-bottom:42.85714%}.embed-responsive-16by9{padding-bottom:56.25%}.embed-responsive-4by3{padding-bottom:75%}.embed-responsive-1by1{padding-bottom:100%}.close{float:right;font-size:1.5rem;font-weight:700;line-height:1;color:#000;text-shadow:0 1px 0 #fff;opacity:.2}.close:focus,.close:hover{color:#000;text-decoration:none;cursor:pointer;opacity:.5}button.close{padding:0;cursor:pointer;background:0 0;border:0;-webkit-appearance:none}.modal-open{overflow:hidden}.modal{position:fixed;top:0;right:0;bottom:0;left:0;z-index:1050;display:none;overflow:hidden;outline:0;-webkit-overflow-scrolling:touch}.modal.fade .modal-dialog{transition:transform .3s ease-out;transform:translate(0,-25%)}.modal.in .modal-dialog{transform:translate(0,0)}.modal-open .modal{overflow-x:hidden;overflow-y:auto}.modal-dialog{position:relative;width:auto;margin:10px}.modal-content{position:relative;background-color:#fff;background-clip:padding-box;border:1px solid rgba(0,0,0,.2);border-radius:.3rem;outline:0}.modal-backdrop{position:fixed;top:0;right:0;bottom:0;left:0;z-index:1040;background-color:#000}.modal-backdrop.fade{opacity:0}.modal-backdrop.in{opacity:.5}.modal-header{padding:15px;border-bottom:1px solid #e5e5e5}.modal-header::after{content:"";display:table;clear:both}.modal-header .close{margin-top:-2px}.modal-title{margin:0;line-height:1.5}.modal-body{position:relative;padding:15px}.modal-footer{padding:15px;text-align:right;border-top:1px solid #e5e5e5}.modal-footer::after{content:"";display:table;clear:both}.modal-scrollbar-measure{position:absolute;top:-9999px;width:50px;height:50px;overflow:scroll}@media(min-width:544px){.modal-dialog{max-width:600px;margin:30px auto}.modal-sm{max-width:300px}}@media(min-width:992px){.modal-lg{max-width:900px}}.tooltip{position:absolute;z-index:1070;display:block;font-family:source sans pro,sans-serif;font-style:normal;font-weight:400;letter-spacing:normal;line-break:auto;line-height:1.5;text-align:left;text-align:start;text-decoration:none;text-shadow:none;text-transform:none;white-space:normal;word-break:normal;word-spacing:normal;font-size:.875rem;word-wrap:break-word;opacity:0}.tooltip.in{opacity:.9}.tooltip.tooltip-top,.tooltip.bs-tether-element-attached-bottom{padding:5px 0;margin-top:-3px}.tooltip.tooltip-top .tooltip-arrow,.tooltip.bs-tether-element-attached-bottom .tooltip-arrow{bottom:0;left:50%;margin-left:-5px;border-width:5px 5px 0;border-top-color:#000}.tooltip.tooltip-right,.tooltip.bs-tether-element-attached-left{padding:0 5px;margin-left:3px}.tooltip.tooltip-right .tooltip-arrow,.tooltip.bs-tether-element-attached-left .tooltip-arrow{top:50%;left:0;margin-top:-5px;border-width:5px 5px 5px 0;border-right-color:#000}.tooltip.tooltip-bottom,.tooltip.bs-tether-element-attached-top{padding:5px 0;margin-top:3px}.tooltip.tooltip-bottom .tooltip-arrow,.tooltip.bs-tether-element-attached-top .tooltip-arrow{top:0;left:50%;margin-left:-5px;border-width:0 5px 5px;border-bottom-color:#000}.tooltip.tooltip-left,.tooltip.bs-tether-element-attached-right{padding:0 5px;margin-left:-3px}.tooltip.tooltip-left .tooltip-arrow,.tooltip.bs-tether-element-attached-right .tooltip-arrow{top:50%;right:0;margin-top:-5px;border-width:5px 0 5px 5px;border-left-color:#000}.tooltip-inner{max-width:200px;padding:3px 8px;color:#fff;text-align:center;background-color:#000;border-radius:.25rem}.tooltip-arrow{position:absolute;width:0;height:0;border-color:transparent;border-style:solid}.popover{position:absolute;top:0;left:0;z-index:1060;display:block;max-width:276px;padding:1px;font-family:source sans pro,sans-serif;font-style:normal;font-weight:400;letter-spacing:normal;line-break:auto;line-height:1.5;text-align:left;text-align:start;text-decoration:none;text-shadow:none;text-transform:none;white-space:normal;word-break:normal;word-spacing:normal;font-size:.875rem;word-wrap:break-word;background-color:#fff;background-clip:padding-box;border:1px solid rgba(0,0,0,.2);border-radius:.3rem}.popover.popover-top,.popover.bs-tether-element-attached-bottom{margin-top:-10px}.popover.popover-top .popover-arrow,.popover.bs-tether-element-attached-bottom .popover-arrow{bottom:-11px;left:50%;margin-left:-11px;border-top-color:rgba(0,0,0,.25);border-bottom-width:0}.popover.popover-top .popover-arrow::after,.popover.bs-tether-element-attached-bottom .popover-arrow::after{bottom:1px;margin-left:-10px;content:"";border-top-color:#fff;border-bottom-width:0}.popover.popover-right,.popover.bs-tether-element-attached-left{margin-left:10px}.popover.popover-right .popover-arrow,.popover.bs-tether-element-attached-left .popover-arrow{top:50%;left:-11px;margin-top:-11px;border-right-color:rgba(0,0,0,.25);border-left-width:0}.popover.popover-right .popover-arrow::after,.popover.bs-tether-element-attached-left .popover-arrow::after{bottom:-10px;left:1px;content:"";border-right-color:#fff;border-left-width:0}.popover.popover-bottom,.popover.bs-tether-element-attached-top{margin-top:10px}.popover.popover-bottom .popover-arrow,.popover.bs-tether-element-attached-top .popover-arrow{top:-11px;left:50%;margin-left:-11px;border-top-width:0;border-bottom-color:rgba(0,0,0,.25)}.popover.popover-bottom .popover-arrow::after,.popover.bs-tether-element-attached-top .popover-arrow::after{top:1px;margin-left:-10px;content:"";border-top-width:0;border-bottom-color:#fff}.popover.popover-left,.popover.bs-tether-element-attached-right{margin-left:-10px}.popover.popover-left .popover-arrow,.popover.bs-tether-element-attached-right .popover-arrow{top:50%;right:-11px;margin-top:-11px;border-right-width:0;border-left-color:rgba(0,0,0,.25)}.popover.popover-left .popover-arrow::after,.popover.bs-tether-element-attached-right .popover-arrow::after{right:1px;bottom:-10px;content:"";border-right-width:0;border-left-color:#fff}.popover-title{padding:8px 14px;margin:0;font-size:1rem;background-color:#f7f7f7;border-bottom:1px solid #ebebeb;border-radius:.2375rem .2375rem 0 0}.popover-content{padding:9px 14px}.popover-arrow,.popover-arrow::after{position:absolute;display:block;width:0;height:0;border-color:transparent;border-style:solid}.popover-arrow{border-width:11px}.popover-arrow::after{content:"";border-width:10px}.carousel{position:relative}.carousel-inner{position:relative;width:100%;overflow:hidden}.carousel-inner>.carousel-item{position:relative;display:none;transition:.6s ease-in-out left}.carousel-inner>.carousel-item>img,.carousel-inner>.carousel-item>a>img{line-height:1}@media all and (transform-3d),(-webkit-transform-3d){.carousel-inner>.carousel-item{transition:transform .6s ease-in-out;backface-visibility:hidden;perspective:1000px}.carousel-inner>.carousel-item.next,.carousel-inner>.carousel-item.active.right{left:0;transform:translate3d(100%,0,0)}.carousel-inner>.carousel-item.prev,.carousel-inner>.carousel-item.active.left{left:0;transform:translate3d(-100%,0,0)}.carousel-inner>.carousel-item.next.left,.carousel-inner>.carousel-item.prev.right,.carousel-inner>.carousel-item.active{left:0;transform:translate3d(0,0,0)}}.carousel-inner>.active,.carousel-inner>.next,.carousel-inner>.prev{display:block}.carousel-inner>.active{left:0}.carousel-inner>.next,.carousel-inner>.prev{position:absolute;top:0;width:100%}.carousel-inner>.next{left:100%}.carousel-inner>.prev{left:-100%}.carousel-inner>.next.left,.carousel-inner>.prev.right{left:0}.carousel-inner>.active.left{left:-100%}.carousel-inner>.active.right{left:100%}.carousel-control{position:absolute;top:0;bottom:0;left:0;width:15%;font-size:20px;color:#fff;text-align:center;text-shadow:0 1px 2px rgba(0,0,0,.6);opacity:.5}.carousel-control.left{background-image:linear-gradient(to right,rgba(0,0,0,0.5) 0%,rgba(0,0,0,0.0001) 100%);background-repeat:repeat-x;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#80000000',endColorstr='#00000000',GradientType=1)}.carousel-control.right{right:0;left:auto;background-image:linear-gradient(to right,rgba(0,0,0,0.0001) 0%,rgba(0,0,0,0.5) 100%);background-repeat:repeat-x;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#00000000',endColorstr='#80000000',GradientType=1)}.carousel-control:focus,.carousel-control:hover{color:#fff;text-decoration:none;outline:0;opacity:.9}.carousel-control .icon-prev,.carousel-control .icon-next{position:absolute;top:50%;z-index:5;display:inline-block;width:20px;height:20px;margin-top:-10px;font-family:serif;line-height:1}.carousel-control .icon-prev{left:50%;margin-left:-10px}.carousel-control .icon-next{right:50%;margin-right:-10px}.carousel-control .icon-prev::before{content:"\2039"}.carousel-control .icon-next::before{content:"\203a"}.carousel-indicators{position:absolute;bottom:10px;left:50%;z-index:15;width:60%;padding-left:0;margin-left:-30%;text-align:center;list-style:none}.carousel-indicators li{display:inline-block;width:10px;height:10px;margin:1px;text-indent:-999px;cursor:pointer;background-color:transparent;border:1px solid #fff;border-radius:10px}.carousel-indicators .active{width:12px;height:12px;margin:0;background-color:#fff}.carousel-caption{position:absolute;right:15%;bottom:20px;left:15%;z-index:10;padding-top:20px;padding-bottom:20px;color:#fff;text-align:center;text-shadow:0 1px 2px rgba(0,0,0,.6)}.carousel-caption .btn{text-shadow:none}@media(min-width:544px){.carousel-control .icon-prev,.carousel-control .icon-next{width:30px;height:30px;margin-top:-15px;font-size:30px}.carousel-control .icon-prev{margin-left:-15px}.carousel-control .icon-next{margin-right:-15px}.carousel-caption{right:20%;left:20%;padding-bottom:30px}.carousel-indicators{bottom:20px}}.bg-inverse{color:#eceeef;background-color:#373a3c}.bg-faded{background-color:#f7f7f9}.bg-primary{color:#fff!important;background-color:#5cb85c!important}a.bg-primary:focus,a.bg-primary:hover{background-color:#449d44!important}.bg-success{color:#fff!important;background-color:#5cb85c!important}a.bg-success:focus,a.bg-success:hover{background-color:#449d44!important}.bg-info{color:#fff!important;background-color:#5bc0de!important}a.bg-info:focus,a.bg-info:hover{background-color:#31b0d5!important}.bg-warning{color:#fff!important;background-color:#f0ad4e!important}a.bg-warning:focus,a.bg-warning:hover{background-color:#ec971f!important}.bg-danger{color:#fff!important;background-color:#b85c5c!important}a.bg-danger:focus,a.bg-danger:hover{background-color:#9d4444!important}.clearfix::after{content:"";display:table;clear:both}.pull-xs-left{float:left!important}.pull-xs-right{float:right!important}.pull-xs-none{float:none!important}@media(min-width:544px){.pull-sm-left{float:left!important}.pull-sm-right{float:right!important}.pull-sm-none{float:none!important}}@media(min-width:768px){.pull-md-left{float:left!important}.pull-md-right{float:right!important}.pull-md-none{float:none!important}}@media(min-width:992px){.pull-lg-left{float:left!important}.pull-lg-right{float:right!important}.pull-lg-none{float:none!important}}@media(min-width:1200px){.pull-xl-left{float:left!important}.pull-xl-right{float:right!important}.pull-xl-none{float:none!important}}.sr-only{position:absolute;width:1px;height:1px;padding:0;margin:-1px;overflow:hidden;clip:rect(0,0,0,0);border:0}.sr-only-focusable:active,.sr-only-focusable:focus{position:static;width:auto;height:auto;margin:0;overflow:visible;clip:auto}.m-x-auto{margin-right:auto!important;margin-left:auto!important}.m-a-0{margin:0!important}.m-t-0{margin-top:0!important}.m-r-0{margin-right:0!important}.m-b-0{margin-bottom:0!important}.m-l-0{margin-left:0!important}.m-x-0{margin-right:0!important;margin-left:0!important}.m-y-0{margin-top:0!important;margin-bottom:0!important}.m-a-1{margin:1rem!important}.m-t-1{margin-top:1rem!important}.m-r-1{margin-right:1rem!important}.m-b-1{margin-bottom:1rem!important}.m-l-1{margin-left:1rem!important}.m-x-1{margin-right:1rem!important;margin-left:1rem!important}.m-y-1{margin-top:1rem!important;margin-bottom:1rem!important}.m-a-2{margin:1.5rem!important}.m-t-2{margin-top:1.5rem!important}.m-r-2{margin-right:1.5rem!important}.m-b-2{margin-bottom:1.5rem!important}.m-l-2{margin-left:1.5rem!important}.m-x-2{margin-right:1.5rem!important;margin-left:1.5rem!important}.m-y-2{margin-top:1.5rem!important;margin-bottom:1.5rem!important}.m-a-3{margin:3rem!important}.m-t-3{margin-top:3rem!important}.m-r-3{margin-right:3rem!important}.m-b-3{margin-bottom:3rem!important}.m-l-3{margin-left:3rem!important}.m-x-3{margin-right:3rem!important;margin-left:3rem!important}.m-y-3{margin-top:3rem!important;margin-bottom:3rem!important}.p-a-0{padding:0!important}.p-t-0{padding-top:0!important}.p-r-0{padding-right:0!important}.p-b-0{padding-bottom:0!important}.p-l-0{padding-left:0!important}.p-x-0{padding-right:0!important;padding-left:0!important}.p-y-0{padding-top:0!important;padding-bottom:0!important}.p-a-1{padding:1rem!important}.p-t-1{padding-top:1rem!important}.p-r-1{padding-right:1rem!important}.p-b-1{padding-bottom:1rem!important}.p-l-1{padding-left:1rem!important}.p-x-1{padding-right:1rem!important;padding-left:1rem!important}.p-y-1{padding-top:1rem!important;padding-bottom:1rem!important}.p-a-2{padding:1.5rem!important}.p-t-2{padding-top:1.5rem!important}.p-r-2{padding-right:1.5rem!important}.p-b-2{padding-bottom:1.5rem!important}.p-l-2{padding-left:1.5rem!important}.p-x-2{padding-right:1.5rem!important;padding-left:1.5rem!important}.p-y-2{padding-top:1.5rem!important;padding-bottom:1.5rem!important}.p-a-3{padding:3rem!important}.p-t-3{padding-top:3rem!important}.p-r-3{padding-right:3rem!important}.p-b-3{padding-bottom:3rem!important}.p-l-3{padding-left:3rem!important}.p-x-3{padding-right:3rem!important;padding-left:3rem!important}.p-y-3{padding-top:3rem!important;padding-bottom:3rem!important}.pos-f-t{position:fixed;top:0;right:0;left:0;z-index:1030}.text-justify{text-align:justify!important}.text-nowrap{white-space:nowrap!important}.text-truncate{overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.text-xs-left{text-align:left!important}.text-xs-right{text-align:right!important}.text-xs-center{text-align:center!important}@media(min-width:544px){.text-sm-left{text-align:left!important}.text-sm-right{text-align:right!important}.text-sm-center{text-align:center!important}}@media(min-width:768px){.text-md-left{text-align:left!important}.text-md-right{text-align:right!important}.text-md-center{text-align:center!important}}@media(min-width:992px){.text-lg-left{text-align:left!important}.text-lg-right{text-align:right!important}.text-lg-center{text-align:center!important}}@media(min-width:1200px){.text-xl-left{text-align:left!important}.text-xl-right{text-align:right!important}.text-xl-center{text-align:center!important}}.text-lowercase{text-transform:lowercase!important}.text-uppercase{text-transform:uppercase!important}.text-capitalize{text-transform:capitalize!important}.font-weight-normal{font-weight:400}.font-weight-bold{font-weight:700}.font-italic{font-style:italic}.text-muted{color:#818a91!important}a.text-muted:focus,a.text-muted:hover{color:#687077}.text-primary{color:#5cb85c!important}a.text-primary:focus,a.text-primary:hover{color:#449d44}.text-success{color:#5cb85c!important}a.text-success:focus,a.text-success:hover{color:#449d44}.text-info{color:#5bc0de!important}a.text-info:focus,a.text-info:hover{color:#31b0d5}.text-warning{color:#f0ad4e!important}a.text-warning:focus,a.text-warning:hover{color:#ec971f}.text-danger{color:#b85c5c!important}a.text-danger:focus,a.text-danger:hover{color:#9d4444}.text-hide{font:0/0 a;color:transparent;text-shadow:none;background-color:transparent;border:0}.invisible{visibility:hidden!important}.hidden-xs-up{display:none!important}@media(max-width:543px){.hidden-xs-down{display:none!important}}@media(min-width:544px){.hidden-sm-up{display:none!important}}@media(max-width:767px){.hidden-sm-down{display:none!important}}@media(min-width:768px){.hidden-md-up{display:none!important}}@media(max-width:991px){.hidden-md-down{display:none!important}}@media(min-width:992px){.hidden-lg-up{display:none!important}}@media(max-width:1199px){.hidden-lg-down{display:none!important}}@media(min-width:1200px){.hidden-xl-up{display:none!important}}.hidden-xl-down{display:none!important}.visible-print-block{display:none!important}@media print{.visible-print-block{display:block!important}}.visible-print-inline{display:none!important}@media print{.visible-print-inline{display:inline!important}}.visible-print-inline-block{display:none!important}@media print{.visible-print-inline-block{display:inline-block!important}}@media print{.hidden-print{display:none!important}}.flex-xs-first{order:-1}.flex-xs-last{order:1}.flex-items-xs-top{align-items:flex-start}.flex-items-xs-middle{align-items:center}.flex-items-xs-bottom{align-items:flex-end}.flex-xs-top{align-self:flex-start}.flex-xs-middle{align-self:center}.flex-xs-bottom{align-self:flex-end}.flex-items-xs-left{justify-content:flex-start}.flex-items-xs-center{justify-content:center}.flex-items-xs-right{justify-content:flex-end}.flex-items-xs-around{justify-content:space-around}.flex-items-xs-between{justify-content:space-between}@media(min-width:544px){.flex-sm-first{order:-1}.flex-sm-last{order:1}}@media(min-width:544px){.flex-items-sm-top{align-items:flex-start}.flex-items-sm-middle{align-items:center}.flex-items-sm-bottom{align-items:flex-end}}@media(min-width:544px){.flex-sm-top{align-self:flex-start}.flex-sm-middle{align-self:center}.flex-sm-bottom{align-self:flex-end}}@media(min-width:544px){.flex-items-sm-left{justify-content:flex-start}.flex-items-sm-center{justify-content:center}.flex-items-sm-right{justify-content:flex-end}.flex-items-sm-around{justify-content:space-around}.flex-items-sm-between{justify-content:space-between}}@media(min-width:768px){.flex-md-first{order:-1}.flex-md-last{order:1}}@media(min-width:768px){.flex-items-md-top{align-items:flex-start}.flex-items-md-middle{align-items:center}.flex-items-md-bottom{align-items:flex-end}}@media(min-width:768px){.flex-md-top{align-self:flex-start}.flex-md-middle{align-self:center}.flex-md-bottom{align-self:flex-end}}@media(min-width:768px){.flex-items-md-left{justify-content:flex-start}.flex-items-md-center{justify-content:center}.flex-items-md-right{justify-content:flex-end}.flex-items-md-around{justify-content:space-around}.flex-items-md-between{justify-content:space-between}}@media(min-width:992px){.flex-lg-first{order:-1}.flex-lg-last{order:1}}@media(min-width:992px){.flex-items-lg-top{align-items:flex-start}.flex-items-lg-middle{align-items:center}.flex-items-lg-bottom{align-items:flex-end}}@media(min-width:992px){.flex-lg-top{align-self:flex-start}.flex-lg-middle{align-self:center}.flex-lg-bottom{align-self:flex-end}}@media(min-width:992px){.flex-items-lg-left{justify-content:flex-start}.flex-items-lg-center{justify-content:center}.flex-items-lg-right{justify-content:flex-end}.flex-items-lg-around{justify-content:space-around}.flex-items-lg-between{justify-content:space-between}}@media(min-width:1200px){.flex-xl-first{order:-1}.flex-xl-last{order:1}}@media(min-width:1200px){.flex-items-xl-top{align-items:flex-start}.flex-items-xl-middle{align-items:center}.flex-items-xl-bottom{align-items:flex-end}}@media(min-width:1200px){.flex-xl-top{align-self:flex-start}.flex-xl-middle{align-self:center}.flex-xl-bottom{align-self:flex-end}}@media(min-width:1200px){.flex-items-xl-left{justify-content:flex-start}.flex-items-xl-center{justify-content:center}.flex-items-xl-right{justify-content:flex-end}.flex-items-xl-around{justify-content:space-around}.flex-items-xl-between{justify-content:space-between}}.tag-default{color:#fff!important;font-size:.8rem;padding-top:.1rem;padding-bottom:.1rem;white-space:nowrap;margin-right:3px;margin-bottom:.2rem;display:inline-block}.tag-default:hover{text-decoration:none}.tag-default.tag-outline{border:1px solid #ddd;color:#aaa!important;background:0 0!important}ul.tag-list{padding-left:0!important;display:inline-block;list-style:none!important}ul.tag-list li{display:inline-block!important}.navbar-brand{font-family:titillium web,sans-serif;font-size:1.5rem!important;padding-top:0!important;margin-right:2rem!important;color:#5cb85c!important}.nav-link .user-pic{height:26px;border-radius:50px;float:left;margin-right:5px}.nav-link:hover{transition:.1s all}.nav-pills.outline-active .nav-link{border-radius:0;border:none;border-bottom:2px solid transparent;background:0 0;color:#aaa}.nav-pills.outline-active .nav-link:hover{color:#555}.nav-pills.outline-active .nav-link.active{background:#fff!important;border-bottom:2px solid #5cb85c!important;color:#5cb85c!important}footer{background:#f3f3f3;margin-top:3rem;padding:1rem 0;position:absolute;bottom:0;width:100%}footer .logo-font{vertical-align:middle}footer .attribution{vertical-align:middle;margin-left:10px;font-size:.8rem;color:#bbb;font-weight:300}.error-messages{color:#b85c5c!important;font-weight:700}.banner{color:#fff;background:#333;padding:2rem;margin-bottom:2rem}.banner h1{text-shadow:0 1px 3px rgba(0,0,0,.3);margin-bottom:0}.container.page{margin-top:1.5rem}.preview-link{color:inherit!important}.preview-link:hover{text-decoration:inherit!important}.article-meta{display:block;position:relative;font-weight:300}.article-meta img{display:inline-block;vertical-align:middle;height:32px;width:32px;border-radius:30px}.article-meta .info{margin:0 1.5rem 0 .3rem;display:inline-block;vertical-align:middle;line-height:1rem}.article-meta .info .author{display:block;font-weight:500!important}.article-meta .info .date{color:#bbb;font-size:.8rem;display:block}.article-preview{border-top:1px solid rgba(0,0,0,.1);padding:1.5rem 0}.article-preview .article-meta{margin:0 0 1rem}.article-preview .preview-link h1{font-weight:600!important;font-size:1.5rem!important;margin-bottom:3px}.article-preview .preview-link p{font-weight:300;font-size:24px;color:#999;margin-bottom:15px;font-size:1rem;line-height:1.3rem}.article-preview .preview-link span{max-width:30%;font-size:.8rem;font-weight:300;color:#bbb;vertical-align:middle}.article-preview .preview-link ul{float:right;max-width:50%;vertical-align:top}.article-preview .preview-link ul li{font-weight:300;font-size:.8rem!important;padding-top:0!important;padding-bottom:0!important}.btn .counter{font-size:.8rem!important}.home-page .banner{background:#5cb85c;box-shadow:inset 0 8px 8px -8px rgba(0,0,0,.3),inset 0 -8px 8px -8px rgba(0,0,0,.3)}.home-page .banner p{color:#fff;text-align:center;font-size:1.5rem;font-weight:300!important;margin-bottom:0}.home-page .banner h1{text-shadow:0 1px 3px rgba(0,0,0,.3);font-weight:700!important;text-align:center;font-size:3.5rem;padding-bottom:.5rem}.home-page .feed-toggle{margin-bottom:-1px}.home-page .sidebar{padding:5px 10px 10px;background:#f3f3f3;border-radius:4px}.home-page .sidebar p{margin-bottom:.2rem}.article-page .banner{padding:2rem 0}.article-page .banner h1{font-size:2.8rem;font-weight:600}.article-page .banner .btn{opacity:.8}.article-page .banner .btn:hover{transition:.1s all;opacity:1}.article-page .banner .article-meta{margin:2rem 0 0}.article-page .banner .article-meta .author{color:#fff}.article-page .article-content p{font-family:'source serif pro',serif;font-size:1.2rem;line-height:1.8rem;margin-bottom:2rem}.article-page .article-content h1,.article-page .article-content h2,.article-page .article-content h3,.article-page .article-content h4,.article-page .article-content h5,.article-page .article-content h6{font-weight:500!important;margin:1.6rem 0 1rem}.article-page .article-actions{text-align:center;margin:1.5rem 0 3rem}.article-page .article-actions .article-meta .info{text-align:left}.article-page .comment-form .card-block{padding:0}.article-page .comment-form .card-block textarea{border:0;padding:1.25rem}.article-page .comment-form .card-footer .btn{font-weight:700;float:right}.article-page .comment-form .card-footer .comment-author-img{height:30px;width:30px}.article-page .card{border:1px solid #e5e5e5;box-shadow:none!important}.article-page .card .card-footer{border-top:1px solid #e5e5e5;box-shadow:none!important;font-size:.8rem;font-weight:300}.article-page .card .comment-author-img{display:inline-block;vertical-align:middle;height:20px;width:20px;border-radius:30px}.article-page .card .comment-author{display:inline-block;vertical-align:middle}.article-page .card .date-posted{display:inline-block;vertical-align:middle;margin-left:5px;color:#bbb}.article-page .card .mod-options{float:right;color:#333;font-size:1rem}.article-page .card .mod-options i{margin-left:5px;opacity:.6;cursor:pointer}.article-page .card .mod-options i:hover{opacity:1}.profile-page .user-info{text-align:center;background:#f3f3f3;padding:2rem 0 1rem}.profile-page .user-info .user-img{width:100px;height:100px;border-radius:100px;margin-bottom:1rem}.profile-page .user-info h4{font-weight:700}.profile-page .user-info p{margin:0 auto .5rem;color:#aaa;max-width:450px;font-weight:300}.profile-page .user-info .action-btn{float:right;color:#999;border:1px solid #999}.profile-page .articles-toggle{margin:1.5rem 0 -1px}.editor-page .tag-list i{font-size:.6rem;margin-right:5px;cursor:pointer} +* Licensed under MIT (https://github.com/twbs/bootstrap/blob/master/LICENSE)*//*!normalize.css commit fe56763 | MIT License | github.com/necolas/normalize.css*/html{font-family:sans-serif;-ms-text-size-adjust:100%;-webkit-text-size-adjust:100%}body{margin:0}article,aside,details,figcaption,figure,footer,header,main,menu,nav,section,summary{display:block}audio,canvas,progress,video{display:inline-block;vertical-align:baseline}audio:not([controls]){display:none;height:0}[hidden],template{display:none}a{background-color:transparent}a:active{outline:0}a:hover{outline:0}abbr[title]{border-bottom:1px dotted}b,strong{font-weight:700}dfn{font-style:italic}h1{font-size:2em;margin:.67em 0}mark{background:#ff0;color:#000}small{font-size:80%}sub,sup{font-size:75%;line-height:0;position:relative;vertical-align:baseline}sup{top:-.5em}sub{bottom:-.25em}img{border:0}svg:not(:root){overflow:hidden}figure{margin:1em 40px}hr{box-sizing:content-box;height:0}pre{overflow:auto}code,kbd,pre,samp{font-family:monospace,monospace;font-size:1em}button,input,optgroup,select,textarea{color:inherit;font:inherit;margin:0}button{overflow:visible}button,select{text-transform:none}button,html input[type=button],input[type=reset],input[type=submit]{-webkit-appearance:button;cursor:pointer}button[disabled],html input[disabled]{cursor:default}button::-moz-focus-inner,input::-moz-focus-inner{border:0;padding:0}input{line-height:normal}input[type=checkbox],input[type=radio]{box-sizing:border-box;padding:0}input[type=number]::-webkit-inner-spin-button,input[type=number]::-webkit-outer-spin-button{height:auto}input[type=search]{-webkit-appearance:textfield}input[type=search]::-webkit-search-cancel-button,input[type=search]::-webkit-search-decoration{-webkit-appearance:none}fieldset{border:1px solid silver;margin:0 2px;padding:.35em .625em .75em}legend{border:0;padding:0}textarea{overflow:auto}optgroup{font-weight:700}table{border-collapse:collapse;border-spacing:0}td,th{padding:0}@media print{*,*::before,*::after,*::first-letter,*::first-line{text-shadow:none!important;box-shadow:none!important}a,a:visited{text-decoration:underline}abbr[title]::after{content:" (" attr(title) ")"}pre,blockquote{border:1px solid #999;page-break-inside:avoid}thead{display:table-header-group}tr,img{page-break-inside:avoid}p,h2,h3{orphans:3;widows:3}h2,h3{page-break-after:avoid}.navbar{display:none}.btn>.caret,.dropup>.btn>.caret{border-top-color:#000!important}.tag{border:1px solid #000}.table{border-collapse:collapse!important}.table td,.table th{background-color:#fff!important}.table-bordered th,.table-bordered td{border:1px solid #ddd!important}}html{box-sizing:border-box}*,*::before,*::after{box-sizing:inherit}@-ms-viewport{width:device-width;}html{font-size:16px;-ms-overflow-style:scrollbar;-webkit-tap-highlight-color:transparent}body{font-family:source sans pro,sans-serif;font-size:1rem;line-height:1.5;color:#373a3c;background-color:#fff}[tabindex="-1"]:focus{outline:none!important}h1,h2,h3,h4,h5,h6{margin-top:0;margin-bottom:.5rem}p{margin-top:0;margin-bottom:1rem}abbr[title],abbr[data-original-title]{cursor:help;border-bottom:1px dotted #818a91}address{margin-bottom:1rem;font-style:normal;line-height:inherit}ol,ul,dl{margin-top:0;margin-bottom:1rem}ol ol,ul ul,ol ul,ul ol{margin-bottom:0}dt{font-weight:700}dd{margin-bottom:.5rem;margin-left:0}blockquote{margin:0 0 1rem}a{color:#5ED9C3;text-decoration:none}a:focus,a:hover{color:#5ed9c29a;text-decoration:underline}a:focus{outline:thin dotted;outline:5px auto -webkit-focus-ring-color;outline-offset:-2px}a:not([href]){color:inherit;text-decoration:none}a:not([href]):focus,a:not([href]):hover{color:inherit;text-decoration:none}a:not([href]):focus{outline:none}pre{margin-top:0;margin-bottom:1rem}figure{margin:0 0 1rem}img{vertical-align:middle}[role=button]{cursor:pointer}a,area,button,[role=button],input,label,select,summary,textarea{touch-action:manipulation}table{background-color:transparent}caption{padding-top:.75rem;padding-bottom:.75rem;color:#818a91;text-align:left;caption-side:bottom}th{text-align:left}label{display:inline-block;margin-bottom:.5rem}button:focus{outline:1px dotted;outline:5px auto -webkit-focus-ring-color}input,button,select,textarea{margin:0;line-height:inherit;border-radius:0}textarea{resize:vertical}fieldset{min-width:0;padding:0;margin:0;border:0}legend{display:block;width:100%;padding:0;margin-bottom:.5rem;font-size:1.5rem;line-height:inherit}input[type=search]{-webkit-appearance:none}output{display:inline-block}[hidden]{display:none!important}h1,h2,h3,h4,h5,h6,.h1,.h2,.h3,.h4,.h5,.h6{margin-bottom:.5rem;font-family:inherit;font-weight:500;line-height:1.1;color:inherit}h1,.h1{font-size:2.5rem}h2,.h2{font-size:2rem}h3,.h3{font-size:1.75rem}h4,.h4{font-size:1.5rem}h5,.h5{font-size:1.25rem}h6,.h6{font-size:1rem}.lead{font-size:1.25rem;font-weight:300}.display-1{font-size:6rem;font-weight:300}.display-2{font-size:5.5rem;font-weight:300}.display-3{font-size:4.5rem;font-weight:300}.display-4{font-size:3.5rem;font-weight:300}hr{margin-top:1rem;margin-bottom:1rem;border:0;border-top:1px solid rgba(0,0,0,.1)}small,.small{font-size:80%;font-weight:400}mark,.mark{padding:.2em;background-color:#fcf8e3}.list-unstyled{padding-left:0;list-style:none}.list-inline{padding-left:0;list-style:none}.list-inline-item{display:inline-block}.list-inline-item:not(:last-child){margin-right:5px}.initialism{font-size:90%;text-transform:uppercase}.blockquote{padding:.5rem 1rem;margin-bottom:1rem;font-size:1.25rem;border-left:.25rem solid #eceeef}.blockquote-footer{display:block;font-size:80%;color:#818a91}.blockquote-footer::before{content:"\2014 \00A0"}.blockquote-reverse{padding-right:1rem;padding-left:0;text-align:right;border-right:.25rem solid #eceeef;border-left:0}.blockquote-reverse .blockquote-footer::before{content:""}.blockquote-reverse .blockquote-footer::after{content:"\00A0 \2014"}.img-fluid,.carousel-inner>.carousel-item>img,.carousel-inner>.carousel-item>a>img{display:block;max-width:100%;height:auto}.img-rounded{border-radius:.3rem}.img-thumbnail{padding:.25rem;background-color:#fff;border:1px solid #ddd;border-radius:.25rem;transition:all .2s ease-in-out;display:inline-block;max-width:100%;height:auto}.img-circle{border-radius:50%}.figure{display:inline-block}.figure-img{margin-bottom:.5rem;line-height:1}.figure-caption{font-size:90%;color:#818a91}code,kbd,pre,samp{font-family:Menlo,Monaco,Consolas,liberation mono,courier new,monospace}code{padding:.2rem .4rem;font-size:90%;color:#bd4147;background-color:#f7f7f9;border-radius:.25rem}kbd{padding:.2rem .4rem;font-size:90%;color:#fff;background-color:#333;border-radius:.2rem}kbd kbd{padding:0;font-size:100%;font-weight:700}pre{display:block;margin-top:0;margin-bottom:1rem;font-size:90%;color:#373a3c}pre code{padding:0;font-size:inherit;color:inherit;background-color:transparent;border-radius:0}.pre-scrollable{max-height:340px;overflow-y:scroll}.container{margin-left:auto;margin-right:auto;padding-left:15px;padding-right:15px}@media(min-width:544px){.container{max-width:576px}}@media(min-width:768px){.container{max-width:720px}}@media(min-width:992px){.container{max-width:940px}}@media(min-width:1200px){.container{max-width:1140px}}.container-fluid{margin-left:auto;margin-right:auto;padding-left:15px;padding-right:15px}.row{display:flex;flex-wrap:wrap;margin-left:-15px;margin-right:-15px}.col-xs{position:relative;flex-basis:0;flex-grow:1;max-width:100%;min-height:1px;padding-right:15px;padding-left:15px}.col-xs-1{position:relative;min-height:1px;padding-right:15px;padding-left:15px;flex:0 0 8.33333%;max-width:8.33333%}.col-xs-2{position:relative;min-height:1px;padding-right:15px;padding-left:15px;flex:0 0 16.66667%;max-width:16.66667%}.col-xs-3{position:relative;min-height:1px;padding-right:15px;padding-left:15px;flex:0 0 25%;max-width:25%}.col-xs-4{position:relative;min-height:1px;padding-right:15px;padding-left:15px;flex:0 0 33.33333%;max-width:33.33333%}.col-xs-5{position:relative;min-height:1px;padding-right:15px;padding-left:15px;flex:0 0 41.66667%;max-width:41.66667%}.col-xs-6{position:relative;min-height:1px;padding-right:15px;padding-left:15px;flex:0 0 50%;max-width:50%}.col-xs-7{position:relative;min-height:1px;padding-right:15px;padding-left:15px;flex:0 0 58.33333%;max-width:58.33333%}.col-xs-8{position:relative;min-height:1px;padding-right:15px;padding-left:15px;flex:0 0 66.66667%;max-width:66.66667%}.col-xs-9{position:relative;min-height:1px;padding-right:15px;padding-left:15px;flex:0 0 75%;max-width:75%}.col-xs-10{position:relative;min-height:1px;padding-right:15px;padding-left:15px;flex:0 0 83.33333%;max-width:83.33333%}.col-xs-11{position:relative;min-height:1px;padding-right:15px;padding-left:15px;flex:0 0 91.66667%;max-width:91.66667%}.col-xs-12{position:relative;min-height:1px;padding-right:15px;padding-left:15px;flex:0 0 100%;max-width:100%}.pull-xs-0{right:auto}.pull-xs-1{right:8.33333%}.pull-xs-2{right:16.66667%}.pull-xs-3{right:25%}.pull-xs-4{right:33.33333%}.pull-xs-5{right:41.66667%}.pull-xs-6{right:50%}.pull-xs-7{right:58.33333%}.pull-xs-8{right:66.66667%}.pull-xs-9{right:75%}.pull-xs-10{right:83.33333%}.pull-xs-11{right:91.66667%}.pull-xs-12{right:100%}.push-xs-0{left:auto}.push-xs-1{left:8.33333%}.push-xs-2{left:16.66667%}.push-xs-3{left:25%}.push-xs-4{left:33.33333%}.push-xs-5{left:41.66667%}.push-xs-6{left:50%}.push-xs-7{left:58.33333%}.push-xs-8{left:66.66667%}.push-xs-9{left:75%}.push-xs-10{left:83.33333%}.push-xs-11{left:91.66667%}.push-xs-12{left:100%}.offset-xs-1{margin-left:8.33333%}.offset-xs-2{margin-left:16.66667%}.offset-xs-3{margin-left:25%}.offset-xs-4{margin-left:33.33333%}.offset-xs-5{margin-left:41.66667%}.offset-xs-6{margin-left:50%}.offset-xs-7{margin-left:58.33333%}.offset-xs-8{margin-left:66.66667%}.offset-xs-9{margin-left:75%}.offset-xs-10{margin-left:83.33333%}.offset-xs-11{margin-left:91.66667%}@media(min-width:544px){.col-sm{position:relative;flex-basis:0;flex-grow:1;max-width:100%;min-height:1px;padding-right:15px;padding-left:15px}.col-sm-1{position:relative;min-height:1px;padding-right:15px;padding-left:15px;flex:0 0 8.33333%;max-width:8.33333%}.col-sm-2{position:relative;min-height:1px;padding-right:15px;padding-left:15px;flex:0 0 16.66667%;max-width:16.66667%}.col-sm-3{position:relative;min-height:1px;padding-right:15px;padding-left:15px;flex:0 0 25%;max-width:25%}.col-sm-4{position:relative;min-height:1px;padding-right:15px;padding-left:15px;flex:0 0 33.33333%;max-width:33.33333%}.col-sm-5{position:relative;min-height:1px;padding-right:15px;padding-left:15px;flex:0 0 41.66667%;max-width:41.66667%}.col-sm-6{position:relative;min-height:1px;padding-right:15px;padding-left:15px;flex:0 0 50%;max-width:50%}.col-sm-7{position:relative;min-height:1px;padding-right:15px;padding-left:15px;flex:0 0 58.33333%;max-width:58.33333%}.col-sm-8{position:relative;min-height:1px;padding-right:15px;padding-left:15px;flex:0 0 66.66667%;max-width:66.66667%}.col-sm-9{position:relative;min-height:1px;padding-right:15px;padding-left:15px;flex:0 0 75%;max-width:75%}.col-sm-10{position:relative;min-height:1px;padding-right:15px;padding-left:15px;flex:0 0 83.33333%;max-width:83.33333%}.col-sm-11{position:relative;min-height:1px;padding-right:15px;padding-left:15px;flex:0 0 91.66667%;max-width:91.66667%}.col-sm-12{position:relative;min-height:1px;padding-right:15px;padding-left:15px;flex:0 0 100%;max-width:100%}.pull-sm-0{right:auto}.pull-sm-1{right:8.33333%}.pull-sm-2{right:16.66667%}.pull-sm-3{right:25%}.pull-sm-4{right:33.33333%}.pull-sm-5{right:41.66667%}.pull-sm-6{right:50%}.pull-sm-7{right:58.33333%}.pull-sm-8{right:66.66667%}.pull-sm-9{right:75%}.pull-sm-10{right:83.33333%}.pull-sm-11{right:91.66667%}.pull-sm-12{right:100%}.push-sm-0{left:auto}.push-sm-1{left:8.33333%}.push-sm-2{left:16.66667%}.push-sm-3{left:25%}.push-sm-4{left:33.33333%}.push-sm-5{left:41.66667%}.push-sm-6{left:50%}.push-sm-7{left:58.33333%}.push-sm-8{left:66.66667%}.push-sm-9{left:75%}.push-sm-10{left:83.33333%}.push-sm-11{left:91.66667%}.push-sm-12{left:100%}.offset-sm-0{margin-left:0%}.offset-sm-1{margin-left:8.33333%}.offset-sm-2{margin-left:16.66667%}.offset-sm-3{margin-left:25%}.offset-sm-4{margin-left:33.33333%}.offset-sm-5{margin-left:41.66667%}.offset-sm-6{margin-left:50%}.offset-sm-7{margin-left:58.33333%}.offset-sm-8{margin-left:66.66667%}.offset-sm-9{margin-left:75%}.offset-sm-10{margin-left:83.33333%}.offset-sm-11{margin-left:91.66667%}}@media(min-width:768px){.col-md{position:relative;flex-basis:0;flex-grow:1;max-width:100%;min-height:1px;padding-right:15px;padding-left:15px}.col-md-1{position:relative;min-height:1px;padding-right:15px;padding-left:15px;flex:0 0 8.33333%;max-width:8.33333%}.col-md-2{position:relative;min-height:1px;padding-right:15px;padding-left:15px;flex:0 0 16.66667%;max-width:16.66667%}.col-md-3{position:relative;min-height:1px;padding-right:15px;padding-left:15px;flex:0 0 25%;max-width:25%}.col-md-4{position:relative;min-height:1px;padding-right:15px;padding-left:15px;flex:0 0 33.33333%;max-width:33.33333%}.col-md-5{position:relative;min-height:1px;padding-right:15px;padding-left:15px;flex:0 0 41.66667%;max-width:41.66667%}.col-md-6{position:relative;min-height:1px;padding-right:15px;padding-left:15px;flex:0 0 50%;max-width:50%}.col-md-7{position:relative;min-height:1px;padding-right:15px;padding-left:15px;flex:0 0 58.33333%;max-width:58.33333%}.col-md-8{position:relative;min-height:1px;padding-right:15px;padding-left:15px;flex:0 0 66.66667%;max-width:66.66667%}.col-md-9{position:relative;min-height:1px;padding-right:15px;padding-left:15px;flex:0 0 75%;max-width:75%}.col-md-10{position:relative;min-height:1px;padding-right:15px;padding-left:15px;flex:0 0 83.33333%;max-width:83.33333%}.col-md-11{position:relative;min-height:1px;padding-right:15px;padding-left:15px;flex:0 0 91.66667%;max-width:91.66667%}.col-md-12{position:relative;min-height:1px;padding-right:15px;padding-left:15px;flex:0 0 100%;max-width:100%}.pull-md-0{right:auto}.pull-md-1{right:8.33333%}.pull-md-2{right:16.66667%}.pull-md-3{right:25%}.pull-md-4{right:33.33333%}.pull-md-5{right:41.66667%}.pull-md-6{right:50%}.pull-md-7{right:58.33333%}.pull-md-8{right:66.66667%}.pull-md-9{right:75%}.pull-md-10{right:83.33333%}.pull-md-11{right:91.66667%}.pull-md-12{right:100%}.push-md-0{left:auto}.push-md-1{left:8.33333%}.push-md-2{left:16.66667%}.push-md-3{left:25%}.push-md-4{left:33.33333%}.push-md-5{left:41.66667%}.push-md-6{left:50%}.push-md-7{left:58.33333%}.push-md-8{left:66.66667%}.push-md-9{left:75%}.push-md-10{left:83.33333%}.push-md-11{left:91.66667%}.push-md-12{left:100%}.offset-md-0{margin-left:0%}.offset-md-1{margin-left:8.33333%}.offset-md-2{margin-left:16.66667%}.offset-md-3{margin-left:25%}.offset-md-4{margin-left:33.33333%}.offset-md-5{margin-left:41.66667%}.offset-md-6{margin-left:50%}.offset-md-7{margin-left:58.33333%}.offset-md-8{margin-left:66.66667%}.offset-md-9{margin-left:75%}.offset-md-10{margin-left:83.33333%}.offset-md-11{margin-left:91.66667%}}@media(min-width:992px){.col-lg{position:relative;flex-basis:0;flex-grow:1;max-width:100%;min-height:1px;padding-right:15px;padding-left:15px}.col-lg-1{position:relative;min-height:1px;padding-right:15px;padding-left:15px;flex:0 0 8.33333%;max-width:8.33333%}.col-lg-2{position:relative;min-height:1px;padding-right:15px;padding-left:15px;flex:0 0 16.66667%;max-width:16.66667%}.col-lg-3{position:relative;min-height:1px;padding-right:15px;padding-left:15px;flex:0 0 25%;max-width:25%}.col-lg-4{position:relative;min-height:1px;padding-right:15px;padding-left:15px;flex:0 0 33.33333%;max-width:33.33333%}.col-lg-5{position:relative;min-height:1px;padding-right:15px;padding-left:15px;flex:0 0 41.66667%;max-width:41.66667%}.col-lg-6{position:relative;min-height:1px;padding-right:15px;padding-left:15px;flex:0 0 50%;max-width:50%}.col-lg-7{position:relative;min-height:1px;padding-right:15px;padding-left:15px;flex:0 0 58.33333%;max-width:58.33333%}.col-lg-8{position:relative;min-height:1px;padding-right:15px;padding-left:15px;flex:0 0 66.66667%;max-width:66.66667%}.col-lg-9{position:relative;min-height:1px;padding-right:15px;padding-left:15px;flex:0 0 75%;max-width:75%}.col-lg-10{position:relative;min-height:1px;padding-right:15px;padding-left:15px;flex:0 0 83.33333%;max-width:83.33333%}.col-lg-11{position:relative;min-height:1px;padding-right:15px;padding-left:15px;flex:0 0 91.66667%;max-width:91.66667%}.col-lg-12{position:relative;min-height:1px;padding-right:15px;padding-left:15px;flex:0 0 100%;max-width:100%}.pull-lg-0{right:auto}.pull-lg-1{right:8.33333%}.pull-lg-2{right:16.66667%}.pull-lg-3{right:25%}.pull-lg-4{right:33.33333%}.pull-lg-5{right:41.66667%}.pull-lg-6{right:50%}.pull-lg-7{right:58.33333%}.pull-lg-8{right:66.66667%}.pull-lg-9{right:75%}.pull-lg-10{right:83.33333%}.pull-lg-11{right:91.66667%}.pull-lg-12{right:100%}.push-lg-0{left:auto}.push-lg-1{left:8.33333%}.push-lg-2{left:16.66667%}.push-lg-3{left:25%}.push-lg-4{left:33.33333%}.push-lg-5{left:41.66667%}.push-lg-6{left:50%}.push-lg-7{left:58.33333%}.push-lg-8{left:66.66667%}.push-lg-9{left:75%}.push-lg-10{left:83.33333%}.push-lg-11{left:91.66667%}.push-lg-12{left:100%}.offset-lg-0{margin-left:0%}.offset-lg-1{margin-left:8.33333%}.offset-lg-2{margin-left:16.66667%}.offset-lg-3{margin-left:25%}.offset-lg-4{margin-left:33.33333%}.offset-lg-5{margin-left:41.66667%}.offset-lg-6{margin-left:50%}.offset-lg-7{margin-left:58.33333%}.offset-lg-8{margin-left:66.66667%}.offset-lg-9{margin-left:75%}.offset-lg-10{margin-left:83.33333%}.offset-lg-11{margin-left:91.66667%}}@media(min-width:1200px){.col-xl{position:relative;flex-basis:0;flex-grow:1;max-width:100%;min-height:1px;padding-right:15px;padding-left:15px}.col-xl-1{position:relative;min-height:1px;padding-right:15px;padding-left:15px;flex:0 0 8.33333%;max-width:8.33333%}.col-xl-2{position:relative;min-height:1px;padding-right:15px;padding-left:15px;flex:0 0 16.66667%;max-width:16.66667%}.col-xl-3{position:relative;min-height:1px;padding-right:15px;padding-left:15px;flex:0 0 25%;max-width:25%}.col-xl-4{position:relative;min-height:1px;padding-right:15px;padding-left:15px;flex:0 0 33.33333%;max-width:33.33333%}.col-xl-5{position:relative;min-height:1px;padding-right:15px;padding-left:15px;flex:0 0 41.66667%;max-width:41.66667%}.col-xl-6{position:relative;min-height:1px;padding-right:15px;padding-left:15px;flex:0 0 50%;max-width:50%}.col-xl-7{position:relative;min-height:1px;padding-right:15px;padding-left:15px;flex:0 0 58.33333%;max-width:58.33333%}.col-xl-8{position:relative;min-height:1px;padding-right:15px;padding-left:15px;flex:0 0 66.66667%;max-width:66.66667%}.col-xl-9{position:relative;min-height:1px;padding-right:15px;padding-left:15px;flex:0 0 75%;max-width:75%}.col-xl-10{position:relative;min-height:1px;padding-right:15px;padding-left:15px;flex:0 0 83.33333%;max-width:83.33333%}.col-xl-11{position:relative;min-height:1px;padding-right:15px;padding-left:15px;flex:0 0 91.66667%;max-width:91.66667%}.col-xl-12{position:relative;min-height:1px;padding-right:15px;padding-left:15px;flex:0 0 100%;max-width:100%}.pull-xl-0{right:auto}.pull-xl-1{right:8.33333%}.pull-xl-2{right:16.66667%}.pull-xl-3{right:25%}.pull-xl-4{right:33.33333%}.pull-xl-5{right:41.66667%}.pull-xl-6{right:50%}.pull-xl-7{right:58.33333%}.pull-xl-8{right:66.66667%}.pull-xl-9{right:75%}.pull-xl-10{right:83.33333%}.pull-xl-11{right:91.66667%}.pull-xl-12{right:100%}.push-xl-0{left:auto}.push-xl-1{left:8.33333%}.push-xl-2{left:16.66667%}.push-xl-3{left:25%}.push-xl-4{left:33.33333%}.push-xl-5{left:41.66667%}.push-xl-6{left:50%}.push-xl-7{left:58.33333%}.push-xl-8{left:66.66667%}.push-xl-9{left:75%}.push-xl-10{left:83.33333%}.push-xl-11{left:91.66667%}.push-xl-12{left:100%}.offset-xl-0{margin-left:0%}.offset-xl-1{margin-left:8.33333%}.offset-xl-2{margin-left:16.66667%}.offset-xl-3{margin-left:25%}.offset-xl-4{margin-left:33.33333%}.offset-xl-5{margin-left:41.66667%}.offset-xl-6{margin-left:50%}.offset-xl-7{margin-left:58.33333%}.offset-xl-8{margin-left:66.66667%}.offset-xl-9{margin-left:75%}.offset-xl-10{margin-left:83.33333%}.offset-xl-11{margin-left:91.66667%}}.table{width:100%;max-width:100%;margin-bottom:1rem}.table th,.table td{padding:.75rem;vertical-align:top;border-top:1px solid #eceeef}.table thead th{vertical-align:bottom;border-bottom:2px solid #eceeef}.table tbody+tbody{border-top:2px solid #eceeef}.table .table{background-color:#fff}.table-sm th,.table-sm td{padding:.3rem}.table-bordered{border:1px solid #eceeef}.table-bordered th,.table-bordered td{border:1px solid #eceeef}.table-bordered thead th,.table-bordered thead td{border-bottom-width:2px}.table-striped tbody tr:nth-of-type(odd){background-color:#f9f9f9}.table-hover tbody tr:hover{background-color:#f5f5f5}.table-active,.table-active>th,.table-active>td{background-color:#f5f5f5}.table-hover .table-active:hover{background-color:#e8e8e8}.table-hover .table-active:hover>td,.table-hover .table-active:hover>th{background-color:#e8e8e8}.table-success,.table-success>th,.table-success>td{background-color:#dff0d8}.table-hover .table-success:hover{background-color:#d0e9c6}.table-hover .table-success:hover>td,.table-hover .table-success:hover>th{background-color:#d0e9c6}.table-info,.table-info>th,.table-info>td{background-color:#d9edf7}.table-hover .table-info:hover{background-color:#c4e3f3}.table-hover .table-info:hover>td,.table-hover .table-info:hover>th{background-color:#c4e3f3}.table-warning,.table-warning>th,.table-warning>td{background-color:#fcf8e3}.table-hover .table-warning:hover{background-color:#faf2cc}.table-hover .table-warning:hover>td,.table-hover .table-warning:hover>th{background-color:#faf2cc}.table-danger,.table-danger>th,.table-danger>td{background-color:#f2dede}.table-hover .table-danger:hover{background-color:#ebcccc}.table-hover .table-danger:hover>td,.table-hover .table-danger:hover>th{background-color:#ebcccc}.table-responsive{display:block;width:100%;min-height:.01%;overflow-x:auto}.thead-inverse th{color:#fff;background-color:#373a3c}.thead-default th{color:#55595c;background-color:#eceeef}.table-inverse{color:#eceeef;background-color:#373a3c}.table-inverse.table-bordered{border:0}.table-inverse th,.table-inverse td,.table-inverse thead th{border-color:#55595c}.table-reflow thead{float:left}.table-reflow tbody{display:block;white-space:nowrap}.table-reflow th,.table-reflow td{border-top:1px solid #eceeef;border-left:1px solid #eceeef}.table-reflow th:last-child,.table-reflow td:last-child{border-right:1px solid #eceeef}.table-reflow thead:last-child tr:last-child th,.table-reflow thead:last-child tr:last-child td,.table-reflow tbody:last-child tr:last-child th,.table-reflow tbody:last-child tr:last-child td,.table-reflow tfoot:last-child tr:last-child th,.table-reflow tfoot:last-child tr:last-child td{border-bottom:1px solid #eceeef}.table-reflow tr{float:left}.table-reflow tr th,.table-reflow tr td{display:block!important;border:1px solid #eceeef}.form-control{display:block;width:100%;padding:.5rem .75rem;font-size:1rem;line-height:1.25;color:#55595c;background-color:#fff;background-image:none;background-clip:padding-box;border:1px solid rgba(0,0,0,.15);border-radius:.25rem}.form-control::-ms-expand{background-color:transparent;border:0}.form-control:focus{border-color:#66afe9;outline:none}.form-control::placeholder{color:#999;opacity:1}.form-control:disabled,.form-control[readonly]{background-color:#eceeef;opacity:1}.form-control:disabled{cursor:not-allowed}select.form-control:not([size]):not([multiple]){height:2.5rem}.form-control-file,.form-control-range{display:block}.form-control-label{padding:.5rem .75rem;margin-bottom:0}.form-control-legend{padding:.5rem .75rem;margin-bottom:0;font-size:1rem}_::-webkit-full-page-media.form-control,input[type=date].form-control,input[type=time].form-control,input[type=datetime-local].form-control,input[type=month].form-control{line-height:2.5rem}_::-webkit-full-page-media.input-sm,.input-group-sm _::-webkit-full-page-media.form-control,input[type=date].input-sm,.input-group-sm input[type=date].form-control,input[type=time].input-sm,.input-group-sm input[type=time].form-control,input[type=datetime-local].input-sm,.input-group-sm input[type=datetime-local].form-control,input[type=month].input-sm,.input-group-sm input[type=month].form-control{line-height:1.8125rem}_::-webkit-full-page-media.input-lg,.input-group-lg _::-webkit-full-page-media.form-control,input[type=date].input-lg,.input-group-lg input[type=date].form-control,input[type=time].input-lg,.input-group-lg input[type=time].form-control,input[type=datetime-local].input-lg,.input-group-lg input[type=datetime-local].form-control,input[type=month].input-lg,.input-group-lg input[type=month].form-control{line-height:3.16667rem}.form-control-static{min-height:2.5rem;padding-top:.5rem;padding-bottom:.5rem;margin-bottom:0}.form-control-static.form-control-sm,.input-group-sm>.form-control-static.form-control,.input-group-sm>.form-control-static.input-group-addon,.input-group-sm>.input-group-btn>.form-control-static.btn,.form-control-static.form-control-lg,.input-group-lg>.form-control-static.form-control,.input-group-lg>.form-control-static.input-group-addon,.input-group-lg>.input-group-btn>.form-control-static.btn{padding-right:0;padding-left:0}.form-control-sm,.input-group-sm>.form-control,.input-group-sm>.input-group-addon,.input-group-sm>.input-group-btn>.btn{padding:.25rem .5rem;font-size:.875rem;border-radius:.2rem}.form-control-lg,.input-group-lg>.form-control,.input-group-lg>.input-group-addon,.input-group-lg>.input-group-btn>.btn{padding:.75rem 1.5rem;font-size:1.25rem;border-radius:.3rem}.form-group{margin-bottom:1rem}.radio,.checkbox{position:relative;display:block;margin-bottom:.75rem}.radio label,.checkbox label{padding-left:1.25rem;margin-bottom:0;cursor:pointer}.radio label input:only-child,.checkbox label input:only-child{position:static}.radio input[type=radio],.radio-inline input[type=radio],.checkbox input[type=checkbox],.checkbox-inline input[type=checkbox]{position:absolute;margin-top:.25rem;margin-left:-1.25rem}.radio+.radio,.checkbox+.checkbox{margin-top:-.25rem}.radio-inline,.checkbox-inline{position:relative;display:inline-block;padding-left:1.25rem;margin-bottom:0;vertical-align:middle;cursor:pointer}.radio-inline+.radio-inline,.checkbox-inline+.checkbox-inline{margin-top:0;margin-left:.75rem}input[type=radio]:disabled,input[type=radio].disabled,input[type=checkbox]:disabled,input[type=checkbox].disabled{cursor:not-allowed}.radio-inline.disabled,.checkbox-inline.disabled{cursor:not-allowed}.radio.disabled label,.checkbox.disabled label{cursor:not-allowed}.form-control-success,.form-control-warning,.form-control-danger{padding-right:2.25rem;background-repeat:no-repeat;background-position:center right .625rem;background-size:1.25rem 1.25rem}.has-success .text-help,.has-success .form-control-label,.has-success .radio,.has-success .checkbox,.has-success .radio-inline,.has-success .checkbox-inline,.has-success.radio label,.has-success.checkbox label,.has-success.radio-inline label,.has-success.checkbox-inline label,.has-success .custom-control{color:#5ED9C3}.has-success .form-control{border-color:#5ED9C3}.has-success .input-group-addon{color:#5ED9C3;border-color:#5ED9C3;background-color:#eaf6ea}.has-success .form-control-feedback{color:#5ED9C3}.has-success .form-control-success{background-image:url(data:image/svg+xml;charset=utf8;base64,PHN2ZyB4bWxucz0naHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmcnIHZpZXdCb3g9JzAgMCA4IDgnPjxwYXRoIGZpbGw9JyM1Y2I4NWMnIGQ9J00yLjMgNi43M0wuNiA0LjUzYy0uNC0xLjA0LjQ2LTEuNCAxLjEtLjhsMS4xIDEuNCAzLjQtMy44Yy42LS42MyAxLjYtLjI3IDEuMi43bC00IDQuNmMtLjQzLjUtLjguNC0xLjEuMXonLz48L3N2Zz4=)}.has-warning .text-help,.has-warning .form-control-label,.has-warning .radio,.has-warning .checkbox,.has-warning .radio-inline,.has-warning .checkbox-inline,.has-warning.radio label,.has-warning.checkbox label,.has-warning.radio-inline label,.has-warning.checkbox-inline label,.has-warning .custom-control{color:#f0ad4e}.has-warning .form-control{border-color:#f0ad4e}.has-warning .input-group-addon{color:#f0ad4e;border-color:#f0ad4e;background-color:#fff}.has-warning .form-control-feedback{color:#f0ad4e}.has-warning .form-control-warning{background-image:url(data:image/svg+xml;charset=utf8;base64,PHN2ZyB4bWxucz0naHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmcnIHZpZXdCb3g9JzAgMCA4IDgnPjxwYXRoIGZpbGw9JyNmMGFkNGUnIGQ9J000LjQgNS4zMjRoLS44di0yLjQ2aC44em0wIDEuNDJoLS44VjUuODloLjh6TTMuNzYuNjNMLjA0IDcuMDc1Yy0uMTE1LjIuMDE2LjQyNS4yNi40MjZoNy4zOTdjLjI0MiAwIC4zNzItLjIyNi4yNTgtLjQyNkM2LjcyNiA0LjkyNCA1LjQ3IDIuNzkgNC4yNTMuNjNjLS4xMTMtLjE3NC0uMzktLjE3NC0uNDk0IDB6Jy8+PC9zdmc+)}.has-danger .text-help,.has-danger .form-control-label,.has-danger .radio,.has-danger .checkbox,.has-danger .radio-inline,.has-danger .checkbox-inline,.has-danger.radio label,.has-danger.checkbox label,.has-danger.radio-inline label,.has-danger.checkbox-inline label,.has-danger .custom-control{color:#b85c5c}.has-danger .form-control{border-color:#b85c5c}.has-danger .input-group-addon{color:#b85c5c;border-color:#b85c5c;background-color:#f6eaea}.has-danger .form-control-feedback{color:#b85c5c}.has-danger .form-control-danger{background-image:url(data:image/svg+xml;charset=utf8;base64,PHN2ZyB4bWxucz0naHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmcnIGZpbGw9JyNkOTUzNGYnIHZpZXdCb3g9Jy0yIC0yIDcgNyc+PHBhdGggc3Ryb2tlPScjZDk1MzRmJyBkPSdNMCAwbDMgM20wLTNMMCAzJy8+PGNpcmNsZSByPScuNScvPjxjaXJjbGUgY3g9JzMnIHI9Jy41Jy8+PGNpcmNsZSBjeT0nMycgcj0nLjUnLz48Y2lyY2xlIGN4PSczJyBjeT0nMycgcj0nLjUnLz48L3N2Zz4=)}@media(min-width:544px){.form-inline .form-group{display:inline-block;margin-bottom:0;vertical-align:middle}.form-inline .form-control{display:inline-block;width:auto;vertical-align:middle}.form-inline .form-control-static{display:inline-block}.form-inline .input-group{display:inline-table;vertical-align:middle}.form-inline .input-group .input-group-addon,.form-inline .input-group .input-group-btn,.form-inline .input-group .form-control{width:auto}.form-inline .input-group>.form-control{width:100%}.form-inline .form-control-label{margin-bottom:0;vertical-align:middle}.form-inline .radio,.form-inline .checkbox{display:inline-block;margin-top:0;margin-bottom:0;vertical-align:middle}.form-inline .radio label,.form-inline .checkbox label{padding-left:0}.form-inline .radio input[type=radio],.form-inline .checkbox input[type=checkbox]{position:relative;margin-left:0}.form-inline .has-feedback .form-control-feedback{top:0}}.btn{display:inline-block;font-weight:400;line-height:1.25;text-align:center;white-space:nowrap;vertical-align:middle;cursor:pointer;user-select:none;border:1px solid transparent;padding:.5rem 1rem;font-size:1rem;border-radius:.25rem}.btn:focus,.btn.focus,.btn:active:focus,.btn:active.focus,.btn.active:focus,.btn.active.focus{outline:thin dotted;outline:5px auto -webkit-focus-ring-color;outline-offset:-2px}.btn:focus,.btn:hover{text-decoration:none}.btn.focus{text-decoration:none}.btn:active,.btn.active{background-image:none;outline:0}.btn.disabled,.btn:disabled{cursor:not-allowed;opacity:.65}a.btn.disabled,fieldset[disabled] a.btn{pointer-events:none}.btn-primary{color:#fff;background-color:#5ED9C3;border-color:#5ED9C3}.btn-primary:hover{color:#fff;background-color:#38b5b3;border-color:#419641}.btn-primary:focus,.btn-primary.focus{color:#fff;background-color:#38b5b3;border-color:#419641}.btn-primary:active,.btn-primary.active,.open>.btn-primary.dropdown-toggle{color:#fff;background-color:#38b5b3;border-color:#419641;background-image:none}.btn-primary:active:hover,.btn-primary:active:focus,.btn-primary:active.focus,.btn-primary.active:hover,.btn-primary.active:focus,.btn-primary.active.focus,.open>.btn-primary.dropdown-toggle:hover,.open>.btn-primary.dropdown-toggle:focus,.open>.btn-primary.dropdown-toggle.focus{color:#fff;background-color:#398439;border-color:#2d672d}.btn-primary.disabled:focus,.btn-primary.disabled.focus,.btn-primary:disabled:focus,.btn-primary:disabled.focus{background-color:#5ED9C3;border-color:#5ED9C3}.btn-primary.disabled:hover,.btn-primary:disabled:hover{background-color:#5ED9C3;border-color:#5ED9C3}.btn-secondary{color:#373a3c;background-color:#fff;border-color:#ccc}.btn-secondary:hover{color:#373a3c;background-color:#e6e6e6;border-color:#adadad}.btn-secondary:focus,.btn-secondary.focus{color:#373a3c;background-color:#e6e6e6;border-color:#adadad}.btn-secondary:active,.btn-secondary.active,.open>.btn-secondary.dropdown-toggle{color:#373a3c;background-color:#e6e6e6;border-color:#adadad;background-image:none}.btn-secondary:active:hover,.btn-secondary:active:focus,.btn-secondary:active.focus,.btn-secondary.active:hover,.btn-secondary.active:focus,.btn-secondary.active.focus,.open>.btn-secondary.dropdown-toggle:hover,.open>.btn-secondary.dropdown-toggle:focus,.open>.btn-secondary.dropdown-toggle.focus{color:#373a3c;background-color:#d4d4d4;border-color:#8c8c8c}.btn-secondary.disabled:focus,.btn-secondary.disabled.focus,.btn-secondary:disabled:focus,.btn-secondary:disabled.focus{background-color:#fff;border-color:#ccc}.btn-secondary.disabled:hover,.btn-secondary:disabled:hover{background-color:#fff;border-color:#ccc}.btn-info{color:#fff;background-color:#5bc0de;border-color:#5bc0de}.btn-info:hover{color:#fff;background-color:#31b0d5;border-color:#2aabd2}.btn-info:focus,.btn-info.focus{color:#fff;background-color:#31b0d5;border-color:#2aabd2}.btn-info:active,.btn-info.active,.open>.btn-info.dropdown-toggle{color:#fff;background-color:#31b0d5;border-color:#2aabd2;background-image:none}.btn-info:active:hover,.btn-info:active:focus,.btn-info:active.focus,.btn-info.active:hover,.btn-info.active:focus,.btn-info.active.focus,.open>.btn-info.dropdown-toggle:hover,.open>.btn-info.dropdown-toggle:focus,.open>.btn-info.dropdown-toggle.focus{color:#fff;background-color:#269abc;border-color:#1f7e9a}.btn-info.disabled:focus,.btn-info.disabled.focus,.btn-info:disabled:focus,.btn-info:disabled.focus{background-color:#5bc0de;border-color:#5bc0de}.btn-info.disabled:hover,.btn-info:disabled:hover{background-color:#5bc0de;border-color:#5bc0de}.btn-success{color:#fff;background-color:#5ED9C3;border-color:#5ED9C3}.btn-success:hover{color:#fff;background-color:#38b5b3;border-color:#419641}.btn-success:focus,.btn-success.focus{color:#fff;background-color:#38b5b3;border-color:#419641}.btn-success:active,.btn-success.active,.open>.btn-success.dropdown-toggle{color:#fff;background-color:#38b5b3;border-color:#419641;background-image:none}.btn-success:active:hover,.btn-success:active:focus,.btn-success:active.focus,.btn-success.active:hover,.btn-success.active:focus,.btn-success.active.focus,.open>.btn-success.dropdown-toggle:hover,.open>.btn-success.dropdown-toggle:focus,.open>.btn-success.dropdown-toggle.focus{color:#fff;background-color:#398439;border-color:#2d672d}.btn-success.disabled:focus,.btn-success.disabled.focus,.btn-success:disabled:focus,.btn-success:disabled.focus{background-color:#5ED9C3;border-color:#5ED9C3}.btn-success.disabled:hover,.btn-success:disabled:hover{background-color:#5ED9C3;border-color:#5ED9C3}.btn-warning{color:#fff;background-color:#f0ad4e;border-color:#f0ad4e}.btn-warning:hover{color:#fff;background-color:#ec971f;border-color:#eb9316}.btn-warning:focus,.btn-warning.focus{color:#fff;background-color:#ec971f;border-color:#eb9316}.btn-warning:active,.btn-warning.active,.open>.btn-warning.dropdown-toggle{color:#fff;background-color:#ec971f;border-color:#eb9316;background-image:none}.btn-warning:active:hover,.btn-warning:active:focus,.btn-warning:active.focus,.btn-warning.active:hover,.btn-warning.active:focus,.btn-warning.active.focus,.open>.btn-warning.dropdown-toggle:hover,.open>.btn-warning.dropdown-toggle:focus,.open>.btn-warning.dropdown-toggle.focus{color:#fff;background-color:#d58512;border-color:#b06d0f}.btn-warning.disabled:focus,.btn-warning.disabled.focus,.btn-warning:disabled:focus,.btn-warning:disabled.focus{background-color:#f0ad4e;border-color:#f0ad4e}.btn-warning.disabled:hover,.btn-warning:disabled:hover{background-color:#f0ad4e;border-color:#f0ad4e}.btn-danger{color:#fff;background-color:#b85c5c;border-color:#b85c5c}.btn-danger:hover{color:#fff;background-color:#9d4444;border-color:#964141}.btn-danger:focus,.btn-danger.focus{color:#fff;background-color:#9d4444;border-color:#964141}.btn-danger:active,.btn-danger.active,.open>.btn-danger.dropdown-toggle{color:#fff;background-color:#9d4444;border-color:#964141;background-image:none}.btn-danger:active:hover,.btn-danger:active:focus,.btn-danger:active.focus,.btn-danger.active:hover,.btn-danger.active:focus,.btn-danger.active.focus,.open>.btn-danger.dropdown-toggle:hover,.open>.btn-danger.dropdown-toggle:focus,.open>.btn-danger.dropdown-toggle.focus{color:#fff;background-color:#843939;border-color:#672d2d}.btn-danger.disabled:focus,.btn-danger.disabled.focus,.btn-danger:disabled:focus,.btn-danger:disabled.focus{background-color:#b85c5c;border-color:#b85c5c}.btn-danger.disabled:hover,.btn-danger:disabled:hover{background-color:#b85c5c;border-color:#b85c5c}.btn-outline-primary{color:#5ED9C3;background-image:none;background-color:transparent;border-color:#5ED9C3}.btn-outline-primary:hover{color:#fff;background-color:#5ED9C3;border-color:#5ED9C3}.btn-outline-primary:focus,.btn-outline-primary.focus{color:#fff;background-color:#5ED9C3;border-color:#5ED9C3}.btn-outline-primary:active,.btn-outline-primary.active,.open>.btn-outline-primary.dropdown-toggle{color:#fff;background-color:#5ED9C3;border-color:#5ED9C3}.btn-outline-primary:active:hover,.btn-outline-primary:active:focus,.btn-outline-primary:active.focus,.btn-outline-primary.active:hover,.btn-outline-primary.active:focus,.btn-outline-primary.active.focus,.open>.btn-outline-primary.dropdown-toggle:hover,.open>.btn-outline-primary.dropdown-toggle:focus,.open>.btn-outline-primary.dropdown-toggle.focus{color:#fff;background-color:#398439;border-color:#2d672d}.btn-outline-primary.disabled:focus,.btn-outline-primary.disabled.focus,.btn-outline-primary:disabled:focus,.btn-outline-primary:disabled.focus{border-color:#a3d7a3}.btn-outline-primary.disabled:hover,.btn-outline-primary:disabled:hover{border-color:#a3d7a3}.btn-outline-secondary{color:#ccc;background-image:none;background-color:transparent;border-color:#ccc}.btn-outline-secondary:hover{color:#fff;background-color:#ccc;border-color:#ccc}.btn-outline-secondary:focus,.btn-outline-secondary.focus{color:#fff;background-color:#ccc;border-color:#ccc}.btn-outline-secondary:active,.btn-outline-secondary.active,.open>.btn-outline-secondary.dropdown-toggle{color:#fff;background-color:#ccc;border-color:#ccc}.btn-outline-secondary:active:hover,.btn-outline-secondary:active:focus,.btn-outline-secondary:active.focus,.btn-outline-secondary.active:hover,.btn-outline-secondary.active:focus,.btn-outline-secondary.active.focus,.open>.btn-outline-secondary.dropdown-toggle:hover,.open>.btn-outline-secondary.dropdown-toggle:focus,.open>.btn-outline-secondary.dropdown-toggle.focus{color:#fff;background-color:#a1a1a1;border-color:#8c8c8c}.btn-outline-secondary.disabled:focus,.btn-outline-secondary.disabled.focus,.btn-outline-secondary:disabled:focus,.btn-outline-secondary:disabled.focus{border-color:#fff}.btn-outline-secondary.disabled:hover,.btn-outline-secondary:disabled:hover{border-color:#fff}.btn-outline-info{color:#5bc0de;background-image:none;background-color:transparent;border-color:#5bc0de}.btn-outline-info:hover{color:#fff;background-color:#5bc0de;border-color:#5bc0de}.btn-outline-info:focus,.btn-outline-info.focus{color:#fff;background-color:#5bc0de;border-color:#5bc0de}.btn-outline-info:active,.btn-outline-info.active,.open>.btn-outline-info.dropdown-toggle{color:#fff;background-color:#5bc0de;border-color:#5bc0de}.btn-outline-info:active:hover,.btn-outline-info:active:focus,.btn-outline-info:active.focus,.btn-outline-info.active:hover,.btn-outline-info.active:focus,.btn-outline-info.active.focus,.open>.btn-outline-info.dropdown-toggle:hover,.open>.btn-outline-info.dropdown-toggle:focus,.open>.btn-outline-info.dropdown-toggle.focus{color:#fff;background-color:#269abc;border-color:#1f7e9a}.btn-outline-info.disabled:focus,.btn-outline-info.disabled.focus,.btn-outline-info:disabled:focus,.btn-outline-info:disabled.focus{border-color:#b0e1ef}.btn-outline-info.disabled:hover,.btn-outline-info:disabled:hover{border-color:#b0e1ef}.btn-outline-success{color:#5ED9C3;background-image:none;background-color:transparent;border-color:#5ED9C3}.btn-outline-success:hover{color:#fff;background-color:#5ED9C3;border-color:#5ED9C3}.btn-outline-success:focus,.btn-outline-success.focus{color:#fff;background-color:#5ED9C3;border-color:#5ED9C3}.btn-outline-success:active,.btn-outline-success.active,.open>.btn-outline-success.dropdown-toggle{color:#fff;background-color:#5ED9C3;border-color:#5ED9C3}.btn-outline-success:active:hover,.btn-outline-success:active:focus,.btn-outline-success:active.focus,.btn-outline-success.active:hover,.btn-outline-success.active:focus,.btn-outline-success.active.focus,.open>.btn-outline-success.dropdown-toggle:hover,.open>.btn-outline-success.dropdown-toggle:focus,.open>.btn-outline-success.dropdown-toggle.focus{color:#fff;background-color:#398439;border-color:#2d672d}.btn-outline-success.disabled:focus,.btn-outline-success.disabled.focus,.btn-outline-success:disabled:focus,.btn-outline-success:disabled.focus{border-color:#a3d7a3}.btn-outline-success.disabled:hover,.btn-outline-success:disabled:hover{border-color:#a3d7a3}.btn-outline-warning{color:#f0ad4e;background-image:none;background-color:transparent;border-color:#f0ad4e}.btn-outline-warning:hover{color:#fff;background-color:#f0ad4e;border-color:#f0ad4e}.btn-outline-warning:focus,.btn-outline-warning.focus{color:#fff;background-color:#f0ad4e;border-color:#f0ad4e}.btn-outline-warning:active,.btn-outline-warning.active,.open>.btn-outline-warning.dropdown-toggle{color:#fff;background-color:#f0ad4e;border-color:#f0ad4e}.btn-outline-warning:active:hover,.btn-outline-warning:active:focus,.btn-outline-warning:active.focus,.btn-outline-warning.active:hover,.btn-outline-warning.active:focus,.btn-outline-warning.active.focus,.open>.btn-outline-warning.dropdown-toggle:hover,.open>.btn-outline-warning.dropdown-toggle:focus,.open>.btn-outline-warning.dropdown-toggle.focus{color:#fff;background-color:#d58512;border-color:#b06d0f}.btn-outline-warning.disabled:focus,.btn-outline-warning.disabled.focus,.btn-outline-warning:disabled:focus,.btn-outline-warning:disabled.focus{border-color:#f8d9ac}.btn-outline-warning.disabled:hover,.btn-outline-warning:disabled:hover{border-color:#f8d9ac}.btn-outline-danger{color:#b85c5c;background-image:none;background-color:transparent;border-color:#b85c5c}.btn-outline-danger:hover{color:#fff;background-color:#b85c5c;border-color:#b85c5c}.btn-outline-danger:focus,.btn-outline-danger.focus{color:#fff;background-color:#b85c5c;border-color:#b85c5c}.btn-outline-danger:active,.btn-outline-danger.active,.open>.btn-outline-danger.dropdown-toggle{color:#fff;background-color:#b85c5c;border-color:#b85c5c}.btn-outline-danger:active:hover,.btn-outline-danger:active:focus,.btn-outline-danger:active.focus,.btn-outline-danger.active:hover,.btn-outline-danger.active:focus,.btn-outline-danger.active.focus,.open>.btn-outline-danger.dropdown-toggle:hover,.open>.btn-outline-danger.dropdown-toggle:focus,.open>.btn-outline-danger.dropdown-toggle.focus{color:#fff;background-color:#843939;border-color:#672d2d}.btn-outline-danger.disabled:focus,.btn-outline-danger.disabled.focus,.btn-outline-danger:disabled:focus,.btn-outline-danger:disabled.focus{border-color:#d7a3a3}.btn-outline-danger.disabled:hover,.btn-outline-danger:disabled:hover{border-color:#d7a3a3}.btn-link{font-weight:400;color:#5ED9C3;border-radius:0}.btn-link,.btn-link:active,.btn-link.active,.btn-link:disabled{background-color:transparent}.btn-link,.btn-link:focus,.btn-link:active{border-color:transparent}.btn-link:hover{border-color:transparent}.btn-link:focus,.btn-link:hover{color:#3d8b3d;text-decoration:underline;background-color:transparent}.btn-link:disabled:focus,.btn-link:disabled:hover{color:#818a91;text-decoration:none}.btn-lg,.btn-group-lg>.btn{padding:.75rem 1.5rem;font-size:1.25rem;border-radius:.3rem}.btn-sm,.btn-group-sm>.btn{padding:.25rem .5rem;font-size:.875rem;border-radius:.2rem}.btn-block{display:block;width:100%}.btn-block+.btn-block{margin-top:5px}input[type=submit].btn-block,input[type=reset].btn-block,input[type=button].btn-block{width:100%}.fade{opacity:0;transition:opacity .15s linear}.fade.in{opacity:1}.collapse{display:none}.collapse.in{display:block}.collapsing{position:relative;height:0;overflow:hidden;transition-timing-function:ease;transition-duration:.35s;transition-property:height}.dropup,.dropdown{position:relative}.dropdown-toggle::after{display:inline-block;width:0;height:0;margin-right:.25rem;margin-left:.25rem;vertical-align:middle;content:"";border-top:.3em solid;border-right:.3em solid transparent;border-left:.3em solid transparent}.dropdown-toggle:focus{outline:0}.dropup .dropdown-toggle::after{border-top:0;border-bottom:.3em solid}.dropdown-menu{position:absolute;top:100%;left:0;z-index:1000;display:none;float:left;min-width:160px;padding:5px 0;margin:2px 0 0;font-size:1rem;color:#373a3c;text-align:left;list-style:none;background-color:#fff;background-clip:padding-box;border:1px solid rgba(0,0,0,.15);border-radius:.25rem}.dropdown-divider{height:1px;margin:.5rem 0;overflow:hidden;background-color:#e5e5e5}.dropdown-item{display:block;width:100%;padding:3px 20px;clear:both;font-weight:400;color:#373a3c;text-align:inherit;white-space:nowrap;background:0 0;border:0}.dropdown-item:focus,.dropdown-item:hover{color:#2b2d2f;text-decoration:none;background-color:#f5f5f5}.dropdown-item.active,.dropdown-item.active:focus,.dropdown-item.active:hover{color:#fff;text-decoration:none;background-color:#5ED9C3;outline:0}.dropdown-item.disabled,.dropdown-item.disabled:focus,.dropdown-item.disabled:hover{color:#818a91}.dropdown-item.disabled:focus,.dropdown-item.disabled:hover{text-decoration:none;cursor:not-allowed;background-color:transparent;background-image:none;filter:"progid:DXImageTransform.Microsoft.gradient(enabled = false)"}.open>.dropdown-menu{display:block}.open>a{outline:0}.dropdown-menu-right{right:0;left:auto}.dropdown-menu-left{right:auto;left:0}.dropdown-header{display:block;padding:5px 20px;font-size:.875rem;color:#818a91;white-space:nowrap}.dropdown-backdrop{position:fixed;top:0;right:0;bottom:0;left:0;z-index:990}.dropup .caret,.navbar-fixed-bottom .dropdown .caret{content:"";border-top:0;border-bottom:.3em solid}.dropup .dropdown-menu,.navbar-fixed-bottom .dropdown .dropdown-menu{top:auto;bottom:100%;margin-bottom:2px}.btn-group,.btn-group-vertical{position:relative;display:inline-block;vertical-align:middle}.btn-group>.btn,.btn-group-vertical>.btn{position:relative;float:left}.btn-group>.btn:focus,.btn-group>.btn:active,.btn-group>.btn.active,.btn-group-vertical>.btn:focus,.btn-group-vertical>.btn:active,.btn-group-vertical>.btn.active{z-index:2}.btn-group>.btn:hover,.btn-group-vertical>.btn:hover{z-index:2}.btn-group .btn+.btn,.btn-group .btn+.btn-group,.btn-group .btn-group+.btn,.btn-group .btn-group+.btn-group{margin-left:-1px}.btn-toolbar{margin-left:-5px}.btn-toolbar::after{content:"";display:table;clear:both}.btn-toolbar .btn-group,.btn-toolbar .input-group{float:left}.btn-toolbar>.btn,.btn-toolbar>.btn-group,.btn-toolbar>.input-group{margin-left:5px}.btn-group>.btn:not(:first-child):not(:last-child):not(.dropdown-toggle){border-radius:0}.btn-group>.btn:first-child{margin-left:0}.btn-group>.btn:first-child:not(:last-child):not(.dropdown-toggle){border-bottom-right-radius:0;border-top-right-radius:0}.btn-group>.btn:last-child:not(:first-child),.btn-group>.dropdown-toggle:not(:first-child){border-bottom-left-radius:0;border-top-left-radius:0}.btn-group>.btn-group{float:left}.btn-group>.btn-group:not(:first-child):not(:last-child)>.btn{border-radius:0}.btn-group>.btn-group:first-child:not(:last-child)>.btn:last-child,.btn-group>.btn-group:first-child:not(:last-child)>.dropdown-toggle{border-bottom-right-radius:0;border-top-right-radius:0}.btn-group>.btn-group:last-child:not(:first-child)>.btn:first-child{border-bottom-left-radius:0;border-top-left-radius:0}.btn-group .dropdown-toggle:active,.btn-group.open .dropdown-toggle{outline:0}.btn-group>.btn+.dropdown-toggle{padding-right:8px;padding-left:8px}.btn-group>.btn-lg+.dropdown-toggle,.btn-group-lg.btn-group>.btn+.dropdown-toggle{padding-right:12px;padding-left:12px}.btn .caret{margin-left:0}.btn-lg .caret,.btn-group-lg>.btn .caret{border-width:.3em .3em 0;border-bottom-width:0}.dropup .btn-lg .caret,.dropup .btn-group-lg>.btn .caret{border-width:0 .3em .3em}.btn-group-vertical>.btn,.btn-group-vertical>.btn-group,.btn-group-vertical>.btn-group>.btn{display:block;float:none;width:100%;max-width:100%}.btn-group-vertical>.btn-group::after{content:"";display:table;clear:both}.btn-group-vertical>.btn-group>.btn{float:none}.btn-group-vertical>.btn+.btn,.btn-group-vertical>.btn+.btn-group,.btn-group-vertical>.btn-group+.btn,.btn-group-vertical>.btn-group+.btn-group{margin-top:-1px;margin-left:0}.btn-group-vertical>.btn:not(:first-child):not(:last-child){border-radius:0}.btn-group-vertical>.btn:first-child:not(:last-child){border-bottom-right-radius:0;border-bottom-left-radius:0}.btn-group-vertical>.btn:last-child:not(:first-child){border-top-right-radius:0;border-top-left-radius:0}.btn-group-vertical>.btn-group:not(:first-child):not(:last-child)>.btn{border-radius:0}.btn-group-vertical>.btn-group:first-child:not(:last-child)>.btn:last-child,.btn-group-vertical>.btn-group:first-child:not(:last-child)>.dropdown-toggle{border-bottom-right-radius:0;border-bottom-left-radius:0}.btn-group-vertical>.btn-group:last-child:not(:first-child)>.btn:first-child{border-top-right-radius:0;border-top-left-radius:0}[data-toggle=buttons]>.btn input[type=radio],[data-toggle=buttons]>.btn input[type=checkbox],[data-toggle=buttons]>.btn-group>.btn input[type=radio],[data-toggle=buttons]>.btn-group>.btn input[type=checkbox]{position:absolute;clip:rect(0,0,0,0);pointer-events:none}.input-group{position:relative;width:100%;display:flex}.input-group .form-control{position:relative;z-index:2;flex:1;margin-bottom:0}.input-group .form-control:focus,.input-group .form-control:active,.input-group .form-control:hover{z-index:3}.input-group-addon:not(:first-child):not(:last-child),.input-group-btn:not(:first-child):not(:last-child),.input-group .form-control:not(:first-child):not(:last-child){border-radius:0}.input-group-addon,.input-group-btn{white-space:nowrap;vertical-align:middle}.input-group-addon{padding:.5rem .75rem;margin-bottom:0;font-size:1rem;font-weight:400;line-height:1.25;color:#55595c;text-align:center;background-color:#eceeef;border:1px solid rgba(0,0,0,.15);border-radius:.25rem}.input-group-addon.form-control-sm,.input-group-sm>.input-group-addon,.input-group-sm>.input-group-btn>.input-group-addon.btn{padding:.25rem .5rem;font-size:.875rem;border-radius:.2rem}.input-group-addon.form-control-lg,.input-group-lg>.input-group-addon,.input-group-lg>.input-group-btn>.input-group-addon.btn{padding:.75rem 1.5rem;font-size:1.25rem;border-radius:.3rem}.input-group-addon input[type=radio],.input-group-addon input[type=checkbox]{margin-top:0}.input-group .form-control:not(:last-child),.input-group-addon:not(:last-child),.input-group-btn:not(:last-child)>.btn,.input-group-btn:not(:last-child)>.btn-group>.btn,.input-group-btn:not(:last-child)>.dropdown-toggle,.input-group-btn:not(:first-child)>.btn:not(:last-child):not(.dropdown-toggle),.input-group-btn:not(:first-child)>.btn-group:not(:last-child)>.btn{border-bottom-right-radius:0;border-top-right-radius:0}.input-group-addon:not(:last-child){border-right:0}.input-group .form-control:not(:first-child),.input-group-addon:not(:first-child),.input-group-btn:not(:first-child)>.btn,.input-group-btn:not(:first-child)>.btn-group>.btn,.input-group-btn:not(:first-child)>.dropdown-toggle,.input-group-btn:not(:last-child)>.btn:not(:first-child),.input-group-btn:not(:last-child)>.btn-group:not(:first-child)>.btn{border-bottom-left-radius:0;border-top-left-radius:0}.form-control+.input-group-addon:not(:first-child){border-left:0}.input-group-btn{position:relative;font-size:0;white-space:nowrap}.input-group-btn>.btn{position:relative}.input-group-btn>.btn+.btn{margin-left:-1px}.input-group-btn>.btn:focus,.input-group-btn>.btn:active,.input-group-btn>.btn:hover{z-index:3}.input-group-btn:not(:last-child)>.btn,.input-group-btn:not(:last-child)>.btn-group{margin-right:-1px}.input-group-btn:not(:first-child)>.btn,.input-group-btn:not(:first-child)>.btn-group{z-index:2;margin-left:-1px}.input-group-btn:not(:first-child)>.btn:focus,.input-group-btn:not(:first-child)>.btn:active,.input-group-btn:not(:first-child)>.btn:hover,.input-group-btn:not(:first-child)>.btn-group:focus,.input-group-btn:not(:first-child)>.btn-group:active,.input-group-btn:not(:first-child)>.btn-group:hover{z-index:3}.custom-control{position:relative;display:inline;padding-left:1.5rem;cursor:pointer}.custom-control+.custom-control{margin-left:1rem}.custom-control-input{position:absolute;z-index:-1;opacity:0}.custom-control-input:checked~.custom-control-indicator{color:#fff;background-color:#0074d9}.custom-control-input:focus~.custom-control-indicator{box-shadow:0 0 0 .075rem #fff,0 0 0 .2rem #0074d9}.custom-control-input:active~.custom-control-indicator{color:#fff;background-color:#84c6ff}.custom-control-input:disabled~.custom-control-indicator{cursor:not-allowed;background-color:#eee}.custom-control-input:disabled~.custom-control-description{color:#767676;cursor:not-allowed}.custom-control-indicator{position:absolute;top:.0625rem;left:0;display:block;width:1rem;height:1rem;pointer-events:none;user-select:none;background-color:#ddd;background-repeat:no-repeat;background-position:center center;background-size:50% 50%}.custom-checkbox .custom-control-indicator{border-radius:.25rem}.custom-checkbox .custom-control-input:checked~.custom-control-indicator{background-image:url(data:image/svg+xml;charset=utf8;base64,PHN2ZyB4bWxucz0naHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmcnIHZpZXdCb3g9JzAgMCA4IDgnPjxwYXRoIGZpbGw9JyNmZmYnIGQ9J002LjU2NC43NWwtMy41OSAzLjYxMi0xLjUzOC0xLjU1TDAgNC4yNiAyLjk3NCA3LjI1IDggMi4xOTN6Jy8+PC9zdmc+)}.custom-checkbox .custom-control-input:indeterminate~.custom-control-indicator{background-color:#0074d9;background-image:url(data:image/svg+xml;charset=utf8;base64,PHN2ZyB4bWxucz0naHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmcnIHZpZXdCb3g9JzAgMCA0IDQnPjxwYXRoIHN0cm9rZT0nI2ZmZicgZD0nTTAgMmg0Jy8+PC9zdmc+)}.custom-radio .custom-control-indicator{border-radius:50%}.custom-radio .custom-control-input:checked~.custom-control-indicator{background-image:url(data:image/svg+xml;charset=utf8;base64,PHN2ZyB4bWxucz0naHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmcnIHZpZXdCb3g9Jy00IC00IDggOCc+PGNpcmNsZSByPSczJyBmaWxsPScjZmZmJy8+PC9zdmc+)}.custom-controls-stacked .custom-control{display:inline}.custom-controls-stacked .custom-control::after{display:block;margin-bottom:.25rem;content:""}.custom-controls-stacked .custom-control+.custom-control{margin-left:0}.custom-select{display:inline-block;max-width:100%;padding:.375rem 1.75rem .375rem .75rem;padding-right:.75rem \9;color:#55595c;vertical-align:middle;background:#fff url(data:image/svg+xml;charset=utf8;base64,PHN2ZyB4bWxucz0naHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmcnIHZpZXdCb3g9JzAgMCA0IDUnPjxwYXRoIGZpbGw9JyMzMzMnIGQ9J00yIDBMMCAyaDR6bTAgNUwwIDNoNHonLz48L3N2Zz4=) no-repeat right .75rem center;background-image:none \9;background-size:8px 10px;border:1px solid rgba(0,0,0,.15);border-radius:.25rem;-moz-appearance:none;-webkit-appearance:none}.custom-select:focus{border-color:#51a7e8;outline:none}.custom-select::-ms-expand{opacity:0}.custom-select-sm{padding-top:.375rem;padding-bottom:.375rem;font-size:75%}.custom-file{position:relative;display:inline-block;max-width:100%;height:2.5rem;cursor:pointer}.custom-file-input{min-width:14rem;max-width:100%;margin:0;filter:alpha(opacity=0);opacity:0}.custom-file-control{position:absolute;top:0;right:0;left:0;z-index:5;height:2.5rem;padding:.5rem 1rem;line-height:1.5;color:#555;user-select:none;background-color:#fff;border:1px solid #ddd;border-radius:.25rem}.custom-file-control:lang(en)::after{content:"Choose file..."}.custom-file-control::before{position:absolute;top:-1px;right:-1px;bottom:-1px;z-index:6;display:block;height:2.5rem;padding:.5rem 1rem;line-height:1.5;color:#555;background-color:#eee;border:1px solid #ddd;border-radius:0 .25rem .25rem 0}.custom-file-control:lang(en)::before{content:"Browse"}.nav{padding-left:0;margin-bottom:0;list-style:none}.nav-link{display:inline-block}.nav-link:focus,.nav-link:hover{text-decoration:none}.nav-link.disabled{color:#818a91}.nav-link.disabled,.nav-link.disabled:focus,.nav-link.disabled:hover{color:#818a91;cursor:not-allowed;background-color:transparent}.nav-inline .nav-item{display:inline-block}.nav-inline .nav-item+.nav-item,.nav-inline .nav-link+.nav-link{margin-left:1rem}.nav-tabs{border-bottom:1px solid #ddd}.nav-tabs::after{content:"";display:table;clear:both}.nav-tabs .nav-item{float:left;margin-bottom:-1px}.nav-tabs .nav-item+.nav-item{margin-left:.2rem}.nav-tabs .nav-link{display:block;padding:.5em 1em;border:1px solid transparent;border-radius:.25rem .25rem 0 0}.nav-tabs .nav-link:focus,.nav-tabs .nav-link:hover{border-color:#eceeef #eceeef #ddd}.nav-tabs .nav-link.disabled,.nav-tabs .nav-link.disabled:focus,.nav-tabs .nav-link.disabled:hover{color:#818a91;background-color:transparent;border-color:transparent}.nav-tabs .nav-link.active,.nav-tabs .nav-link.active:focus,.nav-tabs .nav-link.active:hover,.nav-tabs .nav-item.open .nav-link,.nav-tabs .nav-item.open .nav-link:focus,.nav-tabs .nav-item.open .nav-link:hover{color:#55595c;background-color:#fff;border-color:#ddd #ddd transparent}.nav-tabs .dropdown-menu{margin-top:-1px;border-top-right-radius:0;border-top-left-radius:0}.nav-pills::after{content:"";display:table;clear:both}.nav-pills .nav-item{float:left}.nav-pills .nav-item+.nav-item{margin-left:.2rem}.nav-pills .nav-link{display:block;padding:.5em 1em;border-radius:.25rem}.nav-pills .nav-link.active,.nav-pills .nav-link.active:focus,.nav-pills .nav-link.active:hover,.nav-pills .nav-item.open .nav-link,.nav-pills .nav-item.open .nav-link:focus,.nav-pills .nav-item.open .nav-link:hover{color:#fff;cursor:default;background-color:#5ED9C3}.nav-stacked .nav-item{display:block;float:none}.nav-stacked .nav-item+.nav-item{margin-top:.2rem;margin-left:0}.tab-content>.tab-pane{display:none}.tab-content>.active{display:block}.navbar{position:relative;padding:.5rem 1rem}.navbar::after{content:"";display:table;clear:both}@media(min-width:544px){.navbar{border-radius:.25rem}}.navbar-full{z-index:1000}@media(min-width:544px){.navbar-full{border-radius:0}}.navbar-fixed-top,.navbar-fixed-bottom{position:fixed;right:0;left:0;z-index:1030}@media(min-width:544px){.navbar-fixed-top,.navbar-fixed-bottom{border-radius:0}}.navbar-fixed-top{top:0}.navbar-fixed-bottom{bottom:0}.navbar-sticky-top{position:sticky;top:0;z-index:1030;width:100%}@media(min-width:544px){.navbar-sticky-top{border-radius:0}}.navbar-brand{float:left;padding-top:.25rem;padding-bottom:.25rem;margin-right:1rem;font-size:1.25rem}.navbar-brand:focus,.navbar-brand:hover{text-decoration:none}.navbar-brand>img{display:block}.navbar-divider{float:left;width:1px;padding-top:.425rem;padding-bottom:.425rem;margin-right:1rem;margin-left:1rem;overflow:hidden}.navbar-divider::before{content:"\00a0"}.navbar-toggler{padding:.5rem .75rem;font-size:1.25rem;line-height:1;background:0 0;border:1px solid transparent;border-radius:.25rem}.navbar-toggler:focus,.navbar-toggler:hover{text-decoration:none}@media(min-width:544px){.navbar-toggleable-xs{display:block!important}}@media(min-width:768px){.navbar-toggleable-sm{display:block!important}}@media(min-width:992px){.navbar-toggleable-md{display:block!important}}.navbar-nav .nav-item{float:left}.navbar-nav .nav-link{display:block;padding-top:.425rem;padding-bottom:.425rem}.navbar-nav .nav-link+.nav-link{margin-left:1rem}.navbar-nav .nav-item+.nav-item{margin-left:1rem}.navbar-light .navbar-brand{color:rgba(0,0,0,.8)}.navbar-light .navbar-brand:focus,.navbar-light .navbar-brand:hover{color:rgba(0,0,0,.8)}.navbar-light .navbar-nav .nav-link{color:rgba(0,0,0,.3)}.navbar-light .navbar-nav .nav-link:focus,.navbar-light .navbar-nav .nav-link:hover{color:rgba(0,0,0,.6)}.navbar-light .navbar-nav .open>.nav-link,.navbar-light .navbar-nav .open>.nav-link:focus,.navbar-light .navbar-nav .open>.nav-link:hover,.navbar-light .navbar-nav .active>.nav-link,.navbar-light .navbar-nav .active>.nav-link:focus,.navbar-light .navbar-nav .active>.nav-link:hover,.navbar-light .navbar-nav .nav-link.open,.navbar-light .navbar-nav .nav-link.open:focus,.navbar-light .navbar-nav .nav-link.open:hover,.navbar-light .navbar-nav .nav-link.active,.navbar-light .navbar-nav .nav-link.active:focus,.navbar-light .navbar-nav .nav-link.active:hover{color:rgba(0,0,0,.8)}.navbar-light .navbar-divider{background-color:rgba(0,0,0,.075)}.navbar-dark .navbar-brand{color:#fff}.navbar-dark .navbar-brand:focus,.navbar-dark .navbar-brand:hover{color:#fff}.navbar-dark .navbar-nav .nav-link{color:rgba(255,255,255,.5)}.navbar-dark .navbar-nav .nav-link:focus,.navbar-dark .navbar-nav .nav-link:hover{color:rgba(255,255,255,.75)}.navbar-dark .navbar-nav .open>.nav-link,.navbar-dark .navbar-nav .open>.nav-link:focus,.navbar-dark .navbar-nav .open>.nav-link:hover,.navbar-dark .navbar-nav .active>.nav-link,.navbar-dark .navbar-nav .active>.nav-link:focus,.navbar-dark .navbar-nav .active>.nav-link:hover,.navbar-dark .navbar-nav .nav-link.open,.navbar-dark .navbar-nav .nav-link.open:focus,.navbar-dark .navbar-nav .nav-link.open:hover,.navbar-dark .navbar-nav .nav-link.active,.navbar-dark .navbar-nav .nav-link.active:focus,.navbar-dark .navbar-nav .nav-link.active:hover{color:#fff}.navbar-dark .navbar-divider{background-color:rgba(255,255,255,.075)}.card{position:relative;display:block;margin-bottom:.75rem;background-color:#fff;border-radius:.25rem;box-shadow:inset 0 0 0 1px rgba(0,0,0,.125)}.card-block{padding:1.25rem}.card-block::after{content:"";display:table;clear:both}.card-title{margin-bottom:.75rem}.card-subtitle{margin-top:-.375rem;margin-bottom:0}.card-text:last-child{margin-bottom:0}.card-link:hover{text-decoration:none}.card-link+.card-link{margin-left:1.25rem}.card>.list-group:first-child .list-group-item:first-child{border-top-left-radius:.25rem;border-top-right-radius:.25rem}.card>.list-group:last-child .list-group-item:last-child{border-bottom-right-radius:.25rem;border-bottom-left-radius:.25rem}.card-header{padding:.75rem 1.25rem;background-color:#f5f5f5;box-shadow:inset 0 0 0 1px rgba(0,0,0,.125)}.card-header::after{content:"";display:table;clear:both}.card-header:first-child{border-radius:.25rem .25rem 0 0}.card-footer{height:67px;padding:.75rem 1.25rem;background-color:#f5f5f5;box-shadow:inset 0 0 0 1px rgba(0,0,0,.125)}.card-footer::after{content:"";display:table;clear:both}.card-footer:last-child{border-radius:0 0 .25rem .25rem}.card-header-tabs{margin-right:-.625rem;margin-bottom:-.75rem;margin-left:-.625rem;border-bottom:0}.card-header-tabs .nav-item{margin-bottom:0}.card-header-pills{margin-right:-.625rem;margin-left:-.625rem}.card-primary{background-color:#5ED9C3;border-color:#5ED9C3}.card-success{background-color:#5ED9C3;border-color:#5ED9C3}.card-info{background-color:#5bc0de;border-color:#5bc0de}.card-warning{background-color:#f0ad4e;border-color:#f0ad4e}.card-danger{background-color:#b85c5c;border-color:#b85c5c}.card-outline-primary{background-color:transparent;border-color:#5ED9C3}.card-outline-secondary{background-color:transparent;border-color:#ccc}.card-outline-info{background-color:transparent;border-color:#5bc0de}.card-outline-success{background-color:transparent;border-color:#5ED9C3}.card-outline-warning{background-color:transparent;border-color:#f0ad4e}.card-outline-danger{background-color:transparent;border-color:#b85c5c}.card-inverse .card-header,.card-inverse .card-footer{border-bottom:1px solid rgba(255,255,255,.2)}.card-inverse .card-header,.card-inverse .card-footer,.card-inverse .card-title,.card-inverse .card-blockquote{color:#fff}.card-inverse .card-link,.card-inverse .card-text,.card-inverse .card-blockquote>footer{color:rgba(255,255,255,.65)}.card-inverse .card-link:focus,.card-inverse .card-link:hover{color:#fff}.card-blockquote{padding:0;margin-bottom:0;border-left:0}.card-img{border-radius:.25rem}.card-img-overlay{position:absolute;top:0;right:0;bottom:0;left:0;padding:1.25rem}.card-img-top{border-radius:.25rem .25rem 0 0}.card-img-bottom{border-radius:0 0 .25rem .25rem}@media(min-width:544px){.card-deck{display:flex;flex-flow:row wrap;margin-right:-.625rem;margin-left:-.625rem}.card-deck .card{flex:1 0 0;margin-right:.625rem;margin-left:.625rem}}@media(min-width:544px){.card-group{display:flex;flex-flow:row wrap}.card-group .card{flex:1 0 0}.card-group .card+.card{margin-left:0;border-left:0}.card-group .card:first-child{border-bottom-right-radius:0;border-top-right-radius:0}.card-group .card:first-child .card-img-top{border-top-right-radius:0}.card-group .card:first-child .card-img-bottom{border-bottom-right-radius:0}.card-group .card:last-child{border-bottom-left-radius:0;border-top-left-radius:0}.card-group .card:last-child .card-img-top{border-top-left-radius:0}.card-group .card:last-child .card-img-bottom{border-bottom-left-radius:0}.card-group .card:not(:first-child):not(:last-child){border-radius:0}.card-group .card:not(:first-child):not(:last-child) .card-img-top,.card-group .card:not(:first-child):not(:last-child) .card-img-bottom{border-radius:0}}@media(min-width:544px){.card-columns{column-count:3;column-gap:1.25rem}.card-columns .card{display:inline-block;width:100%}}.breadcrumb{padding:.75rem 1rem;margin-bottom:1rem;list-style:none;background-color:#eceeef;border-radius:.25rem}.breadcrumb::after{content:"";display:table;clear:both}.breadcrumb-item{float:left}.breadcrumb-item+.breadcrumb-item::before{display:inline-block;padding-right:.5rem;padding-left:.5rem;color:#818a91;content:"/"}.breadcrumb-item+.breadcrumb-item:hover::before{text-decoration:underline}.breadcrumb-item+.breadcrumb-item:hover::before{text-decoration:none}.breadcrumb-item.active{color:#818a91}.pagination{display:inline-block;padding-left:0;margin-top:1rem;margin-bottom:1rem;border-radius:.25rem}.page-item{display:inline}.page-item:first-child .page-link{margin-left:0;border-bottom-left-radius:.25rem;border-top-left-radius:.25rem}.page-item:last-child .page-link{border-bottom-right-radius:.25rem;border-top-right-radius:.25rem}.page-item.active .page-link,.page-item.active .page-link:focus,.page-item.active .page-link:hover{z-index:2;color:#fff;cursor:default;background-color:#5ED9C3;border-color:#5ED9C3}.page-item.disabled .page-link,.page-item.disabled .page-link:focus,.page-item.disabled .page-link:hover{color:#818a91;pointer-events:none;cursor:not-allowed;background-color:#fff;border-color:#ddd}.page-link{position:relative;float:left;padding:.5rem .75rem;margin-left:-1px;color:#5ED9C3;text-decoration:none;background-color:#fff;border:1px solid #ddd}.page-link:focus,.page-link:hover{color:#3d8b3d;background-color:#eceeef;border-color:#ddd}.pagination-lg .page-link{padding:.75rem 1.5rem;font-size:1.25rem}.pagination-lg .page-item:first-child .page-link{border-bottom-left-radius:.3rem;border-top-left-radius:.3rem}.pagination-lg .page-item:last-child .page-link{border-bottom-right-radius:.3rem;border-top-right-radius:.3rem}.pagination-sm .page-link{padding:.275rem .75rem;font-size:.875rem}.pagination-sm .page-item:first-child .page-link{border-bottom-left-radius:.2rem;border-top-left-radius:.2rem}.pagination-sm .page-item:last-child .page-link{border-bottom-right-radius:.2rem;border-top-right-radius:.2rem}.tag{display:inline-block;padding:.25em .4em;font-size:75%;font-weight:700;line-height:1;color:#fff;text-align:center;white-space:nowrap;vertical-align:baseline;border-radius:.25rem}.tag:empty{display:none}.btn .tag{position:relative;top:-1px}a.tag:focus,a.tag:hover{color:#fff;text-decoration:none;cursor:pointer}.tag-pill{padding-right:.6em;padding-left:.6em;border-radius:10rem}.tag-default{background-color:#818a91}.tag-default[href]:focus,.tag-default[href]:hover{background-color:#687077}.tag-primary{background-color:#5ED9C3}.tag-primary[href]:focus,.tag-primary[href]:hover{background-color:#38b5b3}.tag-success{background-color:#5ED9C3}.tag-success[href]:focus,.tag-success[href]:hover{background-color:#38b5b3}.tag-info{background-color:#5bc0de}.tag-info[href]:focus,.tag-info[href]:hover{background-color:#31b0d5}.tag-warning{background-color:#f0ad4e}.tag-warning[href]:focus,.tag-warning[href]:hover{background-color:#ec971f}.tag-danger{background-color:#b85c5c}.tag-danger[href]:focus,.tag-danger[href]:hover{background-color:#9d4444}.jumbotron{padding:2rem 1rem;margin-bottom:2rem;background-color:#eceeef;border-radius:.3rem}@media(min-width:544px){.jumbotron{padding:4rem 2rem}}.jumbotron-hr{border-top-color:#d0d5d8}.jumbotron-fluid{padding-right:0;padding-left:0;border-radius:0}.alert{padding:15px;margin-bottom:1rem;border:1px solid transparent;border-radius:.25rem}.alert-heading{color:inherit}.alert-link{font-weight:700}.alert-dismissible{padding-right:35px}.alert-dismissible .close{position:relative;top:-2px;right:-21px;color:inherit}.alert-success{background-color:#dff0d8;border-color:#d0e9c6;color:#3c763d}.alert-success hr{border-top-color:#c1e2b3}.alert-success .alert-link{color:#2b542c}.alert-info{background-color:#d9edf7;border-color:#bcdff1;color:#31708f}.alert-info hr{border-top-color:#a6d5ec}.alert-info .alert-link{color:#245269}.alert-warning{background-color:#fcf8e3;border-color:#faf2cc;color:#8a6d3b}.alert-warning hr{border-top-color:#f7ecb5}.alert-warning .alert-link{color:#66512c}.alert-danger{background-color:#f2dede;border-color:#ebcccc;color:#a94442}.alert-danger hr{border-top-color:#e4b9b9}.alert-danger .alert-link{color:#843534}@keyframes progress-bar-stripes{from{background-position:1rem 0}to{background-position:0 0}}.progress{display:block;width:100%;height:1rem;margin-bottom:1rem}.progress[value]{background-color:#eee;border:0;appearance:none;border-radius:.25rem}.progress[value]::-ms-fill{background-color:#0074d9;border:0}.progress[value]::-moz-progress-bar{background-color:#0074d9;border-bottom-left-radius:.25rem;border-top-left-radius:.25rem}.progress[value]::-webkit-progress-value{background-color:#0074d9;border-bottom-left-radius:.25rem;border-top-left-radius:.25rem}.progress[value="100"]::-moz-progress-bar{border-bottom-right-radius:.25rem;border-top-right-radius:.25rem}.progress[value="100"]::-webkit-progress-value{border-bottom-right-radius:.25rem;border-top-right-radius:.25rem}.progress[value]::-webkit-progress-bar{background-color:#eee;border-radius:.25rem}base::-moz-progress-bar,.progress[value]{background-color:#eee;border-radius:.25rem}@media screen and (min-width:0\0){.progress{background-color:#eee;border-radius:.25rem}.progress-bar{display:inline-block;height:1rem;text-indent:-999rem;background-color:#0074d9;border-bottom-left-radius:.25rem;border-top-left-radius:.25rem}.progress[width="100%"]{border-bottom-right-radius:.25rem;border-top-right-radius:.25rem}}.progress-striped[value]::-webkit-progress-value{background-image:linear-gradient(45deg,rgba(255,255,255,0.15) 25%,transparent 25%,transparent 50%,rgba(255,255,255,0.15) 50%,rgba(255,255,255,0.15) 75%,transparent 75%,transparent);background-size:1rem 1rem}.progress-striped[value]::-moz-progress-bar{background-image:linear-gradient(45deg,rgba(255,255,255,0.15) 25%,transparent 25%,transparent 50%,rgba(255,255,255,0.15) 50%,rgba(255,255,255,0.15) 75%,transparent 75%,transparent);background-size:1rem 1rem}.progress-striped[value]::-ms-fill{background-image:linear-gradient(45deg,rgba(255,255,255,0.15) 25%,transparent 25%,transparent 50%,rgba(255,255,255,0.15) 50%,rgba(255,255,255,0.15) 75%,transparent 75%,transparent);background-size:1rem 1rem}@media screen and (min-width:0\0){.progress-bar-striped{background-image:linear-gradient(45deg,rgba(255,255,255,0.15) 25%,transparent 25%,transparent 50%,rgba(255,255,255,0.15) 50%,rgba(255,255,255,0.15) 75%,transparent 75%,transparent);background-size:1rem 1rem}}.progress-animated[value]::-webkit-progress-value{animation:progress-bar-stripes 2s linear infinite}.progress-animated[value]::-moz-progress-bar{animation:progress-bar-stripes 2s linear infinite}@media screen and (min-width:0\0){.progress-animated .progress-bar-striped{animation:progress-bar-stripes 2s linear infinite}}.progress-success[value]::-webkit-progress-value{background-color:#5ED9C3}.progress-success[value]::-moz-progress-bar{background-color:#5ED9C3}.progress-success[value]::-ms-fill{background-color:#5ED9C3}@media screen and (min-width:0\0){.progress-success .progress-bar{background-color:#5ED9C3}}.progress-info[value]::-webkit-progress-value{background-color:#5bc0de}.progress-info[value]::-moz-progress-bar{background-color:#5bc0de}.progress-info[value]::-ms-fill{background-color:#5bc0de}@media screen and (min-width:0\0){.progress-info .progress-bar{background-color:#5bc0de}}.progress-warning[value]::-webkit-progress-value{background-color:#f0ad4e}.progress-warning[value]::-moz-progress-bar{background-color:#f0ad4e}.progress-warning[value]::-ms-fill{background-color:#f0ad4e}@media screen and (min-width:0\0){.progress-warning .progress-bar{background-color:#f0ad4e}}.progress-danger[value]::-webkit-progress-value{background-color:#b85c5c}.progress-danger[value]::-moz-progress-bar{background-color:#b85c5c}.progress-danger[value]::-ms-fill{background-color:#b85c5c}@media screen and (min-width:0\0){.progress-danger .progress-bar{background-color:#b85c5c}}.media{display:flex;margin-bottom:1rem}.media-body{flex:1}.media-middle{align-self:center}.media-bottom{align-self:flex-end}.media-object{display:block}.media-object.img-thumbnail{max-width:none}.media-right{padding-left:10px}.media-left{padding-right:10px}.media-heading{margin-top:0;margin-bottom:5px}.media-list{padding-left:0;list-style:none}.list-group{padding-left:0;margin-bottom:0}.list-group-item{position:relative;display:block;padding:.75rem 1.25rem;margin-bottom:-1px;background-color:#fff;border:1px solid #ddd}.list-group-item:first-child{border-top-right-radius:.25rem;border-top-left-radius:.25rem}.list-group-item:last-child{margin-bottom:0;border-bottom-right-radius:.25rem;border-bottom-left-radius:.25rem}.list-group-item.disabled,.list-group-item.disabled:focus,.list-group-item.disabled:hover{color:#818a91;cursor:not-allowed;background-color:#eceeef}.list-group-item.disabled .list-group-item-heading,.list-group-item.disabled:focus .list-group-item-heading,.list-group-item.disabled:hover .list-group-item-heading{color:inherit}.list-group-item.disabled .list-group-item-text,.list-group-item.disabled:focus .list-group-item-text,.list-group-item.disabled:hover .list-group-item-text{color:#818a91}.list-group-item.active,.list-group-item.active:focus,.list-group-item.active:hover{z-index:2;color:#fff;text-decoration:none;background-color:#5ED9C3;border-color:#5ED9C3}.list-group-item.active .list-group-item-heading,.list-group-item.active .list-group-item-heading>small,.list-group-item.active .list-group-item-heading>.small,.list-group-item.active:focus .list-group-item-heading,.list-group-item.active:focus .list-group-item-heading>small,.list-group-item.active:focus .list-group-item-heading>.small,.list-group-item.active:hover .list-group-item-heading,.list-group-item.active:hover .list-group-item-heading>small,.list-group-item.active:hover .list-group-item-heading>.small{color:inherit}.list-group-item.active .list-group-item-text,.list-group-item.active:focus .list-group-item-text,.list-group-item.active:hover .list-group-item-text{color:#eaf6ea}.list-group-flush .list-group-item{border-radius:0}.list-group-item-action{width:100%;color:#555;text-align:inherit}.list-group-item-action .list-group-item-heading{color:#333}.list-group-item-action:focus,.list-group-item-action:hover{color:#555;text-decoration:none;background-color:#f5f5f5}.list-group-item-success{color:#3c763d;background-color:#dff0d8}a.list-group-item-success,button.list-group-item-success{color:#3c763d}a.list-group-item-success .list-group-item-heading,button.list-group-item-success .list-group-item-heading{color:inherit}a.list-group-item-success:focus,a.list-group-item-success:hover,button.list-group-item-success:focus,button.list-group-item-success:hover{color:#3c763d;background-color:#d0e9c6}a.list-group-item-success.active,a.list-group-item-success.active:focus,a.list-group-item-success.active:hover,button.list-group-item-success.active,button.list-group-item-success.active:focus,button.list-group-item-success.active:hover{color:#fff;background-color:#3c763d;border-color:#3c763d}.list-group-item-info{color:#31708f;background-color:#d9edf7}a.list-group-item-info,button.list-group-item-info{color:#31708f}a.list-group-item-info .list-group-item-heading,button.list-group-item-info .list-group-item-heading{color:inherit}a.list-group-item-info:focus,a.list-group-item-info:hover,button.list-group-item-info:focus,button.list-group-item-info:hover{color:#31708f;background-color:#c4e3f3}a.list-group-item-info.active,a.list-group-item-info.active:focus,a.list-group-item-info.active:hover,button.list-group-item-info.active,button.list-group-item-info.active:focus,button.list-group-item-info.active:hover{color:#fff;background-color:#31708f;border-color:#31708f}.list-group-item-warning{color:#8a6d3b;background-color:#fcf8e3}a.list-group-item-warning,button.list-group-item-warning{color:#8a6d3b}a.list-group-item-warning .list-group-item-heading,button.list-group-item-warning .list-group-item-heading{color:inherit}a.list-group-item-warning:focus,a.list-group-item-warning:hover,button.list-group-item-warning:focus,button.list-group-item-warning:hover{color:#8a6d3b;background-color:#faf2cc}a.list-group-item-warning.active,a.list-group-item-warning.active:focus,a.list-group-item-warning.active:hover,button.list-group-item-warning.active,button.list-group-item-warning.active:focus,button.list-group-item-warning.active:hover{color:#fff;background-color:#8a6d3b;border-color:#8a6d3b}.list-group-item-danger{color:#a94442;background-color:#f2dede}a.list-group-item-danger,button.list-group-item-danger{color:#a94442}a.list-group-item-danger .list-group-item-heading,button.list-group-item-danger .list-group-item-heading{color:inherit}a.list-group-item-danger:focus,a.list-group-item-danger:hover,button.list-group-item-danger:focus,button.list-group-item-danger:hover{color:#a94442;background-color:#ebcccc}a.list-group-item-danger.active,a.list-group-item-danger.active:focus,a.list-group-item-danger.active:hover,button.list-group-item-danger.active,button.list-group-item-danger.active:focus,button.list-group-item-danger.active:hover{color:#fff;background-color:#a94442;border-color:#a94442}.list-group-item-heading{margin-top:0;margin-bottom:5px}.list-group-item-text{margin-bottom:0;line-height:1.3}.embed-responsive{position:relative;display:block;height:0;padding:0;overflow:hidden}.embed-responsive .embed-responsive-item,.embed-responsive iframe,.embed-responsive embed,.embed-responsive object,.embed-responsive video{position:absolute;top:0;bottom:0;left:0;width:100%;height:100%;border:0}.embed-responsive-21by9{padding-bottom:42.85714%}.embed-responsive-16by9{padding-bottom:56.25%}.embed-responsive-4by3{padding-bottom:75%}.embed-responsive-1by1{padding-bottom:100%}.close{float:right;font-size:1.5rem;font-weight:700;line-height:1;color:#000;text-shadow:0 1px 0 #fff;opacity:.2}.close:focus,.close:hover{color:#000;text-decoration:none;cursor:pointer;opacity:.5}button.close{padding:0;cursor:pointer;background:0 0;border:0;-webkit-appearance:none}.modal-open{overflow:hidden}.modal{position:fixed;top:0;right:0;bottom:0;left:0;z-index:1050;display:none;overflow:hidden;outline:0;-webkit-overflow-scrolling:touch}.modal.fade .modal-dialog{transition:transform .3s ease-out;transform:translate(0,-25%)}.modal.in .modal-dialog{transform:translate(0,0)}.modal-open .modal{overflow-x:hidden;overflow-y:auto}.modal-dialog{position:relative;width:auto;margin:10px}.modal-content{position:relative;background-color:#fff;background-clip:padding-box;border:1px solid rgba(0,0,0,.2);border-radius:.3rem;outline:0}.modal-backdrop{position:fixed;top:0;right:0;bottom:0;left:0;z-index:1040;background-color:#000}.modal-backdrop.fade{opacity:0}.modal-backdrop.in{opacity:.5}.modal-header{padding:15px;border-bottom:1px solid #e5e5e5}.modal-header::after{content:"";display:table;clear:both}.modal-header .close{margin-top:-2px}.modal-title{margin:0;line-height:1.5}.modal-body{position:relative;padding:15px}.modal-footer{padding:15px;text-align:right;border-top:1px solid #e5e5e5}.modal-footer::after{content:"";display:table;clear:both}.modal-scrollbar-measure{position:absolute;top:-9999px;width:50px;height:50px;overflow:scroll}@media(min-width:544px){.modal-dialog{max-width:600px;margin:30px auto}.modal-sm{max-width:300px}}@media(min-width:992px){.modal-lg{max-width:900px}}.tooltip{position:absolute;z-index:1070;display:block;font-family:source sans pro,sans-serif;font-style:normal;font-weight:400;letter-spacing:normal;line-break:auto;line-height:1.5;text-align:left;text-align:start;text-decoration:none;text-shadow:none;text-transform:none;white-space:normal;word-break:normal;word-spacing:normal;font-size:.875rem;word-wrap:break-word;opacity:0}.tooltip.in{opacity:.9}.tooltip.tooltip-top,.tooltip.bs-tether-element-attached-bottom{padding:5px 0;margin-top:-3px}.tooltip.tooltip-top .tooltip-arrow,.tooltip.bs-tether-element-attached-bottom .tooltip-arrow{bottom:0;left:50%;margin-left:-5px;border-width:5px 5px 0;border-top-color:#000}.tooltip.tooltip-right,.tooltip.bs-tether-element-attached-left{padding:0 5px;margin-left:3px}.tooltip.tooltip-right .tooltip-arrow,.tooltip.bs-tether-element-attached-left .tooltip-arrow{top:50%;left:0;margin-top:-5px;border-width:5px 5px 5px 0;border-right-color:#000}.tooltip.tooltip-bottom,.tooltip.bs-tether-element-attached-top{padding:5px 0;margin-top:3px}.tooltip.tooltip-bottom .tooltip-arrow,.tooltip.bs-tether-element-attached-top .tooltip-arrow{top:0;left:50%;margin-left:-5px;border-width:0 5px 5px;border-bottom-color:#000}.tooltip.tooltip-left,.tooltip.bs-tether-element-attached-right{padding:0 5px;margin-left:-3px}.tooltip.tooltip-left .tooltip-arrow,.tooltip.bs-tether-element-attached-right .tooltip-arrow{top:50%;right:0;margin-top:-5px;border-width:5px 0 5px 5px;border-left-color:#000}.tooltip-inner{max-width:200px;padding:3px 8px;color:#fff;text-align:center;background-color:#000;border-radius:.25rem}.tooltip-arrow{position:absolute;width:0;height:0;border-color:transparent;border-style:solid}.popover{position:absolute;top:0;left:0;z-index:1060;display:block;max-width:276px;padding:1px;font-family:source sans pro,sans-serif;font-style:normal;font-weight:400;letter-spacing:normal;line-break:auto;line-height:1.5;text-align:left;text-align:start;text-decoration:none;text-shadow:none;text-transform:none;white-space:normal;word-break:normal;word-spacing:normal;font-size:.875rem;word-wrap:break-word;background-color:#fff;background-clip:padding-box;border:1px solid rgba(0,0,0,.2);border-radius:.3rem}.popover.popover-top,.popover.bs-tether-element-attached-bottom{margin-top:-10px}.popover.popover-top .popover-arrow,.popover.bs-tether-element-attached-bottom .popover-arrow{bottom:-11px;left:50%;margin-left:-11px;border-top-color:rgba(0,0,0,.25);border-bottom-width:0}.popover.popover-top .popover-arrow::after,.popover.bs-tether-element-attached-bottom .popover-arrow::after{bottom:1px;margin-left:-10px;content:"";border-top-color:#fff;border-bottom-width:0}.popover.popover-right,.popover.bs-tether-element-attached-left{margin-left:10px}.popover.popover-right .popover-arrow,.popover.bs-tether-element-attached-left .popover-arrow{top:50%;left:-11px;margin-top:-11px;border-right-color:rgba(0,0,0,.25);border-left-width:0}.popover.popover-right .popover-arrow::after,.popover.bs-tether-element-attached-left .popover-arrow::after{bottom:-10px;left:1px;content:"";border-right-color:#fff;border-left-width:0}.popover.popover-bottom,.popover.bs-tether-element-attached-top{margin-top:10px}.popover.popover-bottom .popover-arrow,.popover.bs-tether-element-attached-top .popover-arrow{top:-11px;left:50%;margin-left:-11px;border-top-width:0;border-bottom-color:rgba(0,0,0,.25)}.popover.popover-bottom .popover-arrow::after,.popover.bs-tether-element-attached-top .popover-arrow::after{top:1px;margin-left:-10px;content:"";border-top-width:0;border-bottom-color:#fff}.popover.popover-left,.popover.bs-tether-element-attached-right{margin-left:-10px}.popover.popover-left .popover-arrow,.popover.bs-tether-element-attached-right .popover-arrow{top:50%;right:-11px;margin-top:-11px;border-right-width:0;border-left-color:rgba(0,0,0,.25)}.popover.popover-left .popover-arrow::after,.popover.bs-tether-element-attached-right .popover-arrow::after{right:1px;bottom:-10px;content:"";border-right-width:0;border-left-color:#fff}.popover-title{padding:8px 14px;margin:0;font-size:1rem;background-color:#f7f7f7;border-bottom:1px solid #ebebeb;border-radius:.2375rem .2375rem 0 0}.popover-content{padding:9px 14px}.popover-arrow,.popover-arrow::after{position:absolute;display:block;width:0;height:0;border-color:transparent;border-style:solid}.popover-arrow{border-width:11px}.popover-arrow::after{content:"";border-width:10px}.carousel{position:relative}.carousel-inner{position:relative;width:100%;overflow:hidden}.carousel-inner>.carousel-item{position:relative;display:none;transition:.6s ease-in-out left}.carousel-inner>.carousel-item>img,.carousel-inner>.carousel-item>a>img{line-height:1}@media all and (transform-3d),(-webkit-transform-3d){.carousel-inner>.carousel-item{transition:transform .6s ease-in-out;backface-visibility:hidden;perspective:1000px}.carousel-inner>.carousel-item.next,.carousel-inner>.carousel-item.active.right{left:0;transform:translate3d(100%,0,0)}.carousel-inner>.carousel-item.prev,.carousel-inner>.carousel-item.active.left{left:0;transform:translate3d(-100%,0,0)}.carousel-inner>.carousel-item.next.left,.carousel-inner>.carousel-item.prev.right,.carousel-inner>.carousel-item.active{left:0;transform:translate3d(0,0,0)}}.carousel-inner>.active,.carousel-inner>.next,.carousel-inner>.prev{display:block}.carousel-inner>.active{left:0}.carousel-inner>.next,.carousel-inner>.prev{position:absolute;top:0;width:100%}.carousel-inner>.next{left:100%}.carousel-inner>.prev{left:-100%}.carousel-inner>.next.left,.carousel-inner>.prev.right{left:0}.carousel-inner>.active.left{left:-100%}.carousel-inner>.active.right{left:100%}.carousel-control{position:absolute;top:0;bottom:0;left:0;width:15%;font-size:20px;color:#fff;text-align:center;text-shadow:0 1px 2px rgba(0,0,0,.6);opacity:.5}.carousel-control.left{background-image:linear-gradient(to right,rgba(0,0,0,0.5) 0%,rgba(0,0,0,0.0001) 100%);background-repeat:repeat-x;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#80000000',endColorstr='#00000000',GradientType=1)}.carousel-control.right{right:0;left:auto;background-image:linear-gradient(to right,rgba(0,0,0,0.0001) 0%,rgba(0,0,0,0.5) 100%);background-repeat:repeat-x;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#00000000',endColorstr='#80000000',GradientType=1)}.carousel-control:focus,.carousel-control:hover{color:#fff;text-decoration:none;outline:0;opacity:.9}.carousel-control .icon-prev,.carousel-control .icon-next{position:absolute;top:50%;z-index:5;display:inline-block;width:20px;height:20px;margin-top:-10px;font-family:serif;line-height:1}.carousel-control .icon-prev{left:50%;margin-left:-10px}.carousel-control .icon-next{right:50%;margin-right:-10px}.carousel-control .icon-prev::before{content:"\2039"}.carousel-control .icon-next::before{content:"\203a"}.carousel-indicators{position:absolute;bottom:10px;left:50%;z-index:15;width:60%;padding-left:0;margin-left:-30%;text-align:center;list-style:none}.carousel-indicators li{display:inline-block;width:10px;height:10px;margin:1px;text-indent:-999px;cursor:pointer;background-color:transparent;border:1px solid #fff;border-radius:10px}.carousel-indicators .active{width:12px;height:12px;margin:0;background-color:#fff}.carousel-caption{position:absolute;right:15%;bottom:20px;left:15%;z-index:10;padding-top:20px;padding-bottom:20px;color:#fff;text-align:center;text-shadow:0 1px 2px rgba(0,0,0,.6)}.carousel-caption .btn{text-shadow:none}@media(min-width:544px){.carousel-control .icon-prev,.carousel-control .icon-next{width:30px;height:30px;margin-top:-15px;font-size:30px}.carousel-control .icon-prev{margin-left:-15px}.carousel-control .icon-next{margin-right:-15px}.carousel-caption{right:20%;left:20%;padding-bottom:30px}.carousel-indicators{bottom:20px}}.bg-inverse{color:#eceeef;background-color:#373a3c}.bg-faded{background-color:#f7f7f9}.bg-primary{color:#fff!important;background-color:#5ED9C3!important}a.bg-primary:focus,a.bg-primary:hover{background-color:#38b5b3!important}.bg-success{color:#fff!important;background-color:#5ED9C3!important}a.bg-success:focus,a.bg-success:hover{background-color:#38b5b3!important}.bg-info{color:#fff!important;background-color:#5bc0de!important}a.bg-info:focus,a.bg-info:hover{background-color:#31b0d5!important}.bg-warning{color:#fff!important;background-color:#f0ad4e!important}a.bg-warning:focus,a.bg-warning:hover{background-color:#ec971f!important}.bg-danger{color:#fff!important;background-color:#b85c5c!important}a.bg-danger:focus,a.bg-danger:hover{background-color:#9d4444!important}.clearfix::after{content:"";display:table;clear:both}.pull-xs-left{float:left!important}.pull-xs-right{float:right!important}.pull-xs-none{float:none!important}@media(min-width:544px){.pull-sm-left{float:left!important}.pull-sm-right{float:right!important}.pull-sm-none{float:none!important}}@media(min-width:768px){.pull-md-left{float:left!important}.pull-md-right{float:right!important}.pull-md-none{float:none!important}}@media(min-width:992px){.pull-lg-left{float:left!important}.pull-lg-right{float:right!important}.pull-lg-none{float:none!important}}@media(min-width:1200px){.pull-xl-left{float:left!important}.pull-xl-right{float:right!important}.pull-xl-none{float:none!important}}.sr-only{position:absolute;width:1px;height:1px;padding:0;margin:-1px;overflow:hidden;clip:rect(0,0,0,0);border:0}.sr-only-focusable:active,.sr-only-focusable:focus{position:static;width:auto;height:auto;margin:0;overflow:visible;clip:auto}.m-x-auto{margin-right:auto!important;margin-left:auto!important}.m-a-0{margin:0!important}.m-t-0{margin-top:0!important}.m-r-0{margin-right:0!important}.m-b-0{margin-bottom:0!important}.m-l-0{margin-left:0!important}.m-x-0{margin-right:0!important;margin-left:0!important}.m-y-0{margin-top:0!important;margin-bottom:0!important}.m-a-1{margin:1rem!important}.m-t-1{margin-top:1rem!important}.m-r-1{margin-right:1rem!important}.m-b-1{margin-bottom:1rem!important}.m-l-1{margin-left:1rem!important}.m-x-1{margin-right:1rem!important;margin-left:1rem!important}.m-y-1{margin-top:1rem!important;margin-bottom:1rem!important}.m-a-2{margin:1.5rem!important}.m-t-2{margin-top:1.5rem!important}.m-r-2{margin-right:1.5rem!important}.m-b-2{margin-bottom:1.5rem!important}.m-l-2{margin-left:1.5rem!important}.m-x-2{margin-right:1.5rem!important;margin-left:1.5rem!important}.m-y-2{margin-top:1.5rem!important;margin-bottom:1.5rem!important}.m-a-3{margin:3rem!important}.m-t-3{margin-top:3rem!important}.m-r-3{margin-right:3rem!important}.m-b-3{margin-bottom:3rem!important}.m-l-3{margin-left:3rem!important}.m-x-3{margin-right:3rem!important;margin-left:3rem!important}.m-y-3{margin-top:3rem!important;margin-bottom:3rem!important}.p-a-0{padding:0!important}.p-t-0{padding-top:0!important}.p-r-0{padding-right:0!important}.p-b-0{padding-bottom:0!important}.p-l-0{padding-left:0!important}.p-x-0{padding-right:0!important;padding-left:0!important}.p-y-0{padding-top:0!important;padding-bottom:0!important}.p-a-1{padding:1rem!important}.p-t-1{padding-top:1rem!important}.p-r-1{padding-right:1rem!important}.p-b-1{padding-bottom:1rem!important}.p-l-1{padding-left:1rem!important}.p-x-1{padding-right:1rem!important;padding-left:1rem!important}.p-y-1{padding-top:1rem!important;padding-bottom:1rem!important}.p-a-2{padding:1.5rem!important}.p-t-2{padding-top:1.5rem!important}.p-r-2{padding-right:1.5rem!important}.p-b-2{padding-bottom:1.5rem!important}.p-l-2{padding-left:1.5rem!important}.p-x-2{padding-right:1.5rem!important;padding-left:1.5rem!important}.p-y-2{padding-top:1.5rem!important;padding-bottom:1.5rem!important}.p-a-3{padding:3rem!important}.p-t-3{padding-top:3rem!important}.p-r-3{padding-right:3rem!important}.p-b-3{padding-bottom:3rem!important}.p-l-3{padding-left:3rem!important}.p-x-3{padding-right:3rem!important;padding-left:3rem!important}.p-y-3{padding-top:3rem!important;padding-bottom:3rem!important}.pos-f-t{position:fixed;top:0;right:0;left:0;z-index:1030}.text-justify{text-align:justify!important}.text-nowrap{white-space:nowrap!important}.text-truncate{overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.text-xs-left{text-align:left!important}.text-xs-right{text-align:right!important}.text-xs-center{text-align:center!important}@media(min-width:544px){.text-sm-left{text-align:left!important}.text-sm-right{text-align:right!important}.text-sm-center{text-align:center!important}}@media(min-width:768px){.text-md-left{text-align:left!important}.text-md-right{text-align:right!important}.text-md-center{text-align:center!important}}@media(min-width:992px){.text-lg-left{text-align:left!important}.text-lg-right{text-align:right!important}.text-lg-center{text-align:center!important}}@media(min-width:1200px){.text-xl-left{text-align:left!important}.text-xl-right{text-align:right!important}.text-xl-center{text-align:center!important}}.text-lowercase{text-transform:lowercase!important}.text-uppercase{text-transform:uppercase!important}.text-capitalize{text-transform:capitalize!important}.font-weight-normal{font-weight:400}.font-weight-bold{font-weight:700}.font-italic{font-style:italic}.text-muted{color:#818a91!important}a.text-muted:focus,a.text-muted:hover{color:#687077}.text-primary{color:#5ED9C3!important}a.text-primary:focus,a.text-primary:hover{color:#38b5b3}.text-success{color:#5ED9C3!important}a.text-success:focus,a.text-success:hover{color:#38b5b3}.text-info{color:#5bc0de!important}a.text-info:focus,a.text-info:hover{color:#31b0d5}.text-warning{color:#f0ad4e!important}a.text-warning:focus,a.text-warning:hover{color:#ec971f}.text-danger{color:#b85c5c!important}a.text-danger:focus,a.text-danger:hover{color:#9d4444}.text-hide{font:0/0 a;color:transparent;text-shadow:none;background-color:transparent;border:0}.invisible{visibility:hidden!important}.hidden-xs-up{display:none!important}@media(max-width:543px){.hidden-xs-down{display:none!important}}@media(min-width:544px){.hidden-sm-up{display:none!important}}@media(max-width:767px){.hidden-sm-down{display:none!important}}@media(min-width:768px){.hidden-md-up{display:none!important}}@media(max-width:991px){.hidden-md-down{display:none!important}}@media(min-width:992px){.hidden-lg-up{display:none!important}}@media(max-width:1199px){.hidden-lg-down{display:none!important}}@media(min-width:1200px){.hidden-xl-up{display:none!important}}.hidden-xl-down{display:none!important}.visible-print-block{display:none!important}@media print{.visible-print-block{display:block!important}}.visible-print-inline{display:none!important}@media print{.visible-print-inline{display:inline!important}}.visible-print-inline-block{display:none!important}@media print{.visible-print-inline-block{display:inline-block!important}}@media print{.hidden-print{display:none!important}}.flex-xs-first{order:-1}.flex-xs-last{order:1}.flex-items-xs-top{align-items:flex-start}.flex-items-xs-middle{align-items:center}.flex-items-xs-bottom{align-items:flex-end}.flex-xs-top{align-self:flex-start}.flex-xs-middle{align-self:center}.flex-xs-bottom{align-self:flex-end}.flex-items-xs-left{justify-content:flex-start}.flex-items-xs-center{justify-content:center}.flex-items-xs-right{justify-content:flex-end}.flex-items-xs-around{justify-content:space-around}.flex-items-xs-between{justify-content:space-between}@media(min-width:544px){.flex-sm-first{order:-1}.flex-sm-last{order:1}}@media(min-width:544px){.flex-items-sm-top{align-items:flex-start}.flex-items-sm-middle{align-items:center}.flex-items-sm-bottom{align-items:flex-end}}@media(min-width:544px){.flex-sm-top{align-self:flex-start}.flex-sm-middle{align-self:center}.flex-sm-bottom{align-self:flex-end}}@media(min-width:544px){.flex-items-sm-left{justify-content:flex-start}.flex-items-sm-center{justify-content:center}.flex-items-sm-right{justify-content:flex-end}.flex-items-sm-around{justify-content:space-around}.flex-items-sm-between{justify-content:space-between}}@media(min-width:768px){.flex-md-first{order:-1}.flex-md-last{order:1}}@media(min-width:768px){.flex-items-md-top{align-items:flex-start}.flex-items-md-middle{align-items:center}.flex-items-md-bottom{align-items:flex-end}}@media(min-width:768px){.flex-md-top{align-self:flex-start}.flex-md-middle{align-self:center}.flex-md-bottom{align-self:flex-end}}@media(min-width:768px){.flex-items-md-left{justify-content:flex-start}.flex-items-md-center{justify-content:center}.flex-items-md-right{justify-content:flex-end}.flex-items-md-around{justify-content:space-around}.flex-items-md-between{justify-content:space-between}}@media(min-width:992px){.flex-lg-first{order:-1}.flex-lg-last{order:1}}@media(min-width:992px){.flex-items-lg-top{align-items:flex-start}.flex-items-lg-middle{align-items:center}.flex-items-lg-bottom{align-items:flex-end}}@media(min-width:992px){.flex-lg-top{align-self:flex-start}.flex-lg-middle{align-self:center}.flex-lg-bottom{align-self:flex-end}}@media(min-width:992px){.flex-items-lg-left{justify-content:flex-start}.flex-items-lg-center{justify-content:center}.flex-items-lg-right{justify-content:flex-end}.flex-items-lg-around{justify-content:space-around}.flex-items-lg-between{justify-content:space-between}}@media(min-width:1200px){.flex-xl-first{order:-1}.flex-xl-last{order:1}}@media(min-width:1200px){.flex-items-xl-top{align-items:flex-start}.flex-items-xl-middle{align-items:center}.flex-items-xl-bottom{align-items:flex-end}}@media(min-width:1200px){.flex-xl-top{align-self:flex-start}.flex-xl-middle{align-self:center}.flex-xl-bottom{align-self:flex-end}}@media(min-width:1200px){.flex-items-xl-left{justify-content:flex-start}.flex-items-xl-center{justify-content:center}.flex-items-xl-right{justify-content:flex-end}.flex-items-xl-around{justify-content:space-around}.flex-items-xl-between{justify-content:space-between}}.tag-default{color:#fff!important;font-size:.8rem;padding-top:.1rem;padding-bottom:.1rem;white-space:nowrap;margin-right:3px;margin-bottom:.2rem;display:inline-block}.tag-default:hover{text-decoration:none}.tag-default.tag-outline{border:1px solid #ddd;color:#aaa!important;background:0 0!important}ul.tag-list{padding-left:0!important;display:inline-block;list-style:none!important}ul.tag-list li{display:inline-block!important}.navbar-brand{font-family:titillium web,sans-serif;font-size:1.5rem!important;padding-top:0!important;margin-right:2rem!important;color:#5ED9C3!important}.nav-link .user-pic{height:26px;border-radius:50px;float:left;margin-right:5px}.nav-link:hover{transition:.1s all}.nav-pills.outline-active .nav-link{border-radius:0;border:none;border-bottom:2px solid transparent;background:0 0;color:#aaa}.nav-pills.outline-active .nav-link:hover{color:#555}.nav-pills.outline-active .nav-link.active{background:#fff!important;border-bottom:2px solid #5ED9C3!important;color:#5ED9C3!important}footer{background:#f3f3f3;margin-top:3rem;padding:1rem 0;position:absolute;bottom:0;width:100%}footer .logo-font{vertical-align:middle}footer .attribution{vertical-align:middle;margin-left:10px;font-size:.8rem;color:#bbb;font-weight:300}.error-messages{color:#b85c5c!important;font-weight:700}.banner{color:#fff;background:#333;padding:2rem;margin-bottom:2rem}.banner h1{text-shadow:0 1px 3px rgba(0,0,0,.3);margin-bottom:0}.container.page{margin-top:1.5rem}.preview-link{color:inherit!important}.preview-link:hover{text-decoration:inherit!important}.article-meta{display:block;position:relative;font-weight:300}.article-meta img{display:inline-block;vertical-align:middle;height:32px;width:32px;border-radius:30px}.article-meta .info{margin:0 1.5rem 0 .3rem;display:inline-block;vertical-align:middle;line-height:1rem}.article-meta .info .author{display:block;font-weight:500!important}.article-meta .info .date{color:#bbb;font-size:.8rem;display:block}.article-preview{border-top:1px solid rgba(0,0,0,.1);padding:1.5rem 0}.article-preview .article-meta{margin:0 0 1rem}.article-preview .preview-link h1{font-weight:600!important;font-size:1.5rem!important;margin-bottom:3px}.article-preview .preview-link p{font-weight:300;font-size:24px;color:#999;margin-bottom:15px;font-size:1rem;line-height:1.3rem}.article-preview .preview-link span{max-width:30%;font-size:.8rem;font-weight:300;color:#bbb;vertical-align:middle}.article-preview .preview-link ul{float:right;max-width:50%;vertical-align:top}.article-preview .preview-link ul li{font-weight:300;font-size:.8rem!important;padding-top:0!important;padding-bottom:0!important}.btn .counter{font-size:.8rem!important}.home-page .banner{background:#5ED9C3;box-shadow:inset 0 8px 8px -8px rgba(0,0,0,.3),inset 0 -8px 8px -8px rgba(0,0,0,.3)}.home-page .banner p{color:#fff;text-align:center;font-size:1.5rem;font-weight:300!important;margin-bottom:0}.home-page .banner h1{text-shadow:0 1px 3px rgba(0,0,0,.3);font-weight:700!important;text-align:center;font-size:3.5rem;padding-bottom:.5rem}.home-page .feed-toggle{margin-bottom:-1px}.home-page .sidebar{padding:5px 10px 10px;background:#f3f3f3;border-radius:4px}.home-page .sidebar p{margin-bottom:.2rem}.article-page .banner{padding:2rem 0}.article-page .banner h1{font-size:2.8rem;font-weight:600}.article-page .banner .btn{opacity:.8}.article-page .banner .btn:hover{transition:.1s all;opacity:1}.article-page .banner .article-meta{margin:2rem 0 0}.article-page .banner .article-meta .author{color:#fff}.article-page .article-content p{font-family:'source serif pro',serif;font-size:1.2rem;line-height:1.8rem;margin-bottom:2rem}.article-page .article-content h1,.article-page .article-content h2,.article-page .article-content h3,.article-page .article-content h4,.article-page .article-content h5,.article-page .article-content h6{font-weight:500!important;margin:1.6rem 0 1rem}.article-page .article-actions{text-align:center;margin:1.5rem 0 3rem}.article-page .article-actions .article-meta .info{text-align:left}.article-page .comment-form .card-block{padding:0}.article-page .comment-form .card-block textarea{border:0;padding:1.25rem}.article-page .comment-form .card-footer .btn{font-weight:700;float:right}.article-page .comment-form .card-footer .comment-author-img{position:absolute;top:110px;height:30px;width:30px}.article-page .card{border:1px solid #e5e5e5;box-shadow:none!important}.article-page .card .card-footer{border-top:1px solid #e5e5e5;box-shadow:none!important;font-size:.8rem;font-weight:300}.article-page .card .comment-author-img{display:inline-block;vertical-align:middle;height:20px;width:20px;border-radius:30px}.article-page .card .comment-author{display:inline-block;vertical-align:middle}.article-page .card .date-posted{display:inline-block;vertical-align:middle;margin-left:5px;color:#bbb}.article-page .card .mod-options{float:right;color:#333;font-size:1rem}.article-page .card .mod-options i{margin-left:5px;opacity:.6;cursor:pointer}.article-page .card .mod-options i:hover{opacity:1}.profile-page .user-info{text-align:center;background:#f3f3f3;padding:2rem 0 1rem}.profile-page .user-info .user-img{width:100px;height:100px;border-radius:100px;margin-bottom:1rem}.profile-page .user-info h4{font-weight:700}.profile-page .user-info p{margin:0 auto .5rem;color:#aaa;max-width:450px;font-weight:300}.profile-page .user-info .action-btn{float:right;color:#999;border:1px solid #999}.profile-page .articles-toggle{margin:1.5rem 0 -1px}.editor-page .tag-list i{font-size:.6rem;margin-right:5px;cursor:pointer} \ No newline at end of file diff --git a/te-frontend/public/word.json b/conduit-front/public/word.json similarity index 100% rename from te-frontend/public/word.json rename to conduit-front/public/word.json diff --git a/conduit-front/src/agent.js b/conduit-front/src/agent.js index 07d3ccf36f38aa158bcebe2fabeafd6be6d76c73..29d7e1430625b84242a1d6c26334cc75a5d70514 100644 --- a/conduit-front/src/agent.js +++ b/conduit-front/src/agent.js @@ -3,7 +3,7 @@ import _superagent from 'superagent'; const superagent = superagentPromise(_superagent, global.Promise); -const API_ROOT = 'http://vm3733.kaj.pouta.csc.fi:30000/api'; +const API_ROOT = process.env.REACT_APP_API_ROOT || 'https://conduit.productionready.io/api'; const encode = encodeURIComponent; const responseBody = res => res.body; @@ -16,8 +16,8 @@ const tokenPlugin = req => { } const requests = { - del: url => - superagent.del(`${API_ROOT}${url}`).use(tokenPlugin).then(responseBody), + del: (url, body) => + superagent.del(`${API_ROOT}${url}`, body).use(tokenPlugin).then(responseBody), get: url => superagent.get(`${API_ROOT}${url}`).use(tokenPlugin).then(responseBody), put: (url, body) => @@ -34,13 +34,16 @@ const Auth = { register: (username, email, password) => requests.post('/users', { user: { username, email, password } }), save: user => - requests.put('/user', { user }) + requests.put('/user', { user }), + delete: user => + requests.del('/user', { user }) }; const Tags = { getAll: () => requests.get('/tags') }; +//feed offset was 0 const limit = (count, p) => `limit=${count}&offset=${p ? p * count : 0}`; const omitSlug = article => Object.assign({}, article, { slug: undefined }) const Articles = { @@ -56,8 +59,8 @@ const Articles = { requests.post(`/articles/${slug}/favorite`), favoritedBy: (author, page) => requests.get(`/articles?favorited=${encode(author)}&${limit(5, page)}`), - feed: () => - requests.get('/articles/feed?limit=10&offset=0'), + feed: (page) => + requests.get(`/articles/feed?${limit(10, page)}`), get: slug => requests.get(`/articles/${slug}`), unfavorite: slug => diff --git a/te-frontend/src/components/App.css b/conduit-front/src/components/App.css similarity index 100% rename from te-frontend/src/components/App.css rename to conduit-front/src/components/App.css diff --git a/conduit-front/src/components/App.js b/conduit-front/src/components/App.js index 980ea72bb4f15a5104a845b6b9cdf03ade4fca9b..8a8ec8a23b958c83e7ff25cfb8b90f54edd99e80 100644 --- a/conduit-front/src/components/App.js +++ b/conduit-front/src/components/App.js @@ -6,6 +6,7 @@ import { APP_LOAD, REDIRECT } from '../constants/actionTypes'; import { Route, Switch } from 'react-router-dom'; import Article from '../components/Article'; import Editor from '../components/Editor'; +import edit from '../components/edit'; import Home from '../components/Home'; import Login from '../components/Login'; import Profile from '../components/Profile'; @@ -14,6 +15,8 @@ import Register from '../components/Register'; import Settings from '../components/Settings'; import { store } from '../store'; import { push } from 'react-router-redux'; +import './App.css'; +import Banner from './Home/Banner'; const mapStateToProps = state => { return { @@ -55,22 +58,23 @@ class App extends React.Component { <Header appName={this.props.appName} currentUser={this.props.currentUser} /> + <Banner/> <Switch> <Route exact path="/" component={Home}/> <Route path="/login" component={Login} /> <Route path="/register" component={Register} /> - <Route path="/editor/:slug" component={Editor} /> + <Route path="/edit/:slug" component={edit} /> <Route path="/editor" component={Editor} /> <Route path="/article/:id" component={Article} /> <Route path="/settings" component={Settings} /> <Route path="/@:username/favorites" component={ProfileFavorites} /> - <Route path="/@:username" component={Profile} /> + <Route path="/@:username" component={Profile} key={Math.random()}/> </Switch> </div> ); } return ( - <div> + <div className=""> <Header appName={this.props.appName} currentUser={this.props.currentUser} /> diff --git a/conduit-front/src/components/Article/ArticleActions.js b/conduit-front/src/components/Article/ArticleActions.js index 032e77da7b13d49c8a3960837f955b5a372e4b69..f1b5f5ba044b8f6418a53e825252d8124709598b 100644 --- a/conduit-front/src/components/Article/ArticleActions.js +++ b/conduit-front/src/components/Article/ArticleActions.js @@ -3,6 +3,7 @@ import React from 'react'; import agent from '../../agent'; import { connect } from 'react-redux'; import { DELETE_ARTICLE } from '../../constants/actionTypes'; +import swal from 'sweetalert'; const mapDispatchToProps = dispatch => ({ onClickDelete: payload => @@ -14,17 +15,37 @@ const ArticleActions = props => { const del = () => { props.onClickDelete(agent.Articles.del(article.slug)) }; + + function deleteArticle() { + swal({ + title: "Are you sure?", + text: "Article and comments will be deleted permanently!", + buttons: true, + dangerMode: true, + }) + .then((willDelete) => { + if (willDelete) { + swal("Your article has been deleted!", { + icon: "success", + content: del() + }); + } else { + swal("Canceled!"); + } + }); + } + if (props.canModify) { return ( <span> <Link - to={`/editor/${article.slug}`} + to={`/edit/${article.slug}`} className="btn btn-outline-secondary btn-sm"> <i className="ion-edit"></i> Edit Article </Link> - <button className="btn btn-outline-danger btn-sm" onClick={del}> + <button className="btn btn-outline-danger btn-sm" onClick={()=>deleteArticle()} style={{marginLeft: 10}}> <i className="ion-trash-a"></i> Delete Article </button> diff --git a/conduit-front/src/components/Article/ArticleMeta.js b/conduit-front/src/components/Article/ArticleMeta.js index a30b35aee8d30dbed52b3ba5cecd1e3c1965a909..c2f490b8d397957ad2f22211e628c81063b28096 100644 --- a/conduit-front/src/components/Article/ArticleMeta.js +++ b/conduit-front/src/components/Article/ArticleMeta.js @@ -1,13 +1,17 @@ import ArticleActions from './ArticleActions'; import { Link } from 'react-router-dom'; import React from 'react'; +import { TwitterShareButton } from "react-share"; const ArticleMeta = props => { const article = props.article; + const link = `${window.location.href}` + return ( - <div className="article-meta"> + <div className="article-meta" > <Link to={`/@${article.author.username}`}> - <img src={article.author.image} alt={article.author.username} /> + <img src={article.author.image} alt={article.author.username} + onError={e => { e.currentTarget.src = "https://static.productionready.io/images/smiley-cyrus.jpg"; }}/> </Link> <div className="info"> @@ -15,11 +19,14 @@ const ArticleMeta = props => { {article.author.username} </Link> <span className="date"> - {new Date(article.createdAt).toDateString()} + {new Date(article.createdAt).toLocaleString()} </span> </div> - - <ArticleActions canModify={props.canModify} article={article} /> + <ArticleActions canModify={props.canModify} article={article}/> + <TwitterShareButton url={link} style={{alignItems: 'right'}}> + {<a class="twitter-share-button" data-show-count="false" target="_blank" style={{margin: 10}}> + <i className="ion-social-twitter"></i>Tweet</a>} + </TwitterShareButton> </div> ); }; diff --git a/conduit-front/src/components/Article/Comment.js b/conduit-front/src/components/Article/Comment.js index adb34a7c2b204347369dd72c48b469a14dc1c1d0..adc2a66b0f6698f01298749a3ff365a696cd811a 100644 --- a/conduit-front/src/components/Article/Comment.js +++ b/conduit-front/src/components/Article/Comment.js @@ -9,7 +9,7 @@ const Comment = props => { return ( <div className="card"> <div className="card-block"> - <p className="card-text">{comment.body}</p> + <p className="card-text-mylly">{comment.body}</p> </div> <div className="card-footer"> <Link @@ -24,7 +24,7 @@ const Comment = props => { {comment.author.username} </Link> <span className="date-posted"> - {new Date(comment.createdAt).toDateString()} + {new Date(comment.createdAt).toLocaleString()} </span> <DeleteButton show={show} slug={props.slug} commentId={comment.id} /> </div> diff --git a/conduit-front/src/components/Article/CommentInput.js b/conduit-front/src/components/Article/CommentInput.js index 3007ab99a3fb7b869a3f28ed59e056cc2a2fab8c..91ca5a1e66063185ad0295a2169016316c22e549 100644 --- a/conduit-front/src/components/Article/CommentInput.js +++ b/conduit-front/src/components/Article/CommentInput.js @@ -2,6 +2,7 @@ import React from 'react'; import agent from '../../agent'; import { connect } from 'react-redux'; import { ADD_COMMENT } from '../../constants/actionTypes'; +import BadWords from '../Badwords'; const mapDispatchToProps = dispatch => ({ onSubmit: payload => @@ -20,6 +21,10 @@ class CommentInput extends React.Component { }; this.createComment = ev => { + if (this.state.body < 1) { + ev.preventDefault() + return null + } ev.preventDefault(); const payload = agent.Comments.create(this.props.slug, { body: this.state.body }); @@ -34,18 +39,26 @@ class CommentInput extends React.Component { <div className="card-block"> <textarea className="form-control" placeholder="Write a comment..." + maxlength="1000" value={this.state.body} onChange={this.setBody} - rows="3"> + rows="3" + onKeyDown={(e) => { + if (e.keyCode === 9) e.preventDefault(); + }}> </textarea> </div> + <div className="card-footer"> <img src={this.props.currentUser.image} - className="comment-author-img" - alt={this.props.currentUser.username} /> + className="comment-author-img-mylly" + alt={this.props.currentUser.username} + onError={e => { e.currentTarget.src = "https://static.productionready.io/images/smiley-cyrus.jpg"; }}/> + <BadWords comment={this.state.body}/> <button - className="btn btn-sm btn-primary" + className="btn-primary-mylly" + id="publish" type="submit"> Post Comment </button> diff --git a/conduit-front/src/components/Article/DeleteButton.js b/conduit-front/src/components/Article/DeleteButton.js index b07d65719a4ba65f3081d3d75142d455e19dca1e..59d70bd02b4dfa33dc86dfda5e277e0a562dbc09 100644 --- a/conduit-front/src/components/Article/DeleteButton.js +++ b/conduit-front/src/components/Article/DeleteButton.js @@ -2,6 +2,7 @@ import React from 'react'; import agent from '../../agent'; import { connect } from 'react-redux'; import { DELETE_COMMENT } from '../../constants/actionTypes'; +import swal from 'sweetalert'; const mapDispatchToProps = dispatch => ({ onClick: (payload, commentId) => @@ -14,10 +15,28 @@ const DeleteButton = props => { props.onClick(payload, props.commentId); }; + function deleteComment() { + swal({ + title: "Are you sure?", + text: "Comment will be deleted permanently!", + buttons: true, + dangerMode: true, + }) + .then((willDelete) => { + if (willDelete) { + swal("Your comment has been deleted!", { + icon: "success", + content: del() + }); + } else { + swal("Canceled!"); + } + }); + } if (props.show) { return ( <span className="mod-options"> - <i className="ion-trash-a" onClick={del}></i> + <i className="ion-trash-a" onClick={()=>deleteComment()}></i> </span> ); } diff --git a/conduit-front/src/components/Article/index.js b/conduit-front/src/components/Article/index.js index 6be8975f62940155903dae13b0ccfb28058f326d..23981143a4bb74c1c7e9688d0493b339b57c4e66 100644 --- a/conduit-front/src/components/Article/index.js +++ b/conduit-front/src/components/Article/index.js @@ -44,7 +44,8 @@ class Article extends React.Component { <div className="banner"> <div className="container"> - <h1>{this.props.article.title}</h1> + <h1 className="article-title-mylly">{this.props.article.title}</h1> + <h5 style={{ color: '#80ffee' }}>{this.props.article.description}</h5> <ArticleMeta article={this.props.article} canModify={canModify} /> @@ -57,7 +58,7 @@ class Article extends React.Component { <div className="row article-content"> <div className="col-xs-12"> - <div dangerouslySetInnerHTML={markup}></div> + <div className="article-text-mylly" dangerouslySetInnerHTML={markup}></div> <ul className="tag-list"> { diff --git a/conduit-front/src/components/ArticlePreview.js b/conduit-front/src/components/ArticlePreview.js index ae9151a54d8043cb76b97c74ab218381832bd775..2ac1cfc7f5b87897e8291d2c86d6427b8056423d 100644 --- a/conduit-front/src/components/ArticlePreview.js +++ b/conduit-front/src/components/ArticlePreview.js @@ -3,10 +3,16 @@ import { Link } from 'react-router-dom'; import agent from '../agent'; import { connect } from 'react-redux'; import { ARTICLE_FAVORITED, ARTICLE_UNFAVORITED } from '../constants/actionTypes'; +import swal from 'sweetalert'; const FAVORITED_CLASS = 'btn btn-sm btn-primary'; const NOT_FAVORITED_CLASS = 'btn btn-sm btn-outline-primary'; +const mapStateToProps = state => ({ + ...state.articleList, + token: state.common.token +}); + const mapDispatchToProps = dispatch => ({ favorite: slug => dispatch({ type: ARTICLE_FAVORITED, @@ -25,19 +31,24 @@ const ArticlePreview = props => { NOT_FAVORITED_CLASS; const handleClick = ev => { - ev.preventDefault(); - if (article.favorited) { - props.unfavorite(article.slug); + if (props.token) { + ev.preventDefault(); + if (article.favorited) { + props.unfavorite(article.slug); + } else { + props.favorite(article.slug); + } } else { - props.favorite(article.slug); + swal("Sign in to favorite article", {buttons: false, timer: 1800}) } }; return ( - <div className="article-preview"> + <div className="article-preview" style={{marginBottom:5}}> <div className="article-meta"> <Link to={`/@${article.author.username}`}> - <img src={article.author.image} alt={article.author.username} /> + <img src={article.author.image} alt={article.author.username} + onError={e => { e.currentTarget.src = "https://static.productionready.io/images/smiley-cyrus.jpg"; }}/> </Link> <div className="info"> @@ -45,7 +56,7 @@ const ArticlePreview = props => { {article.author.username} </Link> <span className="date"> - {new Date(article.createdAt).toDateString()} + {new Date(article.createdAt).toLocaleString()} </span> </div> @@ -57,8 +68,8 @@ const ArticlePreview = props => { </div> <Link to={`/article/${article.slug}`} className="preview-link"> - <h1>{article.title}</h1> - <p>{article.description}</p> + <h1 className="preview-link-mylly">{article.title}</h1> + <p className="preview-link-mylly">{article.description}</p> <span>Read more...</span> <ul className="tag-list"> { @@ -76,4 +87,4 @@ const ArticlePreview = props => { ); } -export default connect(() => ({}), mapDispatchToProps)(ArticlePreview); +export default connect(mapStateToProps, mapDispatchToProps)(ArticlePreview); diff --git a/te-frontend/src/components/Badwords.js b/conduit-front/src/components/Badwords.js similarity index 100% rename from te-frontend/src/components/Badwords.js rename to conduit-front/src/components/Badwords.js diff --git a/te-frontend/src/components/Documents/Tietosuojaseloste.pdf b/conduit-front/src/components/Documents/Tietosuojaseloste.pdf similarity index 100% rename from te-frontend/src/components/Documents/Tietosuojaseloste.pdf rename to conduit-front/src/components/Documents/Tietosuojaseloste.pdf diff --git a/conduit-front/src/components/Editor.js b/conduit-front/src/components/Editor.js index 93d9d19fd3d5564fd2911c6c34e2baf9cf9c1d0e..a912818e453a3aa7b6ec38b58561bfe185233879 100644 --- a/conduit-front/src/components/Editor.js +++ b/conduit-front/src/components/Editor.js @@ -2,6 +2,7 @@ import ListErrors from './ListErrors'; import React from 'react'; import agent from '../agent'; import { connect } from 'react-redux'; +import Badwords from './Badwords'; import { ADD_TAG, EDITOR_PAGE_LOADED, @@ -11,6 +12,7 @@ import { UPDATE_FIELD_EDITOR } from '../constants/actionTypes'; + const mapStateToProps = state => ({ ...state.editor }); @@ -30,6 +32,7 @@ const mapDispatchToProps = dispatch => ({ dispatch({ type: UPDATE_FIELD_EDITOR, key, value }) }); + class Editor extends React.Component { constructor() { super(); @@ -41,10 +44,28 @@ class Editor extends React.Component { this.changeBody = updateFieldEvent('body'); this.changeTagInput = updateFieldEvent('tagInput'); + const errors = (key, error) => this.props.onUpdateField(key, error); + this.watchForEnter = ev => { + if (this.props.tagInput < 1 || (this.props.tagList || []).length > 3) { + errors('errors', {'Tags:': 'Empty tags not allowed'}); + return null + } if (ev.keyCode === 13) { ev.preventDefault(); this.props.onAddTag(); + errors('errors', {}); + } + }; + + this.clickToAdd = ev => { + if (this.props.tagInput < 1 || (this.props.tagList || []).length > 3) { + errors('errors', {'Tags:': 'Empty tags not allowed'}); + return null + } else { + ev.preventDefault(); + this.props.onAddTag(); + errors('errors', {}); } }; @@ -53,6 +74,9 @@ class Editor extends React.Component { }; this.submitForm = ev => { + if (!this.props.title||!this.props.body||!this.props.description) { + errors('errors', {'Please,': 'fill title and body fields.'}); + } else { ev.preventDefault(); const article = { title: this.props.title, @@ -60,14 +84,13 @@ class Editor extends React.Component { body: this.props.body, tagList: this.props.tagList }; - const slug = { slug: this.props.articleSlug }; const promise = this.props.articleSlug ? agent.Articles.update(Object.assign(article, slug)) : agent.Articles.create(article); this.props.onSubmit(promise); - }; + }}; } componentWillReceiveProps(nextProps) { @@ -97,9 +120,7 @@ class Editor extends React.Component { <div className="container page"> <div className="row"> <div className="col-md-10 offset-md-1 col-xs-12"> - - <ListErrors errors={this.props.errors}></ListErrors> - + <form> <fieldset> @@ -108,7 +129,8 @@ class Editor extends React.Component { className="form-control form-control-lg" type="text" placeholder="Article Title" - value={this.props.title} + maxlength="100" + value={this.props.title || ""} onChange={this.changeTitle} /> </fieldset> @@ -117,7 +139,8 @@ class Editor extends React.Component { className="form-control" type="text" placeholder="What's this article about?" - value={this.props.description} + maxlength="140" + value={this.props.description || ""} onChange={this.changeDescription} /> </fieldset> @@ -126,7 +149,8 @@ class Editor extends React.Component { className="form-control" rows="8" placeholder="Write your article (in markdown)" - value={this.props.body} + maxlength="3000" + value={this.props.body || ""} onChange={this.changeBody}> </textarea> </fieldset> @@ -135,10 +159,15 @@ class Editor extends React.Component { <input className="form-control" type="text" - placeholder="Give one tag at a time and press Enter" - value={this.props.tagInput} + placeholder="Enter tags" + maxlength="15" + value={this.props.tagInput ||""} onChange={this.changeTagInput} - onKeyUp={this.watchForEnter} /> + onKeyUp={this.watchForEnter} + onKeyDown={(e) => { + if (e.keyCode === 9) e.preventDefault(); + }}/> + <span onClick={this.clickToAdd} className="tag-enter">Press Enter or Click to Add</span> <div className="tag-list"> { @@ -156,17 +185,33 @@ class Editor extends React.Component { </div> </fieldset> + <button + className="btn btn-lg pull-xs-right btn-danger m-l-1" + type="button" + onClick={() => this.props.history.goBack()}> + Cancel + </button> + <button className="btn btn-lg pull-xs-right btn-primary" type="button" - disabled={this.props.inProgress} + id="publish" + disabled={false} onClick={this.submitForm}> Publish Article </button> + <Badwords title={this.props.title} + description={this.props.description} + body={this.props.body} + tags={this.props.tagInput} + tagList={this.props.tagList || []} + /> + + <ListErrors errors={this.props.errors}></ListErrors> + </fieldset> </form> - </div> </div> </div> diff --git a/conduit-front/src/components/Header.js b/conduit-front/src/components/Header.js index 63641b8ceddbf3f5a0986ba6d3703cb3474089e7..de49981198854cdf0eb4321f149474b359186659 100644 --- a/conduit-front/src/components/Header.js +++ b/conduit-front/src/components/Header.js @@ -1,5 +1,7 @@ import React from 'react'; import { Link } from 'react-router-dom'; +import logo from './images/logo-sm.svg' + const LoggedOutView = props => { if (!props.currentUser) { @@ -7,19 +9,19 @@ const LoggedOutView = props => { <ul className="nav navbar-nav pull-xs-right"> <li className="nav-item"> - <Link to="/" className="nav-link"> + <Link to="/" className="nav-link-mylly"> Home </Link> </li> <li className="nav-item"> - <Link to="/login" className="nav-link"> + <Link to="/login" className="nav-link-mylly"> Sign in </Link> </li> <li className="nav-item"> - <Link to="/register" className="nav-link"> + <Link to="/register" className="nav-link-mylly"> Sign up </Link> </li> @@ -30,35 +32,36 @@ const LoggedOutView = props => { return null; }; +// li classname was "nav-item" const LoggedInView = props => { if (props.currentUser) { return ( <ul className="nav navbar-nav pull-xs-right"> - <li className="nav-item"> - <Link to="/" className="nav-link"> + <li className="nav-link-mylly"> + <Link to="/" className="nav-link-mylly"> Home </Link> </li> - <li className="nav-item"> - <Link to="/editor" className="nav-link"> + <li className="nav-link-mylly"> + <Link to="/editor" className="nav-link-mylly"> <i className="ion-compose"></i> New Post </Link> </li> - <li className="nav-item"> - <Link to="/settings" className="nav-link"> + <li className="nav-link-mylly"> + <Link to="/settings" className="nav-link-mylly"> <i className="ion-gear-a"></i> Settings </Link> </li> - - <li className="nav-item"> + + <li className="nav-link-mylly"> <Link to={`/@${props.currentUser.username}`} - className="nav-link"> - <img src={props.currentUser.image} className="user-pic" alt={props.currentUser.username} /> - {props.currentUser.username} + className="nav-link-mylly-user"> + <img src={props.currentUser.image} className="user-pic-mylly" title={props.currentUser.username} style={{marginLeft:10}}alt={props.currentUser.username} + onError={e => { e.currentTarget.src = "https://static.productionready.io/images/smiley-cyrus.jpg"; }}/> </Link> </li> @@ -72,11 +75,11 @@ const LoggedInView = props => { class Header extends React.Component { render() { return ( - <nav className="navbar navbar-light"> - <div className="container"> - - <Link to="/" className="navbar-brand"> - {this.props.appName.toLowerCase()} + <nav className="navbar-mylly"> + <div className="container-mylly"> + <Link to={{ pathname: "https://www.wimmalab.org/" }} target="_blank" className="navbar-brand-mylly"> + <img src={logo} alt="logo" height="20px"/> + {/* {this.props.appName.toLowerCase()} */} </Link> <LoggedOutView currentUser={this.props.currentUser} /> diff --git a/conduit-front/src/components/Home/Banner.js b/conduit-front/src/components/Home/Banner.js index dfb56908d34c9d8d50dcd1e780709bd048dc9508..cc3d52b4d6294c785061b96c2d0fb48d85204a6b 100644 --- a/conduit-front/src/components/Home/Banner.js +++ b/conduit-front/src/components/Home/Banner.js @@ -1,15 +1,18 @@ import React from 'react'; +import logo from '../images/logo_wimma.png' +import { Link } from 'react-router-dom'; +import homehero from '../images/home-hero.png' const Banner = ({ appName, token }) => { if (token) { return null; } - return ( - <div className="banner"> + return ( // style changed to black + <div className="banner-mylly" style={{ backgroundImage: `url(${homehero})`}}> <div className="container"> - <h1 className="logo-font"> - {appName.toLowerCase()} - </h1> + <Link to="/"> + <img src={logo} alt="logo" height="100px"/> + </Link> <p>A place to share your knowledge.</p> </div> </div> diff --git a/conduit-front/src/components/Home/MainView.js b/conduit-front/src/components/Home/MainView.js index bd581f22c6196a67b043b9c658e6af41e7084bc0..c687523144b7bc367e2272ee0a5d6760650cef47 100644 --- a/conduit-front/src/components/Home/MainView.js +++ b/conduit-front/src/components/Home/MainView.js @@ -47,7 +47,7 @@ const TagFilterTab = props => { } return ( - <li className="nav-item"> + <li className="nav-item" style={{color:'blue'}}> <a href="" className="nav-link active"> <i className="ion-pound"></i> {props.tag} </a> diff --git a/conduit-front/src/components/Home/index.js b/conduit-front/src/components/Home/index.js index 37fddd1cb41efcabf0b364c206dc3471ddb5830e..0ed4ea7116d50ed58f1fc6a244c0830a54d226ce 100644 --- a/conduit-front/src/components/Home/index.js +++ b/conduit-front/src/components/Home/index.js @@ -1,4 +1,4 @@ -import Banner from './Banner'; +// import Banner from './Banner'; import MainView from './MainView'; import React from 'react'; import Tags from './Tags'; @@ -9,6 +9,7 @@ import { HOME_PAGE_UNLOADED, APPLY_TAG_FILTER } from '../../constants/actionTypes'; +import Pdf from '../Documents/Tietosuojaseloste.pdf'; const Promise = global.Promise; @@ -34,12 +35,7 @@ class Home extends React.Component { agent.Articles.feed : agent.Articles.all; - /*this.props.onLoad(tab, articlesPromise, Promise.all([agent.Tags.getAll(), articlesPromise()]));*/ - Promise - .all([agent.Tags.getAll(), articlesPromise()]) - .then((result) => { - this.props.onLoad(tab, articlesPromise, result); - }); + this.props.onLoad(tab, articlesPromise, Promise.all([agent.Tags.getAll(), articlesPromise()])); } componentWillUnmount() { @@ -50,7 +46,7 @@ class Home extends React.Component { return ( <div className="home-page"> - <Banner token={this.props.token} appName={this.props.appName} /> + {/* <Banner token={this.props.token} appName={this.props.appName} /> */} <div className="container page"> <div className="row"> @@ -67,9 +63,12 @@ class Home extends React.Component { </div> </div> + </div> </div> - + <div className="footer"> + <a href = {Pdf} target = "_blank"><p>Privacy policy</p></a> + </div> </div> ); } diff --git a/conduit-front/src/components/Login.js b/conduit-front/src/components/Login.js index c63a0fa6cb4c6629323412d2b19332b3065d441d..a6e895a5ccd61810fb3fb7230a8f63c8249b6e37 100644 --- a/conduit-front/src/components/Login.js +++ b/conduit-front/src/components/Login.js @@ -8,6 +8,8 @@ import { LOGIN, LOGIN_PAGE_UNLOADED } from '../constants/actionTypes'; +import ShowIcon from '@material-ui/icons/Visibility'; +import ShowOffIcon from '@material-ui/icons/VisibilityOff'; const mapStateToProps = state => ({ ...state.auth }); @@ -25,6 +27,9 @@ const mapDispatchToProps = dispatch => ({ class Login extends React.Component { constructor() { super(); + this.state = { + password: true, + }; this.changeEmail = ev => this.props.onChangeEmail(ev.target.value); this.changePassword = ev => this.props.onChangePassword(ev.target.value); this.submitForm = (email, password) => ev => { @@ -53,8 +58,6 @@ class Login extends React.Component { </Link> </p> - <ListErrors errors={this.props.errors} /> - <form onSubmit={this.submitForm(email, password)}> <fieldset> @@ -70,12 +73,19 @@ class Login extends React.Component { <fieldset className="form-group"> <input className="form-control form-control-lg" - type="password" + type={this.state.password ? "password":"text" } placeholder="Password" value={password} onChange={this.changePassword} /> + <span + className="hideshowbtn" + style={{ top: 173 }} + onMouseDown={()=> this.setState({password:!this.state.password})}> + {this.state.password ? <ShowOffIcon/>:<ShowIcon/>} + </span> </fieldset> + <button className="btn btn-lg btn-primary pull-xs-right" type="submit" @@ -83,6 +93,8 @@ class Login extends React.Component { Sign in </button> + <ListErrors errors={this.props.errors} /> + </fieldset> </form> </div> diff --git a/conduit-front/src/components/Profile.js b/conduit-front/src/components/Profile.js index a3a5e6d1455ccfbef23393020a09f5a82d8a2127..031026349af524fc5122ea733dd51d032e0d6fb3 100644 --- a/conduit-front/src/components/Profile.js +++ b/conduit-front/src/components/Profile.js @@ -7,21 +7,10 @@ import { FOLLOW_USER, UNFOLLOW_USER, PROFILE_PAGE_LOADED, - PROFILE_PAGE_UNLOADED + PROFILE_PAGE_UNLOADED, + LOGOUT } from '../constants/actionTypes'; - -const EditProfileSettings = props => { - if (props.isUser) { - return ( - <Link - to="/settings" - className="btn btn-sm btn-outline-secondary action-btn"> - <i className="ion-gear-a"></i> Edit Profile Settings - </Link> - ); - } - return null; -}; +import swal from 'sweetalert'; const FollowUserButton = props => { if (props.isUser) { @@ -36,13 +25,16 @@ const FollowUserButton = props => { } const handleClick = ev => { - ev.preventDefault(); - if (props.user.following) { - props.unfollow(props.user.username) + if (props.isUser === null) { + swal("Sign in to follow user", {buttons: false, timer: 1900}) } else { - props.follow(props.user.username) - } - }; + ev.preventDefault(); + if (props.user.following) { + props.unfollow(props.user.username) + } else { + props.follow(props.user.username) + } + }}; return ( <button @@ -62,6 +54,7 @@ const mapStateToProps = state => ({ }); const mapDispatchToProps = dispatch => ({ + onClickLogout: () => dispatch({ type: LOGOUT }), onFollow: username => dispatch({ type: FOLLOW_USER, payload: agent.Profile.follow(username) @@ -109,23 +102,48 @@ class Profile extends React.Component { } render() { + + const EditProfileSettings = props => { + if (props.isUser) { + return ( + <div> + <Link + to="/settings" + className="btn btn-sm btn-outline-secondary action-btn"> + <i className="ion-gear-a"></i> Edit Profile Settings + </Link> + <button + className="btn btn-sm btn-outline-secondary action-btn" + style={{ + color: '#D56047', + borderColor: 'red', + marginRight:10}} + onClick={this.props.onClickLogout}> + <i className="ion-log-out"></i> Logout + </button> + </div> + ); + } + return null; + }; + const profile = this.props.profile; if (!profile) { return null; } - const isUser = this.props.currentUser && this.props.profile.username === this.props.currentUser.username; return ( <div className="profile-page"> - <div className="user-info"> + <div className="user-info" > <div className="container"> <div className="row"> <div className="col-xs-12 col-md-10 offset-md-1"> - <img src={profile.image} className="user-img" alt={profile.username} /> + <img src={profile.image} className="user-img" alt={profile.username} + onError={e => { e.currentTarget.src = "https://static.productionready.io/images/smiley-cyrus.jpg"; }}/> <h4>{profile.username}</h4> <p>{profile.bio}</p> diff --git a/conduit-front/src/components/Register.js b/conduit-front/src/components/Register.js index ff81749720f1d86cd0bc1e8881767b04f894c26d..82f74155398dd518bf94cbbb4142b27fbfdf5049 100644 --- a/conduit-front/src/components/Register.js +++ b/conduit-front/src/components/Register.js @@ -8,6 +8,9 @@ import { REGISTER, REGISTER_PAGE_UNLOADED } from '../constants/actionTypes'; +import swal from 'sweetalert'; +import ShowIcon from '@material-ui/icons/Visibility'; +import ShowOffIcon from '@material-ui/icons/VisibilityOff'; const mapStateToProps = state => ({ ...state.auth }); @@ -16,10 +19,12 @@ const mapDispatchToProps = dispatch => ({ dispatch({ type: UPDATE_FIELD_AUTH, key: 'email', value }), onChangePassword: value => dispatch({ type: UPDATE_FIELD_AUTH, key: 'password', value }), + onConfirmPassword: value => + dispatch({ type: UPDATE_FIELD_AUTH, key: 'confirmPassword', value }), onChangeUsername: value => dispatch({ type: UPDATE_FIELD_AUTH, key: 'username', value }), - onSubmit: (username, email, password) => { - const payload = agent.Auth.register(username, email, password); + onSubmit: (username, email, password,confirmPassword) => { + const payload = agent.Auth.register(username, email, password,confirmPassword); dispatch({ type: REGISTER, payload }) }, onUnload: () => @@ -29,12 +34,34 @@ const mapDispatchToProps = dispatch => ({ class Register extends React.Component { constructor() { super(); + this.state = { + password: true, + confirmPassword:true + }; this.changeEmail = ev => this.props.onChangeEmail(ev.target.value); this.changePassword = ev => this.props.onChangePassword(ev.target.value); + this.changeConfirmPassword = ev => this.props.onConfirmPassword(ev.target.value); this.changeUsername = ev => this.props.onChangeUsername(ev.target.value); - this.submitForm = (username, email, password) => ev => { + this.submitForm = (username, email, password,confirmPassword) => ev => { ev.preventDefault(); - this.props.onSubmit(username, email, password); + + const regex = /^\S{4,}$/; + if (!username||!email||!password) { + swal("All fields required") + return null; + } + + if(!regex.test(password)){ + + swal("Password at least 4 digits! No whitespaces") + return null; + } + if(password !== confirmPassword ) { + + swal("Passwords does not match ") + return null; + } + this.props.onSubmit(username, email, password,confirmPassword); } } @@ -45,6 +72,7 @@ class Register extends React.Component { render() { const email = this.props.email; const password = this.props.password; + const confirmPassword = this.props.confirmPassword; const username = this.props.username; return ( @@ -60,45 +88,68 @@ class Register extends React.Component { </Link> </p> - <ListErrors errors={this.props.errors} /> - <form onSubmit={this.submitForm(username, email, password)}> + <form onSubmit={this.submitForm(username, email, password,confirmPassword)}> <fieldset> - + <fieldset className="form-group"> - <input + <input + pattern=".{1,}" className="form-control form-control-lg" type="text" placeholder="Username" - value={this.props.username} + value={this.props.username || ""} onChange={this.changeUsername} /> </fieldset> - + <fieldset className="form-group"> <input className="form-control form-control-lg" type="email" placeholder="Email" - value={this.props.email} + value={this.props.email || ""} onChange={this.changeEmail} /> </fieldset> <fieldset className="form-group"> <input className="form-control form-control-lg" - type="password" + type={this.state.password ? "password":"text" } placeholder="Password" - value={this.props.password} + value={this.props.password || ""} onChange={this.changePassword} /> + <span + className="hideshowbtn" + style={{top: 240}} + onMouseDown={()=> this.setState({password:!this.state.password})}> + {this.state.password ? <ShowOffIcon/>:<ShowIcon/>} + </span> </fieldset> - + <fieldset className="form-group"> + <input + className="form-control form-control-lg" + type={this.state.confirmPassword ? "password":"text" } + placeholder="Confirm Password" + value={this.props.confirmPassword } + onChange={this.changeConfirmPassword} /> + <span + className="hideshowbtn" style={{top: 305}} + onMouseDown={()=> this.setState({confirmPassword:!this.state.confirmPassword})}> + {this.state.confirmPassword ? <ShowOffIcon/>:<ShowIcon/>} + </span> + </fieldset> + <button className="btn btn-lg btn-primary pull-xs-right" type="submit" - disabled={this.props.inProgress}> + disabled={this.props.inProgress + }> Sign up </button> + <ListErrors errors={this.props.errors} /> + + </fieldset> </form> </div> diff --git a/conduit-front/src/components/Settings.js b/conduit-front/src/components/Settings.js index 265b17a091a864076ba637b23b3662af26914e31..63a70fa4623ca508d16d196cb7bb37f082dbd773 100644 --- a/conduit-front/src/components/Settings.js +++ b/conduit-front/src/components/Settings.js @@ -5,8 +5,12 @@ import { connect } from 'react-redux'; import { SETTINGS_SAVED, SETTINGS_PAGE_UNLOADED, - LOGOUT + LOGOUT, + DELETE_USER } from '../constants/actionTypes'; +import swal from 'sweetalert'; +import ShowIcon from '@material-ui/icons/Visibility'; +import ShowOffIcon from '@material-ui/icons/VisibilityOff'; class SettingsForm extends React.Component { constructor() { @@ -17,7 +21,8 @@ class SettingsForm extends React.Component { username: '', bio: '', email: '', - password: '' + password: '', + confirmPassword: "" }; this.updateState = field => ev => { @@ -28,13 +33,35 @@ class SettingsForm extends React.Component { this.submitForm = ev => { ev.preventDefault(); - + const regexp = /^\S*$/;//ei välilyöntejä const user = Object.assign({}, this.state); + if(!regexp.test(user.password) || !regexp.test(user.confirmPassword)){ + swal(" Whitespace not allowed") + return null; + } + + if(user.password !== user.confirmPassword ) { + + swal("Passwords does not match ") + return null; + } + if (!user.password) { - delete user.password; + delete user.password; } - + else if(user.password.length < 4){ + swal("Password too short min 4 digits ") + return + } + else{ + swal({title: 'Password changed' , + timer: 1500, + icon: 'success', + showConfirmButton: false,}) + } + this.props.onSubmitForm(user); + }; } @@ -105,10 +132,29 @@ class SettingsForm extends React.Component { <fieldset className="form-group"> <input className="form-control form-control-lg" - type="password" + type={this.state.password ? "password":"text" } placeholder="New Password" - value={this.state.password} + //value={this.state.password} onChange={this.updateState('password')} /> + <span + className="hideshowbtn" style={{top: '67%'}} + onMouseDown={()=> this.setState({password:!this.state.password})}> + {this.state.password ? <ShowOffIcon/>:<ShowIcon/>} + </span> + </fieldset> + + <fieldset className="form-group"> + <input + className="form-control form-control-lg" + type={this.state.confirmPassword ? "password":"text" } + placeholder="Confirm Password" + // value={this.state.confirmPassword } + onChange={this.updateState('confirmPassword')} /> + <span + className="hideshowbtn" style={{top: '76%'}} + onMouseDown={()=> this.setState({confirmPassword:!this.state.confirmPassword})}> + {this.state.confirmPassword ? <ShowOffIcon/>:<ShowIcon/>} + </span> </fieldset> <button @@ -131,12 +177,36 @@ const mapStateToProps = state => ({ const mapDispatchToProps = dispatch => ({ onClickLogout: () => dispatch({ type: LOGOUT }), + onClickDeleteUser: user => + dispatch({ type: DELETE_USER, payload: agent.Auth.delete(user) }), onSubmitForm: user => dispatch({ type: SETTINGS_SAVED, payload: agent.Auth.save(user) }), onUnload: () => dispatch({ type: SETTINGS_PAGE_UNLOADED }) }); + class Settings extends React.Component { + + deleteUser() { + swal({ + title: "Are you sure?", + text: "Once deleted, you will not be able to recover your profile! \n All your articles and comments will be deleted!", + icon: "warning", + buttons: true, + dangerMode: true, + }) + .then((willDelete) => { + if (willDelete) { + swal("Poof! Your profile has been deleted!", { + icon: "success", + content: this.props.onClickDeleteUser() + }); + } else { + swal("Canceled!"); + } + }); + } + render() { return ( <div className="settings-page"> @@ -146,18 +216,25 @@ class Settings extends React.Component { <h1 className="text-xs-center">Your Settings</h1> - <ListErrors errors={this.props.errors}></ListErrors> - <SettingsForm currentUser={this.props.currentUser} onSubmitForm={this.props.onSubmitForm} /> + <ListErrors errors={this.props.errors}></ListErrors> + <hr /> + <button className="btn btn-outline-danger" onClick={this.props.onClickLogout}> - Or click here to logout. + Logout + </button> + + <button + className="btn btn-outline-danger pull-xs-right" + onClick={()=>this.deleteUser()}> + Delete account </button> </div> diff --git a/te-frontend/src/components/edit.js b/conduit-front/src/components/edit.js similarity index 100% rename from te-frontend/src/components/edit.js rename to conduit-front/src/components/edit.js diff --git a/te-frontend/src/components/images/christmas-hat-png-19616.png b/conduit-front/src/components/images/christmas-hat-png-19616.png similarity index 100% rename from te-frontend/src/components/images/christmas-hat-png-19616.png rename to conduit-front/src/components/images/christmas-hat-png-19616.png diff --git a/te-frontend/src/components/images/home-hero.png b/conduit-front/src/components/images/home-hero.png similarity index 100% rename from te-frontend/src/components/images/home-hero.png rename to conduit-front/src/components/images/home-hero.png diff --git a/te-frontend/src/components/images/logo-light_round.png b/conduit-front/src/components/images/logo-light_round.png similarity index 100% rename from te-frontend/src/components/images/logo-light_round.png rename to conduit-front/src/components/images/logo-light_round.png diff --git a/te-frontend/src/components/images/logo-sm-light.png b/conduit-front/src/components/images/logo-sm-light.png similarity index 100% rename from te-frontend/src/components/images/logo-sm-light.png rename to conduit-front/src/components/images/logo-sm-light.png diff --git a/te-frontend/src/components/images/logo-sm.svg b/conduit-front/src/components/images/logo-sm.svg similarity index 100% rename from te-frontend/src/components/images/logo-sm.svg rename to conduit-front/src/components/images/logo-sm.svg diff --git a/te-frontend/src/components/images/logo_wimma.png b/conduit-front/src/components/images/logo_wimma.png similarity index 100% rename from te-frontend/src/components/images/logo_wimma.png rename to conduit-front/src/components/images/logo_wimma.png diff --git a/te-frontend/src/components/images/logo_wimma2.png b/conduit-front/src/components/images/logo_wimma2.png similarity index 100% rename from te-frontend/src/components/images/logo_wimma2.png rename to conduit-front/src/components/images/logo_wimma2.png diff --git a/te-frontend/src/components/images/smiley-cyrus.jpg b/conduit-front/src/components/images/smiley-cyrus.jpg similarity index 100% rename from te-frontend/src/components/images/smiley-cyrus.jpg rename to conduit-front/src/components/images/smiley-cyrus.jpg diff --git a/conduit-front/src/constants/actionTypes.js b/conduit-front/src/constants/actionTypes.js index 8683c60d8f5579deb16eaa5e970c874c2e9478e4..f2d439949aba817ab915eb798ecba2851237f12d 100644 --- a/conduit-front/src/constants/actionTypes.js +++ b/conduit-front/src/constants/actionTypes.js @@ -33,4 +33,5 @@ export const UPDATE_FIELD_EDITOR = 'UPDATE_FIELD_EDITOR'; export const FOLLOW_USER = 'FOLLOW_USER'; export const UNFOLLOW_USER = 'UNFOLLOW_USER'; export const PROFILE_FAVORITES_PAGE_UNLOADED = 'PROFILE_FAVORITES_PAGE_UNLOADED'; -export const PROFILE_FAVORITES_PAGE_LOADED = 'PROFILE_FAVORITES_PAGE_LOADED'; \ No newline at end of file +export const PROFILE_FAVORITES_PAGE_LOADED = 'PROFILE_FAVORITES_PAGE_LOADED'; +export const DELETE_USER = 'DELETE_USER'; \ No newline at end of file diff --git a/conduit-front/src/middleware.js b/conduit-front/src/middleware.js index 9e24a96f9878c712ce047d05f59fcc899093d7fe..17a4d50f81c88cf8aed815cb821f31ec7edfd2f0 100644 --- a/conduit-front/src/middleware.js +++ b/conduit-front/src/middleware.js @@ -4,7 +4,8 @@ import { ASYNC_END, LOGIN, LOGOUT, - REGISTER + REGISTER, + DELETE_USER } from './constants/actionTypes'; const promiseMiddleware = store => next => action => { @@ -52,7 +53,7 @@ const localStorageMiddleware = store => next => action => { window.localStorage.setItem('jwt', action.payload.user.token); agent.setToken(action.payload.user.token); } - } else if (action.type === LOGOUT) { + } else if (action.type === LOGOUT || action.type === DELETE_USER) { window.localStorage.setItem('jwt', ''); agent.setToken(null); } diff --git a/conduit-front/src/reducers/common.js b/conduit-front/src/reducers/common.js index f95b2bb81b0aec5e9bdc3b7ea230b4ff17617276..06d349d91647cd0259325df61313c87b90cb8317 100644 --- a/conduit-front/src/reducers/common.js +++ b/conduit-front/src/reducers/common.js @@ -14,7 +14,8 @@ import { PROFILE_FAVORITES_PAGE_UNLOADED, SETTINGS_PAGE_UNLOADED, LOGIN_PAGE_UNLOADED, - REGISTER_PAGE_UNLOADED + REGISTER_PAGE_UNLOADED, + DELETE_USER } from '../constants/actionTypes'; const defaultState = { @@ -34,10 +35,11 @@ export default (state = defaultState, action) => { }; case REDIRECT: return { ...state, redirectTo: null }; + case DELETE_USER: case LOGOUT: return { ...state, redirectTo: '/', token: null, currentUser: null }; case ARTICLE_SUBMITTED: - const redirectUrl = `/article/${action.payload.article.slug}`; + const redirectUrl = action.error ? null : `/article/${action.payload.article.slug}`; return { ...state, redirectTo: redirectUrl }; case SETTINGS_SAVED: return { diff --git a/te-backend/.circleci/config.yml b/te-backend/.circleci/config.yml deleted file mode 100644 index c0f847243eaa5e9ddbeabe96b2594e7d74f35b55..0000000000000000000000000000000000000000 --- a/te-backend/.circleci/config.yml +++ /dev/null @@ -1,20 +0,0 @@ -version: 2 - -jobs: - build: - docker: - - image: circleci/node:8.5.0 - - image: circleci/mongo:3.4.9 - steps: - - checkout - - run: - name: Start app and test - command: | - yarn - yarn run start & sleep 5 - curl http://localhost:3000/api/tags - yarn run test - - run: - name: Trigger integration tests - command: curl -X POST "https://circleci.com/api/v1.1/project/github/anishkny/realworld-e2e-test?circle-token=$CIRCLE_CI_TOKEN" - diff --git a/te-backend/.dockerignore b/te-backend/.dockerignore deleted file mode 100644 index 64b56b0e24f5a835ed0a022459c06b4ce329c13f..0000000000000000000000000000000000000000 --- a/te-backend/.dockerignore +++ /dev/null @@ -1,8 +0,0 @@ -.git -.gitignore -.gitlab-ci.yml -README.md -node_modules -project-logo.png -back-up-system - diff --git a/te-backend/.gitignore b/te-backend/.gitignore deleted file mode 100644 index 9adbb849f5e613acd86231b98fb68ecbec983fdb..0000000000000000000000000000000000000000 --- a/te-backend/.gitignore +++ /dev/null @@ -1,41 +0,0 @@ -# Logs -logs -*.log -.DS_Store - -npm-debug.log* - -# Runtime data -pids -*.pid -*.seed - -# Directory for instrumented libs generated by jscoverage/JSCover -lib-cov - -# Coverage directory used by tools like istanbul -coverage - -# Grunt intermediate storage (http://gruntjs.com/creating-plugins#storing-task-files) -.grunt - -# node-waf configuration -.lock-wscript - -# Compiled binary addons (http://nodejs.org/api/addons.html) -build/Release - -# Dependency directory -node_modules - -# Optional npm cache directory -.npm - -# Optional REPL history -.node_repl_history - -.idea - -# used only in local development -Dockerfile-dev -docker-compose-dev.yml diff --git a/te-backend/.travis.yml b/te-backend/.travis.yml deleted file mode 100644 index 795ed5202d936ae81fe2ab8dd18a02563486428f..0000000000000000000000000000000000000000 --- a/te-backend/.travis.yml +++ /dev/null @@ -1,10 +0,0 @@ -language: node_js -node_js: "8" - -sudo: required -services: mongodb - -install: yarn - -before_script: yarn start & sleep 5 -script: yarn test diff --git a/te-backend/Dockerfile b/te-backend/Dockerfile deleted file mode 100644 index 5dc91e55714afe6d9aeefed4a34e111e5282f27c..0000000000000000000000000000000000000000 --- a/te-backend/Dockerfile +++ /dev/null @@ -1,18 +0,0 @@ -# Choose base image (maybe use node 14 with fewer warnings??) (changed from node:16-alpine) -FROM node:16 - -# Create app directory -WORKDIR /usr/src/app - -# Install app dependencies -COPY package*.json ./ -RUN npm install - -# Bundle app source -COPY . . - -# Expose port -EXPOSE 3000 - -# Run app with node.js -CMD [ "node", "app.js" ] diff --git a/te-backend/README.md b/te-backend/README.md deleted file mode 100644 index 660365d2f63123f6f0d8d0196fb4a69491ac9652..0000000000000000000000000000000000000000 --- a/te-backend/README.md +++ /dev/null @@ -1,52 +0,0 @@ -#  - -[](https://travis-ci.org/anishkny/node-express-realworld-example-app) - -> ### Example Node (Express + Mongoose) codebase containing real world examples (CRUD, auth, advanced patterns, etc) that adheres to the [RealWorld](https://github.com/gothinkster/realworld-example-apps) API spec. - -<a href="https://thinkster.io/tutorials/node-json-api" target="_blank"><img width="454" src="https://raw.githubusercontent.com/gothinkster/realworld/master/media/learn-btn-hr.png" /></a> - -This repo is functionality complete — PRs and issues welcome! - -# Getting started - -To get the Node server running locally: - -- Clone this repo -- `npm install` to install all required dependencies -- Install MongoDB Community Edition ([instructions](https://docs.mongodb.com/manual/installation/#tutorials)) and run it by executing `mongod` -- `npm run dev` to start the local server - -Alternately, to quickly try out this repo in the cloud, you can [](https://glitch.com/edit/#!/remix/realworld) - -# Code Overview - -## Dependencies - -- [expressjs](https://github.com/expressjs/express) - The server for handling and routing HTTP requests -- [express-jwt](https://github.com/auth0/express-jwt) - Middleware for validating JWTs for authentication -- [jsonwebtoken](https://github.com/auth0/node-jsonwebtoken) - For generating JWTs used by authentication -- [mongoose](https://github.com/Automattic/mongoose) - For modeling and mapping MongoDB data to javascript -- [mongoose-unique-validator](https://github.com/blakehaswell/mongoose-unique-validator) - For handling unique validation errors in Mongoose. Mongoose only handles validation at the document level, so a unique index across a collection will throw an exception at the driver level. The `mongoose-unique-validator` plugin helps us by formatting the error like a normal mongoose `ValidationError`. -- [passport](https://github.com/jaredhanson/passport) - For handling user authentication -- [slug](https://github.com/dodo/node-slug) - For encoding titles into a URL-friendly format - -## Application Structure - -- `app.js` - The entry point to our application. This file defines our express server and connects it to MongoDB using mongoose. It also requires the routes and models we'll be using in the application. -- `config/` - This folder contains configuration for passport as well as a central location for configuration/environment variables. -- `routes/` - This folder contains the route definitions for our API. -- `models/` - This folder contains the schema definitions for our Mongoose models. - -## Error Handling - -In `routes/api/index.js`, we define a error-handling middleware for handling Mongoose's `ValidationError`. This middleware will respond with a 422 status code and format the response to have [error messages the clients can understand](https://github.com/gothinkster/realworld/blob/master/API.md#errors-and-status-codes) - -## Authentication - -Requests are authenticated using the `Authorization` header with a valid JWT. We define two express middlewares in `routes/auth.js` that can be used to authenticate requests. The `required` middleware configures the `express-jwt` middleware using our application's secret and will return a 401 status code if the request cannot be authenticated. The payload of the JWT can then be accessed from `req.payload` in the endpoint. The `optional` middleware configures the `express-jwt` in the same way as `required`, but will *not* return a 401 status code if the request cannot be authenticated. - - -<br /> - -[](https://thinkster.io) diff --git a/te-backend/app.js b/te-backend/app.js deleted file mode 100644 index 460baa408e848adf0b10d8c2812596fed38cc7e1..0000000000000000000000000000000000000000 --- a/te-backend/app.js +++ /dev/null @@ -1,84 +0,0 @@ -var http = require('http'), - path = require('path'), - methods = require('methods'), - express = require('express'), - bodyParser = require('body-parser'), - session = require('express-session'), - cors = require('cors'), - passport = require('passport'), - errorhandler = require('errorhandler'), - mongoose = require('mongoose'); - -var isProduction = process.env.NODE_ENV === 'production'; - -// Create global app object -var app = express(); - -app.use(cors()); - -// Normal express config defaults -app.use(require('morgan')('dev')); -app.use(bodyParser.urlencoded({ extended: false })); -app.use(bodyParser.json()); - -app.use(require('method-override')()); -app.use(express.static(__dirname + '/public')); - -app.use(session({ secret: 'conduit', cookie: { maxAge: 60000 }, resave: false, saveUninitialized: false })); - -if (!isProduction) { - app.use(errorhandler()); -} - -if(isProduction){ - mongoose.connect(process.env.MONGODB_URI); -} else { - mongoose.connect('mongodb://localhost/conduit'); - mongoose.set('debug', true); -} - -require('./models/User'); -require('./models/Article'); -require('./models/Comment'); -require('./config/passport'); - -app.use(require('./routes')); - -/// catch 404 and forward to error handler -app.use(function(req, res, next) { - var err = new Error('Not Found'); - err.status = 404; - next(err); -}); - -/// error handlers - -// development error handler -// will print stacktrace -if (!isProduction) { - app.use(function(err, req, res, next) { - console.log(err.stack); - - res.status(err.status || 500); - - res.json({'errors': { - message: err.message, - error: err - }}); - }); -} - -// production error handler -// no stacktraces leaked to user -app.use(function(err, req, res, next) { - res.status(err.status || 500); - res.json({'errors': { - message: err.message, - error: {} - }}); -}); - -// finally, let's start our server... -var server = app.listen( process.env.PORT || 3000, function(){ - console.log('Listening on port ' + server.address().port); -}); diff --git a/te-backend/config/index.js b/te-backend/config/index.js deleted file mode 100644 index 1bf9d6a124b07a0c5c569dcf40aa538dd55ec38e..0000000000000000000000000000000000000000 --- a/te-backend/config/index.js +++ /dev/null @@ -1,3 +0,0 @@ -module.exports = { - secret: process.env.NODE_ENV === 'production' ? process.env.SECRET : 'secret' -}; diff --git a/te-backend/config/passport.js b/te-backend/config/passport.js deleted file mode 100644 index abe0ce2d6f178cbdf0994364c9faef836a886012..0000000000000000000000000000000000000000 --- a/te-backend/config/passport.js +++ /dev/null @@ -1,18 +0,0 @@ -var passport = require('passport'); -var LocalStrategy = require('passport-local').Strategy; -var mongoose = require('mongoose'); -var User = mongoose.model('User'); - -passport.use(new LocalStrategy({ - usernameField: 'user[email]', - passwordField: 'user[password]' -}, function(email, password, done) { - User.findOne({email: email}).then(function(user){ - if(!user || !user.validPassword(password)){ - return done(null, false, {errors: {'email or password': 'is invalid'}}); - } - - return done(null, user); - }).catch(done); -})); - diff --git a/te-backend/models/Article.js b/te-backend/models/Article.js deleted file mode 100644 index e0d12ed25d1d608506cf6d3c22c6e409482171dc..0000000000000000000000000000000000000000 --- a/te-backend/models/Article.js +++ /dev/null @@ -1,56 +0,0 @@ -var mongoose = require('mongoose'); -var uniqueValidator = require('mongoose-unique-validator'); -var slug = require('slug'); -var User = mongoose.model('User'); - -var ArticleSchema = new mongoose.Schema({ - slug: {type: String, lowercase: true, unique: true}, - title: String, - description: String, - body: String, - favoritesCount: {type: Number, default: 0}, - comments: [{ type: mongoose.Schema.Types.ObjectId, ref: 'Comment' }], - tagList: [{ type: String }], - author: { type: mongoose.Schema.Types.ObjectId, ref: 'User' } -}, {timestamps: true, usePushEach: true}); - -ArticleSchema.plugin(uniqueValidator, {message: 'is already taken'}); - -ArticleSchema.pre('validate', function(next){ - if(!this.slug) { - this.slugify(); - } - - next(); -}); - -ArticleSchema.methods.slugify = function() { - this.slug = slug(this.title) + '-' + (Math.random() * Math.pow(36, 6) | 0).toString(36); -}; - -ArticleSchema.methods.updateFavoriteCount = function() { - var article = this; - - return User.count({favorites: {$in: [article._id]}}).then(function(count){ - article.favoritesCount = count; - - return article.save(); - }); -}; - -ArticleSchema.methods.toJSONFor = function(user){ - return { - slug: this.slug, - title: this.title, - description: this.description, - body: this.body, - createdAt: this.createdAt, - updatedAt: this.updatedAt, - tagList: this.tagList, - favorited: user ? user.isFavorite(this._id) : false, - favoritesCount: this.favoritesCount, - author: this.author.toProfileJSONFor(user) - }; -}; - -mongoose.model('Article', ArticleSchema); diff --git a/te-backend/models/Comment.js b/te-backend/models/Comment.js deleted file mode 100644 index 7def57dd42624edbd32632525c3c2579d2ae5735..0000000000000000000000000000000000000000 --- a/te-backend/models/Comment.js +++ /dev/null @@ -1,19 +0,0 @@ -var mongoose = require('mongoose'); - -var CommentSchema = new mongoose.Schema({ - body: String, - author: { type: mongoose.Schema.Types.ObjectId, ref: 'User' }, - article: { type: mongoose.Schema.Types.ObjectId, ref: 'Article' } -}, {timestamps: true, usePushEach: true}); - -// Requires population of author -CommentSchema.methods.toJSONFor = function(user){ - return { - id: this._id, - body: this.body, - createdAt: this.createdAt, - author: this.author.toProfileJSONFor(user) - }; -}; - -mongoose.model('Comment', CommentSchema); diff --git a/te-backend/models/User.js b/te-backend/models/User.js deleted file mode 100644 index acb2bcfd0af396c108908011cef4b1f851e3063b..0000000000000000000000000000000000000000 --- a/te-backend/models/User.js +++ /dev/null @@ -1,99 +0,0 @@ -var mongoose = require('mongoose'); -var uniqueValidator = require('mongoose-unique-validator'); -var crypto = require('crypto'); -var jwt = require('jsonwebtoken'); -var secret = require('../config').secret; - -var UserSchema = new mongoose.Schema({ - username: {type: String, unique: true, uniqueCaseInsensitive: true, required: [true, "can't be blank"], match: [/^[a-zA-Z0-9]+$/, 'is invalid'], index: true}, - email: {type: String, unique: true, uniqueCaseInsensitive: true, required: [true, "can't be blank"], match: [/\S+@\S+\.\S+/, 'is invalid'], index: true}, - bio: String, - image: String, - favorites: [{ type: mongoose.Schema.Types.ObjectId, ref: 'Article' }], - following: [{ type: mongoose.Schema.Types.ObjectId, ref: 'User' }], - hash: String, - salt: String -}, {timestamps: true, usePushEach: true}); - -UserSchema.plugin(uniqueValidator, {message: 'is already taken.'}); - -UserSchema.methods.validPassword = function(password) { - var hash = crypto.pbkdf2Sync(password, this.salt, 10000, 512, 'sha512').toString('hex'); - return this.hash === hash; -}; - -UserSchema.methods.setPassword = function(password){ - this.salt = crypto.randomBytes(16).toString('hex'); - this.hash = crypto.pbkdf2Sync(password, this.salt, 10000, 512, 'sha512').toString('hex'); -}; - -UserSchema.methods.generateJWT = function() { - var today = new Date(); - var exp = new Date(today); - exp.setDate(today.getDate() + 60); - - return jwt.sign({ - id: this._id, - username: this.username, - exp: parseInt(exp.getTime() / 1000), - }, secret); -}; - -UserSchema.methods.toAuthJSON = function(){ - return { - username: this.username, - email: this.email, - token: this.generateJWT(), - bio: this.bio, - image: this.image || 'https://static.productionready.io/images/smiley-cyrus.jpg' - }; -}; - -UserSchema.methods.toProfileJSONFor = function(user){ - return { - username: this.username, - bio: this.bio, - image: this.image || 'https://static.productionready.io/images/smiley-cyrus.jpg', - following: user ? user.isFollowing(this._id) : false - }; -}; - -UserSchema.methods.favorite = function(id){ - if(this.favorites.indexOf(id) === -1){ - this.favorites.push(id); - } - - return this.save(); -}; - -UserSchema.methods.unfavorite = function(id){ - this.favorites.remove(id); - return this.save(); -}; - -UserSchema.methods.isFavorite = function(id){ - return this.favorites.some(function(favoriteId){ - return favoriteId.toString() === id.toString(); - }); -}; - -UserSchema.methods.follow = function(id){ - if(this.following.indexOf(id) === -1){ - this.following.push(id); - } - - return this.save(); -}; - -UserSchema.methods.unfollow = function(id){ - this.following.remove(id); - return this.save(); -}; - -UserSchema.methods.isFollowing = function(id){ - return this.following.some(function(followId){ - return followId.toString() === id.toString(); - }); -}; - -mongoose.model('User', UserSchema); diff --git a/te-backend/package.json b/te-backend/package.json deleted file mode 100644 index 0b71f5bde85a58970601fff69c3d1ff76ded72b2..0000000000000000000000000000000000000000 --- a/te-backend/package.json +++ /dev/null @@ -1,43 +0,0 @@ -{ - "name": "conduit-node", - "version": "2.0.0", - "description": "conduit on node", - "main": "app.js", - "scripts": { - "mongo:start": "docker run --name realworld-mongo -p 27017:27017 mongo & sleep 5", - "start": "node ./app.js", - "dev": "nodemon ./app.js", - "test": "newman run ./tests/api-tests.postman.json -e ./tests/env-api-tests.postman.json", - "stop": "lsof -ti :3000 | xargs kill", - "mongo:stop": "docker stop realworld-mongo && docker rm realworld-mongo" - }, - "repository": { - "type": "git", - "url": "git+https://github.com/gothinkster/productionready-node-api.git" - }, - "license": "ISC", - "dependencies": { - "body-parser": "1.15.0", - "cors": "2.7.1", - "ejs": "2.4.1", - "errorhandler": "1.4.3", - "express": "4.13.4", - "express-jwt": "3.3.0", - "express-session": "1.13.0", - "jsonwebtoken": "7.1.9", - "method-override": "2.3.5", - "methods": "1.1.2", - "mongoose": "^4.6.4", - "mongoose-unique-validator": "1.0.2", - "morgan": "1.7.0", - "passport": "0.3.2", - "passport-local": "1.0.0", - "request": "2.69.0", - "slug": "0.9.1", - "underscore": "1.8.3" - }, - "devDependencies": { - "newman": "^3.8.2", - "nodemon": "^1.11.0" - } -} diff --git a/te-backend/project-logo.png b/te-backend/project-logo.png deleted file mode 100644 index ef92a4be8c6cb93a3e9c78d72787e558a40cd149..0000000000000000000000000000000000000000 Binary files a/te-backend/project-logo.png and /dev/null differ diff --git a/te-backend/public/.keep b/te-backend/public/.keep deleted file mode 100644 index e69de29bb2d1d6434b8b29ae775ad8c2e48c5391..0000000000000000000000000000000000000000 diff --git a/te-backend/routes/api/articles.js b/te-backend/routes/api/articles.js deleted file mode 100644 index a8ffe736c2dbe63d1ae2fb3b873157748f486d98..0000000000000000000000000000000000000000 --- a/te-backend/routes/api/articles.js +++ /dev/null @@ -1,280 +0,0 @@ -var router = require('express').Router(); -var mongoose = require('mongoose'); -var Article = mongoose.model('Article'); -var Comment = mongoose.model('Comment'); -var User = mongoose.model('User'); -var auth = require('../auth'); - -// Preload article objects on routes with ':article' -router.param('article', function(req, res, next, slug) { - Article.findOne({ slug: slug}) - .populate('author') - .then(function (article) { - if (!article) { return res.sendStatus(404); } - - req.article = article; - - return next(); - }).catch(next); -}); - -router.param('comment', function(req, res, next, id) { - Comment.findById(id).then(function(comment){ - if(!comment) { return res.sendStatus(404); } - - req.comment = comment; - - return next(); - }).catch(next); -}); - -router.get('/', auth.optional, function(req, res, next) { - var query = {}; - var limit = 20; - var offset = 0; - - if(typeof req.query.limit !== 'undefined'){ - limit = req.query.limit; - } - - if(typeof req.query.offset !== 'undefined'){ - offset = req.query.offset; - } - - if( typeof req.query.tag !== 'undefined' ){ - query.tagList = {"$in" : [req.query.tag]}; - } - - Promise.all([ - req.query.author ? User.findOne({username: req.query.author}) : null, - req.query.favorited ? User.findOne({username: req.query.favorited}) : null - ]).then(function(results){ - var author = results[0]; - var favoriter = results[1]; - - if(author){ - query.author = author._id; - } - - if(favoriter){ - query._id = {$in: favoriter.favorites}; - } else if(req.query.favorited){ - query._id = {$in: []}; - } - - return Promise.all([ - Article.find(query) - .limit(Number(limit)) - .skip(Number(offset)) - .sort({createdAt: 'desc'}) - .populate('author') - .exec(), - Article.count(query).exec(), - req.payload ? User.findById(req.payload.id) : null, - ]).then(function(results){ - var articles = results[0]; - var articlesCount = results[1]; - var user = results[2]; - - return res.json({ - articles: articles.map(function(article){ - return article.toJSONFor(user); - }), - articlesCount: articlesCount - }); - }); - }).catch(next); -}); - -router.get('/feed', auth.required, function(req, res, next) { - var limit = 20; - var offset = 0; - - if(typeof req.query.limit !== 'undefined'){ - limit = req.query.limit; - } - - if(typeof req.query.offset !== 'undefined'){ - offset = req.query.offset; - } - - User.findById(req.payload.id).then(function(user){ - if (!user) { return res.sendStatus(401); } - - Promise.all([ - Article.find({ author: {$in: user.following}}) - .limit(Number(limit)) - .sort({createdAt: 'desc'}) - .skip(Number(offset)) - .populate('author') - .exec(), - Article.count({ author: {$in: user.following}}) - ]).then(function(results){ - var articles = results[0]; - var articlesCount = results[1]; - - return res.json({ - articles: articles.map(function(article){ - return article.toJSONFor(user); - }), - articlesCount: articlesCount - }); - }).catch(next); - }); -}); - -router.post('/', auth.required, function(req, res, next) { - User.findById(req.payload.id).then(function(user){ - if (!user) { return res.sendStatus(401); } - - var article = new Article(req.body.article); - - article.author = user; - - return article.save().then(function(){ - console.log(article.author); - return res.json({article: article.toJSONFor(user)}); - }); - }).catch(next); -}); - -// return a article -router.get('/:article', auth.optional, function(req, res, next) { - Promise.all([ - req.payload ? User.findById(req.payload.id) : null, - req.article.populate('author').execPopulate() - ]).then(function(results){ - var user = results[0]; - - return res.json({article: req.article.toJSONFor(user)}); - }).catch(next); -}); - -// update article -router.put('/:article', auth.required, function(req, res, next) { - User.findById(req.payload.id).then(function(user){ - if(req.article.author._id.toString() === req.payload.id.toString()){ - if(typeof req.body.article.title !== 'undefined'){ - req.article.title = req.body.article.title; - } - - if(typeof req.body.article.description !== 'undefined'){ - req.article.description = req.body.article.description; - } - - if(typeof req.body.article.body !== 'undefined'){ - req.article.body = req.body.article.body; - } - - if(typeof req.body.article.tagList !== 'undefined'){ - req.article.tagList = req.body.article.tagList - } - - req.article.save().then(function(article){ - return res.json({article: article.toJSONFor(user)}); - }).catch(next); - } else { - return res.sendStatus(403); - } - }); -}); - -// delete article -router.delete('/:article', auth.required, function(req, res, next) { - User.findById(req.payload.id).then(function(user){ - if (!user) { return res.sendStatus(401); } - - if(req.article.author._id.toString() === req.payload.id.toString()){ - return req.article.remove().then(function(){ - return res.sendStatus(204); - }); - } else { - return res.sendStatus(403); - } - }).catch(next); -}); - -// Favorite an article -router.post('/:article/favorite', auth.required, function(req, res, next) { - var articleId = req.article._id; - - User.findById(req.payload.id).then(function(user){ - if (!user) { return res.sendStatus(401); } - - return user.favorite(articleId).then(function(){ - return req.article.updateFavoriteCount().then(function(article){ - return res.json({article: article.toJSONFor(user)}); - }); - }); - }).catch(next); -}); - -// Unfavorite an article -router.delete('/:article/favorite', auth.required, function(req, res, next) { - var articleId = req.article._id; - - User.findById(req.payload.id).then(function (user){ - if (!user) { return res.sendStatus(401); } - - return user.unfavorite(articleId).then(function(){ - return req.article.updateFavoriteCount().then(function(article){ - return res.json({article: article.toJSONFor(user)}); - }); - }); - }).catch(next); -}); - -// return an article's comments -router.get('/:article/comments', auth.optional, function(req, res, next){ - Promise.resolve(req.payload ? User.findById(req.payload.id) : null).then(function(user){ - return req.article.populate({ - path: 'comments', - populate: { - path: 'author' - }, - options: { - sort: { - createdAt: 'desc' - } - } - }).execPopulate().then(function(article) { - return res.json({comments: req.article.comments.map(function(comment){ - return comment.toJSONFor(user); - })}); - }); - }).catch(next); -}); - -// create a new comment -router.post('/:article/comments', auth.required, function(req, res, next) { - User.findById(req.payload.id).then(function(user){ - if(!user){ return res.sendStatus(401); } - - var comment = new Comment(req.body.comment); - comment.article = req.article; - comment.author = user; - - return comment.save().then(function(){ - req.article.comments.push(comment); - - return req.article.save().then(function(article) { - res.json({comment: comment.toJSONFor(user)}); - }); - }); - }).catch(next); -}); - -router.delete('/:article/comments/:comment', auth.required, function(req, res, next) { - if(req.comment.author.toString() === req.payload.id.toString()){ - req.article.comments.remove(req.comment._id); - req.article.save() - .then(Comment.find({_id: req.comment._id}).remove().exec()) - .then(function(){ - res.sendStatus(204); - }); - } else { - res.sendStatus(403); - } -}); - -module.exports = router; diff --git a/te-backend/routes/api/index.js b/te-backend/routes/api/index.js deleted file mode 100644 index 03f36d676f76d63c361a374a0f90ade02d8d1bf6..0000000000000000000000000000000000000000 --- a/te-backend/routes/api/index.js +++ /dev/null @@ -1,22 +0,0 @@ -var router = require('express').Router(); - -router.use('/', require('./users')); -router.use('/profiles', require('./profiles')); -router.use('/articles', require('./articles')); -router.use('/tags', require('./tags')); - -router.use(function(err, req, res, next){ - if(err.name === 'ValidationError'){ - return res.status(422).json({ - errors: Object.keys(err.errors).reduce(function(errors, key){ - errors[key] = err.errors[key].message; - - return errors; - }, {}) - }); - } - - return next(err); -}); - -module.exports = router; \ No newline at end of file diff --git a/te-backend/routes/api/profiles.js b/te-backend/routes/api/profiles.js deleted file mode 100644 index ffcd83383fa7a4f48f821f55dd2ebc0b1856cad0..0000000000000000000000000000000000000000 --- a/te-backend/routes/api/profiles.js +++ /dev/null @@ -1,53 +0,0 @@ -var router = require('express').Router(); -var mongoose = require('mongoose'); -var User = mongoose.model('User'); -var auth = require('../auth'); - -// Preload user profile on routes with ':username' -router.param('username', function(req, res, next, username){ - User.findOne({username: username}).then(function(user){ - if (!user) { return res.sendStatus(404); } - - req.profile = user; - - return next(); - }).catch(next); -}); - -router.get('/:username', auth.optional, function(req, res, next){ - if(req.payload){ - User.findById(req.payload.id).then(function(user){ - if(!user){ return res.json({profile: req.profile.toProfileJSONFor(false)}); } - - return res.json({profile: req.profile.toProfileJSONFor(user)}); - }); - } else { - return res.json({profile: req.profile.toProfileJSONFor(false)}); - } -}); - -router.post('/:username/follow', auth.required, function(req, res, next){ - var profileId = req.profile._id; - - User.findById(req.payload.id).then(function(user){ - if (!user) { return res.sendStatus(401); } - - return user.follow(profileId).then(function(){ - return res.json({profile: req.profile.toProfileJSONFor(user)}); - }); - }).catch(next); -}); - -router.delete('/:username/follow', auth.required, function(req, res, next){ - var profileId = req.profile._id; - - User.findById(req.payload.id).then(function(user){ - if (!user) { return res.sendStatus(401); } - - return user.unfollow(profileId).then(function(){ - return res.json({profile: req.profile.toProfileJSONFor(user)}); - }); - }).catch(next); -}); - -module.exports = router; diff --git a/te-backend/routes/api/tags.js b/te-backend/routes/api/tags.js deleted file mode 100644 index bea1a806f831e1f3672efb42035f7cd4728076f6..0000000000000000000000000000000000000000 --- a/te-backend/routes/api/tags.js +++ /dev/null @@ -1,12 +0,0 @@ -var router = require('express').Router(); -var mongoose = require('mongoose'); -var Article = mongoose.model('Article'); - -// return a list of tags -router.get('/', function(req, res, next) { - Article.find().distinct('tagList').then(function(tags){ - return res.json({tags: tags}); - }).catch(next); -}); - -module.exports = router; diff --git a/te-backend/routes/api/users.js b/te-backend/routes/api/users.js deleted file mode 100644 index 2fc15476259a2eb850610339b4d9be5d6c85281b..0000000000000000000000000000000000000000 --- a/te-backend/routes/api/users.js +++ /dev/null @@ -1,91 +0,0 @@ -var mongoose = require('mongoose'); -var router = require('express').Router(); -var passport = require('passport'); -var Article = mongoose.model('Article'); -var Comment = mongoose.model('Comment'); -var User = mongoose.model('User'); -var auth = require('../auth'); - -router.get('/user', auth.required, function(req, res, next){ - User.findById(req.payload.id).then(function(user){ - if(!user){ return res.sendStatus(401); } - - return res.json({user: user.toAuthJSON()}); - }).catch(next); -}); - -router.put('/user', auth.required, function(req, res, next){ - User.findById(req.payload.id).then(function(user){ - if(!user){ return res.sendStatus(401); } - - // only update fields that were actually passed... - if(typeof req.body.user.username !== 'undefined'){ - user.username = req.body.user.username; - } - if(typeof req.body.user.email !== 'undefined'){ - user.email = req.body.user.email; - } - if(typeof req.body.user.bio !== 'undefined'){ - user.bio = req.body.user.bio; - } - if(typeof req.body.user.image !== 'undefined'){ - user.image = req.body.user.image; - } - if(typeof req.body.user.password !== 'undefined'){ - user.setPassword(req.body.user.password); - } - - return user.save().then(function(){ - return res.json({user: user.toAuthJSON()}); - }); - }).catch(next); -}); - -router.post('/users/login', function(req, res, next){ - if(!req.body.user.email){ - return res.status(422).json({errors: {email: "can't be blank"}}); - } - - if(!req.body.user.password){ - return res.status(422).json({errors: {password: "can't be blank"}}); - } - - passport.authenticate('local', {session: false}, function(err, user, info){ - if(err){ return next(err); } - - if(user){ - user.token = user.generateJWT(); - return res.json({user: user.toAuthJSON()}); - } else { - return res.status(422).json(info); - } - })(req, res, next); -}); - -router.post('/users', function(req, res, next){ - var user = new User(); - - user.username = req.body.user.username; - user.email = req.body.user.email; - user.setPassword(req.body.user.password); - - user.save().then(function(){ - return res.json({user: user.toAuthJSON()}); - }).catch(next); -}); - -router.delete('/user', auth.required, function(req, res, next){ - //console.log('TESTI', req); // prints to backend container log - User.findByIdAndRemove(req.payload.id).then(function(user){ - if(!user){ return res.sendStatus(401); } - - Promise.all([ - Article.find({ author: user._id }).remove(), - Comment.find({ author: user._id }).remove() - ]).then(function(){ - return res.sendStatus(204); - }).catch(next); - }); -}); - -module.exports = router; diff --git a/te-backend/routes/auth.js b/te-backend/routes/auth.js deleted file mode 100644 index e44a21508741be9b74d21497a5bb68b541e403ae..0000000000000000000000000000000000000000 --- a/te-backend/routes/auth.js +++ /dev/null @@ -1,27 +0,0 @@ -var jwt = require('express-jwt'); -var secret = require('../config').secret; - -function getTokenFromHeader(req){ - if (req.headers.authorization && req.headers.authorization.split(' ')[0] === 'Token' || - req.headers.authorization && req.headers.authorization.split(' ')[0] === 'Bearer') { - return req.headers.authorization.split(' ')[1]; - } - - return null; -} - -var auth = { - required: jwt({ - secret: secret, - userProperty: 'payload', - getToken: getTokenFromHeader - }), - optional: jwt({ - secret: secret, - userProperty: 'payload', - credentialsRequired: false, - getToken: getTokenFromHeader - }) -}; - -module.exports = auth; diff --git a/te-backend/routes/index.js b/te-backend/routes/index.js deleted file mode 100644 index 358576756fc144b9ec097088663ac6ff1a40ea0d..0000000000000000000000000000000000000000 --- a/te-backend/routes/index.js +++ /dev/null @@ -1,5 +0,0 @@ -var router = require('express').Router(); - -router.use('/api', require('./api')); - -module.exports = router; diff --git a/te-backend/te-backend-deployment.yaml b/te-backend/te-backend-deployment.yaml deleted file mode 100644 index 459245118f8c550dfc9b6c8c81558fba0f8c0a61..0000000000000000000000000000000000000000 --- a/te-backend/te-backend-deployment.yaml +++ /dev/null @@ -1,33 +0,0 @@ -apiVersion: apps/v1 -kind: Deployment -metadata: - labels: - app: backend - name: backend - namespace: default -spec: - replicas: 1 - selector: - matchLabels: - app: backend - strategy: - rollingUpdate: - maxSurge: 25% - maxUnavailable: 25% - type: RollingUpdate - template: - metadata: - labels: - app: backend - spec: - containers: - - image: gitlab.labranet.jamk.fi:4567/conduitmiau/te-backend:backend - imagePullPolicy: Always - name: backend - resources: - requests: - cpu: '1' - memory: 1000Mi - - ports: - - containerPort: 3000 diff --git a/te-backend/te-backend-service.yaml b/te-backend/te-backend-service.yaml deleted file mode 100644 index 45feb841e824b8bd767e0e7f3e630f8eaeb04b80..0000000000000000000000000000000000000000 --- a/te-backend/te-backend-service.yaml +++ /dev/null @@ -1,13 +0,0 @@ -apiVersion: v1 -kind: Service -metadata: - name: backend - namespace: default -spec: - type: LoadBalancer - ports: - - nodePort: 30000 - port: 80 - targetPort: 3000 - selector: - app: backend diff --git a/te-backend/tests/api-tests.postman.json b/te-backend/tests/api-tests.postman.json deleted file mode 100644 index bad71a22b6b1ad202cc0f8b211654cb41b98f26a..0000000000000000000000000000000000000000 --- a/te-backend/tests/api-tests.postman.json +++ /dev/null @@ -1,1900 +0,0 @@ -{ - "variables": [], - "info": { - "name": "Conduit API Tests", - "_postman_id": "dda3e595-02d7-bf12-2a43-3daea0970192", - "description": "Collection for testing the Conduit API\n\nhttps://github.com/gothinkster/realworld", - "schema": "https://schema.getpostman.com/json/collection/v2.0.0/collection.json" - }, - "item": [{ - "name": "Auth", - "description": "", - "item": [{ - "name": "Register", - "event": [{ - "listen": "test", - "script": { - "type": "text/javascript", - "exec": [ - "if (!(environment.isIntegrationTest)) {", - "var responseJSON = JSON.parse(responseBody);", - "", - "tests['Response contains \"user\" property'] = responseJSON.hasOwnProperty('user');", - "", - "var user = responseJSON.user || {};", - "", - "tests['User has \"email\" property'] = user.hasOwnProperty('email');", - "tests['User has \"username\" property'] = user.hasOwnProperty('username');", - "tests['User has \"token\" property'] = user.hasOwnProperty('token');", - "}", - "" - ] - } - }], - "request": { - "url": "{{apiUrl}}/users", - "method": "POST", - "header": [{ - "key": "Content-Type", - "value": "application/json", - "description": "" - }, - { - "key": "X-Requested-With", - "value": "XMLHttpRequest", - "description": "" - } - ], - "body": { - "mode": "raw", - "raw": "{\"user\":{\"email\":\"john@jacob.com\", \"password\":\"johnnyjacob\", \"username\":\"johnjacob\"}}" - }, - "description": "" - }, - "response": [] - }, - { - "name": "Login", - "event": [{ - "listen": "test", - "script": { - "type": "text/javascript", - "exec": [ - "var responseJSON = JSON.parse(responseBody);", - "", - "tests['Response contains \"user\" property'] = responseJSON.hasOwnProperty('user');", - "", - "var user = responseJSON.user || {};", - "", - "tests['User has \"email\" property'] = user.hasOwnProperty('email');", - "tests['User has \"username\" property'] = user.hasOwnProperty('username');", - "tests['User has \"token\" property'] = user.hasOwnProperty('token');", - "" - ] - } - }], - "request": { - "url": "{{apiUrl}}/users/login", - "method": "POST", - "header": [{ - "key": "Content-Type", - "value": "application/json", - "description": "" - }, - { - "key": "X-Requested-With", - "value": "XMLHttpRequest", - "description": "" - } - ], - "body": { - "mode": "raw", - "raw": "{\"user\":{\"email\":\"john@jacob.com\", \"password\":\"johnnyjacob\"}}" - }, - "description": "" - }, - "response": [] - }, - { - "name": "Login and Remember Token", - "event": [{ - "listen": "test", - "script": { - "type": "text/javascript", - "exec": [ - "var responseJSON = JSON.parse(responseBody);", - "", - "tests['Response contains \"user\" property'] = responseJSON.hasOwnProperty('user');", - "", - "var user = responseJSON.user || {};", - "", - "tests['User has \"email\" property'] = user.hasOwnProperty('email');", - "tests['User has \"username\" property'] = user.hasOwnProperty('username');", - "tests['User has \"token\" property'] = user.hasOwnProperty('token');", - "", - "if(tests['User has \"token\" property']){", - " postman.setEnvironmentVariable('token', user.token);", - "}", - "", - "tests['Environment variable \"token\" has been set'] = environment.token === user.token;", - "" - ] - } - }], - "request": { - "url": "{{apiUrl}}/users/login", - "method": "POST", - "header": [{ - "key": "Content-Type", - "value": "application/json", - "description": "" - }, - { - "key": "X-Requested-With", - "value": "XMLHttpRequest", - "description": "" - } - ], - "body": { - "mode": "raw", - "raw": "{\"user\":{\"email\":\"john@jacob.com\", \"password\":\"johnnyjacob\"}}" - }, - "description": "" - }, - "response": [] - }, - { - "name": "Current User", - "event": [{ - "listen": "test", - "script": { - "type": "text/javascript", - "exec": [ - "var responseJSON = JSON.parse(responseBody);", - "", - "tests['Response contains \"user\" property'] = responseJSON.hasOwnProperty('user');", - "", - "var user = responseJSON.user || {};", - "", - "tests['User has \"email\" property'] = user.hasOwnProperty('email');", - "tests['User has \"username\" property'] = user.hasOwnProperty('username');", - "tests['User has \"token\" property'] = user.hasOwnProperty('token');", - "" - ] - } - }], - "request": { - "url": "{{apiUrl}}/user", - "method": "GET", - "header": [{ - "key": "Content-Type", - "value": "application/json", - "description": "" - }, - { - "key": "X-Requested-With", - "value": "XMLHttpRequest", - "description": "" - }, - { - "key": "Authorization", - "value": "Token {{token}}", - "description": "" - } - ], - "body": {}, - "description": "" - }, - "response": [] - }, - { - "name": "Update User", - "event": [{ - "listen": "test", - "script": { - "type": "text/javascript", - "exec": [ - "var responseJSON = JSON.parse(responseBody);", - "", - "tests['Response contains \"user\" property'] = responseJSON.hasOwnProperty('user');", - "", - "var user = responseJSON.user || {};", - "", - "tests['User has \"email\" property'] = user.hasOwnProperty('email');", - "tests['User has \"username\" property'] = user.hasOwnProperty('username');", - "tests['User has \"token\" property'] = user.hasOwnProperty('token');", - "" - ] - } - }], - "request": { - "url": "{{apiUrl}}/user", - "method": "PUT", - "header": [{ - "key": "Content-Type", - "value": "application/json", - "description": "" - }, - { - "key": "X-Requested-With", - "value": "XMLHttpRequest", - "description": "" - }, - { - "key": "Authorization", - "value": "Token {{token}}", - "description": "" - } - ], - "body": { - "mode": "raw", - "raw": "{\"user\":{\"email\":\"john@jacob.com\"}}" - }, - "description": "" - }, - "response": [] - } - ] - }, - { - "name": "Articles with authentication", - "description": "", - "item": [{ - "name": "Feed", - "event": [{ - "listen": "test", - "script": { - "type": "text/javascript", - "exec": [ - "var is200Response = responseCode.code === 200;", - "", - "tests['Response code is 200 OK'] = is200Response;", - "", - "if(is200Response){", - " var responseJSON = JSON.parse(responseBody);", - "", - " tests['Response contains \"articles\" property'] = responseJSON.hasOwnProperty('articles');", - " tests['Response contains \"articlesCount\" property'] = responseJSON.hasOwnProperty('articlesCount');", - " tests['articlesCount is an integer'] = Number.isInteger(responseJSON.articlesCount);", - "", - " if(responseJSON.articles.length){", - " var article = responseJSON.articles[0];", - "", - " tests['Article has \"title\" property'] = article.hasOwnProperty('title');", - " tests['Article has \"slug\" property'] = article.hasOwnProperty('slug');", - " tests['Article has \"body\" property'] = article.hasOwnProperty('body');", - " tests['Article has \"createdAt\" property'] = article.hasOwnProperty('createdAt');", - " tests['Article\\'s \"createdAt\" property is an ISO 8601 timestamp'] = new Date(article.createdAt).toISOString() === article.createdAt;", - " tests['Article has \"updatedAt\" property'] = article.hasOwnProperty('updatedAt');", - " tests['Article\\'s \"updatedAt\" property is an ISO 8601 timestamp'] = new Date(article.updatedAt).toISOString() === article.updatedAt;", - " tests['Article has \"description\" property'] = article.hasOwnProperty('description');", - " tests['Article has \"tagList\" property'] = article.hasOwnProperty('tagList');", - " tests['Article\\'s \"tagList\" property is an Array'] = Array.isArray(article.tagList);", - " tests['Article has \"author\" property'] = article.hasOwnProperty('author');", - " tests['Article has \"favorited\" property'] = article.hasOwnProperty('favorited');", - " tests['Article has \"favoritesCount\" property'] = article.hasOwnProperty('favoritesCount');", - " tests['favoritesCount is an integer'] = Number.isInteger(article.favoritesCount);", - " } else {", - " tests['articlesCount is 0 when feed is empty'] = responseJSON.articlesCount === 0;", - " }", - "}", - "" - ] - } - }], - "request": { - "url": "{{apiUrl}}/articles/feed", - "method": "GET", - "header": [{ - "key": "Content-Type", - "value": "application/json", - "description": "" - }, - { - "key": "X-Requested-With", - "value": "XMLHttpRequest", - "description": "" - }, - { - "key": "Authorization", - "value": "Token {{token}}", - "description": "" - } - ], - "body": { - "mode": "raw", - "raw": "" - }, - "description": "" - }, - "response": [] - }, - { - "name": "All Articles", - "event": [{ - "listen": "test", - "script": { - "type": "text/javascript", - "exec": [ - "var is200Response = responseCode.code === 200;", - "", - "tests['Response code is 200 OK'] = is200Response;", - "", - "if(is200Response){", - " var responseJSON = JSON.parse(responseBody);", - "", - " tests['Response contains \"articles\" property'] = responseJSON.hasOwnProperty('articles');", - " tests['Response contains \"articlesCount\" property'] = responseJSON.hasOwnProperty('articlesCount');", - " tests['articlesCount is an integer'] = Number.isInteger(responseJSON.articlesCount);", - "", - " if(responseJSON.articles.length){", - " var article = responseJSON.articles[0];", - "", - " tests['Article has \"title\" property'] = article.hasOwnProperty('title');", - " tests['Article has \"slug\" property'] = article.hasOwnProperty('slug');", - " tests['Article has \"body\" property'] = article.hasOwnProperty('body');", - " tests['Article has \"createdAt\" property'] = article.hasOwnProperty('createdAt');", - " tests['Article\\'s \"createdAt\" property is an ISO 8601 timestamp'] = new Date(article.createdAt).toISOString() === article.createdAt;", - " tests['Article has \"updatedAt\" property'] = article.hasOwnProperty('updatedAt');", - " tests['Article\\'s \"updatedAt\" property is an ISO 8601 timestamp'] = new Date(article.updatedAt).toISOString() === article.updatedAt;", - " tests['Article has \"description\" property'] = article.hasOwnProperty('description');", - " tests['Article has \"tagList\" property'] = article.hasOwnProperty('tagList');", - " tests['Article\\'s \"tagList\" property is an Array'] = Array.isArray(article.tagList);", - " tests['Article has \"author\" property'] = article.hasOwnProperty('author');", - " tests['Article has \"favorited\" property'] = article.hasOwnProperty('favorited');", - " tests['Article has \"favoritesCount\" property'] = article.hasOwnProperty('favoritesCount');", - " tests['favoritesCount is an integer'] = Number.isInteger(article.favoritesCount);", - " } else {", - " tests['articlesCount is 0 when feed is empty'] = responseJSON.articlesCount === 0;", - " }", - "}", - "" - ] - } - }], - "request": { - "url": "{{apiUrl}}/articles", - "method": "GET", - "header": [{ - "key": "Content-Type", - "value": "application/json", - "description": "" - }, - { - "key": "X-Requested-With", - "value": "XMLHttpRequest", - "description": "" - }, - { - "key": "Authorization", - "value": "Token {{token}}", - "description": "" - } - ], - "body": { - "mode": "raw", - "raw": "" - }, - "description": "" - }, - "response": [] - }, - { - "name": "All Articles with auth", - "event": [{ - "listen": "test", - "script": { - "type": "text/javascript", - "exec": [ - "var is200Response = responseCode.code === 200;", - "", - "tests['Response code is 200 OK'] = is200Response;", - "", - "if(is200Response){", - " var responseJSON = JSON.parse(responseBody);", - "", - " tests['Response contains \"articles\" property'] = responseJSON.hasOwnProperty('articles');", - " tests['Response contains \"articlesCount\" property'] = responseJSON.hasOwnProperty('articlesCount');", - " tests['articlesCount is an integer'] = Number.isInteger(responseJSON.articlesCount);", - "", - " if(responseJSON.articles.length){", - " var article = responseJSON.articles[0];", - "", - " tests['Article has \"title\" property'] = article.hasOwnProperty('title');", - " tests['Article has \"slug\" property'] = article.hasOwnProperty('slug');", - " tests['Article has \"body\" property'] = article.hasOwnProperty('body');", - " tests['Article has \"createdAt\" property'] = article.hasOwnProperty('createdAt');", - " tests['Article\\'s \"createdAt\" property is an ISO 8601 timestamp'] = new Date(article.createdAt).toISOString() === article.createdAt;", - " tests['Article has \"updatedAt\" property'] = article.hasOwnProperty('updatedAt');", - " tests['Article\\'s \"updatedAt\" property is an ISO 8601 timestamp'] = new Date(article.updatedAt).toISOString() === article.updatedAt;", - " tests['Article has \"description\" property'] = article.hasOwnProperty('description');", - " tests['Article has \"tagList\" property'] = article.hasOwnProperty('tagList');", - " tests['Article\\'s \"tagList\" property is an Array'] = Array.isArray(article.tagList);", - " tests['Article has \"author\" property'] = article.hasOwnProperty('author');", - " tests['Article has \"favorited\" property'] = article.hasOwnProperty('favorited');", - " tests['Article has \"favoritesCount\" property'] = article.hasOwnProperty('favoritesCount');", - " tests['favoritesCount is an integer'] = Number.isInteger(article.favoritesCount);", - " } else {", - " tests['articlesCount is 0 when feed is empty'] = responseJSON.articlesCount === 0;", - " }", - "}", - "" - ] - } - }], - "request": { - "url": "{{apiUrl}}/articles", - "method": "GET", - "header": [{ - "key": "Content-Type", - "value": "application/json", - "description": "" - }, - { - "key": "X-Requested-With", - "value": "XMLHttpRequest", - "description": "" - }, - { - "key": "Authorization", - "value": "Token {{token}}", - "description": "" - } - ], - "body": { - "mode": "raw", - "raw": "" - }, - "description": "" - }, - "response": [] - }, - { - "name": "Articles by Author", - "event": [{ - "listen": "test", - "script": { - "type": "text/javascript", - "exec": [ - "var is200Response = responseCode.code === 200;", - "", - "tests['Response code is 200 OK'] = is200Response;", - "", - "if(is200Response){", - " var responseJSON = JSON.parse(responseBody);", - "", - " tests['Response contains \"articles\" property'] = responseJSON.hasOwnProperty('articles');", - " tests['Response contains \"articlesCount\" property'] = responseJSON.hasOwnProperty('articlesCount');", - " tests['articlesCount is an integer'] = Number.isInteger(responseJSON.articlesCount);", - "", - " if(responseJSON.articles.length){", - " var article = responseJSON.articles[0];", - "", - " tests['Article has \"title\" property'] = article.hasOwnProperty('title');", - " tests['Article has \"slug\" property'] = article.hasOwnProperty('slug');", - " tests['Article has \"body\" property'] = article.hasOwnProperty('body');", - " tests['Article has \"createdAt\" property'] = article.hasOwnProperty('createdAt');", - " tests['Article\\'s \"createdAt\" property is an ISO 8601 timestamp'] = new Date(article.createdAt).toISOString() === article.createdAt;", - " tests['Article has \"updatedAt\" property'] = article.hasOwnProperty('updatedAt');", - " tests['Article\\'s \"updatedAt\" property is an ISO 8601 timestamp'] = new Date(article.updatedAt).toISOString() === article.updatedAt;", - " tests['Article has \"description\" property'] = article.hasOwnProperty('description');", - " tests['Article has \"tagList\" property'] = article.hasOwnProperty('tagList');", - " tests['Article\\'s \"tagList\" property is an Array'] = Array.isArray(article.tagList);", - " tests['Article has \"author\" property'] = article.hasOwnProperty('author');", - " tests['Article has \"favorited\" property'] = article.hasOwnProperty('favorited');", - " tests['Article has \"favoritesCount\" property'] = article.hasOwnProperty('favoritesCount');", - " tests['favoritesCount is an integer'] = Number.isInteger(article.favoritesCount);", - " } else {", - " tests['articlesCount is 0 when feed is empty'] = responseJSON.articlesCount === 0;", - " }", - "}", - "" - ] - } - }], - "request": { - "url": { - "raw": "{{apiUrl}}/articles?author=johnjacob", - "host": [ - "{{apiUrl}}" - ], - "path": [ - "articles" - ], - "query": [{ - "key": "author", - "value": "johnjacob" - }], - "variable": [] - }, - "method": "GET", - "header": [{ - "key": "Content-Type", - "value": "application/json", - "description": "" - }, - { - "key": "X-Requested-With", - "value": "XMLHttpRequest", - "description": "" - }, - { - "key": "Authorization", - "value": "Token {{token}}", - "description": "" - } - ], - "body": { - "mode": "raw", - "raw": "" - }, - "description": "" - }, - "response": [] - }, - { - "name": "Articles by Author with auth", - "event": [{ - "listen": "test", - "script": { - "type": "text/javascript", - "exec": [ - "var is200Response = responseCode.code === 200;", - "", - "tests['Response code is 200 OK'] = is200Response;", - "", - "if(is200Response){", - " var responseJSON = JSON.parse(responseBody);", - "", - " tests['Response contains \"articles\" property'] = responseJSON.hasOwnProperty('articles');", - " tests['Response contains \"articlesCount\" property'] = responseJSON.hasOwnProperty('articlesCount');", - " tests['articlesCount is an integer'] = Number.isInteger(responseJSON.articlesCount);", - "", - " if(responseJSON.articles.length){", - " var article = responseJSON.articles[0];", - "", - " tests['Article has \"title\" property'] = article.hasOwnProperty('title');", - " tests['Article has \"slug\" property'] = article.hasOwnProperty('slug');", - " tests['Article has \"body\" property'] = article.hasOwnProperty('body');", - " tests['Article has \"createdAt\" property'] = article.hasOwnProperty('createdAt');", - " tests['Article\\'s \"createdAt\" property is an ISO 8601 timestamp'] = new Date(article.createdAt).toISOString() === article.createdAt;", - " tests['Article has \"updatedAt\" property'] = article.hasOwnProperty('updatedAt');", - " tests['Article\\'s \"updatedAt\" property is an ISO 8601 timestamp'] = new Date(article.updatedAt).toISOString() === article.updatedAt;", - " tests['Article has \"description\" property'] = article.hasOwnProperty('description');", - " tests['Article has \"tagList\" property'] = article.hasOwnProperty('tagList');", - " tests['Article\\'s \"tagList\" property is an Array'] = Array.isArray(article.tagList);", - " tests['Article has \"author\" property'] = article.hasOwnProperty('author');", - " tests['Article has \"favorited\" property'] = article.hasOwnProperty('favorited');", - " tests['Article has \"favoritesCount\" property'] = article.hasOwnProperty('favoritesCount');", - " tests['favoritesCount is an integer'] = Number.isInteger(article.favoritesCount);", - " } else {", - " tests['articlesCount is 0 when feed is empty'] = responseJSON.articlesCount === 0;", - " }", - "}", - "" - ] - } - }], - "request": { - "url": { - "raw": "{{apiUrl}}/articles?author=johnjacob", - "host": [ - "{{apiUrl}}" - ], - "path": [ - "articles" - ], - "query": [{ - "key": "author", - "value": "johnjacob", - "equals": true, - "description": "" - }], - "variable": [] - }, - "method": "GET", - "header": [{ - "key": "Content-Type", - "value": "application/json", - "description": "" - }, - { - "key": "X-Requested-With", - "value": "XMLHttpRequest", - "description": "" - }, - { - "key": "Authorization", - "value": "Token {{token}}", - "description": "" - } - ], - "body": {}, - "description": "" - }, - "response": [] - }, - { - "name": "Articles Favorited by Username", - "event": [{ - "listen": "test", - "script": { - "type": "text/javascript", - "exec": [ - "var is200Response = responseCode.code === 200;", - "", - "tests['Response code is 200 OK'] = is200Response;", - "", - "if(is200Response){", - " var responseJSON = JSON.parse(responseBody);", - " ", - " tests['Response contains \"articles\" property'] = responseJSON.hasOwnProperty('articles');", - " tests['Response contains \"articlesCount\" property'] = responseJSON.hasOwnProperty('articlesCount');", - " tests['articlesCount is an integer'] = Number.isInteger(responseJSON.articlesCount);", - "", - " if(responseJSON.articles.length){", - " var article = responseJSON.articles[0];", - "", - " tests['Article has \"title\" property'] = article.hasOwnProperty('title');", - " tests['Article has \"slug\" property'] = article.hasOwnProperty('slug');", - " tests['Article has \"body\" property'] = article.hasOwnProperty('body');", - " tests['Article has \"createdAt\" property'] = article.hasOwnProperty('createdAt');", - " tests['Article\\'s \"createdAt\" property is an ISO 8601 timestamp'] = new Date(article.createdAt).toISOString() === article.createdAt;", - " tests['Article has \"updatedAt\" property'] = article.hasOwnProperty('updatedAt');", - " tests['Article\\'s \"updatedAt\" property is an ISO 8601 timestamp'] = new Date(article.updatedAt).toISOString() === article.updatedAt;", - " tests['Article has \"description\" property'] = article.hasOwnProperty('description');", - " tests['Article has \"tagList\" property'] = article.hasOwnProperty('tagList');", - " tests['Article\\'s \"tagList\" property is an Array'] = Array.isArray(article.tagList);", - " tests['Article has \"author\" property'] = article.hasOwnProperty('author');", - " tests['Article has \"favorited\" property'] = article.hasOwnProperty('favorited');", - " tests['Article has \"favoritesCount\" property'] = article.hasOwnProperty('favoritesCount');", - " tests['favoritesCount is an integer'] = Number.isInteger(article.favoritesCount);", - " } else {", - " tests['articlesCount is 0 when feed is empty'] = responseJSON.articlesCount === 0;", - " }", - "}", - "" - ] - } - }], - "request": { - "url": { - "raw": "{{apiUrl}}/articles?favorited=jane", - "host": [ - "{{apiUrl}}" - ], - "path": [ - "articles" - ], - "query": [{ - "key": "favorited", - "value": "jane" - }], - "variable": [] - }, - "method": "GET", - "header": [{ - "key": "Content-Type", - "value": "application/json", - "description": "" - }, - { - "key": "X-Requested-With", - "value": "XMLHttpRequest", - "description": "" - }, - { - "key": "Authorization", - "value": "Token {{token}}", - "description": "" - } - ], - "body": { - "mode": "raw", - "raw": "" - }, - "description": "" - }, - "response": [] - }, - { - "name": "Articles Favorited by Username with auth", - "event": [{ - "listen": "test", - "script": { - "type": "text/javascript", - "exec": [ - "var is200Response = responseCode.code === 200;", - "", - "tests['Response code is 200 OK'] = is200Response;", - "", - "if(is200Response){", - " var responseJSON = JSON.parse(responseBody);", - " ", - " tests['Response contains \"articles\" property'] = responseJSON.hasOwnProperty('articles');", - " tests['Response contains \"articlesCount\" property'] = responseJSON.hasOwnProperty('articlesCount');", - " tests['articlesCount is an integer'] = Number.isInteger(responseJSON.articlesCount);", - "", - " if(responseJSON.articles.length){", - " var article = responseJSON.articles[0];", - "", - " tests['Article has \"title\" property'] = article.hasOwnProperty('title');", - " tests['Article has \"slug\" property'] = article.hasOwnProperty('slug');", - " tests['Article has \"body\" property'] = article.hasOwnProperty('body');", - " tests['Article has \"createdAt\" property'] = article.hasOwnProperty('createdAt');", - " tests['Article\\'s \"createdAt\" property is an ISO 8601 timestamp'] = new Date(article.createdAt).toISOString() === article.createdAt;", - " tests['Article has \"updatedAt\" property'] = article.hasOwnProperty('updatedAt');", - " tests['Article\\'s \"updatedAt\" property is an ISO 8601 timestamp'] = new Date(article.updatedAt).toISOString() === article.updatedAt;", - " tests['Article has \"description\" property'] = article.hasOwnProperty('description');", - " tests['Article has \"tagList\" property'] = article.hasOwnProperty('tagList');", - " tests['Article\\'s \"tagList\" property is an Array'] = Array.isArray(article.tagList);", - " tests['Article has \"author\" property'] = article.hasOwnProperty('author');", - " tests['Article has \"favorited\" property'] = article.hasOwnProperty('favorited');", - " tests['Article has \"favoritesCount\" property'] = article.hasOwnProperty('favoritesCount');", - " tests['favoritesCount is an integer'] = Number.isInteger(article.favoritesCount);", - " } else {", - " tests['articlesCount is 0 when feed is empty'] = responseJSON.articlesCount === 0;", - " }", - "}", - "" - ] - } - }], - "request": { - "url": { - "raw": "{{apiUrl}}/articles?favorited=jane", - "host": [ - "{{apiUrl}}" - ], - "path": [ - "articles" - ], - "query": [{ - "key": "favorited", - "value": "jane" - }], - "variable": [] - }, - "method": "GET", - "header": [{ - "key": "Content-Type", - "value": "application/json", - "description": "" - }, - { - "key": "X-Requested-With", - "value": "XMLHttpRequest", - "description": "" - }, - { - "key": "Authorization", - "value": "Token {{token}}", - "description": "" - } - ], - "body": { - "mode": "raw", - "raw": "" - }, - "description": "" - }, - "response": [] - }, - { - "name": "Articles by Tag", - "event": [{ - "listen": "test", - "script": { - "type": "text/javascript", - "exec": [ - "var is200Response = responseCode.code === 200;", - "", - "tests['Response code is 200 OK'] = is200Response;", - "", - "if(is200Response){", - " var responseJSON = JSON.parse(responseBody);", - "", - " tests['Response contains \"articles\" property'] = responseJSON.hasOwnProperty('articles');", - " tests['Response contains \"articlesCount\" property'] = responseJSON.hasOwnProperty('articlesCount');", - " tests['articlesCount is an integer'] = Number.isInteger(responseJSON.articlesCount);", - "", - " if(responseJSON.articles.length){", - " var article = responseJSON.articles[0];", - "", - " tests['Article has \"title\" property'] = article.hasOwnProperty('title');", - " tests['Article has \"slug\" property'] = article.hasOwnProperty('slug');", - " tests['Article has \"body\" property'] = article.hasOwnProperty('body');", - " tests['Article has \"createdAt\" property'] = article.hasOwnProperty('createdAt');", - " tests['Article\\'s \"createdAt\" property is an ISO 8601 timestamp'] = new Date(article.createdAt).toISOString() === article.createdAt;", - " tests['Article has \"updatedAt\" property'] = article.hasOwnProperty('updatedAt');", - " tests['Article\\'s \"updatedAt\" property is an ISO 8601 timestamp'] = new Date(article.updatedAt).toISOString() === article.updatedAt;", - " tests['Article has \"description\" property'] = article.hasOwnProperty('description');", - " tests['Article has \"tagList\" property'] = article.hasOwnProperty('tagList');", - " tests['Article\\'s \"tagList\" property is an Array'] = Array.isArray(article.tagList);", - " tests['Article has \"author\" property'] = article.hasOwnProperty('author');", - " tests['Article has \"favorited\" property'] = article.hasOwnProperty('favorited');", - " tests['Article has \"favoritesCount\" property'] = article.hasOwnProperty('favoritesCount');", - " tests['favoritesCount is an integer'] = Number.isInteger(article.favoritesCount);", - " } else {", - " tests['articlesCount is 0 when feed is empty'] = responseJSON.articlesCount === 0;", - " }", - "}", - "" - ] - } - }], - "request": { - "url": { - "raw": "{{apiUrl}}/articles?tag=dragons", - "host": [ - "{{apiUrl}}" - ], - "path": [ - "articles" - ], - "query": [{ - "key": "tag", - "value": "dragons" - }], - "variable": [] - }, - "method": "GET", - "header": [{ - "key": "Content-Type", - "value": "application/json", - "description": "" - }, - { - "key": "X-Requested-With", - "value": "XMLHttpRequest", - "description": "" - }, - { - "key": "Authorization", - "value": "Token {{token}}", - "description": "" - } - ], - "body": { - "mode": "raw", - "raw": "" - }, - "description": "" - }, - "response": [] - }, - { - "name": "Create Article", - "event": [{ - "listen": "test", - "script": { - "type": "text/javascript", - "exec": [ - "var responseJSON = JSON.parse(responseBody);", - "", - "tests['Response contains \"article\" property'] = responseJSON.hasOwnProperty('article');", - "", - "var article = responseJSON.article || {};", - "", - "tests['Article has \"title\" property'] = article.hasOwnProperty('title');", - "tests['Article has \"slug\" property'] = article.hasOwnProperty('slug');", - "if(tests['Article has \"slug\" property']){", - " postman.setEnvironmentVariable('slug', article.slug);", - "}", - "tests['Article has \"body\" property'] = article.hasOwnProperty('body');", - "tests['Article has \"createdAt\" property'] = article.hasOwnProperty('createdAt');", - "tests['Article\\'s \"createdAt\" property is an ISO 8601 timestamp'] = new Date(article.createdAt).toISOString() === article.createdAt;", - "tests['Article has \"updatedAt\" property'] = article.hasOwnProperty('updatedAt');", - "tests['Article\\'s \"updatedAt\" property is an ISO 8601 timestamp'] = new Date(article.updatedAt).toISOString() === article.updatedAt;", - "tests['Article has \"description\" property'] = article.hasOwnProperty('description');", - "tests['Article has \"tagList\" property'] = article.hasOwnProperty('tagList');", - "tests['Article\\'s \"tagList\" property is an Array'] = Array.isArray(article.tagList);", - "tests['Article has \"author\" property'] = article.hasOwnProperty('author');", - "tests['Article has \"favorited\" property'] = article.hasOwnProperty('favorited');", - "tests['Article has \"favoritesCount\" property'] = article.hasOwnProperty('favoritesCount');", - "tests['favoritesCount is an integer'] = Number.isInteger(article.favoritesCount);", - "" - ] - } - }], - "request": { - "url": "{{apiUrl}}/articles", - "method": "POST", - "header": [{ - "key": "Content-Type", - "value": "application/json", - "description": "" - }, - { - "key": "X-Requested-With", - "value": "XMLHttpRequest", - "description": "" - }, - { - "key": "Authorization", - "value": "Token {{token}}", - "description": "" - } - ], - "body": { - "mode": "raw", - "raw": "{\"article\":{\"title\":\"How to train your dragon\", \"description\":\"Ever wonder how?\", \"body\":\"Very carefully.\", \"tagList\":[\"dragons\",\"training\"]}}" - }, - "description": "" - }, - "response": [] - }, - { - "name": "Single Article by slug", - "event": [{ - "listen": "test", - "script": { - "type": "text/javascript", - "exec": [ - "var responseJSON = JSON.parse(responseBody);", - "", - "tests['Response contains \"article\" property'] = responseJSON.hasOwnProperty('article');", - "", - "var article = responseJSON.article || {};", - "", - "tests['Article has \"title\" property'] = article.hasOwnProperty('title');", - "tests['Article has \"slug\" property'] = article.hasOwnProperty('slug');", - "tests['Article has \"body\" property'] = article.hasOwnProperty('body');", - "tests['Article has \"createdAt\" property'] = article.hasOwnProperty('createdAt');", - "tests['Article\\'s \"createdAt\" property is an ISO 8601 timestamp'] = new Date(article.createdAt).toISOString() === article.createdAt;", - "tests['Article has \"updatedAt\" property'] = article.hasOwnProperty('updatedAt');", - "tests['Article\\'s \"updatedAt\" property is an ISO 8601 timestamp'] = new Date(article.updatedAt).toISOString() === article.updatedAt;", - "tests['Article has \"description\" property'] = article.hasOwnProperty('description');", - "tests['Article has \"tagList\" property'] = article.hasOwnProperty('tagList');", - "tests['Article\\'s \"tagList\" property is an Array'] = Array.isArray(article.tagList);", - "tests['Article has \"author\" property'] = article.hasOwnProperty('author');", - "tests['Article has \"favorited\" property'] = article.hasOwnProperty('favorited');", - "tests['Article has \"favoritesCount\" property'] = article.hasOwnProperty('favoritesCount');", - "tests['favoritesCount is an integer'] = Number.isInteger(article.favoritesCount);", - "" - ] - } - }], - "request": { - "url": "{{apiUrl}}/articles/{{slug}}", - "method": "GET", - "header": [{ - "key": "Content-Type", - "value": "application/json", - "description": "" - }, - { - "key": "X-Requested-With", - "value": "XMLHttpRequest", - "description": "" - }, - { - "key": "Authorization", - "value": "Token {{token}}", - "description": "" - } - ], - "body": {}, - "description": "" - }, - "response": [] - }, - { - "name": "Update Article", - "event": [{ - "listen": "test", - "script": { - "type": "text/javascript", - "exec": [ - "if (!(environment.isIntegrationTest)) {", - "var responseJSON = JSON.parse(responseBody);", - "", - "tests['Response contains \"article\" property'] = responseJSON.hasOwnProperty('article');", - "", - "var article = responseJSON.article || {};", - "", - "tests['Article has \"title\" property'] = article.hasOwnProperty('title');", - "tests['Article has \"slug\" property'] = article.hasOwnProperty('slug');", - "tests['Article has \"body\" property'] = article.hasOwnProperty('body');", - "tests['Article has \"createdAt\" property'] = article.hasOwnProperty('createdAt');", - "tests['Article\\'s \"createdAt\" property is an ISO 8601 timestamp'] = new Date(article.createdAt).toISOString() === article.createdAt;", - "tests['Article has \"updatedAt\" property'] = article.hasOwnProperty('updatedAt');", - "tests['Article\\'s \"updatedAt\" property is an ISO 8601 timestamp'] = new Date(article.updatedAt).toISOString() === article.updatedAt;", - "tests['Article has \"description\" property'] = article.hasOwnProperty('description');", - "tests['Article has \"tagList\" property'] = article.hasOwnProperty('tagList');", - "tests['Article\\'s \"tagList\" property is an Array'] = Array.isArray(article.tagList);", - "tests['Article has \"author\" property'] = article.hasOwnProperty('author');", - "tests['Article has \"favorited\" property'] = article.hasOwnProperty('favorited');", - "tests['Article has \"favoritesCount\" property'] = article.hasOwnProperty('favoritesCount');", - "tests['favoritesCount is an integer'] = Number.isInteger(article.favoritesCount);", - "}", - "" - ] - } - }], - "request": { - "url": "{{apiUrl}}/articles/{{slug}}", - "method": "PUT", - "header": [{ - "key": "Content-Type", - "value": "application/json", - "description": "" - }, - { - "key": "X-Requested-With", - "value": "XMLHttpRequest", - "description": "" - }, - { - "key": "Authorization", - "value": "Token {{token}}", - "description": "" - } - ], - "body": { - "mode": "raw", - "raw": "{\"article\":{\"body\":\"With two hands\"}}" - }, - "description": "" - }, - "response": [] - }, - { - "name": "Favorite Article", - "event": [{ - "listen": "test", - "script": { - "type": "text/javascript", - "exec": [ - "var responseJSON = JSON.parse(responseBody);", - "", - "tests['Response contains \"article\" property'] = responseJSON.hasOwnProperty('article');", - "", - "var article = responseJSON.article || {};", - "", - "tests['Article has \"title\" property'] = article.hasOwnProperty('title');", - "tests['Article has \"slug\" property'] = article.hasOwnProperty('slug');", - "tests['Article has \"body\" property'] = article.hasOwnProperty('body');", - "tests['Article has \"createdAt\" property'] = article.hasOwnProperty('createdAt');", - "tests['Article\\'s \"createdAt\" property is an ISO 8601 timestamp'] = new Date(article.createdAt).toISOString() === article.createdAt;", - "tests['Article has \"updatedAt\" property'] = article.hasOwnProperty('updatedAt');", - "tests['Article\\'s \"updatedAt\" property is an ISO 8601 timestamp'] = new Date(article.updatedAt).toISOString() === article.updatedAt;", - "tests['Article has \"description\" property'] = article.hasOwnProperty('description');", - "tests['Article has \"tagList\" property'] = article.hasOwnProperty('tagList');", - "tests['Article\\'s \"tagList\" property is an Array'] = Array.isArray(article.tagList);", - "tests['Article has \"author\" property'] = article.hasOwnProperty('author');", - "tests['Article has \"favorited\" property'] = article.hasOwnProperty('favorited');", - "tests[\"Article's 'favorited' property is true\"] = article.favorited === true;", - "tests['Article has \"favoritesCount\" property'] = article.hasOwnProperty('favoritesCount');", - "tests['favoritesCount is an integer'] = Number.isInteger(article.favoritesCount);", - "tests[\"Article's 'favoritesCount' property is greater than 0\"] = article.favoritesCount > 0;", - "" - ] - } - }], - "request": { - "url": "{{apiUrl}}/articles/{{slug}}/favorite", - "method": "POST", - "header": [{ - "key": "Content-Type", - "value": "application/json", - "description": "" - }, - { - "key": "X-Requested-With", - "value": "XMLHttpRequest", - "description": "" - }, - { - "key": "Authorization", - "value": "Token {{token}}", - "description": "" - } - ], - "body": { - "mode": "raw", - "raw": "" - }, - "description": "" - }, - "response": [] - }, - { - "name": "Unfavorite Article", - "event": [{ - "listen": "test", - "script": { - "type": "text/javascript", - "exec": [ - "var responseJSON = JSON.parse(responseBody);", - "", - "tests['Response contains \"article\" property'] = responseJSON.hasOwnProperty('article');", - "", - "var article = responseJSON.article || {};", - "", - "tests['Article has \"title\" property'] = article.hasOwnProperty('title');", - "tests['Article has \"slug\" property'] = article.hasOwnProperty('slug');", - "tests['Article has \"body\" property'] = article.hasOwnProperty('body');", - "tests['Article has \"createdAt\" property'] = article.hasOwnProperty('createdAt');", - "tests['Article\\'s \"createdAt\" property is an ISO 8601 timestamp'] = new Date(article.createdAt).toISOString() === article.createdAt;", - "tests['Article has \"updatedAt\" property'] = article.hasOwnProperty('updatedAt');", - "tests['Article\\'s \"updatedAt\" property is an ISO 8601 timestamp'] = new Date(article.updatedAt).toISOString() === article.updatedAt;", - "tests['Article has \"description\" property'] = article.hasOwnProperty('description');", - "tests['Article has \"tagList\" property'] = article.hasOwnProperty('tagList');", - "tests['Article\\'s \"tagList\" property is an Array'] = Array.isArray(article.tagList);", - "tests['Article has \"author\" property'] = article.hasOwnProperty('author');", - "tests['Article has \"favorited\" property'] = article.hasOwnProperty('favorited');", - "tests['Article has \"favoritesCount\" property'] = article.hasOwnProperty('favoritesCount');", - "tests['favoritesCount is an integer'] = Number.isInteger(article.favoritesCount);", - "tests[\"Article's \\\"favorited\\\" property is true\"] = article.favorited === false;", - "" - ] - } - }], - "request": { - "url": "{{apiUrl}}/articles/{{slug}}/favorite", - "method": "DELETE", - "header": [{ - "key": "Content-Type", - "value": "application/json", - "description": "" - }, - { - "key": "X-Requested-With", - "value": "XMLHttpRequest", - "description": "" - }, - { - "key": "Authorization", - "value": "Token {{token}}", - "description": "" - } - ], - "body": { - "mode": "raw", - "raw": "" - }, - "description": "" - }, - "response": [] - } - ] - }, - { - "name": "Articles", - "description": "", - "item": [{ - "name": "All Articles", - "event": [{ - "listen": "test", - "script": { - "type": "text/javascript", - "exec": [ - "var is200Response = responseCode.code === 200;", - "", - "tests['Response code is 200 OK'] = is200Response;", - "", - "if(is200Response){", - " var responseJSON = JSON.parse(responseBody);", - "", - " tests['Response contains \"articles\" property'] = responseJSON.hasOwnProperty('articles');", - " tests['Response contains \"articlesCount\" property'] = responseJSON.hasOwnProperty('articlesCount');", - " tests['articlesCount is an integer'] = Number.isInteger(responseJSON.articlesCount);", - "", - " if(responseJSON.articles.length){", - " var article = responseJSON.articles[0];", - "", - " tests['Article has \"title\" property'] = article.hasOwnProperty('title');", - " tests['Article has \"slug\" property'] = article.hasOwnProperty('slug');", - " tests['Article has \"body\" property'] = article.hasOwnProperty('body');", - " tests['Article has \"createdAt\" property'] = article.hasOwnProperty('createdAt');", - " tests['Article\\'s \"createdAt\" property is an ISO 8601 timestamp'] = new Date(article.createdAt).toISOString() === article.createdAt;", - " tests['Article has \"updatedAt\" property'] = article.hasOwnProperty('updatedAt');", - " tests['Article\\'s \"updatedAt\" property is an ISO 8601 timestamp'] = new Date(article.updatedAt).toISOString() === article.updatedAt;", - " tests['Article has \"description\" property'] = article.hasOwnProperty('description');", - " tests['Article has \"tagList\" property'] = article.hasOwnProperty('tagList');", - " tests['Article\\'s \"tagList\" property is an Array'] = Array.isArray(article.tagList);", - " tests['Article has \"author\" property'] = article.hasOwnProperty('author');", - " tests['Article has \"favorited\" property'] = article.hasOwnProperty('favorited');", - " tests['Article has \"favoritesCount\" property'] = article.hasOwnProperty('favoritesCount');", - " tests['favoritesCount is an integer'] = Number.isInteger(article.favoritesCount);", - " } else {", - " tests['articlesCount is 0 when feed is empty'] = responseJSON.articlesCount === 0;", - " }", - "}", - "" - ] - } - }], - "request": { - "url": "{{apiUrl}}/articles", - "method": "GET", - "header": [{ - "key": "Content-Type", - "value": "application/json", - "description": "" - }, - { - "key": "X-Requested-With", - "value": "XMLHttpRequest", - "description": "" - } - ], - "body": { - "mode": "raw", - "raw": "" - }, - "description": "" - }, - "response": [] - }, - { - "name": "Articles by Author", - "event": [{ - "listen": "test", - "script": { - "type": "text/javascript", - "exec": [ - "var is200Response = responseCode.code === 200;", - "", - "tests['Response code is 200 OK'] = is200Response;", - "", - "if(is200Response){", - " var responseJSON = JSON.parse(responseBody);", - "", - " tests['Response contains \"articles\" property'] = responseJSON.hasOwnProperty('articles');", - " tests['Response contains \"articlesCount\" property'] = responseJSON.hasOwnProperty('articlesCount');", - " tests['articlesCount is an integer'] = Number.isInteger(responseJSON.articlesCount);", - "", - " if(responseJSON.articles.length){", - " var article = responseJSON.articles[0];", - "", - " tests['Article has \"title\" property'] = article.hasOwnProperty('title');", - " tests['Article has \"slug\" property'] = article.hasOwnProperty('slug');", - " tests['Article has \"body\" property'] = article.hasOwnProperty('body');", - " tests['Article has \"createdAt\" property'] = article.hasOwnProperty('createdAt');", - " tests['Article\\'s \"createdAt\" property is an ISO 8601 timestamp'] = new Date(article.createdAt).toISOString() === article.createdAt;", - " tests['Article has \"updatedAt\" property'] = article.hasOwnProperty('updatedAt');", - " tests['Article\\'s \"updatedAt\" property is an ISO 8601 timestamp'] = new Date(article.updatedAt).toISOString() === article.updatedAt;", - " tests['Article has \"description\" property'] = article.hasOwnProperty('description');", - " tests['Article has \"tagList\" property'] = article.hasOwnProperty('tagList');", - " tests['Article\\'s \"tagList\" property is an Array'] = Array.isArray(article.tagList);", - " tests['Article has \"author\" property'] = article.hasOwnProperty('author');", - " tests['Article has \"favorited\" property'] = article.hasOwnProperty('favorited');", - " tests['Article has \"favoritesCount\" property'] = article.hasOwnProperty('favoritesCount');", - " tests['favoritesCount is an integer'] = Number.isInteger(article.favoritesCount);", - " } else {", - " tests['articlesCount is 0 when feed is empty'] = responseJSON.articlesCount === 0;", - " }", - "}", - "" - ] - } - }], - "request": { - "url": { - "raw": "{{apiUrl}}/articles?author=johnjacob", - "host": [ - "{{apiUrl}}" - ], - "path": [ - "articles" - ], - "query": [{ - "key": "author", - "value": "johnjacob" - }], - "variable": [] - }, - "method": "GET", - "header": [{ - "key": "Content-Type", - "value": "application/json", - "description": "" - }, - { - "key": "X-Requested-With", - "value": "XMLHttpRequest", - "description": "" - } - ], - "body": { - "mode": "raw", - "raw": "" - }, - "description": "" - }, - "response": [] - }, - { - "name": "Articles Favorited by Username", - "event": [{ - "listen": "test", - "script": { - "type": "text/javascript", - "exec": [ - "var is200Response = responseCode.code === 200;", - "", - "tests['Response code is 200 OK'] = is200Response;", - "", - "if(is200Response){", - " var responseJSON = JSON.parse(responseBody);", - " ", - " tests['Response contains \"articles\" property'] = responseJSON.hasOwnProperty('articles');", - " tests['Response contains \"articlesCount\" property'] = responseJSON.hasOwnProperty('articlesCount');", - " tests['articlesCount is an integer'] = Number.isInteger(responseJSON.articlesCount);", - "", - " if(responseJSON.articles.length){", - " var article = responseJSON.articles[0];", - "", - " tests['Article has \"title\" property'] = article.hasOwnProperty('title');", - " tests['Article has \"slug\" property'] = article.hasOwnProperty('slug');", - " tests['Article has \"body\" property'] = article.hasOwnProperty('body');", - " tests['Article has \"createdAt\" property'] = article.hasOwnProperty('createdAt');", - " tests['Article\\'s \"createdAt\" property is an ISO 8601 timestamp'] = new Date(article.createdAt).toISOString() === article.createdAt;", - " tests['Article has \"updatedAt\" property'] = article.hasOwnProperty('updatedAt');", - " tests['Article\\'s \"updatedAt\" property is an ISO 8601 timestamp'] = new Date(article.updatedAt).toISOString() === article.updatedAt;", - " tests['Article has \"description\" property'] = article.hasOwnProperty('description');", - " tests['Article has \"tagList\" property'] = article.hasOwnProperty('tagList');", - " tests['Article\\'s \"tagList\" property is an Array'] = Array.isArray(article.tagList);", - " tests['Article has \"author\" property'] = article.hasOwnProperty('author');", - " tests['Article has \"favorited\" property'] = article.hasOwnProperty('favorited');", - " tests['Article has \"favoritesCount\" property'] = article.hasOwnProperty('favoritesCount');", - " tests['favoritesCount is an integer'] = Number.isInteger(article.favoritesCount);", - " } else {", - " tests['articlesCount is 0 when feed is empty'] = responseJSON.articlesCount === 0;", - " }", - "}", - "" - ] - } - }], - "request": { - "url": { - "raw": "{{apiUrl}}/articles?favorited=jane", - "host": [ - "{{apiUrl}}" - ], - "path": [ - "articles" - ], - "query": [{ - "key": "favorited", - "value": "jane" - }], - "variable": [] - }, - "method": "GET", - "header": [{ - "key": "Content-Type", - "value": "application/json", - "description": "" - }, - { - "key": "X-Requested-With", - "value": "XMLHttpRequest", - "description": "" - } - ], - "body": { - "mode": "raw", - "raw": "" - }, - "description": "" - }, - "response": [] - }, - { - "name": "Articles by Tag", - "event": [{ - "listen": "test", - "script": { - "type": "text/javascript", - "exec": [ - "var is200Response = responseCode.code === 200;", - "", - "tests['Response code is 200 OK'] = is200Response;", - "", - "if(is200Response){", - " var responseJSON = JSON.parse(responseBody);", - "", - " tests['Response contains \"articles\" property'] = responseJSON.hasOwnProperty('articles');", - " tests['Response contains \"articlesCount\" property'] = responseJSON.hasOwnProperty('articlesCount');", - " tests['articlesCount is an integer'] = Number.isInteger(responseJSON.articlesCount);", - "", - " if(responseJSON.articles.length){", - " var article = responseJSON.articles[0];", - "", - " tests['Article has \"title\" property'] = article.hasOwnProperty('title');", - " tests['Article has \"slug\" property'] = article.hasOwnProperty('slug');", - " tests['Article has \"body\" property'] = article.hasOwnProperty('body');", - " tests['Article has \"createdAt\" property'] = article.hasOwnProperty('createdAt');", - " tests['Article\\'s \"createdAt\" property is an ISO 8601 timestamp'] = new Date(article.createdAt).toISOString() === article.createdAt;", - " tests['Article has \"updatedAt\" property'] = article.hasOwnProperty('updatedAt');", - " tests['Article\\'s \"updatedAt\" property is an ISO 8601 timestamp'] = new Date(article.updatedAt).toISOString() === article.updatedAt;", - " tests['Article has \"description\" property'] = article.hasOwnProperty('description');", - " tests['Article has \"tagList\" property'] = article.hasOwnProperty('tagList');", - " tests['Article\\'s \"tagList\" property is an Array'] = Array.isArray(article.tagList);", - " tests['Article has \"author\" property'] = article.hasOwnProperty('author');", - " tests['Article has \"favorited\" property'] = article.hasOwnProperty('favorited');", - " tests['Article has \"favoritesCount\" property'] = article.hasOwnProperty('favoritesCount');", - " tests['favoritesCount is an integer'] = Number.isInteger(article.favoritesCount);", - " } else {", - " tests['articlesCount is 0 when feed is empty'] = responseJSON.articlesCount === 0;", - " }", - "}", - "" - ] - } - }], - "request": { - "url": { - "raw": "{{apiUrl}}/articles?tag=dragons", - "host": [ - "{{apiUrl}}" - ], - "path": [ - "articles" - ], - "query": [{ - "key": "tag", - "value": "dragons" - }], - "variable": [] - }, - "method": "GET", - "header": [{ - "key": "Content-Type", - "value": "application/json", - "description": "" - }, - { - "key": "X-Requested-With", - "value": "XMLHttpRequest", - "description": "" - } - ], - "body": { - "mode": "raw", - "raw": "" - }, - "description": "" - }, - "response": [] - }, - { - "name": "Single Article by slug", - "event": [{ - "listen": "test", - "script": { - "type": "text/javascript", - "exec": [ - "var responseJSON = JSON.parse(responseBody);", - "", - "tests['Response contains \"article\" property'] = responseJSON.hasOwnProperty('article');", - "", - "var article = responseJSON.article || {};", - "", - "tests['Article has \"title\" property'] = article.hasOwnProperty('title');", - "tests['Article has \"slug\" property'] = article.hasOwnProperty('slug');", - "tests['Article has \"body\" property'] = article.hasOwnProperty('body');", - "tests['Article has \"createdAt\" property'] = article.hasOwnProperty('createdAt');", - "tests['Article\\'s \"createdAt\" property is an ISO 8601 timestamp'] = new Date(article.createdAt).toISOString() === article.createdAt;", - "tests['Article has \"updatedAt\" property'] = article.hasOwnProperty('updatedAt');", - "tests['Article\\'s \"updatedAt\" property is an ISO 8601 timestamp'] = new Date(article.updatedAt).toISOString() === article.updatedAt;", - "tests['Article has \"description\" property'] = article.hasOwnProperty('description');", - "tests['Article has \"tagList\" property'] = article.hasOwnProperty('tagList');", - "tests['Article\\'s \"tagList\" property is an Array'] = Array.isArray(article.tagList);", - "tests['Article has \"author\" property'] = article.hasOwnProperty('author');", - "tests['Article has \"favorited\" property'] = article.hasOwnProperty('favorited');", - "tests['Article has \"favoritesCount\" property'] = article.hasOwnProperty('favoritesCount');", - "tests['favoritesCount is an integer'] = Number.isInteger(article.favoritesCount);", - "" - ] - } - }], - "request": { - "url": "{{apiUrl}}/articles/{{slug}}", - "method": "GET", - "header": [{ - "key": "Content-Type", - "value": "application/json", - "description": "" - }, - { - "key": "X-Requested-With", - "value": "XMLHttpRequest", - "description": "" - } - ], - "body": {}, - "description": "" - }, - "response": [] - } - ] - }, - { - "name": "Comments", - "description": "", - "item": [{ - "name": "All Comments for Article", - "event": [{ - "listen": "test", - "script": { - "type": "text/javascript", - "exec": [ - "var is200Response = responseCode.code === 200", - "", - "tests['Response code is 200 OK'] = is200Response;", - "", - "if(is200Response){", - " var responseJSON = JSON.parse(responseBody);", - "", - " tests['Response contains \"comments\" property'] = responseJSON.hasOwnProperty('comments');", - "", - " if(responseJSON.comments.length){", - " var comment = responseJSON.comments[0];", - "", - " tests['Comment has \"id\" property'] = comment.hasOwnProperty('id');", - " tests['Comment has \"body\" property'] = comment.hasOwnProperty('body');", - " tests['Comment has \"createdAt\" property'] = comment.hasOwnProperty('createdAt');", - " tests['\"createdAt\" property is an ISO 8601 timestamp'] = new Date(comment.createdAt).toISOString() === comment.createdAt;", - " tests['Comment has \"updatedAt\" property'] = comment.hasOwnProperty('updatedAt');", - " tests['\"updatedAt\" property is an ISO 8601 timestamp'] = new Date(comment.updatedAt).toISOString() === comment.updatedAt;", - " tests['Comment has \"author\" property'] = comment.hasOwnProperty('author');", - " }", - "}", - "" - ] - } - }], - "request": { - "url": "{{apiUrl}}/articles/{{slug}}/comments", - "method": "GET", - "header": [{ - "key": "Content-Type", - "value": "application/json", - "description": "" - }, - { - "key": "X-Requested-With", - "value": "XMLHttpRequest", - "description": "" - }, - { - "key": "Authorization", - "value": "Token {{token}}", - "description": "" - } - ], - "body": {}, - "description": "" - }, - "response": [] - }, - { - "name": "Create Comment for Article", - "event": [{ - "listen": "test", - "script": { - "type": "text/javascript", - "exec": [ - "var responseJSON = JSON.parse(responseBody);", - "", - "tests['Response contains \"comment\" property'] = responseJSON.hasOwnProperty('comment');", - "", - "var comment = responseJSON.comment || {};", - "", - "tests['Comment has \"id\" property'] = comment.hasOwnProperty('id');", - "tests['Comment has \"body\" property'] = comment.hasOwnProperty('body');", - "tests['Comment has \"createdAt\" property'] = comment.hasOwnProperty('createdAt');", - "tests['\"createdAt\" property is an ISO 8601 timestamp'] = new Date(comment.createdAt).toISOString() === comment.createdAt;", - "tests['Comment has \"author\" property'] = comment.hasOwnProperty('author');", - "" - ] - } - }], - "request": { - "url": "{{apiUrl}}/articles/{{slug}}/comments", - "method": "POST", - "header": [{ - "key": "Content-Type", - "value": "application/json", - "description": "" - }, - { - "key": "X-Requested-With", - "value": "XMLHttpRequest", - "description": "" - }, - { - "key": "Authorization", - "value": "Token {{token}}", - "description": "" - } - ], - "body": { - "mode": "raw", - "raw": "{\"comment\":{\"body\":\"Thank you so much!\"}}" - }, - "description": "" - }, - "response": [] - }, - { - "name": "Delete Comment for Article", - "request": { - "url": "{{apiUrl}}/articles/{{slug}}/comments/1", - "method": "DELETE", - "header": [{ - "key": "Content-Type", - "value": "application/json", - "description": "" - }, - { - "key": "X-Requested-With", - "value": "XMLHttpRequest", - "description": "" - }, - { - "key": "Authorization", - "value": "Token {{token}}", - "description": "" - } - ], - "body": {}, - "description": "" - }, - "response": [] - } - ] - }, - { - "name": "Profiles", - "description": "", - "item": [{ - "name": "Profile", - "event": [{ - "listen": "test", - "script": { - "type": "text/javascript", - "exec": [ - "if (!(environment.isIntegrationTest)) {", - "var is200Response = responseCode.code === 200;", - "", - "tests['Response code is 200 OK'] = is200Response;", - "", - "if(is200Response){", - " var responseJSON = JSON.parse(responseBody);", - "", - " tests['Response contains \"profile\" property'] = responseJSON.hasOwnProperty('profile');", - " ", - " var profile = responseJSON.profile || {};", - " ", - " tests['Profile has \"username\" property'] = profile.hasOwnProperty('username');", - " tests['Profile has \"image\" property'] = profile.hasOwnProperty('image');", - " tests['Profile has \"following\" property'] = profile.hasOwnProperty('following');", - "}", - "}", - "" - ] - } - }], - "request": { - "url": "{{apiUrl}}/profiles/johnjacob", - "method": "GET", - "header": [{ - "key": "Content-Type", - "value": "application/json", - "description": "" - }, - { - "key": "X-Requested-With", - "value": "XMLHttpRequest", - "description": "" - }, - { - "key": "Authorization", - "value": "Token {{token}}", - "description": "" - } - ], - "body": {}, - "description": "" - }, - "response": [] - }, - { - "name": "Follow Profile", - "event": [{ - "listen": "test", - "script": { - "type": "text/javascript", - "exec": [ - "if (!(environment.isIntegrationTest)) {", - "var is200Response = responseCode.code === 200;", - "", - "tests['Response code is 200 OK'] = is200Response;", - "", - "if(is200Response){", - " var responseJSON = JSON.parse(responseBody);", - "", - " tests['Response contains \"profile\" property'] = responseJSON.hasOwnProperty('profile');", - " ", - " var profile = responseJSON.profile || {};", - " ", - " tests['Profile has \"username\" property'] = profile.hasOwnProperty('username');", - " tests['Profile has \"image\" property'] = profile.hasOwnProperty('image');", - " tests['Profile has \"following\" property'] = profile.hasOwnProperty('following');", - " tests['Profile\\'s \"following\" property is true'] = profile.following === true;", - "}", - "}", - "" - ] - } - }], - "request": { - "url": "{{apiUrl}}/profiles/johnjacob/follow", - "method": "POST", - "header": [{ - "key": "Content-Type", - "value": "application/json", - "description": "" - }, - { - "key": "X-Requested-With", - "value": "XMLHttpRequest", - "description": "" - }, - { - "key": "Authorization", - "value": "Token {{token}}", - "description": "" - } - ], - "body": { - "mode": "raw", - "raw": "{\"user\":{\"email\":\"john@jacob.com\"}}" - }, - "description": "" - }, - "response": [] - }, - { - "name": "Unfollow Profile", - "event": [{ - "listen": "test", - "script": { - "type": "text/javascript", - "exec": [ - "if (!(environment.isIntegrationTest)) {", - "var is200Response = responseCode.code === 200;", - "", - "tests['Response code is 200 OK'] = is200Response;", - "", - "if(is200Response){", - " var responseJSON = JSON.parse(responseBody);", - "", - " tests['Response contains \"profile\" property'] = responseJSON.hasOwnProperty('profile');", - " ", - " var profile = responseJSON.profile || {};", - " ", - " tests['Profile has \"username\" property'] = profile.hasOwnProperty('username');", - " tests['Profile has \"image\" property'] = profile.hasOwnProperty('image');", - " tests['Profile has \"following\" property'] = profile.hasOwnProperty('following');", - " tests['Profile\\'s \"following\" property is false'] = profile.following === false;", - "}", - "}", - "" - ] - } - }], - "request": { - "url": "{{apiUrl}}/profiles/johnjacob/follow", - "method": "DELETE", - "header": [{ - "key": "Content-Type", - "value": "application/json", - "description": "" - }, - { - "key": "X-Requested-With", - "value": "XMLHttpRequest", - "description": "" - }, - { - "key": "Authorization", - "value": "Token {{token}}", - "description": "" - } - ], - "body": {}, - "description": "" - }, - "response": [] - } - ] - }, - { - "name": "Tags", - "description": "", - "item": [{ - "name": "All Tags", - "event": [{ - "listen": "test", - "script": { - "type": "text/javascript", - "exec": [ - "var is200Response = responseCode.code === 200;", - "", - "tests['Response code is 200 OK'] = is200Response;", - "", - "if(is200Response){", - " var responseJSON = JSON.parse(responseBody);", - " ", - " tests['Response contains \"tags\" property'] = responseJSON.hasOwnProperty('tags');", - " tests['\"tags\" property returned as array'] = Array.isArray(responseJSON.tags);", - "}", - "" - ] - } - }], - "request": { - "url": "{{apiUrl}}/tags", - "method": "GET", - "header": [{ - "key": "Content-Type", - "value": "application/json", - "description": "" - }, - { - "key": "X-Requested-With", - "value": "XMLHttpRequest", - "description": "" - } - ], - "body": { - "mode": "raw", - "raw": "" - }, - "description": "" - }, - "response": [] - }] - }, - { - "name": "Cleanup", - "description": "", - "item": [{ - "name": "Delete Article", - "request": { - "url": "{{apiUrl}}/articles/{{slug}}", - "method": "DELETE", - "header": [{ - "key": "Content-Type", - "value": "application/json", - "description": "" - }, - { - "key": "X-Requested-With", - "value": "XMLHttpRequest", - "description": "" - }, - { - "key": "Authorization", - "value": "Token {{token}}", - "description": "" - } - ], - "body": { - "mode": "raw", - "raw": "" - }, - "description": "" - }, - "response": [] - }] - } - ] -} diff --git a/te-backend/tests/env-api-tests.postman.json b/te-backend/tests/env-api-tests.postman.json deleted file mode 100644 index f838f79c99f6aa8ac5290aeaff71b754d4dde497..0000000000000000000000000000000000000000 --- a/te-backend/tests/env-api-tests.postman.json +++ /dev/null @@ -1,14 +0,0 @@ -{ - "id": "4aa60b52-97fc-456d-4d4f-14a350e95dff", - "name": "Conduit API Tests - Environment", - "values": [{ - "enabled": true, - "key": "apiUrl", - "value": "http://localhost:3000/api", - "type": "text" - }], - "timestamp": 1505871382668, - "_postman_variable_scope": "environment", - "_postman_exported_at": "2017-09-20T01:36:34.835Z", - "_postman_exported_using": "Postman/5.2.0" -} diff --git a/te-frontend/.dockerignore b/te-frontend/.dockerignore deleted file mode 100644 index 009becc2ccd296ed11e3d799adc91ef03fd4e2cb..0000000000000000000000000000000000000000 --- a/te-frontend/.dockerignore +++ /dev/null @@ -1,5 +0,0 @@ -.gitignore -.gitlab-ci.yml -README.md -node_modules -project-logo.png diff --git a/te-frontend/.gitignore b/te-frontend/.gitignore deleted file mode 100644 index 22b3f47901f6368b76636f8dcfae1c2fd8370b47..0000000000000000000000000000000000000000 --- a/te-frontend/.gitignore +++ /dev/null @@ -1,20 +0,0 @@ -# See http://help.github.com/ignore-files/ for more about ignoring files. - -# dependencies -node_modules - -# testing -coverage - -# production -build - -# misc -.DS_Store -.env -npm-debug.log -.idea - -# used only in local development -Dockerfile-dev -docker-compose-dev.yml diff --git a/te-frontend/Dockerfile b/te-frontend/Dockerfile deleted file mode 100644 index 6d4d9e88a44114cd7ff4591843df56c886905b87..0000000000000000000000000000000000000000 --- a/te-frontend/Dockerfile +++ /dev/null @@ -1,15 +0,0 @@ -# Choose base image (maybe use node 14 for fewer warnings??) (changed from node:16) -FROM node:14 - -# Create app directory -WORKDIR /usr/src/app - -# Install app dependecies -COPY package*.json ./ -RUN npm install - -# Bundle app source -COPY . . - -EXPOSE 4100 -CMD [ "npm", "start" ] diff --git a/te-frontend/README.md b/te-frontend/README.md deleted file mode 100644 index 776ea0a6038364a08c92913053b53b2689961f7f..0000000000000000000000000000000000000000 --- a/te-frontend/README.md +++ /dev/null @@ -1,74 +0,0 @@ -#  - -[](http://realworld.io) - -> ### React + Redux codebase containing real world examples (CRUD, auth, advanced patterns, etc) that adheres to the [RealWorld](https://github.com/gothinkster/realworld-example-apps) spec and API. - -<a href="https://stackblitz.com/edit/react-redux-realworld" target="_blank"><img width="187" src="https://github.com/gothinkster/realworld/blob/master/media/edit_on_blitz.png?raw=true" /></a> <a href="https://thinkster.io/tutorials/build-a-real-world-react-redux-application" target="_blank"><img width="384" src="https://raw.githubusercontent.com/gothinkster/realworld/master/media/learn-btn-hr.png" /></a> - -### [Demo](https://react-redux.realworld.io) [RealWorld](https://github.com/gothinkster/realworld) - -Originally created for this [GH issue](https://github.com/reactjs/redux/issues/1353). The codebase is now feature complete; please submit bug fixes via pull requests & feedback via issues. - -We also have notes in [**our wiki**](https://github.com/gothinkster/react-redux-realworld-example-app/wiki) about how the various patterns used in this codebase and how they work (thanks [@thejmazz](https://github.com/thejmazz)!) - - -## Getting started - -You can view a live demo over at https://react-redux.realworld.io/ - -To get the frontend running locally: - -- Clone this repo -- `npm install` to install all req'd dependencies -- `npm start` to start the local server (this project uses create-react-app) - -Local web server will use port 4100 instead of standard React's port 3000 to prevent conflicts with some backends like Node or Rails. You can configure port in scripts section of `package.json`: we use [cross-env](https://github.com/kentcdodds/cross-env) to set environment variable PORT for React scripts, this is Windows-compatible way of setting environment variables. - -Alternatively, you can add `.env` file in the root folder of project to set environment variables (use PORT to change webserver's port). This file will be ignored by git, so it is suitable for API keys and other sensitive stuff. Refer to [dotenv](https://github.com/motdotla/dotenv) and [React](https://github.com/facebookincubator/create-react-app/blob/master/packages/react-scripts/template/README.md#adding-development-environment-variables-in-env) documentation for more details. Also, please remove setting variable via script section of `package.json` - `dotenv` never override variables if they are already set. - -### Making requests to the backend API - -For convenience, we have a live API server running at https://conduit.productionready.io/api for the application to make requests against. You can view [the API spec here](https://github.com/GoThinkster/productionready/blob/master/api) which contains all routes & responses for the server. - -The source code for the backend server (available for Node, Rails and Django) can be found in the [main RealWorld repo](https://github.com/gothinkster/realworld). - -If you want to change the API URL to a local server, simply edit `src/agent.js` and change `API_ROOT` to the local server's URL (i.e. `http://localhost:3000/api`) - - -## Functionality overview - -The example application is a social blogging site (i.e. a Medium.com clone) called "Conduit". It uses a custom API for all requests, including authentication. You can view a live demo over at https://redux.productionready.io/ - -**General functionality:** - -- Authenticate users via JWT (login/signup pages + logout button on settings page) -- CRU* users (sign up & settings page - no deleting required) -- CRUD Articles -- CR*D Comments on articles (no updating required) -- GET and display paginated lists of articles -- Favorite articles -- Follow other users - -**The general page breakdown looks like this:** - -- Home page (URL: /#/ ) - - List of tags - - List of articles pulled from either Feed, Global, or by Tag - - Pagination for list of articles -- Sign in/Sign up pages (URL: /#/login, /#/register ) - - Use JWT (store the token in localStorage) -- Settings page (URL: /#/settings ) -- Editor page to create/edit articles (URL: /#/editor, /#/editor/article-slug-here ) -- Article page (URL: /#/article/article-slug-here ) - - Delete article button (only shown to article's author) - - Render markdown from server client side - - Comments section at bottom of page - - Delete comment button (only shown to comment's author) -- Profile page (URL: /#/@username, /#/@username/favorites ) - - Show basic user info - - List of articles populated from author's created articles or author's favorited articles - -<br /> - -[](https://thinkster.io) diff --git a/te-frontend/package.json b/te-frontend/package.json deleted file mode 100644 index 8d7f2c4692aa43d3ef52c67783740226b0ec9b87..0000000000000000000000000000000000000000 --- a/te-frontend/package.json +++ /dev/null @@ -1,51 +0,0 @@ -{ - "name": "react-redux-realworld-example-app", - "version": "2.0.0", - "private": true, - "devDependencies": { - "cross-env": "^5.1.4", - "react-scripts": "^4.0.3" - }, - "dependencies": { - "@emotion/react": "^11.5.0", - "@emotion/styled": "^11.3.0", - "@material-ui/core": "^4.12.3", - "@material-ui/icons": "^4.11.2", - "@mui/material": "^5.1.0", - "history": "^4.6.3", - "marked": "^0.3.6", - "prop-types": "^15.5.10", - "react": "^16.3.0", - "react-dom": "^16.3.0", - "react-redux": "^5.0.7", - "react-router": "^4.1.2", - "react-router-dom": "^4.1.2", - "react-router-redux": "^5.0.0-alpha.6", - "react-share": "^4.4.0", - "redux": "^3.6.0", - "redux-devtools-extension": "^2.13.2", - "redux-logger": "^3.0.1", - "superagent": "^3.8.2", - "superagent-promise": "^1.1.0", - "sweetalert": "^2.1.2", - "sweetalert2": "^11.1.9" - }, - "scripts": { - "start": "cross-env PORT=4100 react-scripts start", - "build": "react-scripts build", - "test": "cross-env PORT=4100 react-scripts test --env=jsdom", - "eject": "react-scripts eject" - }, - "browserslist": { - "production": [ - ">0.2%", - "not dead", - "not op_mini all" - ], - "development": [ - "last 1 chrome version", - "last 1 firefox version", - "last 1 safari version" - ] - } -} diff --git a/te-frontend/project-logo.png b/te-frontend/project-logo.png deleted file mode 100644 index 901b3b61b38fe94e2ded8be5632eb358be039cbc..0000000000000000000000000000000000000000 Binary files a/te-frontend/project-logo.png and /dev/null differ diff --git a/te-frontend/public/favicon.ico b/te-frontend/public/favicon.ico deleted file mode 100644 index 5c125de5d897c1ff5692a656485b3216123dcd89..0000000000000000000000000000000000000000 Binary files a/te-frontend/public/favicon.ico and /dev/null differ diff --git a/te-frontend/public/index.html b/te-frontend/public/index.html deleted file mode 100644 index 21d61d53eb117d3e45ba74ea18f4ed210ab20753..0000000000000000000000000000000000000000 --- a/te-frontend/public/index.html +++ /dev/null @@ -1,83 +0,0 @@ -<!doctype html> -<html lang="en"> - <head> - <style> - #doorbell{ - text-align: center; - } - #doorbell-submit-button{ - background-color: #5ED9C3; - color: rgb(255, 255, 255); - font-size: 1.1rem; - border-color: #5ED9C3; - } - #doorbell-submit-button:hover{ - background-color: #20c2a4; - color: rgb(255, 255, 255); - font-size: 1.1rem; - border-color: #219c86; - } - #doorbell-powered-by{ - color: #5ED9C3 - } - </style> - <!-- Global site tag (gtag.js) - Google Analytics --> - <script async src="https://www.googletagmanager.com/gtag/js?id=G-9FGSY8NE0L"></script> - <script> - window.dataLayer = window.dataLayer || []; - function gtag(){dataLayer.push(arguments);} - gtag('js', new Date()); - - gtag('config', 'G-9FGSY8NE0L'); - </script> - - <meta charset="utf-8"> - <meta name="viewport" content="width=device-width, initial-scale=1"> - <link rel="shortcut icon" href="%PUBLIC_URL%/logo_wimma.png"> - <link rel="stylesheet" href="../main.css"> - <link href="//code.ionicframework.com/ionicons/2.0.1/css/ionicons.min.css" rel="stylesheet" type="text/css"> - <link href="//fonts.googleapis.com/css?family=Titillium+Web:700|Source+Serif+Pro:400,700|Merriweather+Sans:400,700|Source+Sans+Pro:400,300,600,700,300italic,400italic,600italic,700italic" rel="stylesheet" type="text/css"> - <!-- - Notice the use of %PUBLIC_URL% in the tag above. - It will be replaced with the URL of the `public` folder during the build. - Only files inside the `public` folder can be referenced from the HTML. - - Unlike "/favicon.ico" or "favico.ico", "%PUBLIC_URL%/favicon.ico" will - work correctly both with client-side routing and a non-root public URL. - Learn how to configure a non-root public URL by running `npm run build`. - --> - <title>WIMMA Lab Forum</title> - </head> - <body> - <div id="root"></div> - <!-- - This HTML file is a template. - If you open it directly in the browser, you will see an empty page. - - You can add webfonts, meta tags, or analytics to this file. - The build step will place the bundled scripts into the <body> tag. - - To begin the development, run `npm start`. - To create a production bundle, use `npm run build`. - --> - </body> - <script type="text/javascript"> - window.doorbellOptions = { - "id": "12727", - "appKey": "gcS5JEM6B1yEf3mRntjR2cKutewNl36UvthiBAEAv0K7o1kVkYIDi0CIVMpSIX5A", - strings: { - "feedback-button-text": "Feedback", - title: "Wimma Forum Feedback", - "feedback-textarea-placeholder": "Help Wimma Forum to improve, letting us know what you think.", - "email-input-placeholder": "Your email address"}, - email:"", - }; - (function(w, d, t) { - var hasLoaded = false; - function l() { if (hasLoaded) { return; } hasLoaded = true; window.doorbellOptions.windowLoaded = true; var g = d.createElement(t);g.id = 'doorbellScript';g.type = 'text/javascript';g.async = true;g.src = 'https://embed.doorbell.io/button/'+ - window.doorbellOptions['id']+'?t='+(new Date().getTime());(d.getElementsByTagName('head')[0]||d.getElementsByTagName('body')[0]).appendChild(g); } - if (w.attachEvent) { w.attachEvent('onload', l); } else if (w.addEventListener) { w.addEventListener('load', l, false); } else { l(); } - if (d.readyState == 'complete') { l(); } - }(window, document, 'script')); -</script> -</html> diff --git a/te-frontend/public/main.css b/te-frontend/public/main.css deleted file mode 100644 index 0d6dd8f1795efbf51a5493e5d1ec140e359f1475..0000000000000000000000000000000000000000 --- a/te-frontend/public/main.css +++ /dev/null @@ -1,3 +0,0 @@ -.logo-font{font-family:titillium web,sans-serif}html{position:relative;min-height:100vh;padding-bottom:100px}/*!* Bootstrap v4.0.0-alpha.2 (http://getbootstrap.com) -* Copyright 2011-2016 Twitter, Inc. -* Licensed under MIT (https://github.com/twbs/bootstrap/blob/master/LICENSE)*//*!normalize.css commit fe56763 | MIT License | github.com/necolas/normalize.css*/html{font-family:sans-serif;-ms-text-size-adjust:100%;-webkit-text-size-adjust:100%}body{margin:0}article,aside,details,figcaption,figure,footer,header,main,menu,nav,section,summary{display:block}audio,canvas,progress,video{display:inline-block;vertical-align:baseline}audio:not([controls]){display:none;height:0}[hidden],template{display:none}a{background-color:transparent}a:active{outline:0}a:hover{outline:0}abbr[title]{border-bottom:1px dotted}b,strong{font-weight:700}dfn{font-style:italic}h1{font-size:2em;margin:.67em 0}mark{background:#ff0;color:#000}small{font-size:80%}sub,sup{font-size:75%;line-height:0;position:relative;vertical-align:baseline}sup{top:-.5em}sub{bottom:-.25em}img{border:0}svg:not(:root){overflow:hidden}figure{margin:1em 40px}hr{box-sizing:content-box;height:0}pre{overflow:auto}code,kbd,pre,samp{font-family:monospace,monospace;font-size:1em}button,input,optgroup,select,textarea{color:inherit;font:inherit;margin:0}button{overflow:visible}button,select{text-transform:none}button,html input[type=button],input[type=reset],input[type=submit]{-webkit-appearance:button;cursor:pointer}button[disabled],html input[disabled]{cursor:default}button::-moz-focus-inner,input::-moz-focus-inner{border:0;padding:0}input{line-height:normal}input[type=checkbox],input[type=radio]{box-sizing:border-box;padding:0}input[type=number]::-webkit-inner-spin-button,input[type=number]::-webkit-outer-spin-button{height:auto}input[type=search]{-webkit-appearance:textfield}input[type=search]::-webkit-search-cancel-button,input[type=search]::-webkit-search-decoration{-webkit-appearance:none}fieldset{border:1px solid silver;margin:0 2px;padding:.35em .625em .75em}legend{border:0;padding:0}textarea{overflow:auto}optgroup{font-weight:700}table{border-collapse:collapse;border-spacing:0}td,th{padding:0}@media print{*,*::before,*::after,*::first-letter,*::first-line{text-shadow:none!important;box-shadow:none!important}a,a:visited{text-decoration:underline}abbr[title]::after{content:" (" attr(title) ")"}pre,blockquote{border:1px solid #999;page-break-inside:avoid}thead{display:table-header-group}tr,img{page-break-inside:avoid}p,h2,h3{orphans:3;widows:3}h2,h3{page-break-after:avoid}.navbar{display:none}.btn>.caret,.dropup>.btn>.caret{border-top-color:#000!important}.tag{border:1px solid #000}.table{border-collapse:collapse!important}.table td,.table th{background-color:#fff!important}.table-bordered th,.table-bordered td{border:1px solid #ddd!important}}html{box-sizing:border-box}*,*::before,*::after{box-sizing:inherit}@-ms-viewport{width:device-width;}html{font-size:16px;-ms-overflow-style:scrollbar;-webkit-tap-highlight-color:transparent}body{font-family:source sans pro,sans-serif;font-size:1rem;line-height:1.5;color:#373a3c;background-color:#fff}[tabindex="-1"]:focus{outline:none!important}h1,h2,h3,h4,h5,h6{margin-top:0;margin-bottom:.5rem}p{margin-top:0;margin-bottom:1rem}abbr[title],abbr[data-original-title]{cursor:help;border-bottom:1px dotted #818a91}address{margin-bottom:1rem;font-style:normal;line-height:inherit}ol,ul,dl{margin-top:0;margin-bottom:1rem}ol ol,ul ul,ol ul,ul ol{margin-bottom:0}dt{font-weight:700}dd{margin-bottom:.5rem;margin-left:0}blockquote{margin:0 0 1rem}a{color:#5ED9C3;text-decoration:none}a:focus,a:hover{color:#5ed9c29a;text-decoration:underline}a:focus{outline:thin dotted;outline:5px auto -webkit-focus-ring-color;outline-offset:-2px}a:not([href]){color:inherit;text-decoration:none}a:not([href]):focus,a:not([href]):hover{color:inherit;text-decoration:none}a:not([href]):focus{outline:none}pre{margin-top:0;margin-bottom:1rem}figure{margin:0 0 1rem}img{vertical-align:middle}[role=button]{cursor:pointer}a,area,button,[role=button],input,label,select,summary,textarea{touch-action:manipulation}table{background-color:transparent}caption{padding-top:.75rem;padding-bottom:.75rem;color:#818a91;text-align:left;caption-side:bottom}th{text-align:left}label{display:inline-block;margin-bottom:.5rem}button:focus{outline:1px dotted;outline:5px auto -webkit-focus-ring-color}input,button,select,textarea{margin:0;line-height:inherit;border-radius:0}textarea{resize:vertical}fieldset{min-width:0;padding:0;margin:0;border:0}legend{display:block;width:100%;padding:0;margin-bottom:.5rem;font-size:1.5rem;line-height:inherit}input[type=search]{-webkit-appearance:none}output{display:inline-block}[hidden]{display:none!important}h1,h2,h3,h4,h5,h6,.h1,.h2,.h3,.h4,.h5,.h6{margin-bottom:.5rem;font-family:inherit;font-weight:500;line-height:1.1;color:inherit}h1,.h1{font-size:2.5rem}h2,.h2{font-size:2rem}h3,.h3{font-size:1.75rem}h4,.h4{font-size:1.5rem}h5,.h5{font-size:1.25rem}h6,.h6{font-size:1rem}.lead{font-size:1.25rem;font-weight:300}.display-1{font-size:6rem;font-weight:300}.display-2{font-size:5.5rem;font-weight:300}.display-3{font-size:4.5rem;font-weight:300}.display-4{font-size:3.5rem;font-weight:300}hr{margin-top:1rem;margin-bottom:1rem;border:0;border-top:1px solid rgba(0,0,0,.1)}small,.small{font-size:80%;font-weight:400}mark,.mark{padding:.2em;background-color:#fcf8e3}.list-unstyled{padding-left:0;list-style:none}.list-inline{padding-left:0;list-style:none}.list-inline-item{display:inline-block}.list-inline-item:not(:last-child){margin-right:5px}.initialism{font-size:90%;text-transform:uppercase}.blockquote{padding:.5rem 1rem;margin-bottom:1rem;font-size:1.25rem;border-left:.25rem solid #eceeef}.blockquote-footer{display:block;font-size:80%;color:#818a91}.blockquote-footer::before{content:"\2014 \00A0"}.blockquote-reverse{padding-right:1rem;padding-left:0;text-align:right;border-right:.25rem solid #eceeef;border-left:0}.blockquote-reverse .blockquote-footer::before{content:""}.blockquote-reverse .blockquote-footer::after{content:"\00A0 \2014"}.img-fluid,.carousel-inner>.carousel-item>img,.carousel-inner>.carousel-item>a>img{display:block;max-width:100%;height:auto}.img-rounded{border-radius:.3rem}.img-thumbnail{padding:.25rem;background-color:#fff;border:1px solid #ddd;border-radius:.25rem;transition:all .2s ease-in-out;display:inline-block;max-width:100%;height:auto}.img-circle{border-radius:50%}.figure{display:inline-block}.figure-img{margin-bottom:.5rem;line-height:1}.figure-caption{font-size:90%;color:#818a91}code,kbd,pre,samp{font-family:Menlo,Monaco,Consolas,liberation mono,courier new,monospace}code{padding:.2rem .4rem;font-size:90%;color:#bd4147;background-color:#f7f7f9;border-radius:.25rem}kbd{padding:.2rem .4rem;font-size:90%;color:#fff;background-color:#333;border-radius:.2rem}kbd kbd{padding:0;font-size:100%;font-weight:700}pre{display:block;margin-top:0;margin-bottom:1rem;font-size:90%;color:#373a3c}pre code{padding:0;font-size:inherit;color:inherit;background-color:transparent;border-radius:0}.pre-scrollable{max-height:340px;overflow-y:scroll}.container{margin-left:auto;margin-right:auto;padding-left:15px;padding-right:15px}@media(min-width:544px){.container{max-width:576px}}@media(min-width:768px){.container{max-width:720px}}@media(min-width:992px){.container{max-width:940px}}@media(min-width:1200px){.container{max-width:1140px}}.container-fluid{margin-left:auto;margin-right:auto;padding-left:15px;padding-right:15px}.row{display:flex;flex-wrap:wrap;margin-left:-15px;margin-right:-15px}.col-xs{position:relative;flex-basis:0;flex-grow:1;max-width:100%;min-height:1px;padding-right:15px;padding-left:15px}.col-xs-1{position:relative;min-height:1px;padding-right:15px;padding-left:15px;flex:0 0 8.33333%;max-width:8.33333%}.col-xs-2{position:relative;min-height:1px;padding-right:15px;padding-left:15px;flex:0 0 16.66667%;max-width:16.66667%}.col-xs-3{position:relative;min-height:1px;padding-right:15px;padding-left:15px;flex:0 0 25%;max-width:25%}.col-xs-4{position:relative;min-height:1px;padding-right:15px;padding-left:15px;flex:0 0 33.33333%;max-width:33.33333%}.col-xs-5{position:relative;min-height:1px;padding-right:15px;padding-left:15px;flex:0 0 41.66667%;max-width:41.66667%}.col-xs-6{position:relative;min-height:1px;padding-right:15px;padding-left:15px;flex:0 0 50%;max-width:50%}.col-xs-7{position:relative;min-height:1px;padding-right:15px;padding-left:15px;flex:0 0 58.33333%;max-width:58.33333%}.col-xs-8{position:relative;min-height:1px;padding-right:15px;padding-left:15px;flex:0 0 66.66667%;max-width:66.66667%}.col-xs-9{position:relative;min-height:1px;padding-right:15px;padding-left:15px;flex:0 0 75%;max-width:75%}.col-xs-10{position:relative;min-height:1px;padding-right:15px;padding-left:15px;flex:0 0 83.33333%;max-width:83.33333%}.col-xs-11{position:relative;min-height:1px;padding-right:15px;padding-left:15px;flex:0 0 91.66667%;max-width:91.66667%}.col-xs-12{position:relative;min-height:1px;padding-right:15px;padding-left:15px;flex:0 0 100%;max-width:100%}.pull-xs-0{right:auto}.pull-xs-1{right:8.33333%}.pull-xs-2{right:16.66667%}.pull-xs-3{right:25%}.pull-xs-4{right:33.33333%}.pull-xs-5{right:41.66667%}.pull-xs-6{right:50%}.pull-xs-7{right:58.33333%}.pull-xs-8{right:66.66667%}.pull-xs-9{right:75%}.pull-xs-10{right:83.33333%}.pull-xs-11{right:91.66667%}.pull-xs-12{right:100%}.push-xs-0{left:auto}.push-xs-1{left:8.33333%}.push-xs-2{left:16.66667%}.push-xs-3{left:25%}.push-xs-4{left:33.33333%}.push-xs-5{left:41.66667%}.push-xs-6{left:50%}.push-xs-7{left:58.33333%}.push-xs-8{left:66.66667%}.push-xs-9{left:75%}.push-xs-10{left:83.33333%}.push-xs-11{left:91.66667%}.push-xs-12{left:100%}.offset-xs-1{margin-left:8.33333%}.offset-xs-2{margin-left:16.66667%}.offset-xs-3{margin-left:25%}.offset-xs-4{margin-left:33.33333%}.offset-xs-5{margin-left:41.66667%}.offset-xs-6{margin-left:50%}.offset-xs-7{margin-left:58.33333%}.offset-xs-8{margin-left:66.66667%}.offset-xs-9{margin-left:75%}.offset-xs-10{margin-left:83.33333%}.offset-xs-11{margin-left:91.66667%}@media(min-width:544px){.col-sm{position:relative;flex-basis:0;flex-grow:1;max-width:100%;min-height:1px;padding-right:15px;padding-left:15px}.col-sm-1{position:relative;min-height:1px;padding-right:15px;padding-left:15px;flex:0 0 8.33333%;max-width:8.33333%}.col-sm-2{position:relative;min-height:1px;padding-right:15px;padding-left:15px;flex:0 0 16.66667%;max-width:16.66667%}.col-sm-3{position:relative;min-height:1px;padding-right:15px;padding-left:15px;flex:0 0 25%;max-width:25%}.col-sm-4{position:relative;min-height:1px;padding-right:15px;padding-left:15px;flex:0 0 33.33333%;max-width:33.33333%}.col-sm-5{position:relative;min-height:1px;padding-right:15px;padding-left:15px;flex:0 0 41.66667%;max-width:41.66667%}.col-sm-6{position:relative;min-height:1px;padding-right:15px;padding-left:15px;flex:0 0 50%;max-width:50%}.col-sm-7{position:relative;min-height:1px;padding-right:15px;padding-left:15px;flex:0 0 58.33333%;max-width:58.33333%}.col-sm-8{position:relative;min-height:1px;padding-right:15px;padding-left:15px;flex:0 0 66.66667%;max-width:66.66667%}.col-sm-9{position:relative;min-height:1px;padding-right:15px;padding-left:15px;flex:0 0 75%;max-width:75%}.col-sm-10{position:relative;min-height:1px;padding-right:15px;padding-left:15px;flex:0 0 83.33333%;max-width:83.33333%}.col-sm-11{position:relative;min-height:1px;padding-right:15px;padding-left:15px;flex:0 0 91.66667%;max-width:91.66667%}.col-sm-12{position:relative;min-height:1px;padding-right:15px;padding-left:15px;flex:0 0 100%;max-width:100%}.pull-sm-0{right:auto}.pull-sm-1{right:8.33333%}.pull-sm-2{right:16.66667%}.pull-sm-3{right:25%}.pull-sm-4{right:33.33333%}.pull-sm-5{right:41.66667%}.pull-sm-6{right:50%}.pull-sm-7{right:58.33333%}.pull-sm-8{right:66.66667%}.pull-sm-9{right:75%}.pull-sm-10{right:83.33333%}.pull-sm-11{right:91.66667%}.pull-sm-12{right:100%}.push-sm-0{left:auto}.push-sm-1{left:8.33333%}.push-sm-2{left:16.66667%}.push-sm-3{left:25%}.push-sm-4{left:33.33333%}.push-sm-5{left:41.66667%}.push-sm-6{left:50%}.push-sm-7{left:58.33333%}.push-sm-8{left:66.66667%}.push-sm-9{left:75%}.push-sm-10{left:83.33333%}.push-sm-11{left:91.66667%}.push-sm-12{left:100%}.offset-sm-0{margin-left:0%}.offset-sm-1{margin-left:8.33333%}.offset-sm-2{margin-left:16.66667%}.offset-sm-3{margin-left:25%}.offset-sm-4{margin-left:33.33333%}.offset-sm-5{margin-left:41.66667%}.offset-sm-6{margin-left:50%}.offset-sm-7{margin-left:58.33333%}.offset-sm-8{margin-left:66.66667%}.offset-sm-9{margin-left:75%}.offset-sm-10{margin-left:83.33333%}.offset-sm-11{margin-left:91.66667%}}@media(min-width:768px){.col-md{position:relative;flex-basis:0;flex-grow:1;max-width:100%;min-height:1px;padding-right:15px;padding-left:15px}.col-md-1{position:relative;min-height:1px;padding-right:15px;padding-left:15px;flex:0 0 8.33333%;max-width:8.33333%}.col-md-2{position:relative;min-height:1px;padding-right:15px;padding-left:15px;flex:0 0 16.66667%;max-width:16.66667%}.col-md-3{position:relative;min-height:1px;padding-right:15px;padding-left:15px;flex:0 0 25%;max-width:25%}.col-md-4{position:relative;min-height:1px;padding-right:15px;padding-left:15px;flex:0 0 33.33333%;max-width:33.33333%}.col-md-5{position:relative;min-height:1px;padding-right:15px;padding-left:15px;flex:0 0 41.66667%;max-width:41.66667%}.col-md-6{position:relative;min-height:1px;padding-right:15px;padding-left:15px;flex:0 0 50%;max-width:50%}.col-md-7{position:relative;min-height:1px;padding-right:15px;padding-left:15px;flex:0 0 58.33333%;max-width:58.33333%}.col-md-8{position:relative;min-height:1px;padding-right:15px;padding-left:15px;flex:0 0 66.66667%;max-width:66.66667%}.col-md-9{position:relative;min-height:1px;padding-right:15px;padding-left:15px;flex:0 0 75%;max-width:75%}.col-md-10{position:relative;min-height:1px;padding-right:15px;padding-left:15px;flex:0 0 83.33333%;max-width:83.33333%}.col-md-11{position:relative;min-height:1px;padding-right:15px;padding-left:15px;flex:0 0 91.66667%;max-width:91.66667%}.col-md-12{position:relative;min-height:1px;padding-right:15px;padding-left:15px;flex:0 0 100%;max-width:100%}.pull-md-0{right:auto}.pull-md-1{right:8.33333%}.pull-md-2{right:16.66667%}.pull-md-3{right:25%}.pull-md-4{right:33.33333%}.pull-md-5{right:41.66667%}.pull-md-6{right:50%}.pull-md-7{right:58.33333%}.pull-md-8{right:66.66667%}.pull-md-9{right:75%}.pull-md-10{right:83.33333%}.pull-md-11{right:91.66667%}.pull-md-12{right:100%}.push-md-0{left:auto}.push-md-1{left:8.33333%}.push-md-2{left:16.66667%}.push-md-3{left:25%}.push-md-4{left:33.33333%}.push-md-5{left:41.66667%}.push-md-6{left:50%}.push-md-7{left:58.33333%}.push-md-8{left:66.66667%}.push-md-9{left:75%}.push-md-10{left:83.33333%}.push-md-11{left:91.66667%}.push-md-12{left:100%}.offset-md-0{margin-left:0%}.offset-md-1{margin-left:8.33333%}.offset-md-2{margin-left:16.66667%}.offset-md-3{margin-left:25%}.offset-md-4{margin-left:33.33333%}.offset-md-5{margin-left:41.66667%}.offset-md-6{margin-left:50%}.offset-md-7{margin-left:58.33333%}.offset-md-8{margin-left:66.66667%}.offset-md-9{margin-left:75%}.offset-md-10{margin-left:83.33333%}.offset-md-11{margin-left:91.66667%}}@media(min-width:992px){.col-lg{position:relative;flex-basis:0;flex-grow:1;max-width:100%;min-height:1px;padding-right:15px;padding-left:15px}.col-lg-1{position:relative;min-height:1px;padding-right:15px;padding-left:15px;flex:0 0 8.33333%;max-width:8.33333%}.col-lg-2{position:relative;min-height:1px;padding-right:15px;padding-left:15px;flex:0 0 16.66667%;max-width:16.66667%}.col-lg-3{position:relative;min-height:1px;padding-right:15px;padding-left:15px;flex:0 0 25%;max-width:25%}.col-lg-4{position:relative;min-height:1px;padding-right:15px;padding-left:15px;flex:0 0 33.33333%;max-width:33.33333%}.col-lg-5{position:relative;min-height:1px;padding-right:15px;padding-left:15px;flex:0 0 41.66667%;max-width:41.66667%}.col-lg-6{position:relative;min-height:1px;padding-right:15px;padding-left:15px;flex:0 0 50%;max-width:50%}.col-lg-7{position:relative;min-height:1px;padding-right:15px;padding-left:15px;flex:0 0 58.33333%;max-width:58.33333%}.col-lg-8{position:relative;min-height:1px;padding-right:15px;padding-left:15px;flex:0 0 66.66667%;max-width:66.66667%}.col-lg-9{position:relative;min-height:1px;padding-right:15px;padding-left:15px;flex:0 0 75%;max-width:75%}.col-lg-10{position:relative;min-height:1px;padding-right:15px;padding-left:15px;flex:0 0 83.33333%;max-width:83.33333%}.col-lg-11{position:relative;min-height:1px;padding-right:15px;padding-left:15px;flex:0 0 91.66667%;max-width:91.66667%}.col-lg-12{position:relative;min-height:1px;padding-right:15px;padding-left:15px;flex:0 0 100%;max-width:100%}.pull-lg-0{right:auto}.pull-lg-1{right:8.33333%}.pull-lg-2{right:16.66667%}.pull-lg-3{right:25%}.pull-lg-4{right:33.33333%}.pull-lg-5{right:41.66667%}.pull-lg-6{right:50%}.pull-lg-7{right:58.33333%}.pull-lg-8{right:66.66667%}.pull-lg-9{right:75%}.pull-lg-10{right:83.33333%}.pull-lg-11{right:91.66667%}.pull-lg-12{right:100%}.push-lg-0{left:auto}.push-lg-1{left:8.33333%}.push-lg-2{left:16.66667%}.push-lg-3{left:25%}.push-lg-4{left:33.33333%}.push-lg-5{left:41.66667%}.push-lg-6{left:50%}.push-lg-7{left:58.33333%}.push-lg-8{left:66.66667%}.push-lg-9{left:75%}.push-lg-10{left:83.33333%}.push-lg-11{left:91.66667%}.push-lg-12{left:100%}.offset-lg-0{margin-left:0%}.offset-lg-1{margin-left:8.33333%}.offset-lg-2{margin-left:16.66667%}.offset-lg-3{margin-left:25%}.offset-lg-4{margin-left:33.33333%}.offset-lg-5{margin-left:41.66667%}.offset-lg-6{margin-left:50%}.offset-lg-7{margin-left:58.33333%}.offset-lg-8{margin-left:66.66667%}.offset-lg-9{margin-left:75%}.offset-lg-10{margin-left:83.33333%}.offset-lg-11{margin-left:91.66667%}}@media(min-width:1200px){.col-xl{position:relative;flex-basis:0;flex-grow:1;max-width:100%;min-height:1px;padding-right:15px;padding-left:15px}.col-xl-1{position:relative;min-height:1px;padding-right:15px;padding-left:15px;flex:0 0 8.33333%;max-width:8.33333%}.col-xl-2{position:relative;min-height:1px;padding-right:15px;padding-left:15px;flex:0 0 16.66667%;max-width:16.66667%}.col-xl-3{position:relative;min-height:1px;padding-right:15px;padding-left:15px;flex:0 0 25%;max-width:25%}.col-xl-4{position:relative;min-height:1px;padding-right:15px;padding-left:15px;flex:0 0 33.33333%;max-width:33.33333%}.col-xl-5{position:relative;min-height:1px;padding-right:15px;padding-left:15px;flex:0 0 41.66667%;max-width:41.66667%}.col-xl-6{position:relative;min-height:1px;padding-right:15px;padding-left:15px;flex:0 0 50%;max-width:50%}.col-xl-7{position:relative;min-height:1px;padding-right:15px;padding-left:15px;flex:0 0 58.33333%;max-width:58.33333%}.col-xl-8{position:relative;min-height:1px;padding-right:15px;padding-left:15px;flex:0 0 66.66667%;max-width:66.66667%}.col-xl-9{position:relative;min-height:1px;padding-right:15px;padding-left:15px;flex:0 0 75%;max-width:75%}.col-xl-10{position:relative;min-height:1px;padding-right:15px;padding-left:15px;flex:0 0 83.33333%;max-width:83.33333%}.col-xl-11{position:relative;min-height:1px;padding-right:15px;padding-left:15px;flex:0 0 91.66667%;max-width:91.66667%}.col-xl-12{position:relative;min-height:1px;padding-right:15px;padding-left:15px;flex:0 0 100%;max-width:100%}.pull-xl-0{right:auto}.pull-xl-1{right:8.33333%}.pull-xl-2{right:16.66667%}.pull-xl-3{right:25%}.pull-xl-4{right:33.33333%}.pull-xl-5{right:41.66667%}.pull-xl-6{right:50%}.pull-xl-7{right:58.33333%}.pull-xl-8{right:66.66667%}.pull-xl-9{right:75%}.pull-xl-10{right:83.33333%}.pull-xl-11{right:91.66667%}.pull-xl-12{right:100%}.push-xl-0{left:auto}.push-xl-1{left:8.33333%}.push-xl-2{left:16.66667%}.push-xl-3{left:25%}.push-xl-4{left:33.33333%}.push-xl-5{left:41.66667%}.push-xl-6{left:50%}.push-xl-7{left:58.33333%}.push-xl-8{left:66.66667%}.push-xl-9{left:75%}.push-xl-10{left:83.33333%}.push-xl-11{left:91.66667%}.push-xl-12{left:100%}.offset-xl-0{margin-left:0%}.offset-xl-1{margin-left:8.33333%}.offset-xl-2{margin-left:16.66667%}.offset-xl-3{margin-left:25%}.offset-xl-4{margin-left:33.33333%}.offset-xl-5{margin-left:41.66667%}.offset-xl-6{margin-left:50%}.offset-xl-7{margin-left:58.33333%}.offset-xl-8{margin-left:66.66667%}.offset-xl-9{margin-left:75%}.offset-xl-10{margin-left:83.33333%}.offset-xl-11{margin-left:91.66667%}}.table{width:100%;max-width:100%;margin-bottom:1rem}.table th,.table td{padding:.75rem;vertical-align:top;border-top:1px solid #eceeef}.table thead th{vertical-align:bottom;border-bottom:2px solid #eceeef}.table tbody+tbody{border-top:2px solid #eceeef}.table .table{background-color:#fff}.table-sm th,.table-sm td{padding:.3rem}.table-bordered{border:1px solid #eceeef}.table-bordered th,.table-bordered td{border:1px solid #eceeef}.table-bordered thead th,.table-bordered thead td{border-bottom-width:2px}.table-striped tbody tr:nth-of-type(odd){background-color:#f9f9f9}.table-hover tbody tr:hover{background-color:#f5f5f5}.table-active,.table-active>th,.table-active>td{background-color:#f5f5f5}.table-hover .table-active:hover{background-color:#e8e8e8}.table-hover .table-active:hover>td,.table-hover .table-active:hover>th{background-color:#e8e8e8}.table-success,.table-success>th,.table-success>td{background-color:#dff0d8}.table-hover .table-success:hover{background-color:#d0e9c6}.table-hover .table-success:hover>td,.table-hover .table-success:hover>th{background-color:#d0e9c6}.table-info,.table-info>th,.table-info>td{background-color:#d9edf7}.table-hover .table-info:hover{background-color:#c4e3f3}.table-hover .table-info:hover>td,.table-hover .table-info:hover>th{background-color:#c4e3f3}.table-warning,.table-warning>th,.table-warning>td{background-color:#fcf8e3}.table-hover .table-warning:hover{background-color:#faf2cc}.table-hover .table-warning:hover>td,.table-hover .table-warning:hover>th{background-color:#faf2cc}.table-danger,.table-danger>th,.table-danger>td{background-color:#f2dede}.table-hover .table-danger:hover{background-color:#ebcccc}.table-hover .table-danger:hover>td,.table-hover .table-danger:hover>th{background-color:#ebcccc}.table-responsive{display:block;width:100%;min-height:.01%;overflow-x:auto}.thead-inverse th{color:#fff;background-color:#373a3c}.thead-default th{color:#55595c;background-color:#eceeef}.table-inverse{color:#eceeef;background-color:#373a3c}.table-inverse.table-bordered{border:0}.table-inverse th,.table-inverse td,.table-inverse thead th{border-color:#55595c}.table-reflow thead{float:left}.table-reflow tbody{display:block;white-space:nowrap}.table-reflow th,.table-reflow td{border-top:1px solid #eceeef;border-left:1px solid #eceeef}.table-reflow th:last-child,.table-reflow td:last-child{border-right:1px solid #eceeef}.table-reflow thead:last-child tr:last-child th,.table-reflow thead:last-child tr:last-child td,.table-reflow tbody:last-child tr:last-child th,.table-reflow tbody:last-child tr:last-child td,.table-reflow tfoot:last-child tr:last-child th,.table-reflow tfoot:last-child tr:last-child td{border-bottom:1px solid #eceeef}.table-reflow tr{float:left}.table-reflow tr th,.table-reflow tr td{display:block!important;border:1px solid #eceeef}.form-control{display:block;width:100%;padding:.5rem .75rem;font-size:1rem;line-height:1.25;color:#55595c;background-color:#fff;background-image:none;background-clip:padding-box;border:1px solid rgba(0,0,0,.15);border-radius:.25rem}.form-control::-ms-expand{background-color:transparent;border:0}.form-control:focus{border-color:#66afe9;outline:none}.form-control::placeholder{color:#999;opacity:1}.form-control:disabled,.form-control[readonly]{background-color:#eceeef;opacity:1}.form-control:disabled{cursor:not-allowed}select.form-control:not([size]):not([multiple]){height:2.5rem}.form-control-file,.form-control-range{display:block}.form-control-label{padding:.5rem .75rem;margin-bottom:0}.form-control-legend{padding:.5rem .75rem;margin-bottom:0;font-size:1rem}_::-webkit-full-page-media.form-control,input[type=date].form-control,input[type=time].form-control,input[type=datetime-local].form-control,input[type=month].form-control{line-height:2.5rem}_::-webkit-full-page-media.input-sm,.input-group-sm _::-webkit-full-page-media.form-control,input[type=date].input-sm,.input-group-sm input[type=date].form-control,input[type=time].input-sm,.input-group-sm input[type=time].form-control,input[type=datetime-local].input-sm,.input-group-sm input[type=datetime-local].form-control,input[type=month].input-sm,.input-group-sm input[type=month].form-control{line-height:1.8125rem}_::-webkit-full-page-media.input-lg,.input-group-lg _::-webkit-full-page-media.form-control,input[type=date].input-lg,.input-group-lg input[type=date].form-control,input[type=time].input-lg,.input-group-lg input[type=time].form-control,input[type=datetime-local].input-lg,.input-group-lg input[type=datetime-local].form-control,input[type=month].input-lg,.input-group-lg input[type=month].form-control{line-height:3.16667rem}.form-control-static{min-height:2.5rem;padding-top:.5rem;padding-bottom:.5rem;margin-bottom:0}.form-control-static.form-control-sm,.input-group-sm>.form-control-static.form-control,.input-group-sm>.form-control-static.input-group-addon,.input-group-sm>.input-group-btn>.form-control-static.btn,.form-control-static.form-control-lg,.input-group-lg>.form-control-static.form-control,.input-group-lg>.form-control-static.input-group-addon,.input-group-lg>.input-group-btn>.form-control-static.btn{padding-right:0;padding-left:0}.form-control-sm,.input-group-sm>.form-control,.input-group-sm>.input-group-addon,.input-group-sm>.input-group-btn>.btn{padding:.25rem .5rem;font-size:.875rem;border-radius:.2rem}.form-control-lg,.input-group-lg>.form-control,.input-group-lg>.input-group-addon,.input-group-lg>.input-group-btn>.btn{padding:.75rem 1.5rem;font-size:1.25rem;border-radius:.3rem}.form-group{margin-bottom:1rem}.radio,.checkbox{position:relative;display:block;margin-bottom:.75rem}.radio label,.checkbox label{padding-left:1.25rem;margin-bottom:0;cursor:pointer}.radio label input:only-child,.checkbox label input:only-child{position:static}.radio input[type=radio],.radio-inline input[type=radio],.checkbox input[type=checkbox],.checkbox-inline input[type=checkbox]{position:absolute;margin-top:.25rem;margin-left:-1.25rem}.radio+.radio,.checkbox+.checkbox{margin-top:-.25rem}.radio-inline,.checkbox-inline{position:relative;display:inline-block;padding-left:1.25rem;margin-bottom:0;vertical-align:middle;cursor:pointer}.radio-inline+.radio-inline,.checkbox-inline+.checkbox-inline{margin-top:0;margin-left:.75rem}input[type=radio]:disabled,input[type=radio].disabled,input[type=checkbox]:disabled,input[type=checkbox].disabled{cursor:not-allowed}.radio-inline.disabled,.checkbox-inline.disabled{cursor:not-allowed}.radio.disabled label,.checkbox.disabled label{cursor:not-allowed}.form-control-success,.form-control-warning,.form-control-danger{padding-right:2.25rem;background-repeat:no-repeat;background-position:center right .625rem;background-size:1.25rem 1.25rem}.has-success .text-help,.has-success .form-control-label,.has-success .radio,.has-success .checkbox,.has-success .radio-inline,.has-success .checkbox-inline,.has-success.radio label,.has-success.checkbox label,.has-success.radio-inline label,.has-success.checkbox-inline label,.has-success .custom-control{color:#5ED9C3}.has-success .form-control{border-color:#5ED9C3}.has-success .input-group-addon{color:#5ED9C3;border-color:#5ED9C3;background-color:#eaf6ea}.has-success .form-control-feedback{color:#5ED9C3}.has-success .form-control-success{background-image:url(data:image/svg+xml;charset=utf8;base64,PHN2ZyB4bWxucz0naHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmcnIHZpZXdCb3g9JzAgMCA4IDgnPjxwYXRoIGZpbGw9JyM1Y2I4NWMnIGQ9J00yLjMgNi43M0wuNiA0LjUzYy0uNC0xLjA0LjQ2LTEuNCAxLjEtLjhsMS4xIDEuNCAzLjQtMy44Yy42LS42MyAxLjYtLjI3IDEuMi43bC00IDQuNmMtLjQzLjUtLjguNC0xLjEuMXonLz48L3N2Zz4=)}.has-warning .text-help,.has-warning .form-control-label,.has-warning .radio,.has-warning .checkbox,.has-warning .radio-inline,.has-warning .checkbox-inline,.has-warning.radio label,.has-warning.checkbox label,.has-warning.radio-inline label,.has-warning.checkbox-inline label,.has-warning .custom-control{color:#f0ad4e}.has-warning .form-control{border-color:#f0ad4e}.has-warning .input-group-addon{color:#f0ad4e;border-color:#f0ad4e;background-color:#fff}.has-warning .form-control-feedback{color:#f0ad4e}.has-warning .form-control-warning{background-image:url(data:image/svg+xml;charset=utf8;base64,PHN2ZyB4bWxucz0naHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmcnIHZpZXdCb3g9JzAgMCA4IDgnPjxwYXRoIGZpbGw9JyNmMGFkNGUnIGQ9J000LjQgNS4zMjRoLS44di0yLjQ2aC44em0wIDEuNDJoLS44VjUuODloLjh6TTMuNzYuNjNMLjA0IDcuMDc1Yy0uMTE1LjIuMDE2LjQyNS4yNi40MjZoNy4zOTdjLjI0MiAwIC4zNzItLjIyNi4yNTgtLjQyNkM2LjcyNiA0LjkyNCA1LjQ3IDIuNzkgNC4yNTMuNjNjLS4xMTMtLjE3NC0uMzktLjE3NC0uNDk0IDB6Jy8+PC9zdmc+)}.has-danger .text-help,.has-danger .form-control-label,.has-danger .radio,.has-danger .checkbox,.has-danger .radio-inline,.has-danger .checkbox-inline,.has-danger.radio label,.has-danger.checkbox label,.has-danger.radio-inline label,.has-danger.checkbox-inline label,.has-danger .custom-control{color:#b85c5c}.has-danger .form-control{border-color:#b85c5c}.has-danger .input-group-addon{color:#b85c5c;border-color:#b85c5c;background-color:#f6eaea}.has-danger .form-control-feedback{color:#b85c5c}.has-danger .form-control-danger{background-image:url(data:image/svg+xml;charset=utf8;base64,PHN2ZyB4bWxucz0naHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmcnIGZpbGw9JyNkOTUzNGYnIHZpZXdCb3g9Jy0yIC0yIDcgNyc+PHBhdGggc3Ryb2tlPScjZDk1MzRmJyBkPSdNMCAwbDMgM20wLTNMMCAzJy8+PGNpcmNsZSByPScuNScvPjxjaXJjbGUgY3g9JzMnIHI9Jy41Jy8+PGNpcmNsZSBjeT0nMycgcj0nLjUnLz48Y2lyY2xlIGN4PSczJyBjeT0nMycgcj0nLjUnLz48L3N2Zz4=)}@media(min-width:544px){.form-inline .form-group{display:inline-block;margin-bottom:0;vertical-align:middle}.form-inline .form-control{display:inline-block;width:auto;vertical-align:middle}.form-inline .form-control-static{display:inline-block}.form-inline .input-group{display:inline-table;vertical-align:middle}.form-inline .input-group .input-group-addon,.form-inline .input-group .input-group-btn,.form-inline .input-group .form-control{width:auto}.form-inline .input-group>.form-control{width:100%}.form-inline .form-control-label{margin-bottom:0;vertical-align:middle}.form-inline .radio,.form-inline .checkbox{display:inline-block;margin-top:0;margin-bottom:0;vertical-align:middle}.form-inline .radio label,.form-inline .checkbox label{padding-left:0}.form-inline .radio input[type=radio],.form-inline .checkbox input[type=checkbox]{position:relative;margin-left:0}.form-inline .has-feedback .form-control-feedback{top:0}}.btn{display:inline-block;font-weight:400;line-height:1.25;text-align:center;white-space:nowrap;vertical-align:middle;cursor:pointer;user-select:none;border:1px solid transparent;padding:.5rem 1rem;font-size:1rem;border-radius:.25rem}.btn:focus,.btn.focus,.btn:active:focus,.btn:active.focus,.btn.active:focus,.btn.active.focus{outline:thin dotted;outline:5px auto -webkit-focus-ring-color;outline-offset:-2px}.btn:focus,.btn:hover{text-decoration:none}.btn.focus{text-decoration:none}.btn:active,.btn.active{background-image:none;outline:0}.btn.disabled,.btn:disabled{cursor:not-allowed;opacity:.65}a.btn.disabled,fieldset[disabled] a.btn{pointer-events:none}.btn-primary{color:#fff;background-color:#5ED9C3;border-color:#5ED9C3}.btn-primary:hover{color:#fff;background-color:#38b5b3;border-color:#419641}.btn-primary:focus,.btn-primary.focus{color:#fff;background-color:#38b5b3;border-color:#419641}.btn-primary:active,.btn-primary.active,.open>.btn-primary.dropdown-toggle{color:#fff;background-color:#38b5b3;border-color:#419641;background-image:none}.btn-primary:active:hover,.btn-primary:active:focus,.btn-primary:active.focus,.btn-primary.active:hover,.btn-primary.active:focus,.btn-primary.active.focus,.open>.btn-primary.dropdown-toggle:hover,.open>.btn-primary.dropdown-toggle:focus,.open>.btn-primary.dropdown-toggle.focus{color:#fff;background-color:#398439;border-color:#2d672d}.btn-primary.disabled:focus,.btn-primary.disabled.focus,.btn-primary:disabled:focus,.btn-primary:disabled.focus{background-color:#5ED9C3;border-color:#5ED9C3}.btn-primary.disabled:hover,.btn-primary:disabled:hover{background-color:#5ED9C3;border-color:#5ED9C3}.btn-secondary{color:#373a3c;background-color:#fff;border-color:#ccc}.btn-secondary:hover{color:#373a3c;background-color:#e6e6e6;border-color:#adadad}.btn-secondary:focus,.btn-secondary.focus{color:#373a3c;background-color:#e6e6e6;border-color:#adadad}.btn-secondary:active,.btn-secondary.active,.open>.btn-secondary.dropdown-toggle{color:#373a3c;background-color:#e6e6e6;border-color:#adadad;background-image:none}.btn-secondary:active:hover,.btn-secondary:active:focus,.btn-secondary:active.focus,.btn-secondary.active:hover,.btn-secondary.active:focus,.btn-secondary.active.focus,.open>.btn-secondary.dropdown-toggle:hover,.open>.btn-secondary.dropdown-toggle:focus,.open>.btn-secondary.dropdown-toggle.focus{color:#373a3c;background-color:#d4d4d4;border-color:#8c8c8c}.btn-secondary.disabled:focus,.btn-secondary.disabled.focus,.btn-secondary:disabled:focus,.btn-secondary:disabled.focus{background-color:#fff;border-color:#ccc}.btn-secondary.disabled:hover,.btn-secondary:disabled:hover{background-color:#fff;border-color:#ccc}.btn-info{color:#fff;background-color:#5bc0de;border-color:#5bc0de}.btn-info:hover{color:#fff;background-color:#31b0d5;border-color:#2aabd2}.btn-info:focus,.btn-info.focus{color:#fff;background-color:#31b0d5;border-color:#2aabd2}.btn-info:active,.btn-info.active,.open>.btn-info.dropdown-toggle{color:#fff;background-color:#31b0d5;border-color:#2aabd2;background-image:none}.btn-info:active:hover,.btn-info:active:focus,.btn-info:active.focus,.btn-info.active:hover,.btn-info.active:focus,.btn-info.active.focus,.open>.btn-info.dropdown-toggle:hover,.open>.btn-info.dropdown-toggle:focus,.open>.btn-info.dropdown-toggle.focus{color:#fff;background-color:#269abc;border-color:#1f7e9a}.btn-info.disabled:focus,.btn-info.disabled.focus,.btn-info:disabled:focus,.btn-info:disabled.focus{background-color:#5bc0de;border-color:#5bc0de}.btn-info.disabled:hover,.btn-info:disabled:hover{background-color:#5bc0de;border-color:#5bc0de}.btn-success{color:#fff;background-color:#5ED9C3;border-color:#5ED9C3}.btn-success:hover{color:#fff;background-color:#38b5b3;border-color:#419641}.btn-success:focus,.btn-success.focus{color:#fff;background-color:#38b5b3;border-color:#419641}.btn-success:active,.btn-success.active,.open>.btn-success.dropdown-toggle{color:#fff;background-color:#38b5b3;border-color:#419641;background-image:none}.btn-success:active:hover,.btn-success:active:focus,.btn-success:active.focus,.btn-success.active:hover,.btn-success.active:focus,.btn-success.active.focus,.open>.btn-success.dropdown-toggle:hover,.open>.btn-success.dropdown-toggle:focus,.open>.btn-success.dropdown-toggle.focus{color:#fff;background-color:#398439;border-color:#2d672d}.btn-success.disabled:focus,.btn-success.disabled.focus,.btn-success:disabled:focus,.btn-success:disabled.focus{background-color:#5ED9C3;border-color:#5ED9C3}.btn-success.disabled:hover,.btn-success:disabled:hover{background-color:#5ED9C3;border-color:#5ED9C3}.btn-warning{color:#fff;background-color:#f0ad4e;border-color:#f0ad4e}.btn-warning:hover{color:#fff;background-color:#ec971f;border-color:#eb9316}.btn-warning:focus,.btn-warning.focus{color:#fff;background-color:#ec971f;border-color:#eb9316}.btn-warning:active,.btn-warning.active,.open>.btn-warning.dropdown-toggle{color:#fff;background-color:#ec971f;border-color:#eb9316;background-image:none}.btn-warning:active:hover,.btn-warning:active:focus,.btn-warning:active.focus,.btn-warning.active:hover,.btn-warning.active:focus,.btn-warning.active.focus,.open>.btn-warning.dropdown-toggle:hover,.open>.btn-warning.dropdown-toggle:focus,.open>.btn-warning.dropdown-toggle.focus{color:#fff;background-color:#d58512;border-color:#b06d0f}.btn-warning.disabled:focus,.btn-warning.disabled.focus,.btn-warning:disabled:focus,.btn-warning:disabled.focus{background-color:#f0ad4e;border-color:#f0ad4e}.btn-warning.disabled:hover,.btn-warning:disabled:hover{background-color:#f0ad4e;border-color:#f0ad4e}.btn-danger{color:#fff;background-color:#b85c5c;border-color:#b85c5c}.btn-danger:hover{color:#fff;background-color:#9d4444;border-color:#964141}.btn-danger:focus,.btn-danger.focus{color:#fff;background-color:#9d4444;border-color:#964141}.btn-danger:active,.btn-danger.active,.open>.btn-danger.dropdown-toggle{color:#fff;background-color:#9d4444;border-color:#964141;background-image:none}.btn-danger:active:hover,.btn-danger:active:focus,.btn-danger:active.focus,.btn-danger.active:hover,.btn-danger.active:focus,.btn-danger.active.focus,.open>.btn-danger.dropdown-toggle:hover,.open>.btn-danger.dropdown-toggle:focus,.open>.btn-danger.dropdown-toggle.focus{color:#fff;background-color:#843939;border-color:#672d2d}.btn-danger.disabled:focus,.btn-danger.disabled.focus,.btn-danger:disabled:focus,.btn-danger:disabled.focus{background-color:#b85c5c;border-color:#b85c5c}.btn-danger.disabled:hover,.btn-danger:disabled:hover{background-color:#b85c5c;border-color:#b85c5c}.btn-outline-primary{color:#5ED9C3;background-image:none;background-color:transparent;border-color:#5ED9C3}.btn-outline-primary:hover{color:#fff;background-color:#5ED9C3;border-color:#5ED9C3}.btn-outline-primary:focus,.btn-outline-primary.focus{color:#fff;background-color:#5ED9C3;border-color:#5ED9C3}.btn-outline-primary:active,.btn-outline-primary.active,.open>.btn-outline-primary.dropdown-toggle{color:#fff;background-color:#5ED9C3;border-color:#5ED9C3}.btn-outline-primary:active:hover,.btn-outline-primary:active:focus,.btn-outline-primary:active.focus,.btn-outline-primary.active:hover,.btn-outline-primary.active:focus,.btn-outline-primary.active.focus,.open>.btn-outline-primary.dropdown-toggle:hover,.open>.btn-outline-primary.dropdown-toggle:focus,.open>.btn-outline-primary.dropdown-toggle.focus{color:#fff;background-color:#398439;border-color:#2d672d}.btn-outline-primary.disabled:focus,.btn-outline-primary.disabled.focus,.btn-outline-primary:disabled:focus,.btn-outline-primary:disabled.focus{border-color:#a3d7a3}.btn-outline-primary.disabled:hover,.btn-outline-primary:disabled:hover{border-color:#a3d7a3}.btn-outline-secondary{color:#ccc;background-image:none;background-color:transparent;border-color:#ccc}.btn-outline-secondary:hover{color:#fff;background-color:#ccc;border-color:#ccc}.btn-outline-secondary:focus,.btn-outline-secondary.focus{color:#fff;background-color:#ccc;border-color:#ccc}.btn-outline-secondary:active,.btn-outline-secondary.active,.open>.btn-outline-secondary.dropdown-toggle{color:#fff;background-color:#ccc;border-color:#ccc}.btn-outline-secondary:active:hover,.btn-outline-secondary:active:focus,.btn-outline-secondary:active.focus,.btn-outline-secondary.active:hover,.btn-outline-secondary.active:focus,.btn-outline-secondary.active.focus,.open>.btn-outline-secondary.dropdown-toggle:hover,.open>.btn-outline-secondary.dropdown-toggle:focus,.open>.btn-outline-secondary.dropdown-toggle.focus{color:#fff;background-color:#a1a1a1;border-color:#8c8c8c}.btn-outline-secondary.disabled:focus,.btn-outline-secondary.disabled.focus,.btn-outline-secondary:disabled:focus,.btn-outline-secondary:disabled.focus{border-color:#fff}.btn-outline-secondary.disabled:hover,.btn-outline-secondary:disabled:hover{border-color:#fff}.btn-outline-info{color:#5bc0de;background-image:none;background-color:transparent;border-color:#5bc0de}.btn-outline-info:hover{color:#fff;background-color:#5bc0de;border-color:#5bc0de}.btn-outline-info:focus,.btn-outline-info.focus{color:#fff;background-color:#5bc0de;border-color:#5bc0de}.btn-outline-info:active,.btn-outline-info.active,.open>.btn-outline-info.dropdown-toggle{color:#fff;background-color:#5bc0de;border-color:#5bc0de}.btn-outline-info:active:hover,.btn-outline-info:active:focus,.btn-outline-info:active.focus,.btn-outline-info.active:hover,.btn-outline-info.active:focus,.btn-outline-info.active.focus,.open>.btn-outline-info.dropdown-toggle:hover,.open>.btn-outline-info.dropdown-toggle:focus,.open>.btn-outline-info.dropdown-toggle.focus{color:#fff;background-color:#269abc;border-color:#1f7e9a}.btn-outline-info.disabled:focus,.btn-outline-info.disabled.focus,.btn-outline-info:disabled:focus,.btn-outline-info:disabled.focus{border-color:#b0e1ef}.btn-outline-info.disabled:hover,.btn-outline-info:disabled:hover{border-color:#b0e1ef}.btn-outline-success{color:#5ED9C3;background-image:none;background-color:transparent;border-color:#5ED9C3}.btn-outline-success:hover{color:#fff;background-color:#5ED9C3;border-color:#5ED9C3}.btn-outline-success:focus,.btn-outline-success.focus{color:#fff;background-color:#5ED9C3;border-color:#5ED9C3}.btn-outline-success:active,.btn-outline-success.active,.open>.btn-outline-success.dropdown-toggle{color:#fff;background-color:#5ED9C3;border-color:#5ED9C3}.btn-outline-success:active:hover,.btn-outline-success:active:focus,.btn-outline-success:active.focus,.btn-outline-success.active:hover,.btn-outline-success.active:focus,.btn-outline-success.active.focus,.open>.btn-outline-success.dropdown-toggle:hover,.open>.btn-outline-success.dropdown-toggle:focus,.open>.btn-outline-success.dropdown-toggle.focus{color:#fff;background-color:#398439;border-color:#2d672d}.btn-outline-success.disabled:focus,.btn-outline-success.disabled.focus,.btn-outline-success:disabled:focus,.btn-outline-success:disabled.focus{border-color:#a3d7a3}.btn-outline-success.disabled:hover,.btn-outline-success:disabled:hover{border-color:#a3d7a3}.btn-outline-warning{color:#f0ad4e;background-image:none;background-color:transparent;border-color:#f0ad4e}.btn-outline-warning:hover{color:#fff;background-color:#f0ad4e;border-color:#f0ad4e}.btn-outline-warning:focus,.btn-outline-warning.focus{color:#fff;background-color:#f0ad4e;border-color:#f0ad4e}.btn-outline-warning:active,.btn-outline-warning.active,.open>.btn-outline-warning.dropdown-toggle{color:#fff;background-color:#f0ad4e;border-color:#f0ad4e}.btn-outline-warning:active:hover,.btn-outline-warning:active:focus,.btn-outline-warning:active.focus,.btn-outline-warning.active:hover,.btn-outline-warning.active:focus,.btn-outline-warning.active.focus,.open>.btn-outline-warning.dropdown-toggle:hover,.open>.btn-outline-warning.dropdown-toggle:focus,.open>.btn-outline-warning.dropdown-toggle.focus{color:#fff;background-color:#d58512;border-color:#b06d0f}.btn-outline-warning.disabled:focus,.btn-outline-warning.disabled.focus,.btn-outline-warning:disabled:focus,.btn-outline-warning:disabled.focus{border-color:#f8d9ac}.btn-outline-warning.disabled:hover,.btn-outline-warning:disabled:hover{border-color:#f8d9ac}.btn-outline-danger{color:#b85c5c;background-image:none;background-color:transparent;border-color:#b85c5c}.btn-outline-danger:hover{color:#fff;background-color:#b85c5c;border-color:#b85c5c}.btn-outline-danger:focus,.btn-outline-danger.focus{color:#fff;background-color:#b85c5c;border-color:#b85c5c}.btn-outline-danger:active,.btn-outline-danger.active,.open>.btn-outline-danger.dropdown-toggle{color:#fff;background-color:#b85c5c;border-color:#b85c5c}.btn-outline-danger:active:hover,.btn-outline-danger:active:focus,.btn-outline-danger:active.focus,.btn-outline-danger.active:hover,.btn-outline-danger.active:focus,.btn-outline-danger.active.focus,.open>.btn-outline-danger.dropdown-toggle:hover,.open>.btn-outline-danger.dropdown-toggle:focus,.open>.btn-outline-danger.dropdown-toggle.focus{color:#fff;background-color:#843939;border-color:#672d2d}.btn-outline-danger.disabled:focus,.btn-outline-danger.disabled.focus,.btn-outline-danger:disabled:focus,.btn-outline-danger:disabled.focus{border-color:#d7a3a3}.btn-outline-danger.disabled:hover,.btn-outline-danger:disabled:hover{border-color:#d7a3a3}.btn-link{font-weight:400;color:#5ED9C3;border-radius:0}.btn-link,.btn-link:active,.btn-link.active,.btn-link:disabled{background-color:transparent}.btn-link,.btn-link:focus,.btn-link:active{border-color:transparent}.btn-link:hover{border-color:transparent}.btn-link:focus,.btn-link:hover{color:#3d8b3d;text-decoration:underline;background-color:transparent}.btn-link:disabled:focus,.btn-link:disabled:hover{color:#818a91;text-decoration:none}.btn-lg,.btn-group-lg>.btn{padding:.75rem 1.5rem;font-size:1.25rem;border-radius:.3rem}.btn-sm,.btn-group-sm>.btn{padding:.25rem .5rem;font-size:.875rem;border-radius:.2rem}.btn-block{display:block;width:100%}.btn-block+.btn-block{margin-top:5px}input[type=submit].btn-block,input[type=reset].btn-block,input[type=button].btn-block{width:100%}.fade{opacity:0;transition:opacity .15s linear}.fade.in{opacity:1}.collapse{display:none}.collapse.in{display:block}.collapsing{position:relative;height:0;overflow:hidden;transition-timing-function:ease;transition-duration:.35s;transition-property:height}.dropup,.dropdown{position:relative}.dropdown-toggle::after{display:inline-block;width:0;height:0;margin-right:.25rem;margin-left:.25rem;vertical-align:middle;content:"";border-top:.3em solid;border-right:.3em solid transparent;border-left:.3em solid transparent}.dropdown-toggle:focus{outline:0}.dropup .dropdown-toggle::after{border-top:0;border-bottom:.3em solid}.dropdown-menu{position:absolute;top:100%;left:0;z-index:1000;display:none;float:left;min-width:160px;padding:5px 0;margin:2px 0 0;font-size:1rem;color:#373a3c;text-align:left;list-style:none;background-color:#fff;background-clip:padding-box;border:1px solid rgba(0,0,0,.15);border-radius:.25rem}.dropdown-divider{height:1px;margin:.5rem 0;overflow:hidden;background-color:#e5e5e5}.dropdown-item{display:block;width:100%;padding:3px 20px;clear:both;font-weight:400;color:#373a3c;text-align:inherit;white-space:nowrap;background:0 0;border:0}.dropdown-item:focus,.dropdown-item:hover{color:#2b2d2f;text-decoration:none;background-color:#f5f5f5}.dropdown-item.active,.dropdown-item.active:focus,.dropdown-item.active:hover{color:#fff;text-decoration:none;background-color:#5ED9C3;outline:0}.dropdown-item.disabled,.dropdown-item.disabled:focus,.dropdown-item.disabled:hover{color:#818a91}.dropdown-item.disabled:focus,.dropdown-item.disabled:hover{text-decoration:none;cursor:not-allowed;background-color:transparent;background-image:none;filter:"progid:DXImageTransform.Microsoft.gradient(enabled = false)"}.open>.dropdown-menu{display:block}.open>a{outline:0}.dropdown-menu-right{right:0;left:auto}.dropdown-menu-left{right:auto;left:0}.dropdown-header{display:block;padding:5px 20px;font-size:.875rem;color:#818a91;white-space:nowrap}.dropdown-backdrop{position:fixed;top:0;right:0;bottom:0;left:0;z-index:990}.dropup .caret,.navbar-fixed-bottom .dropdown .caret{content:"";border-top:0;border-bottom:.3em solid}.dropup .dropdown-menu,.navbar-fixed-bottom .dropdown .dropdown-menu{top:auto;bottom:100%;margin-bottom:2px}.btn-group,.btn-group-vertical{position:relative;display:inline-block;vertical-align:middle}.btn-group>.btn,.btn-group-vertical>.btn{position:relative;float:left}.btn-group>.btn:focus,.btn-group>.btn:active,.btn-group>.btn.active,.btn-group-vertical>.btn:focus,.btn-group-vertical>.btn:active,.btn-group-vertical>.btn.active{z-index:2}.btn-group>.btn:hover,.btn-group-vertical>.btn:hover{z-index:2}.btn-group .btn+.btn,.btn-group .btn+.btn-group,.btn-group .btn-group+.btn,.btn-group .btn-group+.btn-group{margin-left:-1px}.btn-toolbar{margin-left:-5px}.btn-toolbar::after{content:"";display:table;clear:both}.btn-toolbar .btn-group,.btn-toolbar .input-group{float:left}.btn-toolbar>.btn,.btn-toolbar>.btn-group,.btn-toolbar>.input-group{margin-left:5px}.btn-group>.btn:not(:first-child):not(:last-child):not(.dropdown-toggle){border-radius:0}.btn-group>.btn:first-child{margin-left:0}.btn-group>.btn:first-child:not(:last-child):not(.dropdown-toggle){border-bottom-right-radius:0;border-top-right-radius:0}.btn-group>.btn:last-child:not(:first-child),.btn-group>.dropdown-toggle:not(:first-child){border-bottom-left-radius:0;border-top-left-radius:0}.btn-group>.btn-group{float:left}.btn-group>.btn-group:not(:first-child):not(:last-child)>.btn{border-radius:0}.btn-group>.btn-group:first-child:not(:last-child)>.btn:last-child,.btn-group>.btn-group:first-child:not(:last-child)>.dropdown-toggle{border-bottom-right-radius:0;border-top-right-radius:0}.btn-group>.btn-group:last-child:not(:first-child)>.btn:first-child{border-bottom-left-radius:0;border-top-left-radius:0}.btn-group .dropdown-toggle:active,.btn-group.open .dropdown-toggle{outline:0}.btn-group>.btn+.dropdown-toggle{padding-right:8px;padding-left:8px}.btn-group>.btn-lg+.dropdown-toggle,.btn-group-lg.btn-group>.btn+.dropdown-toggle{padding-right:12px;padding-left:12px}.btn .caret{margin-left:0}.btn-lg .caret,.btn-group-lg>.btn .caret{border-width:.3em .3em 0;border-bottom-width:0}.dropup .btn-lg .caret,.dropup .btn-group-lg>.btn .caret{border-width:0 .3em .3em}.btn-group-vertical>.btn,.btn-group-vertical>.btn-group,.btn-group-vertical>.btn-group>.btn{display:block;float:none;width:100%;max-width:100%}.btn-group-vertical>.btn-group::after{content:"";display:table;clear:both}.btn-group-vertical>.btn-group>.btn{float:none}.btn-group-vertical>.btn+.btn,.btn-group-vertical>.btn+.btn-group,.btn-group-vertical>.btn-group+.btn,.btn-group-vertical>.btn-group+.btn-group{margin-top:-1px;margin-left:0}.btn-group-vertical>.btn:not(:first-child):not(:last-child){border-radius:0}.btn-group-vertical>.btn:first-child:not(:last-child){border-bottom-right-radius:0;border-bottom-left-radius:0}.btn-group-vertical>.btn:last-child:not(:first-child){border-top-right-radius:0;border-top-left-radius:0}.btn-group-vertical>.btn-group:not(:first-child):not(:last-child)>.btn{border-radius:0}.btn-group-vertical>.btn-group:first-child:not(:last-child)>.btn:last-child,.btn-group-vertical>.btn-group:first-child:not(:last-child)>.dropdown-toggle{border-bottom-right-radius:0;border-bottom-left-radius:0}.btn-group-vertical>.btn-group:last-child:not(:first-child)>.btn:first-child{border-top-right-radius:0;border-top-left-radius:0}[data-toggle=buttons]>.btn input[type=radio],[data-toggle=buttons]>.btn input[type=checkbox],[data-toggle=buttons]>.btn-group>.btn input[type=radio],[data-toggle=buttons]>.btn-group>.btn input[type=checkbox]{position:absolute;clip:rect(0,0,0,0);pointer-events:none}.input-group{position:relative;width:100%;display:flex}.input-group .form-control{position:relative;z-index:2;flex:1;margin-bottom:0}.input-group .form-control:focus,.input-group .form-control:active,.input-group .form-control:hover{z-index:3}.input-group-addon:not(:first-child):not(:last-child),.input-group-btn:not(:first-child):not(:last-child),.input-group .form-control:not(:first-child):not(:last-child){border-radius:0}.input-group-addon,.input-group-btn{white-space:nowrap;vertical-align:middle}.input-group-addon{padding:.5rem .75rem;margin-bottom:0;font-size:1rem;font-weight:400;line-height:1.25;color:#55595c;text-align:center;background-color:#eceeef;border:1px solid rgba(0,0,0,.15);border-radius:.25rem}.input-group-addon.form-control-sm,.input-group-sm>.input-group-addon,.input-group-sm>.input-group-btn>.input-group-addon.btn{padding:.25rem .5rem;font-size:.875rem;border-radius:.2rem}.input-group-addon.form-control-lg,.input-group-lg>.input-group-addon,.input-group-lg>.input-group-btn>.input-group-addon.btn{padding:.75rem 1.5rem;font-size:1.25rem;border-radius:.3rem}.input-group-addon input[type=radio],.input-group-addon input[type=checkbox]{margin-top:0}.input-group .form-control:not(:last-child),.input-group-addon:not(:last-child),.input-group-btn:not(:last-child)>.btn,.input-group-btn:not(:last-child)>.btn-group>.btn,.input-group-btn:not(:last-child)>.dropdown-toggle,.input-group-btn:not(:first-child)>.btn:not(:last-child):not(.dropdown-toggle),.input-group-btn:not(:first-child)>.btn-group:not(:last-child)>.btn{border-bottom-right-radius:0;border-top-right-radius:0}.input-group-addon:not(:last-child){border-right:0}.input-group .form-control:not(:first-child),.input-group-addon:not(:first-child),.input-group-btn:not(:first-child)>.btn,.input-group-btn:not(:first-child)>.btn-group>.btn,.input-group-btn:not(:first-child)>.dropdown-toggle,.input-group-btn:not(:last-child)>.btn:not(:first-child),.input-group-btn:not(:last-child)>.btn-group:not(:first-child)>.btn{border-bottom-left-radius:0;border-top-left-radius:0}.form-control+.input-group-addon:not(:first-child){border-left:0}.input-group-btn{position:relative;font-size:0;white-space:nowrap}.input-group-btn>.btn{position:relative}.input-group-btn>.btn+.btn{margin-left:-1px}.input-group-btn>.btn:focus,.input-group-btn>.btn:active,.input-group-btn>.btn:hover{z-index:3}.input-group-btn:not(:last-child)>.btn,.input-group-btn:not(:last-child)>.btn-group{margin-right:-1px}.input-group-btn:not(:first-child)>.btn,.input-group-btn:not(:first-child)>.btn-group{z-index:2;margin-left:-1px}.input-group-btn:not(:first-child)>.btn:focus,.input-group-btn:not(:first-child)>.btn:active,.input-group-btn:not(:first-child)>.btn:hover,.input-group-btn:not(:first-child)>.btn-group:focus,.input-group-btn:not(:first-child)>.btn-group:active,.input-group-btn:not(:first-child)>.btn-group:hover{z-index:3}.custom-control{position:relative;display:inline;padding-left:1.5rem;cursor:pointer}.custom-control+.custom-control{margin-left:1rem}.custom-control-input{position:absolute;z-index:-1;opacity:0}.custom-control-input:checked~.custom-control-indicator{color:#fff;background-color:#0074d9}.custom-control-input:focus~.custom-control-indicator{box-shadow:0 0 0 .075rem #fff,0 0 0 .2rem #0074d9}.custom-control-input:active~.custom-control-indicator{color:#fff;background-color:#84c6ff}.custom-control-input:disabled~.custom-control-indicator{cursor:not-allowed;background-color:#eee}.custom-control-input:disabled~.custom-control-description{color:#767676;cursor:not-allowed}.custom-control-indicator{position:absolute;top:.0625rem;left:0;display:block;width:1rem;height:1rem;pointer-events:none;user-select:none;background-color:#ddd;background-repeat:no-repeat;background-position:center center;background-size:50% 50%}.custom-checkbox .custom-control-indicator{border-radius:.25rem}.custom-checkbox .custom-control-input:checked~.custom-control-indicator{background-image:url(data:image/svg+xml;charset=utf8;base64,PHN2ZyB4bWxucz0naHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmcnIHZpZXdCb3g9JzAgMCA4IDgnPjxwYXRoIGZpbGw9JyNmZmYnIGQ9J002LjU2NC43NWwtMy41OSAzLjYxMi0xLjUzOC0xLjU1TDAgNC4yNiAyLjk3NCA3LjI1IDggMi4xOTN6Jy8+PC9zdmc+)}.custom-checkbox .custom-control-input:indeterminate~.custom-control-indicator{background-color:#0074d9;background-image:url(data:image/svg+xml;charset=utf8;base64,PHN2ZyB4bWxucz0naHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmcnIHZpZXdCb3g9JzAgMCA0IDQnPjxwYXRoIHN0cm9rZT0nI2ZmZicgZD0nTTAgMmg0Jy8+PC9zdmc+)}.custom-radio .custom-control-indicator{border-radius:50%}.custom-radio .custom-control-input:checked~.custom-control-indicator{background-image:url(data:image/svg+xml;charset=utf8;base64,PHN2ZyB4bWxucz0naHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmcnIHZpZXdCb3g9Jy00IC00IDggOCc+PGNpcmNsZSByPSczJyBmaWxsPScjZmZmJy8+PC9zdmc+)}.custom-controls-stacked .custom-control{display:inline}.custom-controls-stacked .custom-control::after{display:block;margin-bottom:.25rem;content:""}.custom-controls-stacked .custom-control+.custom-control{margin-left:0}.custom-select{display:inline-block;max-width:100%;padding:.375rem 1.75rem .375rem .75rem;padding-right:.75rem \9;color:#55595c;vertical-align:middle;background:#fff url(data:image/svg+xml;charset=utf8;base64,PHN2ZyB4bWxucz0naHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmcnIHZpZXdCb3g9JzAgMCA0IDUnPjxwYXRoIGZpbGw9JyMzMzMnIGQ9J00yIDBMMCAyaDR6bTAgNUwwIDNoNHonLz48L3N2Zz4=) no-repeat right .75rem center;background-image:none \9;background-size:8px 10px;border:1px solid rgba(0,0,0,.15);border-radius:.25rem;-moz-appearance:none;-webkit-appearance:none}.custom-select:focus{border-color:#51a7e8;outline:none}.custom-select::-ms-expand{opacity:0}.custom-select-sm{padding-top:.375rem;padding-bottom:.375rem;font-size:75%}.custom-file{position:relative;display:inline-block;max-width:100%;height:2.5rem;cursor:pointer}.custom-file-input{min-width:14rem;max-width:100%;margin:0;filter:alpha(opacity=0);opacity:0}.custom-file-control{position:absolute;top:0;right:0;left:0;z-index:5;height:2.5rem;padding:.5rem 1rem;line-height:1.5;color:#555;user-select:none;background-color:#fff;border:1px solid #ddd;border-radius:.25rem}.custom-file-control:lang(en)::after{content:"Choose file..."}.custom-file-control::before{position:absolute;top:-1px;right:-1px;bottom:-1px;z-index:6;display:block;height:2.5rem;padding:.5rem 1rem;line-height:1.5;color:#555;background-color:#eee;border:1px solid #ddd;border-radius:0 .25rem .25rem 0}.custom-file-control:lang(en)::before{content:"Browse"}.nav{padding-left:0;margin-bottom:0;list-style:none}.nav-link{display:inline-block}.nav-link:focus,.nav-link:hover{text-decoration:none}.nav-link.disabled{color:#818a91}.nav-link.disabled,.nav-link.disabled:focus,.nav-link.disabled:hover{color:#818a91;cursor:not-allowed;background-color:transparent}.nav-inline .nav-item{display:inline-block}.nav-inline .nav-item+.nav-item,.nav-inline .nav-link+.nav-link{margin-left:1rem}.nav-tabs{border-bottom:1px solid #ddd}.nav-tabs::after{content:"";display:table;clear:both}.nav-tabs .nav-item{float:left;margin-bottom:-1px}.nav-tabs .nav-item+.nav-item{margin-left:.2rem}.nav-tabs .nav-link{display:block;padding:.5em 1em;border:1px solid transparent;border-radius:.25rem .25rem 0 0}.nav-tabs .nav-link:focus,.nav-tabs .nav-link:hover{border-color:#eceeef #eceeef #ddd}.nav-tabs .nav-link.disabled,.nav-tabs .nav-link.disabled:focus,.nav-tabs .nav-link.disabled:hover{color:#818a91;background-color:transparent;border-color:transparent}.nav-tabs .nav-link.active,.nav-tabs .nav-link.active:focus,.nav-tabs .nav-link.active:hover,.nav-tabs .nav-item.open .nav-link,.nav-tabs .nav-item.open .nav-link:focus,.nav-tabs .nav-item.open .nav-link:hover{color:#55595c;background-color:#fff;border-color:#ddd #ddd transparent}.nav-tabs .dropdown-menu{margin-top:-1px;border-top-right-radius:0;border-top-left-radius:0}.nav-pills::after{content:"";display:table;clear:both}.nav-pills .nav-item{float:left}.nav-pills .nav-item+.nav-item{margin-left:.2rem}.nav-pills .nav-link{display:block;padding:.5em 1em;border-radius:.25rem}.nav-pills .nav-link.active,.nav-pills .nav-link.active:focus,.nav-pills .nav-link.active:hover,.nav-pills .nav-item.open .nav-link,.nav-pills .nav-item.open .nav-link:focus,.nav-pills .nav-item.open .nav-link:hover{color:#fff;cursor:default;background-color:#5ED9C3}.nav-stacked .nav-item{display:block;float:none}.nav-stacked .nav-item+.nav-item{margin-top:.2rem;margin-left:0}.tab-content>.tab-pane{display:none}.tab-content>.active{display:block}.navbar{position:relative;padding:.5rem 1rem}.navbar::after{content:"";display:table;clear:both}@media(min-width:544px){.navbar{border-radius:.25rem}}.navbar-full{z-index:1000}@media(min-width:544px){.navbar-full{border-radius:0}}.navbar-fixed-top,.navbar-fixed-bottom{position:fixed;right:0;left:0;z-index:1030}@media(min-width:544px){.navbar-fixed-top,.navbar-fixed-bottom{border-radius:0}}.navbar-fixed-top{top:0}.navbar-fixed-bottom{bottom:0}.navbar-sticky-top{position:sticky;top:0;z-index:1030;width:100%}@media(min-width:544px){.navbar-sticky-top{border-radius:0}}.navbar-brand{float:left;padding-top:.25rem;padding-bottom:.25rem;margin-right:1rem;font-size:1.25rem}.navbar-brand:focus,.navbar-brand:hover{text-decoration:none}.navbar-brand>img{display:block}.navbar-divider{float:left;width:1px;padding-top:.425rem;padding-bottom:.425rem;margin-right:1rem;margin-left:1rem;overflow:hidden}.navbar-divider::before{content:"\00a0"}.navbar-toggler{padding:.5rem .75rem;font-size:1.25rem;line-height:1;background:0 0;border:1px solid transparent;border-radius:.25rem}.navbar-toggler:focus,.navbar-toggler:hover{text-decoration:none}@media(min-width:544px){.navbar-toggleable-xs{display:block!important}}@media(min-width:768px){.navbar-toggleable-sm{display:block!important}}@media(min-width:992px){.navbar-toggleable-md{display:block!important}}.navbar-nav .nav-item{float:left}.navbar-nav .nav-link{display:block;padding-top:.425rem;padding-bottom:.425rem}.navbar-nav .nav-link+.nav-link{margin-left:1rem}.navbar-nav .nav-item+.nav-item{margin-left:1rem}.navbar-light .navbar-brand{color:rgba(0,0,0,.8)}.navbar-light .navbar-brand:focus,.navbar-light .navbar-brand:hover{color:rgba(0,0,0,.8)}.navbar-light .navbar-nav .nav-link{color:rgba(0,0,0,.3)}.navbar-light .navbar-nav .nav-link:focus,.navbar-light .navbar-nav .nav-link:hover{color:rgba(0,0,0,.6)}.navbar-light .navbar-nav .open>.nav-link,.navbar-light .navbar-nav .open>.nav-link:focus,.navbar-light .navbar-nav .open>.nav-link:hover,.navbar-light .navbar-nav .active>.nav-link,.navbar-light .navbar-nav .active>.nav-link:focus,.navbar-light .navbar-nav .active>.nav-link:hover,.navbar-light .navbar-nav .nav-link.open,.navbar-light .navbar-nav .nav-link.open:focus,.navbar-light .navbar-nav .nav-link.open:hover,.navbar-light .navbar-nav .nav-link.active,.navbar-light .navbar-nav .nav-link.active:focus,.navbar-light .navbar-nav .nav-link.active:hover{color:rgba(0,0,0,.8)}.navbar-light .navbar-divider{background-color:rgba(0,0,0,.075)}.navbar-dark .navbar-brand{color:#fff}.navbar-dark .navbar-brand:focus,.navbar-dark .navbar-brand:hover{color:#fff}.navbar-dark .navbar-nav .nav-link{color:rgba(255,255,255,.5)}.navbar-dark .navbar-nav .nav-link:focus,.navbar-dark .navbar-nav .nav-link:hover{color:rgba(255,255,255,.75)}.navbar-dark .navbar-nav .open>.nav-link,.navbar-dark .navbar-nav .open>.nav-link:focus,.navbar-dark .navbar-nav .open>.nav-link:hover,.navbar-dark .navbar-nav .active>.nav-link,.navbar-dark .navbar-nav .active>.nav-link:focus,.navbar-dark .navbar-nav .active>.nav-link:hover,.navbar-dark .navbar-nav .nav-link.open,.navbar-dark .navbar-nav .nav-link.open:focus,.navbar-dark .navbar-nav .nav-link.open:hover,.navbar-dark .navbar-nav .nav-link.active,.navbar-dark .navbar-nav .nav-link.active:focus,.navbar-dark .navbar-nav .nav-link.active:hover{color:#fff}.navbar-dark .navbar-divider{background-color:rgba(255,255,255,.075)}.card{position:relative;display:block;margin-bottom:.75rem;background-color:#fff;border-radius:.25rem;box-shadow:inset 0 0 0 1px rgba(0,0,0,.125)}.card-block{padding:1.25rem}.card-block::after{content:"";display:table;clear:both}.card-title{margin-bottom:.75rem}.card-subtitle{margin-top:-.375rem;margin-bottom:0}.card-text:last-child{margin-bottom:0}.card-link:hover{text-decoration:none}.card-link+.card-link{margin-left:1.25rem}.card>.list-group:first-child .list-group-item:first-child{border-top-left-radius:.25rem;border-top-right-radius:.25rem}.card>.list-group:last-child .list-group-item:last-child{border-bottom-right-radius:.25rem;border-bottom-left-radius:.25rem}.card-header{padding:.75rem 1.25rem;background-color:#f5f5f5;box-shadow:inset 0 0 0 1px rgba(0,0,0,.125)}.card-header::after{content:"";display:table;clear:both}.card-header:first-child{border-radius:.25rem .25rem 0 0}.card-footer{height:67px;padding:.75rem 1.25rem;background-color:#f5f5f5;box-shadow:inset 0 0 0 1px rgba(0,0,0,.125)}.card-footer::after{content:"";display:table;clear:both}.card-footer:last-child{border-radius:0 0 .25rem .25rem}.card-header-tabs{margin-right:-.625rem;margin-bottom:-.75rem;margin-left:-.625rem;border-bottom:0}.card-header-tabs .nav-item{margin-bottom:0}.card-header-pills{margin-right:-.625rem;margin-left:-.625rem}.card-primary{background-color:#5ED9C3;border-color:#5ED9C3}.card-success{background-color:#5ED9C3;border-color:#5ED9C3}.card-info{background-color:#5bc0de;border-color:#5bc0de}.card-warning{background-color:#f0ad4e;border-color:#f0ad4e}.card-danger{background-color:#b85c5c;border-color:#b85c5c}.card-outline-primary{background-color:transparent;border-color:#5ED9C3}.card-outline-secondary{background-color:transparent;border-color:#ccc}.card-outline-info{background-color:transparent;border-color:#5bc0de}.card-outline-success{background-color:transparent;border-color:#5ED9C3}.card-outline-warning{background-color:transparent;border-color:#f0ad4e}.card-outline-danger{background-color:transparent;border-color:#b85c5c}.card-inverse .card-header,.card-inverse .card-footer{border-bottom:1px solid rgba(255,255,255,.2)}.card-inverse .card-header,.card-inverse .card-footer,.card-inverse .card-title,.card-inverse .card-blockquote{color:#fff}.card-inverse .card-link,.card-inverse .card-text,.card-inverse .card-blockquote>footer{color:rgba(255,255,255,.65)}.card-inverse .card-link:focus,.card-inverse .card-link:hover{color:#fff}.card-blockquote{padding:0;margin-bottom:0;border-left:0}.card-img{border-radius:.25rem}.card-img-overlay{position:absolute;top:0;right:0;bottom:0;left:0;padding:1.25rem}.card-img-top{border-radius:.25rem .25rem 0 0}.card-img-bottom{border-radius:0 0 .25rem .25rem}@media(min-width:544px){.card-deck{display:flex;flex-flow:row wrap;margin-right:-.625rem;margin-left:-.625rem}.card-deck .card{flex:1 0 0;margin-right:.625rem;margin-left:.625rem}}@media(min-width:544px){.card-group{display:flex;flex-flow:row wrap}.card-group .card{flex:1 0 0}.card-group .card+.card{margin-left:0;border-left:0}.card-group .card:first-child{border-bottom-right-radius:0;border-top-right-radius:0}.card-group .card:first-child .card-img-top{border-top-right-radius:0}.card-group .card:first-child .card-img-bottom{border-bottom-right-radius:0}.card-group .card:last-child{border-bottom-left-radius:0;border-top-left-radius:0}.card-group .card:last-child .card-img-top{border-top-left-radius:0}.card-group .card:last-child .card-img-bottom{border-bottom-left-radius:0}.card-group .card:not(:first-child):not(:last-child){border-radius:0}.card-group .card:not(:first-child):not(:last-child) .card-img-top,.card-group .card:not(:first-child):not(:last-child) .card-img-bottom{border-radius:0}}@media(min-width:544px){.card-columns{column-count:3;column-gap:1.25rem}.card-columns .card{display:inline-block;width:100%}}.breadcrumb{padding:.75rem 1rem;margin-bottom:1rem;list-style:none;background-color:#eceeef;border-radius:.25rem}.breadcrumb::after{content:"";display:table;clear:both}.breadcrumb-item{float:left}.breadcrumb-item+.breadcrumb-item::before{display:inline-block;padding-right:.5rem;padding-left:.5rem;color:#818a91;content:"/"}.breadcrumb-item+.breadcrumb-item:hover::before{text-decoration:underline}.breadcrumb-item+.breadcrumb-item:hover::before{text-decoration:none}.breadcrumb-item.active{color:#818a91}.pagination{display:inline-block;padding-left:0;margin-top:1rem;margin-bottom:1rem;border-radius:.25rem}.page-item{display:inline}.page-item:first-child .page-link{margin-left:0;border-bottom-left-radius:.25rem;border-top-left-radius:.25rem}.page-item:last-child .page-link{border-bottom-right-radius:.25rem;border-top-right-radius:.25rem}.page-item.active .page-link,.page-item.active .page-link:focus,.page-item.active .page-link:hover{z-index:2;color:#fff;cursor:default;background-color:#5ED9C3;border-color:#5ED9C3}.page-item.disabled .page-link,.page-item.disabled .page-link:focus,.page-item.disabled .page-link:hover{color:#818a91;pointer-events:none;cursor:not-allowed;background-color:#fff;border-color:#ddd}.page-link{position:relative;float:left;padding:.5rem .75rem;margin-left:-1px;color:#5ED9C3;text-decoration:none;background-color:#fff;border:1px solid #ddd}.page-link:focus,.page-link:hover{color:#3d8b3d;background-color:#eceeef;border-color:#ddd}.pagination-lg .page-link{padding:.75rem 1.5rem;font-size:1.25rem}.pagination-lg .page-item:first-child .page-link{border-bottom-left-radius:.3rem;border-top-left-radius:.3rem}.pagination-lg .page-item:last-child .page-link{border-bottom-right-radius:.3rem;border-top-right-radius:.3rem}.pagination-sm .page-link{padding:.275rem .75rem;font-size:.875rem}.pagination-sm .page-item:first-child .page-link{border-bottom-left-radius:.2rem;border-top-left-radius:.2rem}.pagination-sm .page-item:last-child .page-link{border-bottom-right-radius:.2rem;border-top-right-radius:.2rem}.tag{display:inline-block;padding:.25em .4em;font-size:75%;font-weight:700;line-height:1;color:#fff;text-align:center;white-space:nowrap;vertical-align:baseline;border-radius:.25rem}.tag:empty{display:none}.btn .tag{position:relative;top:-1px}a.tag:focus,a.tag:hover{color:#fff;text-decoration:none;cursor:pointer}.tag-pill{padding-right:.6em;padding-left:.6em;border-radius:10rem}.tag-default{background-color:#818a91}.tag-default[href]:focus,.tag-default[href]:hover{background-color:#687077}.tag-primary{background-color:#5ED9C3}.tag-primary[href]:focus,.tag-primary[href]:hover{background-color:#38b5b3}.tag-success{background-color:#5ED9C3}.tag-success[href]:focus,.tag-success[href]:hover{background-color:#38b5b3}.tag-info{background-color:#5bc0de}.tag-info[href]:focus,.tag-info[href]:hover{background-color:#31b0d5}.tag-warning{background-color:#f0ad4e}.tag-warning[href]:focus,.tag-warning[href]:hover{background-color:#ec971f}.tag-danger{background-color:#b85c5c}.tag-danger[href]:focus,.tag-danger[href]:hover{background-color:#9d4444}.jumbotron{padding:2rem 1rem;margin-bottom:2rem;background-color:#eceeef;border-radius:.3rem}@media(min-width:544px){.jumbotron{padding:4rem 2rem}}.jumbotron-hr{border-top-color:#d0d5d8}.jumbotron-fluid{padding-right:0;padding-left:0;border-radius:0}.alert{padding:15px;margin-bottom:1rem;border:1px solid transparent;border-radius:.25rem}.alert-heading{color:inherit}.alert-link{font-weight:700}.alert-dismissible{padding-right:35px}.alert-dismissible .close{position:relative;top:-2px;right:-21px;color:inherit}.alert-success{background-color:#dff0d8;border-color:#d0e9c6;color:#3c763d}.alert-success hr{border-top-color:#c1e2b3}.alert-success .alert-link{color:#2b542c}.alert-info{background-color:#d9edf7;border-color:#bcdff1;color:#31708f}.alert-info hr{border-top-color:#a6d5ec}.alert-info .alert-link{color:#245269}.alert-warning{background-color:#fcf8e3;border-color:#faf2cc;color:#8a6d3b}.alert-warning hr{border-top-color:#f7ecb5}.alert-warning .alert-link{color:#66512c}.alert-danger{background-color:#f2dede;border-color:#ebcccc;color:#a94442}.alert-danger hr{border-top-color:#e4b9b9}.alert-danger .alert-link{color:#843534}@keyframes progress-bar-stripes{from{background-position:1rem 0}to{background-position:0 0}}.progress{display:block;width:100%;height:1rem;margin-bottom:1rem}.progress[value]{background-color:#eee;border:0;appearance:none;border-radius:.25rem}.progress[value]::-ms-fill{background-color:#0074d9;border:0}.progress[value]::-moz-progress-bar{background-color:#0074d9;border-bottom-left-radius:.25rem;border-top-left-radius:.25rem}.progress[value]::-webkit-progress-value{background-color:#0074d9;border-bottom-left-radius:.25rem;border-top-left-radius:.25rem}.progress[value="100"]::-moz-progress-bar{border-bottom-right-radius:.25rem;border-top-right-radius:.25rem}.progress[value="100"]::-webkit-progress-value{border-bottom-right-radius:.25rem;border-top-right-radius:.25rem}.progress[value]::-webkit-progress-bar{background-color:#eee;border-radius:.25rem}base::-moz-progress-bar,.progress[value]{background-color:#eee;border-radius:.25rem}@media screen and (min-width:0\0){.progress{background-color:#eee;border-radius:.25rem}.progress-bar{display:inline-block;height:1rem;text-indent:-999rem;background-color:#0074d9;border-bottom-left-radius:.25rem;border-top-left-radius:.25rem}.progress[width="100%"]{border-bottom-right-radius:.25rem;border-top-right-radius:.25rem}}.progress-striped[value]::-webkit-progress-value{background-image:linear-gradient(45deg,rgba(255,255,255,0.15) 25%,transparent 25%,transparent 50%,rgba(255,255,255,0.15) 50%,rgba(255,255,255,0.15) 75%,transparent 75%,transparent);background-size:1rem 1rem}.progress-striped[value]::-moz-progress-bar{background-image:linear-gradient(45deg,rgba(255,255,255,0.15) 25%,transparent 25%,transparent 50%,rgba(255,255,255,0.15) 50%,rgba(255,255,255,0.15) 75%,transparent 75%,transparent);background-size:1rem 1rem}.progress-striped[value]::-ms-fill{background-image:linear-gradient(45deg,rgba(255,255,255,0.15) 25%,transparent 25%,transparent 50%,rgba(255,255,255,0.15) 50%,rgba(255,255,255,0.15) 75%,transparent 75%,transparent);background-size:1rem 1rem}@media screen and (min-width:0\0){.progress-bar-striped{background-image:linear-gradient(45deg,rgba(255,255,255,0.15) 25%,transparent 25%,transparent 50%,rgba(255,255,255,0.15) 50%,rgba(255,255,255,0.15) 75%,transparent 75%,transparent);background-size:1rem 1rem}}.progress-animated[value]::-webkit-progress-value{animation:progress-bar-stripes 2s linear infinite}.progress-animated[value]::-moz-progress-bar{animation:progress-bar-stripes 2s linear infinite}@media screen and (min-width:0\0){.progress-animated .progress-bar-striped{animation:progress-bar-stripes 2s linear infinite}}.progress-success[value]::-webkit-progress-value{background-color:#5ED9C3}.progress-success[value]::-moz-progress-bar{background-color:#5ED9C3}.progress-success[value]::-ms-fill{background-color:#5ED9C3}@media screen and (min-width:0\0){.progress-success .progress-bar{background-color:#5ED9C3}}.progress-info[value]::-webkit-progress-value{background-color:#5bc0de}.progress-info[value]::-moz-progress-bar{background-color:#5bc0de}.progress-info[value]::-ms-fill{background-color:#5bc0de}@media screen and (min-width:0\0){.progress-info .progress-bar{background-color:#5bc0de}}.progress-warning[value]::-webkit-progress-value{background-color:#f0ad4e}.progress-warning[value]::-moz-progress-bar{background-color:#f0ad4e}.progress-warning[value]::-ms-fill{background-color:#f0ad4e}@media screen and (min-width:0\0){.progress-warning .progress-bar{background-color:#f0ad4e}}.progress-danger[value]::-webkit-progress-value{background-color:#b85c5c}.progress-danger[value]::-moz-progress-bar{background-color:#b85c5c}.progress-danger[value]::-ms-fill{background-color:#b85c5c}@media screen and (min-width:0\0){.progress-danger .progress-bar{background-color:#b85c5c}}.media{display:flex;margin-bottom:1rem}.media-body{flex:1}.media-middle{align-self:center}.media-bottom{align-self:flex-end}.media-object{display:block}.media-object.img-thumbnail{max-width:none}.media-right{padding-left:10px}.media-left{padding-right:10px}.media-heading{margin-top:0;margin-bottom:5px}.media-list{padding-left:0;list-style:none}.list-group{padding-left:0;margin-bottom:0}.list-group-item{position:relative;display:block;padding:.75rem 1.25rem;margin-bottom:-1px;background-color:#fff;border:1px solid #ddd}.list-group-item:first-child{border-top-right-radius:.25rem;border-top-left-radius:.25rem}.list-group-item:last-child{margin-bottom:0;border-bottom-right-radius:.25rem;border-bottom-left-radius:.25rem}.list-group-item.disabled,.list-group-item.disabled:focus,.list-group-item.disabled:hover{color:#818a91;cursor:not-allowed;background-color:#eceeef}.list-group-item.disabled .list-group-item-heading,.list-group-item.disabled:focus .list-group-item-heading,.list-group-item.disabled:hover .list-group-item-heading{color:inherit}.list-group-item.disabled .list-group-item-text,.list-group-item.disabled:focus .list-group-item-text,.list-group-item.disabled:hover .list-group-item-text{color:#818a91}.list-group-item.active,.list-group-item.active:focus,.list-group-item.active:hover{z-index:2;color:#fff;text-decoration:none;background-color:#5ED9C3;border-color:#5ED9C3}.list-group-item.active .list-group-item-heading,.list-group-item.active .list-group-item-heading>small,.list-group-item.active .list-group-item-heading>.small,.list-group-item.active:focus .list-group-item-heading,.list-group-item.active:focus .list-group-item-heading>small,.list-group-item.active:focus .list-group-item-heading>.small,.list-group-item.active:hover .list-group-item-heading,.list-group-item.active:hover .list-group-item-heading>small,.list-group-item.active:hover .list-group-item-heading>.small{color:inherit}.list-group-item.active .list-group-item-text,.list-group-item.active:focus .list-group-item-text,.list-group-item.active:hover .list-group-item-text{color:#eaf6ea}.list-group-flush .list-group-item{border-radius:0}.list-group-item-action{width:100%;color:#555;text-align:inherit}.list-group-item-action .list-group-item-heading{color:#333}.list-group-item-action:focus,.list-group-item-action:hover{color:#555;text-decoration:none;background-color:#f5f5f5}.list-group-item-success{color:#3c763d;background-color:#dff0d8}a.list-group-item-success,button.list-group-item-success{color:#3c763d}a.list-group-item-success .list-group-item-heading,button.list-group-item-success .list-group-item-heading{color:inherit}a.list-group-item-success:focus,a.list-group-item-success:hover,button.list-group-item-success:focus,button.list-group-item-success:hover{color:#3c763d;background-color:#d0e9c6}a.list-group-item-success.active,a.list-group-item-success.active:focus,a.list-group-item-success.active:hover,button.list-group-item-success.active,button.list-group-item-success.active:focus,button.list-group-item-success.active:hover{color:#fff;background-color:#3c763d;border-color:#3c763d}.list-group-item-info{color:#31708f;background-color:#d9edf7}a.list-group-item-info,button.list-group-item-info{color:#31708f}a.list-group-item-info .list-group-item-heading,button.list-group-item-info .list-group-item-heading{color:inherit}a.list-group-item-info:focus,a.list-group-item-info:hover,button.list-group-item-info:focus,button.list-group-item-info:hover{color:#31708f;background-color:#c4e3f3}a.list-group-item-info.active,a.list-group-item-info.active:focus,a.list-group-item-info.active:hover,button.list-group-item-info.active,button.list-group-item-info.active:focus,button.list-group-item-info.active:hover{color:#fff;background-color:#31708f;border-color:#31708f}.list-group-item-warning{color:#8a6d3b;background-color:#fcf8e3}a.list-group-item-warning,button.list-group-item-warning{color:#8a6d3b}a.list-group-item-warning .list-group-item-heading,button.list-group-item-warning .list-group-item-heading{color:inherit}a.list-group-item-warning:focus,a.list-group-item-warning:hover,button.list-group-item-warning:focus,button.list-group-item-warning:hover{color:#8a6d3b;background-color:#faf2cc}a.list-group-item-warning.active,a.list-group-item-warning.active:focus,a.list-group-item-warning.active:hover,button.list-group-item-warning.active,button.list-group-item-warning.active:focus,button.list-group-item-warning.active:hover{color:#fff;background-color:#8a6d3b;border-color:#8a6d3b}.list-group-item-danger{color:#a94442;background-color:#f2dede}a.list-group-item-danger,button.list-group-item-danger{color:#a94442}a.list-group-item-danger .list-group-item-heading,button.list-group-item-danger .list-group-item-heading{color:inherit}a.list-group-item-danger:focus,a.list-group-item-danger:hover,button.list-group-item-danger:focus,button.list-group-item-danger:hover{color:#a94442;background-color:#ebcccc}a.list-group-item-danger.active,a.list-group-item-danger.active:focus,a.list-group-item-danger.active:hover,button.list-group-item-danger.active,button.list-group-item-danger.active:focus,button.list-group-item-danger.active:hover{color:#fff;background-color:#a94442;border-color:#a94442}.list-group-item-heading{margin-top:0;margin-bottom:5px}.list-group-item-text{margin-bottom:0;line-height:1.3}.embed-responsive{position:relative;display:block;height:0;padding:0;overflow:hidden}.embed-responsive .embed-responsive-item,.embed-responsive iframe,.embed-responsive embed,.embed-responsive object,.embed-responsive video{position:absolute;top:0;bottom:0;left:0;width:100%;height:100%;border:0}.embed-responsive-21by9{padding-bottom:42.85714%}.embed-responsive-16by9{padding-bottom:56.25%}.embed-responsive-4by3{padding-bottom:75%}.embed-responsive-1by1{padding-bottom:100%}.close{float:right;font-size:1.5rem;font-weight:700;line-height:1;color:#000;text-shadow:0 1px 0 #fff;opacity:.2}.close:focus,.close:hover{color:#000;text-decoration:none;cursor:pointer;opacity:.5}button.close{padding:0;cursor:pointer;background:0 0;border:0;-webkit-appearance:none}.modal-open{overflow:hidden}.modal{position:fixed;top:0;right:0;bottom:0;left:0;z-index:1050;display:none;overflow:hidden;outline:0;-webkit-overflow-scrolling:touch}.modal.fade .modal-dialog{transition:transform .3s ease-out;transform:translate(0,-25%)}.modal.in .modal-dialog{transform:translate(0,0)}.modal-open .modal{overflow-x:hidden;overflow-y:auto}.modal-dialog{position:relative;width:auto;margin:10px}.modal-content{position:relative;background-color:#fff;background-clip:padding-box;border:1px solid rgba(0,0,0,.2);border-radius:.3rem;outline:0}.modal-backdrop{position:fixed;top:0;right:0;bottom:0;left:0;z-index:1040;background-color:#000}.modal-backdrop.fade{opacity:0}.modal-backdrop.in{opacity:.5}.modal-header{padding:15px;border-bottom:1px solid #e5e5e5}.modal-header::after{content:"";display:table;clear:both}.modal-header .close{margin-top:-2px}.modal-title{margin:0;line-height:1.5}.modal-body{position:relative;padding:15px}.modal-footer{padding:15px;text-align:right;border-top:1px solid #e5e5e5}.modal-footer::after{content:"";display:table;clear:both}.modal-scrollbar-measure{position:absolute;top:-9999px;width:50px;height:50px;overflow:scroll}@media(min-width:544px){.modal-dialog{max-width:600px;margin:30px auto}.modal-sm{max-width:300px}}@media(min-width:992px){.modal-lg{max-width:900px}}.tooltip{position:absolute;z-index:1070;display:block;font-family:source sans pro,sans-serif;font-style:normal;font-weight:400;letter-spacing:normal;line-break:auto;line-height:1.5;text-align:left;text-align:start;text-decoration:none;text-shadow:none;text-transform:none;white-space:normal;word-break:normal;word-spacing:normal;font-size:.875rem;word-wrap:break-word;opacity:0}.tooltip.in{opacity:.9}.tooltip.tooltip-top,.tooltip.bs-tether-element-attached-bottom{padding:5px 0;margin-top:-3px}.tooltip.tooltip-top .tooltip-arrow,.tooltip.bs-tether-element-attached-bottom .tooltip-arrow{bottom:0;left:50%;margin-left:-5px;border-width:5px 5px 0;border-top-color:#000}.tooltip.tooltip-right,.tooltip.bs-tether-element-attached-left{padding:0 5px;margin-left:3px}.tooltip.tooltip-right .tooltip-arrow,.tooltip.bs-tether-element-attached-left .tooltip-arrow{top:50%;left:0;margin-top:-5px;border-width:5px 5px 5px 0;border-right-color:#000}.tooltip.tooltip-bottom,.tooltip.bs-tether-element-attached-top{padding:5px 0;margin-top:3px}.tooltip.tooltip-bottom .tooltip-arrow,.tooltip.bs-tether-element-attached-top .tooltip-arrow{top:0;left:50%;margin-left:-5px;border-width:0 5px 5px;border-bottom-color:#000}.tooltip.tooltip-left,.tooltip.bs-tether-element-attached-right{padding:0 5px;margin-left:-3px}.tooltip.tooltip-left .tooltip-arrow,.tooltip.bs-tether-element-attached-right .tooltip-arrow{top:50%;right:0;margin-top:-5px;border-width:5px 0 5px 5px;border-left-color:#000}.tooltip-inner{max-width:200px;padding:3px 8px;color:#fff;text-align:center;background-color:#000;border-radius:.25rem}.tooltip-arrow{position:absolute;width:0;height:0;border-color:transparent;border-style:solid}.popover{position:absolute;top:0;left:0;z-index:1060;display:block;max-width:276px;padding:1px;font-family:source sans pro,sans-serif;font-style:normal;font-weight:400;letter-spacing:normal;line-break:auto;line-height:1.5;text-align:left;text-align:start;text-decoration:none;text-shadow:none;text-transform:none;white-space:normal;word-break:normal;word-spacing:normal;font-size:.875rem;word-wrap:break-word;background-color:#fff;background-clip:padding-box;border:1px solid rgba(0,0,0,.2);border-radius:.3rem}.popover.popover-top,.popover.bs-tether-element-attached-bottom{margin-top:-10px}.popover.popover-top .popover-arrow,.popover.bs-tether-element-attached-bottom .popover-arrow{bottom:-11px;left:50%;margin-left:-11px;border-top-color:rgba(0,0,0,.25);border-bottom-width:0}.popover.popover-top .popover-arrow::after,.popover.bs-tether-element-attached-bottom .popover-arrow::after{bottom:1px;margin-left:-10px;content:"";border-top-color:#fff;border-bottom-width:0}.popover.popover-right,.popover.bs-tether-element-attached-left{margin-left:10px}.popover.popover-right .popover-arrow,.popover.bs-tether-element-attached-left .popover-arrow{top:50%;left:-11px;margin-top:-11px;border-right-color:rgba(0,0,0,.25);border-left-width:0}.popover.popover-right .popover-arrow::after,.popover.bs-tether-element-attached-left .popover-arrow::after{bottom:-10px;left:1px;content:"";border-right-color:#fff;border-left-width:0}.popover.popover-bottom,.popover.bs-tether-element-attached-top{margin-top:10px}.popover.popover-bottom .popover-arrow,.popover.bs-tether-element-attached-top .popover-arrow{top:-11px;left:50%;margin-left:-11px;border-top-width:0;border-bottom-color:rgba(0,0,0,.25)}.popover.popover-bottom .popover-arrow::after,.popover.bs-tether-element-attached-top .popover-arrow::after{top:1px;margin-left:-10px;content:"";border-top-width:0;border-bottom-color:#fff}.popover.popover-left,.popover.bs-tether-element-attached-right{margin-left:-10px}.popover.popover-left .popover-arrow,.popover.bs-tether-element-attached-right .popover-arrow{top:50%;right:-11px;margin-top:-11px;border-right-width:0;border-left-color:rgba(0,0,0,.25)}.popover.popover-left .popover-arrow::after,.popover.bs-tether-element-attached-right .popover-arrow::after{right:1px;bottom:-10px;content:"";border-right-width:0;border-left-color:#fff}.popover-title{padding:8px 14px;margin:0;font-size:1rem;background-color:#f7f7f7;border-bottom:1px solid #ebebeb;border-radius:.2375rem .2375rem 0 0}.popover-content{padding:9px 14px}.popover-arrow,.popover-arrow::after{position:absolute;display:block;width:0;height:0;border-color:transparent;border-style:solid}.popover-arrow{border-width:11px}.popover-arrow::after{content:"";border-width:10px}.carousel{position:relative}.carousel-inner{position:relative;width:100%;overflow:hidden}.carousel-inner>.carousel-item{position:relative;display:none;transition:.6s ease-in-out left}.carousel-inner>.carousel-item>img,.carousel-inner>.carousel-item>a>img{line-height:1}@media all and (transform-3d),(-webkit-transform-3d){.carousel-inner>.carousel-item{transition:transform .6s ease-in-out;backface-visibility:hidden;perspective:1000px}.carousel-inner>.carousel-item.next,.carousel-inner>.carousel-item.active.right{left:0;transform:translate3d(100%,0,0)}.carousel-inner>.carousel-item.prev,.carousel-inner>.carousel-item.active.left{left:0;transform:translate3d(-100%,0,0)}.carousel-inner>.carousel-item.next.left,.carousel-inner>.carousel-item.prev.right,.carousel-inner>.carousel-item.active{left:0;transform:translate3d(0,0,0)}}.carousel-inner>.active,.carousel-inner>.next,.carousel-inner>.prev{display:block}.carousel-inner>.active{left:0}.carousel-inner>.next,.carousel-inner>.prev{position:absolute;top:0;width:100%}.carousel-inner>.next{left:100%}.carousel-inner>.prev{left:-100%}.carousel-inner>.next.left,.carousel-inner>.prev.right{left:0}.carousel-inner>.active.left{left:-100%}.carousel-inner>.active.right{left:100%}.carousel-control{position:absolute;top:0;bottom:0;left:0;width:15%;font-size:20px;color:#fff;text-align:center;text-shadow:0 1px 2px rgba(0,0,0,.6);opacity:.5}.carousel-control.left{background-image:linear-gradient(to right,rgba(0,0,0,0.5) 0%,rgba(0,0,0,0.0001) 100%);background-repeat:repeat-x;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#80000000',endColorstr='#00000000',GradientType=1)}.carousel-control.right{right:0;left:auto;background-image:linear-gradient(to right,rgba(0,0,0,0.0001) 0%,rgba(0,0,0,0.5) 100%);background-repeat:repeat-x;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#00000000',endColorstr='#80000000',GradientType=1)}.carousel-control:focus,.carousel-control:hover{color:#fff;text-decoration:none;outline:0;opacity:.9}.carousel-control .icon-prev,.carousel-control .icon-next{position:absolute;top:50%;z-index:5;display:inline-block;width:20px;height:20px;margin-top:-10px;font-family:serif;line-height:1}.carousel-control .icon-prev{left:50%;margin-left:-10px}.carousel-control .icon-next{right:50%;margin-right:-10px}.carousel-control .icon-prev::before{content:"\2039"}.carousel-control .icon-next::before{content:"\203a"}.carousel-indicators{position:absolute;bottom:10px;left:50%;z-index:15;width:60%;padding-left:0;margin-left:-30%;text-align:center;list-style:none}.carousel-indicators li{display:inline-block;width:10px;height:10px;margin:1px;text-indent:-999px;cursor:pointer;background-color:transparent;border:1px solid #fff;border-radius:10px}.carousel-indicators .active{width:12px;height:12px;margin:0;background-color:#fff}.carousel-caption{position:absolute;right:15%;bottom:20px;left:15%;z-index:10;padding-top:20px;padding-bottom:20px;color:#fff;text-align:center;text-shadow:0 1px 2px rgba(0,0,0,.6)}.carousel-caption .btn{text-shadow:none}@media(min-width:544px){.carousel-control .icon-prev,.carousel-control .icon-next{width:30px;height:30px;margin-top:-15px;font-size:30px}.carousel-control .icon-prev{margin-left:-15px}.carousel-control .icon-next{margin-right:-15px}.carousel-caption{right:20%;left:20%;padding-bottom:30px}.carousel-indicators{bottom:20px}}.bg-inverse{color:#eceeef;background-color:#373a3c}.bg-faded{background-color:#f7f7f9}.bg-primary{color:#fff!important;background-color:#5ED9C3!important}a.bg-primary:focus,a.bg-primary:hover{background-color:#38b5b3!important}.bg-success{color:#fff!important;background-color:#5ED9C3!important}a.bg-success:focus,a.bg-success:hover{background-color:#38b5b3!important}.bg-info{color:#fff!important;background-color:#5bc0de!important}a.bg-info:focus,a.bg-info:hover{background-color:#31b0d5!important}.bg-warning{color:#fff!important;background-color:#f0ad4e!important}a.bg-warning:focus,a.bg-warning:hover{background-color:#ec971f!important}.bg-danger{color:#fff!important;background-color:#b85c5c!important}a.bg-danger:focus,a.bg-danger:hover{background-color:#9d4444!important}.clearfix::after{content:"";display:table;clear:both}.pull-xs-left{float:left!important}.pull-xs-right{float:right!important}.pull-xs-none{float:none!important}@media(min-width:544px){.pull-sm-left{float:left!important}.pull-sm-right{float:right!important}.pull-sm-none{float:none!important}}@media(min-width:768px){.pull-md-left{float:left!important}.pull-md-right{float:right!important}.pull-md-none{float:none!important}}@media(min-width:992px){.pull-lg-left{float:left!important}.pull-lg-right{float:right!important}.pull-lg-none{float:none!important}}@media(min-width:1200px){.pull-xl-left{float:left!important}.pull-xl-right{float:right!important}.pull-xl-none{float:none!important}}.sr-only{position:absolute;width:1px;height:1px;padding:0;margin:-1px;overflow:hidden;clip:rect(0,0,0,0);border:0}.sr-only-focusable:active,.sr-only-focusable:focus{position:static;width:auto;height:auto;margin:0;overflow:visible;clip:auto}.m-x-auto{margin-right:auto!important;margin-left:auto!important}.m-a-0{margin:0!important}.m-t-0{margin-top:0!important}.m-r-0{margin-right:0!important}.m-b-0{margin-bottom:0!important}.m-l-0{margin-left:0!important}.m-x-0{margin-right:0!important;margin-left:0!important}.m-y-0{margin-top:0!important;margin-bottom:0!important}.m-a-1{margin:1rem!important}.m-t-1{margin-top:1rem!important}.m-r-1{margin-right:1rem!important}.m-b-1{margin-bottom:1rem!important}.m-l-1{margin-left:1rem!important}.m-x-1{margin-right:1rem!important;margin-left:1rem!important}.m-y-1{margin-top:1rem!important;margin-bottom:1rem!important}.m-a-2{margin:1.5rem!important}.m-t-2{margin-top:1.5rem!important}.m-r-2{margin-right:1.5rem!important}.m-b-2{margin-bottom:1.5rem!important}.m-l-2{margin-left:1.5rem!important}.m-x-2{margin-right:1.5rem!important;margin-left:1.5rem!important}.m-y-2{margin-top:1.5rem!important;margin-bottom:1.5rem!important}.m-a-3{margin:3rem!important}.m-t-3{margin-top:3rem!important}.m-r-3{margin-right:3rem!important}.m-b-3{margin-bottom:3rem!important}.m-l-3{margin-left:3rem!important}.m-x-3{margin-right:3rem!important;margin-left:3rem!important}.m-y-3{margin-top:3rem!important;margin-bottom:3rem!important}.p-a-0{padding:0!important}.p-t-0{padding-top:0!important}.p-r-0{padding-right:0!important}.p-b-0{padding-bottom:0!important}.p-l-0{padding-left:0!important}.p-x-0{padding-right:0!important;padding-left:0!important}.p-y-0{padding-top:0!important;padding-bottom:0!important}.p-a-1{padding:1rem!important}.p-t-1{padding-top:1rem!important}.p-r-1{padding-right:1rem!important}.p-b-1{padding-bottom:1rem!important}.p-l-1{padding-left:1rem!important}.p-x-1{padding-right:1rem!important;padding-left:1rem!important}.p-y-1{padding-top:1rem!important;padding-bottom:1rem!important}.p-a-2{padding:1.5rem!important}.p-t-2{padding-top:1.5rem!important}.p-r-2{padding-right:1.5rem!important}.p-b-2{padding-bottom:1.5rem!important}.p-l-2{padding-left:1.5rem!important}.p-x-2{padding-right:1.5rem!important;padding-left:1.5rem!important}.p-y-2{padding-top:1.5rem!important;padding-bottom:1.5rem!important}.p-a-3{padding:3rem!important}.p-t-3{padding-top:3rem!important}.p-r-3{padding-right:3rem!important}.p-b-3{padding-bottom:3rem!important}.p-l-3{padding-left:3rem!important}.p-x-3{padding-right:3rem!important;padding-left:3rem!important}.p-y-3{padding-top:3rem!important;padding-bottom:3rem!important}.pos-f-t{position:fixed;top:0;right:0;left:0;z-index:1030}.text-justify{text-align:justify!important}.text-nowrap{white-space:nowrap!important}.text-truncate{overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.text-xs-left{text-align:left!important}.text-xs-right{text-align:right!important}.text-xs-center{text-align:center!important}@media(min-width:544px){.text-sm-left{text-align:left!important}.text-sm-right{text-align:right!important}.text-sm-center{text-align:center!important}}@media(min-width:768px){.text-md-left{text-align:left!important}.text-md-right{text-align:right!important}.text-md-center{text-align:center!important}}@media(min-width:992px){.text-lg-left{text-align:left!important}.text-lg-right{text-align:right!important}.text-lg-center{text-align:center!important}}@media(min-width:1200px){.text-xl-left{text-align:left!important}.text-xl-right{text-align:right!important}.text-xl-center{text-align:center!important}}.text-lowercase{text-transform:lowercase!important}.text-uppercase{text-transform:uppercase!important}.text-capitalize{text-transform:capitalize!important}.font-weight-normal{font-weight:400}.font-weight-bold{font-weight:700}.font-italic{font-style:italic}.text-muted{color:#818a91!important}a.text-muted:focus,a.text-muted:hover{color:#687077}.text-primary{color:#5ED9C3!important}a.text-primary:focus,a.text-primary:hover{color:#38b5b3}.text-success{color:#5ED9C3!important}a.text-success:focus,a.text-success:hover{color:#38b5b3}.text-info{color:#5bc0de!important}a.text-info:focus,a.text-info:hover{color:#31b0d5}.text-warning{color:#f0ad4e!important}a.text-warning:focus,a.text-warning:hover{color:#ec971f}.text-danger{color:#b85c5c!important}a.text-danger:focus,a.text-danger:hover{color:#9d4444}.text-hide{font:0/0 a;color:transparent;text-shadow:none;background-color:transparent;border:0}.invisible{visibility:hidden!important}.hidden-xs-up{display:none!important}@media(max-width:543px){.hidden-xs-down{display:none!important}}@media(min-width:544px){.hidden-sm-up{display:none!important}}@media(max-width:767px){.hidden-sm-down{display:none!important}}@media(min-width:768px){.hidden-md-up{display:none!important}}@media(max-width:991px){.hidden-md-down{display:none!important}}@media(min-width:992px){.hidden-lg-up{display:none!important}}@media(max-width:1199px){.hidden-lg-down{display:none!important}}@media(min-width:1200px){.hidden-xl-up{display:none!important}}.hidden-xl-down{display:none!important}.visible-print-block{display:none!important}@media print{.visible-print-block{display:block!important}}.visible-print-inline{display:none!important}@media print{.visible-print-inline{display:inline!important}}.visible-print-inline-block{display:none!important}@media print{.visible-print-inline-block{display:inline-block!important}}@media print{.hidden-print{display:none!important}}.flex-xs-first{order:-1}.flex-xs-last{order:1}.flex-items-xs-top{align-items:flex-start}.flex-items-xs-middle{align-items:center}.flex-items-xs-bottom{align-items:flex-end}.flex-xs-top{align-self:flex-start}.flex-xs-middle{align-self:center}.flex-xs-bottom{align-self:flex-end}.flex-items-xs-left{justify-content:flex-start}.flex-items-xs-center{justify-content:center}.flex-items-xs-right{justify-content:flex-end}.flex-items-xs-around{justify-content:space-around}.flex-items-xs-between{justify-content:space-between}@media(min-width:544px){.flex-sm-first{order:-1}.flex-sm-last{order:1}}@media(min-width:544px){.flex-items-sm-top{align-items:flex-start}.flex-items-sm-middle{align-items:center}.flex-items-sm-bottom{align-items:flex-end}}@media(min-width:544px){.flex-sm-top{align-self:flex-start}.flex-sm-middle{align-self:center}.flex-sm-bottom{align-self:flex-end}}@media(min-width:544px){.flex-items-sm-left{justify-content:flex-start}.flex-items-sm-center{justify-content:center}.flex-items-sm-right{justify-content:flex-end}.flex-items-sm-around{justify-content:space-around}.flex-items-sm-between{justify-content:space-between}}@media(min-width:768px){.flex-md-first{order:-1}.flex-md-last{order:1}}@media(min-width:768px){.flex-items-md-top{align-items:flex-start}.flex-items-md-middle{align-items:center}.flex-items-md-bottom{align-items:flex-end}}@media(min-width:768px){.flex-md-top{align-self:flex-start}.flex-md-middle{align-self:center}.flex-md-bottom{align-self:flex-end}}@media(min-width:768px){.flex-items-md-left{justify-content:flex-start}.flex-items-md-center{justify-content:center}.flex-items-md-right{justify-content:flex-end}.flex-items-md-around{justify-content:space-around}.flex-items-md-between{justify-content:space-between}}@media(min-width:992px){.flex-lg-first{order:-1}.flex-lg-last{order:1}}@media(min-width:992px){.flex-items-lg-top{align-items:flex-start}.flex-items-lg-middle{align-items:center}.flex-items-lg-bottom{align-items:flex-end}}@media(min-width:992px){.flex-lg-top{align-self:flex-start}.flex-lg-middle{align-self:center}.flex-lg-bottom{align-self:flex-end}}@media(min-width:992px){.flex-items-lg-left{justify-content:flex-start}.flex-items-lg-center{justify-content:center}.flex-items-lg-right{justify-content:flex-end}.flex-items-lg-around{justify-content:space-around}.flex-items-lg-between{justify-content:space-between}}@media(min-width:1200px){.flex-xl-first{order:-1}.flex-xl-last{order:1}}@media(min-width:1200px){.flex-items-xl-top{align-items:flex-start}.flex-items-xl-middle{align-items:center}.flex-items-xl-bottom{align-items:flex-end}}@media(min-width:1200px){.flex-xl-top{align-self:flex-start}.flex-xl-middle{align-self:center}.flex-xl-bottom{align-self:flex-end}}@media(min-width:1200px){.flex-items-xl-left{justify-content:flex-start}.flex-items-xl-center{justify-content:center}.flex-items-xl-right{justify-content:flex-end}.flex-items-xl-around{justify-content:space-around}.flex-items-xl-between{justify-content:space-between}}.tag-default{color:#fff!important;font-size:.8rem;padding-top:.1rem;padding-bottom:.1rem;white-space:nowrap;margin-right:3px;margin-bottom:.2rem;display:inline-block}.tag-default:hover{text-decoration:none}.tag-default.tag-outline{border:1px solid #ddd;color:#aaa!important;background:0 0!important}ul.tag-list{padding-left:0!important;display:inline-block;list-style:none!important}ul.tag-list li{display:inline-block!important}.navbar-brand{font-family:titillium web,sans-serif;font-size:1.5rem!important;padding-top:0!important;margin-right:2rem!important;color:#5ED9C3!important}.nav-link .user-pic{height:26px;border-radius:50px;float:left;margin-right:5px}.nav-link:hover{transition:.1s all}.nav-pills.outline-active .nav-link{border-radius:0;border:none;border-bottom:2px solid transparent;background:0 0;color:#aaa}.nav-pills.outline-active .nav-link:hover{color:#555}.nav-pills.outline-active .nav-link.active{background:#fff!important;border-bottom:2px solid #5ED9C3!important;color:#5ED9C3!important}footer{background:#f3f3f3;margin-top:3rem;padding:1rem 0;position:absolute;bottom:0;width:100%}footer .logo-font{vertical-align:middle}footer .attribution{vertical-align:middle;margin-left:10px;font-size:.8rem;color:#bbb;font-weight:300}.error-messages{color:#b85c5c!important;font-weight:700}.banner{color:#fff;background:#333;padding:2rem;margin-bottom:2rem}.banner h1{text-shadow:0 1px 3px rgba(0,0,0,.3);margin-bottom:0}.container.page{margin-top:1.5rem}.preview-link{color:inherit!important}.preview-link:hover{text-decoration:inherit!important}.article-meta{display:block;position:relative;font-weight:300}.article-meta img{display:inline-block;vertical-align:middle;height:32px;width:32px;border-radius:30px}.article-meta .info{margin:0 1.5rem 0 .3rem;display:inline-block;vertical-align:middle;line-height:1rem}.article-meta .info .author{display:block;font-weight:500!important}.article-meta .info .date{color:#bbb;font-size:.8rem;display:block}.article-preview{border-top:1px solid rgba(0,0,0,.1);padding:1.5rem 0}.article-preview .article-meta{margin:0 0 1rem}.article-preview .preview-link h1{font-weight:600!important;font-size:1.5rem!important;margin-bottom:3px}.article-preview .preview-link p{font-weight:300;font-size:24px;color:#999;margin-bottom:15px;font-size:1rem;line-height:1.3rem}.article-preview .preview-link span{max-width:30%;font-size:.8rem;font-weight:300;color:#bbb;vertical-align:middle}.article-preview .preview-link ul{float:right;max-width:50%;vertical-align:top}.article-preview .preview-link ul li{font-weight:300;font-size:.8rem!important;padding-top:0!important;padding-bottom:0!important}.btn .counter{font-size:.8rem!important}.home-page .banner{background:#5ED9C3;box-shadow:inset 0 8px 8px -8px rgba(0,0,0,.3),inset 0 -8px 8px -8px rgba(0,0,0,.3)}.home-page .banner p{color:#fff;text-align:center;font-size:1.5rem;font-weight:300!important;margin-bottom:0}.home-page .banner h1{text-shadow:0 1px 3px rgba(0,0,0,.3);font-weight:700!important;text-align:center;font-size:3.5rem;padding-bottom:.5rem}.home-page .feed-toggle{margin-bottom:-1px}.home-page .sidebar{padding:5px 10px 10px;background:#f3f3f3;border-radius:4px}.home-page .sidebar p{margin-bottom:.2rem}.article-page .banner{padding:2rem 0}.article-page .banner h1{font-size:2.8rem;font-weight:600}.article-page .banner .btn{opacity:.8}.article-page .banner .btn:hover{transition:.1s all;opacity:1}.article-page .banner .article-meta{margin:2rem 0 0}.article-page .banner .article-meta .author{color:#fff}.article-page .article-content p{font-family:'source serif pro',serif;font-size:1.2rem;line-height:1.8rem;margin-bottom:2rem}.article-page .article-content h1,.article-page .article-content h2,.article-page .article-content h3,.article-page .article-content h4,.article-page .article-content h5,.article-page .article-content h6{font-weight:500!important;margin:1.6rem 0 1rem}.article-page .article-actions{text-align:center;margin:1.5rem 0 3rem}.article-page .article-actions .article-meta .info{text-align:left}.article-page .comment-form .card-block{padding:0}.article-page .comment-form .card-block textarea{border:0;padding:1.25rem}.article-page .comment-form .card-footer .btn{font-weight:700;float:right}.article-page .comment-form .card-footer .comment-author-img{position:absolute;top:110px;height:30px;width:30px}.article-page .card{border:1px solid #e5e5e5;box-shadow:none!important}.article-page .card .card-footer{border-top:1px solid #e5e5e5;box-shadow:none!important;font-size:.8rem;font-weight:300}.article-page .card .comment-author-img{display:inline-block;vertical-align:middle;height:20px;width:20px;border-radius:30px}.article-page .card .comment-author{display:inline-block;vertical-align:middle}.article-page .card .date-posted{display:inline-block;vertical-align:middle;margin-left:5px;color:#bbb}.article-page .card .mod-options{float:right;color:#333;font-size:1rem}.article-page .card .mod-options i{margin-left:5px;opacity:.6;cursor:pointer}.article-page .card .mod-options i:hover{opacity:1}.profile-page .user-info{text-align:center;background:#f3f3f3;padding:2rem 0 1rem}.profile-page .user-info .user-img{width:100px;height:100px;border-radius:100px;margin-bottom:1rem}.profile-page .user-info h4{font-weight:700}.profile-page .user-info p{margin:0 auto .5rem;color:#aaa;max-width:450px;font-weight:300}.profile-page .user-info .action-btn{float:right;color:#999;border:1px solid #999}.profile-page .articles-toggle{margin:1.5rem 0 -1px}.editor-page .tag-list i{font-size:.6rem;margin-right:5px;cursor:pointer} \ No newline at end of file diff --git a/te-frontend/src/agent.js b/te-frontend/src/agent.js deleted file mode 100644 index 29d7e1430625b84242a1d6c26334cc75a5d70514..0000000000000000000000000000000000000000 --- a/te-frontend/src/agent.js +++ /dev/null @@ -1,99 +0,0 @@ -import superagentPromise from 'superagent-promise'; -import _superagent from 'superagent'; - -const superagent = superagentPromise(_superagent, global.Promise); - -const API_ROOT = process.env.REACT_APP_API_ROOT || 'https://conduit.productionready.io/api'; - -const encode = encodeURIComponent; -const responseBody = res => res.body; - -let token = null; -const tokenPlugin = req => { - if (token) { - req.set('authorization', `Token ${token}`); - } -} - -const requests = { - del: (url, body) => - superagent.del(`${API_ROOT}${url}`, body).use(tokenPlugin).then(responseBody), - get: url => - superagent.get(`${API_ROOT}${url}`).use(tokenPlugin).then(responseBody), - put: (url, body) => - superagent.put(`${API_ROOT}${url}`, body).use(tokenPlugin).then(responseBody), - post: (url, body) => - superagent.post(`${API_ROOT}${url}`, body).use(tokenPlugin).then(responseBody) -}; - -const Auth = { - current: () => - requests.get('/user'), - login: (email, password) => - requests.post('/users/login', { user: { email, password } }), - register: (username, email, password) => - requests.post('/users', { user: { username, email, password } }), - save: user => - requests.put('/user', { user }), - delete: user => - requests.del('/user', { user }) -}; - -const Tags = { - getAll: () => requests.get('/tags') -}; - -//feed offset was 0 -const limit = (count, p) => `limit=${count}&offset=${p ? p * count : 0}`; -const omitSlug = article => Object.assign({}, article, { slug: undefined }) -const Articles = { - all: page => - requests.get(`/articles?${limit(10, page)}`), - byAuthor: (author, page) => - requests.get(`/articles?author=${encode(author)}&${limit(5, page)}`), - byTag: (tag, page) => - requests.get(`/articles?tag=${encode(tag)}&${limit(10, page)}`), - del: slug => - requests.del(`/articles/${slug}`), - favorite: slug => - requests.post(`/articles/${slug}/favorite`), - favoritedBy: (author, page) => - requests.get(`/articles?favorited=${encode(author)}&${limit(5, page)}`), - feed: (page) => - requests.get(`/articles/feed?${limit(10, page)}`), - get: slug => - requests.get(`/articles/${slug}`), - unfavorite: slug => - requests.del(`/articles/${slug}/favorite`), - update: article => - requests.put(`/articles/${article.slug}`, { article: omitSlug(article) }), - create: article => - requests.post('/articles', { article }) -}; - -const Comments = { - create: (slug, comment) => - requests.post(`/articles/${slug}/comments`, { comment }), - delete: (slug, commentId) => - requests.del(`/articles/${slug}/comments/${commentId}`), - forArticle: slug => - requests.get(`/articles/${slug}/comments`) -}; - -const Profile = { - follow: username => - requests.post(`/profiles/${username}/follow`), - get: username => - requests.get(`/profiles/${username}`), - unfollow: username => - requests.del(`/profiles/${username}/follow`) -}; - -export default { - Articles, - Auth, - Comments, - Profile, - Tags, - setToken: _token => { token = _token; } -}; diff --git a/te-frontend/src/components/App.js b/te-frontend/src/components/App.js deleted file mode 100644 index 8a8ec8a23b958c83e7ff25cfb8b90f54edd99e80..0000000000000000000000000000000000000000 --- a/te-frontend/src/components/App.js +++ /dev/null @@ -1,90 +0,0 @@ -import agent from '../agent'; -import Header from './Header'; -import React from 'react'; -import { connect } from 'react-redux'; -import { APP_LOAD, REDIRECT } from '../constants/actionTypes'; -import { Route, Switch } from 'react-router-dom'; -import Article from '../components/Article'; -import Editor from '../components/Editor'; -import edit from '../components/edit'; -import Home from '../components/Home'; -import Login from '../components/Login'; -import Profile from '../components/Profile'; -import ProfileFavorites from '../components/ProfileFavorites'; -import Register from '../components/Register'; -import Settings from '../components/Settings'; -import { store } from '../store'; -import { push } from 'react-router-redux'; -import './App.css'; -import Banner from './Home/Banner'; - -const mapStateToProps = state => { - return { - appLoaded: state.common.appLoaded, - appName: state.common.appName, - currentUser: state.common.currentUser, - redirectTo: state.common.redirectTo - }}; - -const mapDispatchToProps = dispatch => ({ - onLoad: (payload, token) => - dispatch({ type: APP_LOAD, payload, token, skipTracking: true }), - onRedirect: () => - dispatch({ type: REDIRECT }) -}); - -class App extends React.Component { - componentWillReceiveProps(nextProps) { - if (nextProps.redirectTo) { - // this.context.router.replace(nextProps.redirectTo); - store.dispatch(push(nextProps.redirectTo)); - this.props.onRedirect(); - } - } - - componentWillMount() { - const token = window.localStorage.getItem('jwt'); - if (token) { - agent.setToken(token); - } - - this.props.onLoad(token ? agent.Auth.current() : null, token); - } - - render() { - if (this.props.appLoaded) { - return ( - <div> - <Header - appName={this.props.appName} - currentUser={this.props.currentUser} /> - <Banner/> - <Switch> - <Route exact path="/" component={Home}/> - <Route path="/login" component={Login} /> - <Route path="/register" component={Register} /> - <Route path="/edit/:slug" component={edit} /> - <Route path="/editor" component={Editor} /> - <Route path="/article/:id" component={Article} /> - <Route path="/settings" component={Settings} /> - <Route path="/@:username/favorites" component={ProfileFavorites} /> - <Route path="/@:username" component={Profile} key={Math.random()}/> - </Switch> - </div> - ); - } - return ( - <div className=""> - <Header - appName={this.props.appName} - currentUser={this.props.currentUser} /> - </div> - ); - } -} - -// App.contextTypes = { -// router: PropTypes.object.isRequired -// }; - -export default connect(mapStateToProps, mapDispatchToProps)(App); diff --git a/te-frontend/src/components/Article/ArticleActions.js b/te-frontend/src/components/Article/ArticleActions.js deleted file mode 100644 index f1b5f5ba044b8f6418a53e825252d8124709598b..0000000000000000000000000000000000000000 --- a/te-frontend/src/components/Article/ArticleActions.js +++ /dev/null @@ -1,62 +0,0 @@ -import { Link } from 'react-router-dom'; -import React from 'react'; -import agent from '../../agent'; -import { connect } from 'react-redux'; -import { DELETE_ARTICLE } from '../../constants/actionTypes'; -import swal from 'sweetalert'; - -const mapDispatchToProps = dispatch => ({ - onClickDelete: payload => - dispatch({ type: DELETE_ARTICLE, payload }) -}); - -const ArticleActions = props => { - const article = props.article; - const del = () => { - props.onClickDelete(agent.Articles.del(article.slug)) - }; - - function deleteArticle() { - swal({ - title: "Are you sure?", - text: "Article and comments will be deleted permanently!", - buttons: true, - dangerMode: true, - }) - .then((willDelete) => { - if (willDelete) { - swal("Your article has been deleted!", { - icon: "success", - content: del() - }); - } else { - swal("Canceled!"); - } - }); - } - - if (props.canModify) { - return ( - <span> - - <Link - to={`/edit/${article.slug}`} - className="btn btn-outline-secondary btn-sm"> - <i className="ion-edit"></i> Edit Article - </Link> - - <button className="btn btn-outline-danger btn-sm" onClick={()=>deleteArticle()} style={{marginLeft: 10}}> - <i className="ion-trash-a"></i> Delete Article - </button> - - </span> - ); - } - - return ( - <span> - </span> - ); -}; - -export default connect(() => ({}), mapDispatchToProps)(ArticleActions); diff --git a/te-frontend/src/components/Article/ArticleMeta.js b/te-frontend/src/components/Article/ArticleMeta.js deleted file mode 100644 index c2f490b8d397957ad2f22211e628c81063b28096..0000000000000000000000000000000000000000 --- a/te-frontend/src/components/Article/ArticleMeta.js +++ /dev/null @@ -1,34 +0,0 @@ -import ArticleActions from './ArticleActions'; -import { Link } from 'react-router-dom'; -import React from 'react'; -import { TwitterShareButton } from "react-share"; - -const ArticleMeta = props => { - const article = props.article; - const link = `${window.location.href}` - - return ( - <div className="article-meta" > - <Link to={`/@${article.author.username}`}> - <img src={article.author.image} alt={article.author.username} - onError={e => { e.currentTarget.src = "https://static.productionready.io/images/smiley-cyrus.jpg"; }}/> - </Link> - - <div className="info"> - <Link to={`/@${article.author.username}`} className="author"> - {article.author.username} - </Link> - <span className="date"> - {new Date(article.createdAt).toLocaleString()} - </span> - </div> - <ArticleActions canModify={props.canModify} article={article}/> - <TwitterShareButton url={link} style={{alignItems: 'right'}}> - {<a class="twitter-share-button" data-show-count="false" target="_blank" style={{margin: 10}}> - <i className="ion-social-twitter"></i>Tweet</a>} - </TwitterShareButton> - </div> - ); -}; - -export default ArticleMeta; diff --git a/te-frontend/src/components/Article/Comment.js b/te-frontend/src/components/Article/Comment.js deleted file mode 100644 index adc2a66b0f6698f01298749a3ff365a696cd811a..0000000000000000000000000000000000000000 --- a/te-frontend/src/components/Article/Comment.js +++ /dev/null @@ -1,35 +0,0 @@ -import DeleteButton from './DeleteButton'; -import { Link } from 'react-router-dom'; -import React from 'react'; - -const Comment = props => { - const comment = props.comment; - const show = props.currentUser && - props.currentUser.username === comment.author.username; - return ( - <div className="card"> - <div className="card-block"> - <p className="card-text-mylly">{comment.body}</p> - </div> - <div className="card-footer"> - <Link - to={`/@${comment.author.username}`} - className="comment-author"> - <img src={comment.author.image} className="comment-author-img" alt={comment.author.username} /> - </Link> - - <Link - to={`/@${comment.author.username}`} - className="comment-author"> - {comment.author.username} - </Link> - <span className="date-posted"> - {new Date(comment.createdAt).toLocaleString()} - </span> - <DeleteButton show={show} slug={props.slug} commentId={comment.id} /> - </div> - </div> - ); -}; - -export default Comment; diff --git a/te-frontend/src/components/Article/CommentContainer.js b/te-frontend/src/components/Article/CommentContainer.js deleted file mode 100644 index 625194ecdf4b0857b83201bdba1845015217d997..0000000000000000000000000000000000000000 --- a/te-frontend/src/components/Article/CommentContainer.js +++ /dev/null @@ -1,40 +0,0 @@ -import CommentInput from './CommentInput'; -import CommentList from './CommentList'; -import { Link } from 'react-router-dom'; -import React from 'react'; - -const CommentContainer = props => { - if (props.currentUser) { - return ( - <div className="col-xs-12 col-md-8 offset-md-2"> - <div> - <list-errors errors={props.errors}></list-errors> - <CommentInput slug={props.slug} currentUser={props.currentUser} /> - </div> - - <CommentList - comments={props.comments} - slug={props.slug} - currentUser={props.currentUser} /> - </div> - ); - } else { - return ( - <div className="col-xs-12 col-md-8 offset-md-2"> - <p> - <Link to="/login">Sign in</Link> - or - <Link to="/register">sign up</Link> - to add comments on this article. - </p> - - <CommentList - comments={props.comments} - slug={props.slug} - currentUser={props.currentUser} /> - </div> - ); - } -}; - -export default CommentContainer; diff --git a/te-frontend/src/components/Article/CommentInput.js b/te-frontend/src/components/Article/CommentInput.js deleted file mode 100644 index 91ca5a1e66063185ad0295a2169016316c22e549..0000000000000000000000000000000000000000 --- a/te-frontend/src/components/Article/CommentInput.js +++ /dev/null @@ -1,71 +0,0 @@ -import React from 'react'; -import agent from '../../agent'; -import { connect } from 'react-redux'; -import { ADD_COMMENT } from '../../constants/actionTypes'; -import BadWords from '../Badwords'; - -const mapDispatchToProps = dispatch => ({ - onSubmit: payload => - dispatch({ type: ADD_COMMENT, payload }) -}); - -class CommentInput extends React.Component { - constructor() { - super(); - this.state = { - body: '' - }; - - this.setBody = ev => { - this.setState({ body: ev.target.value }); - }; - - this.createComment = ev => { - if (this.state.body < 1) { - ev.preventDefault() - return null - } - ev.preventDefault(); - const payload = agent.Comments.create(this.props.slug, - { body: this.state.body }); - this.setState({ body: '' }); - this.props.onSubmit(payload); - }; - } - - render() { - return ( - <form className="card comment-form" onSubmit={this.createComment}> - <div className="card-block"> - <textarea className="form-control" - placeholder="Write a comment..." - maxlength="1000" - value={this.state.body} - onChange={this.setBody} - rows="3" - onKeyDown={(e) => { - if (e.keyCode === 9) e.preventDefault(); - }}> - </textarea> - </div> - - <div className="card-footer"> - <img - src={this.props.currentUser.image} - className="comment-author-img-mylly" - alt={this.props.currentUser.username} - onError={e => { e.currentTarget.src = "https://static.productionready.io/images/smiley-cyrus.jpg"; }}/> - <BadWords comment={this.state.body}/> - <button - className="btn-primary-mylly" - id="publish" - type="submit"> - Post Comment - </button> - </div> - </form> - ); - } -} - -export default connect(() => ({}), mapDispatchToProps)(CommentInput); diff --git a/te-frontend/src/components/Article/CommentList.js b/te-frontend/src/components/Article/CommentList.js deleted file mode 100644 index b62889de8460880583f7da64d5579be4c03f4111..0000000000000000000000000000000000000000 --- a/te-frontend/src/components/Article/CommentList.js +++ /dev/null @@ -1,22 +0,0 @@ -import Comment from './Comment'; -import React from 'react'; - -const CommentList = props => { - return ( - <div> - { - props.comments.map(comment => { - return ( - <Comment - comment={comment} - currentUser={props.currentUser} - slug={props.slug} - key={comment.id} /> - ); - }) - } - </div> - ); -}; - -export default CommentList; diff --git a/te-frontend/src/components/Article/DeleteButton.js b/te-frontend/src/components/Article/DeleteButton.js deleted file mode 100644 index 59d70bd02b4dfa33dc86dfda5e277e0a562dbc09..0000000000000000000000000000000000000000 --- a/te-frontend/src/components/Article/DeleteButton.js +++ /dev/null @@ -1,46 +0,0 @@ -import React from 'react'; -import agent from '../../agent'; -import { connect } from 'react-redux'; -import { DELETE_COMMENT } from '../../constants/actionTypes'; -import swal from 'sweetalert'; - -const mapDispatchToProps = dispatch => ({ - onClick: (payload, commentId) => - dispatch({ type: DELETE_COMMENT, payload, commentId }) -}); - -const DeleteButton = props => { - const del = () => { - const payload = agent.Comments.delete(props.slug, props.commentId); - props.onClick(payload, props.commentId); - }; - - function deleteComment() { - swal({ - title: "Are you sure?", - text: "Comment will be deleted permanently!", - buttons: true, - dangerMode: true, - }) - .then((willDelete) => { - if (willDelete) { - swal("Your comment has been deleted!", { - icon: "success", - content: del() - }); - } else { - swal("Canceled!"); - } - }); - } - if (props.show) { - return ( - <span className="mod-options"> - <i className="ion-trash-a" onClick={()=>deleteComment()}></i> - </span> - ); - } - return null; -}; - -export default connect(() => ({}), mapDispatchToProps)(DeleteButton); diff --git a/te-frontend/src/components/Article/index.js b/te-frontend/src/components/Article/index.js deleted file mode 100644 index 23981143a4bb74c1c7e9688d0493b339b57c4e66..0000000000000000000000000000000000000000 --- a/te-frontend/src/components/Article/index.js +++ /dev/null @@ -1,98 +0,0 @@ -import ArticleMeta from './ArticleMeta'; -import CommentContainer from './CommentContainer'; -import React from 'react'; -import agent from '../../agent'; -import { connect } from 'react-redux'; -import marked from 'marked'; -import { ARTICLE_PAGE_LOADED, ARTICLE_PAGE_UNLOADED } from '../../constants/actionTypes'; - -const mapStateToProps = state => ({ - ...state.article, - currentUser: state.common.currentUser -}); - -const mapDispatchToProps = dispatch => ({ - onLoad: payload => - dispatch({ type: ARTICLE_PAGE_LOADED, payload }), - onUnload: () => - dispatch({ type: ARTICLE_PAGE_UNLOADED }) -}); - -class Article extends React.Component { - componentWillMount() { - this.props.onLoad(Promise.all([ - agent.Articles.get(this.props.match.params.id), - agent.Comments.forArticle(this.props.match.params.id) - ])); - } - - componentWillUnmount() { - this.props.onUnload(); - } - - render() { - if (!this.props.article) { - return null; - } - - const markup = { __html: marked(this.props.article.body, { sanitize: true }) }; - const canModify = this.props.currentUser && - this.props.currentUser.username === this.props.article.author.username; - return ( - <div className="article-page"> - - <div className="banner"> - <div className="container"> - - <h1 className="article-title-mylly">{this.props.article.title}</h1> - <h5 style={{ color: '#80ffee' }}>{this.props.article.description}</h5> - <ArticleMeta - article={this.props.article} - canModify={canModify} /> - - </div> - </div> - - <div className="container page"> - - <div className="row article-content"> - <div className="col-xs-12"> - - <div className="article-text-mylly" dangerouslySetInnerHTML={markup}></div> - - <ul className="tag-list"> - { - this.props.article.tagList.map(tag => { - return ( - <li - className="tag-default tag-pill tag-outline" - key={tag}> - {tag} - </li> - ); - }) - } - </ul> - - </div> - </div> - - <hr /> - - <div className="article-actions"> - </div> - - <div className="row"> - <CommentContainer - comments={this.props.comments || []} - errors={this.props.commentErrors} - slug={this.props.match.params.id} - currentUser={this.props.currentUser} /> - </div> - </div> - </div> - ); - } -} - -export default connect(mapStateToProps, mapDispatchToProps)(Article); diff --git a/te-frontend/src/components/ArticleList.js b/te-frontend/src/components/ArticleList.js deleted file mode 100644 index 1c17836d66dae3ac6b2f506354aaadacf401253c..0000000000000000000000000000000000000000 --- a/te-frontend/src/components/ArticleList.js +++ /dev/null @@ -1,38 +0,0 @@ -import ArticlePreview from './ArticlePreview'; -import ListPagination from './ListPagination'; -import React from 'react'; - -const ArticleList = props => { - if (!props.articles) { - return ( - <div className="article-preview">Loading...</div> - ); - } - - if (props.articles.length === 0) { - return ( - <div className="article-preview"> - No articles are here... yet. - </div> - ); - } - - return ( - <div> - { - props.articles.map(article => { - return ( - <ArticlePreview article={article} key={article.slug} /> - ); - }) - } - - <ListPagination - pager={props.pager} - articlesCount={props.articlesCount} - currentPage={props.currentPage} /> - </div> - ); -}; - -export default ArticleList; diff --git a/te-frontend/src/components/ArticlePreview.js b/te-frontend/src/components/ArticlePreview.js deleted file mode 100644 index 2ac1cfc7f5b87897e8291d2c86d6427b8056423d..0000000000000000000000000000000000000000 --- a/te-frontend/src/components/ArticlePreview.js +++ /dev/null @@ -1,90 +0,0 @@ -import React from 'react'; -import { Link } from 'react-router-dom'; -import agent from '../agent'; -import { connect } from 'react-redux'; -import { ARTICLE_FAVORITED, ARTICLE_UNFAVORITED } from '../constants/actionTypes'; -import swal from 'sweetalert'; - -const FAVORITED_CLASS = 'btn btn-sm btn-primary'; -const NOT_FAVORITED_CLASS = 'btn btn-sm btn-outline-primary'; - -const mapStateToProps = state => ({ - ...state.articleList, - token: state.common.token -}); - -const mapDispatchToProps = dispatch => ({ - favorite: slug => dispatch({ - type: ARTICLE_FAVORITED, - payload: agent.Articles.favorite(slug) - }), - unfavorite: slug => dispatch({ - type: ARTICLE_UNFAVORITED, - payload: agent.Articles.unfavorite(slug) - }) -}); - -const ArticlePreview = props => { - const article = props.article; - const favoriteButtonClass = article.favorited ? - FAVORITED_CLASS : - NOT_FAVORITED_CLASS; - - const handleClick = ev => { - if (props.token) { - ev.preventDefault(); - if (article.favorited) { - props.unfavorite(article.slug); - } else { - props.favorite(article.slug); - } - } else { - swal("Sign in to favorite article", {buttons: false, timer: 1800}) - } - }; - - return ( - <div className="article-preview" style={{marginBottom:5}}> - <div className="article-meta"> - <Link to={`/@${article.author.username}`}> - <img src={article.author.image} alt={article.author.username} - onError={e => { e.currentTarget.src = "https://static.productionready.io/images/smiley-cyrus.jpg"; }}/> - </Link> - - <div className="info"> - <Link className="author" to={`/@${article.author.username}`}> - {article.author.username} - </Link> - <span className="date"> - {new Date(article.createdAt).toLocaleString()} - </span> - </div> - - <div className="pull-xs-right"> - <button className={favoriteButtonClass} onClick={handleClick}> - <i className="ion-heart"></i> {article.favoritesCount} - </button> - </div> - </div> - - <Link to={`/article/${article.slug}`} className="preview-link"> - <h1 className="preview-link-mylly">{article.title}</h1> - <p className="preview-link-mylly">{article.description}</p> - <span>Read more...</span> - <ul className="tag-list"> - { - article.tagList.map(tag => { - return ( - <li className="tag-default tag-pill tag-outline" key={tag}> - {tag} - </li> - ) - }) - } - </ul> - </Link> - </div> - ); -} - -export default connect(mapStateToProps, mapDispatchToProps)(ArticlePreview); diff --git a/te-frontend/src/components/Editor.js b/te-frontend/src/components/Editor.js deleted file mode 100644 index a912818e453a3aa7b6ec38b58561bfe185233879..0000000000000000000000000000000000000000 --- a/te-frontend/src/components/Editor.js +++ /dev/null @@ -1,223 +0,0 @@ -import ListErrors from './ListErrors'; -import React from 'react'; -import agent from '../agent'; -import { connect } from 'react-redux'; -import Badwords from './Badwords'; -import { - ADD_TAG, - EDITOR_PAGE_LOADED, - REMOVE_TAG, - ARTICLE_SUBMITTED, - EDITOR_PAGE_UNLOADED, - UPDATE_FIELD_EDITOR -} from '../constants/actionTypes'; - - -const mapStateToProps = state => ({ - ...state.editor -}); - -const mapDispatchToProps = dispatch => ({ - onAddTag: () => - dispatch({ type: ADD_TAG }), - onLoad: payload => - dispatch({ type: EDITOR_PAGE_LOADED, payload }), - onRemoveTag: tag => - dispatch({ type: REMOVE_TAG, tag }), - onSubmit: payload => - dispatch({ type: ARTICLE_SUBMITTED, payload }), - onUnload: payload => - dispatch({ type: EDITOR_PAGE_UNLOADED }), - onUpdateField: (key, value) => - dispatch({ type: UPDATE_FIELD_EDITOR, key, value }) -}); - - -class Editor extends React.Component { - constructor() { - super(); - - const updateFieldEvent = - key => ev => this.props.onUpdateField(key, ev.target.value); - this.changeTitle = updateFieldEvent('title'); - this.changeDescription = updateFieldEvent('description'); - this.changeBody = updateFieldEvent('body'); - this.changeTagInput = updateFieldEvent('tagInput'); - - const errors = (key, error) => this.props.onUpdateField(key, error); - - this.watchForEnter = ev => { - if (this.props.tagInput < 1 || (this.props.tagList || []).length > 3) { - errors('errors', {'Tags:': 'Empty tags not allowed'}); - return null - } - if (ev.keyCode === 13) { - ev.preventDefault(); - this.props.onAddTag(); - errors('errors', {}); - } - }; - - this.clickToAdd = ev => { - if (this.props.tagInput < 1 || (this.props.tagList || []).length > 3) { - errors('errors', {'Tags:': 'Empty tags not allowed'}); - return null - } else { - ev.preventDefault(); - this.props.onAddTag(); - errors('errors', {}); - } - }; - - this.removeTagHandler = tag => () => { - this.props.onRemoveTag(tag); - }; - - this.submitForm = ev => { - if (!this.props.title||!this.props.body||!this.props.description) { - errors('errors', {'Please,': 'fill title and body fields.'}); - } else { - ev.preventDefault(); - const article = { - title: this.props.title, - description: this.props.description, - body: this.props.body, - tagList: this.props.tagList - }; - const slug = { slug: this.props.articleSlug }; - const promise = this.props.articleSlug ? - agent.Articles.update(Object.assign(article, slug)) : - agent.Articles.create(article); - - this.props.onSubmit(promise); - }}; - } - - componentWillReceiveProps(nextProps) { - if (this.props.match.params.slug !== nextProps.match.params.slug) { - if (nextProps.match.params.slug) { - this.props.onUnload(); - return this.props.onLoad(agent.Articles.get(this.props.match.params.slug)); - } - this.props.onLoad(null); - } - } - - componentWillMount() { - if (this.props.match.params.slug) { - return this.props.onLoad(agent.Articles.get(this.props.match.params.slug)); - } - this.props.onLoad(null); - } - - componentWillUnmount() { - this.props.onUnload(); - } - - render() { - return ( - <div className="editor-page"> - <div className="container page"> - <div className="row"> - <div className="col-md-10 offset-md-1 col-xs-12"> - - <form> - <fieldset> - - <fieldset className="form-group"> - <input - className="form-control form-control-lg" - type="text" - placeholder="Article Title" - maxlength="100" - value={this.props.title || ""} - onChange={this.changeTitle} /> - </fieldset> - - <fieldset className="form-group"> - <input - className="form-control" - type="text" - placeholder="What's this article about?" - maxlength="140" - value={this.props.description || ""} - onChange={this.changeDescription} /> - </fieldset> - - <fieldset className="form-group"> - <textarea - className="form-control" - rows="8" - placeholder="Write your article (in markdown)" - maxlength="3000" - value={this.props.body || ""} - onChange={this.changeBody}> - </textarea> - </fieldset> - - <fieldset className="form-group"> - <input - className="form-control" - type="text" - placeholder="Enter tags" - maxlength="15" - value={this.props.tagInput ||""} - onChange={this.changeTagInput} - onKeyUp={this.watchForEnter} - onKeyDown={(e) => { - if (e.keyCode === 9) e.preventDefault(); - }}/> - <span onClick={this.clickToAdd} className="tag-enter">Press Enter or Click to Add</span> - - <div className="tag-list"> - { - (this.props.tagList || []).map(tag => { - return ( - <span className="tag-default tag-pill" key={tag}> - <i className="ion-close-round" - onClick={this.removeTagHandler(tag)}> - </i> - {tag} - </span> - ); - }) - } - </div> - </fieldset> - - <button - className="btn btn-lg pull-xs-right btn-danger m-l-1" - type="button" - onClick={() => this.props.history.goBack()}> - Cancel - </button> - - <button - className="btn btn-lg pull-xs-right btn-primary" - type="button" - id="publish" - disabled={false} - onClick={this.submitForm}> - Publish Article - </button> - - <Badwords title={this.props.title} - description={this.props.description} - body={this.props.body} - tags={this.props.tagInput} - tagList={this.props.tagList || []} - /> - - <ListErrors errors={this.props.errors}></ListErrors> - - </fieldset> - </form> - </div> - </div> - </div> - </div> - ); - } -} - -export default connect(mapStateToProps, mapDispatchToProps)(Editor); diff --git a/te-frontend/src/components/Header.js b/te-frontend/src/components/Header.js deleted file mode 100644 index de49981198854cdf0eb4321f149474b359186659..0000000000000000000000000000000000000000 --- a/te-frontend/src/components/Header.js +++ /dev/null @@ -1,94 +0,0 @@ -import React from 'react'; -import { Link } from 'react-router-dom'; -import logo from './images/logo-sm.svg' - - -const LoggedOutView = props => { - if (!props.currentUser) { - return ( - <ul className="nav navbar-nav pull-xs-right"> - - <li className="nav-item"> - <Link to="/" className="nav-link-mylly"> - Home - </Link> - </li> - - <li className="nav-item"> - <Link to="/login" className="nav-link-mylly"> - Sign in - </Link> - </li> - - <li className="nav-item"> - <Link to="/register" className="nav-link-mylly"> - Sign up - </Link> - </li> - - </ul> - ); - } - return null; -}; - -// li classname was "nav-item" -const LoggedInView = props => { - if (props.currentUser) { - return ( - <ul className="nav navbar-nav pull-xs-right"> - - <li className="nav-link-mylly"> - <Link to="/" className="nav-link-mylly"> - Home - </Link> - </li> - - <li className="nav-link-mylly"> - <Link to="/editor" className="nav-link-mylly"> - <i className="ion-compose"></i> New Post - </Link> - </li> - - <li className="nav-link-mylly"> - <Link to="/settings" className="nav-link-mylly"> - <i className="ion-gear-a"></i> Settings - </Link> - </li> - - <li className="nav-link-mylly"> - <Link - to={`/@${props.currentUser.username}`} - className="nav-link-mylly-user"> - <img src={props.currentUser.image} className="user-pic-mylly" title={props.currentUser.username} style={{marginLeft:10}}alt={props.currentUser.username} - onError={e => { e.currentTarget.src = "https://static.productionready.io/images/smiley-cyrus.jpg"; }}/> - </Link> - </li> - - </ul> - ); - } - - return null; -}; - -class Header extends React.Component { - render() { - return ( - <nav className="navbar-mylly"> - <div className="container-mylly"> - <Link to={{ pathname: "https://www.wimmalab.org/" }} target="_blank" className="navbar-brand-mylly"> - <img src={logo} alt="logo" height="20px"/> - {/* {this.props.appName.toLowerCase()} */} - </Link> - - <LoggedOutView currentUser={this.props.currentUser} /> - - <LoggedInView currentUser={this.props.currentUser} /> - </div> - </nav> - ); - } -} - -export default Header; diff --git a/te-frontend/src/components/Home/Banner.js b/te-frontend/src/components/Home/Banner.js deleted file mode 100644 index cc3d52b4d6294c785061b96c2d0fb48d85204a6b..0000000000000000000000000000000000000000 --- a/te-frontend/src/components/Home/Banner.js +++ /dev/null @@ -1,22 +0,0 @@ -import React from 'react'; -import logo from '../images/logo_wimma.png' -import { Link } from 'react-router-dom'; -import homehero from '../images/home-hero.png' - -const Banner = ({ appName, token }) => { - if (token) { - return null; - } - return ( // style changed to black - <div className="banner-mylly" style={{ backgroundImage: `url(${homehero})`}}> - <div className="container"> - <Link to="/"> - <img src={logo} alt="logo" height="100px"/> - </Link> - <p>A place to share your knowledge.</p> - </div> - </div> - ); -}; - -export default Banner; diff --git a/te-frontend/src/components/Home/MainView.js b/te-frontend/src/components/Home/MainView.js deleted file mode 100644 index c687523144b7bc367e2272ee0a5d6760650cef47..0000000000000000000000000000000000000000 --- a/te-frontend/src/components/Home/MainView.js +++ /dev/null @@ -1,96 +0,0 @@ -import ArticleList from '../ArticleList'; -import React from 'react'; -import agent from '../../agent'; -import { connect } from 'react-redux'; -import { CHANGE_TAB } from '../../constants/actionTypes'; - -const YourFeedTab = props => { - if (props.token) { - const clickHandler = ev => { - ev.preventDefault(); - props.onTabClick('feed', agent.Articles.feed, agent.Articles.feed()); - } - - return ( - <li className="nav-item"> - <a href="" - className={ props.tab === 'feed' ? 'nav-link active' : 'nav-link' } - onClick={clickHandler}> - Your Feed - </a> - </li> - ); - } - return null; -}; - -const GlobalFeedTab = props => { - const clickHandler = ev => { - ev.preventDefault(); - props.onTabClick('all', agent.Articles.all, agent.Articles.all()); - }; - return ( - <li className="nav-item"> - <a - href="" - className={ props.tab === 'all' ? 'nav-link active' : 'nav-link' } - onClick={clickHandler}> - Global Feed - </a> - </li> - ); -}; - -const TagFilterTab = props => { - if (!props.tag) { - return null; - } - - return ( - <li className="nav-item" style={{color:'blue'}}> - <a href="" className="nav-link active"> - <i className="ion-pound"></i> {props.tag} - </a> - </li> - ); -}; - -const mapStateToProps = state => ({ - ...state.articleList, - tags: state.home.tags, - token: state.common.token -}); - -const mapDispatchToProps = dispatch => ({ - onTabClick: (tab, pager, payload) => dispatch({ type: CHANGE_TAB, tab, pager, payload }) -}); - -const MainView = props => { - return ( - <div className="col-md-9"> - <div className="feed-toggle"> - <ul className="nav nav-pills outline-active"> - - <YourFeedTab - token={props.token} - tab={props.tab} - onTabClick={props.onTabClick} /> - - <GlobalFeedTab tab={props.tab} onTabClick={props.onTabClick} /> - - <TagFilterTab tag={props.tag} /> - - </ul> - </div> - - <ArticleList - pager={props.pager} - articles={props.articles} - loading={props.loading} - articlesCount={props.articlesCount} - currentPage={props.currentPage} /> - </div> - ); -}; - -export default connect(mapStateToProps, mapDispatchToProps)(MainView); diff --git a/te-frontend/src/components/Home/Tags.js b/te-frontend/src/components/Home/Tags.js deleted file mode 100644 index fb498ae678412054ca0000176bef3d2ff1fe6a98..0000000000000000000000000000000000000000 --- a/te-frontend/src/components/Home/Tags.js +++ /dev/null @@ -1,36 +0,0 @@ -import React from 'react'; -import agent from '../../agent'; - -const Tags = props => { - const tags = props.tags; - if (tags) { - return ( - <div className="tag-list"> - { - tags.map(tag => { - const handleClick = ev => { - ev.preventDefault(); - props.onClickTag(tag, page => agent.Articles.byTag(tag, page), agent.Articles.byTag(tag)); - }; - - return ( - <a - href="" - className="tag-default tag-pill" - key={tag} - onClick={handleClick}> - {tag} - </a> - ); - }) - } - </div> - ); - } else { - return ( - <div>Loading Tags...</div> - ); - } -}; - -export default Tags; diff --git a/te-frontend/src/components/Home/index.js b/te-frontend/src/components/Home/index.js deleted file mode 100644 index 0ed4ea7116d50ed58f1fc6a244c0830a54d226ce..0000000000000000000000000000000000000000 --- a/te-frontend/src/components/Home/index.js +++ /dev/null @@ -1,77 +0,0 @@ -// import Banner from './Banner'; -import MainView from './MainView'; -import React from 'react'; -import Tags from './Tags'; -import agent from '../../agent'; -import { connect } from 'react-redux'; -import { - HOME_PAGE_LOADED, - HOME_PAGE_UNLOADED, - APPLY_TAG_FILTER -} from '../../constants/actionTypes'; -import Pdf from '../Documents/Tietosuojaseloste.pdf'; - -const Promise = global.Promise; - -const mapStateToProps = state => ({ - ...state.home, - appName: state.common.appName, - token: state.common.token -}); - -const mapDispatchToProps = dispatch => ({ - onClickTag: (tag, pager, payload) => - dispatch({ type: APPLY_TAG_FILTER, tag, pager, payload }), - onLoad: (tab, pager, payload) => - dispatch({ type: HOME_PAGE_LOADED, tab, pager, payload }), - onUnload: () => - dispatch({ type: HOME_PAGE_UNLOADED }) -}); - -class Home extends React.Component { - componentWillMount() { - const tab = this.props.token ? 'feed' : 'all'; - const articlesPromise = this.props.token ? - agent.Articles.feed : - agent.Articles.all; - - this.props.onLoad(tab, articlesPromise, Promise.all([agent.Tags.getAll(), articlesPromise()])); - } - - componentWillUnmount() { - this.props.onUnload(); - } - - render() { - return ( - <div className="home-page"> - - {/* <Banner token={this.props.token} appName={this.props.appName} /> */} - - <div className="container page"> - <div className="row"> - <MainView /> - - <div className="col-md-3"> - <div className="sidebar"> - - <p>Popular Tags</p> - - <Tags - tags={this.props.tags} - onClickTag={this.props.onClickTag} /> - - </div> - </div> - - </div> - </div> - <div className="footer"> - <a href = {Pdf} target = "_blank"><p>Privacy policy</p></a> - </div> - </div> - ); - } -} - -export default connect(mapStateToProps, mapDispatchToProps)(Home); diff --git a/te-frontend/src/components/ListErrors.js b/te-frontend/src/components/ListErrors.js deleted file mode 100644 index 3b4002260340f547e70f426377ed56347ae89389..0000000000000000000000000000000000000000 --- a/te-frontend/src/components/ListErrors.js +++ /dev/null @@ -1,26 +0,0 @@ -import React from 'react'; - -class ListErrors extends React.Component { - render() { - const errors = this.props.errors; - if (errors) { - return ( - <ul className="error-messages"> - { - Object.keys(errors).map(key => { - return ( - <li key={key}> - {key} {errors[key]} - </li> - ); - }) - } - </ul> - ); - } else { - return null; - } - } -} - -export default ListErrors; diff --git a/te-frontend/src/components/ListPagination.js b/te-frontend/src/components/ListPagination.js deleted file mode 100644 index ba38a6e99932546c5adf2cec79965efa40125188..0000000000000000000000000000000000000000 --- a/te-frontend/src/components/ListPagination.js +++ /dev/null @@ -1,58 +0,0 @@ -import React from 'react'; -import agent from '../agent'; -import { connect } from 'react-redux'; -import { SET_PAGE } from '../constants/actionTypes'; - -const mapDispatchToProps = dispatch => ({ - onSetPage: (page, payload) => - dispatch({ type: SET_PAGE, page, payload }) -}); - -const ListPagination = props => { - if (props.articlesCount <= 10) { - return null; - } - - const range = []; - for (let i = 0; i < Math.ceil(props.articlesCount / 10); ++i) { - range.push(i); - } - - const setPage = page => { - if(props.pager) { - props.onSetPage(page, props.pager(page)); - }else { - props.onSetPage(page, agent.Articles.all(page)) - } - }; - - return ( - <nav> - <ul className="pagination"> - - { - range.map(v => { - const isCurrent = v === props.currentPage; - const onClick = ev => { - ev.preventDefault(); - setPage(v); - }; - return ( - <li - className={ isCurrent ? 'page-item active' : 'page-item' } - onClick={onClick} - key={v.toString()}> - - <a className="page-link" href="">{v + 1}</a> - - </li> - ); - }) - } - - </ul> - </nav> - ); -}; - -export default connect(() => ({}), mapDispatchToProps)(ListPagination); diff --git a/te-frontend/src/components/Login.js b/te-frontend/src/components/Login.js deleted file mode 100644 index a6e895a5ccd61810fb3fb7230a8f63c8249b6e37..0000000000000000000000000000000000000000 --- a/te-frontend/src/components/Login.js +++ /dev/null @@ -1,109 +0,0 @@ -import { Link } from 'react-router-dom'; -import ListErrors from './ListErrors'; -import React from 'react'; -import agent from '../agent'; -import { connect } from 'react-redux'; -import { - UPDATE_FIELD_AUTH, - LOGIN, - LOGIN_PAGE_UNLOADED -} from '../constants/actionTypes'; -import ShowIcon from '@material-ui/icons/Visibility'; -import ShowOffIcon from '@material-ui/icons/VisibilityOff'; - -const mapStateToProps = state => ({ ...state.auth }); - -const mapDispatchToProps = dispatch => ({ - onChangeEmail: value => - dispatch({ type: UPDATE_FIELD_AUTH, key: 'email', value }), - onChangePassword: value => - dispatch({ type: UPDATE_FIELD_AUTH, key: 'password', value }), - onSubmit: (email, password) => - dispatch({ type: LOGIN, payload: agent.Auth.login(email, password) }), - onUnload: () => - dispatch({ type: LOGIN_PAGE_UNLOADED }) -}); - -class Login extends React.Component { - constructor() { - super(); - this.state = { - password: true, - }; - this.changeEmail = ev => this.props.onChangeEmail(ev.target.value); - this.changePassword = ev => this.props.onChangePassword(ev.target.value); - this.submitForm = (email, password) => ev => { - ev.preventDefault(); - this.props.onSubmit(email, password); - }; - } - - componentWillUnmount() { - this.props.onUnload(); - } - - render() { - const email = this.props.email; - const password = this.props.password; - return ( - <div className="auth-page"> - <div className="container page"> - <div className="row"> - - <div className="col-md-6 offset-md-3 col-xs-12"> - <h1 className="text-xs-center">Sign In</h1> - <p className="text-xs-center"> - <Link to="/register"> - Need an account? - </Link> - </p> - - <form onSubmit={this.submitForm(email, password)}> - <fieldset> - - <fieldset className="form-group"> - <input - className="form-control form-control-lg" - type="email" - placeholder="Email" - value={email} - onChange={this.changeEmail} /> - </fieldset> - - <fieldset className="form-group"> - <input - className="form-control form-control-lg" - type={this.state.password ? "password":"text" } - placeholder="Password" - value={password} - onChange={this.changePassword} /> - <span - className="hideshowbtn" - style={{ top: 173 }} - onMouseDown={()=> this.setState({password:!this.state.password})}> - {this.state.password ? <ShowOffIcon/>:<ShowIcon/>} - </span> - </fieldset> - - - <button - className="btn btn-lg btn-primary pull-xs-right" - type="submit" - disabled={this.props.inProgress}> - Sign in - </button> - - <ListErrors errors={this.props.errors} /> - - </fieldset> - </form> - </div> - - </div> - </div> - </div> - ); - } -} - -export default connect(mapStateToProps, mapDispatchToProps)(Login); diff --git a/te-frontend/src/components/Profile.js b/te-frontend/src/components/Profile.js deleted file mode 100644 index 031026349af524fc5122ea733dd51d032e0d6fb3..0000000000000000000000000000000000000000 --- a/te-frontend/src/components/Profile.js +++ /dev/null @@ -1,188 +0,0 @@ -import ArticleList from './ArticleList'; -import React from 'react'; -import { Link } from 'react-router-dom'; -import agent from '../agent'; -import { connect } from 'react-redux'; -import { - FOLLOW_USER, - UNFOLLOW_USER, - PROFILE_PAGE_LOADED, - PROFILE_PAGE_UNLOADED, - LOGOUT -} from '../constants/actionTypes'; -import swal from 'sweetalert'; - -const FollowUserButton = props => { - if (props.isUser) { - return null; - } - - let classes = 'btn btn-sm action-btn'; - if (props.user.following) { - classes += ' btn-secondary'; - } else { - classes += ' btn-outline-secondary'; - } - - const handleClick = ev => { - if (props.isUser === null) { - swal("Sign in to follow user", {buttons: false, timer: 1900}) - } else { - ev.preventDefault(); - if (props.user.following) { - props.unfollow(props.user.username) - } else { - props.follow(props.user.username) - } - }}; - - return ( - <button - className={classes} - onClick={handleClick}> - <i className="ion-plus-round"></i> - - {props.user.following ? 'Unfollow' : 'Follow'} {props.user.username} - </button> - ); -}; - -const mapStateToProps = state => ({ - ...state.articleList, - currentUser: state.common.currentUser, - profile: state.profile -}); - -const mapDispatchToProps = dispatch => ({ - onClickLogout: () => dispatch({ type: LOGOUT }), - onFollow: username => dispatch({ - type: FOLLOW_USER, - payload: agent.Profile.follow(username) - }), - onLoad: payload => dispatch({ type: PROFILE_PAGE_LOADED, payload }), - onUnfollow: username => dispatch({ - type: UNFOLLOW_USER, - payload: agent.Profile.unfollow(username) - }), - onUnload: () => dispatch({ type: PROFILE_PAGE_UNLOADED }) -}); - -class Profile extends React.Component { - componentWillMount() { - this.props.onLoad(Promise.all([ - agent.Profile.get(this.props.match.params.username), - agent.Articles.byAuthor(this.props.match.params.username) - ])); - } - - componentWillUnmount() { - this.props.onUnload(); - } - - renderTabs() { - return ( - <ul className="nav nav-pills outline-active"> - <li className="nav-item"> - <Link - className="nav-link active" - to={`/@${this.props.profile.username}`}> - My Articles - </Link> - </li> - - <li className="nav-item"> - <Link - className="nav-link" - to={`/@${this.props.profile.username}/favorites`}> - Favorited Articles - </Link> - </li> - </ul> - ); - } - - render() { - - const EditProfileSettings = props => { - if (props.isUser) { - return ( - <div> - <Link - to="/settings" - className="btn btn-sm btn-outline-secondary action-btn"> - <i className="ion-gear-a"></i> Edit Profile Settings - </Link> - <button - className="btn btn-sm btn-outline-secondary action-btn" - style={{ - color: '#D56047', - borderColor: 'red', - marginRight:10}} - onClick={this.props.onClickLogout}> - <i className="ion-log-out"></i> Logout - </button> - </div> - ); - } - return null; - }; - - const profile = this.props.profile; - if (!profile) { - return null; - } - const isUser = this.props.currentUser && - this.props.profile.username === this.props.currentUser.username; - - return ( - <div className="profile-page"> - - <div className="user-info" > - <div className="container"> - <div className="row"> - <div className="col-xs-12 col-md-10 offset-md-1"> - - <img src={profile.image} className="user-img" alt={profile.username} - onError={e => { e.currentTarget.src = "https://static.productionready.io/images/smiley-cyrus.jpg"; }}/> - <h4>{profile.username}</h4> - <p>{profile.bio}</p> - - <EditProfileSettings isUser={isUser} /> - <FollowUserButton - isUser={isUser} - user={profile} - follow={this.props.onFollow} - unfollow={this.props.onUnfollow} - /> - - </div> - </div> - </div> - </div> - - <div className="container"> - <div className="row"> - - <div className="col-xs-12 col-md-10 offset-md-1"> - - <div className="articles-toggle"> - {this.renderTabs()} - </div> - - <ArticleList - pager={this.props.pager} - articles={this.props.articles} - articlesCount={this.props.articlesCount} - state={this.props.currentPage} /> - </div> - - </div> - </div> - - </div> - ); - } -} - -export default connect(mapStateToProps, mapDispatchToProps)(Profile); -export { Profile, mapStateToProps }; diff --git a/te-frontend/src/components/ProfileFavorites.js b/te-frontend/src/components/ProfileFavorites.js deleted file mode 100644 index 43ddbbe1897ad1aa505fa01c6079b2d6996fa529..0000000000000000000000000000000000000000 --- a/te-frontend/src/components/ProfileFavorites.js +++ /dev/null @@ -1,53 +0,0 @@ -import { Profile, mapStateToProps } from './Profile'; -import React from 'react'; -import { Link } from 'react-router-dom'; -import agent from '../agent'; -import { connect } from 'react-redux'; -import { - PROFILE_PAGE_LOADED, - PROFILE_PAGE_UNLOADED -} from '../constants/actionTypes'; - -const mapDispatchToProps = dispatch => ({ - onLoad: (pager, payload) => - dispatch({ type: PROFILE_PAGE_LOADED, pager, payload }), - onUnload: () => - dispatch({ type: PROFILE_PAGE_UNLOADED }) -}); - -class ProfileFavorites extends Profile { - componentWillMount() { - this.props.onLoad(page => agent.Articles.favoritedBy(this.props.match.params.username, page), Promise.all([ - agent.Profile.get(this.props.match.params.username), - agent.Articles.favoritedBy(this.props.match.params.username) - ])); - } - - componentWillUnmount() { - this.props.onUnload(); - } - - renderTabs() { - return ( - <ul className="nav nav-pills outline-active"> - <li className="nav-item"> - <Link - className="nav-link" - to={`/@${this.props.profile.username}`}> - My Articles - </Link> - </li> - - <li className="nav-item"> - <Link - className="nav-link active" - to={`/@${this.props.profile.username}/favorites`}> - Favorited Articles - </Link> - </li> - </ul> - ); - } -} - -export default connect(mapStateToProps, mapDispatchToProps)(ProfileFavorites); diff --git a/te-frontend/src/components/Register.js b/te-frontend/src/components/Register.js deleted file mode 100644 index 82f74155398dd518bf94cbbb4142b27fbfdf5049..0000000000000000000000000000000000000000 --- a/te-frontend/src/components/Register.js +++ /dev/null @@ -1,164 +0,0 @@ -import { Link } from 'react-router-dom'; -import ListErrors from './ListErrors'; -import React from 'react'; -import agent from '../agent'; -import { connect } from 'react-redux'; -import { - UPDATE_FIELD_AUTH, - REGISTER, - REGISTER_PAGE_UNLOADED -} from '../constants/actionTypes'; -import swal from 'sweetalert'; -import ShowIcon from '@material-ui/icons/Visibility'; -import ShowOffIcon from '@material-ui/icons/VisibilityOff'; - -const mapStateToProps = state => ({ ...state.auth }); - -const mapDispatchToProps = dispatch => ({ - onChangeEmail: value => - dispatch({ type: UPDATE_FIELD_AUTH, key: 'email', value }), - onChangePassword: value => - dispatch({ type: UPDATE_FIELD_AUTH, key: 'password', value }), - onConfirmPassword: value => - dispatch({ type: UPDATE_FIELD_AUTH, key: 'confirmPassword', value }), - onChangeUsername: value => - dispatch({ type: UPDATE_FIELD_AUTH, key: 'username', value }), - onSubmit: (username, email, password,confirmPassword) => { - const payload = agent.Auth.register(username, email, password,confirmPassword); - dispatch({ type: REGISTER, payload }) - }, - onUnload: () => - dispatch({ type: REGISTER_PAGE_UNLOADED }) -}); - -class Register extends React.Component { - constructor() { - super(); - this.state = { - password: true, - confirmPassword:true - }; - this.changeEmail = ev => this.props.onChangeEmail(ev.target.value); - this.changePassword = ev => this.props.onChangePassword(ev.target.value); - this.changeConfirmPassword = ev => this.props.onConfirmPassword(ev.target.value); - this.changeUsername = ev => this.props.onChangeUsername(ev.target.value); - this.submitForm = (username, email, password,confirmPassword) => ev => { - ev.preventDefault(); - - const regex = /^\S{4,}$/; - if (!username||!email||!password) { - swal("All fields required") - return null; - } - - if(!regex.test(password)){ - - swal("Password at least 4 digits! No whitespaces") - return null; - } - if(password !== confirmPassword ) { - - swal("Passwords does not match ") - return null; - } - this.props.onSubmit(username, email, password,confirmPassword); - } - } - - componentWillUnmount() { - this.props.onUnload(); - } - - render() { - const email = this.props.email; - const password = this.props.password; - const confirmPassword = this.props.confirmPassword; - const username = this.props.username; - - return ( - <div className="auth-page"> - <div className="container page"> - <div className="row"> - - <div className="col-md-6 offset-md-3 col-xs-12"> - <h1 className="text-xs-center">Sign Up</h1> - <p className="text-xs-center"> - <Link to="/login"> - Have an account? - </Link> - </p> - - - <form onSubmit={this.submitForm(username, email, password,confirmPassword)}> - <fieldset> - - <fieldset className="form-group"> - <input - pattern=".{1,}" - className="form-control form-control-lg" - type="text" - placeholder="Username" - value={this.props.username || ""} - onChange={this.changeUsername} /> - </fieldset> - - <fieldset className="form-group"> - <input - className="form-control form-control-lg" - type="email" - placeholder="Email" - value={this.props.email || ""} - onChange={this.changeEmail} /> - </fieldset> - - <fieldset className="form-group"> - <input - className="form-control form-control-lg" - type={this.state.password ? "password":"text" } - placeholder="Password" - value={this.props.password || ""} - onChange={this.changePassword} /> - <span - className="hideshowbtn" - style={{top: 240}} - onMouseDown={()=> this.setState({password:!this.state.password})}> - {this.state.password ? <ShowOffIcon/>:<ShowIcon/>} - </span> - </fieldset> - <fieldset className="form-group"> - <input - className="form-control form-control-lg" - type={this.state.confirmPassword ? "password":"text" } - placeholder="Confirm Password" - value={this.props.confirmPassword } - onChange={this.changeConfirmPassword} /> - <span - className="hideshowbtn" style={{top: 305}} - onMouseDown={()=> this.setState({confirmPassword:!this.state.confirmPassword})}> - {this.state.confirmPassword ? <ShowOffIcon/>:<ShowIcon/>} - </span> - </fieldset> - - <button - className="btn btn-lg btn-primary pull-xs-right" - type="submit" - disabled={this.props.inProgress - }> - Sign up - </button> - - <ListErrors errors={this.props.errors} /> - - - </fieldset> - </form> - </div> - - </div> - </div> - </div> - ); - } -} - -export default connect(mapStateToProps, mapDispatchToProps)(Register); diff --git a/te-frontend/src/components/Settings.js b/te-frontend/src/components/Settings.js deleted file mode 100644 index 63a70fa4623ca508d16d196cb7bb37f082dbd773..0000000000000000000000000000000000000000 --- a/te-frontend/src/components/Settings.js +++ /dev/null @@ -1,248 +0,0 @@ -import ListErrors from './ListErrors'; -import React from 'react'; -import agent from '../agent'; -import { connect } from 'react-redux'; -import { - SETTINGS_SAVED, - SETTINGS_PAGE_UNLOADED, - LOGOUT, - DELETE_USER -} from '../constants/actionTypes'; -import swal from 'sweetalert'; -import ShowIcon from '@material-ui/icons/Visibility'; -import ShowOffIcon from '@material-ui/icons/VisibilityOff'; - -class SettingsForm extends React.Component { - constructor() { - super(); - - this.state = { - image: '', - username: '', - bio: '', - email: '', - password: '', - confirmPassword: "" - }; - - this.updateState = field => ev => { - const state = this.state; - const newState = Object.assign({}, state, { [field]: ev.target.value }); - this.setState(newState); - }; - - this.submitForm = ev => { - ev.preventDefault(); - const regexp = /^\S*$/;//ei välilyöntejä - const user = Object.assign({}, this.state); - if(!regexp.test(user.password) || !regexp.test(user.confirmPassword)){ - swal(" Whitespace not allowed") - return null; - } - - if(user.password !== user.confirmPassword ) { - - swal("Passwords does not match ") - return null; - } - - if (!user.password) { - delete user.password; - } - else if(user.password.length < 4){ - swal("Password too short min 4 digits ") - return - } - else{ - swal({title: 'Password changed' , - timer: 1500, - icon: 'success', - showConfirmButton: false,}) - } - - this.props.onSubmitForm(user); - - }; - } - - componentWillMount() { - if (this.props.currentUser) { - Object.assign(this.state, { - image: this.props.currentUser.image || '', - username: this.props.currentUser.username, - bio: this.props.currentUser.bio, - email: this.props.currentUser.email - }); - } - } - - componentWillReceiveProps(nextProps) { - if (nextProps.currentUser) { - this.setState(Object.assign({}, this.state, { - image: nextProps.currentUser.image || '', - username: nextProps.currentUser.username, - bio: nextProps.currentUser.bio, - email: nextProps.currentUser.email - })); - } - } - - render() { - return ( - <form onSubmit={this.submitForm}> - <fieldset> - - <fieldset className="form-group"> - <input - className="form-control" - type="text" - placeholder="URL of profile picture" - value={this.state.image} - onChange={this.updateState('image')} /> - </fieldset> - - <fieldset className="form-group"> - <input - className="form-control form-control-lg" - type="text" - placeholder="Username" - value={this.state.username} - onChange={this.updateState('username')} /> - </fieldset> - - <fieldset className="form-group"> - <textarea - className="form-control form-control-lg" - rows="8" - placeholder="Short bio about you" - value={this.state.bio} - onChange={this.updateState('bio')}> - </textarea> - </fieldset> - - <fieldset className="form-group"> - <input - className="form-control form-control-lg" - type="email" - placeholder="Email" - value={this.state.email} - onChange={this.updateState('email')} /> - </fieldset> - - <fieldset className="form-group"> - <input - className="form-control form-control-lg" - type={this.state.password ? "password":"text" } - placeholder="New Password" - //value={this.state.password} - onChange={this.updateState('password')} /> - <span - className="hideshowbtn" style={{top: '67%'}} - onMouseDown={()=> this.setState({password:!this.state.password})}> - {this.state.password ? <ShowOffIcon/>:<ShowIcon/>} - </span> - </fieldset> - - <fieldset className="form-group"> - <input - className="form-control form-control-lg" - type={this.state.confirmPassword ? "password":"text" } - placeholder="Confirm Password" - // value={this.state.confirmPassword } - onChange={this.updateState('confirmPassword')} /> - <span - className="hideshowbtn" style={{top: '76%'}} - onMouseDown={()=> this.setState({confirmPassword:!this.state.confirmPassword})}> - {this.state.confirmPassword ? <ShowOffIcon/>:<ShowIcon/>} - </span> - </fieldset> - - <button - className="btn btn-lg btn-primary pull-xs-right" - type="submit" - disabled={this.state.inProgress}> - Update Settings - </button> - - </fieldset> - </form> - ); - } -} - -const mapStateToProps = state => ({ - ...state.settings, - currentUser: state.common.currentUser -}); - -const mapDispatchToProps = dispatch => ({ - onClickLogout: () => dispatch({ type: LOGOUT }), - onClickDeleteUser: user => - dispatch({ type: DELETE_USER, payload: agent.Auth.delete(user) }), - onSubmitForm: user => - dispatch({ type: SETTINGS_SAVED, payload: agent.Auth.save(user) }), - onUnload: () => dispatch({ type: SETTINGS_PAGE_UNLOADED }) -}); - - -class Settings extends React.Component { - - deleteUser() { - swal({ - title: "Are you sure?", - text: "Once deleted, you will not be able to recover your profile! \n All your articles and comments will be deleted!", - icon: "warning", - buttons: true, - dangerMode: true, - }) - .then((willDelete) => { - if (willDelete) { - swal("Poof! Your profile has been deleted!", { - icon: "success", - content: this.props.onClickDeleteUser() - }); - } else { - swal("Canceled!"); - } - }); - } - - render() { - return ( - <div className="settings-page"> - <div className="container page"> - <div className="row"> - <div className="col-md-6 offset-md-3 col-xs-12"> - - <h1 className="text-xs-center">Your Settings</h1> - - <SettingsForm - currentUser={this.props.currentUser} - onSubmitForm={this.props.onSubmitForm} /> - - <ListErrors errors={this.props.errors}></ListErrors> - - <hr /> - - - <button - className="btn btn-outline-danger" - onClick={this.props.onClickLogout}> - Logout - </button> - - <button - className="btn btn-outline-danger pull-xs-right" - onClick={()=>this.deleteUser()}> - Delete account - </button> - - </div> - </div> - </div> - </div> - ); - } -} - -export default connect(mapStateToProps, mapDispatchToProps)(Settings); diff --git a/te-frontend/src/constants/actionTypes.js b/te-frontend/src/constants/actionTypes.js deleted file mode 100644 index f2d439949aba817ab915eb798ecba2851237f12d..0000000000000000000000000000000000000000 --- a/te-frontend/src/constants/actionTypes.js +++ /dev/null @@ -1,37 +0,0 @@ -export const APP_LOAD = 'APP_LOAD'; -export const REDIRECT = 'REDIRECT'; -export const ARTICLE_SUBMITTED = 'ARTICLE_SUBMITTED'; -export const SETTINGS_SAVED = 'SETTINGS_SAVED'; -export const DELETE_ARTICLE = 'DELETE_ARTICLE'; -export const SETTINGS_PAGE_UNLOADED = 'SETTINGS_PAGE_UNLOADED'; -export const HOME_PAGE_LOADED = 'HOME_PAGE_LOADED'; -export const HOME_PAGE_UNLOADED = 'HOME_PAGE_UNLOADED'; -export const ARTICLE_PAGE_LOADED = 'ARTICLE_PAGE_LOADED'; -export const ARTICLE_PAGE_UNLOADED = 'ARTICLE_PAGE_UNLOADED'; -export const ADD_COMMENT = 'ADD_COMMENT'; -export const DELETE_COMMENT = 'DELETE_COMMENT'; -export const ARTICLE_FAVORITED = 'ARTICLE_FAVORITED'; -export const ARTICLE_UNFAVORITED = 'ARTICLE_UNFAVORITED'; -export const SET_PAGE = 'SET_PAGE'; -export const APPLY_TAG_FILTER = 'APPLY_TAG_FILTER'; -export const CHANGE_TAB = 'CHANGE_TAB'; -export const PROFILE_PAGE_LOADED = 'PROFILE_PAGE_LOADED'; -export const PROFILE_PAGE_UNLOADED = 'PROFILE_PAGE_UNLOADED'; -export const LOGIN = 'LOGIN'; -export const LOGOUT = 'LOGOUT'; -export const REGISTER = 'REGISTER'; -export const LOGIN_PAGE_UNLOADED = 'LOGIN_PAGE_UNLOADED'; -export const REGISTER_PAGE_UNLOADED = 'REGISTER_PAGE_UNLOADED'; -export const ASYNC_START = 'ASYNC_START'; -export const ASYNC_END = 'ASYNC_END'; -export const EDITOR_PAGE_LOADED = 'EDITOR_PAGE_LOADED'; -export const EDITOR_PAGE_UNLOADED = 'EDITOR_PAGE_UNLOADED'; -export const ADD_TAG = 'ADD_TAG'; -export const REMOVE_TAG = 'REMOVE_TAG'; -export const UPDATE_FIELD_AUTH = 'UPDATE_FIELD_AUTH'; -export const UPDATE_FIELD_EDITOR = 'UPDATE_FIELD_EDITOR'; -export const FOLLOW_USER = 'FOLLOW_USER'; -export const UNFOLLOW_USER = 'UNFOLLOW_USER'; -export const PROFILE_FAVORITES_PAGE_UNLOADED = 'PROFILE_FAVORITES_PAGE_UNLOADED'; -export const PROFILE_FAVORITES_PAGE_LOADED = 'PROFILE_FAVORITES_PAGE_LOADED'; -export const DELETE_USER = 'DELETE_USER'; \ No newline at end of file diff --git a/te-frontend/src/index.js b/te-frontend/src/index.js deleted file mode 100644 index 3d20aa2a6c8f3a2fc62e6f3ec72308c2ab3396fc..0000000000000000000000000000000000000000 --- a/te-frontend/src/index.js +++ /dev/null @@ -1,20 +0,0 @@ -import ReactDOM from 'react-dom'; -import { Provider } from 'react-redux'; -import React from 'react'; -import { store, history} from './store'; - -import { Route, Switch } from 'react-router-dom'; -import { ConnectedRouter } from 'react-router-redux'; - -import App from './components/App'; - -ReactDOM.render(( - <Provider store={store}> - <ConnectedRouter history={history}> - <Switch> - <Route path="/" component={App} /> - </Switch> - </ConnectedRouter> - </Provider> - -), document.getElementById('root')); diff --git a/te-frontend/src/middleware.js b/te-frontend/src/middleware.js deleted file mode 100644 index 17a4d50f81c88cf8aed815cb821f31ec7edfd2f0..0000000000000000000000000000000000000000 --- a/te-frontend/src/middleware.js +++ /dev/null @@ -1,69 +0,0 @@ -import agent from './agent'; -import { - ASYNC_START, - ASYNC_END, - LOGIN, - LOGOUT, - REGISTER, - DELETE_USER -} from './constants/actionTypes'; - -const promiseMiddleware = store => next => action => { - if (isPromise(action.payload)) { - store.dispatch({ type: ASYNC_START, subtype: action.type }); - - const currentView = store.getState().viewChangeCounter; - const skipTracking = action.skipTracking; - - action.payload.then( - res => { - const currentState = store.getState() - if (!skipTracking && currentState.viewChangeCounter !== currentView) { - return - } - console.log('RESULT', res); - action.payload = res; - store.dispatch({ type: ASYNC_END, promise: action.payload }); - store.dispatch(action); - }, - error => { - const currentState = store.getState() - if (!skipTracking && currentState.viewChangeCounter !== currentView) { - return - } - console.log('ERROR', error); - action.error = true; - action.payload = error.response.body; - if (!action.skipTracking) { - store.dispatch({ type: ASYNC_END, promise: action.payload }); - } - store.dispatch(action); - } - ); - - return; - } - - next(action); -}; - -const localStorageMiddleware = store => next => action => { - if (action.type === REGISTER || action.type === LOGIN) { - if (!action.error) { - window.localStorage.setItem('jwt', action.payload.user.token); - agent.setToken(action.payload.user.token); - } - } else if (action.type === LOGOUT || action.type === DELETE_USER) { - window.localStorage.setItem('jwt', ''); - agent.setToken(null); - } - - next(action); -}; - -function isPromise(v) { - return v && typeof v.then === 'function'; -} - - -export { promiseMiddleware, localStorageMiddleware } diff --git a/te-frontend/src/reducer.js b/te-frontend/src/reducer.js deleted file mode 100644 index 5005911af96c07a566569f02ae262c15b72b5019..0000000000000000000000000000000000000000 --- a/te-frontend/src/reducer.js +++ /dev/null @@ -1,22 +0,0 @@ -import article from './reducers/article'; -import articleList from './reducers/articleList'; -import auth from './reducers/auth'; -import { combineReducers } from 'redux'; -import common from './reducers/common'; -import editor from './reducers/editor'; -import home from './reducers/home'; -import profile from './reducers/profile'; -import settings from './reducers/settings'; -import { routerReducer } from 'react-router-redux'; - -export default combineReducers({ - article, - articleList, - auth, - common, - editor, - home, - profile, - settings, - router: routerReducer -}); diff --git a/te-frontend/src/reducers/article.js b/te-frontend/src/reducers/article.js deleted file mode 100644 index 50ec7a9d5b55ecec496c99bddd76fdc5516b1749..0000000000000000000000000000000000000000 --- a/te-frontend/src/reducers/article.js +++ /dev/null @@ -1,35 +0,0 @@ -import { - ARTICLE_PAGE_LOADED, - ARTICLE_PAGE_UNLOADED, - ADD_COMMENT, - DELETE_COMMENT -} from '../constants/actionTypes'; - -export default (state = {}, action) => { - switch (action.type) { - case ARTICLE_PAGE_LOADED: - return { - ...state, - article: action.payload[0].article, - comments: action.payload[1].comments - }; - case ARTICLE_PAGE_UNLOADED: - return {}; - case ADD_COMMENT: - return { - ...state, - commentErrors: action.error ? action.payload.errors : null, - comments: action.error ? - null : - (state.comments || []).concat([action.payload.comment]) - }; - case DELETE_COMMENT: - const commentId = action.commentId - return { - ...state, - comments: state.comments.filter(comment => comment.id !== commentId) - }; - default: - return state; - } -}; diff --git a/te-frontend/src/reducers/articleList.js b/te-frontend/src/reducers/articleList.js deleted file mode 100644 index 1fe962daaaeb032f2b8a10dea1ca04c51798c823..0000000000000000000000000000000000000000 --- a/te-frontend/src/reducers/articleList.js +++ /dev/null @@ -1,86 +0,0 @@ -import { - ARTICLE_FAVORITED, - ARTICLE_UNFAVORITED, - SET_PAGE, - APPLY_TAG_FILTER, - HOME_PAGE_LOADED, - HOME_PAGE_UNLOADED, - CHANGE_TAB, - PROFILE_PAGE_LOADED, - PROFILE_PAGE_UNLOADED, - PROFILE_FAVORITES_PAGE_LOADED, - PROFILE_FAVORITES_PAGE_UNLOADED -} from '../constants/actionTypes'; - -export default (state = {}, action) => { - switch (action.type) { - case ARTICLE_FAVORITED: - case ARTICLE_UNFAVORITED: - return { - ...state, - articles: state.articles.map(article => { - if (article.slug === action.payload.article.slug) { - return { - ...article, - favorited: action.payload.article.favorited, - favoritesCount: action.payload.article.favoritesCount - }; - } - return article; - }) - }; - case SET_PAGE: - return { - ...state, - articles: action.payload.articles, - articlesCount: action.payload.articlesCount, - currentPage: action.page - }; - case APPLY_TAG_FILTER: - return { - ...state, - pager: action.pager, - articles: action.payload.articles, - articlesCount: action.payload.articlesCount, - tab: null, - tag: action.tag, - currentPage: 0 - }; - case HOME_PAGE_LOADED: - return { - ...state, - pager: action.pager, - tags: action.payload[0].tags, - articles: action.payload[1].articles, - articlesCount: action.payload[1].articlesCount, - currentPage: 0, - tab: action.tab - }; - case HOME_PAGE_UNLOADED: - return {}; - case CHANGE_TAB: - return { - ...state, - pager: action.pager, - articles: action.payload.articles, - articlesCount: action.payload.articlesCount, - tab: action.tab, - currentPage: 0, - tag: null - }; - case PROFILE_PAGE_LOADED: - case PROFILE_FAVORITES_PAGE_LOADED: - return { - ...state, - pager: action.pager, - articles: action.payload[1].articles, - articlesCount: action.payload[1].articlesCount, - currentPage: 0 - }; - case PROFILE_PAGE_UNLOADED: - case PROFILE_FAVORITES_PAGE_UNLOADED: - return {}; - default: - return state; - } -}; diff --git a/te-frontend/src/reducers/auth.js b/te-frontend/src/reducers/auth.js deleted file mode 100644 index 6e8383866f86fe2f38d7abbba2d1870e8e655afd..0000000000000000000000000000000000000000 --- a/te-frontend/src/reducers/auth.js +++ /dev/null @@ -1,34 +0,0 @@ -import { - LOGIN, - REGISTER, - LOGIN_PAGE_UNLOADED, - REGISTER_PAGE_UNLOADED, - ASYNC_START, - UPDATE_FIELD_AUTH -} from '../constants/actionTypes'; - -export default (state = {}, action) => { - switch (action.type) { - case LOGIN: - case REGISTER: - return { - ...state, - inProgress: false, - errors: action.error ? action.payload.errors : null - }; - case LOGIN_PAGE_UNLOADED: - case REGISTER_PAGE_UNLOADED: - return {}; - case ASYNC_START: - if (action.subtype === LOGIN || action.subtype === REGISTER) { - return { ...state, inProgress: true }; - } - break; - case UPDATE_FIELD_AUTH: - return { ...state, [action.key]: action.value }; - default: - return state; - } - - return state; -}; diff --git a/te-frontend/src/reducers/common.js b/te-frontend/src/reducers/common.js deleted file mode 100644 index 06d349d91647cd0259325df61313c87b90cb8317..0000000000000000000000000000000000000000 --- a/te-frontend/src/reducers/common.js +++ /dev/null @@ -1,72 +0,0 @@ -import { - APP_LOAD, - REDIRECT, - LOGOUT, - ARTICLE_SUBMITTED, - SETTINGS_SAVED, - LOGIN, - REGISTER, - DELETE_ARTICLE, - ARTICLE_PAGE_UNLOADED, - EDITOR_PAGE_UNLOADED, - HOME_PAGE_UNLOADED, - PROFILE_PAGE_UNLOADED, - PROFILE_FAVORITES_PAGE_UNLOADED, - SETTINGS_PAGE_UNLOADED, - LOGIN_PAGE_UNLOADED, - REGISTER_PAGE_UNLOADED, - DELETE_USER -} from '../constants/actionTypes'; - -const defaultState = { - appName: 'Conduit', - token: null, - viewChangeCounter: 0 -}; - -export default (state = defaultState, action) => { - switch (action.type) { - case APP_LOAD: - return { - ...state, - token: action.token || null, - appLoaded: true, - currentUser: action.payload ? action.payload.user : null - }; - case REDIRECT: - return { ...state, redirectTo: null }; - case DELETE_USER: - case LOGOUT: - return { ...state, redirectTo: '/', token: null, currentUser: null }; - case ARTICLE_SUBMITTED: - const redirectUrl = action.error ? null : `/article/${action.payload.article.slug}`; - return { ...state, redirectTo: redirectUrl }; - case SETTINGS_SAVED: - return { - ...state, - redirectTo: action.error ? null : '/', - currentUser: action.error ? null : action.payload.user - }; - case LOGIN: - case REGISTER: - return { - ...state, - redirectTo: action.error ? null : '/', - token: action.error ? null : action.payload.user.token, - currentUser: action.error ? null : action.payload.user - }; - case DELETE_ARTICLE: - return { ...state, redirectTo: '/' }; - case ARTICLE_PAGE_UNLOADED: - case EDITOR_PAGE_UNLOADED: - case HOME_PAGE_UNLOADED: - case PROFILE_PAGE_UNLOADED: - case PROFILE_FAVORITES_PAGE_UNLOADED: - case SETTINGS_PAGE_UNLOADED: - case LOGIN_PAGE_UNLOADED: - case REGISTER_PAGE_UNLOADED: - return { ...state, viewChangeCounter: state.viewChangeCounter + 1 }; - default: - return state; - } -}; diff --git a/te-frontend/src/reducers/editor.js b/te-frontend/src/reducers/editor.js deleted file mode 100644 index fd4563313882910bc8a31ce1443e5e8d60d01806..0000000000000000000000000000000000000000 --- a/te-frontend/src/reducers/editor.js +++ /dev/null @@ -1,54 +0,0 @@ -import { - EDITOR_PAGE_LOADED, - EDITOR_PAGE_UNLOADED, - ARTICLE_SUBMITTED, - ASYNC_START, - ADD_TAG, - REMOVE_TAG, - UPDATE_FIELD_EDITOR -} from '../constants/actionTypes'; - -export default (state = {}, action) => { - switch (action.type) { - case EDITOR_PAGE_LOADED: - return { - ...state, - articleSlug: action.payload ? action.payload.article.slug : '', - title: action.payload ? action.payload.article.title : '', - description: action.payload ? action.payload.article.description : '', - body: action.payload ? action.payload.article.body : '', - tagInput: '', - tagList: action.payload ? action.payload.article.tagList : [] - }; - case EDITOR_PAGE_UNLOADED: - return {}; - case ARTICLE_SUBMITTED: - return { - ...state, - inProgress: null, - errors: action.error ? action.payload.errors : null - }; - case ASYNC_START: - if (action.subtype === ARTICLE_SUBMITTED) { - return { ...state, inProgress: true }; - } - break; - case ADD_TAG: - return { - ...state, - tagList: state.tagList.concat([state.tagInput]), - tagInput: '' - }; - case REMOVE_TAG: - return { - ...state, - tagList: state.tagList.filter(tag => tag !== action.tag) - }; - case UPDATE_FIELD_EDITOR: - return { ...state, [action.key]: action.value }; - default: - return state; - } - - return state; -}; diff --git a/te-frontend/src/reducers/home.js b/te-frontend/src/reducers/home.js deleted file mode 100644 index c1cd49af1483ba043bdf4c6e1540e4e2f7c5fd43..0000000000000000000000000000000000000000 --- a/te-frontend/src/reducers/home.js +++ /dev/null @@ -1,15 +0,0 @@ -import { HOME_PAGE_LOADED, HOME_PAGE_UNLOADED } from '../constants/actionTypes'; - -export default (state = {}, action) => { - switch (action.type) { - case HOME_PAGE_LOADED: - return { - ...state, - tags: action.payload[0].tags - }; - case HOME_PAGE_UNLOADED: - return {}; - default: - return state; - } -}; diff --git a/te-frontend/src/reducers/profile.js b/te-frontend/src/reducers/profile.js deleted file mode 100644 index 5e67a79de7cbad6483cdf3d4f8a971b7e94097b7..0000000000000000000000000000000000000000 --- a/te-frontend/src/reducers/profile.js +++ /dev/null @@ -1,24 +0,0 @@ -import { - PROFILE_PAGE_LOADED, - PROFILE_PAGE_UNLOADED, - FOLLOW_USER, - UNFOLLOW_USER -} from '../constants/actionTypes'; - -export default (state = {}, action) => { - switch (action.type) { - case PROFILE_PAGE_LOADED: - return { - ...action.payload[0].profile - }; - case PROFILE_PAGE_UNLOADED: - return {}; - case FOLLOW_USER: - case UNFOLLOW_USER: - return { - ...action.payload.profile - }; - default: - return state; - } -}; diff --git a/te-frontend/src/reducers/settings.js b/te-frontend/src/reducers/settings.js deleted file mode 100644 index 8e3751e634ba0e875248feac6720cd725e1ee9b5..0000000000000000000000000000000000000000 --- a/te-frontend/src/reducers/settings.js +++ /dev/null @@ -1,25 +0,0 @@ -import { - SETTINGS_SAVED, - SETTINGS_PAGE_UNLOADED, - ASYNC_START -} from '../constants/actionTypes'; - -export default (state = {}, action) => { - switch (action.type) { - case SETTINGS_SAVED: - return { - ...state, - inProgress: false, - errors: action.error ? action.payload.errors : null - }; - case SETTINGS_PAGE_UNLOADED: - return {}; - case ASYNC_START: - return { - ...state, - inProgress: true - }; - default: - return state; - } -}; diff --git a/te-frontend/src/store.js b/te-frontend/src/store.js deleted file mode 100644 index 185d866fba9a63469f75a62fbee35656bbfbf7ff..0000000000000000000000000000000000000000 --- a/te-frontend/src/store.js +++ /dev/null @@ -1,25 +0,0 @@ -import { applyMiddleware, createStore } from 'redux'; -import { createLogger } from 'redux-logger' -import { composeWithDevTools } from 'redux-devtools-extension/developmentOnly'; -import { promiseMiddleware, localStorageMiddleware } from './middleware'; -import reducer from './reducer'; - -import { routerMiddleware } from 'react-router-redux' -import createHistory from 'history/createBrowserHistory'; - -export const history = createHistory(); - -// Build the middleware for intercepting and dispatching navigation actions -const myRouterMiddleware = routerMiddleware(history); - -const getMiddleware = () => { - if (process.env.NODE_ENV === 'production') { - return applyMiddleware(myRouterMiddleware, promiseMiddleware, localStorageMiddleware); - } else { - // Enable additional logging in non-production environments. - return applyMiddleware(myRouterMiddleware, promiseMiddleware, localStorageMiddleware, createLogger()) - } -}; - -export const store = createStore( - reducer, composeWithDevTools(getMiddleware())); diff --git a/te-frontend/te-frontend-deployment.yaml b/te-frontend/te-frontend-deployment.yaml deleted file mode 100644 index 76d311c51bd7aa8207bd4b0feeda3f7b825ca979..0000000000000000000000000000000000000000 --- a/te-frontend/te-frontend-deployment.yaml +++ /dev/null @@ -1,33 +0,0 @@ -apiVersion: apps/v1 -kind: Deployment -metadata: - labels: - app: frontend - name: frontend - namespace: default -spec: - replicas: 1 - selector: - matchLabels: - app: frontend - strategy: - rollingUpdate: - maxSurge: 25% - maxUnavailable: 25% - type: RollingUpdate - template: - metadata: - labels: - app: frontend - spec: - containers: - - image: gitlab.labranet.jamk.fi:4567/conduitmiau/te-frontend:frontend - imagePullPolicy: Always - name: frontend - resources: - requests: - cpu: '1' - memory: 1000Mi - - ports: - - containerPort: 4100 diff --git a/te-frontend/te-frontend-service.yaml b/te-frontend/te-frontend-service.yaml deleted file mode 100644 index a1958099272f9c76d987d135000bc5e879919a83..0000000000000000000000000000000000000000 --- a/te-frontend/te-frontend-service.yaml +++ /dev/null @@ -1,13 +0,0 @@ -apiVersion: v1 -kind: Service -metadata: - name: frontend - namespace: default -spec: - type: LoadBalancer - ports: - - nodePort: 30100 - port: 80 - targetPort: 4100 - selector: - app: frontend