├── .gitignore ├── .travis.yml ├── LICENSE ├── __init__.py ├── blowdrycss_settings.py ├── deployed └── static │ └── favicon.ico ├── development.md ├── example_secrets.py ├── gzipper.py ├── readme.md ├── requirements.txt ├── setup.py ├── static ├── css │ ├── combined.css │ └── processed │ │ ├── combined.min.css │ │ └── combined.min.css.gz ├── favicon.ico ├── free-zipcode-database-Primary.csv ├── js │ └── vendor │ │ ├── dev.js │ │ ├── firebase.js │ │ ├── foundation.js │ │ ├── jquery.js │ │ ├── production.js │ │ ├── production.js.gz │ │ ├── vue.js │ │ ├── vue.min.js │ │ ├── vuefire.js │ │ └── what-input.js ├── keyed-zipcode-lat-long-json.json ├── scss │ ├── _blowdry.scss │ ├── _foundation.scss │ ├── _main.scss │ ├── _materialicons.scss │ └── combined.scss └── zipcode-lat-lon-only.csv ├── templates └── index.html ├── tox.ini ├── tox_requirements.txt ├── travis_requirements.txt ├── unit_tests ├── __init__.py ├── test_deployed_site.py └── test_vue_flask.py ├── vue_flask.py └── zappa_settings.json /.gitignore: -------------------------------------------------------------------------------- 1 | ########## 2 | # PYTHON # 3 | ########## 4 | 5 | # Secret file 6 | test_secrets.py 7 | secrets.py 8 | 9 | # Zappa Zip File 10 | *.zip 11 | 12 | # Byte-compiled / optimized / DLL files 13 | __pycache__/ 14 | *.py[cod] 15 | *$py.class 16 | 17 | # C extensions 18 | *.so 19 | 20 | # Distribution / packaging 21 | .Python 22 | 23 | env/ 24 | archive/ 25 | *archive/ 26 | archive/0.2.9/build/ 27 | develop-eggs/ 28 | archive/0.2.9/dist/ 29 | archive/0.3.0/build/ 30 | archive/0.3.0/dist/ 31 | downloads/ 32 | eggs/ 33 | .eggs/ 34 | lib/ 35 | lib64/ 36 | parts/ 37 | sdist/ 38 | var/ 39 | *.egg-info/ 40 | .installed.cfg 41 | *.egg 42 | *.xml 43 | *.pypirc 44 | 45 | # PyInstaller 46 | # Usually these files are written by a python script from a template 47 | # before PyInstaller builds the exe, so as to inject date/other infos into it. 48 | *.manifest 49 | *.spec 50 | 51 | # Installer logs 52 | pip-log.txt 53 | pip-delete-this-directory.txt 54 | 55 | # Unit test / coverage reports 56 | htmlcov/ 57 | .tox/ 58 | .coverage 59 | .coverage.* 60 | .cache 61 | nosetests.xml 62 | coverage.xml 63 | *,cover 64 | 65 | # Translations 66 | *.mo 67 | *.pot 68 | 69 | # Django stuff: 70 | *.log 71 | 72 | # Sphinx documentation 73 | docs/_build/ 74 | 75 | # PyBuilder 76 | target/ 77 | 78 | ####### 79 | # CSS # 80 | ####### 81 | blowdry.css 82 | blowdry.min.css 83 | 84 | ########### 85 | # NODE.JS # 86 | ########### 87 | 88 | # Logs 89 | logs 90 | *.log 91 | 92 | # Runtime data 93 | pids 94 | *.pid 95 | *.seed 96 | 97 | # Directory for instrumented libs generated by jscoverage/JSCover 98 | lib-cov 99 | 100 | # Coverage directory used by tools like istanbul 101 | coverage 102 | 103 | # Grunt intermediate storage (http://gruntjs.com/creating-plugins#storing-task-files) 104 | .grunt 105 | 106 | # node-waf configuration 107 | .lock-wscript 108 | 109 | # Compiled binary addons (http://nodejs.org/api/addons.html) 110 | build_archive/0.0.8/build/Release 111 | 112 | # Dependency directory 113 | # https://www.npmjs.org/doc/misc/npm-faq.html#should-i-check-my-node_modules-folder-into-git 114 | node_modules -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | #sudo: required 2 | 3 | language: python 4 | 5 | #os: 6 | # - linux 7 | # - osx 8 | 9 | python: 10 | - "2.7" 11 | - "3.6" 12 | 13 | # command to install dependencies 14 | install: 15 | - pip --version 16 | - pip install -r travis_requirements.txt 17 | 18 | # command to run tests 19 | script: 20 | - tox -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2017 Chad Nelson 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nueverest/vue_flask/48ffccb2b028ab3bceb70640aa575d63c79a21d8/__init__.py -------------------------------------------------------------------------------- /blowdrycss_settings.py: -------------------------------------------------------------------------------- 1 | """ 2 | **Usage Notes:** 3 | 4 | The first time ``blowdrycss`` is run it auto-builds ``blowdrycss_settings.py`` via ``__init__.py``. 5 | This makes it easy to find and customize related settings. 6 | 7 | **Why such a long name? -- blowdrycss_settings.py** 8 | 9 | Popular web frameworks such as django and flask already auto-generate a settings file called ``settings.py``. 10 | The longer more specific name is used to prevent naming conflicts, and increase clarity. 11 | 12 | **Parameters:** 13 | 14 | | markdown_directory (*string*) -- Generally used for development purposes and github documentation. 15 | 16 | | project_directory (*string*) -- Path to recursively search for all defined ``file_types``. 17 | 18 | | css_directory (*string*) -- Path where the projects CSS files are located. 19 | 20 | | docs_directory (*string*) -- Path where Sphinx docs are located (requires sphinx to be installed and run). 21 | 22 | | file_types = (*tuple of strings*) -- All file types/extensions to search for in the defined project_directory 23 | that contain encoded class selectors. 24 | 25 | | timing_enabled (*bool*) -- Run performance timer to see the performance of ``blowdrycss``. 26 | 27 | | markdown_docs (*bool*) -- Generate a markdown files that provides a quick syntax and clashing alias reference. 28 | Normally set to False except when posting to github. 29 | 30 | | html_docs (*bool*) -- Generate a html file that provides a quick syntax and clashing alias reference. 31 | 32 | | rst_docs (*bool*) -- Generate a sphinx rst file that provides a quick syntax and clashing alias reference. 33 | 34 | | human_readable (*bool*) -- Generate a standard human readable css file. This file is named ``blowdry.css`` by 35 | default. 36 | 37 | | minify (*bool*) -- Generate a minified version of the css file. This file is named ``blowdry.min.css`` by default. 38 | 39 | | media_queries_enabled (*bool*) -- Generate breakpoint and scaling media queries. 40 | 41 | | use_em (*bool*) -- A ``pixels`` to ``em`` unit conversion flag. True enables unit conversion. 42 | False disables unit conversions meaning any pixel value remains unchanged. 43 | 44 | | base (*int*) -- Base used for unit conversion (typically set to 16). The pixel value will be divided by 45 | ``base`` during unit conversion. 46 | 47 | | xxsmall (*tuple of floats*) -- (0px, upper limit in pixels) 48 | 49 | | xsmall (*tuple of floats*) -- (xxsmall upper limit + 1px, upper limit in pixels) 50 | 51 | | small (*tuple of floats*) -- (xsmall upper limit + 1px, upper limit in pixels) 52 | 53 | | medium (*tuple of floats*) -- (small upper limit + 1px, upper limit in pixels) 54 | 55 | | large (*tuple of floats*) -- (medium upper limit + 1px, upper limit in pixels) 56 | 57 | | xlarge (*tuple of floats*) -- (large upper limit + 1px, upper limit in pixels) 58 | 59 | | xxlarge (*tuple of floats*) -- (xlarge upper limit + 1px, upper limit in pixels) 60 | 61 | | giant (*tuple of floats*) -- (xxlarge upper limit + 1px, upper limit in pixels) 62 | 63 | | xgiant (*tuple of floats*) -- (giant upper limit + 1px, upper limit in pixels) 64 | 65 | | xxgiant (*tuple of floats*) -- (xgiant upper limit + 1px, 1E+6) [Technically the upper limit is infinity, 66 | but CSS does not permit it.] 67 | 68 | **Custom Alias Syntax:** 69 | 70 | | custom_property_alias_dict (*dict*) -- Contains customized shorthand encodings for a CSS property name. 71 | e.g. ``'c-'`` is an alias for ``'color'``. This saves on typing. 72 | 73 | | These encoded class selectors can be used inside of Web project files matching ``file_type``. 74 | They can be customized to your liking. 75 | 76 | | For more details about how to create custom aliases head on over to :doc:`advancedtopics`. 77 | 78 | **cssutils Patch:** 79 | 80 | ``cssutils`` does not currently support all CSS 3 Units. The patch in this file allows length units of 81 | ``q``, ``ch``, ``rem``, ``vw``, ``vh``, ``vmin``, and ``vmax``. It also allows angle units of ``turn``. 82 | 83 | """ 84 | 85 | # python 2 86 | from __future__ import absolute_import, division, unicode_literals 87 | from builtins import round 88 | 89 | # builtins 90 | from os import getcwd, path 91 | from string import digits 92 | from logging import DEBUG, INFO, WARNING, ERROR, CRITICAL 93 | 94 | # plugins 95 | from cssutils import profile 96 | 97 | __project__ = 'blowdrycss' 98 | 99 | 100 | # Set project_directory to the one containing the files you want to DRY out. 101 | # Change these to whatever you want. 102 | cwd = getcwd() 103 | markdown_directory = path.join(cwd, 'docs', 'markdown') 104 | project_directory = path.join(cwd, 'templates') 105 | css_directory = path.join(cwd, 'static', 'scss') 106 | docs_directory = path.join(cwd, 'docs') 107 | 108 | # Output File 109 | output_file_name = '_blowdry' 110 | output_extension = '.scss' 111 | 112 | # Logging 113 | logging_enabled = False 114 | logging_level = DEBUG # Allowed: DEBUG, INFO, WARNING, ERROR, CRITICAL 115 | log_to_console = False 116 | log_to_file = False 117 | log_directory = path.join(cwd, 'log') 118 | log_file_name = 'blowdrycss.log' 119 | one_mega_byte = 1048576 120 | log_file_size = 4 * one_mega_byte # Max log file size 121 | log_backup_count = 1 # Maximum number of backup log files. 122 | 123 | # All file types/extensions to search for in the defined project_directory that contain encoded class selectors. 124 | # Available formats: 125 | # ('*.html', '*.js', '*.ts', '*.vue', '*.jinja', '*.jinja2', '*.jnj', '*.ja', '*.djt', '*.djhtml', 126 | # '*.cs', '*.aspx', '*.ascx', '*.master', '*.erb', '*.php', ) 127 | file_types = ('*.html', '*.js', '*.vue') 128 | 129 | # Timing 130 | time_limit = 1800 # Frequency of a comprehensive run in seconds. See timing.LimitTimer() for details. 131 | 132 | # Boolean Flags 133 | auto_generate = True # Auto-generate blowdry.css when a file that matches files_types is saved. (Watchdog) 134 | hide_css_errors = True # Hide errors and warnings generated by cssutils. 135 | timing_enabled = True # Run performance timer 136 | markdown_docs = False # Generate a markdown files that provides a quick syntax and clashing alias reference. 137 | html_docs = False # Generate a html file that provides a quick syntax and clashing alias reference. 138 | rst_docs = False # Generate a sphinx rst file that provides a quick syntax and clashing alias reference. 139 | human_readable = True # Generate a standard human readable css file. 140 | minify = False # Generate a minified version of the css file. 141 | media_queries_enabled = True # Generate breakpoint and scaling media queries. 142 | 143 | # ...Not Implemented Yet... 144 | # use_hex = True # Using hex and browser performance: http://jsperf.com/css-color-names-vs-hex-codes/18 145 | # extra_dry = False # Combine identical CSS discovered under different class selector names. 146 | # http_server = False # Auto-Start a simple webserver on localhost:8080. 147 | # public_url = False # Uses ngrok to generate a temporary public url for testings and demo purposes. 148 | # condense_classes = False # Edits HTML Files after discovering common patterns (Not DRY do not implement). 149 | 150 | # Unit Conversion Defaults 151 | use_em = True 152 | base = 16 153 | 154 | 155 | def px_to_em(pixels): 156 | """ Convert a numeric value from px to em using ``settings.base`` as the unit conversion factor. 157 | 158 | **Rules:** 159 | 160 | - ``pixels`` shall only contain [0-9.-]. 161 | - Inputs that contain any other value are simply passed through unchanged. 162 | - Default ``base`` is 16 meaning ``16px = 1rem`` 163 | 164 | **Note:** Does not check the ``property_name`` or ``use_em`` values. Rather, it blindly converts 165 | whatever input is provided. The calling method is expected to know what it is doing. 166 | 167 | Rounds float to a maximum of 4 decimal places. 168 | 169 | :type pixels: str, int, float 170 | :param pixels: A numeric value with the units stripped. 171 | :return: (str) 172 | 173 | - If the input is convertible return the converted number as a string with the units ``em`` 174 | appended to the end. 175 | - If the input is not convertible return the unprocessed input. 176 | 177 | >>> from blowdrycss_settings import px_to_em 178 | >>> # settings.use_em = True 179 | >>> px_to_em(pixels='-16.0') 180 | -1em 181 | >>> # settings.use_em = False 182 | >>> px_to_em(pixels='42px') 183 | 42px 184 | >>> # Invalid input passes through. 185 | >>> px_to_em(pixels='invalid') 186 | invalid 187 | 188 | """ 189 | if set(str(pixels)) <= set(digits + '-.'): 190 | em = float(pixels) / float(base) 191 | em = round(em, 4) 192 | em = str(em) + 'rem' # Add 'rem'. OVERRIDE em -> rem 193 | return em 194 | return pixels 195 | 196 | 197 | # Default Screen Breakpoints / Transition Triggers 198 | # Tuple Format (Lower Limit, Upper Limit) in pixels. 199 | # Note: These values change if unit conversion is enabled i.e. ``use_em`` is ``True``. 200 | # Common Screen Resolutions: https://en.wikipedia.org/wiki/List_of_common_resolutions 201 | xxsmall = (px_to_em(0), px_to_em(120)) # 0.0 - 7.5em 202 | xsmall = (px_to_em(121), px_to_em(240)) # 7.5625 - 15.0em 203 | small = (px_to_em(241), px_to_em(480)) # 15.0625 - 30.0em 204 | medium = (px_to_em(481), px_to_em(720)) # 30.0625 - 45.0em # Typical mobile device break point @ 720px. 205 | large = (px_to_em(721), px_to_em(1024)) # 45.0625 - 64.0em 206 | xlarge = (px_to_em(1025), px_to_em(1366)) # 64.0625 - 85.375em 207 | xxlarge = (px_to_em(1367), px_to_em(1920)) # 85.4375 - 120.0em 208 | giant = (px_to_em(1921), px_to_em(2560)) # 120.0625 - 160.0em 209 | xgiant = (px_to_em(2561), px_to_em(2800)) # 160.0625 - 175.0em 210 | xxgiant = (px_to_em(2801), px_to_em(10**6)) # 175.0625 - float('inf')) # Python 2.x representation of Infinity. 211 | 212 | # Custom CSS Property Syntax 213 | custom_property_alias_dict = { 214 | 'background': {'bg-', }, 215 | 'background-color': {'bgc-', 'bg-c-', 'bg-color-', }, 216 | 'color': {'c-', }, 217 | 'font-size': {'fsize-', 'f-size-', }, 218 | 'font-weight': {'fweight-', 'f-weight-', }, 219 | 'height': {'h-', }, 220 | 'margin': {'m-', }, 221 | 'margin-top': {'m-top-', }, 222 | 'margin-bottom': {'m-bot-', }, 223 | 'padding': {'p-', 'pad-', }, 224 | 'padding-top': {'p-top-', }, 225 | 'position': {'pos-', }, 226 | 'text-align': {'talign-', 't-align-', }, 227 | 'vertical-align': {'valign-', 'v-align-', }, 228 | 'width': {'w-', }, 229 | } 230 | 231 | # Patches cssutils - Generally this does not need to be edited. 232 | profile._MACROS['length'] = r'0|{num}(em|ex|px|in|cm|mm|pt|pc|q|ch|rem|vw|vh|vmin|vmax)' 233 | profile._MACROS['positivelength'] = r'0|{positivenum}(em|ex|px|in|cm|mm|pt|pc|q|ch|rem|vw|vh|vmin|vmax)' 234 | profile._MACROS['angle'] = r'0|{num}(deg|grad|rad|turn)' 235 | profile._resetProperties() 236 | 237 | -------------------------------------------------------------------------------- /deployed/static/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nueverest/vue_flask/48ffccb2b028ab3bceb70640aa575d63c79a21d8/deployed/static/favicon.ico -------------------------------------------------------------------------------- /development.md: -------------------------------------------------------------------------------- 1 | # Helpful notes about deployment. 2 | - Combine, minify, and gzip CSS files. 3 | - Combine, minify, and gzip JS files. 4 | - You can use `gzip -9 filename.css`. The `-9` provides the best compression. 5 | - Zopfli https://github.com/obp/zopfli developed by Google is more powerful and may one day be integrated with this project. 6 | - Upload these two files to S3 and make the files public http://www.rightbrainnetworks.com/blog/serving-compressed-gzipped-static-files-from-amazon-s3-or-cloudfront/ 7 | - AWS Cloudfront is a CDN that can be linked to your S3 bucket. A Content Delivery Network (CDN) places the data closer to the end user by storing your files all over the world. 8 | - Enable file Caching so that browsers do not need to download the files every time the page loads. https://developers.google.com/web/fundamentals/performance/optimizing-content-efficiency/http-caching#defining-optimal-cache-control-policy 9 | - Check your site speed with pingdom or other service https://tools.pingdom.com/#!/c1UXFm/https://kw4udfbos9.execute-api.us-west-2.amazonaws.com/production 10 | 11 | # Development 12 | 13 | # How to Setup blowdrycss (optional tool for rapidly styling the frontend) 14 | - Activate project virtual environment 15 | - `pip install blowdrycss` 16 | - Navigate to project folder. 17 | - Run `blowdrycss` 18 | - Open the newly created blowdrycss_settings.py file. 19 | - Change the following variable to match these values: 20 | - project_directory = path.join(cwd, 'templates') 21 | - css_directory = path.join(cwd, 'static', 'scss') 22 | - output_file_name = '_blowdry' 23 | - output_extension = '.scss' 24 | - auto_generate = True 25 | - human_readable = True 26 | - minify = False 27 | - Go to the px_to_em() function 28 | - Find this line `em = str(em) + 'em'` 29 | - Change to this `em = str(em) + 'rem'` 30 | 31 | # Prerequisite for npm powered automation 32 | - Install node package manager (npm) on your machine. 33 | - Place npm on the system path. 34 | 35 | # Setup Sass and SCSS Transpiler 36 | - Download and Install Ruby http://www.ruby-lang.org/en/downloads/ 37 | - Setup system path for Ruby 38 | - At command prompt enter `gem install sass` to install sass. 39 | - If you are using an IDE like PyCharm you can setup a File Watcher https://www.jetbrains.com/help/pycharm/2016.3/transpiling-sass-less-and-scss-to-css.html#intro 40 | - Configuring File Watcher output path http://stackoverflow.com/a/36038914/1783439 41 | 42 | # Combine Javascript files into one and minify with uglifyjs 43 | - Open command line as Administrator. 44 | - Run `npm install -g uglify-js` the `-g` install uglify-js globally. 45 | - Run `npm list -g --depth=0` to confirm installation `--depth=0` prevents all of the dependencies from being listed. 46 | - You can manually run `uglifyjs --compress --mangle --output combined.min.js -- {filename1}.js {filename2}.js {filename3}.js` reference: http://www.aip.im/2015/02/how-to-minify-and-merge-javascript-files-with-uglifyjs-2/ 47 | - Recommended: Setup a custom File Watcher for PyCharm or your IDE. https://www.jetbrains.com/help/pycharm/2016.3/minifying-javascript.html 48 | - Configuring File Watcher output path http://stackoverflow.com/a/36038914/1783439 49 | - Set this up twice. One for combined.dev.js (vue.js debugging included) and one for combined.js (vue.min.js debugging removed) 50 | 51 | # Setup auto-minify CSS using node.js yuicompress 52 | - Open command line as Administrator. 53 | - Run `npm install -g yuicompressor` 54 | - Run `npm list -g --depth=0` to confirm installation. 55 | - Setup a File Watcher for YUI CSS https://www.jetbrains.com/help/pycharm/2016.3/minifying-css.html 56 | - Configuring File Watcher output path http://stackoverflow.com/a/36038914/1783439 57 | 58 | # Setup node-zopfli to compress combined CSS and JS files for Production 59 | - Zopfli Compresses gzip files 5% better than gzip. To achieve the 5% it takes 100x longer than gzip. That is fine for us since we are not doing on-the-fly or just-in-time compression. https://www.npmjs.com/package/node-zopfli 60 | - Requires Python 2.7 and either GCC (Unix) or Visual Studio Express (Windows) 61 | - To install `gcc` on Windows via MinGW use this https://yichaoou.github.io/tutorials/software/2016/06/28/git-bash-install-gcc 62 | - Open command line as Administrator. 63 | - Run `npm install -g node-zopfli` 64 | - Run `npm list -g --depth=0` to confirm installation. 65 | - Navigate to /npm/node_modules/node-zopfli/zopfli on your machine. 66 | - Run `gcc src/zopfli/*.c -O2 -W -Wall -Wextra -Wno-unused-function -ansi -pedantic -lm -o zopfli` 67 | - On ubuntu you can use `apt-get install zopfli` 68 | - An executable file named `zopfli` (linux) or `zopfli.exe` (windows) should now appear in your folder. 69 | - Setup two Custom File Watchers with zopfli for CSS and JS. Use combined.min.css and combined.js (production) 70 | - Configuring File Watcher output path http://stackoverflow.com/a/36038914/1783439 71 | 72 | # Testing with Flask-Testing, Selenium, Google chromedriver, Mozilla geckodriver 73 | - `pip install selenium` 74 | - Google chromedriver https://sites.google.com/a/chromium.org/chromedriver/downloads 75 | - Mozilla geckodriver https://github.com/mozilla/geckodriver/releases 76 | - Microsoft edge webdriver https://developer.microsoft.com/en-us/microsoft-edge/tools/webdriver/ 77 | - Microsoft Internet Explorer http://selenium-release.storage.googleapis.com/index.html 78 | - Mac Safari webdriver https://webkit.org/blog/6900/webdriver-support-in-safari-10/ 79 | -------------------------------------------------------------------------------- /example_secrets.py: -------------------------------------------------------------------------------- 1 | import uuid 2 | 3 | 4 | def is_production(): 5 | """ Determines if app is running on the production server via uuid comparison. 6 | 7 | HOW TO USE: 8 | Open a terminal 9 | > python 10 | > import uuid 11 | > uuid.getnode() 12 | 12345678987654321 <-- Copy whatever is returned and replace 111111111 with this. 13 | 14 | Ensure .gitignore excludes ``secrets.py``. 15 | Finally, rename this file to ``secrets.py``. 16 | 17 | Compare uuid for the machine against the known uuid(s) of your development machine(s). 18 | :return: (bool) True if code is running on the production server, and False otherwise. 19 | """ 20 | developer_machines = [111111111, ] 21 | return uuid.getnode() not in developer_machines -------------------------------------------------------------------------------- /gzipper.py: -------------------------------------------------------------------------------- 1 | # python 2 2 | from __future__ import absolute_import, print_function, unicode_literals 3 | 4 | # builtins 5 | from io import StringIO 6 | import gzip 7 | 8 | # plugins 9 | from flask import after_this_request, request 10 | import functools 11 | 12 | 13 | def gzipped(f): 14 | """" Custom view decorator that gzips the view if the server is capable. 15 | 16 | Background: AWS serverless architecture places the responsibility of gzipping a flask view on the application. 17 | Normally, the web server (nginx, apache, iis) would handle this task. 18 | Source: http://flask.pocoo.org/snippets/122/ 19 | """ 20 | @functools.wraps(f) 21 | def view_func(*args, **kwargs): 22 | @after_this_request 23 | def zipper(response): 24 | accept_encoding = request.headers.get('Accept-Encoding', '') 25 | 26 | if 'gzip' not in accept_encoding.lower(): 27 | return response 28 | 29 | response.direct_passthrough = False 30 | 31 | if 200 > response.status_code >= 300 or 'Content-Encoding' in response.headers: 32 | return response 33 | 34 | gzip_buffer = StringIO() 35 | gzip_file = gzip.GzipFile(mode='wb', fileobj=gzip_buffer) 36 | gzip_file.write(response.data) 37 | gzip_file.close() 38 | 39 | response.data = gzip_buffer.getvalue() 40 | response.headers['Content-Encoding'] = 'gzip' 41 | response.headers['Vary'] = 'Accept-Encoding' 42 | response.headers['Content-Length'] = len(response.data) 43 | response.headers['Cache-Control'] = 'max-age=0' 44 | 45 | return response 46 | 47 | return f(*args, **kwargs) 48 | 49 | return view_func 50 | -------------------------------------------------------------------------------- /readme.md: -------------------------------------------------------------------------------- 1 | # vue_flask 2 | Responsive AWS Serverless SPA using Vue.js, Flask, Firebase, and more. 3 | 4 | # Live Serverless Demo 5 | https://kw4udfbos9.execute-api.us-west-2.amazonaws.com/production 6 | 7 | # Benefits of Serverless 8 | Serverless operations do use a server, but it only runs 20-60 milliseconds at a time. 9 | - High Performance 10 | - High Availability 11 | - High Scalability 12 | - No Server and associated infrastructure to understand and maintain. 13 | - Easy undeployment 14 | 15 | # Disadvantage of Serverless 16 | - Deployment and Update actions require the entire project to be uploaded. Even a tiny change such as adding one 17 | character to a file requires the full project to be uploaded. Projects take minutes to upload. 18 | 19 | # Technologies Used 20 | - Vue.js - Frontend Javascript Framework https://vuejs.org/ 21 | - Vuefire - Firebase Anonymous Authentication https://github.com/chrisbraddock/vuefire-auth-demo 22 | - Flask - Python Web Framework http://flask.pocoo.org/ 23 | - Flask-S3 - Serve Static Media from AWS S3. https://flask-s3.readthedocs.io/en/latest/ 24 | - Firebase - Realtime Persistent Datastore https://firebase.google.com/ 25 | - Zurb Foundation 6 - CSS Frontend Framework http://foundation.zurb.com/ 26 | - Blowdrycss - Python Atomic CSS compiler https://github.com/nueverest/blowdrycss 27 | - Google Material Icons - Icons that behave like fonts https://google.github.io/material-design-icons/ 28 | - SCSS - Sassy CSS a superset of CSS3's syntax. 29 | - Zappa - Python serverless deployment interface https://github.com/Miserlou/Zappa 30 | - Amazon Web Services (AWS) - Cloud Infrastructure - S3, Lambda, API Gateway, CloudWatch, IAM https://aws.amazon.com/ 31 | 32 | # Requirements 33 | - Python 2.7.x or Python 3.6+ 34 | - See requirements.txt 35 | 36 | # How to Setup Firebase 37 | - Setup a firebase account. 38 | - In firebase, click "Create New Project". 39 | - Open the project. 40 | - On the Overview page for your project click "Add Firebase to your web app". 41 | - Use this data to replace apiKey, authDomain, databaseURL, storageBucket, and messagingSenderId in `templates/index.html`. 42 | 43 | # Change Flask-S3 Config in vue_flask.py 44 | - app.config['FLASKS3_BUCKET_NAME'] = 'your_s3_bucket_name_here' 45 | 46 | # How to Deploy using Zappa - Serverless. 47 | - Create and Activate a virtual environment for this project. Reference: https://virtualenv.pypa.io/en/stable/installation/ 48 | - `pip install requirements.txt` 49 | - Place aws credentials with correct permissions in `\.aws\credentials` Reference: https://github.com/Miserlou/Zappa/issues/244 50 | - Open zappa_settings.json and change "s3_bucket" and "aws_region" to match your AWS account. 51 | - `zappa deploy production` 52 | - Navigate to generated url. 53 | 54 | # How to Update Deployment 55 | - `zappa update production` 56 | 57 | # How to Undeploy 58 | - `zappa undeploy production` 59 | -------------------------------------------------------------------------------- /requirements.txt: -------------------------------------------------------------------------------- 1 | blowdrycss>=1.0.2 2 | boto3>=1.4.4 3 | botocore>=1.5.18 4 | coverage>=4.3.4 5 | Flask>=0.12 6 | Flask-S3==0.3.3 7 | future>=0.16.0 8 | futures>=3.0.5 9 | requests>=2.13.0 10 | selenium>=3.0.2 11 | yuicompressor>=2.4.8 12 | zappa>=0.37.1 13 | -------------------------------------------------------------------------------- /setup.py: -------------------------------------------------------------------------------- 1 | # python 2.7 2 | from __future__ import absolute_import, unicode_literals 3 | from io import open 4 | 5 | # builtins 6 | from setuptools import setup, find_packages # Always prefer setuptools over distutils 7 | 8 | __author__ = 'chad nelson' 9 | __project__ = 'vue_flask' 10 | __version__ = '0.0.2' 11 | 12 | # Get readme.rst from sphinx docs (if available). 13 | try: 14 | with open('readme.rst', encoding='utf-8') as f: 15 | long_description = f.read() 16 | except (IOError, ImportError): 17 | # default description 18 | long_description = 'Demonstration of how to integrate many different frontend and backend technologies, and ' + \ 19 | 'rapidly deploy the app serverlessly via Zappa.' 20 | 21 | setup( 22 | name='vue_flask', 23 | 24 | # Versions should comply with PEP440. For a discussion on single-sourcing 25 | # the version across setup.py and the project code, see 26 | # https://packaging.python.org/en/latest/single_source_version.html 27 | version=__version__, 28 | 29 | description='VueJS, Flask, Zappa serverless deployment demo.', 30 | long_description=long_description, 31 | 32 | # The project's main homepage. 33 | url='', 34 | download_url='https://github.com/nueverest/vue_flask/', 35 | 36 | # Author details 37 | author=__author__, 38 | author_email='nu.everest@gmail.com', 39 | 40 | # License 41 | license='MIT', 42 | 43 | # Classifier Reference: https://pypi.python.org/pypi?%3Aaction=list_classifiers 44 | classifiers=[ 45 | 'Development Status :: 4 - Beta', # 3 - Alpha, 4 - Beta, 5 - Production/Stable 46 | 'Intended Audience :: Developers', 47 | 'Intended Audience :: Information Technology', 48 | 'Natural Language :: English', 49 | 'Operating System :: OS Independent', 50 | 51 | # Topics 52 | 'Topic :: Internet', 53 | 'Topic :: Internet :: WWW/HTTP', 54 | 'Topic :: Internet :: WWW/HTTP :: Dynamic Content', 55 | 'Topic :: Internet :: WWW/HTTP :: Site Management', 56 | 'Topic :: Software Development', 57 | 58 | # License 59 | 'License :: OSI Approved :: MIT License', 60 | 61 | # Python version support. 62 | 'Programming Language :: Python :: 2', 63 | 'Programming Language :: Python :: 2.7', 64 | 'Programming Language :: Python :: 3', 65 | 'Programming Language :: Python :: 3.3', 66 | 'Programming Language :: Python :: 3.4', 67 | 'Programming Language :: Python :: 3.5', 68 | 'Programming Language :: Python :: 3.6', 69 | ], 70 | 71 | # What does your project relate to? 72 | keywords=( 73 | 'vue_flask vuejs flask zappa scss firebase foundation6 tutorial example blowdrycss' 74 | ), 75 | 76 | # Packages - reference: https://pythonhosted.org/setuptools/setuptools.html#using-find-packages 77 | #package_dir={'': 'vue_flask'}, 78 | #packages=find_packages('vue_flask', exclude=['*.settings', '*.settings.*', 'settings.*', 'settings']), 79 | #packages=find_packages(exclude=['*.settings']), # THIS ONE WORKED BUT IS NO LONGER NEEDED 80 | packages=find_packages(), 81 | 82 | # Alternatively, if you want to distribute just a my_module.py, uncomment 83 | # this: 84 | # py_modules=["my_module"], 85 | 86 | # List run-time dependencies here. These will be installed by pip when 87 | # your project is installed. For an analysis of "install_requires" vs pip's 88 | # requirements files see: 89 | # https://packaging.python.org/en/latest/requirements.html 90 | install_requires=['Flask>=0.12', 'Flask-S3==0.3.3', 'future>=0.16.0'], 91 | 92 | # List additional groups of dependencies here (e.g. development 93 | # dependencies). You can install these using the following syntax, 94 | # for example: 95 | # $ pip install vue_flask -e .[testing, development] 96 | extras_require={ 97 | 'testing': ['tox>=2.6.0', 'tox-travis>=0.8', 'coverage>=4.3.4', 'selenium>=3.0.2', ], 98 | 'development': [ 99 | 'tox>=2.6.0', 'tox-travis>=0.8', 'coverage>=4.3.4', 'selenium>=3.0.2', 'blowdrycss>=1.0.2', 'zappa>=0.37.1', 100 | ], 101 | }, 102 | 103 | # If there are data files included in your packages that need to be 104 | # installed, specify them here. If using Python 2.6 or less, then these 105 | # have to be included in MANIFEST.in as well. 106 | # package_data={ 107 | # 'vue_flask': ['package_data.dat'], 108 | # }, 109 | 110 | # Although 'package_data' is the preferred approach, in some case you may 111 | # need to place data files outside of your packages. See: 112 | # http://docs.python.org/3.4/distutils/setupscript.html#installing-additional-files # noqa 113 | # In this case, 'data_file' will be installed into '/my_data' 114 | # data_files=[('my_data', ['data/data_file'])], 115 | 116 | # To provide executable scripts, use entry points in preference to the 117 | # "scripts" keyword. Entry points provide cross-platform support and allow 118 | # pip to create the appropriate form of executable for the target platform. 119 | # entry_points={ 120 | # }, 121 | 122 | # unit_tests 123 | test_suite="vue_flask.unit_tests", 124 | #tests_require=['tox', 'coverage', ], 125 | ) -------------------------------------------------------------------------------- /static/css/processed/combined.min.css: -------------------------------------------------------------------------------- 1 | @charset "UTF-8";/*! normalize-scss | MIT/GPLv2 License | bit.ly/normalize-scss */html{font-family:sans-serif;line-height:1.15;-ms-text-size-adjust:100%;-webkit-text-size-adjust:100%}body{margin:0}article,aside,footer,header,nav,section{display:block}h1{font-size:2em;margin:.67em 0}figcaption,figure{display:block}figure{margin:1em 40px}hr{box-sizing:content-box;height:0;overflow:visible}main{display:block}pre{font-family:monospace,monospace;font-size:1em}a{background-color:transparent;-webkit-text-decoration-skip:objects}a:active,a:hover{outline-width:0}abbr[title]{border-bottom:0;text-decoration:underline;text-decoration:underline dotted}b,strong{font-weight:inherit}b,strong{font-weight:bolder}code,kbd,samp{font-family:monospace,monospace;font-size:1em}dfn{font-style:italic}mark{background-color:#ff0;color:#000}small{font-size:80%}sub,sup{font-size:75%;line-height:0;position:relative;vertical-align:baseline}sub{bottom:-0.25em}sup{top:-0.5em}audio,video{display:inline-block}audio:not([controls]){display:none;height:0}img{border-style:none}svg:not(:root){overflow:hidden}button,input,optgroup,select,textarea{font-family:sans-serif;font-size:100%;line-height:1.15;margin:0}button{overflow:visible}button,select{text-transform:none}button,html [type="button"],[type="reset"],[type="submit"]{-webkit-appearance:button}button::-moz-focus-inner,[type="button"]::-moz-focus-inner,[type="reset"]::-moz-focus-inner,[type="submit"]::-moz-focus-inner{border-style:none;padding:0}button:-moz-focusring,[type="button"]:-moz-focusring,[type="reset"]:-moz-focusring,[type="submit"]:-moz-focusring{outline:1px dotted ButtonText}input{overflow:visible}[type="checkbox"],[type="radio"]{box-sizing:border-box;padding:0}[type="number"]::-webkit-inner-spin-button,[type="number"]::-webkit-outer-spin-button{height:auto}[type="search"]{-webkit-appearance:textfield;outline-offset:-2px}[type="search"]::-webkit-search-cancel-button,[type="search"]::-webkit-search-decoration{-webkit-appearance:none}::-webkit-file-upload-button{-webkit-appearance:button;font:inherit}fieldset{border:1px solid silver;margin:0 2px;padding:.35em .625em .75em}legend{box-sizing:border-box;display:table;max-width:100%;padding:0;color:inherit;white-space:normal}progress{display:inline-block;vertical-align:baseline}textarea{overflow:auto}details{display:block}summary{display:list-item}menu{display:block}canvas{display:inline-block}template{display:none}[hidden]{display:none}.foundation-mq{font-family:"small=0em&medium=40em&large=64em&xlarge=75em&xxlarge=90em"}html{box-sizing:border-box;font-size:100%}*,*::before,*::after{box-sizing:inherit}body{margin:0;padding:0;background:#fefefe;font-family:"Helvetica Neue",Helvetica,Roboto,Arial,sans-serif;font-weight:normal;line-height:1.5;color:#0a0a0a;-webkit-font-smoothing:antialiased;-moz-osx-font-smoothing:grayscale}img{display:inline-block;vertical-align:middle;max-width:100%;height:auto;-ms-interpolation-mode:bicubic}textarea{height:auto;min-height:50px;border-radius:0}select{width:100%;border-radius:0}.map_canvas img,.map_canvas embed,.map_canvas object,.mqa-display img,.mqa-display embed,.mqa-display object{max-width:none !important}button{padding:0;-webkit-appearance:none;-moz-appearance:none;appearance:none;border:0;border-radius:0;background:transparent;line-height:1}[data-whatinput='mouse'] button{outline:0}.is-visible{display:block !important}.is-hidden{display:none !important}div,dl,dt,dd,ul,ol,li,h1,h2,h3,h4,h5,h6,pre,form,p,blockquote,th,td{margin:0;padding:0}p{margin-bottom:1rem;font-size:inherit;line-height:1.6;text-rendering:optimizeLegibility}em,i{font-style:italic;line-height:inherit}strong,b{font-weight:bold;line-height:inherit}small{font-size:80%;line-height:inherit}h1,h2,h3,h4,h5,h6{font-family:"Helvetica Neue",Helvetica,Roboto,Arial,sans-serif;font-style:normal;font-weight:normal;color:inherit;text-rendering:optimizeLegibility}h1 small,h2 small,h3 small,h4 small,h5 small,h6 small{line-height:0;color:#cacaca}h1{font-size:1.5rem;line-height:1.4;margin-top:0;margin-bottom:.5rem}h2{font-size:1.25rem;line-height:1.4;margin-top:0;margin-bottom:.5rem}h3{font-size:1.1875rem;line-height:1.4;margin-top:0;margin-bottom:.5rem}h4{font-size:1.125rem;line-height:1.4;margin-top:0;margin-bottom:.5rem}h5{font-size:1.0625rem;line-height:1.4;margin-top:0;margin-bottom:.5rem}h6{font-size:1rem;line-height:1.4;margin-top:0;margin-bottom:.5rem}@media print,screen and (min-width:40em){h1{font-size:3rem}h2{font-size:2.5rem}h3{font-size:1.9375rem}h4{font-size:1.5625rem}h5{font-size:1.25rem}h6{font-size:1rem}}a{line-height:inherit;color:#1779ba;text-decoration:none;cursor:pointer}a:hover,a:focus{color:#1468a0}a img{border:0}hr{clear:both;max-width:75rem;height:0;margin:1.25rem auto;border-top:0;border-right:0;border-bottom:1px solid #cacaca;border-left:0}ul,ol,dl{margin-bottom:1rem;list-style-position:outside;line-height:1.6}li{font-size:inherit}ul{margin-left:1.25rem;list-style-type:disc}ol{margin-left:1.25rem}ul ul,ol ul,ul ol,ol ol{margin-left:1.25rem;margin-bottom:0}dl{margin-bottom:1rem}dl dt{margin-bottom:.3rem;font-weight:bold}blockquote{margin:0 0 1rem;padding:.5625rem 1.25rem 0 1.1875rem;border-left:1px solid #cacaca}blockquote,blockquote p{line-height:1.6;color:#8a8a8a}cite{display:block;font-size:.8125rem;color:#8a8a8a}cite:before{content:"— "}abbr{border-bottom:1px dotted #0a0a0a;color:#0a0a0a;cursor:help}figure{margin:0}code{padding:.125rem .3125rem .0625rem;border:1px solid #cacaca;background-color:#e6e6e6;font-family:Consolas,"Liberation Mono",Courier,monospace;font-weight:normal;color:#0a0a0a}kbd{margin:0;padding:.125rem .25rem 0;background-color:#e6e6e6;font-family:Consolas,"Liberation Mono",Courier,monospace;color:#0a0a0a}.subheader{margin-top:.2rem;margin-bottom:.5rem;font-weight:normal;line-height:1.4;color:#8a8a8a}.lead{font-size:125%;line-height:1.6}.stat{font-size:2.5rem;line-height:1}p+.stat{margin-top:-1rem}.no-bullet{margin-left:0;list-style:none}.text-left{text-align:left}.text-right{text-align:right}.text-center{text-align:center}.text-justify{text-align:justify}@media print,screen and (min-width:40em){.medium-text-left{text-align:left}.medium-text-right{text-align:right}.medium-text-center{text-align:center}.medium-text-justify{text-align:justify}}@media print,screen and (min-width:64em){.large-text-left{text-align:left}.large-text-right{text-align:right}.large-text-center{text-align:center}.large-text-justify{text-align:justify}}.show-for-print{display:none !important}@media print{*{background:transparent !important;box-shadow:none !important;color:black !important;text-shadow:none !important}.show-for-print{display:block !important}.hide-for-print{display:none !important}table.show-for-print{display:table !important}thead.show-for-print{display:table-header-group !important}tbody.show-for-print{display:table-row-group !important}tr.show-for-print{display:table-row !important}td.show-for-print{display:table-cell !important}th.show-for-print{display:table-cell !important}a,a:visited{text-decoration:underline}a[href]:after{content:" (" attr(href) ")"}.ir a:after,a[href^='javascript:']:after,a[href^='#']:after{content:''}abbr[title]:after{content:" (" attr(title) ")"}pre,blockquote{border:1px solid #8a8a8a;page-break-inside:avoid}thead{display:table-header-group}tr,img{page-break-inside:avoid}img{max-width:100% !important}@page{margin:.5cm}p,h2,h3{orphans:3;widows:3}h2,h3{page-break-after:avoid}}.row{max-width:75rem;margin-right:auto;margin-left:auto}.row::before,.row::after{display:table;content:' '}.row::after{clear:both}.row.collapse>.column,.row.collapse>.columns{padding-right:0;padding-left:0}.row .row{margin-right:-0.625rem;margin-left:-0.625rem}@media print,screen and (min-width:40em){.row .row{margin-right:-0.9375rem;margin-left:-0.9375rem}}@media print,screen and (min-width:64em){.row .row{margin-right:-0.9375rem;margin-left:-0.9375rem}}.row .row.collapse{margin-right:0;margin-left:0}.row.expanded{max-width:none}.row.expanded .row{margin-right:auto;margin-left:auto}.row.gutter-small>.column,.row.gutter-small>.columns{padding-right:.625rem;padding-left:.625rem}.row.gutter-medium>.column,.row.gutter-medium>.columns{padding-right:.9375rem;padding-left:.9375rem}.column,.columns{width:100%;float:left;padding-right:.625rem;padding-left:.625rem}@media print,screen and (min-width:40em){.column,.columns{padding-right:.9375rem;padding-left:.9375rem}}.column:last-child:not(:first-child),.columns:last-child:not(:first-child){float:right}.column.end:last-child:last-child,.end.columns:last-child:last-child{float:left}.column.row.row,.row.row.columns{float:none}.row .column.row.row,.row .row.row.columns{margin-right:0;margin-left:0;padding-right:0;padding-left:0}.small-1{width:8.33333%}.small-push-1{position:relative;left:8.33333%}.small-pull-1{position:relative;left:-8.33333%}.small-offset-0{margin-left:0}.small-2{width:16.66667%}.small-push-2{position:relative;left:16.66667%}.small-pull-2{position:relative;left:-16.66667%}.small-offset-1{margin-left:8.33333%}.small-3{width:25%}.small-push-3{position:relative;left:25%}.small-pull-3{position:relative;left:-25%}.small-offset-2{margin-left:16.66667%}.small-4{width:33.33333%}.small-push-4{position:relative;left:33.33333%}.small-pull-4{position:relative;left:-33.33333%}.small-offset-3{margin-left:25%}.small-5{width:41.66667%}.small-push-5{position:relative;left:41.66667%}.small-pull-5{position:relative;left:-41.66667%}.small-offset-4{margin-left:33.33333%}.small-6{width:50%}.small-push-6{position:relative;left:50%}.small-pull-6{position:relative;left:-50%}.small-offset-5{margin-left:41.66667%}.small-7{width:58.33333%}.small-push-7{position:relative;left:58.33333%}.small-pull-7{position:relative;left:-58.33333%}.small-offset-6{margin-left:50%}.small-8{width:66.66667%}.small-push-8{position:relative;left:66.66667%}.small-pull-8{position:relative;left:-66.66667%}.small-offset-7{margin-left:58.33333%}.small-9{width:75%}.small-push-9{position:relative;left:75%}.small-pull-9{position:relative;left:-75%}.small-offset-8{margin-left:66.66667%}.small-10{width:83.33333%}.small-push-10{position:relative;left:83.33333%}.small-pull-10{position:relative;left:-83.33333%}.small-offset-9{margin-left:75%}.small-11{width:91.66667%}.small-push-11{position:relative;left:91.66667%}.small-pull-11{position:relative;left:-91.66667%}.small-offset-10{margin-left:83.33333%}.small-12{width:100%}.small-offset-11{margin-left:91.66667%}.small-up-1>.column,.small-up-1>.columns{float:left;width:100%}.small-up-1>.column:nth-of-type(1n),.small-up-1>.columns:nth-of-type(1n){clear:none}.small-up-1>.column:nth-of-type(1n+1),.small-up-1>.columns:nth-of-type(1n+1){clear:both}.small-up-1>.column:last-child,.small-up-1>.columns:last-child{float:left}.small-up-2>.column,.small-up-2>.columns{float:left;width:50%}.small-up-2>.column:nth-of-type(1n),.small-up-2>.columns:nth-of-type(1n){clear:none}.small-up-2>.column:nth-of-type(2n+1),.small-up-2>.columns:nth-of-type(2n+1){clear:both}.small-up-2>.column:last-child,.small-up-2>.columns:last-child{float:left}.small-up-3>.column,.small-up-3>.columns{float:left;width:33.33333%}.small-up-3>.column:nth-of-type(1n),.small-up-3>.columns:nth-of-type(1n){clear:none}.small-up-3>.column:nth-of-type(3n+1),.small-up-3>.columns:nth-of-type(3n+1){clear:both}.small-up-3>.column:last-child,.small-up-3>.columns:last-child{float:left}.small-up-4>.column,.small-up-4>.columns{float:left;width:25%}.small-up-4>.column:nth-of-type(1n),.small-up-4>.columns:nth-of-type(1n){clear:none}.small-up-4>.column:nth-of-type(4n+1),.small-up-4>.columns:nth-of-type(4n+1){clear:both}.small-up-4>.column:last-child,.small-up-4>.columns:last-child{float:left}.small-up-5>.column,.small-up-5>.columns{float:left;width:20%}.small-up-5>.column:nth-of-type(1n),.small-up-5>.columns:nth-of-type(1n){clear:none}.small-up-5>.column:nth-of-type(5n+1),.small-up-5>.columns:nth-of-type(5n+1){clear:both}.small-up-5>.column:last-child,.small-up-5>.columns:last-child{float:left}.small-up-6>.column,.small-up-6>.columns{float:left;width:16.66667%}.small-up-6>.column:nth-of-type(1n),.small-up-6>.columns:nth-of-type(1n){clear:none}.small-up-6>.column:nth-of-type(6n+1),.small-up-6>.columns:nth-of-type(6n+1){clear:both}.small-up-6>.column:last-child,.small-up-6>.columns:last-child{float:left}.small-up-7>.column,.small-up-7>.columns{float:left;width:14.28571%}.small-up-7>.column:nth-of-type(1n),.small-up-7>.columns:nth-of-type(1n){clear:none}.small-up-7>.column:nth-of-type(7n+1),.small-up-7>.columns:nth-of-type(7n+1){clear:both}.small-up-7>.column:last-child,.small-up-7>.columns:last-child{float:left}.small-up-8>.column,.small-up-8>.columns{float:left;width:12.5%}.small-up-8>.column:nth-of-type(1n),.small-up-8>.columns:nth-of-type(1n){clear:none}.small-up-8>.column:nth-of-type(8n+1),.small-up-8>.columns:nth-of-type(8n+1){clear:both}.small-up-8>.column:last-child,.small-up-8>.columns:last-child{float:left}.small-collapse>.column,.small-collapse>.columns{padding-right:0;padding-left:0}.small-collapse .row{margin-right:0;margin-left:0}.expanded.row .small-collapse.row{margin-right:0;margin-left:0}.small-uncollapse>.column,.small-uncollapse>.columns{padding-right:.625rem;padding-left:.625rem}.small-centered{margin-right:auto;margin-left:auto}.small-centered,.small-centered:last-child:not(:first-child){float:none;clear:both}.small-uncentered,.small-push-0,.small-pull-0{position:static;float:left;margin-right:0;margin-left:0}@media print,screen and (min-width:40em){.medium-1{width:8.33333%}.medium-push-1{position:relative;left:8.33333%}.medium-pull-1{position:relative;left:-8.33333%}.medium-offset-0{margin-left:0}.medium-2{width:16.66667%}.medium-push-2{position:relative;left:16.66667%}.medium-pull-2{position:relative;left:-16.66667%}.medium-offset-1{margin-left:8.33333%}.medium-3{width:25%}.medium-push-3{position:relative;left:25%}.medium-pull-3{position:relative;left:-25%}.medium-offset-2{margin-left:16.66667%}.medium-4{width:33.33333%}.medium-push-4{position:relative;left:33.33333%}.medium-pull-4{position:relative;left:-33.33333%}.medium-offset-3{margin-left:25%}.medium-5{width:41.66667%}.medium-push-5{position:relative;left:41.66667%}.medium-pull-5{position:relative;left:-41.66667%}.medium-offset-4{margin-left:33.33333%}.medium-6{width:50%}.medium-push-6{position:relative;left:50%}.medium-pull-6{position:relative;left:-50%}.medium-offset-5{margin-left:41.66667%}.medium-7{width:58.33333%}.medium-push-7{position:relative;left:58.33333%}.medium-pull-7{position:relative;left:-58.33333%}.medium-offset-6{margin-left:50%}.medium-8{width:66.66667%}.medium-push-8{position:relative;left:66.66667%}.medium-pull-8{position:relative;left:-66.66667%}.medium-offset-7{margin-left:58.33333%}.medium-9{width:75%}.medium-push-9{position:relative;left:75%}.medium-pull-9{position:relative;left:-75%}.medium-offset-8{margin-left:66.66667%}.medium-10{width:83.33333%}.medium-push-10{position:relative;left:83.33333%}.medium-pull-10{position:relative;left:-83.33333%}.medium-offset-9{margin-left:75%}.medium-11{width:91.66667%}.medium-push-11{position:relative;left:91.66667%}.medium-pull-11{position:relative;left:-91.66667%}.medium-offset-10{margin-left:83.33333%}.medium-12{width:100%}.medium-offset-11{margin-left:91.66667%}.medium-up-1>.column,.medium-up-1>.columns{float:left;width:100%}.medium-up-1>.column:nth-of-type(1n),.medium-up-1>.columns:nth-of-type(1n){clear:none}.medium-up-1>.column:nth-of-type(1n+1),.medium-up-1>.columns:nth-of-type(1n+1){clear:both}.medium-up-1>.column:last-child,.medium-up-1>.columns:last-child{float:left}.medium-up-2>.column,.medium-up-2>.columns{float:left;width:50%}.medium-up-2>.column:nth-of-type(1n),.medium-up-2>.columns:nth-of-type(1n){clear:none}.medium-up-2>.column:nth-of-type(2n+1),.medium-up-2>.columns:nth-of-type(2n+1){clear:both}.medium-up-2>.column:last-child,.medium-up-2>.columns:last-child{float:left}.medium-up-3>.column,.medium-up-3>.columns{float:left;width:33.33333%}.medium-up-3>.column:nth-of-type(1n),.medium-up-3>.columns:nth-of-type(1n){clear:none}.medium-up-3>.column:nth-of-type(3n+1),.medium-up-3>.columns:nth-of-type(3n+1){clear:both}.medium-up-3>.column:last-child,.medium-up-3>.columns:last-child{float:left}.medium-up-4>.column,.medium-up-4>.columns{float:left;width:25%}.medium-up-4>.column:nth-of-type(1n),.medium-up-4>.columns:nth-of-type(1n){clear:none}.medium-up-4>.column:nth-of-type(4n+1),.medium-up-4>.columns:nth-of-type(4n+1){clear:both}.medium-up-4>.column:last-child,.medium-up-4>.columns:last-child{float:left}.medium-up-5>.column,.medium-up-5>.columns{float:left;width:20%}.medium-up-5>.column:nth-of-type(1n),.medium-up-5>.columns:nth-of-type(1n){clear:none}.medium-up-5>.column:nth-of-type(5n+1),.medium-up-5>.columns:nth-of-type(5n+1){clear:both}.medium-up-5>.column:last-child,.medium-up-5>.columns:last-child{float:left}.medium-up-6>.column,.medium-up-6>.columns{float:left;width:16.66667%}.medium-up-6>.column:nth-of-type(1n),.medium-up-6>.columns:nth-of-type(1n){clear:none}.medium-up-6>.column:nth-of-type(6n+1),.medium-up-6>.columns:nth-of-type(6n+1){clear:both}.medium-up-6>.column:last-child,.medium-up-6>.columns:last-child{float:left}.medium-up-7>.column,.medium-up-7>.columns{float:left;width:14.28571%}.medium-up-7>.column:nth-of-type(1n),.medium-up-7>.columns:nth-of-type(1n){clear:none}.medium-up-7>.column:nth-of-type(7n+1),.medium-up-7>.columns:nth-of-type(7n+1){clear:both}.medium-up-7>.column:last-child,.medium-up-7>.columns:last-child{float:left}.medium-up-8>.column,.medium-up-8>.columns{float:left;width:12.5%}.medium-up-8>.column:nth-of-type(1n),.medium-up-8>.columns:nth-of-type(1n){clear:none}.medium-up-8>.column:nth-of-type(8n+1),.medium-up-8>.columns:nth-of-type(8n+1){clear:both}.medium-up-8>.column:last-child,.medium-up-8>.columns:last-child{float:left}.medium-collapse>.column,.medium-collapse>.columns{padding-right:0;padding-left:0}.medium-collapse .row{margin-right:0;margin-left:0}.expanded.row .medium-collapse.row{margin-right:0;margin-left:0}.medium-uncollapse>.column,.medium-uncollapse>.columns{padding-right:.9375rem;padding-left:.9375rem}.medium-centered{margin-right:auto;margin-left:auto}.medium-centered,.medium-centered:last-child:not(:first-child){float:none;clear:both}.medium-uncentered,.medium-push-0,.medium-pull-0{position:static;float:left;margin-right:0;margin-left:0}}@media print,screen and (min-width:64em){.large-1{width:8.33333%}.large-push-1{position:relative;left:8.33333%}.large-pull-1{position:relative;left:-8.33333%}.large-offset-0{margin-left:0}.large-2{width:16.66667%}.large-push-2{position:relative;left:16.66667%}.large-pull-2{position:relative;left:-16.66667%}.large-offset-1{margin-left:8.33333%}.large-3{width:25%}.large-push-3{position:relative;left:25%}.large-pull-3{position:relative;left:-25%}.large-offset-2{margin-left:16.66667%}.large-4{width:33.33333%}.large-push-4{position:relative;left:33.33333%}.large-pull-4{position:relative;left:-33.33333%}.large-offset-3{margin-left:25%}.large-5{width:41.66667%}.large-push-5{position:relative;left:41.66667%}.large-pull-5{position:relative;left:-41.66667%}.large-offset-4{margin-left:33.33333%}.large-6{width:50%}.large-push-6{position:relative;left:50%}.large-pull-6{position:relative;left:-50%}.large-offset-5{margin-left:41.66667%}.large-7{width:58.33333%}.large-push-7{position:relative;left:58.33333%}.large-pull-7{position:relative;left:-58.33333%}.large-offset-6{margin-left:50%}.large-8{width:66.66667%}.large-push-8{position:relative;left:66.66667%}.large-pull-8{position:relative;left:-66.66667%}.large-offset-7{margin-left:58.33333%}.large-9{width:75%}.large-push-9{position:relative;left:75%}.large-pull-9{position:relative;left:-75%}.large-offset-8{margin-left:66.66667%}.large-10{width:83.33333%}.large-push-10{position:relative;left:83.33333%}.large-pull-10{position:relative;left:-83.33333%}.large-offset-9{margin-left:75%}.large-11{width:91.66667%}.large-push-11{position:relative;left:91.66667%}.large-pull-11{position:relative;left:-91.66667%}.large-offset-10{margin-left:83.33333%}.large-12{width:100%}.large-offset-11{margin-left:91.66667%}.large-up-1>.column,.large-up-1>.columns{float:left;width:100%}.large-up-1>.column:nth-of-type(1n),.large-up-1>.columns:nth-of-type(1n){clear:none}.large-up-1>.column:nth-of-type(1n+1),.large-up-1>.columns:nth-of-type(1n+1){clear:both}.large-up-1>.column:last-child,.large-up-1>.columns:last-child{float:left}.large-up-2>.column,.large-up-2>.columns{float:left;width:50%}.large-up-2>.column:nth-of-type(1n),.large-up-2>.columns:nth-of-type(1n){clear:none}.large-up-2>.column:nth-of-type(2n+1),.large-up-2>.columns:nth-of-type(2n+1){clear:both}.large-up-2>.column:last-child,.large-up-2>.columns:last-child{float:left}.large-up-3>.column,.large-up-3>.columns{float:left;width:33.33333%}.large-up-3>.column:nth-of-type(1n),.large-up-3>.columns:nth-of-type(1n){clear:none}.large-up-3>.column:nth-of-type(3n+1),.large-up-3>.columns:nth-of-type(3n+1){clear:both}.large-up-3>.column:last-child,.large-up-3>.columns:last-child{float:left}.large-up-4>.column,.large-up-4>.columns{float:left;width:25%}.large-up-4>.column:nth-of-type(1n),.large-up-4>.columns:nth-of-type(1n){clear:none}.large-up-4>.column:nth-of-type(4n+1),.large-up-4>.columns:nth-of-type(4n+1){clear:both}.large-up-4>.column:last-child,.large-up-4>.columns:last-child{float:left}.large-up-5>.column,.large-up-5>.columns{float:left;width:20%}.large-up-5>.column:nth-of-type(1n),.large-up-5>.columns:nth-of-type(1n){clear:none}.large-up-5>.column:nth-of-type(5n+1),.large-up-5>.columns:nth-of-type(5n+1){clear:both}.large-up-5>.column:last-child,.large-up-5>.columns:last-child{float:left}.large-up-6>.column,.large-up-6>.columns{float:left;width:16.66667%}.large-up-6>.column:nth-of-type(1n),.large-up-6>.columns:nth-of-type(1n){clear:none}.large-up-6>.column:nth-of-type(6n+1),.large-up-6>.columns:nth-of-type(6n+1){clear:both}.large-up-6>.column:last-child,.large-up-6>.columns:last-child{float:left}.large-up-7>.column,.large-up-7>.columns{float:left;width:14.28571%}.large-up-7>.column:nth-of-type(1n),.large-up-7>.columns:nth-of-type(1n){clear:none}.large-up-7>.column:nth-of-type(7n+1),.large-up-7>.columns:nth-of-type(7n+1){clear:both}.large-up-7>.column:last-child,.large-up-7>.columns:last-child{float:left}.large-up-8>.column,.large-up-8>.columns{float:left;width:12.5%}.large-up-8>.column:nth-of-type(1n),.large-up-8>.columns:nth-of-type(1n){clear:none}.large-up-8>.column:nth-of-type(8n+1),.large-up-8>.columns:nth-of-type(8n+1){clear:both}.large-up-8>.column:last-child,.large-up-8>.columns:last-child{float:left}.large-collapse>.column,.large-collapse>.columns{padding-right:0;padding-left:0}.large-collapse .row{margin-right:0;margin-left:0}.expanded.row .large-collapse.row{margin-right:0;margin-left:0}.large-uncollapse>.column,.large-uncollapse>.columns{padding-right:.9375rem;padding-left:.9375rem}.large-centered{margin-right:auto;margin-left:auto}.large-centered,.large-centered:last-child:not(:first-child){float:none;clear:both}.large-uncentered,.large-push-0,.large-pull-0{position:static;float:left;margin-right:0;margin-left:0}}.column-block{margin-bottom:1.25rem}.column-block>:last-child{margin-bottom:0}@media print,screen and (min-width:40em){.column-block{margin-bottom:1.875rem}.column-block>:last-child{margin-bottom:0}}[type='text'],[type='password'],[type='date'],[type='datetime'],[type='datetime-local'],[type='month'],[type='week'],[type='email'],[type='number'],[type='search'],[type='tel'],[type='time'],[type='url'],[type='color'],textarea{display:block;box-sizing:border-box;width:100%;height:2.4375rem;margin:0 0 1rem;padding:.5rem;border:1px solid #cacaca;border-radius:0;background-color:#fefefe;box-shadow:inset 0 1px 2px rgba(10,10,10,0.1);font-family:inherit;font-size:1rem;font-weight:normal;color:#0a0a0a;transition:box-shadow .5s,border-color .25s ease-in-out;-webkit-appearance:none;-moz-appearance:none;appearance:none}[type='text']:focus,[type='password']:focus,[type='date']:focus,[type='datetime']:focus,[type='datetime-local']:focus,[type='month']:focus,[type='week']:focus,[type='email']:focus,[type='number']:focus,[type='search']:focus,[type='tel']:focus,[type='time']:focus,[type='url']:focus,[type='color']:focus,textarea:focus{outline:0;border:1px solid #8a8a8a;background-color:#fefefe;box-shadow:0 0 5px #cacaca;transition:box-shadow .5s,border-color .25s ease-in-out}textarea{max-width:100%}textarea[rows]{height:auto}input::-webkit-input-placeholder,textarea::-webkit-input-placeholder{color:#cacaca}input::-moz-placeholder,textarea::-moz-placeholder{color:#cacaca}input:-ms-input-placeholder,textarea:-ms-input-placeholder{color:#cacaca}input::placeholder,textarea::placeholder{color:#cacaca}input:disabled,input[readonly],textarea:disabled,textarea[readonly]{background-color:#e6e6e6;cursor:not-allowed}[type='submit'],[type='button']{-webkit-appearance:none;-moz-appearance:none;appearance:none;border-radius:0}input[type='search']{box-sizing:border-box}[type='file'],[type='checkbox'],[type='radio']{margin:0 0 1rem}[type='checkbox']+label,[type='radio']+label{display:inline-block;vertical-align:baseline;margin-left:.5rem;margin-right:1rem;margin-bottom:0}[type='checkbox']+label[for],[type='radio']+label[for]{cursor:pointer}label>[type='checkbox'],label>[type='radio']{margin-right:.5rem}[type='file']{width:100%}label{display:block;margin:0;font-size:.875rem;font-weight:normal;line-height:1.8;color:#0a0a0a}label.middle{margin:0 0 1rem;padding:.5625rem 0}.help-text{margin-top:-0.5rem;font-size:.8125rem;font-style:italic;color:#0a0a0a}.input-group{display:table;width:100%;margin-bottom:1rem}.input-group>:first-child{border-radius:0}.input-group>:last-child>*{border-radius:0}.input-group-label,.input-group-field,.input-group-button,.input-group-button a,.input-group-button input,.input-group-button button,.input-group-button label{margin:0;white-space:nowrap;display:table-cell;vertical-align:middle}.input-group-label{padding:0 1rem;border:1px solid #cacaca;background:#e6e6e6;color:#0a0a0a;text-align:center;white-space:nowrap;width:1%;height:100%}.input-group-label:first-child{border-right:0}.input-group-label:last-child{border-left:0}.input-group-field{border-radius:0;height:2.5rem}.input-group-button{padding-top:0;padding-bottom:0;text-align:center;width:1%;height:100%}.input-group-button a,.input-group-button input,.input-group-button button,.input-group-button label{height:2.5rem;padding-top:0;padding-bottom:0;font-size:1rem}.input-group .input-group-button{display:table-cell}fieldset{margin:0;padding:0;border:0}legend{max-width:100%;margin-bottom:.5rem}.fieldset{margin:1.125rem 0;padding:1.25rem;border:1px solid #cacaca}.fieldset legend{margin:0;margin-left:-0.1875rem;padding:0 .1875rem;background:#fefefe}select{height:2.4375rem;margin:0 0 1rem;padding:.5rem;-webkit-appearance:none;-moz-appearance:none;appearance:none;border:1px solid #cacaca;border-radius:0;background-color:#fefefe;font-family:inherit;font-size:1rem;line-height:normal;color:#0a0a0a;background-image:url("data:image/svg+xml;utf8,");background-origin:content-box;background-position:right -1rem center;background-repeat:no-repeat;background-size:9px 6px;padding-right:1.5rem;transition:box-shadow .5s,border-color .25s ease-in-out}@media screen and (min-width:0\0){select{background-image:url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACAAAAAYCAYAAACbU/80AAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAAIpJREFUeNrEkckNgDAMBBfRkEt0ObRBBdsGXUDgmQfK4XhH2m8czQAAy27R3tsw4Qfe2x8uOO6oYLb6GlOor3GF+swURAOmUJ+RwtEJs9WvTGEYxBXqI1MQAZhCfUQKRzDMVj+TwrAIV6jvSUEkYAr1LSkcyTBb/V+KYfX7xAeusq3sLDtGH3kEGACPWIflNZfhRQAAAABJRU5ErkJggg==")}}select:focus{outline:0;border:1px solid #8a8a8a;background-color:#fefefe;box-shadow:0 0 5px #cacaca;transition:box-shadow .5s,border-color .25s ease-in-out}select:disabled{background-color:#e6e6e6;cursor:not-allowed}select::-ms-expand{display:none}select[multiple]{height:auto;background-image:none}.is-invalid-input:not(:focus){border-color:#cc4b37;background-color:#f9ecea}.is-invalid-input:not(:focus)::-webkit-input-placeholder{color:#cc4b37}.is-invalid-input:not(:focus)::-moz-placeholder{color:#cc4b37}.is-invalid-input:not(:focus):-ms-input-placeholder{color:#cc4b37}.is-invalid-input:not(:focus)::placeholder{color:#cc4b37}.is-invalid-label{color:#cc4b37}.form-error{display:none;margin-top:-0.5rem;margin-bottom:1rem;font-size:.75rem;font-weight:bold;color:#cc4b37}.form-error.is-visible{display:block}.button{display:inline-block;vertical-align:middle;margin:0 0 1rem 0;padding:.85em 1em;-webkit-appearance:none;border:1px solid transparent;border-radius:0;transition:background-color .25s ease-out,color .25s ease-out;font-size:.9rem;line-height:1;text-align:center;cursor:pointer;background-color:#1779ba;color:#fefefe}[data-whatinput='mouse'] .button{outline:0}.button:hover,.button:focus{background-color:#14679e;color:#fefefe}.button.tiny{font-size:.6rem}.button.small{font-size:.75rem}.button.large{font-size:1.25rem}.button.expanded{display:block;width:100%;margin-right:0;margin-left:0}.button.primary{background-color:#1779ba;color:#fefefe}.button.primary:hover,.button.primary:focus{background-color:#126195;color:#fefefe}.button.secondary{background-color:#767676;color:#fefefe}.button.secondary:hover,.button.secondary:focus{background-color:#5e5e5e;color:#fefefe}.button.success{background-color:#3adb76;color:#0a0a0a}.button.success:hover,.button.success:focus{background-color:#22bb5b;color:#0a0a0a}.button.warning{background-color:#ffae00;color:#0a0a0a}.button.warning:hover,.button.warning:focus{background-color:#cc8b00;color:#0a0a0a}.button.alert{background-color:#cc4b37;color:#fefefe}.button.alert:hover,.button.alert:focus{background-color:#a53b2a;color:#fefefe}.button.hollow{border:1px solid #1779ba;color:#1779ba}.button.hollow,.button.hollow:hover,.button.hollow:focus{background-color:transparent}.button.hollow:hover,.button.hollow:focus{border-color:#0c3d5d;color:#0c3d5d}.button.hollow.primary{border:1px solid #1779ba;color:#1779ba}.button.hollow.primary:hover,.button.hollow.primary:focus{border-color:#0c3d5d;color:#0c3d5d}.button.hollow.secondary{border:1px solid #767676;color:#767676}.button.hollow.secondary:hover,.button.hollow.secondary:focus{border-color:#3b3b3b;color:#3b3b3b}.button.hollow.success{border:1px solid #3adb76;color:#3adb76}.button.hollow.success:hover,.button.hollow.success:focus{border-color:#157539;color:#157539}.button.hollow.warning{border:1px solid #ffae00;color:#ffae00}.button.hollow.warning:hover,.button.hollow.warning:focus{border-color:#805700;color:#805700}.button.hollow.alert{border:1px solid #cc4b37;color:#cc4b37}.button.hollow.alert:hover,.button.hollow.alert:focus{border-color:#67251a;color:#67251a}.button.disabled,.button[disabled]{opacity:.25;cursor:not-allowed}.button.disabled:hover,.button.disabled:focus,.button[disabled]:hover,.button[disabled]:focus{background-color:#1779ba;color:#fefefe}.button.disabled.primary,.button[disabled].primary{opacity:.25;cursor:not-allowed}.button.disabled.primary:hover,.button.disabled.primary:focus,.button[disabled].primary:hover,.button[disabled].primary:focus{background-color:#1779ba;color:#fefefe}.button.disabled.secondary,.button[disabled].secondary{opacity:.25;cursor:not-allowed}.button.disabled.secondary:hover,.button.disabled.secondary:focus,.button[disabled].secondary:hover,.button[disabled].secondary:focus{background-color:#767676;color:#fefefe}.button.disabled.success,.button[disabled].success{opacity:.25;cursor:not-allowed}.button.disabled.success:hover,.button.disabled.success:focus,.button[disabled].success:hover,.button[disabled].success:focus{background-color:#3adb76;color:#fefefe}.button.disabled.warning,.button[disabled].warning{opacity:.25;cursor:not-allowed}.button.disabled.warning:hover,.button.disabled.warning:focus,.button[disabled].warning:hover,.button[disabled].warning:focus{background-color:#ffae00;color:#fefefe}.button.disabled.alert,.button[disabled].alert{opacity:.25;cursor:not-allowed}.button.disabled.alert:hover,.button.disabled.alert:focus,.button[disabled].alert:hover,.button[disabled].alert:focus{background-color:#cc4b37;color:#fefefe}.button.dropdown::after{display:block;width:0;height:0;border:inset .4em;content:'';border-bottom-width:0;border-top-style:solid;border-color:#fefefe transparent transparent;position:relative;top:.4em;display:inline-block;float:right;margin-left:1em}.button.arrow-only::after{top:-0.1em;float:none;margin-left:0}.callout{position:relative;margin:0 0 1rem 0;padding:1rem;border:1px solid rgba(10,10,10,0.25);border-radius:0;background-color:white;color:#0a0a0a}.callout>:first-child{margin-top:0}.callout>:last-child{margin-bottom:0}.callout.primary{background-color:#d7ecfa;color:#0a0a0a}.callout.secondary{background-color:#eaeaea;color:#0a0a0a}.callout.success{background-color:#e1faea;color:#0a0a0a}.callout.warning{background-color:#fff3d9;color:#0a0a0a}.callout.alert{background-color:#f7e4e1;color:#0a0a0a}.callout.small{padding-top:.5rem;padding-right:.5rem;padding-bottom:.5rem;padding-left:.5rem}.callout.large{padding-top:3rem;padding-right:3rem;padding-bottom:3rem;padding-left:3rem}body.is-reveal-open{overflow:hidden}html.is-reveal-open,html.is-reveal-open body{min-height:100%;overflow:hidden;-webkit-user-select:none;-moz-user-select:none;-ms-user-select:none;user-select:none}.reveal-overlay{position:fixed;top:0;right:0;bottom:0;left:0;z-index:1005;display:none;background-color:rgba(10,10,10,0.45);overflow-y:scroll}.reveal{z-index:1006;-webkit-backface-visibility:hidden;backface-visibility:hidden;display:none;padding:1rem;border:1px solid #cacaca;border-radius:0;background-color:#fefefe;position:relative;top:100px;margin-right:auto;margin-left:auto;overflow-y:auto}[data-whatinput='mouse'] .reveal{outline:0}@media print,screen and (min-width:40em){.reveal{min-height:0}}.reveal .column,.reveal .columns,.reveal .columns{min-width:0}.reveal>:last-child{margin-bottom:0}@media print,screen and (min-width:40em){.reveal{width:600px;max-width:75rem}}@media print,screen and (min-width:40em){.reveal .reveal{right:auto;left:auto;margin:0 auto}}.reveal.collapse{padding:0}@media print,screen and (min-width:40em){.reveal.tiny{width:30%;max-width:75rem}}@media print,screen and (min-width:40em){.reveal.small{width:50%;max-width:75rem}}@media print,screen and (min-width:40em){.reveal.large{width:90%;max-width:75rem}}.reveal.full{top:0;left:0;width:100%;max-width:none;height:100%;height:100vh;min-height:100vh;margin-left:0;border:0;border-radius:0}@media screen and (max-width:39.9375em){.reveal{top:0;left:0;width:100%;max-width:none;height:100%;height:100vh;min-height:100vh;margin-left:0;border:0;border-radius:0}}.reveal.without-overlay{position:fixed}.slide-in-down.mui-enter{transition-duration:500ms;transition-timing-function:linear;-ms-transform:translateY(-100%);transform:translateY(-100%);transition-property:transform,opacity;-webkit-backface-visibility:hidden;backface-visibility:hidden}.slide-in-down.mui-enter.mui-enter-active{-ms-transform:translateY(0);transform:translateY(0)}.slide-in-left.mui-enter{transition-duration:500ms;transition-timing-function:linear;-ms-transform:translateX(-100%);transform:translateX(-100%);transition-property:transform,opacity;-webkit-backface-visibility:hidden;backface-visibility:hidden}.slide-in-left.mui-enter.mui-enter-active{-ms-transform:translateX(0);transform:translateX(0)}.slide-in-up.mui-enter{transition-duration:500ms;transition-timing-function:linear;-ms-transform:translateY(100%);transform:translateY(100%);transition-property:transform,opacity;-webkit-backface-visibility:hidden;backface-visibility:hidden}.slide-in-up.mui-enter.mui-enter-active{-ms-transform:translateY(0);transform:translateY(0)}.slide-in-right.mui-enter{transition-duration:500ms;transition-timing-function:linear;-ms-transform:translateX(100%);transform:translateX(100%);transition-property:transform,opacity;-webkit-backface-visibility:hidden;backface-visibility:hidden}.slide-in-right.mui-enter.mui-enter-active{-ms-transform:translateX(0);transform:translateX(0)}.slide-out-down.mui-leave{transition-duration:500ms;transition-timing-function:linear;-ms-transform:translateY(0);transform:translateY(0);transition-property:transform,opacity;-webkit-backface-visibility:hidden;backface-visibility:hidden}.slide-out-down.mui-leave.mui-leave-active{-ms-transform:translateY(100%);transform:translateY(100%)}.slide-out-right.mui-leave{transition-duration:500ms;transition-timing-function:linear;-ms-transform:translateX(0);transform:translateX(0);transition-property:transform,opacity;-webkit-backface-visibility:hidden;backface-visibility:hidden}.slide-out-right.mui-leave.mui-leave-active{-ms-transform:translateX(100%);transform:translateX(100%)}.slide-out-up.mui-leave{transition-duration:500ms;transition-timing-function:linear;-ms-transform:translateY(0);transform:translateY(0);transition-property:transform,opacity;-webkit-backface-visibility:hidden;backface-visibility:hidden}.slide-out-up.mui-leave.mui-leave-active{-ms-transform:translateY(-100%);transform:translateY(-100%)}.slide-out-left.mui-leave{transition-duration:500ms;transition-timing-function:linear;-ms-transform:translateX(0);transform:translateX(0);transition-property:transform,opacity;-webkit-backface-visibility:hidden;backface-visibility:hidden}.slide-out-left.mui-leave.mui-leave-active{-ms-transform:translateX(-100%);transform:translateX(-100%)}.fade-in.mui-enter{transition-duration:500ms;transition-timing-function:linear;opacity:0;transition-property:opacity}.fade-in.mui-enter.mui-enter-active{opacity:1}.fade-out.mui-leave{transition-duration:500ms;transition-timing-function:linear;opacity:1;transition-property:opacity}.fade-out.mui-leave.mui-leave-active{opacity:0}.hinge-in-from-top.mui-enter{transition-duration:500ms;transition-timing-function:linear;transform:perspective(2000px) rotateX(-90deg);-ms-transform-origin:top;transform-origin:top;transition-property:transform,opacity;opacity:0}.hinge-in-from-top.mui-enter.mui-enter-active{transform:perspective(2000px) rotate(0);opacity:1}.hinge-in-from-right.mui-enter{transition-duration:500ms;transition-timing-function:linear;transform:perspective(2000px) rotateY(-90deg);-ms-transform-origin:right;transform-origin:right;transition-property:transform,opacity;opacity:0}.hinge-in-from-right.mui-enter.mui-enter-active{transform:perspective(2000px) rotate(0);opacity:1}.hinge-in-from-bottom.mui-enter{transition-duration:500ms;transition-timing-function:linear;transform:perspective(2000px) rotateX(90deg);-ms-transform-origin:bottom;transform-origin:bottom;transition-property:transform,opacity;opacity:0}.hinge-in-from-bottom.mui-enter.mui-enter-active{transform:perspective(2000px) rotate(0);opacity:1}.hinge-in-from-left.mui-enter{transition-duration:500ms;transition-timing-function:linear;transform:perspective(2000px) rotateY(90deg);-ms-transform-origin:left;transform-origin:left;transition-property:transform,opacity;opacity:0}.hinge-in-from-left.mui-enter.mui-enter-active{transform:perspective(2000px) rotate(0);opacity:1}.hinge-in-from-middle-x.mui-enter{transition-duration:500ms;transition-timing-function:linear;transform:perspective(2000px) rotateX(-90deg);-ms-transform-origin:center;transform-origin:center;transition-property:transform,opacity;opacity:0}.hinge-in-from-middle-x.mui-enter.mui-enter-active{transform:perspective(2000px) rotate(0);opacity:1}.hinge-in-from-middle-y.mui-enter{transition-duration:500ms;transition-timing-function:linear;transform:perspective(2000px) rotateY(-90deg);-ms-transform-origin:center;transform-origin:center;transition-property:transform,opacity;opacity:0}.hinge-in-from-middle-y.mui-enter.mui-enter-active{transform:perspective(2000px) rotate(0);opacity:1}.hinge-out-from-top.mui-leave{transition-duration:500ms;transition-timing-function:linear;transform:perspective(2000px) rotate(0);-ms-transform-origin:top;transform-origin:top;transition-property:transform,opacity;opacity:1}.hinge-out-from-top.mui-leave.mui-leave-active{transform:perspective(2000px) rotateX(-90deg);opacity:0}.hinge-out-from-right.mui-leave{transition-duration:500ms;transition-timing-function:linear;transform:perspective(2000px) rotate(0);-ms-transform-origin:right;transform-origin:right;transition-property:transform,opacity;opacity:1}.hinge-out-from-right.mui-leave.mui-leave-active{transform:perspective(2000px) rotateY(-90deg);opacity:0}.hinge-out-from-bottom.mui-leave{transition-duration:500ms;transition-timing-function:linear;transform:perspective(2000px) rotate(0);-ms-transform-origin:bottom;transform-origin:bottom;transition-property:transform,opacity;opacity:1}.hinge-out-from-bottom.mui-leave.mui-leave-active{transform:perspective(2000px) rotateX(90deg);opacity:0}.hinge-out-from-left.mui-leave{transition-duration:500ms;transition-timing-function:linear;transform:perspective(2000px) rotate(0);-ms-transform-origin:left;transform-origin:left;transition-property:transform,opacity;opacity:1}.hinge-out-from-left.mui-leave.mui-leave-active{transform:perspective(2000px) rotateY(90deg);opacity:0}.hinge-out-from-middle-x.mui-leave{transition-duration:500ms;transition-timing-function:linear;transform:perspective(2000px) rotate(0);-ms-transform-origin:center;transform-origin:center;transition-property:transform,opacity;opacity:1}.hinge-out-from-middle-x.mui-leave.mui-leave-active{transform:perspective(2000px) rotateX(-90deg);opacity:0}.hinge-out-from-middle-y.mui-leave{transition-duration:500ms;transition-timing-function:linear;transform:perspective(2000px) rotate(0);-ms-transform-origin:center;transform-origin:center;transition-property:transform,opacity;opacity:1}.hinge-out-from-middle-y.mui-leave.mui-leave-active{transform:perspective(2000px) rotateY(-90deg);opacity:0}.scale-in-up.mui-enter{transition-duration:500ms;transition-timing-function:linear;-ms-transform:scale(0.5);transform:scale(0.5);transition-property:transform,opacity;opacity:0}.scale-in-up.mui-enter.mui-enter-active{-ms-transform:scale(1);transform:scale(1);opacity:1}.scale-in-down.mui-enter{transition-duration:500ms;transition-timing-function:linear;-ms-transform:scale(1.5);transform:scale(1.5);transition-property:transform,opacity;opacity:0}.scale-in-down.mui-enter.mui-enter-active{-ms-transform:scale(1);transform:scale(1);opacity:1}.scale-out-up.mui-leave{transition-duration:500ms;transition-timing-function:linear;-ms-transform:scale(1);transform:scale(1);transition-property:transform,opacity;opacity:1}.scale-out-up.mui-leave.mui-leave-active{-ms-transform:scale(1.5);transform:scale(1.5);opacity:0}.scale-out-down.mui-leave{transition-duration:500ms;transition-timing-function:linear;-ms-transform:scale(1);transform:scale(1);transition-property:transform,opacity;opacity:1}.scale-out-down.mui-leave.mui-leave-active{-ms-transform:scale(0.5);transform:scale(0.5);opacity:0}.spin-in.mui-enter{transition-duration:500ms;transition-timing-function:linear;-ms-transform:rotate(-0.75turn);transform:rotate(-0.75turn);transition-property:transform,opacity;opacity:0}.spin-in.mui-enter.mui-enter-active{-ms-transform:rotate(0);transform:rotate(0);opacity:1}.spin-out.mui-leave{transition-duration:500ms;transition-timing-function:linear;-ms-transform:rotate(0);transform:rotate(0);transition-property:transform,opacity;opacity:1}.spin-out.mui-leave.mui-leave-active{-ms-transform:rotate(0.75turn);transform:rotate(0.75turn);opacity:0}.spin-in-ccw.mui-enter{transition-duration:500ms;transition-timing-function:linear;-ms-transform:rotate(0.75turn);transform:rotate(0.75turn);transition-property:transform,opacity;opacity:0}.spin-in-ccw.mui-enter.mui-enter-active{-ms-transform:rotate(0);transform:rotate(0);opacity:1}.spin-out-ccw.mui-leave{transition-duration:500ms;transition-timing-function:linear;-ms-transform:rotate(0);transform:rotate(0);transition-property:transform,opacity;opacity:1}.spin-out-ccw.mui-leave.mui-leave-active{-ms-transform:rotate(-0.75turn);transform:rotate(-0.75turn);opacity:0}.slow{transition-duration:750ms !important}.fast{transition-duration:250ms !important}.linear{transition-timing-function:linear !important}.ease{transition-timing-function:ease !important}.ease-in{transition-timing-function:ease-in !important}.ease-out{transition-timing-function:ease-out !important}.ease-in-out{transition-timing-function:ease-in-out !important}.bounce-in{transition-timing-function:cubic-bezier(0.485,0.155,0.24,1.245) !important}.bounce-out{transition-timing-function:cubic-bezier(0.485,0.155,0.515,0.845) !important}.bounce-in-out{transition-timing-function:cubic-bezier(0.76,-0.245,0.24,1.245) !important}.short-delay{transition-delay:300ms !important}.long-delay{transition-delay:700ms !important}.shake{animation-name:shake-7}@keyframes shake-7{0,10%,20%,30%,40%,50%,60%,70%,80%,90%{transform:translateX(7%)}5%,15%,25%,35%,45%,55%,65%,75%,85%,95%{transform:translateX(-7%)}}.spin-cw{animation-name:spin-cw-1turn}@keyframes spin-cw-1turn{0{transform:rotate(-1turn)}100%{transform:rotate(0)}}.spin-ccw{animation-name:spin-cw-1turn}@keyframes spin-cw-1turn{0{transform:rotate(0)}100%{transform:rotate(1turn)}}.wiggle{animation-name:wiggle-7deg}@keyframes wiggle-7deg{40%,50%,60%{transform:rotate(7deg)}35%,45%,55%,65%{transform:rotate(-7deg)}0,30%,70%,100%{transform:rotate(0)}}.shake,.spin-cw,.spin-ccw,.wiggle{animation-duration:500ms}.infinite{animation-iteration-count:infinite}.slow{animation-duration:750ms !important}.fast{animation-duration:250ms !important}.linear{animation-timing-function:linear !important}.ease{animation-timing-function:ease !important}.ease-in{animation-timing-function:ease-in !important}.ease-out{animation-timing-function:ease-out !important}.ease-in-out{animation-timing-function:ease-in-out !important}.bounce-in{animation-timing-function:cubic-bezier(0.485,0.155,0.24,1.245) !important}.bounce-out{animation-timing-function:cubic-bezier(0.485,0.155,0.515,0.845) !important}.bounce-in-out{animation-timing-function:cubic-bezier(0.76,-0.245,0.24,1.245) !important}.short-delay{animation-delay:300ms !important}.long-delay{animation-delay:700ms !important}.form{position:relative;z-index:1;background:#fff;margin:0 0 65px 0;padding:10px 37px 30px 37px;box-shadow:0 0 20px 0 rgba(0,0,0,0.2),0 5px 5px 0 rgba(0,0,0,0.24)}input[type="text"],input[type="date"]{margin:.25rem 0 0 0;font-family:sans-serif;color:#111;font-size:18px;appearance:none;box-shadow:none;border-radius:unset}input[type="text"]:focus,input[type="date"]:focus{outline:0}.input-underline input[type="text"],.input-underline input[type="date"]{padding:10px;border:0;border-bottom:solid 1px #c9c9c9;transition:border .3s}.input-underline input[type="text"]:focus,.input-underline input[type="text"].focus,.input-underline input[type="date"]:focus,.input-underline input[type="date"].focus{border:0;border-bottom:solid 1px #969696}.box-shadow{box-shadow:0 0 0 0 rgba(0,0,0,0.2),0 1px 8px 2px rgba(0,0,0,0.24)}.bgc-alternate:nth-child(odd){background:#f2f2f2}.material-icons{display:inline-flex;align-items:center;justify-content:center;vertical-align:middle}.rotate-m15{-webkit-transform:rotate(-15deg);-moz-transform:rotate(-15deg);-o-transform:rotate(-15deg);-ms-transform:rotate(-15deg);transform:rotate(-15deg)}.rotate-m30{-webkit-transform:rotate(-30deg);-moz-transform:rotate(-30deg);-o-transform:rotate(-30deg);-ms-transform:rotate(-30deg);transform:rotate(-30deg)}.engrave-hfef9cf{text-shadow:0 1px 0 #fef8cf}.ripple1-out:after,.ripple2-out:before{display:inline-block;border-radius:3px;width:3px;height:3px;opacity:.125;-webkit-animation:ripple-out 1s ease-out;-moz-animation:ripple-out 1s ease-out}.ripple1-out:after,.ripple2-out:before{content:'';position:absolute;top:43%;left:50%}@-webkit-keyframes ripple-out{0{box-shadow:0 0 0 0 transparent,0 0 0 0 transparent,0 0 0 0 transparent,0 0 0 0 transparent}15%{box-shadow:0 0 0 0 #fff,0 0 0 0 #000,0 0 0 0 #fff,0 0 0 0 #000}100%{box-shadow:0 0 30px 15.55556px #fff,0 0 .3px 18.66667px #000,0 0 5px 21.78988px #fff,0 0 1.75px 28px #000}}@-moz-keyframes ripple-out{0{box-shadow:0 0 0 0 transparent,0 0 0 0 transparent,0 0 0 0 transparent,0 0 0 0 transparent}15%{box-shadow:0 0 0 0 #fff,0 0 0 0 #000,0 0 0 0 #fff,0 0 0 0 #000}100%{box-shadow:0 0 30px 15.55556px #fff,0 0 .3px 18.66667px #000,0 0 5px 21.78988px #fff,0 0 1.75px 28px #000}}.ripple1-in:after,.ripple2-in:before{display:inline-block;border-radius:3px;width:3px;height:3px;opacity:.125;-webkit-animation:ripple-in 1s ease-out;-moz-animation:ripple-in 1s ease-out}.ripple1-in:after,.ripple2-in:before{content:'';position:absolute;top:43%;left:50%}@-webkit-keyframes ripple-in{0{box-shadow:0 0 0 0 transparent,0 0 0 0 transparent,0 0 0 0 transparent,0 0 0 0 transparent}15%{box-shadow:0 0 0 0 #fff,0 0 0 0 #000,0 0 0 0 #fff,0 0 0 0 #000}100%{box-shadow:0 0 30px 15.55556px #fff,0 0 .3px 18.66667px #000,0 0 5px 21.78988px #fff,0 0 1.75px 28px #000}}@-moz-keyframes ripple-in{0{box-shadow:0 0 0 0 transparent,0 0 0 0 transparent,0 0 0 0 transparent,0 0 0 0 transparent}15%{box-shadow:0 0 0 0 #fff,0 0 0 0 #000,0 0 0 0 #fff,0 0 0 0 #000}100%{box-shadow:0 0 30px 15.55556px #fff,0 0 .3px 18.66667px #000,0 0 5px 21.78988px #fff,0 0 1.75px 28px #000}}.font-size-1{font-size:.0625rem}.padding-bottom-30{padding-bottom:1.875rem}.margin-bottom-10{margin-bottom:.625rem}.white{color:white}.border-radius-3{border-radius:.1875rem}.margin-top-148{margin-top:9.25rem}.margin-0-auto{margin:0rem auto}.margin-top-35{margin-top:2.1875rem}.bgc-hf2f2f2{background-color:#f2f2f2}.font-size-48-i{font-size:3rem !important}.font-size-55-i{font-size:3.4375rem !important}.text-align-center{text-align:center}.margin-top-15{margin-top:.9375rem}.font-size-18-i{font-size:1.125rem !important}.cursor-pointer{cursor:pointer}.bgc-h52a9b8{background-color:#52a9b8}.float-right{float:right}.border-radius-5{border-radius:.3125rem}.black{color:black}.h4b4513{color:#4b4513}.margin-top-10{margin-top:.625rem}.bgc-black{background-color:black}.line-height-2rem{line-height:2rem}.font-size-14{font-size:.875rem}.margin-top-69{margin-top:4.3125rem}.line-height-9{line-height:.5625rem}.position-relative{position:relative}.margin-top-16{margin-top:1rem}.bold{font-weight:bold}.padding-top-20{padding-top:1.25rem}.bgc-h5276b8{background-color:#5276b8}.margin-top-20-i{margin-top:1.25rem !important}.max-width-100p{max-width:100%}.bgc-h31466e{background-color:#31466e}.bgc-h76b852{background-color:#76b852}.margin-top-20{margin-top:1.25rem}.font-size-16{font-size:1rem}.hb85276{color:#b85276}.margin-15-0-20-0{margin:.9375rem 0rem 1.25rem 0rem}.bgc-hfde942{background-color:#fde942}.margin-left-7{margin-left:.4375rem}.width-93p{width:93%}.font-size-18{font-size:1.125rem}.padding-20{padding:1.25rem}.bgc-white{background-color:white}.inline-block{display:inline-block}.font-size-36-i{font-size:2.25rem !important}.padding-16-s-i{padding:1rem !important}@media only screen and (max-width:64rem){.padding-16-s-i{padding:.9588rem !important}}@media only screen and (max-width:45rem){.padding-16-s-i{padding:.8889rem !important}}@media only screen and (max-width:30rem){.padding-16-s-i{padding:.8rem !important}}.font-size-36-s{font-size:2.25rem}@media only screen and (max-width:64rem){.font-size-36-s{font-size:2.1572rem}}@media only screen and (max-width:45rem){.font-size-36-s{font-size:2rem}}@media only screen and (max-width:30rem){.font-size-36-s{font-size:1.8rem}}@media only screen and (max-width:64.0625rem){.display-xlarge-up-i{display:none !important}}@media only screen and (max-width:45.0625rem){.display-large-up-i{display:none !important}}.font-size-32-s{font-size:2rem}@media only screen and (max-width:64rem){.font-size-32-s{font-size:1.9175rem}}@media only screen and (max-width:45rem){.font-size-32-s{font-size:1.7778rem}}@media only screen and (max-width:30rem){.font-size-32-s{font-size:1.6rem}}@media only screen and (max-width:45.0625rem){.display-large-up{display:none}}.font-size-20-s{font-size:1.25rem}@media only screen and (max-width:64rem){.font-size-20-s{font-size:1.1985rem}}@media only screen and (max-width:45rem){.font-size-20-s{font-size:1.1111rem}}@media only screen and (max-width:30rem){.font-size-20-s{font-size:1rem}}.font-size-40-s{font-size:2.5rem}@media only screen and (max-width:64rem){.font-size-40-s{font-size:2.3969rem}}@media only screen and (max-width:45rem){.font-size-40-s{font-size:2.2222rem}}@media only screen and (max-width:30rem){.font-size-40-s{font-size:2rem}}@font-face{font-family:'Material Icons';font-style:normal;font-weight:400;src:local("Material Icons"),local("MaterialIcons-Regular"),url(https://fonts.gstatic.com/s/materialicons/v21/2fcrYFNaTjcS6g4U3t-Y5ZjZjT5FdEJ140U2DJYC3mY.woff2) format("woff2")}.material-icons{font-family:'Material Icons';font-weight:normal;font-style:normal;font-size:24px;line-height:1;letter-spacing:normal;text-transform:none;display:inline-block;white-space:nowrap;word-wrap:normal;direction:ltr;-webkit-font-feature-settings:'liga';-webkit-font-smoothing:antialiased} -------------------------------------------------------------------------------- /static/css/processed/combined.min.css.gz: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nueverest/vue_flask/48ffccb2b028ab3bceb70640aa575d63c79a21d8/static/css/processed/combined.min.css.gz -------------------------------------------------------------------------------- /static/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nueverest/vue_flask/48ffccb2b028ab3bceb70640aa575d63c79a21d8/static/favicon.ico -------------------------------------------------------------------------------- /static/js/vendor/production.js.gz: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nueverest/vue_flask/48ffccb2b028ab3bceb70640aa575d63c79a21d8/static/js/vendor/production.js.gz -------------------------------------------------------------------------------- /static/js/vendor/vue.min.js: -------------------------------------------------------------------------------- 1 | /*! 2 | * Vue.js v2.2.1 3 | * (c) 2014-2017 Evan You 4 | * Released under the MIT License. 5 | * Source: https://unpkg.com/vue@2.2.1/dist/ 6 | */ 7 | !function(e,t){"object"==typeof exports&&"undefined"!=typeof module?module.exports=t():"function"==typeof define&&define.amd?define(t):e.Vue=t()}(this,function(){"use strict";function e(e){return null==e?"":"object"==typeof e?JSON.stringify(e,null,2):String(e)}function t(e){var t=parseFloat(e);return isNaN(t)?e:t}function n(e,t){for(var n=Object.create(null),r=e.split(","),i=0;i-1)return e.splice(n,1)}}function i(e,t){return Ei.call(e,t)}function o(e){return"string"==typeof e||"number"==typeof e}function a(e){var t=Object.create(null);return function(n){var r=t[n];return r||(t[n]=e(n))}}function s(e,t){function n(n){var r=arguments.length;return r?r>1?e.apply(t,arguments):e.call(t,n):e.call(t)}return n._length=e.length,n}function c(e,t){t=t||0;for(var n=e.length-t,r=new Array(n);n--;)r[n]=e[n+t];return r}function u(e,t){for(var n in t)e[n]=t[n];return e}function l(e){return null!==e&&"object"==typeof e}function f(e){return Mi.call(e)===Pi}function p(e){for(var t={},n=0;n1?c(n):n;for(var r=c(arguments,1),i=0,o=n.length;i=0&&bo[n].id>e.id;)n--;bo.splice(Math.max(n,xo)+1,0,e)}else bo.push(e);wo||(wo=!0,Xi(he))}}function ge(e){Oo.clear(),ye(e,Oo)}function ye(e,t){var n,r,i=Array.isArray(e);if((i||l(e))&&Object.isExtensible(e)){if(e.__ob__){var o=e.__ob__.dep.id;if(t.has(o))return;t.add(o)}if(i)for(n=e.length;n--;)ye(e[n],t);else for(r=Object.keys(e),n=r.length;n--;)ye(e[r[n]],t)}}function _e(e,t,n){So.get=function(){return this[t][n]},So.set=function(e){this[t][n]=e},Object.defineProperty(e,n,So)}function be(e){e._watchers=[];var t=e.$options;t.props&&$e(e,t.props),t.methods&&Ae(e,t.methods),t.data?we(e):A(e._data={},!0),t.computed&&Ce(e,t.computed),t.watch&&Oe(e,t.watch)}function $e(e,t){var n=e.$options.propsData||{},r=e._props={},i=e.$options._propKeys=[],o=!e.$parent;uo.shouldConvert=o;var a=function(o){i.push(o);var a=R(o,t,n,e);O(r,o,a),o in e||_e(e,"_props",o)};for(var s in t)a(s);uo.shouldConvert=!0}function we(e){var t=e.$options.data;t=e._data="function"==typeof t?t.call(e):t||{},f(t)||(t={});for(var n=Object.keys(t),r=e.$options.props,o=n.length;o--;)r&&i(r,n[o])||_(n[o])||_e(e,"_data",n[o]);A(t,!0)}function Ce(e,t){var n=e._computedWatchers=Object.create(null);for(var r in t){var i=t[r],o="function"==typeof i?i:i.get;n[r]=new Ao(e,o,d,To),r in e||xe(e,r,i)}}function xe(e,t,n){"function"==typeof n?(So.get=ke(t),So.set=d):(So.get=n.get?n.cache!==!1?ke(t):n.get:d,So.set=n.set?n.set:d),Object.defineProperty(e,t,So)}function ke(e){return function(){var t=this._computedWatchers&&this._computedWatchers[e];if(t)return t.dirty&&t.evaluate(),io.target&&t.depend(),t.value}}function Ae(e,t){e.$options.props;for(var n in t)e[n]=null==t[n]?d:s(t[n],e)}function Oe(e,t){for(var n in t){var r=t[n];if(Array.isArray(r))for(var i=0;i-1:e instanceof RegExp&&e.test(t)}function yt(e,t){for(var n in e){var r=e[n];if(r){var i=mt(r.componentOptions);i&&!t(i)&&(_t(r),e[n]=null)}}}function _t(e){e&&(e.componentInstance._inactive||de(e.componentInstance,"deactivated"),e.componentInstance.$destroy())}function bt(e){var t={};t.get=function(){return Hi},Object.defineProperty(e,"config",t),e.util={warn:no,extend:u,mergeOptions:M,defineReactive:O},e.set=S,e.delete=T,e.nextTick=Xi,e.options=Object.create(null),Hi._assetTypes.forEach(function(t){e.options[t+"s"]=Object.create(null)}),e.options._base=e,u(e.options.components,Po),lt(e),ft(e),pt(e),ht(e)}function $t(e){for(var t=e.data,n=e,r=e;r.componentInstance;)r=r.componentInstance._vnode,r.data&&(t=wt(r.data,t));for(;n=n.parent;)n.data&&(t=wt(t,n.data));return Ct(t)}function wt(e,t){return{staticClass:xt(e.staticClass,t.staticClass),class:e.class?[e.class,t.class]:t.class}}function Ct(e){var t=e.class,n=e.staticClass;return n||t?xt(n,kt(t)):""}function xt(e,t){return e?t?e+" "+t:e:t||""}function kt(e){var t="";if(!e)return t;if("string"==typeof e)return e;if(Array.isArray(e)){for(var n,r=0,i=e.length;r-1?aa[e]=t.constructor===window.HTMLUnknownElement||t.constructor===window.HTMLElement:aa[e]=/HTMLUnknownElement/.test(t.toString())}function St(e){if("string"==typeof e){var t=document.querySelector(e);return t?t:document.createElement("div")}return e}function Tt(e,t){var n=document.createElement(e);return"select"!==e?n:(t.data&&t.data.attrs&&void 0!==t.data.attrs.multiple&&n.setAttribute("multiple","multiple"),n)}function Et(e,t){return document.createElementNS(ta[e],t)}function jt(e){return document.createTextNode(e)}function Nt(e){return document.createComment(e)}function It(e,t,n){e.insertBefore(t,n)}function Lt(e,t){e.removeChild(t)}function Dt(e,t){e.appendChild(t)}function Mt(e){return e.parentNode}function Pt(e){return e.nextSibling}function Rt(e){return e.tagName}function Ft(e,t){e.textContent=t}function Ht(e,t,n){e.setAttribute(t,n)}function Ut(e,t){var n=e.data.ref;if(n){var i=e.context,o=e.componentInstance||e.elm,a=i.$refs;t?Array.isArray(a[n])?r(a[n],o):a[n]===o&&(a[n]=void 0):e.data.refInFor?Array.isArray(a[n])&&a[n].indexOf(o)<0?a[n].push(o):a[n]=[o]:a[n]=o}}function Bt(e){return null==e}function Vt(e){return null!=e}function zt(e,t){return e.key===t.key&&e.tag===t.tag&&e.isComment===t.isComment&&!e.data==!t.data}function Jt(e,t,n){var r,i,o={};for(r=t;r<=n;++r)i=e[r].key,Vt(i)&&(o[i]=r);return o}function Kt(e){function t(e){return new vo(O.tagName(e).toLowerCase(),{},[],void 0,e)}function r(e,t){function n(){0===--n.listeners&&i(e)}return n.listeners=t,n}function i(e){var t=O.parentNode(e);t&&O.removeChild(t,e)}function a(e,t,n,r,i){if(e.isRootInsert=!i,!s(e,t,n,r)){var o=e.data,a=e.children,c=e.tag;Vt(c)?(e.elm=e.ns?O.createElementNS(e.ns,c):O.createElement(c,e),v(e),f(e,a,t),Vt(o)&&d(e,t),l(n,e.elm,r)):e.isComment?(e.elm=O.createComment(e.text),l(n,e.elm,r)):(e.elm=O.createTextNode(e.text),l(n,e.elm,r))}}function s(e,t,n,r){var i=e.data;if(Vt(i)){var o=Vt(e.componentInstance)&&i.keepAlive;if(Vt(i=i.hook)&&Vt(i=i.init)&&i(e,!1,n,r),Vt(e.componentInstance))return c(e,t),o&&u(e,t,n,r),!0}}function c(e,t){e.data.pendingInsert&&t.push.apply(t,e.data.pendingInsert),e.elm=e.componentInstance.$el,p(e)?(d(e,t),v(e)):(Ut(e),t.push(e))}function u(e,t,n,r){for(var i,o=e;o.componentInstance;)if(o=o.componentInstance._vnode,Vt(i=o.data)&&Vt(i=i.transition)){for(i=0;ip?(u=Bt(n[m+1])?null:n[m+1].elm,h(e,u,n,f,m,r)):f>m&&g(e,t,l,p)}function b(e,t,n,r){if(e!==t){if(t.isStatic&&e.isStatic&&t.key===e.key&&(t.isCloned||t.isOnce))return t.elm=e.elm,void(t.componentInstance=e.componentInstance);var i,o=t.data,a=Vt(o);a&&Vt(i=o.hook)&&Vt(i=i.prepatch)&&i(e,t);var s=t.elm=e.elm,c=e.children,u=t.children;if(a&&p(t)){for(i=0;i=0&&(m=e.charAt(h)," "===m);h--);m&&ma.test(m)||(l=!0)}}else void 0===o?(v=i+1,o=e.slice(0,i).trim()):t();if(void 0===o?o=e.slice(0,i).trim():0!==v&&t(),a)for(i=0;i=Ro}function gn(e){return 34===e||39===e}function yn(e){var t=1;for(Bo=Uo;!mn();)if(e=hn(),gn(e))_n(e);else if(91===e&&t++,93===e&&t--,0===t){Vo=Uo;break}}function _n(e){for(var t=e;!mn()&&(e=hn(),e!==t););}function bn(e,t,n){zo=n;var r=t.value,i=t.modifiers,o=e.tag,a=e.attrsMap.type;if("select"===o)Cn(e,r,i);else if("input"===o&&"checkbox"===a)$n(e,r,i);else if("input"===o&&"radio"===a)wn(e,r,i);else if("input"===o||"textarea"===o)xn(e,r,i);else if(!Hi.isReservedTag(o))return pn(e,r,i),!1;return!0}function $n(e,t,n){var r=n&&n.number,i=ln(e,"value")||"null",o=ln(e,"true-value")||"true",a=ln(e,"false-value")||"false";an(e,"checked","Array.isArray("+t+")?_i("+t+","+i+")>-1"+("true"===o?":("+t+")":":_q("+t+","+o+")")),un(e,ya,"var $$a="+t+",$$el=$event.target,$$c=$$el.checked?("+o+"):("+a+");if(Array.isArray($$a)){var $$v="+(r?"_n("+i+")":i)+",$$i=_i($$a,$$v);if($$c){$$i<0&&("+t+"=$$a.concat($$v))}else{$$i>-1&&("+t+"=$$a.slice(0,$$i).concat($$a.slice($$i+1)))}}else{"+t+"=$$c}",null,!0)}function wn(e,t,n){var r=n&&n.number,i=ln(e,"value")||"null";i=r?"_n("+i+")":i,an(e,"checked","_q("+t+","+i+")"),un(e,ya,dn(t,i),null,!0)}function Cn(e,t,n){var r=n&&n.number,i='Array.prototype.filter.call($event.target.options,function(o){return o.selected}).map(function(o){var val = "_value" in o ? o._value : o.value;return '+(r?"_n(val)":"val")+"})",o="$event.target.multiple ? $$selectedVal : $$selectedVal[0]",a="var $$selectedVal = "+i+";";a=a+" "+dn(t,o),un(e,"change",a,null,!0)}function xn(e,t,n){var r=e.attrsMap.type,i=n||{},o=i.lazy,a=i.number,s=i.trim,c=!o&&"range"!==r,u=o?"change":"range"===r?ga:"input",l="$event.target.value";s&&(l="$event.target.value.trim()"),a&&(l="_n("+l+")");var f=dn(t,l);c&&(f="if($event.target.composing)return;"+f),an(e,"value","("+t+")"),un(e,u,f,null,!0),(s||a||"number"===r)&&un(e,"blur","$forceUpdate()")}function kn(e){var t;e[ga]&&(t=zi?"change":"input",e[t]=[].concat(e[ga],e[t]||[]),delete e[ga]),e[ya]&&(t=Zi?"click":"change",e[t]=[].concat(e[ya],e[t]||[]),delete e[ya])}function An(e,t,n,r){if(n){var i=t,o=Jo;t=function(n){var a=1===arguments.length?i(n):i.apply(null,arguments);null!==a&&On(e,t,r,o)}}Jo.addEventListener(e,t,r)}function On(e,t,n,r){(r||Jo).removeEventListener(e,t,n)}function Sn(e,t){if(e.data.on||t.data.on){var n=t.data.on||{},r=e.data.on||{};Jo=t.elm,kn(n),q(n,r,An,On,t.context)}}function Tn(e,t){if(e.data.domProps||t.data.domProps){var n,r,i=t.elm,o=e.data.domProps||{},a=t.data.domProps||{};a.__ob__&&(a=t.data.domProps=u({},a));for(n in o)null==a[n]&&(i[n]="");for(n in a)if(r=a[n],"textContent"!==n&&"innerHTML"!==n||(t.children&&(t.children.length=0),r!==o[n]))if("value"===n){i._value=r;var s=null==r?"":String(r);En(i,t,s)&&(i.value=s)}else i[n]=r}}function En(e,t,n){return!e.composing&&("option"===t.tag||jn(e,n)||Nn(e,n))}function jn(e,t){return document.activeElement!==e&&e.value!==t}function Nn(e,n){var r=e.value,i=e._vModifiers;return i&&i.number||"number"===e.type?t(r)!==t(n):i&&i.trim?r.trim()!==n.trim():r!==n}function In(e){var t=Ln(e.style);return e.staticStyle?u(e.staticStyle,t):t}function Ln(e){return Array.isArray(e)?p(e):"string"==typeof e?$a(e):e}function Dn(e,t){var n,r={};if(t)for(var i=e;i.componentInstance;)i=i.componentInstance._vnode,i.data&&(n=In(i.data))&&u(r,n);(n=In(e.data))&&u(r,n);for(var o=e;o=o.parent;)o.data&&(n=In(o.data))&&u(r,n);return r}function Mn(e,t){var n=t.data,r=e.data;if(n.staticStyle||n.style||r.staticStyle||r.style){var i,o,a=t.elm,s=e.data.staticStyle,c=e.data.style||{},l=s||c,f=Ln(t.data.style)||{};t.data.style=f.__ob__?u({},f):f;var p=Dn(t,!0);for(o in l)null==p[o]&&xa(a,o,"");for(o in p)i=p[o],i!==l[o]&&xa(a,o,null==i?"":i)}}function Pn(e,t){if(t&&(t=t.trim()))if(e.classList)t.indexOf(" ")>-1?t.split(/\s+/).forEach(function(t){return e.classList.add(t)}):e.classList.add(t);else{var n=" "+(e.getAttribute("class")||"")+" ";n.indexOf(" "+t+" ")<0&&e.setAttribute("class",(n+t).trim())}}function Rn(e,t){if(t&&(t=t.trim()))if(e.classList)t.indexOf(" ")>-1?t.split(/\s+/).forEach(function(t){return e.classList.remove(t)}):e.classList.remove(t);else{for(var n=" "+(e.getAttribute("class")||"")+" ",r=" "+t+" ";n.indexOf(r)>=0;)n=n.replace(r," ");e.setAttribute("class",n.trim())}}function Fn(e){if(e){if("object"==typeof e){var t={};return e.css!==!1&&u(t,Sa(e.name||"v")),u(t,e),t}return"string"==typeof e?Sa(e):void 0}}function Hn(e){Ma(function(){Ma(e)})}function Un(e,t){(e._transitionClasses||(e._transitionClasses=[])).push(t),Pn(e,t)}function Bn(e,t){e._transitionClasses&&r(e._transitionClasses,t),Rn(e,t)}function Vn(e,t,n){var r=zn(e,t),i=r.type,o=r.timeout,a=r.propCount;if(!i)return n();var s=i===Ea?Ia:Da,c=0,u=function(){e.removeEventListener(s,l),n()},l=function(t){t.target===e&&++c>=a&&u()};setTimeout(function(){c0&&(n=Ea,l=a,f=o.length):t===ja?u>0&&(n=ja,l=u,f=c.length):(l=Math.max(a,u),n=l>0?a>u?Ea:ja:null,f=n?n===Ea?o.length:c.length:0);var p=n===Ea&&Pa.test(r[Na+"Property"]);return{type:n,timeout:l,propCount:f,hasTransform:p}}function Jn(e,t){for(;e.length1}function Yn(e,t){t.data.show||qn(t)}function Qn(e,t,n){var r=t.value,i=e.multiple;if(!i||Array.isArray(r)){for(var o,a,s=0,c=e.options.length;s-1,a.selected!==o&&(a.selected=o);else if(h(er(a),r))return void(e.selectedIndex!==s&&(e.selectedIndex=s));i||(e.selectedIndex=-1)}}function Xn(e,t){for(var n=0,r=t.length;n',n.innerHTML.indexOf(t)>0}function vr(e){return Ga=Ga||document.createElement("div"),Ga.innerHTML=e,Ga.textContent}function hr(e,t){var n=t?Ms:Ds;return e.replace(n,function(e){return Ls[e]})}function mr(e,t){function n(t){f+=t,e=e.substring(t)}function r(){var t=e.match(ss);if(t){var r={tagName:t[1],attrs:[],start:f};n(t[0].length);for(var i,o;!(i=e.match(cs))&&(o=e.match(is));)n(o[0].length),r.attrs.push(o);if(i)return r.unarySlash=i[1],n(i[0].length),r.end=f,r}}function i(e){var n=e.tagName,r=e.unarySlash;u&&("p"===s&&es(n)&&o(s),Xa(n)&&s===n&&o(n));for(var i=l(n)||"html"===n&&"head"===s||!!r,a=e.attrs.length,f=new Array(a),p=0;p=0&&c[i].lowerCasedTag!==o;i--);else i=0;if(i>=0){for(var a=c.length-1;a>=i;a--)t.end&&t.end(c[a].tag,n,r);c.length=i,s=i&&c[i-1].tag}else"br"===o?t.start&&t.start(e,[],!0,n,r):"p"===o&&(t.start&&t.start(e,[],!1,n,r),t.end&&t.end(e,n,r))}for(var a,s,c=[],u=t.expectHTML,l=t.isUnaryTag||Ri,f=0;e;){if(a=e,s&&Ns(s)){var p=s.toLowerCase(),d=Is[p]||(Is[p]=new RegExp("([\\s\\S]*?)(]*>)","i")),v=0,h=e.replace(d,function(e,n,r){return v=r.length,"script"!==p&&"style"!==p&&"noscript"!==p&&(n=n.replace(//g,"$1").replace(//g,"$1")),t.chars&&t.chars(n),""});f+=e.length-h.length,e=h,o(p,f-v,f)}else{var m=e.indexOf("<");if(0===m){if(fs.test(e)){var g=e.indexOf("-->");if(g>=0){n(g+3);continue}}if(ps.test(e)){var y=e.indexOf("]>");if(y>=0){n(y+2);continue}}var _=e.match(ls);if(_){n(_[0].length);continue}var b=e.match(us);if(b){var $=f;n(b[0].length),o(b[1],$,f);continue}var w=r();if(w){i(w);continue}}var C=void 0,x=void 0,k=void 0;if(m>=0){for(x=e.slice(m);!(us.test(x)||ss.test(x)||fs.test(x)||ps.test(x)||(k=x.indexOf("<",1),k<0));)m+=k,x=e.slice(m);C=e.substring(0,m),n(m)}m<0&&(C=e,e=""),t.chars&&C&&t.chars(C)}if(e===a){t.chars&&t.chars(e);break}}o()}function gr(e,t){var n=t?Fs(t):Ps;if(n.test(e)){for(var r,i,o=[],a=n.lastIndex=0;r=n.exec(e);){i=r.index,i>a&&o.push(JSON.stringify(e.slice(a,i)));var s=tn(r[1].trim());o.push("_s("+s+")"),a=i+r[0].length}return a0,Ki=Vi&&Vi.indexOf("edge/")>0,qi=Vi&&Vi.indexOf("android")>0,Wi=Vi&&/iphone|ipad|ipod|ios/.test(Vi),Zi=Vi&&/chrome\/\d+/.test(Vi)&&!Ki,Gi=function(){return void 0===Oi&&(Oi=!Bi&&"undefined"!=typeof global&&"server"===global.process.env.VUE_ENV),Oi},Yi=Bi&&window.__VUE_DEVTOOLS_GLOBAL_HOOK__,Qi="undefined"!=typeof Symbol&&y(Symbol)&&"undefined"!=typeof Reflect&&y(Reflect.ownKeys),Xi=function(){function e(){r=!1;var e=n.slice(0);n.length=0;for(var t=0;t1&&(t[n[0].trim()]=n[1].trim())}}),t}),wa=/^--/,Ca=/\s*!important$/,xa=function(e,t,n){wa.test(t)?e.style.setProperty(t,n):Ca.test(n)?e.style.setProperty(t,n.replace(Ca,""),"important"):e.style[Aa(t)]=n},ka=["Webkit","Moz","ms"],Aa=a(function(e){if(Ko=Ko||document.createElement("div"),e=Ni(e),"filter"!==e&&e in Ko.style)return e;for(var t=e.charAt(0).toUpperCase()+e.slice(1),n=0;n\/=]+)/,ns=/(?:=)/,rs=[/"([^"]*)"+/.source,/'([^']*)'+/.source,/([^\s"'=<>`]+)/.source],is=new RegExp("^\\s*"+ts.source+"(?:\\s*("+ns.source+")\\s*(?:"+rs.join("|")+"))?"),os="[a-zA-Z_][\\w\\-\\.]*",as="((?:"+os+"\\:)?"+os+")",ss=new RegExp("^<"+as),cs=/^\s*(\/?)>/,us=new RegExp("^<\\/"+as+"[^>]*>"),ls=/^]+>/i,fs=/^ 49 | Date of Birth 50 | Required 51 | Past date only 52 | 53 | 54 | 55 | 56 |
57 |
58 | US Zip Code 59 | Required 60 | Max Len {[ zipcode_max_length ]} 61 | Example: 85812 62 | 63 |
64 |
65 | 66 |
67 |
68 | 72 |
Population Limit ({[ population_limit ]} max)
73 |
74 |
75 | 76 | 77 | 78 | 79 | 80 |
81 |
People Created
82 |
83 | Total Population: 84 |
85 | {{ persons.length }} 86 |
87 |
88 | 89 |
90 |
91 |
92 |
93 |
94 |
Name
95 |
Age
96 |
 
