├── .gitignore ├── README.md ├── app ├── __init__.py ├── configuration.py ├── forms.py ├── models.py ├── static │ ├── assets │ │ ├── css │ │ │ ├── font-awesome.min.css │ │ │ ├── main.css │ │ │ └── noscript.css │ │ ├── fonts │ │ │ ├── FontAwesome.otf │ │ │ ├── fontawesome-webfont.eot │ │ │ ├── fontawesome-webfont.svg │ │ │ ├── fontawesome-webfont.ttf │ │ │ ├── fontawesome-webfont.woff │ │ │ └── fontawesome-webfont.woff2 │ │ ├── js │ │ │ ├── breakpoints.min.js │ │ │ ├── browser.min.js │ │ │ ├── jquery.min.js │ │ │ ├── main.js │ │ │ └── util.js │ │ └── sass │ │ │ ├── base │ │ │ ├── _page.scss │ │ │ ├── _reset.scss │ │ │ └── _typography.scss │ │ │ ├── components │ │ │ ├── _actions.scss │ │ │ ├── _box.scss │ │ │ ├── _button.scss │ │ │ ├── _form.scss │ │ │ ├── _icon.scss │ │ │ ├── _icons.scss │ │ │ ├── _image.scss │ │ │ ├── _list.scss │ │ │ ├── _row.scss │ │ │ ├── _section.scss │ │ │ ├── _table.scss │ │ │ └── _tiles.scss │ │ │ ├── layout │ │ │ ├── _footer.scss │ │ │ ├── _header.scss │ │ │ ├── _main.scss │ │ │ ├── _menu.scss │ │ │ └── _wrapper.scss │ │ │ ├── libs │ │ │ ├── _breakpoints.scss │ │ │ ├── _functions.scss │ │ │ ├── _html-grid.scss │ │ │ ├── _mixins.scss │ │ │ ├── _vars.scss │ │ │ └── _vendor.scss │ │ │ ├── main.scss │ │ │ └── noscript.scss │ ├── favicon.ico │ ├── images │ │ ├── logo.svg │ │ ├── pic01.jpg │ │ ├── pic02.jpg │ │ ├── pic03.jpg │ │ ├── pic04.jpg │ │ ├── pic05.jpg │ │ ├── pic06.jpg │ │ ├── pic07.jpg │ │ ├── pic08.jpg │ │ ├── pic09.jpg │ │ ├── pic10.jpg │ │ ├── pic11.jpg │ │ ├── pic12.jpg │ │ ├── pic13.jpg │ │ ├── pic14.jpg │ │ └── pic15.jpg │ └── sitemap.xml ├── templates │ ├── includes │ │ ├── footer.html │ │ ├── navigation.html │ │ └── scripts.html │ ├── layouts │ │ └── base-site.html │ └── pages │ │ ├── about.html │ │ ├── elements.html │ │ ├── error-404.html │ │ ├── index.html │ │ ├── login.html │ │ └── register.html ├── util.py └── views.py ├── package.json ├── requirements.txt ├── run.py └── screenshots ├── flask-boilerplate-elements.jpg ├── flask-boilerplate-generic-page.jpg ├── flask-boilerplate-intro.gif ├── flask-boilerplate-login.jpg ├── flask-boilerplate-main.jpg └── flask-boilerplate-menu.jpg /.gitignore: -------------------------------------------------------------------------------- 1 | flask/ 2 | *.pyc 3 | dev 4 | node_modules 5 | app/database.db 6 | app/build 7 | yarn.lock 8 | yarn-error.log 9 | *.psd 10 | env/ 11 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # [Flaskplay Boilerplate](https://www.youtube.com/watch?v=XgkIZsMgkBc) 2 | 3 | **Open-Source Web App** coded in **[Flask Framework](https://palletsprojects.com/p/flask/)** - Provided by **AppSeed** [Web App Generator](https://appseed.us/app-generator). 4 | 5 | ### App Features: 6 | 7 | - SQLite database 8 | - SQLAlchemy ORM 9 | - Session-Based authentication flow (login, register) 10 | - UI Kit: Phantom by **Html5UP** 11 | 12 |
13 | 14 | ## Want more? Go PRO! 15 | 16 | PRO versions include **Premium UI Kits**, Lifetime updates and **24/7 LIVE Support** (via [Discord](https://discord.gg/fZC6hup)) 17 | 18 | | [Flask Dashboard Material PRO](https://appseed.us/admin-dashboards/flask-dashboard-material-pro) | [Flask Dashboard Dashkit PRO](https://appseed.us/admin-dashboards/flask-dashboard-dashkit-pro) | [Flask Dashboard Black PRO](https://appseed.us/admin-dashboards/flask-dashboard-black-pro) | 19 | | --- | --- | --- | 20 | | [![Flask Dashboard Material PRO](https://raw.githubusercontent.com/app-generator/static/master/products/flask-dashboard-material-pro-screen.png)](https://appseed.us/admin-dashboards/flask-dashboard-material-pro) | [![Flask Dashboard Dashkit PRO](https://raw.githubusercontent.com/app-generator/static/master/products/flask-dashboard-dashkit-pro-screen.png)](https://appseed.us/admin-dashboards/flask-dashboard-dashkit-pro) | [![Flask Dashboard Black PRO](https://raw.githubusercontent.com/app-generator/static/master/products/flask-dashboard-black-pro-screen.png)](https://appseed.us/admin-dashboards/flask-dashboard-black-pro) 21 | 22 |
23 |
24 | 25 | ![Flask Paper Kit - Open-Source Web App.](https://raw.githubusercontent.com/app-generator/static/master/products/flask-boilerplate-flaskplay-screen.png) 26 | 27 |
28 | 29 | ## Build from sources 30 | 31 | ```bash 32 | $ # Clone the sources 33 | $ git clone https://github.com/app-generator/flask-boilerplate-flaskplay.git 34 | $ cd flask-boilerplate-flaskplay 35 | $ 36 | $ # Virtualenv modules installation (Unix based systems) 37 | $ virtualenv --no-site-packages env 38 | $ source env/bin/activate 39 | $ 40 | $ # Virtualenv modules installation (Windows based systems) 41 | $ # virtualenv --no-site-packages env 42 | $ # .\env\Scripts\activate 43 | $ 44 | $ # Install requirements 45 | $ pip3 install -r requirements.txt 46 | $ 47 | $ # Set the FLASK_APP environment variable 48 | $ (Unix/Mac) export FLASK_APP=run.py 49 | $ (Windows) set FLASK_APP=run.py 50 | $ (Powershell) $env:FLASK_APP = ".\run.py" 51 | $ 52 | $ # Set up the DEBUG environment 53 | $ # (Unix/Mac) export FLASK_ENV=development 54 | $ # (Windows) set FLASK_ENV=development 55 | $ # (Powershell) $env:FLASK_ENV = "development" 56 | $ 57 | $ # Run the application 58 | $ # --host=0.0.0.0 - expose the app on all network interfaces (default 127.0.0.1) 59 | $ # --port=5000 - specify the app port (default 5000) 60 | $ flask run --host=0.0.0.0 --port=5000 61 | $ 62 | $ # Access the app in browser: http://127.0.0.1:5000/ 63 | ``` 64 | 65 |
66 | 67 | ## Support 68 | 69 | - Free support via eMail < [support @ appseed.us](https://appseed.us/support) > and **Github** issues tracker 70 | - 24/7 Live Support via [Discord](https://discord.gg/fZC6hup) for paid plans and commercial products. 71 | 72 |
73 | 74 | ## Credits & Links 75 | 76 | - [Flask Framework](https://www.palletsprojects.com/p/flask/) - The offcial website 77 | - [Flask Boilerplate - Open-Source Apps built with automation tools](https://dev.to/sm0ke/flask-boilerplate-open-source-apps-built-with-automation-tools-4925) - a popular article published on **Dev.to** platform 78 |
79 | 80 | ## License 81 | 82 | @MIT 83 | 84 |
85 | 86 | --- 87 | [Flaskplay Boilerplate](https://www.youtube.com/watch?v=XgkIZsMgkBc) - Provided by **AppSeed** [Web App Generator](https://appseed.us/app-generator). 88 | -------------------------------------------------------------------------------- /app/__init__.py: -------------------------------------------------------------------------------- 1 | # -*- encoding: utf-8 -*- 2 | """ 3 | License: MIT 4 | Copyright (c) 2019 - present AppSeed.us 5 | """ 6 | 7 | import os 8 | 9 | from flask import Flask 10 | from flask_sqlalchemy import SQLAlchemy 11 | from flask_login import LoginManager 12 | from flask_bcrypt import Bcrypt 13 | 14 | # Grabs the folder where the script runs. 15 | basedir = os.path.abspath(os.path.dirname(__file__)) 16 | 17 | app = Flask(__name__) 18 | 19 | app.config.from_object('app.configuration.Config') 20 | 21 | db = SQLAlchemy (app) # flask-sqlalchemy 22 | bc = Bcrypt (app) # flask-bcrypt 23 | 24 | lm = LoginManager( ) # flask-loginmanager 25 | lm.init_app(app) # init the login manager 26 | 27 | # Setup database 28 | @app.before_first_request 29 | def initialize_database(): 30 | db.create_all() 31 | 32 | # Import routing, models and Start the App 33 | from app import views, models 34 | -------------------------------------------------------------------------------- /app/configuration.py: -------------------------------------------------------------------------------- 1 | # -*- encoding: utf-8 -*- 2 | """ 3 | License: MIT 4 | Copyright (c) 2019 - present AppSeed.us 5 | """ 6 | 7 | import os 8 | 9 | # Grabs the folder where the script runs. 10 | basedir = os.path.abspath(os.path.dirname(__file__)) 11 | 12 | class Config(): 13 | 14 | CSRF_ENABLED = True 15 | SECRET_KEY = "77tgFCdrEEdv77554##@3" 16 | 17 | SQLALCHEMY_TRACK_MODIFICATIONS = False 18 | 19 | SQLALCHEMY_DATABASE_URI = 'sqlite:///' + os.path.join(basedir, 'database.db') 20 | -------------------------------------------------------------------------------- /app/forms.py: -------------------------------------------------------------------------------- 1 | # -*- encoding: utf-8 -*- 2 | """ 3 | License: MIT 4 | Copyright (c) 2019 - present AppSeed.us 5 | """ 6 | 7 | from flask_wtf import FlaskForm 8 | from flask_wtf.file import FileField, FileRequired 9 | from wtforms import StringField, TextAreaField, SubmitField, PasswordField 10 | from wtforms.validators import InputRequired, Email, DataRequired 11 | 12 | class LoginForm(FlaskForm): 13 | username = StringField (u'Username' , validators=[DataRequired()]) 14 | password = PasswordField(u'Password' , validators=[DataRequired()]) 15 | 16 | class RegisterForm(FlaskForm): 17 | name = StringField (u'Name' ) 18 | username = StringField (u'Username' , validators=[DataRequired()]) 19 | password = PasswordField(u'Password' , validators=[DataRequired()]) 20 | email = StringField (u'Email' , validators=[DataRequired(), Email()]) 21 | -------------------------------------------------------------------------------- /app/models.py: -------------------------------------------------------------------------------- 1 | # -*- encoding: utf-8 -*- 2 | """ 3 | License: MIT 4 | Copyright (c) 2019 - present AppSeed.us 5 | """ 6 | 7 | from app import db 8 | from flask_login import UserMixin 9 | 10 | class User(UserMixin, db.Model): 11 | 12 | id = db.Column(db.Integer, primary_key=True) 13 | user = db.Column(db.String(64), unique = True) 14 | email = db.Column(db.String(120), unique = True) 15 | password = db.Column(db.String(500)) 16 | 17 | def __init__(self, user, email, password): 18 | self.user = user 19 | self.password = password 20 | self.email = email 21 | 22 | def __repr__(self): 23 | return str(self.id) + ' - ' + str(self.user) 24 | 25 | def save(self): 26 | 27 | # inject self into db session 28 | db.session.add ( self ) 29 | 30 | # commit change and save the object 31 | db.session.commit( ) 32 | 33 | return self 34 | -------------------------------------------------------------------------------- /app/static/assets/css/font-awesome.min.css: -------------------------------------------------------------------------------- 1 | /*! 2 | * Font Awesome 4.7.0 by @davegandy - http://fontawesome.io - @fontawesome 3 | * License - http://fontawesome.io/license (Font: SIL OFL 1.1, CSS: MIT License) 4 | */@font-face{font-family:'FontAwesome';src:url('../fonts/fontawesome-webfont.eot?v=4.7.0');src:url('../fonts/fontawesome-webfont.eot?#iefix&v=4.7.0') format('embedded-opentype'),url('../fonts/fontawesome-webfont.woff2?v=4.7.0') format('woff2'),url('../fonts/fontawesome-webfont.woff?v=4.7.0') format('woff'),url('../fonts/fontawesome-webfont.ttf?v=4.7.0') format('truetype'),url('../fonts/fontawesome-webfont.svg?v=4.7.0#fontawesomeregular') format('svg');font-weight:normal;font-style:normal}.fa{display:inline-block;font:normal normal normal 14px/1 FontAwesome;font-size:inherit;text-rendering:auto;-webkit-font-smoothing:antialiased;-moz-osx-font-smoothing:grayscale}.fa-lg{font-size:1.33333333em;line-height:.75em;vertical-align:-15%}.fa-2x{font-size:2em}.fa-3x{font-size:3em}.fa-4x{font-size:4em}.fa-5x{font-size:5em}.fa-fw{width:1.28571429em;text-align:center}.fa-ul{padding-left:0;margin-left:2.14285714em;list-style-type:none}.fa-ul>li{position:relative}.fa-li{position:absolute;left:-2.14285714em;width:2.14285714em;top:.14285714em;text-align:center}.fa-li.fa-lg{left:-1.85714286em}.fa-border{padding:.2em .25em .15em;border:solid .08em #eee;border-radius:.1em}.fa-pull-left{float:left}.fa-pull-right{float:right}.fa.fa-pull-left{margin-right:.3em}.fa.fa-pull-right{margin-left:.3em}.pull-right{float:right}.pull-left{float:left}.fa.pull-left{margin-right:.3em}.fa.pull-right{margin-left:.3em}.fa-spin{-webkit-animation:fa-spin 2s infinite linear;animation:fa-spin 2s infinite linear}.fa-pulse{-webkit-animation:fa-spin 1s infinite steps(8);animation:fa-spin 1s infinite steps(8)}@-webkit-keyframes fa-spin{0%{-webkit-transform:rotate(0deg);transform:rotate(0deg)}100%{-webkit-transform:rotate(359deg);transform:rotate(359deg)}}@keyframes fa-spin{0%{-webkit-transform:rotate(0deg);transform:rotate(0deg)}100%{-webkit-transform:rotate(359deg);transform:rotate(359deg)}}.fa-rotate-90{-ms-filter:"progid:DXImageTransform.Microsoft.BasicImage(rotation=1)";-webkit-transform:rotate(90deg);-ms-transform:rotate(90deg);transform:rotate(90deg)}.fa-rotate-180{-ms-filter:"progid:DXImageTransform.Microsoft.BasicImage(rotation=2)";-webkit-transform:rotate(180deg);-ms-transform:rotate(180deg);transform:rotate(180deg)}.fa-rotate-270{-ms-filter:"progid:DXImageTransform.Microsoft.BasicImage(rotation=3)";-webkit-transform:rotate(270deg);-ms-transform:rotate(270deg);transform:rotate(270deg)}.fa-flip-horizontal{-ms-filter:"progid:DXImageTransform.Microsoft.BasicImage(rotation=0, mirror=1)";-webkit-transform:scale(-1, 1);-ms-transform:scale(-1, 1);transform:scale(-1, 1)}.fa-flip-vertical{-ms-filter:"progid:DXImageTransform.Microsoft.BasicImage(rotation=2, mirror=1)";-webkit-transform:scale(1, -1);-ms-transform:scale(1, -1);transform:scale(1, -1)}:root .fa-rotate-90,:root .fa-rotate-180,:root .fa-rotate-270,:root .fa-flip-horizontal,:root .fa-flip-vertical{filter:none}.fa-stack{position:relative;display:inline-block;width:2em;height:2em;line-height:2em;vertical-align:middle}.fa-stack-1x,.fa-stack-2x{position:absolute;left:0;width:100%;text-align:center}.fa-stack-1x{line-height:inherit}.fa-stack-2x{font-size:2em}.fa-inverse{color:#fff}.fa-glass:before{content:"\f000"}.fa-music:before{content:"\f001"}.fa-search:before{content:"\f002"}.fa-envelope-o:before{content:"\f003"}.fa-heart:before{content:"\f004"}.fa-star:before{content:"\f005"}.fa-star-o:before{content:"\f006"}.fa-user:before{content:"\f007"}.fa-film:before{content:"\f008"}.fa-th-large:before{content:"\f009"}.fa-th:before{content:"\f00a"}.fa-th-list:before{content:"\f00b"}.fa-check:before{content:"\f00c"}.fa-remove:before,.fa-close:before,.fa-times:before{content:"\f00d"}.fa-search-plus:before{content:"\f00e"}.fa-search-minus:before{content:"\f010"}.fa-power-off:before{content:"\f011"}.fa-signal:before{content:"\f012"}.fa-gear:before,.fa-cog:before{content:"\f013"}.fa-trash-o:before{content:"\f014"}.fa-home:before{content:"\f015"}.fa-file-o:before{content:"\f016"}.fa-clock-o:before{content:"\f017"}.fa-road:before{content:"\f018"}.fa-download:before{content:"\f019"}.fa-arrow-circle-o-down:before{content:"\f01a"}.fa-arrow-circle-o-up:before{content:"\f01b"}.fa-inbox:before{content:"\f01c"}.fa-play-circle-o:before{content:"\f01d"}.fa-rotate-right:before,.fa-repeat:before{content:"\f01e"}.fa-refresh:before{content:"\f021"}.fa-list-alt:before{content:"\f022"}.fa-lock:before{content:"\f023"}.fa-flag:before{content:"\f024"}.fa-headphones:before{content:"\f025"}.fa-volume-off:before{content:"\f026"}.fa-volume-down:before{content:"\f027"}.fa-volume-up:before{content:"\f028"}.fa-qrcode:before{content:"\f029"}.fa-barcode:before{content:"\f02a"}.fa-tag:before{content:"\f02b"}.fa-tags:before{content:"\f02c"}.fa-book:before{content:"\f02d"}.fa-bookmark:before{content:"\f02e"}.fa-print:before{content:"\f02f"}.fa-camera:before{content:"\f030"}.fa-font:before{content:"\f031"}.fa-bold:before{content:"\f032"}.fa-italic:before{content:"\f033"}.fa-text-height:before{content:"\f034"}.fa-text-width:before{content:"\f035"}.fa-align-left:before{content:"\f036"}.fa-align-center:before{content:"\f037"}.fa-align-right:before{content:"\f038"}.fa-align-justify:before{content:"\f039"}.fa-list:before{content:"\f03a"}.fa-dedent:before,.fa-outdent:before{content:"\f03b"}.fa-indent:before{content:"\f03c"}.fa-video-camera:before{content:"\f03d"}.fa-photo:before,.fa-image:before,.fa-picture-o:before{content:"\f03e"}.fa-pencil:before{content:"\f040"}.fa-map-marker:before{content:"\f041"}.fa-adjust:before{content:"\f042"}.fa-tint:before{content:"\f043"}.fa-edit:before,.fa-pencil-square-o:before{content:"\f044"}.fa-share-square-o:before{content:"\f045"}.fa-check-square-o:before{content:"\f046"}.fa-arrows:before{content:"\f047"}.fa-step-backward:before{content:"\f048"}.fa-fast-backward:before{content:"\f049"}.fa-backward:before{content:"\f04a"}.fa-play:before{content:"\f04b"}.fa-pause:before{content:"\f04c"}.fa-stop:before{content:"\f04d"}.fa-forward:before{content:"\f04e"}.fa-fast-forward:before{content:"\f050"}.fa-step-forward:before{content:"\f051"}.fa-eject:before{content:"\f052"}.fa-chevron-left:before{content:"\f053"}.fa-chevron-right:before{content:"\f054"}.fa-plus-circle:before{content:"\f055"}.fa-minus-circle:before{content:"\f056"}.fa-times-circle:before{content:"\f057"}.fa-check-circle:before{content:"\f058"}.fa-question-circle:before{content:"\f059"}.fa-info-circle:before{content:"\f05a"}.fa-crosshairs:before{content:"\f05b"}.fa-times-circle-o:before{content:"\f05c"}.fa-check-circle-o:before{content:"\f05d"}.fa-ban:before{content:"\f05e"}.fa-arrow-left:before{content:"\f060"}.fa-arrow-right:before{content:"\f061"}.fa-arrow-up:before{content:"\f062"}.fa-arrow-down:before{content:"\f063"}.fa-mail-forward:before,.fa-share:before{content:"\f064"}.fa-expand:before{content:"\f065"}.fa-compress:before{content:"\f066"}.fa-plus:before{content:"\f067"}.fa-minus:before{content:"\f068"}.fa-asterisk:before{content:"\f069"}.fa-exclamation-circle:before{content:"\f06a"}.fa-gift:before{content:"\f06b"}.fa-leaf:before{content:"\f06c"}.fa-fire:before{content:"\f06d"}.fa-eye:before{content:"\f06e"}.fa-eye-slash:before{content:"\f070"}.fa-warning:before,.fa-exclamation-triangle:before{content:"\f071"}.fa-plane:before{content:"\f072"}.fa-calendar:before{content:"\f073"}.fa-random:before{content:"\f074"}.fa-comment:before{content:"\f075"}.fa-magnet:before{content:"\f076"}.fa-chevron-up:before{content:"\f077"}.fa-chevron-down:before{content:"\f078"}.fa-retweet:before{content:"\f079"}.fa-shopping-cart:before{content:"\f07a"}.fa-folder:before{content:"\f07b"}.fa-folder-open:before{content:"\f07c"}.fa-arrows-v:before{content:"\f07d"}.fa-arrows-h:before{content:"\f07e"}.fa-bar-chart-o:before,.fa-bar-chart:before{content:"\f080"}.fa-twitter-square:before{content:"\f081"}.fa-facebook-square:before{content:"\f082"}.fa-camera-retro:before{content:"\f083"}.fa-key:before{content:"\f084"}.fa-gears:before,.fa-cogs:before{content:"\f085"}.fa-comments:before{content:"\f086"}.fa-thumbs-o-up:before{content:"\f087"}.fa-thumbs-o-down:before{content:"\f088"}.fa-star-half:before{content:"\f089"}.fa-heart-o:before{content:"\f08a"}.fa-sign-out:before{content:"\f08b"}.fa-linkedin-square:before{content:"\f08c"}.fa-thumb-tack:before{content:"\f08d"}.fa-external-link:before{content:"\f08e"}.fa-sign-in:before{content:"\f090"}.fa-trophy:before{content:"\f091"}.fa-github-square:before{content:"\f092"}.fa-upload:before{content:"\f093"}.fa-lemon-o:before{content:"\f094"}.fa-phone:before{content:"\f095"}.fa-square-o:before{content:"\f096"}.fa-bookmark-o:before{content:"\f097"}.fa-phone-square:before{content:"\f098"}.fa-twitter:before{content:"\f099"}.fa-facebook-f:before,.fa-facebook:before{content:"\f09a"}.fa-github:before{content:"\f09b"}.fa-unlock:before{content:"\f09c"}.fa-credit-card:before{content:"\f09d"}.fa-feed:before,.fa-rss:before{content:"\f09e"}.fa-hdd-o:before{content:"\f0a0"}.fa-bullhorn:before{content:"\f0a1"}.fa-bell:before{content:"\f0f3"}.fa-certificate:before{content:"\f0a3"}.fa-hand-o-right:before{content:"\f0a4"}.fa-hand-o-left:before{content:"\f0a5"}.fa-hand-o-up:before{content:"\f0a6"}.fa-hand-o-down:before{content:"\f0a7"}.fa-arrow-circle-left:before{content:"\f0a8"}.fa-arrow-circle-right:before{content:"\f0a9"}.fa-arrow-circle-up:before{content:"\f0aa"}.fa-arrow-circle-down:before{content:"\f0ab"}.fa-globe:before{content:"\f0ac"}.fa-wrench:before{content:"\f0ad"}.fa-tasks:before{content:"\f0ae"}.fa-filter:before{content:"\f0b0"}.fa-briefcase:before{content:"\f0b1"}.fa-arrows-alt:before{content:"\f0b2"}.fa-group:before,.fa-users:before{content:"\f0c0"}.fa-chain:before,.fa-link:before{content:"\f0c1"}.fa-cloud:before{content:"\f0c2"}.fa-flask:before{content:"\f0c3"}.fa-cut:before,.fa-scissors:before{content:"\f0c4"}.fa-copy:before,.fa-files-o:before{content:"\f0c5"}.fa-paperclip:before{content:"\f0c6"}.fa-save:before,.fa-floppy-o:before{content:"\f0c7"}.fa-square:before{content:"\f0c8"}.fa-navicon:before,.fa-reorder:before,.fa-bars:before{content:"\f0c9"}.fa-list-ul:before{content:"\f0ca"}.fa-list-ol:before{content:"\f0cb"}.fa-strikethrough:before{content:"\f0cc"}.fa-underline:before{content:"\f0cd"}.fa-table:before{content:"\f0ce"}.fa-magic:before{content:"\f0d0"}.fa-truck:before{content:"\f0d1"}.fa-pinterest:before{content:"\f0d2"}.fa-pinterest-square:before{content:"\f0d3"}.fa-google-plus-square:before{content:"\f0d4"}.fa-google-plus:before{content:"\f0d5"}.fa-money:before{content:"\f0d6"}.fa-caret-down:before{content:"\f0d7"}.fa-caret-up:before{content:"\f0d8"}.fa-caret-left:before{content:"\f0d9"}.fa-caret-right:before{content:"\f0da"}.fa-columns:before{content:"\f0db"}.fa-unsorted:before,.fa-sort:before{content:"\f0dc"}.fa-sort-down:before,.fa-sort-desc:before{content:"\f0dd"}.fa-sort-up:before,.fa-sort-asc:before{content:"\f0de"}.fa-envelope:before{content:"\f0e0"}.fa-linkedin:before{content:"\f0e1"}.fa-rotate-left:before,.fa-undo:before{content:"\f0e2"}.fa-legal:before,.fa-gavel:before{content:"\f0e3"}.fa-dashboard:before,.fa-tachometer:before{content:"\f0e4"}.fa-comment-o:before{content:"\f0e5"}.fa-comments-o:before{content:"\f0e6"}.fa-flash:before,.fa-bolt:before{content:"\f0e7"}.fa-sitemap:before{content:"\f0e8"}.fa-umbrella:before{content:"\f0e9"}.fa-paste:before,.fa-clipboard:before{content:"\f0ea"}.fa-lightbulb-o:before{content:"\f0eb"}.fa-exchange:before{content:"\f0ec"}.fa-cloud-download:before{content:"\f0ed"}.fa-cloud-upload:before{content:"\f0ee"}.fa-user-md:before{content:"\f0f0"}.fa-stethoscope:before{content:"\f0f1"}.fa-suitcase:before{content:"\f0f2"}.fa-bell-o:before{content:"\f0a2"}.fa-coffee:before{content:"\f0f4"}.fa-cutlery:before{content:"\f0f5"}.fa-file-text-o:before{content:"\f0f6"}.fa-building-o:before{content:"\f0f7"}.fa-hospital-o:before{content:"\f0f8"}.fa-ambulance:before{content:"\f0f9"}.fa-medkit:before{content:"\f0fa"}.fa-fighter-jet:before{content:"\f0fb"}.fa-beer:before{content:"\f0fc"}.fa-h-square:before{content:"\f0fd"}.fa-plus-square:before{content:"\f0fe"}.fa-angle-double-left:before{content:"\f100"}.fa-angle-double-right:before{content:"\f101"}.fa-angle-double-up:before{content:"\f102"}.fa-angle-double-down:before{content:"\f103"}.fa-angle-left:before{content:"\f104"}.fa-angle-right:before{content:"\f105"}.fa-angle-up:before{content:"\f106"}.fa-angle-down:before{content:"\f107"}.fa-desktop:before{content:"\f108"}.fa-laptop:before{content:"\f109"}.fa-tablet:before{content:"\f10a"}.fa-mobile-phone:before,.fa-mobile:before{content:"\f10b"}.fa-circle-o:before{content:"\f10c"}.fa-quote-left:before{content:"\f10d"}.fa-quote-right:before{content:"\f10e"}.fa-spinner:before{content:"\f110"}.fa-circle:before{content:"\f111"}.fa-mail-reply:before,.fa-reply:before{content:"\f112"}.fa-github-alt:before{content:"\f113"}.fa-folder-o:before{content:"\f114"}.fa-folder-open-o:before{content:"\f115"}.fa-smile-o:before{content:"\f118"}.fa-frown-o:before{content:"\f119"}.fa-meh-o:before{content:"\f11a"}.fa-gamepad:before{content:"\f11b"}.fa-keyboard-o:before{content:"\f11c"}.fa-flag-o:before{content:"\f11d"}.fa-flag-checkered:before{content:"\f11e"}.fa-terminal:before{content:"\f120"}.fa-code:before{content:"\f121"}.fa-mail-reply-all:before,.fa-reply-all:before{content:"\f122"}.fa-star-half-empty:before,.fa-star-half-full:before,.fa-star-half-o:before{content:"\f123"}.fa-location-arrow:before{content:"\f124"}.fa-crop:before{content:"\f125"}.fa-code-fork:before{content:"\f126"}.fa-unlink:before,.fa-chain-broken:before{content:"\f127"}.fa-question:before{content:"\f128"}.fa-info:before{content:"\f129"}.fa-exclamation:before{content:"\f12a"}.fa-superscript:before{content:"\f12b"}.fa-subscript:before{content:"\f12c"}.fa-eraser:before{content:"\f12d"}.fa-puzzle-piece:before{content:"\f12e"}.fa-microphone:before{content:"\f130"}.fa-microphone-slash:before{content:"\f131"}.fa-shield:before{content:"\f132"}.fa-calendar-o:before{content:"\f133"}.fa-fire-extinguisher:before{content:"\f134"}.fa-rocket:before{content:"\f135"}.fa-maxcdn:before{content:"\f136"}.fa-chevron-circle-left:before{content:"\f137"}.fa-chevron-circle-right:before{content:"\f138"}.fa-chevron-circle-up:before{content:"\f139"}.fa-chevron-circle-down:before{content:"\f13a"}.fa-html5:before{content:"\f13b"}.fa-css3:before{content:"\f13c"}.fa-anchor:before{content:"\f13d"}.fa-unlock-alt:before{content:"\f13e"}.fa-bullseye:before{content:"\f140"}.fa-ellipsis-h:before{content:"\f141"}.fa-ellipsis-v:before{content:"\f142"}.fa-rss-square:before{content:"\f143"}.fa-play-circle:before{content:"\f144"}.fa-ticket:before{content:"\f145"}.fa-minus-square:before{content:"\f146"}.fa-minus-square-o:before{content:"\f147"}.fa-level-up:before{content:"\f148"}.fa-level-down:before{content:"\f149"}.fa-check-square:before{content:"\f14a"}.fa-pencil-square:before{content:"\f14b"}.fa-external-link-square:before{content:"\f14c"}.fa-share-square:before{content:"\f14d"}.fa-compass:before{content:"\f14e"}.fa-toggle-down:before,.fa-caret-square-o-down:before{content:"\f150"}.fa-toggle-up:before,.fa-caret-square-o-up:before{content:"\f151"}.fa-toggle-right:before,.fa-caret-square-o-right:before{content:"\f152"}.fa-euro:before,.fa-eur:before{content:"\f153"}.fa-gbp:before{content:"\f154"}.fa-dollar:before,.fa-usd:before{content:"\f155"}.fa-rupee:before,.fa-inr:before{content:"\f156"}.fa-cny:before,.fa-rmb:before,.fa-yen:before,.fa-jpy:before{content:"\f157"}.fa-ruble:before,.fa-rouble:before,.fa-rub:before{content:"\f158"}.fa-won:before,.fa-krw:before{content:"\f159"}.fa-bitcoin:before,.fa-btc:before{content:"\f15a"}.fa-file:before{content:"\f15b"}.fa-file-text:before{content:"\f15c"}.fa-sort-alpha-asc:before{content:"\f15d"}.fa-sort-alpha-desc:before{content:"\f15e"}.fa-sort-amount-asc:before{content:"\f160"}.fa-sort-amount-desc:before{content:"\f161"}.fa-sort-numeric-asc:before{content:"\f162"}.fa-sort-numeric-desc:before{content:"\f163"}.fa-thumbs-up:before{content:"\f164"}.fa-thumbs-down:before{content:"\f165"}.fa-youtube-square:before{content:"\f166"}.fa-youtube:before{content:"\f167"}.fa-xing:before{content:"\f168"}.fa-xing-square:before{content:"\f169"}.fa-youtube-play:before{content:"\f16a"}.fa-dropbox:before{content:"\f16b"}.fa-stack-overflow:before{content:"\f16c"}.fa-instagram:before{content:"\f16d"}.fa-flickr:before{content:"\f16e"}.fa-adn:before{content:"\f170"}.fa-bitbucket:before{content:"\f171"}.fa-bitbucket-square:before{content:"\f172"}.fa-tumblr:before{content:"\f173"}.fa-tumblr-square:before{content:"\f174"}.fa-long-arrow-down:before{content:"\f175"}.fa-long-arrow-up:before{content:"\f176"}.fa-long-arrow-left:before{content:"\f177"}.fa-long-arrow-right:before{content:"\f178"}.fa-apple:before{content:"\f179"}.fa-windows:before{content:"\f17a"}.fa-android:before{content:"\f17b"}.fa-linux:before{content:"\f17c"}.fa-dribbble:before{content:"\f17d"}.fa-skype:before{content:"\f17e"}.fa-foursquare:before{content:"\f180"}.fa-trello:before{content:"\f181"}.fa-female:before{content:"\f182"}.fa-male:before{content:"\f183"}.fa-gittip:before,.fa-gratipay:before{content:"\f184"}.fa-sun-o:before{content:"\f185"}.fa-moon-o:before{content:"\f186"}.fa-archive:before{content:"\f187"}.fa-bug:before{content:"\f188"}.fa-vk:before{content:"\f189"}.fa-weibo:before{content:"\f18a"}.fa-renren:before{content:"\f18b"}.fa-pagelines:before{content:"\f18c"}.fa-stack-exchange:before{content:"\f18d"}.fa-arrow-circle-o-right:before{content:"\f18e"}.fa-arrow-circle-o-left:before{content:"\f190"}.fa-toggle-left:before,.fa-caret-square-o-left:before{content:"\f191"}.fa-dot-circle-o:before{content:"\f192"}.fa-wheelchair:before{content:"\f193"}.fa-vimeo-square:before{content:"\f194"}.fa-turkish-lira:before,.fa-try:before{content:"\f195"}.fa-plus-square-o:before{content:"\f196"}.fa-space-shuttle:before{content:"\f197"}.fa-slack:before{content:"\f198"}.fa-envelope-square:before{content:"\f199"}.fa-wordpress:before{content:"\f19a"}.fa-openid:before{content:"\f19b"}.fa-institution:before,.fa-bank:before,.fa-university:before{content:"\f19c"}.fa-mortar-board:before,.fa-graduation-cap:before{content:"\f19d"}.fa-yahoo:before{content:"\f19e"}.fa-google:before{content:"\f1a0"}.fa-reddit:before{content:"\f1a1"}.fa-reddit-square:before{content:"\f1a2"}.fa-stumbleupon-circle:before{content:"\f1a3"}.fa-stumbleupon:before{content:"\f1a4"}.fa-delicious:before{content:"\f1a5"}.fa-digg:before{content:"\f1a6"}.fa-pied-piper-pp:before{content:"\f1a7"}.fa-pied-piper-alt:before{content:"\f1a8"}.fa-drupal:before{content:"\f1a9"}.fa-joomla:before{content:"\f1aa"}.fa-language:before{content:"\f1ab"}.fa-fax:before{content:"\f1ac"}.fa-building:before{content:"\f1ad"}.fa-child:before{content:"\f1ae"}.fa-paw:before{content:"\f1b0"}.fa-spoon:before{content:"\f1b1"}.fa-cube:before{content:"\f1b2"}.fa-cubes:before{content:"\f1b3"}.fa-behance:before{content:"\f1b4"}.fa-behance-square:before{content:"\f1b5"}.fa-steam:before{content:"\f1b6"}.fa-steam-square:before{content:"\f1b7"}.fa-recycle:before{content:"\f1b8"}.fa-automobile:before,.fa-car:before{content:"\f1b9"}.fa-cab:before,.fa-taxi:before{content:"\f1ba"}.fa-tree:before{content:"\f1bb"}.fa-spotify:before{content:"\f1bc"}.fa-deviantart:before{content:"\f1bd"}.fa-soundcloud:before{content:"\f1be"}.fa-database:before{content:"\f1c0"}.fa-file-pdf-o:before{content:"\f1c1"}.fa-file-word-o:before{content:"\f1c2"}.fa-file-excel-o:before{content:"\f1c3"}.fa-file-powerpoint-o:before{content:"\f1c4"}.fa-file-photo-o:before,.fa-file-picture-o:before,.fa-file-image-o:before{content:"\f1c5"}.fa-file-zip-o:before,.fa-file-archive-o:before{content:"\f1c6"}.fa-file-sound-o:before,.fa-file-audio-o:before{content:"\f1c7"}.fa-file-movie-o:before,.fa-file-video-o:before{content:"\f1c8"}.fa-file-code-o:before{content:"\f1c9"}.fa-vine:before{content:"\f1ca"}.fa-codepen:before{content:"\f1cb"}.fa-jsfiddle:before{content:"\f1cc"}.fa-life-bouy:before,.fa-life-buoy:before,.fa-life-saver:before,.fa-support:before,.fa-life-ring:before{content:"\f1cd"}.fa-circle-o-notch:before{content:"\f1ce"}.fa-ra:before,.fa-resistance:before,.fa-rebel:before{content:"\f1d0"}.fa-ge:before,.fa-empire:before{content:"\f1d1"}.fa-git-square:before{content:"\f1d2"}.fa-git:before{content:"\f1d3"}.fa-y-combinator-square:before,.fa-yc-square:before,.fa-hacker-news:before{content:"\f1d4"}.fa-tencent-weibo:before{content:"\f1d5"}.fa-qq:before{content:"\f1d6"}.fa-wechat:before,.fa-weixin:before{content:"\f1d7"}.fa-send:before,.fa-paper-plane:before{content:"\f1d8"}.fa-send-o:before,.fa-paper-plane-o:before{content:"\f1d9"}.fa-history:before{content:"\f1da"}.fa-circle-thin:before{content:"\f1db"}.fa-header:before{content:"\f1dc"}.fa-paragraph:before{content:"\f1dd"}.fa-sliders:before{content:"\f1de"}.fa-share-alt:before{content:"\f1e0"}.fa-share-alt-square:before{content:"\f1e1"}.fa-bomb:before{content:"\f1e2"}.fa-soccer-ball-o:before,.fa-futbol-o:before{content:"\f1e3"}.fa-tty:before{content:"\f1e4"}.fa-binoculars:before{content:"\f1e5"}.fa-plug:before{content:"\f1e6"}.fa-slideshare:before{content:"\f1e7"}.fa-twitch:before{content:"\f1e8"}.fa-yelp:before{content:"\f1e9"}.fa-newspaper-o:before{content:"\f1ea"}.fa-wifi:before{content:"\f1eb"}.fa-calculator:before{content:"\f1ec"}.fa-paypal:before{content:"\f1ed"}.fa-google-wallet:before{content:"\f1ee"}.fa-cc-visa:before{content:"\f1f0"}.fa-cc-mastercard:before{content:"\f1f1"}.fa-cc-discover:before{content:"\f1f2"}.fa-cc-amex:before{content:"\f1f3"}.fa-cc-paypal:before{content:"\f1f4"}.fa-cc-stripe:before{content:"\f1f5"}.fa-bell-slash:before{content:"\f1f6"}.fa-bell-slash-o:before{content:"\f1f7"}.fa-trash:before{content:"\f1f8"}.fa-copyright:before{content:"\f1f9"}.fa-at:before{content:"\f1fa"}.fa-eyedropper:before{content:"\f1fb"}.fa-paint-brush:before{content:"\f1fc"}.fa-birthday-cake:before{content:"\f1fd"}.fa-area-chart:before{content:"\f1fe"}.fa-pie-chart:before{content:"\f200"}.fa-line-chart:before{content:"\f201"}.fa-lastfm:before{content:"\f202"}.fa-lastfm-square:before{content:"\f203"}.fa-toggle-off:before{content:"\f204"}.fa-toggle-on:before{content:"\f205"}.fa-bicycle:before{content:"\f206"}.fa-bus:before{content:"\f207"}.fa-ioxhost:before{content:"\f208"}.fa-angellist:before{content:"\f209"}.fa-cc:before{content:"\f20a"}.fa-shekel:before,.fa-sheqel:before,.fa-ils:before{content:"\f20b"}.fa-meanpath:before{content:"\f20c"}.fa-buysellads:before{content:"\f20d"}.fa-connectdevelop:before{content:"\f20e"}.fa-dashcube:before{content:"\f210"}.fa-forumbee:before{content:"\f211"}.fa-leanpub:before{content:"\f212"}.fa-sellsy:before{content:"\f213"}.fa-shirtsinbulk:before{content:"\f214"}.fa-simplybuilt:before{content:"\f215"}.fa-skyatlas:before{content:"\f216"}.fa-cart-plus:before{content:"\f217"}.fa-cart-arrow-down:before{content:"\f218"}.fa-diamond:before{content:"\f219"}.fa-ship:before{content:"\f21a"}.fa-user-secret:before{content:"\f21b"}.fa-motorcycle:before{content:"\f21c"}.fa-street-view:before{content:"\f21d"}.fa-heartbeat:before{content:"\f21e"}.fa-venus:before{content:"\f221"}.fa-mars:before{content:"\f222"}.fa-mercury:before{content:"\f223"}.fa-intersex:before,.fa-transgender:before{content:"\f224"}.fa-transgender-alt:before{content:"\f225"}.fa-venus-double:before{content:"\f226"}.fa-mars-double:before{content:"\f227"}.fa-venus-mars:before{content:"\f228"}.fa-mars-stroke:before{content:"\f229"}.fa-mars-stroke-v:before{content:"\f22a"}.fa-mars-stroke-h:before{content:"\f22b"}.fa-neuter:before{content:"\f22c"}.fa-genderless:before{content:"\f22d"}.fa-facebook-official:before{content:"\f230"}.fa-pinterest-p:before{content:"\f231"}.fa-whatsapp:before{content:"\f232"}.fa-server:before{content:"\f233"}.fa-user-plus:before{content:"\f234"}.fa-user-times:before{content:"\f235"}.fa-hotel:before,.fa-bed:before{content:"\f236"}.fa-viacoin:before{content:"\f237"}.fa-train:before{content:"\f238"}.fa-subway:before{content:"\f239"}.fa-medium:before{content:"\f23a"}.fa-yc:before,.fa-y-combinator:before{content:"\f23b"}.fa-optin-monster:before{content:"\f23c"}.fa-opencart:before{content:"\f23d"}.fa-expeditedssl:before{content:"\f23e"}.fa-battery-4:before,.fa-battery:before,.fa-battery-full:before{content:"\f240"}.fa-battery-3:before,.fa-battery-three-quarters:before{content:"\f241"}.fa-battery-2:before,.fa-battery-half:before{content:"\f242"}.fa-battery-1:before,.fa-battery-quarter:before{content:"\f243"}.fa-battery-0:before,.fa-battery-empty:before{content:"\f244"}.fa-mouse-pointer:before{content:"\f245"}.fa-i-cursor:before{content:"\f246"}.fa-object-group:before{content:"\f247"}.fa-object-ungroup:before{content:"\f248"}.fa-sticky-note:before{content:"\f249"}.fa-sticky-note-o:before{content:"\f24a"}.fa-cc-jcb:before{content:"\f24b"}.fa-cc-diners-club:before{content:"\f24c"}.fa-clone:before{content:"\f24d"}.fa-balance-scale:before{content:"\f24e"}.fa-hourglass-o:before{content:"\f250"}.fa-hourglass-1:before,.fa-hourglass-start:before{content:"\f251"}.fa-hourglass-2:before,.fa-hourglass-half:before{content:"\f252"}.fa-hourglass-3:before,.fa-hourglass-end:before{content:"\f253"}.fa-hourglass:before{content:"\f254"}.fa-hand-grab-o:before,.fa-hand-rock-o:before{content:"\f255"}.fa-hand-stop-o:before,.fa-hand-paper-o:before{content:"\f256"}.fa-hand-scissors-o:before{content:"\f257"}.fa-hand-lizard-o:before{content:"\f258"}.fa-hand-spock-o:before{content:"\f259"}.fa-hand-pointer-o:before{content:"\f25a"}.fa-hand-peace-o:before{content:"\f25b"}.fa-trademark:before{content:"\f25c"}.fa-registered:before{content:"\f25d"}.fa-creative-commons:before{content:"\f25e"}.fa-gg:before{content:"\f260"}.fa-gg-circle:before{content:"\f261"}.fa-tripadvisor:before{content:"\f262"}.fa-odnoklassniki:before{content:"\f263"}.fa-odnoklassniki-square:before{content:"\f264"}.fa-get-pocket:before{content:"\f265"}.fa-wikipedia-w:before{content:"\f266"}.fa-safari:before{content:"\f267"}.fa-chrome:before{content:"\f268"}.fa-firefox:before{content:"\f269"}.fa-opera:before{content:"\f26a"}.fa-internet-explorer:before{content:"\f26b"}.fa-tv:before,.fa-television:before{content:"\f26c"}.fa-contao:before{content:"\f26d"}.fa-500px:before{content:"\f26e"}.fa-amazon:before{content:"\f270"}.fa-calendar-plus-o:before{content:"\f271"}.fa-calendar-minus-o:before{content:"\f272"}.fa-calendar-times-o:before{content:"\f273"}.fa-calendar-check-o:before{content:"\f274"}.fa-industry:before{content:"\f275"}.fa-map-pin:before{content:"\f276"}.fa-map-signs:before{content:"\f277"}.fa-map-o:before{content:"\f278"}.fa-map:before{content:"\f279"}.fa-commenting:before{content:"\f27a"}.fa-commenting-o:before{content:"\f27b"}.fa-houzz:before{content:"\f27c"}.fa-vimeo:before{content:"\f27d"}.fa-black-tie:before{content:"\f27e"}.fa-fonticons:before{content:"\f280"}.fa-reddit-alien:before{content:"\f281"}.fa-edge:before{content:"\f282"}.fa-credit-card-alt:before{content:"\f283"}.fa-codiepie:before{content:"\f284"}.fa-modx:before{content:"\f285"}.fa-fort-awesome:before{content:"\f286"}.fa-usb:before{content:"\f287"}.fa-product-hunt:before{content:"\f288"}.fa-mixcloud:before{content:"\f289"}.fa-scribd:before{content:"\f28a"}.fa-pause-circle:before{content:"\f28b"}.fa-pause-circle-o:before{content:"\f28c"}.fa-stop-circle:before{content:"\f28d"}.fa-stop-circle-o:before{content:"\f28e"}.fa-shopping-bag:before{content:"\f290"}.fa-shopping-basket:before{content:"\f291"}.fa-hashtag:before{content:"\f292"}.fa-bluetooth:before{content:"\f293"}.fa-bluetooth-b:before{content:"\f294"}.fa-percent:before{content:"\f295"}.fa-gitlab:before{content:"\f296"}.fa-wpbeginner:before{content:"\f297"}.fa-wpforms:before{content:"\f298"}.fa-envira:before{content:"\f299"}.fa-universal-access:before{content:"\f29a"}.fa-wheelchair-alt:before{content:"\f29b"}.fa-question-circle-o:before{content:"\f29c"}.fa-blind:before{content:"\f29d"}.fa-audio-description:before{content:"\f29e"}.fa-volume-control-phone:before{content:"\f2a0"}.fa-braille:before{content:"\f2a1"}.fa-assistive-listening-systems:before{content:"\f2a2"}.fa-asl-interpreting:before,.fa-american-sign-language-interpreting:before{content:"\f2a3"}.fa-deafness:before,.fa-hard-of-hearing:before,.fa-deaf:before{content:"\f2a4"}.fa-glide:before{content:"\f2a5"}.fa-glide-g:before{content:"\f2a6"}.fa-signing:before,.fa-sign-language:before{content:"\f2a7"}.fa-low-vision:before{content:"\f2a8"}.fa-viadeo:before{content:"\f2a9"}.fa-viadeo-square:before{content:"\f2aa"}.fa-snapchat:before{content:"\f2ab"}.fa-snapchat-ghost:before{content:"\f2ac"}.fa-snapchat-square:before{content:"\f2ad"}.fa-pied-piper:before{content:"\f2ae"}.fa-first-order:before{content:"\f2b0"}.fa-yoast:before{content:"\f2b1"}.fa-themeisle:before{content:"\f2b2"}.fa-google-plus-circle:before,.fa-google-plus-official:before{content:"\f2b3"}.fa-fa:before,.fa-font-awesome:before{content:"\f2b4"}.fa-handshake-o:before{content:"\f2b5"}.fa-envelope-open:before{content:"\f2b6"}.fa-envelope-open-o:before{content:"\f2b7"}.fa-linode:before{content:"\f2b8"}.fa-address-book:before{content:"\f2b9"}.fa-address-book-o:before{content:"\f2ba"}.fa-vcard:before,.fa-address-card:before{content:"\f2bb"}.fa-vcard-o:before,.fa-address-card-o:before{content:"\f2bc"}.fa-user-circle:before{content:"\f2bd"}.fa-user-circle-o:before{content:"\f2be"}.fa-user-o:before{content:"\f2c0"}.fa-id-badge:before{content:"\f2c1"}.fa-drivers-license:before,.fa-id-card:before{content:"\f2c2"}.fa-drivers-license-o:before,.fa-id-card-o:before{content:"\f2c3"}.fa-quora:before{content:"\f2c4"}.fa-free-code-camp:before{content:"\f2c5"}.fa-telegram:before{content:"\f2c6"}.fa-thermometer-4:before,.fa-thermometer:before,.fa-thermometer-full:before{content:"\f2c7"}.fa-thermometer-3:before,.fa-thermometer-three-quarters:before{content:"\f2c8"}.fa-thermometer-2:before,.fa-thermometer-half:before{content:"\f2c9"}.fa-thermometer-1:before,.fa-thermometer-quarter:before{content:"\f2ca"}.fa-thermometer-0:before,.fa-thermometer-empty:before{content:"\f2cb"}.fa-shower:before{content:"\f2cc"}.fa-bathtub:before,.fa-s15:before,.fa-bath:before{content:"\f2cd"}.fa-podcast:before{content:"\f2ce"}.fa-window-maximize:before{content:"\f2d0"}.fa-window-minimize:before{content:"\f2d1"}.fa-window-restore:before{content:"\f2d2"}.fa-times-rectangle:before,.fa-window-close:before{content:"\f2d3"}.fa-times-rectangle-o:before,.fa-window-close-o:before{content:"\f2d4"}.fa-bandcamp:before{content:"\f2d5"}.fa-grav:before{content:"\f2d6"}.fa-etsy:before{content:"\f2d7"}.fa-imdb:before{content:"\f2d8"}.fa-ravelry:before{content:"\f2d9"}.fa-eercast:before{content:"\f2da"}.fa-microchip:before{content:"\f2db"}.fa-snowflake-o:before{content:"\f2dc"}.fa-superpowers:before{content:"\f2dd"}.fa-wpexplorer:before{content:"\f2de"}.fa-meetup:before{content:"\f2e0"}.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} 5 | -------------------------------------------------------------------------------- /app/static/assets/css/noscript.css: -------------------------------------------------------------------------------- 1 | /* 2 | Phantom by HTML5 UP 3 | html5up.net | @ajlkn 4 | Free for personal and commercial use under the CCA 3.0 license (html5up.net/license) 5 | */ 6 | 7 | /* Tiles */ 8 | 9 | body.is-preload .tiles article { 10 | -moz-transform: none; 11 | -webkit-transform: none; 12 | -ms-transform: none; 13 | transform: none; 14 | opacity: 1; 15 | } -------------------------------------------------------------------------------- /app/static/assets/fonts/FontAwesome.otf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/app-generator/flask-boilerplate-flaskplay/62b50bb52b63b307410fac5dbd38c81705bd9d31/app/static/assets/fonts/FontAwesome.otf -------------------------------------------------------------------------------- /app/static/assets/fonts/fontawesome-webfont.eot: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/app-generator/flask-boilerplate-flaskplay/62b50bb52b63b307410fac5dbd38c81705bd9d31/app/static/assets/fonts/fontawesome-webfont.eot -------------------------------------------------------------------------------- /app/static/assets/fonts/fontawesome-webfont.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/app-generator/flask-boilerplate-flaskplay/62b50bb52b63b307410fac5dbd38c81705bd9d31/app/static/assets/fonts/fontawesome-webfont.ttf -------------------------------------------------------------------------------- /app/static/assets/fonts/fontawesome-webfont.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/app-generator/flask-boilerplate-flaskplay/62b50bb52b63b307410fac5dbd38c81705bd9d31/app/static/assets/fonts/fontawesome-webfont.woff -------------------------------------------------------------------------------- /app/static/assets/fonts/fontawesome-webfont.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/app-generator/flask-boilerplate-flaskplay/62b50bb52b63b307410fac5dbd38c81705bd9d31/app/static/assets/fonts/fontawesome-webfont.woff2 -------------------------------------------------------------------------------- /app/static/assets/js/breakpoints.min.js: -------------------------------------------------------------------------------- 1 | /* breakpoints.js v1.0 | @ajlkn | MIT licensed */ 2 | var breakpoints=function(){"use strict";function e(e){t.init(e)}var t={list:null,media:{},events:[],init:function(e){t.list=e,window.addEventListener("resize",t.poll),window.addEventListener("orientationchange",t.poll),window.addEventListener("load",t.poll),window.addEventListener("fullscreenchange",t.poll)},active:function(e){var n,a,s,i,r,d,c;if(!(e in t.media)){if(">="==e.substr(0,2)?(a="gte",n=e.substr(2)):"<="==e.substr(0,2)?(a="lte",n=e.substr(2)):">"==e.substr(0,1)?(a="gt",n=e.substr(1)):"<"==e.substr(0,1)?(a="lt",n=e.substr(1)):"!"==e.substr(0,1)?(a="not",n=e.substr(1)):(a="eq",n=e),n&&n in t.list)if(i=t.list[n],Array.isArray(i)){if(r=parseInt(i[0]),d=parseInt(i[1]),isNaN(r)){if(isNaN(d))return;c=i[1].substr(String(d).length)}else c=i[0].substr(String(r).length);if(isNaN(r))switch(a){case"gte":s="screen";break;case"lte":s="screen and (max-width: "+d+c+")";break;case"gt":s="screen and (min-width: "+(d+1)+c+")";break;case"lt":s="screen and (max-width: -1px)";break;case"not":s="screen and (min-width: "+(d+1)+c+")";break;default:s="screen and (max-width: "+d+c+")"}else if(isNaN(d))switch(a){case"gte":s="screen and (min-width: "+r+c+")";break;case"lte":s="screen";break;case"gt":s="screen and (max-width: -1px)";break;case"lt":s="screen and (max-width: "+(r-1)+c+")";break;case"not":s="screen and (max-width: "+(r-1)+c+")";break;default:s="screen and (min-width: "+r+c+")"}else switch(a){case"gte":s="screen and (min-width: "+r+c+")";break;case"lte":s="screen and (max-width: "+d+c+")";break;case"gt":s="screen and (min-width: "+(d+1)+c+")";break;case"lt":s="screen and (max-width: "+(r-1)+c+")";break;case"not":s="screen and (max-width: "+(r-1)+c+"), screen and (min-width: "+(d+1)+c+")";break;default:s="screen and (min-width: "+r+c+") and (max-width: "+d+c+")"}}else s="("==i.charAt(0)?"screen and "+i:i;t.media[e]=!!s&&s}return t.media[e]!==!1&&window.matchMedia(t.media[e]).matches},on:function(e,n){t.events.push({query:e,handler:n,state:!1}),t.active(e)&&n()},poll:function(){var e,n;for(e=0;e0:!!("ontouchstart"in window),e.mobile="wp"==e.os||"android"==e.os||"ios"==e.os||"bb"==e.os}};return e.init(),e}();!function(e,n){"function"==typeof define&&define.amd?define([],n):"object"==typeof exports?module.exports=n():e.browser=n()}(this,function(){return browser}); 3 | -------------------------------------------------------------------------------- /app/static/assets/js/main.js: -------------------------------------------------------------------------------- 1 | /* 2 | Phantom by HTML5 UP 3 | html5up.net | @ajlkn 4 | Free for personal and commercial use under the CCA 3.0 license (html5up.net/license) 5 | */ 6 | 7 | (function($) { 8 | 9 | var $window = $(window), 10 | $body = $('body'); 11 | 12 | // Breakpoints. 13 | breakpoints({ 14 | xlarge: [ '1281px', '1680px' ], 15 | large: [ '981px', '1280px' ], 16 | medium: [ '737px', '980px' ], 17 | small: [ '481px', '736px' ], 18 | xsmall: [ '361px', '480px' ], 19 | xxsmall: [ null, '360px' ] 20 | }); 21 | 22 | // Play initial animations on page load. 23 | $window.on('load', function() { 24 | window.setTimeout(function() { 25 | $body.removeClass('is-preload'); 26 | }, 100); 27 | }); 28 | 29 | // Touch? 30 | if (browser.mobile) 31 | $body.addClass('is-touch'); 32 | 33 | // Forms. 34 | var $form = $('form'); 35 | 36 | // Auto-resizing textareas. 37 | $form.find('textarea').each(function() { 38 | 39 | var $this = $(this), 40 | $wrapper = $('
'), 41 | $submits = $this.find('input[type="submit"]'); 42 | 43 | $this 44 | .wrap($wrapper) 45 | .attr('rows', 1) 46 | .css('overflow', 'hidden') 47 | .css('resize', 'none') 48 | .on('keydown', function(event) { 49 | 50 | if (event.keyCode == 13 51 | && event.ctrlKey) { 52 | 53 | event.preventDefault(); 54 | event.stopPropagation(); 55 | 56 | $(this).blur(); 57 | 58 | } 59 | 60 | }) 61 | .on('blur focus', function() { 62 | $this.val($.trim($this.val())); 63 | }) 64 | .on('input blur focus --init', function() { 65 | 66 | $wrapper 67 | .css('height', $this.height()); 68 | 69 | $this 70 | .css('height', 'auto') 71 | .css('height', $this.prop('scrollHeight') + 'px'); 72 | 73 | }) 74 | .on('keyup', function(event) { 75 | 76 | if (event.keyCode == 9) 77 | $this 78 | .select(); 79 | 80 | }) 81 | .triggerHandler('--init'); 82 | 83 | // Fix. 84 | if (browser.name == 'ie' 85 | || browser.mobile) 86 | $this 87 | .css('max-height', '10em') 88 | .css('overflow-y', 'auto'); 89 | 90 | }); 91 | 92 | // Menu. 93 | var $menu = $('#menu'); 94 | 95 | $menu.wrapInner('
'); 96 | 97 | $menu._locked = false; 98 | 99 | $menu._lock = function() { 100 | 101 | if ($menu._locked) 102 | return false; 103 | 104 | $menu._locked = true; 105 | 106 | window.setTimeout(function() { 107 | $menu._locked = false; 108 | }, 350); 109 | 110 | return true; 111 | 112 | }; 113 | 114 | $menu._show = function() { 115 | 116 | if ($menu._lock()) 117 | $body.addClass('is-menu-visible'); 118 | 119 | }; 120 | 121 | $menu._hide = function() { 122 | 123 | if ($menu._lock()) 124 | $body.removeClass('is-menu-visible'); 125 | 126 | }; 127 | 128 | $menu._toggle = function() { 129 | 130 | if ($menu._lock()) 131 | $body.toggleClass('is-menu-visible'); 132 | 133 | }; 134 | 135 | $menu 136 | .appendTo($body) 137 | .on('click', function(event) { 138 | event.stopPropagation(); 139 | }) 140 | .on('click', 'a', function(event) { 141 | 142 | var href = $(this).attr('href'); 143 | 144 | event.preventDefault(); 145 | event.stopPropagation(); 146 | 147 | // Hide. 148 | $menu._hide(); 149 | 150 | // Redirect. 151 | if (href == '#menu') 152 | return; 153 | 154 | window.setTimeout(function() { 155 | window.location.href = href; 156 | }, 350); 157 | 158 | }) 159 | .append('Close'); 160 | 161 | $body 162 | .on('click', 'a[href="#menu"]', function(event) { 163 | 164 | event.stopPropagation(); 165 | event.preventDefault(); 166 | 167 | // Toggle. 168 | $menu._toggle(); 169 | 170 | }) 171 | .on('click', function(event) { 172 | 173 | // Hide. 174 | $menu._hide(); 175 | 176 | }) 177 | .on('keydown', function(event) { 178 | 179 | // Hide on escape. 180 | if (event.keyCode == 27) 181 | $menu._hide(); 182 | 183 | }); 184 | 185 | })(jQuery); -------------------------------------------------------------------------------- /app/static/assets/js/util.js: -------------------------------------------------------------------------------- 1 | (function($) { 2 | 3 | /** 4 | * Generate an indented list of links from a nav. Meant for use with panel(). 5 | * @return {jQuery} jQuery object. 6 | */ 7 | $.fn.navList = function() { 8 | 9 | var $this = $(this); 10 | $a = $this.find('a'), 11 | b = []; 12 | 13 | $a.each(function() { 14 | 15 | var $this = $(this), 16 | indent = Math.max(0, $this.parents('li').length - 1), 17 | href = $this.attr('href'), 18 | target = $this.attr('target'); 19 | 20 | b.push( 21 | '' + 26 | '' + 27 | $this.text() + 28 | '' 29 | ); 30 | 31 | }); 32 | 33 | return b.join(''); 34 | 35 | }; 36 | 37 | /** 38 | * Panel-ify an element. 39 | * @param {object} userConfig User config. 40 | * @return {jQuery} jQuery object. 41 | */ 42 | $.fn.panel = function(userConfig) { 43 | 44 | // No elements? 45 | if (this.length == 0) 46 | return $this; 47 | 48 | // Multiple elements? 49 | if (this.length > 1) { 50 | 51 | for (var i=0; i < this.length; i++) 52 | $(this[i]).panel(userConfig); 53 | 54 | return $this; 55 | 56 | } 57 | 58 | // Vars. 59 | var $this = $(this), 60 | $body = $('body'), 61 | $window = $(window), 62 | id = $this.attr('id'), 63 | config; 64 | 65 | // Config. 66 | config = $.extend({ 67 | 68 | // Delay. 69 | delay: 0, 70 | 71 | // Hide panel on link click. 72 | hideOnClick: false, 73 | 74 | // Hide panel on escape keypress. 75 | hideOnEscape: false, 76 | 77 | // Hide panel on swipe. 78 | hideOnSwipe: false, 79 | 80 | // Reset scroll position on hide. 81 | resetScroll: false, 82 | 83 | // Reset forms on hide. 84 | resetForms: false, 85 | 86 | // Side of viewport the panel will appear. 87 | side: null, 88 | 89 | // Target element for "class". 90 | target: $this, 91 | 92 | // Class to toggle. 93 | visibleClass: 'visible' 94 | 95 | }, userConfig); 96 | 97 | // Expand "target" if it's not a jQuery object already. 98 | if (typeof config.target != 'jQuery') 99 | config.target = $(config.target); 100 | 101 | // Panel. 102 | 103 | // Methods. 104 | $this._hide = function(event) { 105 | 106 | // Already hidden? Bail. 107 | if (!config.target.hasClass(config.visibleClass)) 108 | return; 109 | 110 | // If an event was provided, cancel it. 111 | if (event) { 112 | 113 | event.preventDefault(); 114 | event.stopPropagation(); 115 | 116 | } 117 | 118 | // Hide. 119 | config.target.removeClass(config.visibleClass); 120 | 121 | // Post-hide stuff. 122 | window.setTimeout(function() { 123 | 124 | // Reset scroll position. 125 | if (config.resetScroll) 126 | $this.scrollTop(0); 127 | 128 | // Reset forms. 129 | if (config.resetForms) 130 | $this.find('form').each(function() { 131 | this.reset(); 132 | }); 133 | 134 | }, config.delay); 135 | 136 | }; 137 | 138 | // Vendor fixes. 139 | $this 140 | .css('-ms-overflow-style', '-ms-autohiding-scrollbar') 141 | .css('-webkit-overflow-scrolling', 'touch'); 142 | 143 | // Hide on click. 144 | if (config.hideOnClick) { 145 | 146 | $this.find('a') 147 | .css('-webkit-tap-highlight-color', 'rgba(0,0,0,0)'); 148 | 149 | $this 150 | .on('click', 'a', function(event) { 151 | 152 | var $a = $(this), 153 | href = $a.attr('href'), 154 | target = $a.attr('target'); 155 | 156 | if (!href || href == '#' || href == '' || href == '#' + id) 157 | return; 158 | 159 | // Cancel original event. 160 | event.preventDefault(); 161 | event.stopPropagation(); 162 | 163 | // Hide panel. 164 | $this._hide(); 165 | 166 | // Redirect to href. 167 | window.setTimeout(function() { 168 | 169 | if (target == '_blank') 170 | window.open(href); 171 | else 172 | window.location.href = href; 173 | 174 | }, config.delay + 10); 175 | 176 | }); 177 | 178 | } 179 | 180 | // Event: Touch stuff. 181 | $this.on('touchstart', function(event) { 182 | 183 | $this.touchPosX = event.originalEvent.touches[0].pageX; 184 | $this.touchPosY = event.originalEvent.touches[0].pageY; 185 | 186 | }) 187 | 188 | $this.on('touchmove', function(event) { 189 | 190 | if ($this.touchPosX === null 191 | || $this.touchPosY === null) 192 | return; 193 | 194 | var diffX = $this.touchPosX - event.originalEvent.touches[0].pageX, 195 | diffY = $this.touchPosY - event.originalEvent.touches[0].pageY, 196 | th = $this.outerHeight(), 197 | ts = ($this.get(0).scrollHeight - $this.scrollTop()); 198 | 199 | // Hide on swipe? 200 | if (config.hideOnSwipe) { 201 | 202 | var result = false, 203 | boundary = 20, 204 | delta = 50; 205 | 206 | switch (config.side) { 207 | 208 | case 'left': 209 | result = (diffY < boundary && diffY > (-1 * boundary)) && (diffX > delta); 210 | break; 211 | 212 | case 'right': 213 | result = (diffY < boundary && diffY > (-1 * boundary)) && (diffX < (-1 * delta)); 214 | break; 215 | 216 | case 'top': 217 | result = (diffX < boundary && diffX > (-1 * boundary)) && (diffY > delta); 218 | break; 219 | 220 | case 'bottom': 221 | result = (diffX < boundary && diffX > (-1 * boundary)) && (diffY < (-1 * delta)); 222 | break; 223 | 224 | default: 225 | break; 226 | 227 | } 228 | 229 | if (result) { 230 | 231 | $this.touchPosX = null; 232 | $this.touchPosY = null; 233 | $this._hide(); 234 | 235 | return false; 236 | 237 | } 238 | 239 | } 240 | 241 | // Prevent vertical scrolling past the top or bottom. 242 | if (($this.scrollTop() < 0 && diffY < 0) 243 | || (ts > (th - 2) && ts < (th + 2) && diffY > 0)) { 244 | 245 | event.preventDefault(); 246 | event.stopPropagation(); 247 | 248 | } 249 | 250 | }); 251 | 252 | // Event: Prevent certain events inside the panel from bubbling. 253 | $this.on('click touchend touchstart touchmove', function(event) { 254 | event.stopPropagation(); 255 | }); 256 | 257 | // Event: Hide panel if a child anchor tag pointing to its ID is clicked. 258 | $this.on('click', 'a[href="#' + id + '"]', function(event) { 259 | 260 | event.preventDefault(); 261 | event.stopPropagation(); 262 | 263 | config.target.removeClass(config.visibleClass); 264 | 265 | }); 266 | 267 | // Body. 268 | 269 | // Event: Hide panel on body click/tap. 270 | $body.on('click touchend', function(event) { 271 | $this._hide(event); 272 | }); 273 | 274 | // Event: Toggle. 275 | $body.on('click', 'a[href="#' + id + '"]', function(event) { 276 | 277 | event.preventDefault(); 278 | event.stopPropagation(); 279 | 280 | config.target.toggleClass(config.visibleClass); 281 | 282 | }); 283 | 284 | // Window. 285 | 286 | // Event: Hide on ESC. 287 | if (config.hideOnEscape) 288 | $window.on('keydown', function(event) { 289 | 290 | if (event.keyCode == 27) 291 | $this._hide(event); 292 | 293 | }); 294 | 295 | return $this; 296 | 297 | }; 298 | 299 | /** 300 | * Apply "placeholder" attribute polyfill to one or more forms. 301 | * @return {jQuery} jQuery object. 302 | */ 303 | $.fn.placeholder = function() { 304 | 305 | // Browser natively supports placeholders? Bail. 306 | if (typeof (document.createElement('input')).placeholder != 'undefined') 307 | return $(this); 308 | 309 | // No elements? 310 | if (this.length == 0) 311 | return $this; 312 | 313 | // Multiple elements? 314 | if (this.length > 1) { 315 | 316 | for (var i=0; i < this.length; i++) 317 | $(this[i]).placeholder(); 318 | 319 | return $this; 320 | 321 | } 322 | 323 | // Vars. 324 | var $this = $(this); 325 | 326 | // Text, TextArea. 327 | $this.find('input[type=text],textarea') 328 | .each(function() { 329 | 330 | var i = $(this); 331 | 332 | if (i.val() == '' 333 | || i.val() == i.attr('placeholder')) 334 | i 335 | .addClass('polyfill-placeholder') 336 | .val(i.attr('placeholder')); 337 | 338 | }) 339 | .on('blur', function() { 340 | 341 | var i = $(this); 342 | 343 | if (i.attr('name').match(/-polyfill-field$/)) 344 | return; 345 | 346 | if (i.val() == '') 347 | i 348 | .addClass('polyfill-placeholder') 349 | .val(i.attr('placeholder')); 350 | 351 | }) 352 | .on('focus', function() { 353 | 354 | var i = $(this); 355 | 356 | if (i.attr('name').match(/-polyfill-field$/)) 357 | return; 358 | 359 | if (i.val() == i.attr('placeholder')) 360 | i 361 | .removeClass('polyfill-placeholder') 362 | .val(''); 363 | 364 | }); 365 | 366 | // Password. 367 | $this.find('input[type=password]') 368 | .each(function() { 369 | 370 | var i = $(this); 371 | var x = $( 372 | $('
') 373 | .append(i.clone()) 374 | .remove() 375 | .html() 376 | .replace(/type="password"/i, 'type="text"') 377 | .replace(/type=password/i, 'type=text') 378 | ); 379 | 380 | if (i.attr('id') != '') 381 | x.attr('id', i.attr('id') + '-polyfill-field'); 382 | 383 | if (i.attr('name') != '') 384 | x.attr('name', i.attr('name') + '-polyfill-field'); 385 | 386 | x.addClass('polyfill-placeholder') 387 | .val(x.attr('placeholder')).insertAfter(i); 388 | 389 | if (i.val() == '') 390 | i.hide(); 391 | else 392 | x.hide(); 393 | 394 | i 395 | .on('blur', function(event) { 396 | 397 | event.preventDefault(); 398 | 399 | var x = i.parent().find('input[name=' + i.attr('name') + '-polyfill-field]'); 400 | 401 | if (i.val() == '') { 402 | 403 | i.hide(); 404 | x.show(); 405 | 406 | } 407 | 408 | }); 409 | 410 | x 411 | .on('focus', function(event) { 412 | 413 | event.preventDefault(); 414 | 415 | var i = x.parent().find('input[name=' + x.attr('name').replace('-polyfill-field', '') + ']'); 416 | 417 | x.hide(); 418 | 419 | i 420 | .show() 421 | .focus(); 422 | 423 | }) 424 | .on('keypress', function(event) { 425 | 426 | event.preventDefault(); 427 | x.val(''); 428 | 429 | }); 430 | 431 | }); 432 | 433 | // Events. 434 | $this 435 | .on('submit', function() { 436 | 437 | $this.find('input[type=text],input[type=password],textarea') 438 | .each(function(event) { 439 | 440 | var i = $(this); 441 | 442 | if (i.attr('name').match(/-polyfill-field$/)) 443 | i.attr('name', ''); 444 | 445 | if (i.val() == i.attr('placeholder')) { 446 | 447 | i.removeClass('polyfill-placeholder'); 448 | i.val(''); 449 | 450 | } 451 | 452 | }); 453 | 454 | }) 455 | .on('reset', function(event) { 456 | 457 | event.preventDefault(); 458 | 459 | $this.find('select') 460 | .val($('option:first').val()); 461 | 462 | $this.find('input,textarea') 463 | .each(function() { 464 | 465 | var i = $(this), 466 | x; 467 | 468 | i.removeClass('polyfill-placeholder'); 469 | 470 | switch (this.type) { 471 | 472 | case 'submit': 473 | case 'reset': 474 | break; 475 | 476 | case 'password': 477 | i.val(i.attr('defaultValue')); 478 | 479 | x = i.parent().find('input[name=' + i.attr('name') + '-polyfill-field]'); 480 | 481 | if (i.val() == '') { 482 | i.hide(); 483 | x.show(); 484 | } 485 | else { 486 | i.show(); 487 | x.hide(); 488 | } 489 | 490 | break; 491 | 492 | case 'checkbox': 493 | case 'radio': 494 | i.attr('checked', i.attr('defaultValue')); 495 | break; 496 | 497 | case 'text': 498 | case 'textarea': 499 | i.val(i.attr('defaultValue')); 500 | 501 | if (i.val() == '') { 502 | i.addClass('polyfill-placeholder'); 503 | i.val(i.attr('placeholder')); 504 | } 505 | 506 | break; 507 | 508 | default: 509 | i.val(i.attr('defaultValue')); 510 | break; 511 | 512 | } 513 | }); 514 | 515 | }); 516 | 517 | return $this; 518 | 519 | }; 520 | 521 | /** 522 | * Moves elements to/from the first positions of their respective parents. 523 | * @param {jQuery} $elements Elements (or selector) to move. 524 | * @param {bool} condition If true, moves elements to the top. Otherwise, moves elements back to their original locations. 525 | */ 526 | $.prioritize = function($elements, condition) { 527 | 528 | var key = '__prioritize'; 529 | 530 | // Expand $elements if it's not already a jQuery object. 531 | if (typeof $elements != 'jQuery') 532 | $elements = $($elements); 533 | 534 | // Step through elements. 535 | $elements.each(function() { 536 | 537 | var $e = $(this), $p, 538 | $parent = $e.parent(); 539 | 540 | // No parent? Bail. 541 | if ($parent.length == 0) 542 | return; 543 | 544 | // Not moved? Move it. 545 | if (!$e.data(key)) { 546 | 547 | // Condition is false? Bail. 548 | if (!condition) 549 | return; 550 | 551 | // Get placeholder (which will serve as our point of reference for when this element needs to move back). 552 | $p = $e.prev(); 553 | 554 | // Couldn't find anything? Means this element's already at the top, so bail. 555 | if ($p.length == 0) 556 | return; 557 | 558 | // Move element to top of parent. 559 | $e.prependTo($parent); 560 | 561 | // Mark element as moved. 562 | $e.data(key, $p); 563 | 564 | } 565 | 566 | // Moved already? 567 | else { 568 | 569 | // Condition is true? Bail. 570 | if (condition) 571 | return; 572 | 573 | $p = $e.data(key); 574 | 575 | // Move element back to its original location (using our placeholder). 576 | $e.insertAfter($p); 577 | 578 | // Unmark element as moved. 579 | $e.removeData(key); 580 | 581 | } 582 | 583 | }); 584 | 585 | }; 586 | 587 | })(jQuery); -------------------------------------------------------------------------------- /app/static/assets/sass/base/_page.scss: -------------------------------------------------------------------------------- 1 | /// 2 | /// Phantom by HTML5 UP 3 | /// html5up.net | @ajlkn 4 | /// Free for personal and commercial use under the CCA 3.0 license (html5up.net/license) 5 | /// 6 | 7 | /* Basic */ 8 | 9 | // MSIE: Required for IEMobile. 10 | @-ms-viewport { 11 | width: device-width; 12 | } 13 | 14 | // MSIE: Prevents scrollbar from overlapping content. 15 | body { 16 | -ms-overflow-style: scrollbar; 17 | } 18 | 19 | // Ensures page width is always >=320px. 20 | @include breakpoint('<=xsmall') { 21 | html, body { 22 | min-width: 320px; 23 | } 24 | } 25 | 26 | // Set box model to border-box. 27 | // Based on css-tricks.com/inheriting-box-sizing-probably-slightly-better-best-practice 28 | html { 29 | box-sizing: border-box; 30 | } 31 | 32 | *, *:before, *:after { 33 | box-sizing: inherit; 34 | } 35 | 36 | body { 37 | background: _palette(bg); 38 | 39 | // Stops initial animations until page loads. 40 | &.is-preload { 41 | *, *:before, *:after { 42 | @include vendor('animation', 'none !important'); 43 | @include vendor('transition', 'none !important'); 44 | } 45 | } 46 | 47 | } -------------------------------------------------------------------------------- /app/static/assets/sass/base/_reset.scss: -------------------------------------------------------------------------------- 1 | /// 2 | /// Phantom by HTML5 UP 3 | /// html5up.net | @ajlkn 4 | /// Free for personal and commercial use under the CCA 3.0 license (html5up.net/license) 5 | /// 6 | 7 | // Reset. 8 | // Based on meyerweb.com/eric/tools/css/reset (v2.0 | 20110126 | License: public domain) 9 | 10 | html, body, div, span, applet, object, 11 | iframe, h1, h2, h3, h4, h5, h6, p, blockquote, 12 | pre, a, abbr, acronym, address, big, cite, 13 | code, del, dfn, em, img, ins, kbd, q, s, samp, 14 | small, strike, strong, sub, sup, tt, var, b, 15 | u, i, center, dl, dt, dd, ol, ul, li, fieldset, 16 | form, label, legend, table, caption, tbody, 17 | tfoot, thead, tr, th, td, article, aside, 18 | canvas, details, embed, figure, figcaption, 19 | footer, header, hgroup, menu, nav, output, ruby, 20 | section, summary, time, mark, audio, video { 21 | margin: 0; 22 | padding: 0; 23 | border: 0; 24 | font-size: 100%; 25 | font: inherit; 26 | vertical-align: baseline; 27 | } 28 | 29 | article, aside, details, figcaption, figure, 30 | footer, header, hgroup, menu, nav, section { 31 | display: block; 32 | } 33 | 34 | body { 35 | line-height: 1; 36 | } 37 | 38 | ol, ul { 39 | list-style:none; 40 | } 41 | 42 | blockquote, q { 43 | quotes: none; 44 | 45 | &:before, 46 | &:after { 47 | content: ''; 48 | content: none; 49 | } 50 | } 51 | 52 | table { 53 | border-collapse: collapse; 54 | border-spacing: 0; 55 | } 56 | 57 | body { 58 | -webkit-text-size-adjust: none; 59 | } 60 | 61 | mark { 62 | background-color: transparent; 63 | color: inherit; 64 | } 65 | 66 | input::-moz-focus-inner { 67 | border: 0; 68 | padding: 0; 69 | } 70 | 71 | input, select, textarea { 72 | -moz-appearance: none; 73 | -webkit-appearance: none; 74 | -ms-appearance: none; 75 | appearance: none; 76 | } -------------------------------------------------------------------------------- /app/static/assets/sass/base/_typography.scss: -------------------------------------------------------------------------------- 1 | /// 2 | /// Phantom by HTML5 UP 3 | /// html5up.net | @ajlkn 4 | /// Free for personal and commercial use under the CCA 3.0 license (html5up.net/license) 5 | /// 6 | 7 | /* Type */ 8 | 9 | body, input, select, textarea { 10 | color: _palette(fg); 11 | font-family: _font(family); 12 | font-size: 16pt; 13 | font-weight: _font(weight); 14 | line-height: 1.75; 15 | 16 | @include breakpoint('<=xlarge') { 17 | font-size: 14pt; 18 | } 19 | 20 | @include breakpoint('<=large') { 21 | font-size: 12pt; 22 | } 23 | } 24 | 25 | a { 26 | @include vendor('transition', ( 27 | 'border-bottom-color #{_duration(transition)} ease', 28 | 'color #{_duration(transition)} ease' 29 | )); 30 | text-decoration: none; 31 | color: _palette(fg); 32 | border-bottom: dotted 1px transparentize(_palette(fg), 0.5); 33 | 34 | &:hover { 35 | border-bottom-color: transparent; 36 | color: _palette(accent1) !important; 37 | } 38 | } 39 | 40 | strong, b { 41 | color: _palette(fg-bold); 42 | font-weight: _font(weight-bold); 43 | } 44 | 45 | em, i { 46 | font-style: italic; 47 | } 48 | 49 | p { 50 | margin: 0 0 _size(element-margin) 0; 51 | } 52 | 53 | h1 { 54 | font-size: 2.75em; 55 | color: _palette(fg-bold); 56 | font-weight: _font(weight-bold-alt); 57 | line-height: 1.3; 58 | margin: 0 0 (_size(element-margin) * 0.5) 0; 59 | letter-spacing: _font(letter-spacing-alt); 60 | 61 | a { 62 | color: inherit; 63 | } 64 | 65 | @include breakpoint('<=small') { 66 | font-size: 2em; 67 | margin: 0 0 (_size(element-margin) * 0.5) 0; 68 | } 69 | 70 | @include breakpoint('<=xxsmall') { 71 | font-size: 1.75em; 72 | } 73 | } 74 | 75 | h2, h3, h4, h5, h6 { 76 | color: _palette(fg-bold); 77 | font-weight: _font(weight-bold); 78 | line-height: 1.5; 79 | margin: 0 0 (_size(element-margin) * 1) 0; 80 | text-transform: uppercase; 81 | letter-spacing: _font(letter-spacing); 82 | 83 | a { 84 | color: inherit; 85 | } 86 | } 87 | 88 | h2 { 89 | font-size: 1.1em; 90 | } 91 | 92 | h3 { 93 | font-size: 1em; 94 | } 95 | 96 | h4 { 97 | font-size: 0.8em; 98 | } 99 | 100 | h5 { 101 | font-size: 0.8em; 102 | } 103 | 104 | h6 { 105 | font-size: 0.8em; 106 | } 107 | 108 | @include breakpoint('<=medium') { 109 | h1, h2, h3, h4, h5, h6 { 110 | br { 111 | display: none; 112 | } 113 | } 114 | } 115 | 116 | @include breakpoint('<=small') { 117 | h2 { 118 | font-size: 1em; 119 | } 120 | 121 | h3 { 122 | font-size: 0.8em; 123 | } 124 | } 125 | 126 | sub { 127 | font-size: 0.8em; 128 | position: relative; 129 | top: 0.5em; 130 | } 131 | 132 | sup { 133 | font-size: 0.8em; 134 | position: relative; 135 | top: -0.5em; 136 | } 137 | 138 | blockquote { 139 | border-left: solid (_size(border-width) * 4) _palette(border); 140 | font-style: italic; 141 | margin: 0 0 _size(element-margin) 0; 142 | padding: (_size(element-margin) / 4) 0 (_size(element-margin) / 4) _size(element-margin); 143 | } 144 | 145 | code { 146 | background: _palette(border-bg); 147 | border-radius: _size(border-radius); 148 | border: solid _size(border-width) _palette(border); 149 | font-family: _font(family-fixed); 150 | font-size: 0.9em; 151 | margin: 0 0.25em; 152 | padding: 0.25em 0.65em; 153 | } 154 | 155 | pre { 156 | -webkit-overflow-scrolling: touch; 157 | font-family: _font(family-fixed); 158 | font-size: 0.9em; 159 | margin: 0 0 _size(element-margin) 0; 160 | 161 | code { 162 | display: block; 163 | line-height: 1.75; 164 | padding: 1em 1.5em; 165 | overflow-x: auto; 166 | } 167 | } 168 | 169 | hr { 170 | border: 0; 171 | border-bottom: solid _size(border-width) _palette(border); 172 | margin: _size(element-margin) 0; 173 | 174 | &.major { 175 | margin: (_size(element-margin) * 1.5) 0; 176 | } 177 | } 178 | 179 | .align-left { 180 | text-align: left; 181 | } 182 | 183 | .align-center { 184 | text-align: center; 185 | } 186 | 187 | .align-right { 188 | text-align: right; 189 | } -------------------------------------------------------------------------------- /app/static/assets/sass/components/_actions.scss: -------------------------------------------------------------------------------- 1 | /// 2 | /// Phantom by HTML5 UP 3 | /// html5up.net | @ajlkn 4 | /// Free for personal and commercial use under the CCA 3.0 license (html5up.net/license) 5 | /// 6 | 7 | /* Actions */ 8 | 9 | ul.actions { 10 | @include vendor('display', 'flex'); 11 | cursor: default; 12 | list-style: none; 13 | margin-left: (_size(element-margin) * -0.5); 14 | padding-left: 0; 15 | 16 | li { 17 | padding: 0 0 0 (_size(element-margin) * 0.5); 18 | vertical-align: middle; 19 | } 20 | 21 | &.special { 22 | @include vendor('justify-content', 'center'); 23 | width: 100%; 24 | margin-left: 0; 25 | 26 | li { 27 | &:first-child { 28 | padding-left: 0; 29 | } 30 | } 31 | } 32 | 33 | &.stacked { 34 | @include vendor('flex-direction', 'column'); 35 | margin-left: 0; 36 | 37 | li { 38 | padding: (_size(element-margin) * 0.65) 0 0 0; 39 | 40 | &:first-child { 41 | padding-top: 0; 42 | } 43 | } 44 | } 45 | 46 | &.fit { 47 | width: calc(100% + #{_size(element-margin) * 0.5}); 48 | 49 | li { 50 | @include vendor('flex-grow', '1'); 51 | @include vendor('flex-shrink', '1'); 52 | width: 100%; 53 | 54 | > * { 55 | width: 100%; 56 | } 57 | } 58 | 59 | &.stacked { 60 | width: 100%; 61 | } 62 | } 63 | 64 | @include breakpoint('<=xsmall') { 65 | &:not(.fixed) { 66 | @include vendor('flex-direction', 'column'); 67 | margin-left: 0; 68 | width: 100% !important; 69 | 70 | li { 71 | @include vendor('flex-grow', '1'); 72 | @include vendor('flex-shrink', '1'); 73 | padding: (_size(element-margin) * 0.5) 0 0 0; 74 | text-align: center; 75 | width: 100%; 76 | 77 | > * { 78 | width: 100%; 79 | } 80 | 81 | &:first-child { 82 | padding-top: 0; 83 | } 84 | 85 | input[type="submit"], 86 | input[type="reset"], 87 | input[type="button"], 88 | button, 89 | .button { 90 | width: 100%; 91 | 92 | &.icon { 93 | &:before { 94 | margin-left: -0.5rem; 95 | } 96 | } 97 | } 98 | } 99 | } 100 | } 101 | } -------------------------------------------------------------------------------- /app/static/assets/sass/components/_box.scss: -------------------------------------------------------------------------------- 1 | /// 2 | /// Phantom by HTML5 UP 3 | /// html5up.net | @ajlkn 4 | /// Free for personal and commercial use under the CCA 3.0 license (html5up.net/license) 5 | /// 6 | 7 | /* Box */ 8 | 9 | .box { 10 | border-radius: _size(border-radius); 11 | border: solid _size(border-width) _palette(border); 12 | margin-bottom: _size(element-margin); 13 | padding: 1.5em; 14 | 15 | > :last-child, 16 | > :last-child > :last-child, 17 | > :last-child > :last-child > :last-child { 18 | margin-bottom: 0; 19 | } 20 | 21 | &.alt { 22 | border: 0; 23 | border-radius: 0; 24 | padding: 0; 25 | } 26 | } -------------------------------------------------------------------------------- /app/static/assets/sass/components/_button.scss: -------------------------------------------------------------------------------- 1 | /// 2 | /// Phantom by HTML5 UP 3 | /// html5up.net | @ajlkn 4 | /// Free for personal and commercial use under the CCA 3.0 license (html5up.net/license) 5 | /// 6 | 7 | /* Button */ 8 | 9 | input[type="submit"], 10 | input[type="reset"], 11 | input[type="button"], 12 | button, 13 | .button { 14 | @include vendor('appearance', 'none'); 15 | @include vendor('transition', ( 16 | 'background-color #{_duration(transition)} ease-in-out', 17 | 'color #{_duration(transition)} ease-in-out', 18 | 'box-shadow #{_duration(transition)} ease-in-out' 19 | )); 20 | background-color: transparent; 21 | border-radius: _size(border-radius); 22 | border: 0; 23 | box-shadow: inset 0 0 0 (_size(border-width) * 2) _palette(fg); 24 | color: _palette(fg) !important; 25 | cursor: pointer; 26 | display: inline-block; 27 | font-size: 0.8em; 28 | font-weight: _font(weight-bold); 29 | height: 3.5em; 30 | letter-spacing: _font(letter-spacing); 31 | line-height: 3.45em; 32 | overflow: hidden; 33 | padding: 0 1.25em 0 #{1.25em + _font(letter-spacing)}; 34 | text-align: center; 35 | text-decoration: none; 36 | text-overflow: ellipsis; 37 | text-transform: uppercase; 38 | white-space: nowrap; 39 | 40 | &.icon { 41 | &:before { 42 | margin-right: 0.5em; 43 | } 44 | } 45 | 46 | &.fit { 47 | width: 100%; 48 | } 49 | 50 | &:hover { 51 | color: _palette(accent1) !important; 52 | box-shadow: inset 0 0 0 (_size(border-width) * 2) _palette(accent1); 53 | } 54 | 55 | &:active { 56 | background-color: transparentize(_palette(accent1), 0.9); 57 | } 58 | 59 | &.small { 60 | font-size: 0.6em; 61 | } 62 | 63 | &.large { 64 | font-size: 1em; 65 | } 66 | 67 | &.primary { 68 | box-shadow: none; 69 | background-color: _palette(fg); 70 | color: _palette(bg) !important; 71 | 72 | &:hover { 73 | background-color: _palette(accent1); 74 | } 75 | 76 | &:active { 77 | background-color: darken(_palette(accent1), 8); 78 | } 79 | } 80 | 81 | &.disabled, 82 | &:disabled { 83 | @include vendor('pointer-events', 'none'); 84 | opacity: 0.25; 85 | } 86 | } -------------------------------------------------------------------------------- /app/static/assets/sass/components/_form.scss: -------------------------------------------------------------------------------- 1 | /// 2 | /// Phantom by HTML5 UP 3 | /// html5up.net | @ajlkn 4 | /// Free for personal and commercial use under the CCA 3.0 license (html5up.net/license) 5 | /// 6 | 7 | /* Form */ 8 | 9 | form { 10 | margin: 0 0 _size(element-margin) 0; 11 | overflow-x: hidden; 12 | 13 | > :last-child { 14 | margin-bottom: 0; 15 | } 16 | 17 | > .fields { 18 | $gutter: (_size(element-margin) * 0.75); 19 | 20 | @include vendor('display', 'flex'); 21 | @include vendor('flex-wrap', 'wrap'); 22 | width: calc(100% + #{$gutter * 2}); 23 | margin: ($gutter * -1) 0 _size(element-margin) ($gutter * -1); 24 | 25 | > .field { 26 | @include vendor('flex-grow', '0'); 27 | @include vendor('flex-shrink', '0'); 28 | padding: $gutter 0 0 $gutter; 29 | width: calc(100% - #{$gutter * 1}); 30 | 31 | &.half { 32 | width: calc(50% - #{$gutter * 0.5}); 33 | } 34 | 35 | &.third { 36 | width: calc(#{100% / 3} - #{$gutter * (1 / 3)}); 37 | } 38 | 39 | &.quarter { 40 | width: calc(25% - #{$gutter * 0.25}); 41 | } 42 | } 43 | } 44 | 45 | @include breakpoint('<=xsmall') { 46 | > .fields { 47 | $gutter: (_size(element-margin) * 0.75); 48 | 49 | width: calc(100% + #{$gutter * 2}); 50 | margin: ($gutter * -1) 0 _size(element-margin) ($gutter * -1); 51 | 52 | > .field { 53 | padding: $gutter 0 0 $gutter; 54 | width: calc(100% - #{$gutter * 1}); 55 | 56 | &.half { 57 | width: calc(100% - #{$gutter * 1}); 58 | } 59 | 60 | &.third { 61 | width: calc(100% - #{$gutter * 1}); 62 | } 63 | 64 | &.quarter { 65 | width: calc(100% - #{$gutter * 1}); 66 | } 67 | } 68 | } 69 | } 70 | } 71 | 72 | label { 73 | display: block; 74 | font-size: 0.9em; 75 | font-weight: _font(weight-bold); 76 | margin: 0 0 (_size(element-margin) * 0.5) 0; 77 | } 78 | 79 | input[type="text"], 80 | input[type="password"], 81 | input[type="email"], 82 | input[type="tel"], 83 | select, 84 | textarea { 85 | @include vendor('appearance', 'none'); 86 | background-color: transparent; 87 | border: none; 88 | border-radius: 0; 89 | border-bottom: solid _size(border-width) _palette(border); 90 | color: inherit; 91 | display: block; 92 | outline: 0; 93 | padding: 0; 94 | text-decoration: none; 95 | width: 100%; 96 | 97 | &:invalid { 98 | box-shadow: none; 99 | } 100 | 101 | &:focus { 102 | border-bottom-color: _palette(accent1); 103 | box-shadow: inset 0 -1px 0 0 _palette(accent1); 104 | } 105 | } 106 | 107 | select { 108 | background-image: svg-url(""); 109 | background-size: 1.25rem; 110 | background-repeat: no-repeat; 111 | background-position: calc(100% - 1rem) center; 112 | height: _size(element-height); 113 | padding-right: _size(element-height); 114 | text-overflow: ellipsis; 115 | 116 | option { 117 | color: _palette(fg-bold); 118 | background: _palette(bg); 119 | } 120 | 121 | &:focus { 122 | &::-ms-value { 123 | background-color: transparent; 124 | } 125 | } 126 | 127 | &::-ms-expand { 128 | display: none; 129 | } 130 | } 131 | 132 | input[type="text"], 133 | input[type="password"], 134 | input[type="email"], 135 | select { 136 | height: _size(element-height); 137 | } 138 | 139 | textarea { 140 | padding: 0; 141 | min-height: (_size(element-height) * 1.25); 142 | } 143 | 144 | input[type="checkbox"], 145 | input[type="radio"], { 146 | @include vendor('appearance', 'none'); 147 | display: block; 148 | float: left; 149 | margin-right: -2em; 150 | opacity: 0; 151 | width: 1em; 152 | z-index: -1; 153 | 154 | & + label { 155 | @include icon; 156 | color: _palette(fg); 157 | cursor: pointer; 158 | display: inline-block; 159 | font-size: 1em; 160 | font-weight: _font(weight); 161 | padding-left: (_size(element-height) * 0.6) + 0.75em; 162 | padding-right: 0.75em; 163 | position: relative; 164 | 165 | &:before { 166 | border-radius: _size(border-radius); 167 | border: solid _size(border-width) _palette(border); 168 | content: ''; 169 | display: inline-block; 170 | height: (_size(element-height) * 0.6); 171 | left: 0; 172 | line-height: (_size(element-height) * 0.575); 173 | position: absolute; 174 | text-align: center; 175 | top: 0; 176 | width: (_size(element-height) * 0.6); 177 | } 178 | } 179 | 180 | &:checked + label { 181 | &:before { 182 | background: _palette(fg); 183 | border-color: _palette(fg); 184 | color: _palette(bg); 185 | content: '\f00c'; 186 | } 187 | } 188 | 189 | &:focus + label { 190 | &:before { 191 | border-color: _palette(accent1); 192 | box-shadow: 0 0 0 _size(border-width) _palette(accent1); 193 | } 194 | } 195 | } 196 | 197 | input[type="checkbox"] { 198 | & + label { 199 | &:before { 200 | border-radius: _size(border-radius); 201 | } 202 | } 203 | } 204 | 205 | input[type="radio"] { 206 | & + label { 207 | &:before { 208 | border-radius: 100%; 209 | } 210 | } 211 | } -------------------------------------------------------------------------------- /app/static/assets/sass/components/_icon.scss: -------------------------------------------------------------------------------- 1 | /// 2 | /// Phantom by HTML5 UP 3 | /// html5up.net | @ajlkn 4 | /// Free for personal and commercial use under the CCA 3.0 license (html5up.net/license) 5 | /// 6 | 7 | /* Icon */ 8 | 9 | .icon { 10 | @include icon; 11 | border-bottom: none; 12 | position: relative; 13 | 14 | > .label { 15 | display: none; 16 | } 17 | 18 | &.style1 { 19 | } 20 | 21 | &.style2 { 22 | @include vendor('transition', ( 23 | 'background-color #{_duration(transition)} ease-in-out', 24 | 'color #{_duration(transition)} ease-in-out', 25 | 'border-color #{_duration(transition)} ease-in-out' 26 | )); 27 | background-color: transparent; 28 | border: solid 1px _palette(border); 29 | border-radius: _size(border-radius); 30 | width: 2.65em; 31 | height: 2.65em; 32 | display: inline-block; 33 | text-align: center; 34 | line-height: 2.65em; 35 | color: inherit; 36 | 37 | &:before { 38 | font-size: 1.1em; 39 | } 40 | 41 | &:hover { 42 | color: _palette(accent1); 43 | border-color: _palette(accent1); 44 | } 45 | 46 | &:active { 47 | background-color: transparentize(_palette(accent1), 0.9); 48 | } 49 | } 50 | } -------------------------------------------------------------------------------- /app/static/assets/sass/components/_icons.scss: -------------------------------------------------------------------------------- 1 | /// 2 | /// Phantom by HTML5 UP 3 | /// html5up.net | @ajlkn 4 | /// Free for personal and commercial use under the CCA 3.0 license (html5up.net/license) 5 | /// 6 | 7 | /* Icons */ 8 | 9 | ul.icons { 10 | cursor: default; 11 | list-style: none; 12 | padding-left: 0; 13 | margin: -1em 0 _size(element-margin) -1em; 14 | 15 | li { 16 | display: inline-block; 17 | padding: 1em 0 0 1em; 18 | } 19 | } -------------------------------------------------------------------------------- /app/static/assets/sass/components/_image.scss: -------------------------------------------------------------------------------- 1 | /// 2 | /// Phantom by HTML5 UP 3 | /// html5up.net | @ajlkn 4 | /// Free for personal and commercial use under the CCA 3.0 license (html5up.net/license) 5 | /// 6 | 7 | /* Image */ 8 | 9 | .image { 10 | border-radius: _size(border-radius); 11 | border: 0; 12 | display: inline-block; 13 | position: relative; 14 | 15 | img { 16 | border-radius: _size(border-radius); 17 | display: block; 18 | } 19 | 20 | &.left, 21 | &.right { 22 | max-width: 40%; 23 | 24 | img { 25 | width: 100%; 26 | } 27 | } 28 | 29 | &.left { 30 | float: left; 31 | padding: 0 1.5em 1em 0; 32 | top: 0.25em; 33 | } 34 | 35 | &.right { 36 | float: right; 37 | padding: 0 0 1em 1.5em; 38 | top: 0.25em; 39 | } 40 | 41 | &.fit { 42 | display: block; 43 | margin: 0 0 _size(element-margin) 0; 44 | width: 100%; 45 | 46 | img { 47 | width: 100%; 48 | } 49 | } 50 | 51 | &.main { 52 | display: block; 53 | margin: 0 0 (_size(element-margin) * 1.5) 0; 54 | width: 100%; 55 | 56 | img { 57 | width: 100%; 58 | } 59 | 60 | @include breakpoint('<=small') { 61 | margin: 0 0 _size(element-margin) 0; 62 | } 63 | } 64 | } -------------------------------------------------------------------------------- /app/static/assets/sass/components/_list.scss: -------------------------------------------------------------------------------- 1 | /// 2 | /// Phantom by HTML5 UP 3 | /// html5up.net | @ajlkn 4 | /// Free for personal and commercial use under the CCA 3.0 license (html5up.net/license) 5 | /// 6 | 7 | /* List */ 8 | 9 | ol { 10 | list-style: decimal; 11 | margin: 0 0 _size(element-margin) 0; 12 | padding-left: 1.25em; 13 | 14 | li { 15 | padding-left: 0.25em; 16 | } 17 | } 18 | 19 | ul { 20 | list-style: disc; 21 | margin: 0 0 _size(element-margin) 0; 22 | padding-left: 1em; 23 | 24 | li { 25 | padding-left: 0.5em; 26 | } 27 | 28 | &.alt { 29 | list-style: none; 30 | padding-left: 0; 31 | 32 | li { 33 | border-top: solid _size(border-width) _palette(border); 34 | padding: 0.5em 0; 35 | 36 | &:first-child { 37 | border-top: 0; 38 | padding-top: 0; 39 | } 40 | } 41 | } 42 | } 43 | 44 | dl { 45 | margin: 0 0 _size(element-margin) 0; 46 | 47 | dt { 48 | display: block; 49 | font-weight: _font(weight-bold); 50 | margin: 0 0 (_size(element-margin) * 0.5) 0; 51 | } 52 | 53 | dd { 54 | margin-left: _size(element-margin); 55 | } 56 | } -------------------------------------------------------------------------------- /app/static/assets/sass/components/_row.scss: -------------------------------------------------------------------------------- 1 | /// 2 | /// Phantom by HTML5 UP 3 | /// html5up.net | @ajlkn 4 | /// Free for personal and commercial use under the CCA 3.0 license (html5up.net/license) 5 | /// 6 | 7 | /* Row */ 8 | 9 | .row { 10 | @include html-grid(2em); 11 | 12 | @include breakpoint('<=xlarge') { 13 | @include html-grid(2em, 'xlarge'); 14 | } 15 | 16 | @include breakpoint('<=large') { 17 | @include html-grid(2em, 'large'); 18 | } 19 | 20 | @include breakpoint('<=medium') { 21 | @include html-grid(1.5em, 'medium'); 22 | } 23 | 24 | @include breakpoint('<=small') { 25 | @include html-grid(1em, 'small'); 26 | } 27 | 28 | @include breakpoint('<=xsmall') { 29 | @include html-grid(1em, 'xsmall'); 30 | } 31 | } -------------------------------------------------------------------------------- /app/static/assets/sass/components/_section.scss: -------------------------------------------------------------------------------- 1 | /// 2 | /// Phantom by HTML5 UP 3 | /// html5up.net | @ajlkn 4 | /// Free for personal and commercial use under the CCA 3.0 license (html5up.net/license) 5 | /// 6 | 7 | /* Section/Article */ 8 | 9 | section, article { 10 | &.special { 11 | text-align: center; 12 | } 13 | } 14 | 15 | header { 16 | p { 17 | margin-top: _size(element-margin) * -0.5; 18 | } 19 | 20 | @include breakpoint('<=small') { 21 | p { 22 | margin-top: 0; 23 | } 24 | } 25 | } -------------------------------------------------------------------------------- /app/static/assets/sass/components/_table.scss: -------------------------------------------------------------------------------- 1 | /// 2 | /// Phantom by HTML5 UP 3 | /// html5up.net | @ajlkn 4 | /// Free for personal and commercial use under the CCA 3.0 license (html5up.net/license) 5 | /// 6 | 7 | /* Table */ 8 | 9 | .table-wrapper { 10 | -webkit-overflow-scrolling: touch; 11 | overflow-x: auto; 12 | } 13 | 14 | table { 15 | margin: 0 0 _size(element-margin) 0; 16 | width: 100%; 17 | 18 | tbody { 19 | tr { 20 | border: solid _size(border-width) _palette(border); 21 | border-left: 0; 22 | border-right: 0; 23 | 24 | &:nth-child(2n + 1) { 25 | background-color: _palette(border-bg); 26 | } 27 | } 28 | } 29 | 30 | td { 31 | padding: 0.75em 0.75em; 32 | } 33 | 34 | th { 35 | color: _palette(fg-bold); 36 | font-size: 0.9em; 37 | font-weight: _font(weight-bold); 38 | padding: 0 0.75em 0.75em 0.75em; 39 | text-align: left; 40 | } 41 | 42 | thead { 43 | border-bottom: solid (_size(border-width) * 2) _palette(border); 44 | } 45 | 46 | tfoot { 47 | border-top: solid (_size(border-width) * 2) _palette(border); 48 | } 49 | 50 | &.alt { 51 | border-collapse: separate; 52 | 53 | tbody { 54 | tr { 55 | td { 56 | border: solid _size(border-width) _palette(border); 57 | border-left-width: 0; 58 | border-top-width: 0; 59 | 60 | &:first-child { 61 | border-left-width: _size(border-width); 62 | } 63 | } 64 | 65 | &:first-child { 66 | td { 67 | border-top-width: _size(border-width); 68 | } 69 | } 70 | } 71 | } 72 | 73 | thead { 74 | border-bottom: 0; 75 | } 76 | 77 | tfoot { 78 | border-top: 0; 79 | } 80 | } 81 | } -------------------------------------------------------------------------------- /app/static/assets/sass/components/_tiles.scss: -------------------------------------------------------------------------------- 1 | /// 2 | /// Phantom by HTML5 UP 3 | /// html5up.net | @ajlkn 4 | /// Free for personal and commercial use under the CCA 3.0 license (html5up.net/license) 5 | /// 6 | 7 | /* Tiles */ 8 | 9 | .tiles { 10 | $gutter: _size(gutter); 11 | $duration: 0.5s; 12 | $ease: 'ease'; 13 | 14 | @include vendor('display', 'flex'); 15 | @include vendor('flex-wrap', 'wrap'); 16 | postiion: relative; 17 | margin: ($gutter * -1) 0 0 ($gutter * -1); 18 | 19 | article { 20 | @include vendor('transition', ( 21 | 'transform #{$duration} #{$ease}', 22 | 'opacity #{$duration} #{$ease}' 23 | )); 24 | position: relative; 25 | width: calc(#{(100% / 3)} - #{$gutter * 1}); 26 | margin: $gutter 0 0 $gutter; 27 | 28 | > .image { 29 | @include vendor('transition', 'transform #{$duration} #{$ease}'); 30 | position: relative; 31 | display: block; 32 | width: 100%; 33 | border-radius: _size(border-radius); 34 | overflow: hidden; 35 | 36 | img { 37 | display: block; 38 | width: 100%; 39 | } 40 | 41 | &:before { 42 | @include vendor('pointer-events', 'none'); 43 | @include vendor('transition', ( 44 | 'background-color #{$duration} #{$ease}', 45 | 'opacity #{$duration} #{$ease}' 46 | )); 47 | content: ''; 48 | display: block; 49 | position: absolute; 50 | top: 0; 51 | left: 0; 52 | width: 100%; 53 | height: 100%; 54 | opacity: 1.0; 55 | z-index: 1; 56 | opacity: 0.8; 57 | } 58 | 59 | &:after { 60 | @include vendor('pointer-events', 'none'); 61 | @include vendor('transition', 'opacity #{$duration} #{$ease}'); 62 | content: ''; 63 | display: block; 64 | position: absolute; 65 | top: 0; 66 | left: 0; 67 | width: 100%; 68 | height: 100%; 69 | background-image: svg-url(''); 70 | background-position: center; 71 | background-repeat: no-repeat; 72 | background-size: 100% 100%; 73 | opacity: 0.25; 74 | z-index: 2; 75 | } 76 | } 77 | 78 | > a { 79 | @include vendor('display', 'flex'); 80 | @include vendor('flex-direction', 'column'); 81 | @include vendor('align-items', 'center'); 82 | @include vendor('justify-content', 'center'); 83 | @include vendor('transition', ( 84 | 'background-color #{$duration} #{$ease}', 85 | 'transform #{$duration} #{$ease}' 86 | )); 87 | position: absolute; 88 | top: 0; 89 | left: 0; 90 | width: 100%; 91 | height: 100%; 92 | padding: 1em; 93 | border-radius: _size(border-radius); 94 | border-bottom: 0; 95 | color: _palette(fg-accent); 96 | text-align: center; 97 | text-decoration: none; 98 | z-index: 3; 99 | 100 | > :last-child { 101 | margin: 0; 102 | } 103 | 104 | &:hover { 105 | color: _palette(fg-accent) !important; 106 | } 107 | 108 | h2 { 109 | margin: 0; 110 | } 111 | 112 | .content { 113 | @include vendor('transition', ( 114 | 'max-height #{$duration} #{$ease}', 115 | 'opacity #{$duration} #{$ease}' 116 | )); 117 | width: 100%; 118 | max-height: 0; 119 | line-height: 1.5; 120 | margin-top: 0.35em; 121 | opacity: 0; 122 | 123 | > :last-child { 124 | margin-bottom: 0; 125 | } 126 | } 127 | } 128 | 129 | &.style1 { 130 | > .image:before { 131 | background-color: _palette(accent1); 132 | } 133 | } 134 | 135 | &.style2 { 136 | > .image:before { 137 | background-color: _palette(accent2); 138 | } 139 | } 140 | 141 | &.style3 { 142 | > .image:before { 143 | background-color: _palette(accent3); 144 | } 145 | } 146 | 147 | &.style4 { 148 | > .image:before { 149 | background-color: _palette(accent4); 150 | } 151 | } 152 | 153 | &.style5 { 154 | > .image:before { 155 | background-color: _palette(accent5); 156 | } 157 | } 158 | 159 | &.style6 { 160 | > .image:before { 161 | background-color: _palette(accent6); 162 | } 163 | } 164 | 165 | body:not(.is-touch) & { 166 | &:hover { 167 | > .image { 168 | @include vendor('transform', 'scale(1.1)'); 169 | 170 | &:before { 171 | background-color: _palette(bg-accent); 172 | opacity: 0.35; 173 | } 174 | 175 | &:after { 176 | opacity: 0; 177 | } 178 | } 179 | 180 | .content { 181 | max-height: 15em; 182 | opacity: 1; 183 | } 184 | } 185 | } 186 | } 187 | 188 | * + & { 189 | margin-top: _size(element-margin); 190 | } 191 | 192 | body.is-preload & { 193 | article { 194 | @include vendor('transform', 'scale(0.9)'); 195 | opacity: 0; 196 | } 197 | } 198 | 199 | body.is-touch & { 200 | article { 201 | .content { 202 | max-height: 15em; 203 | opacity: 1; 204 | } 205 | } 206 | } 207 | 208 | @include breakpoint('<=large') { 209 | $gutter: _size(gutter) * 0.5; 210 | 211 | margin: ($gutter * -1) 0 0 ($gutter * -1); 212 | 213 | article { 214 | width: calc(#{(100% / 3)} - #{$gutter * 1}); 215 | margin: $gutter 0 0 $gutter; 216 | } 217 | } 218 | 219 | @include breakpoint('<=medium') { 220 | $gutter: _size(gutter); 221 | 222 | margin: ($gutter * -1) 0 0 ($gutter * -1); 223 | 224 | article { 225 | width: calc(#{(100% / 2)} - #{$gutter * 1}); 226 | margin: $gutter 0 0 $gutter; 227 | } 228 | } 229 | 230 | @include breakpoint('<=small') { 231 | $gutter: _size(gutter) * 0.5; 232 | 233 | margin: ($gutter * -1) 0 0 ($gutter * -1); 234 | 235 | article { 236 | width: calc(#{(100% / 2)} - #{$gutter * 1}); 237 | margin: $gutter 0 0 $gutter; 238 | 239 | &:hover { 240 | > .image { 241 | @include vendor('transform', 'scale(1.0)'); 242 | } 243 | } 244 | } 245 | } 246 | 247 | @include breakpoint('<=xsmall') { 248 | $gutter: _size(gutter) * 0.5; 249 | 250 | margin: 0; 251 | 252 | article { 253 | width: 100%; 254 | margin: $gutter 0 0 0; 255 | } 256 | } 257 | } 258 | 259 | -------------------------------------------------------------------------------- /app/static/assets/sass/layout/_footer.scss: -------------------------------------------------------------------------------- 1 | /// 2 | /// Phantom by HTML5 UP 3 | /// html5up.net | @ajlkn 4 | /// Free for personal and commercial use under the CCA 3.0 license (html5up.net/license) 5 | /// 6 | 7 | /* Footer */ 8 | 9 | #footer { 10 | $gutter: _size(gutter); 11 | 12 | @include padding(5em, 0, (0, 0, 3em, 0)); 13 | background-color: _palette(bg-alt); 14 | 15 | > .inner { 16 | @include vendor('display', 'flex'); 17 | @include vendor('flex-wrap', 'wrap'); 18 | @include vendor('flex-direction', 'row'); 19 | 20 | > * > :last-child { 21 | margin-bottom: 0; 22 | } 23 | 24 | section:nth-child(1) { 25 | width: calc(66% - #{$gutter}); 26 | margin-right: $gutter; 27 | } 28 | 29 | section:nth-child(2) { 30 | width: calc(33% - #{$gutter}); 31 | margin-left: $gutter; 32 | } 33 | 34 | .copyright { 35 | width: 100%; 36 | padding: 0; 37 | margin-top: 5em; 38 | list-style: none; 39 | font-size: 0.8em; 40 | color: transparentize(_palette(fg), 0.5); 41 | 42 | a { 43 | color: inherit; 44 | } 45 | 46 | li { 47 | display: inline-block; 48 | border-left: solid 1px transparentize(_palette(fg), 0.85); 49 | line-height: 1; 50 | padding: 0 0 0 1em; 51 | margin: 0 0 0 1em; 52 | 53 | &:first-child { 54 | border-left: 0; 55 | padding-left: 0; 56 | margin-left: 0; 57 | } 58 | } 59 | } 60 | } 61 | 62 | @include breakpoint('<=large') { 63 | $gutter: _size(gutter) * 0.5; 64 | 65 | @include padding(5em, 0); 66 | 67 | > .inner { 68 | section:nth-child(1) { 69 | width: calc(66% - #{$gutter}); 70 | margin-right: $gutter; 71 | } 72 | 73 | section:nth-child(2) { 74 | width: calc(33% - #{$gutter}); 75 | margin-left: $gutter; 76 | } 77 | } 78 | } 79 | 80 | @include breakpoint('<=medium') { 81 | $gutter: _size(gutter); 82 | 83 | > .inner { 84 | section:nth-child(1) { 85 | width: 66%; 86 | margin-right: 0; 87 | } 88 | 89 | section:nth-child(2) { 90 | width: calc(33% - #{$gutter}); 91 | margin-left: $gutter; 92 | } 93 | } 94 | } 95 | 96 | @include breakpoint('<=small') { 97 | @include padding(3em, 0); 98 | 99 | > .inner { 100 | @include vendor('flex-direction', 'column'); 101 | 102 | section:nth-child(1) { 103 | width: 100%; 104 | margin-right: 0; 105 | margin: 3em 0 0 0; 106 | } 107 | 108 | section:nth-child(2) { 109 | @include vendor('order', '-1'); 110 | width: 100%; 111 | margin-left: 0; 112 | } 113 | 114 | .copyright { 115 | margin-top: 3em; 116 | } 117 | } 118 | } 119 | 120 | @include breakpoint('<=xsmall') { 121 | > .inner { 122 | .copyright { 123 | margin-top: 3em; 124 | 125 | li { 126 | border-left: 0; 127 | padding-left: 0; 128 | margin: 0.75em 0 0 0; 129 | display: block; 130 | line-height: inherit; 131 | 132 | &:first-child { 133 | margin-top: 0; 134 | } 135 | } 136 | } 137 | } 138 | } 139 | } -------------------------------------------------------------------------------- /app/static/assets/sass/layout/_header.scss: -------------------------------------------------------------------------------- 1 | /// 2 | /// Phantom by HTML5 UP 3 | /// html5up.net | @ajlkn 4 | /// Free for personal and commercial use under the CCA 3.0 license (html5up.net/license) 5 | /// 6 | 7 | /* Header */ 8 | 9 | #header { 10 | @include padding(5em, 0, (3em, 0, -5em, 0)); 11 | 12 | .logo { 13 | display: block; 14 | border-bottom: 0; 15 | color: inherit; 16 | font-weight: _font(weight-bold); 17 | letter-spacing: _font(letter-spacing); 18 | margin: 0 0 (_size(element-margin) * 1.25) 0; 19 | text-decoration: none; 20 | text-transform: uppercase; 21 | display: inline-block; 22 | 23 | > * { 24 | display: inline-block; 25 | vertical-align: middle; 26 | } 27 | 28 | .symbol { 29 | margin-right: 0.65em; 30 | 31 | img { 32 | display: block; 33 | width: 2em; 34 | height: 2em; 35 | } 36 | } 37 | } 38 | 39 | nav { 40 | position: fixed; 41 | right: 2em; 42 | top: 2em; 43 | z-index: _misc(z-index-base); 44 | 45 | ul { 46 | @include vendor('display', 'flex'); 47 | @include vendor('align-items', 'center'); 48 | list-style: none; 49 | margin: 0; 50 | padding: 0; 51 | 52 | li { 53 | display: block; 54 | padding: 0; 55 | 56 | a { 57 | display: block; 58 | position: relative; 59 | height: 3em; 60 | line-height: 3em; 61 | padding: 0 1.5em; 62 | background-color: transparentize(_palette(bg), 0.5); 63 | border-radius: _size(border-radius); 64 | border: 0; 65 | font-size: 0.8em; 66 | font-weight: _font(weight-bold); 67 | letter-spacing: _font(letter-spacing); 68 | text-transform: uppercase; 69 | } 70 | 71 | a[href="#menu"] { 72 | -webkit-tap-highlight-color: transparent; 73 | width: 4em; 74 | text-indent: 4em; 75 | font-size: 1em; 76 | overflow: hidden; 77 | padding: 0; 78 | white-space: nowrap; 79 | 80 | &:before, &:after { 81 | @include vendor('transition', 'opacity #{_duration(transition)} ease'); 82 | content: ''; 83 | display: block; 84 | position: absolute; 85 | top: 0; 86 | left: 0; 87 | width: 100%; 88 | height: 100%; 89 | background-position: center; 90 | background-repeat: no-repeat; 91 | background-size: 2em 2em; 92 | } 93 | 94 | &:before { 95 | background-image: svg-url(''); 96 | opacity: 0; 97 | } 98 | 99 | &:after { 100 | background-image: svg-url(''); 101 | opacity: 1; 102 | } 103 | 104 | &:hover { 105 | &:before { 106 | opacity: 1; 107 | } 108 | 109 | &:after { 110 | opacity: 0; 111 | } 112 | } 113 | } 114 | } 115 | } 116 | } 117 | 118 | @include breakpoint('<=small') { 119 | @include padding(3em, 0, (1em, 0, -3em, 0)); 120 | 121 | nav { 122 | right: 0.5em; 123 | top: 0.5em; 124 | 125 | ul { 126 | li { 127 | a[href="#menu"] { 128 | &:before, &:after { 129 | background-size: 1.5em 1.5em; 130 | } 131 | } 132 | } 133 | } 134 | } 135 | } 136 | } -------------------------------------------------------------------------------- /app/static/assets/sass/layout/_main.scss: -------------------------------------------------------------------------------- 1 | /// 2 | /// Phantom by HTML5 UP 3 | /// html5up.net | @ajlkn 4 | /// Free for personal and commercial use under the CCA 3.0 license (html5up.net/license) 5 | /// 6 | 7 | /* Main */ 8 | 9 | #main { 10 | @include padding(5em, 0, (-5em, 0, 3em, 0)); 11 | 12 | @include breakpoint('<=small') { 13 | @include padding(3em, 0, (-3em, 0, 3em, 0)); 14 | } 15 | } -------------------------------------------------------------------------------- /app/static/assets/sass/layout/_menu.scss: -------------------------------------------------------------------------------- 1 | /// 2 | /// Phantom by HTML5 UP 3 | /// html5up.net | @ajlkn 4 | /// Free for personal and commercial use under the CCA 3.0 license (html5up.net/license) 5 | /// 6 | 7 | /* Menu */ 8 | 9 | #wrapper { 10 | @include vendor('transition', 'opacity #{_duration(menu)} ease'); 11 | opacity: 1; 12 | } 13 | 14 | #menu { 15 | @include vendor('transform', 'translateX(#{_size(menu)})'); 16 | @include vendor('transition', ('transform #{_duration(menu)} ease', 'visibility #{_duration(menu)}')); 17 | position: fixed; 18 | top: 0; 19 | right: 0; 20 | width: _size(menu); 21 | max-width: 80%; 22 | height: 100%; 23 | -webkit-overflow-scrolling: touch; 24 | background: _palette(fg); 25 | color: _palette(bg); 26 | cursor: default; 27 | visibility: hidden; 28 | z-index: _misc(z-index-base) + 2; 29 | 30 | > .inner { 31 | @include vendor('transition', 'opacity #{_duration(menu)} ease'); 32 | -webkit-overflow-scrolling: touch; 33 | position: absolute; 34 | top: 0; 35 | left: 0; 36 | width: 100%; 37 | height: 100%; 38 | padding: 2.75em; 39 | opacity: 0; 40 | overflow-y: auto; 41 | 42 | > ul { 43 | list-style: none; 44 | margin: 0 0 (_size(element-margin) * 0.5) 0; 45 | padding: 0; 46 | 47 | > li { 48 | padding: 0; 49 | border-top: solid 1px transparentize(_palette(bg), 0.85); 50 | 51 | a { 52 | display: block; 53 | padding: 1em 0; 54 | line-height: 1.5; 55 | border: 0; 56 | color: inherit; 57 | } 58 | 59 | &:first-child { 60 | border-top: 0; 61 | margin-top: -1em; 62 | } 63 | } 64 | } 65 | } 66 | 67 | > .close { 68 | @include vendor('transition', ( 69 | 'opacity #{_duration(menu)} ease', 70 | 'transform #{_duration(menu)} ease' 71 | )); 72 | @include vendor('transform', 'scale(0.25) rotate(180deg)'); 73 | -webkit-tap-highlight-color: transparent; 74 | display: block; 75 | position: absolute; 76 | top: 2em; 77 | left: -6em; 78 | width: 6em; 79 | text-indent: 6em; 80 | height: 3em; 81 | border: 0; 82 | font-size: 1em; 83 | opacity: 0; 84 | overflow: hidden; 85 | padding: 0; 86 | white-space: nowrap; 87 | 88 | &:before, &:after { 89 | @include vendor('transition', 'opacity #{_duration(transition)} ease'); 90 | content: ''; 91 | display: block; 92 | position: absolute; 93 | top: 0; 94 | left: 0; 95 | width: 100%; 96 | height: 100%; 97 | background-position: center; 98 | background-repeat: no-repeat; 99 | background-size: 2em 2em; 100 | } 101 | 102 | &:before { 103 | background-image: svg-url(''); 104 | opacity: 0; 105 | } 106 | 107 | &:after { 108 | background-image: svg-url(''); 109 | opacity: 1; 110 | } 111 | 112 | &:hover { 113 | &:before { 114 | opacity: 1; 115 | } 116 | 117 | &:after { 118 | opacity: 0; 119 | } 120 | } 121 | } 122 | 123 | @include breakpoint('<=small') { 124 | @include vendor('transform', 'translateX(#{_size(menu) * 0.75})'); 125 | width: (_size(menu) * 0.75); 126 | 127 | > .inner { 128 | padding: 2.75em 1.5em; 129 | } 130 | 131 | > .close { 132 | top: 0.5em; 133 | left: -4.25em; 134 | width: 4.25em; 135 | text-indent: 4.25em; 136 | 137 | &:before, &:after { 138 | background-size: 1.5em 1.5em; 139 | } 140 | } 141 | } 142 | } 143 | 144 | body.is-menu-visible { 145 | #wrapper { 146 | @include vendor('pointer-events', 'none'); 147 | cursor: default; 148 | opacity: 0.25; 149 | } 150 | 151 | #menu { 152 | @include vendor('transform', 'translateX(0)'); 153 | visibility: visible; 154 | 155 | > * { 156 | opacity: 1; 157 | } 158 | 159 | .close { 160 | @include vendor('transform', 'scale(1.0) rotate(0deg)'); 161 | opacity: 1; 162 | } 163 | } 164 | } -------------------------------------------------------------------------------- /app/static/assets/sass/layout/_wrapper.scss: -------------------------------------------------------------------------------- 1 | /// 2 | /// Phantom by HTML5 UP 3 | /// html5up.net | @ajlkn 4 | /// Free for personal and commercial use under the CCA 3.0 license (html5up.net/license) 5 | /// 6 | 7 | /* Wrapper */ 8 | 9 | #wrapper { 10 | > * { 11 | > .inner { 12 | $gutter: _size(gutter); 13 | 14 | width: 100%; 15 | max-width: _size(inner); 16 | margin: 0 auto; 17 | padding: 0 $gutter; 18 | 19 | @include breakpoint('<=small') { 20 | $gutter: _size(gutter) * 0.5; 21 | 22 | padding: 0 $gutter; 23 | } 24 | } 25 | } 26 | } -------------------------------------------------------------------------------- /app/static/assets/sass/libs/_breakpoints.scss: -------------------------------------------------------------------------------- 1 | // breakpoints.scss v1.0 | @ajlkn | MIT licensed */ 2 | 3 | // Vars. 4 | 5 | /// Breakpoints. 6 | /// @var {list} 7 | $breakpoints: () !global; 8 | 9 | // Mixins. 10 | 11 | /// Sets breakpoints. 12 | /// @param {map} $x Breakpoints. 13 | @mixin breakpoints($x: ()) { 14 | $breakpoints: $x !global; 15 | } 16 | 17 | /// Wraps @content in a @media block targeting a specific orientation. 18 | /// @param {string} $orientation Orientation. 19 | @mixin orientation($orientation) { 20 | @media screen and (orientation: #{$orientation}) { 21 | @content; 22 | } 23 | } 24 | 25 | /// Wraps @content in a @media block using a given query. 26 | /// @param {string} $query Query. 27 | @mixin breakpoint($query: null) { 28 | 29 | $breakpoint: null; 30 | $op: null; 31 | $media: null; 32 | 33 | // Determine operator, breakpoint. 34 | 35 | // Greater than or equal. 36 | @if (str-slice($query, 0, 2) == '>=') { 37 | 38 | $op: 'gte'; 39 | $breakpoint: str-slice($query, 3); 40 | 41 | } 42 | 43 | // Less than or equal. 44 | @elseif (str-slice($query, 0, 2) == '<=') { 45 | 46 | $op: 'lte'; 47 | $breakpoint: str-slice($query, 3); 48 | 49 | } 50 | 51 | // Greater than. 52 | @elseif (str-slice($query, 0, 1) == '>') { 53 | 54 | $op: 'gt'; 55 | $breakpoint: str-slice($query, 2); 56 | 57 | } 58 | 59 | // Less than. 60 | @elseif (str-slice($query, 0, 1) == '<') { 61 | 62 | $op: 'lt'; 63 | $breakpoint: str-slice($query, 2); 64 | 65 | } 66 | 67 | // Not. 68 | @elseif (str-slice($query, 0, 1) == '!') { 69 | 70 | $op: 'not'; 71 | $breakpoint: str-slice($query, 2); 72 | 73 | } 74 | 75 | // Equal. 76 | @else { 77 | 78 | $op: 'eq'; 79 | $breakpoint: $query; 80 | 81 | } 82 | 83 | // Build media. 84 | @if ($breakpoint and map-has-key($breakpoints, $breakpoint)) { 85 | 86 | $a: map-get($breakpoints, $breakpoint); 87 | 88 | // Range. 89 | @if (type-of($a) == 'list') { 90 | 91 | $x: nth($a, 1); 92 | $y: nth($a, 2); 93 | 94 | // Max only. 95 | @if ($x == null) { 96 | 97 | // Greater than or equal (>= 0 / anything) 98 | @if ($op == 'gte') { 99 | $media: 'screen'; 100 | } 101 | 102 | // Less than or equal (<= y) 103 | @elseif ($op == 'lte') { 104 | $media: 'screen and (max-width: ' + $y + ')'; 105 | } 106 | 107 | // Greater than (> y) 108 | @elseif ($op == 'gt') { 109 | $media: 'screen and (min-width: ' + ($y + 1) + ')'; 110 | } 111 | 112 | // Less than (< 0 / invalid) 113 | @elseif ($op == 'lt') { 114 | $media: 'screen and (max-width: -1px)'; 115 | } 116 | 117 | // Not (> y) 118 | @elseif ($op == 'not') { 119 | $media: 'screen and (min-width: ' + ($y + 1) + ')'; 120 | } 121 | 122 | // Equal (<= y) 123 | @else { 124 | $media: 'screen and (max-width: ' + $y + ')'; 125 | } 126 | 127 | } 128 | 129 | // Min only. 130 | @else if ($y == null) { 131 | 132 | // Greater than or equal (>= x) 133 | @if ($op == 'gte') { 134 | $media: 'screen and (min-width: ' + $x + ')'; 135 | } 136 | 137 | // Less than or equal (<= inf / anything) 138 | @elseif ($op == 'lte') { 139 | $media: 'screen'; 140 | } 141 | 142 | // Greater than (> inf / invalid) 143 | @elseif ($op == 'gt') { 144 | $media: 'screen and (max-width: -1px)'; 145 | } 146 | 147 | // Less than (< x) 148 | @elseif ($op == 'lt') { 149 | $media: 'screen and (max-width: ' + ($x - 1) + ')'; 150 | } 151 | 152 | // Not (< x) 153 | @elseif ($op == 'not') { 154 | $media: 'screen and (max-width: ' + ($x - 1) + ')'; 155 | } 156 | 157 | // Equal (>= x) 158 | @else { 159 | $media: 'screen and (min-width: ' + $x + ')'; 160 | } 161 | 162 | } 163 | 164 | // Min and max. 165 | @else { 166 | 167 | // Greater than or equal (>= x) 168 | @if ($op == 'gte') { 169 | $media: 'screen and (min-width: ' + $x + ')'; 170 | } 171 | 172 | // Less than or equal (<= y) 173 | @elseif ($op == 'lte') { 174 | $media: 'screen and (max-width: ' + $y + ')'; 175 | } 176 | 177 | // Greater than (> y) 178 | @elseif ($op == 'gt') { 179 | $media: 'screen and (min-width: ' + ($y + 1) + ')'; 180 | } 181 | 182 | // Less than (< x) 183 | @elseif ($op == 'lt') { 184 | $media: 'screen and (max-width: ' + ($x - 1) + ')'; 185 | } 186 | 187 | // Not (< x and > y) 188 | @elseif ($op == 'not') { 189 | $media: 'screen and (max-width: ' + ($x - 1) + '), screen and (min-width: ' + ($y + 1) + ')'; 190 | } 191 | 192 | // Equal (>= x and <= y) 193 | @else { 194 | $media: 'screen and (min-width: ' + $x + ') and (max-width: ' + $y + ')'; 195 | } 196 | 197 | } 198 | 199 | } 200 | 201 | // String. 202 | @else { 203 | 204 | // Missing a media type? Prefix with "screen". 205 | @if (str-slice($a, 0, 1) == '(') { 206 | $media: 'screen and ' + $a; 207 | } 208 | 209 | // Otherwise, use as-is. 210 | @else { 211 | $media: $a; 212 | } 213 | 214 | } 215 | 216 | } 217 | 218 | // Output. 219 | @media #{$media} { 220 | @content; 221 | } 222 | 223 | } -------------------------------------------------------------------------------- /app/static/assets/sass/libs/_functions.scss: -------------------------------------------------------------------------------- 1 | /// Removes a specific item from a list. 2 | /// @author Hugo Giraudel 3 | /// @param {list} $list List. 4 | /// @param {integer} $index Index. 5 | /// @return {list} Updated list. 6 | @function remove-nth($list, $index) { 7 | 8 | $result: null; 9 | 10 | @if type-of($index) != number { 11 | @warn "$index: #{quote($index)} is not a number for `remove-nth`."; 12 | } 13 | @else if $index == 0 { 14 | @warn "List index 0 must be a non-zero integer for `remove-nth`."; 15 | } 16 | @else if abs($index) > length($list) { 17 | @warn "List index is #{$index} but list is only #{length($list)} item long for `remove-nth`."; 18 | } 19 | @else { 20 | 21 | $result: (); 22 | $index: if($index < 0, length($list) + $index + 1, $index); 23 | 24 | @for $i from 1 through length($list) { 25 | 26 | @if $i != $index { 27 | $result: append($result, nth($list, $i)); 28 | } 29 | 30 | } 31 | 32 | } 33 | 34 | @return $result; 35 | 36 | } 37 | 38 | /// Gets a value from a map. 39 | /// @author Hugo Giraudel 40 | /// @param {map} $map Map. 41 | /// @param {string} $keys Key(s). 42 | /// @return {string} Value. 43 | @function val($map, $keys...) { 44 | 45 | @if nth($keys, 1) == null { 46 | $keys: remove-nth($keys, 1); 47 | } 48 | 49 | @each $key in $keys { 50 | $map: map-get($map, $key); 51 | } 52 | 53 | @return $map; 54 | 55 | } 56 | 57 | /// Gets a duration value. 58 | /// @param {string} $keys Key(s). 59 | /// @return {string} Value. 60 | @function _duration($keys...) { 61 | @return val($duration, $keys...); 62 | } 63 | 64 | /// Gets a font value. 65 | /// @param {string} $keys Key(s). 66 | /// @return {string} Value. 67 | @function _font($keys...) { 68 | @return val($font, $keys...); 69 | } 70 | 71 | /// Gets a misc value. 72 | /// @param {string} $keys Key(s). 73 | /// @return {string} Value. 74 | @function _misc($keys...) { 75 | @return val($misc, $keys...); 76 | } 77 | 78 | /// Gets a palette value. 79 | /// @param {string} $keys Key(s). 80 | /// @return {string} Value. 81 | @function _palette($keys...) { 82 | @return val($palette, $keys...); 83 | } 84 | 85 | /// Gets a size value. 86 | /// @param {string} $keys Key(s). 87 | /// @return {string} Value. 88 | @function _size($keys...) { 89 | @return val($size, $keys...); 90 | } -------------------------------------------------------------------------------- /app/static/assets/sass/libs/_html-grid.scss: -------------------------------------------------------------------------------- 1 | // html-grid.scss v1.0 | @ajlkn | MIT licensed */ 2 | 3 | // Mixins. 4 | 5 | /// Initializes the current element as an HTML grid. 6 | /// @param {mixed} $gutters Gutters (either a single number to set both column/row gutters, or a list to set them individually). 7 | /// @param {mixed} $suffix Column class suffix (optional; either a single suffix or a list). 8 | @mixin html-grid($gutters: 1.5em, $suffix: '') { 9 | 10 | // Initialize. 11 | $cols: 12; 12 | $multipliers: 0, 0.25, 0.5, 1, 1.50, 2.00; 13 | $unit: 100% / $cols; 14 | 15 | // Suffixes. 16 | $suffixes: null; 17 | 18 | @if (type-of($suffix) == 'list') { 19 | $suffixes: $suffix; 20 | } 21 | @else { 22 | $suffixes: ($suffix); 23 | } 24 | 25 | // Gutters. 26 | $guttersCols: null; 27 | $guttersRows: null; 28 | 29 | @if (type-of($gutters) == 'list') { 30 | 31 | $guttersCols: nth($gutters, 1); 32 | $guttersRows: nth($gutters, 2); 33 | 34 | } 35 | @else { 36 | 37 | $guttersCols: $gutters; 38 | $guttersRows: 0; 39 | 40 | } 41 | 42 | // Row. 43 | display: flex; 44 | flex-wrap: wrap; 45 | box-sizing: border-box; 46 | align-items: stretch; 47 | 48 | // Columns. 49 | > * { 50 | box-sizing: border-box; 51 | } 52 | 53 | // Gutters. 54 | &.gtr-uniform { 55 | > * { 56 | > :last-child { 57 | margin-bottom: 0; 58 | } 59 | } 60 | } 61 | 62 | // Alignment. 63 | &.aln-left { 64 | justify-content: flex-start; 65 | } 66 | 67 | &.aln-center { 68 | justify-content: center; 69 | } 70 | 71 | &.aln-right { 72 | justify-content: flex-end; 73 | } 74 | 75 | &.aln-top { 76 | align-items: flex-start; 77 | } 78 | 79 | &.aln-middle { 80 | align-items: center; 81 | } 82 | 83 | &.aln-bottom { 84 | align-items: flex-end; 85 | } 86 | 87 | // Step through suffixes. 88 | @each $suffix in $suffixes { 89 | 90 | // Suffix. 91 | @if ($suffix != '') { 92 | $suffix: '-' + $suffix; 93 | } 94 | @else { 95 | $suffix: ''; 96 | } 97 | 98 | // Row. 99 | 100 | // Important. 101 | > .imp#{$suffix} { 102 | order: -1; 103 | } 104 | 105 | // Columns, offsets. 106 | @for $i from 1 through $cols { 107 | > .col-#{$i}#{$suffix} { 108 | width: $unit * $i; 109 | } 110 | 111 | > .off-#{$i}#{$suffix} { 112 | margin-left: $unit * $i; 113 | } 114 | } 115 | 116 | // Step through multipliers. 117 | @each $multiplier in $multipliers { 118 | 119 | // Gutters. 120 | $class: null; 121 | 122 | @if ($multiplier != 1) { 123 | $class: '.gtr-' + ($multiplier * 100); 124 | } 125 | 126 | &#{$class} { 127 | margin-top: ($guttersRows * $multiplier * -1); 128 | margin-left: ($guttersCols * $multiplier * -1); 129 | 130 | > * { 131 | padding: ($guttersRows * $multiplier) 0 0 ($guttersCols * $multiplier); 132 | } 133 | 134 | // Uniform. 135 | &.gtr-uniform { 136 | margin-top: $guttersCols * $multiplier * -1; 137 | 138 | > * { 139 | padding-top: $guttersCols * $multiplier; 140 | } 141 | } 142 | 143 | } 144 | 145 | } 146 | 147 | } 148 | 149 | } -------------------------------------------------------------------------------- /app/static/assets/sass/libs/_mixins.scss: -------------------------------------------------------------------------------- 1 | /// Makes an element's :before pseudoelement a FontAwesome icon. 2 | /// @param {string} $content Optional content value to use. 3 | /// @param {string} $where Optional pseudoelement to target (before or after). 4 | @mixin icon($content: false, $where: before) { 5 | 6 | text-decoration: none; 7 | 8 | &:#{$where} { 9 | 10 | @if $content { 11 | content: $content; 12 | } 13 | 14 | -moz-osx-font-smoothing: grayscale; 15 | -webkit-font-smoothing: antialiased; 16 | font-family: FontAwesome; 17 | font-style: normal; 18 | font-weight: normal; 19 | text-transform: none !important; 20 | 21 | } 22 | 23 | } 24 | 25 | /// Applies padding to an element, taking the current element-margin value into account. 26 | /// @param {mixed} $tb Top/bottom padding. 27 | /// @param {mixed} $lr Left/right padding. 28 | /// @param {list} $pad Optional extra padding (in the following order top, right, bottom, left) 29 | /// @param {bool} $important If true, adds !important. 30 | @mixin padding($tb, $lr, $pad: (0,0,0,0), $important: null) { 31 | 32 | @if $important { 33 | $important: '!important'; 34 | } 35 | 36 | $x: 0.1em; 37 | 38 | @if unit(_size(element-margin)) == 'rem' { 39 | $x: 0.1rem; 40 | } 41 | 42 | padding: ($tb + nth($pad,1)) ($lr + nth($pad,2)) max($x, $tb - _size(element-margin) + nth($pad,3)) ($lr + nth($pad,4)) #{$important}; 43 | 44 | } 45 | 46 | /// Encodes a SVG data URL so IE doesn't choke (via codepen.io/jakob-e/pen/YXXBrp). 47 | /// @param {string} $svg SVG data URL. 48 | /// @return {string} Encoded SVG data URL. 49 | @function svg-url($svg) { 50 | 51 | $svg: str-replace($svg, '"', '\''); 52 | $svg: str-replace($svg, '%', '%25'); 53 | $svg: str-replace($svg, '<', '%3C'); 54 | $svg: str-replace($svg, '>', '%3E'); 55 | $svg: str-replace($svg, '&', '%26'); 56 | $svg: str-replace($svg, '#', '%23'); 57 | $svg: str-replace($svg, '{', '%7B'); 58 | $svg: str-replace($svg, '}', '%7D'); 59 | $svg: str-replace($svg, ';', '%3B'); 60 | 61 | @return url("data:image/svg+xml;charset=utf8,#{$svg}"); 62 | 63 | } -------------------------------------------------------------------------------- /app/static/assets/sass/libs/_vars.scss: -------------------------------------------------------------------------------- 1 | // Misc. 2 | $misc: ( 3 | z-index-base: 10000 4 | ); 5 | 6 | // Duration. 7 | $duration: ( 8 | menu: 0.45s, 9 | transition: 0.2s 10 | ); 11 | 12 | // Size. 13 | $size: ( 14 | border-radius: 4px, 15 | border-width: 1px, 16 | element-height: 3em, 17 | element-margin: 2em, 18 | gutter: 2.5em, 19 | field-gutter: 2em, 20 | inner: 68em, 21 | menu: 22em 22 | ); 23 | 24 | // Font. 25 | $font: ( 26 | family: ('Source Sans Pro', Helvetica, sans-serif), 27 | family-fixed: ('Courier New', monospace), 28 | weight: 300, 29 | weight-bold: 900, 30 | weight-bold-alt: 700, 31 | letter-spacing: 0.35em, 32 | letter-spacing-alt: -0.035em 33 | ); 34 | 35 | // Palette. 36 | $palette: ( 37 | bg: #ffffff, 38 | bg-accent: #333333, 39 | bg-alt: #f6f6f6, 40 | fg: #585858, 41 | fg-accent: #ffffff, 42 | border: #c9c9c9, 43 | border-bg: rgba(144,144,144,0.075), 44 | accent1: #f2849e, 45 | accent2: #7ecaf6, 46 | accent3: #7bd0c1, 47 | accent4: #c75b9b, 48 | accent5: #ae85ca, 49 | accent6: #8499e7, 50 | ); -------------------------------------------------------------------------------- /app/static/assets/sass/libs/_vendor.scss: -------------------------------------------------------------------------------- 1 | // vendor.scss v1.0 | @ajlkn | MIT licensed */ 2 | 3 | // Vars. 4 | 5 | /// Vendor prefixes. 6 | /// @var {list} 7 | $vendor-prefixes: ( 8 | '-moz-', 9 | '-webkit-', 10 | '-ms-', 11 | '' 12 | ); 13 | 14 | /// Properties that should be vendorized. 15 | /// Data via caniuse.com, github.com/postcss/autoprefixer, and developer.mozilla.org 16 | /// @var {list} 17 | $vendor-properties: ( 18 | 19 | // Animation. 20 | 'animation', 21 | 'animation-delay', 22 | 'animation-direction', 23 | 'animation-duration', 24 | 'animation-fill-mode', 25 | 'animation-iteration-count', 26 | 'animation-name', 27 | 'animation-play-state', 28 | 'animation-timing-function', 29 | 30 | // Appearance. 31 | 'appearance', 32 | 33 | // Backdrop filter. 34 | 'backdrop-filter', 35 | 36 | // Background image options. 37 | 'background-clip', 38 | 'background-origin', 39 | 'background-size', 40 | 41 | // Box sizing. 42 | 'box-sizing', 43 | 44 | // Clip path. 45 | 'clip-path', 46 | 47 | // Filter effects. 48 | 'filter', 49 | 50 | // Flexbox. 51 | 'align-content', 52 | 'align-items', 53 | 'align-self', 54 | 'flex', 55 | 'flex-basis', 56 | 'flex-direction', 57 | 'flex-flow', 58 | 'flex-grow', 59 | 'flex-shrink', 60 | 'flex-wrap', 61 | 'justify-content', 62 | 'order', 63 | 64 | // Font feature. 65 | 'font-feature-settings', 66 | 'font-language-override', 67 | 'font-variant-ligatures', 68 | 69 | // Font kerning. 70 | 'font-kerning', 71 | 72 | // Fragmented borders and backgrounds. 73 | 'box-decoration-break', 74 | 75 | // Grid layout. 76 | 'grid-column', 77 | 'grid-column-align', 78 | 'grid-column-end', 79 | 'grid-column-start', 80 | 'grid-row', 81 | 'grid-row-align', 82 | 'grid-row-end', 83 | 'grid-row-start', 84 | 'grid-template-columns', 85 | 'grid-template-rows', 86 | 87 | // Hyphens. 88 | 'hyphens', 89 | 'word-break', 90 | 91 | // Masks. 92 | 'mask', 93 | 'mask-border', 94 | 'mask-border-outset', 95 | 'mask-border-repeat', 96 | 'mask-border-slice', 97 | 'mask-border-source', 98 | 'mask-border-width', 99 | 'mask-clip', 100 | 'mask-composite', 101 | 'mask-image', 102 | 'mask-origin', 103 | 'mask-position', 104 | 'mask-repeat', 105 | 'mask-size', 106 | 107 | // Multicolumn. 108 | 'break-after', 109 | 'break-before', 110 | 'break-inside', 111 | 'column-count', 112 | 'column-fill', 113 | 'column-gap', 114 | 'column-rule', 115 | 'column-rule-color', 116 | 'column-rule-style', 117 | 'column-rule-width', 118 | 'column-span', 119 | 'column-width', 120 | 'columns', 121 | 122 | // Object fit. 123 | 'object-fit', 124 | 'object-position', 125 | 126 | // Regions. 127 | 'flow-from', 128 | 'flow-into', 129 | 'region-fragment', 130 | 131 | // Scroll snap points. 132 | 'scroll-snap-coordinate', 133 | 'scroll-snap-destination', 134 | 'scroll-snap-points-x', 135 | 'scroll-snap-points-y', 136 | 'scroll-snap-type', 137 | 138 | // Shapes. 139 | 'shape-image-threshold', 140 | 'shape-margin', 141 | 'shape-outside', 142 | 143 | // Tab size. 144 | 'tab-size', 145 | 146 | // Text align last. 147 | 'text-align-last', 148 | 149 | // Text decoration. 150 | 'text-decoration-color', 151 | 'text-decoration-line', 152 | 'text-decoration-skip', 153 | 'text-decoration-style', 154 | 155 | // Text emphasis. 156 | 'text-emphasis', 157 | 'text-emphasis-color', 158 | 'text-emphasis-position', 159 | 'text-emphasis-style', 160 | 161 | // Text size adjust. 162 | 'text-size-adjust', 163 | 164 | // Text spacing. 165 | 'text-spacing', 166 | 167 | // Transform. 168 | 'transform', 169 | 'transform-origin', 170 | 171 | // Transform 3D. 172 | 'backface-visibility', 173 | 'perspective', 174 | 'perspective-origin', 175 | 'transform-style', 176 | 177 | // Transition. 178 | 'transition', 179 | 'transition-delay', 180 | 'transition-duration', 181 | 'transition-property', 182 | 'transition-timing-function', 183 | 184 | // Unicode bidi. 185 | 'unicode-bidi', 186 | 187 | // User select. 188 | 'user-select', 189 | 190 | // Writing mode. 191 | 'writing-mode', 192 | 193 | ); 194 | 195 | /// Values that should be vendorized. 196 | /// Data via caniuse.com, github.com/postcss/autoprefixer, and developer.mozilla.org 197 | /// @var {list} 198 | $vendor-values: ( 199 | 200 | // Cross fade. 201 | 'cross-fade', 202 | 203 | // Element function. 204 | 'element', 205 | 206 | // Filter function. 207 | 'filter', 208 | 209 | // Flexbox. 210 | 'flex', 211 | 'inline-flex', 212 | 213 | // Grab cursors. 214 | 'grab', 215 | 'grabbing', 216 | 217 | // Gradients. 218 | 'linear-gradient', 219 | 'repeating-linear-gradient', 220 | 'radial-gradient', 221 | 'repeating-radial-gradient', 222 | 223 | // Grid layout. 224 | 'grid', 225 | 'inline-grid', 226 | 227 | // Image set. 228 | 'image-set', 229 | 230 | // Intrinsic width. 231 | 'max-content', 232 | 'min-content', 233 | 'fit-content', 234 | 'fill', 235 | 'fill-available', 236 | 'stretch', 237 | 238 | // Sticky position. 239 | 'sticky', 240 | 241 | // Transform. 242 | 'transform', 243 | 244 | // Zoom cursors. 245 | 'zoom-in', 246 | 'zoom-out', 247 | 248 | ); 249 | 250 | // Functions. 251 | 252 | /// Removes a specific item from a list. 253 | /// @author Hugo Giraudel 254 | /// @param {list} $list List. 255 | /// @param {integer} $index Index. 256 | /// @return {list} Updated list. 257 | @function remove-nth($list, $index) { 258 | 259 | $result: null; 260 | 261 | @if type-of($index) != number { 262 | @warn "$index: #{quote($index)} is not a number for `remove-nth`."; 263 | } 264 | @else if $index == 0 { 265 | @warn "List index 0 must be a non-zero integer for `remove-nth`."; 266 | } 267 | @else if abs($index) > length($list) { 268 | @warn "List index is #{$index} but list is only #{length($list)} item long for `remove-nth`."; 269 | } 270 | @else { 271 | 272 | $result: (); 273 | $index: if($index < 0, length($list) + $index + 1, $index); 274 | 275 | @for $i from 1 through length($list) { 276 | 277 | @if $i != $index { 278 | $result: append($result, nth($list, $i)); 279 | } 280 | 281 | } 282 | 283 | } 284 | 285 | @return $result; 286 | 287 | } 288 | 289 | /// Replaces a substring within another string. 290 | /// @author Hugo Giraudel 291 | /// @param {string} $string String. 292 | /// @param {string} $search Substring. 293 | /// @param {string} $replace Replacement. 294 | /// @return {string} Updated string. 295 | @function str-replace($string, $search, $replace: '') { 296 | 297 | $index: str-index($string, $search); 298 | 299 | @if $index { 300 | @return str-slice($string, 1, $index - 1) + $replace + str-replace(str-slice($string, $index + str-length($search)), $search, $replace); 301 | } 302 | 303 | @return $string; 304 | 305 | } 306 | 307 | /// Replaces a substring within each string in a list. 308 | /// @param {list} $strings List of strings. 309 | /// @param {string} $search Substring. 310 | /// @param {string} $replace Replacement. 311 | /// @return {list} Updated list of strings. 312 | @function str-replace-all($strings, $search, $replace: '') { 313 | 314 | @each $string in $strings { 315 | $strings: set-nth($strings, index($strings, $string), str-replace($string, $search, $replace)); 316 | } 317 | 318 | @return $strings; 319 | 320 | } 321 | 322 | // Mixins. 323 | 324 | /// Wraps @content in vendorized keyframe blocks. 325 | /// @param {string} $name Name. 326 | @mixin keyframes($name) { 327 | 328 | @-moz-keyframes #{$name} { @content; } 329 | @-webkit-keyframes #{$name} { @content; } 330 | @-ms-keyframes #{$name} { @content; } 331 | @keyframes #{$name} { @content; } 332 | 333 | } 334 | 335 | /// Vendorizes a declaration's property and/or value(s). 336 | /// @param {string} $property Property. 337 | /// @param {mixed} $value String/list of value(s). 338 | @mixin vendor($property, $value) { 339 | 340 | // Determine if property should expand. 341 | $expandProperty: index($vendor-properties, $property); 342 | 343 | // Determine if value should expand (and if so, add '-prefix-' placeholder). 344 | $expandValue: false; 345 | 346 | @each $x in $value { 347 | @each $y in $vendor-values { 348 | @if $y == str-slice($x, 1, str-length($y)) { 349 | 350 | $value: set-nth($value, index($value, $x), '-prefix-' + $x); 351 | $expandValue: true; 352 | 353 | } 354 | } 355 | } 356 | 357 | // Expand property? 358 | @if $expandProperty { 359 | @each $vendor in $vendor-prefixes { 360 | #{$vendor}#{$property}: #{str-replace-all($value, '-prefix-', $vendor)}; 361 | } 362 | } 363 | 364 | // Expand just the value? 365 | @elseif $expandValue { 366 | @each $vendor in $vendor-prefixes { 367 | #{$property}: #{str-replace-all($value, '-prefix-', $vendor)}; 368 | } 369 | } 370 | 371 | // Neither? Treat them as a normal declaration. 372 | @else { 373 | #{$property}: #{$value}; 374 | } 375 | 376 | } -------------------------------------------------------------------------------- /app/static/assets/sass/main.scss: -------------------------------------------------------------------------------- 1 | @import 'libs/vars'; 2 | @import 'libs/functions'; 3 | @import 'libs/mixins'; 4 | @import 'libs/vendor'; 5 | @import 'libs/breakpoints'; 6 | @import 'libs/html-grid'; 7 | @import 'font-awesome.min.css'; 8 | @import url('https://fonts.googleapis.com/css?family=Source+Sans+Pro:300,700,900'); 9 | 10 | /* 11 | Phantom by HTML5 UP 12 | html5up.net | @ajlkn 13 | Free for personal and commercial use under the CCA 3.0 license (html5up.net/license) 14 | */ 15 | 16 | // Breakpoints. 17 | 18 | @include breakpoints(( 19 | xlarge: ( 1281px, 1680px ), 20 | large: ( 981px, 1280px ), 21 | medium: ( 737px, 980px ), 22 | small: ( 481px, 736px ), 23 | xsmall: ( 361px, 480px ), 24 | xxsmall: ( null, 360px ) 25 | )); 26 | 27 | // Base. 28 | 29 | @import 'base/reset'; 30 | @import 'base/page'; 31 | @import 'base/typography'; 32 | 33 | // Component. 34 | 35 | @import 'components/row'; 36 | @import 'components/section'; 37 | @import 'components/icon'; 38 | @import 'components/list'; 39 | @import 'components/actions'; 40 | @import 'components/icons'; 41 | @import 'components/form'; 42 | @import 'components/box'; 43 | @import 'components/image'; 44 | @import 'components/table'; 45 | @import 'components/button'; 46 | @import 'components/tiles'; 47 | 48 | // Layout. 49 | 50 | @import 'layout/header'; 51 | @import 'layout/menu'; 52 | @import 'layout/main'; 53 | @import 'layout/footer'; 54 | @import 'layout/wrapper'; -------------------------------------------------------------------------------- /app/static/assets/sass/noscript.scss: -------------------------------------------------------------------------------- 1 | @import 'libs/vars'; 2 | @import 'libs/functions'; 3 | @import 'libs/mixins'; 4 | @import 'libs/vendor'; 5 | @import 'libs/breakpoints'; 6 | @import 'libs/html-grid'; 7 | 8 | /* 9 | Phantom by HTML5 UP 10 | html5up.net | @ajlkn 11 | Free for personal and commercial use under the CCA 3.0 license (html5up.net/license) 12 | */ 13 | 14 | /* Tiles */ 15 | 16 | .tiles { 17 | body.is-preload & { 18 | article { 19 | @include vendor('transform', 'none'); 20 | opacity: 1; 21 | } 22 | } 23 | } -------------------------------------------------------------------------------- /app/static/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/app-generator/flask-boilerplate-flaskplay/62b50bb52b63b307410fac5dbd38c81705bd9d31/app/static/favicon.ico -------------------------------------------------------------------------------- /app/static/images/logo.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 6 | 7 | 14 | 15 | -------------------------------------------------------------------------------- /app/static/images/pic01.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/app-generator/flask-boilerplate-flaskplay/62b50bb52b63b307410fac5dbd38c81705bd9d31/app/static/images/pic01.jpg -------------------------------------------------------------------------------- /app/static/images/pic02.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/app-generator/flask-boilerplate-flaskplay/62b50bb52b63b307410fac5dbd38c81705bd9d31/app/static/images/pic02.jpg -------------------------------------------------------------------------------- /app/static/images/pic03.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/app-generator/flask-boilerplate-flaskplay/62b50bb52b63b307410fac5dbd38c81705bd9d31/app/static/images/pic03.jpg -------------------------------------------------------------------------------- /app/static/images/pic04.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/app-generator/flask-boilerplate-flaskplay/62b50bb52b63b307410fac5dbd38c81705bd9d31/app/static/images/pic04.jpg -------------------------------------------------------------------------------- /app/static/images/pic05.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/app-generator/flask-boilerplate-flaskplay/62b50bb52b63b307410fac5dbd38c81705bd9d31/app/static/images/pic05.jpg -------------------------------------------------------------------------------- /app/static/images/pic06.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/app-generator/flask-boilerplate-flaskplay/62b50bb52b63b307410fac5dbd38c81705bd9d31/app/static/images/pic06.jpg -------------------------------------------------------------------------------- /app/static/images/pic07.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/app-generator/flask-boilerplate-flaskplay/62b50bb52b63b307410fac5dbd38c81705bd9d31/app/static/images/pic07.jpg -------------------------------------------------------------------------------- /app/static/images/pic08.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/app-generator/flask-boilerplate-flaskplay/62b50bb52b63b307410fac5dbd38c81705bd9d31/app/static/images/pic08.jpg -------------------------------------------------------------------------------- /app/static/images/pic09.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/app-generator/flask-boilerplate-flaskplay/62b50bb52b63b307410fac5dbd38c81705bd9d31/app/static/images/pic09.jpg -------------------------------------------------------------------------------- /app/static/images/pic10.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/app-generator/flask-boilerplate-flaskplay/62b50bb52b63b307410fac5dbd38c81705bd9d31/app/static/images/pic10.jpg -------------------------------------------------------------------------------- /app/static/images/pic11.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/app-generator/flask-boilerplate-flaskplay/62b50bb52b63b307410fac5dbd38c81705bd9d31/app/static/images/pic11.jpg -------------------------------------------------------------------------------- /app/static/images/pic12.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/app-generator/flask-boilerplate-flaskplay/62b50bb52b63b307410fac5dbd38c81705bd9d31/app/static/images/pic12.jpg -------------------------------------------------------------------------------- /app/static/images/pic13.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/app-generator/flask-boilerplate-flaskplay/62b50bb52b63b307410fac5dbd38c81705bd9d31/app/static/images/pic13.jpg -------------------------------------------------------------------------------- /app/static/images/pic14.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/app-generator/flask-boilerplate-flaskplay/62b50bb52b63b307410fac5dbd38c81705bd9d31/app/static/images/pic14.jpg -------------------------------------------------------------------------------- /app/static/images/pic15.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/app-generator/flask-boilerplate-flaskplay/62b50bb52b63b307410fac5dbd38c81705bd9d31/app/static/images/pic15.jpg -------------------------------------------------------------------------------- /app/static/sitemap.xml: -------------------------------------------------------------------------------- 1 | 2 | 7 | 8 | 9 | 10 | https://flask-boilerplate.appseed.us 11 | 1 12 | monthly 13 | 14 | 15 | 16 | 17 | -------------------------------------------------------------------------------- /app/templates/includes/footer.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 32 | 33 | 34 | -------------------------------------------------------------------------------- /app/templates/includes/navigation.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 18 | 19 | -------------------------------------------------------------------------------- /app/templates/includes/scripts.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /app/templates/layouts/base-site.html: -------------------------------------------------------------------------------- 1 | 2 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | FlaskPlay Boilerplate - {% block title %}{% endblock %} | AppSeed 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 24 | 25 | 26 | 27 |
28 | 29 | 30 | 50 | 51 | {% include 'includes/navigation.html' %} 52 | 53 | 54 |
55 |
56 | 57 | {% block content %}{% endblock content %} 58 | 59 |
60 |
61 | 62 | {% include 'includes/footer.html' %} 63 | 64 |
65 | 66 | {% include 'includes/scripts.html' %} 67 | 68 | 69 | 70 | 71 | -------------------------------------------------------------------------------- /app/templates/pages/about.html: -------------------------------------------------------------------------------- 1 | {% extends "layouts/base-site.html" %} 2 | 3 | {% block title %} About {% endblock %} 4 | 5 | 6 | {% block stylesheets %}{% endblock stylesheets %} 7 | 8 | {% block content %} 9 | 10 |

About FlaskPlay

11 | 12 |

Donec eget ex magna. Interdum et malesuada fames ac ante ipsum primis in faucibus. Pellentesque venenatis dolor imperdiet dolor mattis sagittis. Praesent rutrum sem diam, vitae egestas enim auctor sit amet. Pellentesque leo mauris, consectetur id ipsum sit amet, fergiat. Pellentesque in mi eu massa lacinia malesuada et a elit. Donec urna ex, lacinia in purus ac, pretium pulvinar mauris. Curabitur sapien risus, commodo eget turpis at, elementum convallis elit. Pellentesque enim turpis, hendrerit tristique.

13 |

Lorem ipsum dolor sit amet, consectetur adipiscing elit. Duis dapibus rutrum facilisis. Class aptent taciti sociosqu ad litora torquent per conubia nostra, per inceptos himenaeos. Etiam tristique libero eu nibh porttitor fermentum. Nullam venenatis erat id vehicula viverra. Nunc ultrices eros ut ultricies condimentum. Mauris risus lacus, blandit sit amet venenatis non, bibendum vitae dolor. Nunc lorem mauris, fringilla in aliquam at, euismod in lectus. Pellentesque habitant morbi tristique senectus et netus et malesuada fames ac turpis egestas. In non lorem sit amet elit placerat maximus. Pellentesque aliquam maximus risus, vel venenatis mauris vehicula hendrerit.

14 |

Interdum et malesuada fames ac ante ipsum primis in faucibus. Pellentesque venenatis dolor imperdiet dolor mattis sagittis. Praesent rutrum sem diam, vitae egestas enim auctor sit amet. Pellentesque leo mauris, consectetur id ipsum sit amet, fersapien risus, commodo eget turpis at, elementum convallis elit. Pellentesque enim turpis, hendrerit tristique lorem ipsum dolor.

15 | 16 | {% endblock content %} 17 | 18 | 19 | {% block javascripts %}{% endblock javascripts %} 20 | -------------------------------------------------------------------------------- /app/templates/pages/elements.html: -------------------------------------------------------------------------------- 1 | {% extends "layouts/base-site.html" %} 2 | 3 | {% block title %} UI Elements {% endblock %} 4 | 5 | 6 | {% block stylesheets %}{% endblock stylesheets %} 7 | 8 | {% block content %} 9 | 10 |

Elements

11 | 12 | 13 |
14 |

Text

15 |

This is bold and this is strong. This is italic and this is emphasized. 16 | This is superscript text and this is subscript text. 17 | This is underlined and this is code: for (;;) { ... }. Finally, this is a link.

18 |
19 |

Nunc lacinia ante nunc ac lobortis. Interdum adipiscing gravida odio porttitor sem non mi integer non faucibus ornare mi ut ante amet placerat aliquet. Volutpat eu sed ante lacinia sapien lorem accumsan varius montes viverra nibh in adipiscing blandit tempus accumsan.

20 |
21 |

Heading Level 2

22 |

Heading Level 3

23 |

Heading Level 4

24 |
25 |

Blockquote

26 |
Fringilla nisl. Donec accumsan interdum nisi, quis tincidunt felis sagittis eget tempus euismod. Vestibulum ante ipsum primis in faucibus vestibulum. Blandit adipiscing eu felis iaculis volutpat ac adipiscing accumsan faucibus. Vestibulum ante ipsum primis in faucibus lorem ipsum dolor sit amet nullam adipiscing eu felis.
27 |

Preformatted

28 |
i = 0;
 29 | 
 30 | while (!deck.isInOrder()) {
 31 |     print 'Iteration ' + i;
 32 |     deck.shuffle();
 33 |     i++;
 34 | }
 35 | 
 36 | print 'It took ' + i + ' iterations to sort the deck.';
37 |
38 | 39 | 40 |
41 |

Lists

42 |
43 |
44 |

Unordered

45 |
    46 |
  • Dolor pulvinar etiam.
  • 47 |
  • Sagittis adipiscing.
  • 48 |
  • Felis enim feugiat.
  • 49 |
50 |

Alternate

51 |
    52 |
  • Dolor pulvinar etiam.
  • 53 |
  • Sagittis adipiscing.
  • 54 |
  • Felis enim feugiat.
  • 55 |
56 |
57 |
58 |

Ordered

59 |
    60 |
  1. Dolor pulvinar etiam.
  2. 61 |
  3. Etiam vel felis viverra.
  4. 62 |
  5. Felis enim feugiat.
  6. 63 |
  7. Dolor pulvinar etiam.
  8. 64 |
  9. Etiam vel felis lorem.
  10. 65 |
  11. Felis enim et feugiat.
  12. 66 |
67 |

Icons

68 | 74 | 80 |
81 |
82 |

Actions

83 |
84 |
85 | 89 | 93 | 97 | 101 |
102 |
103 | 107 | 111 |
112 |
113 |
114 | 115 | 116 |
117 |

Table

118 |

Default

119 |
120 | 121 | 122 | 123 | 124 | 125 | 126 | 127 | 128 | 129 | 130 | 131 | 132 | 133 | 134 | 135 | 136 | 137 | 138 | 139 | 140 | 141 | 142 | 143 | 144 | 145 | 146 | 147 | 148 | 149 | 150 | 151 | 152 | 153 | 154 | 155 | 156 | 157 | 158 | 159 | 160 | 161 |
NameDescriptionPrice
Item OneAnte turpis integer aliquet porttitor.29.99
Item TwoVis ac commodo adipiscing arcu aliquet.19.99
Item Three Morbi faucibus arcu accumsan lorem.29.99
Item FourVitae integer tempus condimentum.19.99
Item FiveAnte turpis integer aliquet porttitor.29.99
100.00
162 |
163 | 164 |

Alternate

165 |
166 | 167 | 168 | 169 | 170 | 171 | 172 | 173 | 174 | 175 | 176 | 177 | 178 | 179 | 180 | 181 | 182 | 183 | 184 | 185 | 186 | 187 | 188 | 189 | 190 | 191 | 192 | 193 | 194 | 195 | 196 | 197 | 198 | 199 | 200 | 201 | 202 | 203 | 204 | 205 | 206 | 207 |
NameDescriptionPrice
Item OneAnte turpis integer aliquet porttitor.29.99
Item TwoVis ac commodo adipiscing arcu aliquet.19.99
Item Three Morbi faucibus arcu accumsan lorem.29.99
Item FourVitae integer tempus condimentum.19.99
Item FiveAnte turpis integer aliquet porttitor.29.99
100.00
208 |
209 |
210 | 211 | 212 |
213 |

Buttons

214 | 218 | 223 | 228 | 233 | 238 |
    239 |
  • Disabled
  • 240 |
  • Disabled
  • 241 |
242 |
243 | 244 | 245 |
246 |

Form

247 |
248 |
249 |
250 | 251 |
252 |
253 | 254 |
255 |
256 | 263 |
264 |
265 | 266 | 267 |
268 |
269 | 270 | 271 |
272 |
273 | 274 | 275 |
276 |
277 | 278 | 279 |
280 |
281 | 282 | 283 |
284 |
285 | 286 |
287 |
288 |
    289 |
  • 290 |
  • 291 |
292 |
293 |
294 |
295 |
296 | 297 | 298 |
299 |

Image

300 |

Fit

301 |
302 |
303 |
304 |
305 |
306 |
307 |
308 |
309 |
310 |
311 |
312 |
313 |
314 |
315 |

Left & Right

316 |

Fringilla nisl. Donec accumsan interdum nisi, quis tincidunt felis sagittis eget. tempus euismod. Vestibulum ante ipsum primis in faucibus vestibulum. Blandit adipiscing eu felis iaculis volutpat ac adipiscing accumsan eu faucibus. Integer ac pellentesque praesent tincidunt felis sagittis eget. tempus euismod. Vestibulum ante ipsum primis in faucibus vestibulum. Blandit adipiscing eu felis iaculis volutpat ac adipiscing accumsan eu faucibus. Integer ac pellentesque praesent. Donec accumsan interdum nisi, quis tincidunt felis sagittis eget. tempus euismod. Vestibulum ante ipsum primis in faucibus vestibulum. Blandit adipiscing eu felis iaculis volutpat ac adipiscing accumsan eu faucibus. Integer ac pellentesque praesent tincidunt felis sagittis eget. tempus euismod. Vestibulum ante ipsum primis in faucibus vestibulum. Blandit adipiscing eu felis iaculis volutpat ac adipiscing accumsan eu faucibus. Integer ac pellentesque praesent. Blandit adipiscing eu felis iaculis volutpat ac adipiscing accumsan eu faucibus. Integer ac pellentesque praesent tincidunt felis sagittis eget. tempus euismod. Vestibulum ante ipsum primis in faucibus vestibulum. Blandit adipiscing eu felis iaculis volutpat ac adipiscing accumsan eu faucibus. Integer ac pellentesque praesent.

317 |

Fringilla nisl. Donec accumsan interdum nisi, quis tincidunt felis sagittis eget. tempus euismod. Vestibulum ante ipsum primis in faucibus vestibulum. Blandit adipiscing eu felis iaculis volutpat ac adipiscing accumsan eu faucibus. Integer ac pellentesque praesent tincidunt felis sagittis eget. tempus euismod. Vestibulum ante ipsum primis in faucibus vestibulum. Blandit adipiscing eu felis iaculis volutpat ac adipiscing accumsan eu faucibus. Integer ac pellentesque praesent. Donec accumsan interdum nisi, quis tincidunt felis sagittis eget. tempus euismod. Vestibulum ante ipsum primis in faucibus vestibulum. Blandit adipiscing eu felis iaculis volutpat ac adipiscing accumsan eu faucibus. Integer ac pellentesque praesent tincidunt felis sagittis eget. tempus euismod. Vestibulum ante ipsum primis in faucibus vestibulum. Blandit adipiscing eu felis iaculis volutpat ac adipiscing accumsan eu faucibus. Integer ac pellentesque praesent. Blandit adipiscing eu felis iaculis volutpat ac adipiscing accumsan eu faucibus. Integer ac pellentesque praesent tincidunt felis sagittis eget. tempus euismod. Vestibulum ante ipsum primis in faucibus vestibulum. Blandit adipiscing eu felis iaculis volutpat ac adipiscing accumsan eu faucibus. Integer ac pellentesque praesent.

318 |
319 | 320 | 321 | {% endblock content %} 322 | 323 | 324 | {% block javascripts %}{% endblock javascripts %} 325 | -------------------------------------------------------------------------------- /app/templates/pages/error-404.html: -------------------------------------------------------------------------------- 1 | {% extends "layouts/base-site.html" %} 2 | 3 | {% block title %} About {% endblock %} 4 | 5 | 6 | {% block stylesheets %}{% endblock stylesheets %} 7 | 8 | {% block content %} 9 | 10 |

Error 404

11 |

12 | Page not found - Home 13 |

14 | 15 | {% endblock content %} 16 | 17 | 18 | {% block javascripts %}{% endblock javascripts %} 19 | -------------------------------------------------------------------------------- /app/templates/pages/index.html: -------------------------------------------------------------------------------- 1 | {% extends "layouts/base-site.html" %} 2 | 3 | {% block title %} Home {% endblock %} 4 | 5 | 6 | {% block stylesheets %}{% endblock stylesheets %} 7 | 8 | {% block content %} 9 | 10 |
11 | 12 |

Flask Boilerplate

13 | 14 |

This is FlaskPlay, an open-source Flask Boilerplate 15 | designed by HTML5 UP and coded in Flask Microframework. 16 |

17 | 18 |

19 | {% if current_user.is_authenticated %} 20 | You are authenticated, Logout 21 | {% else %} 22 | You are NOT authenticated, please Login 23 | {% endif %} 24 |

25 | 26 |

27 | Flask is a Python web framework built with a small core and easy-to-extend philosophy. 28 | Flask is also easy to get started with as a beginner because there is little boilerplate code for getting a simple app up and running. 29 |

30 | 31 |

32 | Get the source code or 33 | read more about this Flask Boilerplate. 34 |

35 | 36 |
37 | 38 |
39 | 51 | 62 | 73 | 84 | 95 | 106 | 117 | 128 | 139 | 140 |
141 | 142 | {% endblock content %} 143 | 144 | 145 | {% block javascripts %}{% endblock javascripts %} 146 | -------------------------------------------------------------------------------- /app/templates/pages/login.html: -------------------------------------------------------------------------------- 1 | {% extends "layouts/base-site.html" %} 2 | 3 | {% block title %} Login {% endblock %} 4 | 5 | 6 | {% block stylesheets %}{% endblock stylesheets %} 7 | 8 | {% block content %} 9 | 10 |

Login

11 | 12 | 13 |
14 | 15 | {% if msg %} 16 |

{{ msg | safe }}

17 | {% else %} 18 |

Enter your credentials

19 | {% endif %} 20 | 21 |
22 | 23 | {{ form.hidden_tag() }} 24 | 25 |
26 |
27 | {{ form.username(placeholder="Username") }} 28 |
29 |
30 | {{ form.password(placeholder="Password") }} 31 |
32 | 33 |
34 |
    35 |
  • 36 | 37 |
  • 38 |
  • 39 | or Register 40 |
  • 41 |
42 |
43 |
44 |
45 |
46 | 47 | {% endblock content %} 48 | 49 | 50 | {% block javascripts %}{% endblock javascripts %} 51 | -------------------------------------------------------------------------------- /app/templates/pages/register.html: -------------------------------------------------------------------------------- 1 | {% extends "layouts/base-site.html" %} 2 | 3 | {% block title %} Register {% endblock %} 4 | 5 | 6 | {% block stylesheets %}{% endblock stylesheets %} 7 | 8 | {% block content %} 9 | 10 |

Register

11 | 12 | 13 |
14 | 15 | {% if msg %} 16 |

{{ msg | safe }}

17 | {% else %} 18 |

Enter your credentials

19 | {% endif %} 20 | 21 |
22 | 23 | {{ form.hidden_tag() }} 24 | 25 |
26 |
27 | {{ form.name(placeholder="Friendly Name") }} 28 |
29 |
30 | {{ form.username(placeholder="Username") }} 31 |
32 |
33 | {{ form.email(placeholder="eMail") }} 34 |
35 |
36 | {{ form.password(placeholder="Password") }} 37 |
38 | 39 |
40 |
    41 |
  • 42 | 43 |
  • 44 |
  • 45 | or Login 46 |
  • 47 |
48 |
49 |
50 |
51 |
52 | 53 | {% endblock content %} 54 | 55 | 56 | {% block javascripts %}{% endblock javascripts %} 57 | -------------------------------------------------------------------------------- /app/util.py: -------------------------------------------------------------------------------- 1 | # -*- encoding: utf-8 -*- 2 | """ 3 | License: MIT 4 | Copyright (c) 2019 - present AppSeed.us 5 | """ 6 | 7 | from flask import json, url_for, jsonify, render_template 8 | from jinja2 import TemplateNotFound 9 | from app import app 10 | 11 | from . models import User 12 | from app import app,db,bc,mail 13 | from . common import * 14 | from sqlalchemy import desc,or_ 15 | import hashlib 16 | from flask_mail import Message 17 | import re 18 | from flask import render_template 19 | 20 | import os, datetime, time, random 21 | 22 | # build a Json response 23 | def response( data ): 24 | return app.response_class( response=json.dumps(data), 25 | status=200, 26 | mimetype='application/json' ) 27 | 28 | def g_db_commit( ): 29 | 30 | db.session.commit( ); 31 | 32 | def g_db_add( obj ): 33 | 34 | if obj: 35 | db.session.add ( obj ) 36 | 37 | def g_db_del( obj ): 38 | 39 | if obj: 40 | db.session.delete ( obj ) 41 | -------------------------------------------------------------------------------- /app/views.py: -------------------------------------------------------------------------------- 1 | # -*- encoding: utf-8 -*- 2 | """ 3 | License: MIT 4 | Copyright (c) 2019 - present AppSeed.us 5 | """ 6 | 7 | # Python modules 8 | import os, logging 9 | 10 | # Flask modules 11 | from flask import render_template, request, url_for, redirect, send_from_directory 12 | from flask_login import login_user, logout_user, current_user, login_required 13 | from werkzeug.exceptions import HTTPException, NotFound, abort 14 | 15 | # App modules 16 | from app import app, lm, db, bc 17 | from app.models import User 18 | from app.forms import LoginForm, RegisterForm 19 | 20 | # provide login manager with load_user callback 21 | @lm.user_loader 22 | def load_user(user_id): 23 | return User.query.get(int(user_id)) 24 | 25 | # Logout user 26 | @app.route('/logout.html') 27 | def logout(): 28 | logout_user() 29 | return redirect(url_for('index')) 30 | 31 | # Register a new user 32 | @app.route('/register.html', methods=['GET', 'POST']) 33 | def register(): 34 | 35 | # declare the Registration Form 36 | form = RegisterForm(request.form) 37 | 38 | msg = None 39 | 40 | if request.method == 'GET': 41 | 42 | return render_template( 'pages/register.html', form=form, msg=msg ) 43 | 44 | # check if both http method is POST and form is valid on submit 45 | if form.validate_on_submit(): 46 | 47 | # assign form data to variables 48 | username = request.form.get('username', '', type=str) 49 | password = request.form.get('password', '', type=str) 50 | email = request.form.get('email' , '', type=str) 51 | 52 | # filter User out of database through username 53 | user = User.query.filter_by(user=username).first() 54 | 55 | # filter User out of database through username 56 | user_by_email = User.query.filter_by(email=email).first() 57 | 58 | if user or user_by_email: 59 | msg = 'Error: User exists!' 60 | 61 | else: 62 | 63 | pw_hash = password #bc.generate_password_hash(password) 64 | 65 | user = User(username, email, pw_hash) 66 | 67 | user.save() 68 | 69 | msg = 'User created, please login' 70 | 71 | else: 72 | msg = 'Input error' 73 | 74 | return render_template( 'pages/register.html', form=form, msg=msg ) 75 | 76 | # Authenticate user 77 | @app.route('/login.html', methods=['GET', 'POST']) 78 | def login(): 79 | 80 | # Declare the login form 81 | form = LoginForm(request.form) 82 | 83 | # Flask message injected into the page, in case of any errors 84 | msg = None 85 | 86 | # check if both http method is POST and form is valid on submit 87 | if form.validate_on_submit(): 88 | 89 | # assign form data to variables 90 | username = request.form.get('username', '', type=str) 91 | password = request.form.get('password', '', type=str) 92 | 93 | # filter User out of database through username 94 | user = User.query.filter_by(user=username).first() 95 | 96 | if user: 97 | 98 | #if bc.check_password_hash(user.password, password): 99 | if user.password == password: 100 | login_user(user) 101 | return redirect(url_for('index')) 102 | else: 103 | msg = "Wrong password. Please try again." 104 | else: 105 | msg = "Unknown user" 106 | 107 | return render_template( 'pages/login.html', form=form, msg=msg ) 108 | 109 | # App main route + generic routing 110 | @app.route('/', defaults={'path': 'index.html'}) 111 | @app.route('/') 112 | def index(path): 113 | 114 | if not current_user.is_authenticated: 115 | return redirect(url_for('login')) 116 | 117 | content = None 118 | 119 | #try: 120 | 121 | # try to match the pages defined in -> pages/ 122 | return render_template( 'pages/'+path ) 123 | 124 | #except: 125 | # 126 | # return render_template( 'pages/error-404.html' ) 127 | 128 | # Return sitemap 129 | @app.route('/sitemap.xml') 130 | def sitemap(): 131 | return send_from_directory(os.path.join(app.root_path, 'static'), 'sitemap.xml') 132 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "flask-boilerplate-flaskplay", 3 | "version": "1.0.0", 4 | "description": "An open-source Flask boilerplate suitable for simple applications.", 5 | "main": "gruntfile.js", 6 | "devDependencies": { 7 | "rimraf": "^2.5.2" 8 | }, 9 | "engines": { 10 | "node": ">=0.10.1" 11 | }, 12 | "scripts": { 13 | "start" : "flask run", 14 | "shell" : "flask shell" 15 | }, 16 | "repository": { 17 | "type": "git", 18 | "url": "https://github.com/app-generator/flask-boilerplate-flaskplay" 19 | }, 20 | "bugs": { 21 | "url": "https://github.com/app-generator/flask-boilerplate-flaskplay/issues", 22 | "email": "support@appseed.us" 23 | }, 24 | "author": "AppSeed.us (https://appseed.us)", 25 | "license": "MIT", 26 | "dependencies": { 27 | "ftp-deploy": "^2.3.3" 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /requirements.txt: -------------------------------------------------------------------------------- 1 | flask 2 | flask_login 3 | flask_migrate 4 | flask_wtf 5 | flask_sqlalchemy 6 | flask_bcrypt 7 | gunicorn 8 | -------------------------------------------------------------------------------- /run.py: -------------------------------------------------------------------------------- 1 | # -*- encoding: utf-8 -*- 2 | """ 3 | License: MIT 4 | Copyright (c) 2019 - present AppSeed.us 5 | """ 6 | 7 | from app import app, db 8 | -------------------------------------------------------------------------------- /screenshots/flask-boilerplate-elements.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/app-generator/flask-boilerplate-flaskplay/62b50bb52b63b307410fac5dbd38c81705bd9d31/screenshots/flask-boilerplate-elements.jpg -------------------------------------------------------------------------------- /screenshots/flask-boilerplate-generic-page.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/app-generator/flask-boilerplate-flaskplay/62b50bb52b63b307410fac5dbd38c81705bd9d31/screenshots/flask-boilerplate-generic-page.jpg -------------------------------------------------------------------------------- /screenshots/flask-boilerplate-intro.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/app-generator/flask-boilerplate-flaskplay/62b50bb52b63b307410fac5dbd38c81705bd9d31/screenshots/flask-boilerplate-intro.gif -------------------------------------------------------------------------------- /screenshots/flask-boilerplate-login.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/app-generator/flask-boilerplate-flaskplay/62b50bb52b63b307410fac5dbd38c81705bd9d31/screenshots/flask-boilerplate-login.jpg -------------------------------------------------------------------------------- /screenshots/flask-boilerplate-main.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/app-generator/flask-boilerplate-flaskplay/62b50bb52b63b307410fac5dbd38c81705bd9d31/screenshots/flask-boilerplate-main.jpg -------------------------------------------------------------------------------- /screenshots/flask-boilerplate-menu.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/app-generator/flask-boilerplate-flaskplay/62b50bb52b63b307410fac5dbd38c81705bd9d31/screenshots/flask-boilerplate-menu.jpg --------------------------------------------------------------------------------