97 |
98 |
99 |
{{ person.firstname }} {{ person.lastname }}
100 |
{{ person.age }}
101 |
102 | 103 |
104 |
105 |
106 |
107 |

No people exist.

108 | Use the form on the left to add people. 109 |
110 |
111 |
112 |
113 |
114 |
115 |
116 | 117 | 118 |
119 |
Population Table
120 |
121 |
122 |
123 |
124 |
Name
125 |
Age
126 |
Date of Birth
127 |
US Zip Code
128 |
 
129 |
130 |
131 |
{{ person.firstname }} {{ person.lastname }}
132 |
{{ person.age }}
133 |
{{ person.dob }}
134 |
{{ person.zipcode }}
135 |
136 | 137 |
138 |
139 |
140 |
141 |

No people exist.

142 | Use the form on the top-left to add people. 143 |
144 |
145 |
146 |
147 |
148 |
149 | 150 |
151 |
152 |
Author
153 |
Chad Nelson
154 |
155 | 156 |
157 |
Github Account
158 | 159 |
160 | 161 |
162 |
Project Repository
163 | 164 |
165 |
166 | 167 | 168 | 169 | 170 | 342 | 343 | 355 | 356 | -------------------------------------------------------------------------------- /tox.ini: -------------------------------------------------------------------------------- 1 | [tox] 2 | envlist = py36,py27 3 | 4 | [testenv] 5 | passenv = TRAVIS TRAVIS_JOB_ID TRAVIS_BRANCH 6 | 7 | deps= -rtox_requirements.txt 8 | 9 | commands= 10 | python -m unittest discover -s . -p "test_vue_flask.py" 11 | -------------------------------------------------------------------------------- /tox_requirements.txt: -------------------------------------------------------------------------------- 1 | # Python 2.7 Support 2 | future>=0.16.0 3 | 4 | # Flask 5 | Flask>=0.12 6 | Flask-S3>=0.3.3 7 | 8 | # Development 9 | coverage>=4.3.4 10 | coveralls>=1.1 -------------------------------------------------------------------------------- /travis_requirements.txt: -------------------------------------------------------------------------------- 1 | # Python 2.7 Support 2 | future>=0.16.0 3 | 4 | # Flask 5 | Flask>=0.12 6 | Flask-S3>=0.3.3 7 | 8 | # Tox and Travis Interface 9 | tox>=2.3.1 10 | tox-travis>=0.4 11 | 12 | # Development 13 | coverage>=4.3.4 14 | coveralls>=1.1 -------------------------------------------------------------------------------- /unit_tests/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nueverest/vue_flask/48ffccb2b028ab3bceb70640aa575d63c79a21d8/unit_tests/__init__.py -------------------------------------------------------------------------------- /unit_tests/test_deployed_site.py: -------------------------------------------------------------------------------- 1 | # builtins 2 | from unittest import TestCase, main 3 | import requests 4 | from time import sleep 5 | 6 | # plugins 7 | from selenium import webdriver 8 | from selenium.webdriver.common.by import By 9 | from selenium.webdriver.support.ui import WebDriverWait 10 | from selenium.webdriver.support import expected_conditions 11 | from selenium.common.exceptions import TimeoutException 12 | 13 | # application 14 | from vue_flask import get_input_id, get_population_limit 15 | 16 | 17 | class TestDeployedSiteWithRequests(TestCase): 18 | def setUp(self): 19 | self.site = 'https://kw4udfbos9.execute-api.us-west-2.amazonaws.com/production' 20 | 21 | def test_server_is_up_and_running(self): 22 | request = requests.get(self.site) 23 | self.assertEqual(200, request.status_code) 24 | 25 | def test_server_content_type(self): 26 | request = requests.get(self.site) 27 | self.assertEqual('text/html; charset=utf-8', request.headers['Content-Type'], msg=request.headers) 28 | 29 | 30 | class TestDeployedSiteWithSelenium(TestCase): 31 | def setUp(self): 32 | # Reference: http://seleniumhq.github.io/selenium/docs/api/py/ 33 | self.site = 'https://kw4udfbos9.execute-api.us-west-2.amazonaws.com/production' 34 | self.browsers = [ 35 | # webdriver.Chrome(), 36 | webdriver.Firefox(), 37 | # webdriver.Ie() 38 | ] 39 | self.input_id = get_input_id() 40 | self.creationform = self.input_id['creationform'] 41 | self.firstname = self.input_id['firstname'] 42 | self.lastname = self.input_id['lastname'] 43 | self.dob = self.input_id['dob'] 44 | self.zipcode = self.input_id['zipcode'] 45 | self.submit = self.input_id['submit'] 46 | self.population = self.input_id['population'] 47 | 48 | self.population_limit = get_population_limit() 49 | 50 | def tearDown(self): 51 | for browser in self.browsers: 52 | self.addCleanup(browser.quit) 53 | 54 | def create_person(self, browser, input): 55 | element_ids = [self.firstname, self.lastname, self.dob, self.zipcode, ] 56 | 57 | for index, element_id in enumerate(element_ids): 58 | input_element = browser.find_element_by_id(element_id) 59 | input_element.send_keys(input[index]) 60 | 61 | submit_button = browser.find_element_by_id(self.submit) 62 | submit_button.click() 63 | 64 | def delete_person(self, browser): 65 | # Delete first person is list. First in First out. 66 | first_delete_icon = browser.find_element_by_id('0') 67 | first_delete_icon.click() 68 | 69 | def handle_population_limits(self, browser, create=False, delete=False): 70 | """ The population value comes from firebase and is loading by vue. Both of these operations take time. A delay 71 | is required to ensure that the population value is correct since vue sometimes loads the value 0 then changes 72 | it once firebase returns. 73 | 74 | Steps: 75 | Wait for vue and firebase. 76 | Test that population is within limits. 77 | If population is at the min population limit and delete mode increment population. 78 | If population is at the max population limit and create mode decrement population. 79 | Return population value. 80 | """ 81 | population_element = browser.find_element_by_id(self.population) 82 | sleep(1.5) # Wait for vue and firebase lag. 83 | population = int(population_element.text) 84 | 85 | self.assertTrue(0 <= population <= self.population_limit, msg='Population out of bounds.') 86 | 87 | # Pre-Deletion 88 | if population == 0 and delete: 89 | self.create_person(browser, input=['Gerotty', 'Rafan', '1968-10-08', '00760-2341', ]) 90 | return population + 1 91 | 92 | # Pre-Creation 93 | if population == self.population_limit and create: 94 | self.delete_person(browser) 95 | self.assertTrue(self.population_decremented(browser, population)) 96 | return population - 1 97 | 98 | return population 99 | 100 | def population_changed(self, browser, population_expected): 101 | # Reference: http://docs.seleniumhq.org/docs/04_webdriver_advanced.jsp#explicit-waits 102 | # https://github.com/SeleniumHQ/selenium/blob/master/py/selenium/webdriver/support/expected_conditions.py 103 | delay = 5 # in seconds 104 | try: 105 | WebDriverWait(browser, delay).until( 106 | expected_conditions.text_to_be_present_in_element((By.ID, self.population), population_expected) 107 | ) 108 | return True 109 | except TimeoutException: 110 | return False 111 | 112 | def population_decremented(self, browser, population_before): 113 | population_expected = unicode(int(population_before) - 1) 114 | return self.population_changed(browser, population_expected) 115 | 116 | def population_incremented(self, browser, population_before): 117 | population_expected = unicode(int(population_before) + 1) 118 | return self.population_changed(browser, population_expected) 119 | 120 | def test_page_title(self): 121 | for browser in self.browsers: 122 | browser.get(self.site) 123 | self.assertIn('Person Creation Machine', browser.title) 124 | 125 | def test_population_exists(self): 126 | for browser in self.browsers: 127 | browser.get(self.site) 128 | population_element = browser.find_element_by_id(self.population) 129 | population = int(population_element.text) 130 | self.assertTrue(0 <= population <= self.population_limit) 131 | 132 | def test_create_person_form_with_valid_data(self): 133 | for browser in self.browsers: 134 | browser.get(self.site) 135 | population_before = self.handle_population_limits(browser, create=True) 136 | self.create_person(browser, input=['Xython', 'Ber', '1999-09-09', '85000', ]) 137 | self.assertTrue(self.population_incremented(browser, population_before)) 138 | 139 | def test_button_disabled(self): 140 | submit = self.submit 141 | 142 | for browser in self.browsers: 143 | browser.get(self.site) 144 | submit_button = browser.find_element_by_id(submit) 145 | self.assertFalse(submit_button.is_enabled()) 146 | 147 | # def test_create_a_person_form_input_length_exceeded(self): 148 | # pass 149 | # 150 | # def test_create_a_person_form_future_birth_day(self): 151 | # pass 152 | # 153 | # def test_create_a_person_form_invalid_zipcodes(self): 154 | # pass 155 | # 156 | def test_remove_person_top_table(self): 157 | for browser in self.browsers: 158 | browser.get(self.site) 159 | population_before = self.handle_population_limits(browser, delete=True) 160 | self.delete_person(browser) 161 | self.assertTrue(self.population_decremented(browser, population_before)) 162 | 163 | if __name__ == '__main__': 164 | main() -------------------------------------------------------------------------------- /unit_tests/test_vue_flask.py: -------------------------------------------------------------------------------- 1 | # python 2 2 | from builtins import bytes 3 | 4 | # builtins 5 | from unittest import TestCase, main 6 | 7 | # plugins 8 | import flask 9 | 10 | # custom 11 | import vue_flask 12 | 13 | 14 | # Localhost Functionality Test 15 | class TestVueFlask(TestCase): 16 | def setUp(self): 17 | vue_flask.app.config['TESTING'] = True 18 | self.app = vue_flask.app 19 | 20 | def tearDown(self): 21 | pass 22 | 23 | def go(self, path): 24 | return self.app.test_client().get(path) 25 | 26 | def test_server_is_up_and_running(self): 27 | with self.app.test_request_context(): 28 | home = '/' 29 | response = self.go(home) 30 | self.assertTrue(response.status_code == 200) 31 | self.assertTrue(flask.request.path == home) 32 | self.assertTrue(bytes('Person Creation Machine', 'utf-8') in response.data, msg=response.data) 33 | 34 | def test_is_production(self): 35 | with self.app.test_request_context(): 36 | self.assertFalse(vue_flask.is_production()) 37 | 38 | def test_select_url_for_http(self): 39 | with self.app.test_request_context(): 40 | expected = ['/static/favicon.ico', '/static/js/vendor/foundation.min.js'] 41 | actual = [ 42 | vue_flask.select_url_for('static', filename='favicon.ico'), 43 | vue_flask.select_url_for('static', filename='js/vendor/foundation.min.js') 44 | ] 45 | for index, expected_value in enumerate(expected): 46 | self.assertEqual(expected_value, actual[index]) 47 | 48 | def test_select_url_for_https(self): 49 | # refenerence: http://stackoverflow.com/questions/42469831/how-to-test-https-in-flask/42470048#42470048 50 | with self.app.test_request_context(environ_overrides={'wsgi.url_scheme': 'https'}): 51 | expected = [ 52 | 'https://nueverest.s3.amazonaws.com/static/favicon.ico', 53 | 'https://nueverest.s3.amazonaws.com/static/js/vendor/foundation.min.js' 54 | ] 55 | actual = [ 56 | vue_flask.select_url_for('static', filename='favicon.ico'), 57 | vue_flask.select_url_for('static', filename='js/vendor/foundation.min.js') 58 | ] 59 | for index, expected_value in enumerate(expected): 60 | self.assertEqual(expected_value, actual[index]) 61 | 62 | 63 | if __name__ == '__main__': 64 | main() -------------------------------------------------------------------------------- /vue_flask.py: -------------------------------------------------------------------------------- 1 | # python 2 2 | from __future__ import absolute_import, print_function, unicode_literals 3 | 4 | # Flask 5 | from flask import Flask, render_template, request 6 | from flask import url_for as local_url_for 7 | from flask_s3 import FlaskS3 8 | from flask_s3 import url_for as s3_url_for 9 | 10 | # custom 11 | from gzipper import gzipped 12 | 13 | # Ignore missing secrets file for Travis CI, and local development. 14 | try: 15 | from secrets import is_production 16 | except ImportError: 17 | def is_production(): 18 | return False 19 | 20 | 21 | class CustomFlask(Flask): 22 | """ 23 | Override Jinja2 default delimiters. {{ }} --> {[ ]} 24 | Reference: https://gist.github.com/lost-theory/3925738 25 | """ 26 | jinja_options = Flask.jinja_options.copy() 27 | jinja_options.update(dict( 28 | block_start_string='{%', 29 | block_end_string='%}', 30 | variable_start_string='{[', 31 | variable_end_string=']}', 32 | comment_start_string='{#', 33 | comment_end_string='#}', 34 | )) 35 | 36 | 37 | # Configuration Options 38 | class Config(object): 39 | DEBUG = False 40 | TESTING = False 41 | FLASKS3_BUCKET_NAME = 'nueverest' 42 | FLASKS3_USE_HTTPS = True 43 | USE_S3_DEBUG = False 44 | 45 | 46 | class Production(Config): 47 | pass 48 | 49 | 50 | class Development(Config): 51 | DEBUG = True 52 | USE_S3_DEBUG = True 53 | 54 | 55 | class Testing(Config): 56 | TESTING = True 57 | 58 | 59 | # Initialize Application 60 | app = CustomFlask(__name__) 61 | app.config.from_object(Production) if is_production() else app.config.from_object(Development) 62 | s3 = FlaskS3(app) 63 | 64 | 65 | @app.route('/') 66 | @gzipped 67 | def index(): 68 | url_for = { 69 | 'favicon': select_url_for('static', filename='favicon.ico'), 70 | 'combinedcss': select_url_for('static', filename=get_css_filename()), 71 | 'combinedjs': select_url_for('static', filename=get_js_filename()), 72 | # 'materialicons': 'https://fonts.googleapis.com/icon?family=Material+Icons', 73 | # 'vuejs': 'https://unpkg.com/vue@2.0.7/dist/vue.js', 74 | # 'firebase': 'https://www.gstatic.com/firebasejs/3.6.10/firebase.js', 75 | # 'vuefire': 'https://unpkg.com/vuefire@1.3.0/dist/vuefire.js', 76 | } 77 | 78 | input_id = get_input_id() 79 | name_max_length = 100 80 | zipcode_max_length = 10 81 | 82 | return render_template( 83 | 'index.html', 84 | url_for=url_for, 85 | input_id=input_id, 86 | name_max_length=name_max_length, 87 | zipcode_max_length=zipcode_max_length, 88 | population_limit=get_population_limit(), 89 | ) 90 | 91 | 92 | def get_css_filename(): 93 | return 'css/combined1.min.css.gz' if is_production() else 'css/processed/combined.min.css' 94 | 95 | 96 | def get_js_filename(): 97 | return 'js/vendor/production.js.gz' if is_production() else 'js/vendor/dev.js' 98 | 99 | 100 | def get_input_id(): 101 | return { 102 | 'creationform': 'creationform', 103 | 'firstname': 'firstname', 104 | 'lastname': 'lastname', 105 | 'dob': 'dob', 106 | 'zipcode': 'zipcode', 107 | 'submit': 'submit', 108 | 'population': 'population', 109 | } 110 | 111 | 112 | def get_population_limit(): 113 | return 18 114 | 115 | 116 | def select_url_for(endpoint, filename): 117 | """ Select the static media url based on the server url. 118 | 119 | :param endpoint: (string) Media folder 120 | :param filename: (string) Media filename including subpath if necessary e.g. 'css/main.css', 'flower.jpg' 121 | :return: (string) Local url sub-path or full S3 url for media. 122 | """ 123 | if is_production() or request.is_secure: 124 | return s3_url_for(endpoint=endpoint, filename=filename) 125 | 126 | return local_url_for(endpoint=endpoint, filename=filename) 127 | 128 | 129 | if __name__ == '__main__': 130 | app.run() 131 | -------------------------------------------------------------------------------- /zappa_settings.json: -------------------------------------------------------------------------------- 1 | { 2 | "production": { 3 | "s3_bucket": "nueverest", 4 | "aws_region": "us-west-2", 5 | "app_function": "vue_flask.app", 6 | "delete_local_zip": true, 7 | "exclude": [".idea", "archive", "deployed", "static", "unit_tests", "*.scss", "*.css", "*.js", "*.json", "*.csv", "*.ico", "*.md"] 8 | } 9 | } --------------------------------------------------------------------------------