├── .checkignore ├── .gitignore ├── .travis.yml ├── LICENSE ├── MANIFEST.in ├── Makefile ├── README.md ├── flask_extras ├── __init__.py ├── decorators.py ├── filters │ ├── __init__.py │ ├── config.py │ ├── datetimes.py │ ├── filters.py │ ├── layout.py │ ├── munging.py │ └── random.py ├── forms │ ├── __init__.py │ ├── validators │ │ ├── __init__.py │ │ ├── network.py │ │ └── serialization.py │ └── wizard.py ├── macros │ ├── __init__.py │ ├── bootstrap.html │ ├── content_blocks.html │ ├── dates.html │ ├── extras_code.html │ ├── extras_msg.html │ ├── macros.html │ └── utils.html └── views │ ├── __init__.py │ └── statuses.py ├── macros.md ├── setup.py ├── test_app ├── app.py ├── static │ ├── bootstrap.min.css │ ├── bootstrap.min.js │ ├── font-awesome.min.css │ ├── fonts │ │ ├── FontAwesome.otf │ │ ├── fontawesome-webfont.eot │ │ ├── fontawesome-webfont.svg │ │ ├── fontawesome-webfont.ttf │ │ ├── fontawesome-webfont.woff │ │ └── fontawesome-webfont.woff2 │ ├── glyphicons-halflings-regular.eot │ ├── glyphicons-halflings-regular.svg │ ├── glyphicons-halflings-regular.ttf │ ├── glyphicons-halflings-regular.woff │ ├── glyphicons-halflings-regular.woff2 │ └── jquery-2.2.4.min.js └── templates │ ├── layouts │ └── base.html │ └── pages │ ├── bootstrap.html │ ├── content_blocks.html │ ├── dates.html │ ├── extras_code.html │ ├── extras_msg.html │ ├── index.html │ ├── macros.html │ └── utils.html ├── tests ├── __init__.py ├── conftest.py ├── test_config.py ├── test_datetimes.py ├── test_decorators.py ├── test_filters.py ├── test_layout.py ├── test_munging.py ├── test_random.py ├── test_validators_network.py ├── test_validators_serialization.py └── test_wizard.py ├── tox.ini └── wiki └── old_setup.md /.checkignore: -------------------------------------------------------------------------------- 1 | # Ignore folder content 2 | tests/* 3 | 4 | # Ignore file in all folders 5 | tests/**/test_*.py 6 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # ---------- Misc --------- # 2 | 3 | *.sublime-* 4 | *.log 5 | *.pyc 6 | settings_dev.py 7 | *.db 8 | node_modules/ 9 | /**/node_modules/ 10 | /**/*.log 11 | ignored 12 | .DS_Store 13 | .idea 14 | *.log 15 | src/ensemble-app 16 | __pycache__ 17 | 18 | # Byte-compiled / optimized / DLL files 19 | __pycache__/ 20 | *.py[cod] 21 | *$py.class 22 | 23 | python3* 24 | python2.* 25 | 26 | # C extensions 27 | *.so 28 | 29 | # Distribution / packaging 30 | .Python 31 | env/ 32 | build/ 33 | develop-eggs/ 34 | ./dist/ 35 | downloads/ 36 | eggs/ 37 | .eggs/ 38 | lib/ 39 | lib64/ 40 | parts/ 41 | sdist/ 42 | var/ 43 | *.egg-info/ 44 | .installed.cfg 45 | *.egg 46 | 47 | # PyInstaller 48 | # Usually these files are written by a python script from a template 49 | # before PyInstaller builds the exe, so as to inject date/other infos into it. 50 | *.manifest 51 | *.spec 52 | 53 | # Installer logs 54 | pip-log.txt 55 | pip-delete-this-directory.txt 56 | 57 | # Unit test / coverage reports 58 | htmlcov/ 59 | .tox/ 60 | .coverage 61 | .coverage.* 62 | .cache 63 | nosetests.xml 64 | coverage.xml 65 | *,cover 66 | .hypothesis/ 67 | 68 | # Translations 69 | *.mo 70 | *.pot 71 | 72 | # Django stuff: 73 | *.log 74 | local_settings.py 75 | 76 | # Flask stuff: 77 | instance/ 78 | .webassets-cache 79 | 80 | # Scrapy stuff: 81 | .scrapy 82 | 83 | # Sphinx documentation 84 | docs/_build/ 85 | 86 | # PyBuilder 87 | target/ 88 | 89 | # IPython Notebook 90 | .ipynb_checkpoints 91 | 92 | # pyenv 93 | .python-version 94 | 95 | # celery beat schedule file 96 | celerybeat-schedule 97 | 98 | # dotenv 99 | .env 100 | 101 | # virtualenv 102 | venv/ 103 | ENV/ 104 | 105 | # Spyder project settings 106 | .spyderproject 107 | 108 | # Rope project settings 109 | .ropeproject 110 | 111 | # ---------- Virtual Env --------- # 112 | bin/ 113 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | test: 2 | script 3 | sudo: false 4 | python: 5 | - "2.7" 6 | language: python 7 | install: 8 | - pip install -U pytest 9 | - python setup.py install 10 | script: pytest tests 11 | after_install: 12 | - pip install pytest-cov 13 | - pytest tests --cov=flask_jsondash tests 14 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2016 Chris Tabor 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 | -------------------------------------------------------------------------------- /MANIFEST.in: -------------------------------------------------------------------------------- 1 | graft flask_extras 2 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | test: 2 | nosetests -w tests 3 | all: test 4 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | [](https://www.codacy.com/app/dxdstudio/flask_extras?utm_source=github.com&utm_medium=referral&utm_content=christabor/flask_extras&utm_campaign=badger) 2 | [](https://travis-ci.org/christabor/flask_extras) 3 | [](https://scrutinizer-ci.com/g/christabor/flask_extras/?branch=master) 4 | [](https://codeclimate.com/github/christabor/flask_extras) 5 | [](https://coveralls.io/github/christabor/flask_extras?branch=master) 6 | [](https://landscape.io/github/christabor/flask_extras/master) 7 | 8 | # Flask Extras 9 | Assorted useful flask views, blueprints, Jinja2 template filters, and templates/macros. 10 | 11 | ## Overall setup 12 | 13 | As of `3.4.0`, filters and templates will automatically be registered and available through the following simple command: 14 | 15 | ```python 16 | from flask_extras import FlaskExtras 17 | app = Flask('myapp') 18 | FlaskExtras(app) 19 | ``` 20 | 21 | For the old way, check out [this page](wiki/old_setup.md) 22 | 23 | ## Available features 24 | 25 | ### Views 26 | 27 | Import them like usual: 28 | 29 | ```python 30 | from flask_extras.views import ( 31 | statuses, 32 | ) 33 | ``` 34 | 35 | *Note:* each view must have a valid template in your apps templates dir. See each view for the required names and locations. 36 | 37 | *Note:* each view has configuration helpers to inject or configure your app. See source for details. 38 | 39 | ### Macros 40 | 41 | All macro DEMOS are available by cloning this repo and firing up the flask test server, e.g: 42 | 43 | ```shell 44 | git clone https://github.com/christabor/flask_extras 45 | cd flask_extras 46 | virtualenv env 47 | source env/bin/active 48 | python setup.py install 49 | cd test_app 50 | python app.py 51 | open http://localhost:5014 52 | ``` 53 | 54 | #### Philosophy 55 | 56 | Many macros leverage the structure of data to find a common mapping. For example, a dictionary looks a lot like a definition list (dl), and a list looks really like a ...list, in html terms. But stepping even further into things, more complex data structures can have fairly elegant mappings when using macros to hide away a lot of html cruft. 57 | 58 | This makes rendering complex server side data easy, without having to do lots of transformation. This won't work for cases where every last DOM element need to be stylized, but many users will find incredibly powerful tools available to make UI development much easier. 59 | 60 | **Many more macros** are available. You can use them like so: 61 | 62 | ```html 63 | {% from 'macros.html' import list_group, objects2table %} 64 | ``` 65 | 66 | For the most comprehensive docs, check out each [macro](flask_extras/macros/). Comment "docstrings" are inline using jinja2 comments (these are not rendered in your html). 67 | 68 | Also, check the source and/or output to see what classes are available for style overrides. 69 | 70 | ### Statuses 71 | 72 | Provides views for common status codes. Usage: 73 | 74 | ```python 75 | app = statuses.inject_error_views(app) 76 | ``` 77 | 78 | See source for more. 79 | 80 | ### Decorators 81 | 82 | See the source for more. Usage example: 83 | 84 | ```python 85 | from flask_extras.decorators import require_headers 86 | 87 | app.route('/') 88 | @require_headers(['X-Foo']) 89 | def foo(): 90 | pass 91 | ``` 92 | 93 | 94 | ### Forms 95 | 96 | #### WTForm Multi-step wizard 97 | 98 | A WTForm extension for handling an arbitrary number of separate forms as a single, multi-step, multi-POST wizard. All state and data are handled by apps' session backend. Building forms is just like you're used to -- simple and intuitive. Just inherit the `MultiStepWizard` class and put a `__forms__` key on it, which is just a list of all the forms you want to use. *Note*: list order matters for your form steps. 99 | 100 | Usage example: 101 | 102 | ```python 103 | from flask.ext.wtf import FlaskForm 104 | 105 | from flask_extras.forms.wizard import MultiStepWizard 106 | 107 | 108 | class MultiStepTest1(FlaskForm): 109 | field1 = StringField(validators=[validators.DataRequired()],) 110 | field2 = IntegerField(validators=[validators.DataRequired()],) 111 | 112 | 113 | class MultiStepTest2(FlaskForm): 114 | field3 = StringField(validators=[validators.DataRequired()],) 115 | field4 = IntegerField(validators=[validators.DataRequired()],) 116 | 117 | 118 | class MyCoolForm(MultiStepWizard): 119 | __forms__ = [ 120 | MultiStepTest1, 121 | MultiStepTest2, 122 | ] 123 | ``` 124 | 125 | and an example route: 126 | 127 | ```python 128 | from forms import MyCoolForm 129 | 130 | @app.route('/', methods=['GET', 'POST']) 131 | def index(): 132 | curr_step = request.args.get('curr_step') 133 | form_kwargs = dict(session_key='mycustomkey') 134 | if curr_step is not None: 135 | form_kwargs.update(curr_step=curr_step) 136 | form = forms.MyCoolForm(**form_kwargs) 137 | kwargs = dict(form=form) 138 | if request.method == 'POST': 139 | if form.validate_on_submit(): 140 | if form.is_complete(): 141 | data = form.alldata(combine_fields=True, flush_after=True) 142 | flash('Form validated and complete! data = {}'.format(data), 143 | 'success') 144 | return jsonify(data) 145 | else: 146 | flash('Great job, but not done yet ({} steps remain!).'.format(form.remaining)) 147 | else: 148 | flash('Invalid form data.', 'error') 149 | return render_template('index.html', **kwargs) 150 | ``` 151 | 152 | and an example html page (using the [wtform_form](flask_extras/macros/macros.html) macro also available): 153 | 154 | ```html 155 | {% if form.is_complete() %} 156 | Complete! 157 | {% else %} 158 |
bar
152 |{{ v }}
161 |Lorem ipsum...
12 | #} 13 | {% for heading, para in data.items() %} 14 | <{{ hsize }} class="{{ apply_classes(heading_classes) }}"> 15 | {{ heading }} 16 | {{ hsize }}> 17 |18 | {{ para }} 19 |
20 | {% endfor %} 21 | {% endmacro -%} 22 | -------------------------------------------------------------------------------- /flask_extras/macros/dates.html: -------------------------------------------------------------------------------- 1 | {%- macro simpledatetime(timestr) %} 2 | {% set t = timestr|str2dt %} 3 | {% if t %}{{ t.strftime('%Y-%m-%d %H:%M:%S') }}{% else %}{{ t }}{% endif %} 4 | {% endmacro -%} 5 | 6 | 7 | {%- macro simpledate(timestr) %} 8 | {% set t = timestr|str2dt %} 9 | {% if t %}{{ t.strftime('%Y-%m-%d') }}{% else %}{{ t }}{% endif %} 10 | {% endmacro -%} 11 | -------------------------------------------------------------------------------- /flask_extras/macros/extras_code.html: -------------------------------------------------------------------------------- 1 | {% from 'utils.html' import apply_dattrs, apply_classes %} 2 | 3 | {%- macro inline_code(code) %} 4 |{{ code }}
5 | {% endmacro -%}
6 |
7 |
8 | {%- macro code(code, lang='json') %}
9 |
10 | {{ code }}
11 |
12 | {% endmacro -%}
13 |
14 |
15 | {%- macro tokenize_code(code, delimiter=' ',
16 | use_pre=True,
17 | use_code=True,
18 | classes=[],
19 | token_classes={},
20 | token_dattrs={},
21 | replacers=[],
22 | wrap_all=True) %}
23 | {#
24 | Convert a code string into a code block with span wrappers for all
25 | matching tokens specified. All values in `token_classes` will be used as classes to apply to each token wrapper.
26 |
27 | Tokens are determined by splitting on `delimiter`.
28 |
29 | Unmatched tokens will still be wrapped in a span unless `wrap_all` is False.
30 |
31 | Usage:
32 | tokenize_code(
33 | 'FOO bar BAZ "quux"',
34 | delimiter=' ',
35 | token_dattrs={'BAZ': {'val': 'foo'}},
36 | token_classes={'FOO': ['foocls']},
37 | replacers=['"'],
38 | wrap_all=True,
39 | )
40 |
41 |
42 |
43 | FOO
44 | bar
45 | BAZ
46 | quux
47 |
48 |
49 | #}
50 | {% if use_pre %}{% endif %}
51 | {% if use_code %}{% endif %}
52 | {% for token in code.split(delimiter) %}
53 | {% if wrap_all %}
54 | {% set tok = token.strip()|cut(replacers) %}
55 | {% set _dattrs = token_dattrs.get(tok, {})%}
56 | {% set _classes = token_classes.get(tok, []) + classes%}
57 | {{ tok }}
61 | {% endif %}
62 | {% endfor %}
63 | {% if use_code %}
{% endif %}
64 | {% if use_pre %}
{% endif %}
65 | {% endmacro -%}
66 |
--------------------------------------------------------------------------------
/flask_extras/macros/extras_msg.html:
--------------------------------------------------------------------------------
1 | {%- macro alert_type(val) %}
2 | {% set types = ['error', 'warning', 'info', 'success'] %}
3 | {%- if val == 'error' -%}alert-danger{% endif %}
4 | {%- if val == 'warning' -%}alert-warning{% endif %}
5 | {%- if val == 'info' -%}alert-info{% endif %}
6 | {%- if val == 'success' -%}alert-success{% endif %}
7 | {%- if val not in types -%}alert-info{% endif %}
8 | {% endmacro -%}
9 |
10 | {%- macro flash_messages(close_btn=True) %}
11 | {% with messages = get_flashed_messages(with_categories=True) %}
12 | {% if messages %}
13 | {% for category, message in messages %}
14 | 21 | {{ category|capitalize }}: 22 | {{ message|safe }} 23 |
24 |{{ header_macros[heading](heading) }} | 248 | {% else %} 249 |{{ heading }} | 250 | {% endif %} 251 | {% endif %} 252 | {% endfor %} 253 | {% endif %} 254 | {% endfor %} 255 | 256 | 257 | {% for obj in objs %} 258 | {% if order %} 259 | {% set obj = obj|sort_dict_keys_from_reflist(order) %} 260 | {% else %} 261 | {% set obj = obj.items() %} 262 | {% endif %} 263 |
---|---|
271 | {# Handle all primary key links, a common occurence #} 272 | {% if pk_link and k == 'id' %} 273 | {{ v }} 274 | {% elif handle_links and v|is_url %} 275 | {{ v }} 276 | {% elif k in field_macros.keys() %} 277 | {# If a field macro is specified by key, call it on this field for arbitrary levels of customization #} 278 | {{ field_macros[k](v) }} 279 | {% else %} 280 | {{ v }} 281 | {% endif %} 282 | | 283 | {% else %} 284 |285 | {% endif %} 286 | {% endif %} 287 | {% endfor %} 288 | |
Error(s) for '{{ field }}':
305 |
186 | a, b, c = 1, 2, 3
187 |
188 | ```
189 |
190 | ### inline_code
191 |
192 | ```jinja2
193 | {{ inline_code('a, b, c = 1, 2, 3') }}
194 | ```
195 |
196 | becomes
197 |
198 | ```html
199 | a, b, c = 1, 2, 3
200 | ```
201 |
202 | ## `messages.html`
203 |
204 | ### flash_messages
205 |
206 | Render a flask messages object inline, including background coloring (using bootstrap) for each status type (e.g. info, warning, error)
207 |
208 | ```jinja2
209 | {{ flash_messages() }}
210 | ```
211 |
212 | ## `content_blocks.html`
213 |
214 | ### dict_heading_blocks
215 |
--------------------------------------------------------------------------------
/setup.py:
--------------------------------------------------------------------------------
1 | """Setup for Flask Extras."""
2 |
3 | from setuptools import setup
4 |
5 | requirements = [
6 | 'Flask-WTF==0.13',
7 | 'Flask==0.10.1',
8 | 'python-dateutil',
9 | 'netaddr',
10 | ]
11 |
12 |
13 | def readme():
14 | """Grab the long README file."""
15 | try:
16 | with open('README.md', 'r') as fobj:
17 | return fobj.read()
18 | except IOError:
19 | try:
20 | with open('README.rst', 'r') as fobj:
21 | return fobj.read()
22 | except IOError:
23 | return __doc__
24 |
25 |
26 | setup(
27 | name='flask_extras',
28 | version='4.0.4',
29 | description=('Assorted useful flask views, blueprints, '
30 | 'Jinja2 template filters, and templates/macros'),
31 | long_description=readme(),
32 | author='Chris Tabor',
33 | author_email='dxdstudio@gmail.com',
34 | url='https://github.com/christabor/flask_extras',
35 | license='MIT',
36 | classifiers=[
37 | 'Topic :: Software Development',
38 | 'Programming Language :: Python :: 2.7',
39 | ],
40 | install_requires=requirements,
41 | package_dir={'flask_extras': 'flask_extras'},
42 | packages=['flask_extras'],
43 | package_data={
44 | 'flask_extras': [
45 | 'macros/*.html',
46 | ],
47 | },
48 | zip_safe=False,
49 | include_package_data=True,
50 | )
51 |
--------------------------------------------------------------------------------
/test_app/app.py:
--------------------------------------------------------------------------------
1 | """A Test app to demonstrate various aspects of flask_extras."""
2 |
3 | from collections import OrderedDict
4 | from datetime import datetime as dt
5 |
6 | from flask import (
7 | Flask,
8 | render_template,
9 | flash,
10 | request,
11 | )
12 |
13 | from flask_wtf import FlaskForm
14 |
15 | from flask_extras import FlaskExtras
16 | from wtforms import (
17 | BooleanField,
18 | IntegerField,
19 | RadioField,
20 | HiddenField,
21 | PasswordField,
22 | SelectField,
23 | SelectMultipleField,
24 | StringField,
25 | SubmitField,
26 | TextAreaField,
27 | validators,
28 | )
29 |
30 | from flask_extras.views import statuses
31 |
32 | app = Flask('flask_extras_test')
33 | app.secret_key = 'abc1234'
34 |
35 | FlaskExtras(app)
36 |
37 |
38 | class SomeForm(FlaskForm):
39 | """Form."""
40 |
41 | hideme = HiddenField()
42 | favorite_food = RadioField(
43 | choices=[('pizza', 'Pizza'), ('ice-cream', 'Ice Cream')]
44 | )
45 | age = IntegerField(validators=[validators.DataRequired()])
46 | name = StringField(
47 | description='enter your name',
48 | validators=[validators.DataRequired()],
49 | )
50 | nickname = StringField('What do people call you?')
51 |
52 |
53 | class SomeForm2(FlaskForm):
54 | """Form."""
55 |
56 | hideme = HiddenField()
57 | frobnicate = BooleanField()
58 | baz = StringField()
59 | quux = SelectMultipleField(
60 | choices=[(v, v) for v in ['quux', 'baz', 'foo']])
61 |
62 |
63 | @app.context_processor
64 | def ctx():
65 | """Add global ctx."""
66 | return dict(
67 | ghub_url='https://github.com/christabor/flask_extras/blob/master/flask_extras/macros/',
68 | name=str(request.url_rule),
69 | links=sorted(get_rulesmap()),
70 | )
71 |
72 |
73 | def get_rulesmap():
74 | """Get all rules so we ensure they're dynamic and always updated."""
75 | bad = ['static', 'index']
76 | return [r.endpoint for r in app.url_map._rules if r.endpoint not in bad]
77 |
78 |
79 | @app.route('/')
80 | def index():
81 | """Demo page links."""
82 | return render_template('pages/index.html', **dict(home=True))
83 |
84 |
85 | @app.route('/extras_msg.html')
86 | def extras_msg():
87 | """Demo page."""
88 | flash('I am a success message!', 'success')
89 | flash('I am warning message!', 'warning')
90 | flash('I am an error (danger) message!', 'error')
91 | flash('I am an info message!', 'info')
92 | return render_template('pages/extras_msg.html')
93 |
94 |
95 | @app.route('/content_blocks.html')
96 | def content_blocks():
97 | """Demo page."""
98 | return render_template('pages/content_blocks.html')
99 |
100 |
101 | @app.route('/extras_code.html')
102 | def extras_code():
103 | """Demo page."""
104 | return render_template('pages/extras_code.html')
105 |
106 |
107 | @app.route('/dates.html')
108 | def dates():
109 | """Demo page."""
110 | kwargs = dict(somedate=dt.now())
111 | return render_template('pages/dates.html', **kwargs)
112 |
113 |
114 | @app.route('/utils.html')
115 | def utils():
116 | """Demo page."""
117 | kwargs = dict()
118 | return render_template('pages/utils.html', **kwargs)
119 |
120 |
121 | @app.route('/macros.html')
122 | def macros():
123 | """Demo page."""
124 | kwargs = dict(
125 | dicttest=dict(
126 | foo='Some bar',
127 | bar='Some foo',
128 | ),
129 | dictlist=[
130 | dict(
131 | foo='Some bar',
132 | bar='Some foo',
133 | )
134 | ],
135 | dictlist2=[
136 | dict(name='foo', age=10, dob='01/01/1900', gender='M'),
137 | dict(name='bar', age=22, dob='01/01/1901', gender='F'),
138 | dict(name='quux', age=120, dob='01/01/1830', gender='X'),
139 | ],
140 | form=SomeForm(),
141 | recursedict=vars(request),
142 | )
143 | return render_template('pages/macros.html', **kwargs)
144 |
145 |
146 | @app.route('/bootstrap.html')
147 | def bootstrap():
148 | """Demo page."""
149 | dicttest = dict(
150 | foo='Some bar',
151 | bar='Some foo',
152 | )
153 | dictlist = [
154 | dict(name='zomb', age=999, dob='03/01/2030', gender='Z102'),
155 | dict(name='foo', age=10, dob='01/01/1900', gender='M'),
156 | dict(name='bar', age=22, dob='01/01/1901', gender='F'),
157 | dict(name='quux', age=120, dob='01/01/1830', gender='X'),
158 | ]
159 | kwargs = dict(
160 | dicttest=dicttest,
161 | dictlist=dictlist,
162 | form=SomeForm(),
163 | form2=SomeForm2(),
164 | pagination=OrderedDict(
165 | zip(['/somelink/{}'.format(i) for i in range(10)], range(10))
166 | ),
167 | )
168 | return render_template('pages/bootstrap.html', **kwargs)
169 |
170 |
171 | if __name__ == '__main__':
172 | app.run(debug=True, host='0.0.0.0', port=5014)
173 |
--------------------------------------------------------------------------------
/test_app/static/font-awesome.min.css:
--------------------------------------------------------------------------------
1 | /*!
2 | * Font Awesome 4.5.0 by @davegandy - http://fontawesome.io - @fontawesome
3 | * License - http://fontawesome.io/license (Font: SIL OFL 1.1, CSS: MIT License)
4 | */@font-face{font-family:'FontAwesome';src:url('fonts/fontawesome-webfont.eot?v=4.5.0');src:url('fonts/fontawesome-webfont.eot?#iefix&v=4.5.0') format('embedded-opentype'),url('fonts/fontawesome-webfont.woff2?v=4.5.0') format('woff2'),url('fonts/fontawesome-webfont.woff?v=4.5.0') format('woff'),url('fonts/fontawesome-webfont.ttf?v=4.5.0') format('truetype'),url('fonts/fontawesome-webfont.svg?v=4.5.0#fontawesomeregular') format('svg');font-weight:normal;font-style:normal}.fa{display:inline-block;font:normal normal normal 14px/1 FontAwesome;font-size:inherit;text-rendering:auto;-webkit-font-smoothing:antialiased;-moz-osx-font-smoothing:grayscale}.fa-lg{font-size:1.33333333em;line-height:.75em;vertical-align:-15%}.fa-2x{font-size:2em}.fa-3x{font-size:3em}.fa-4x{font-size:4em}.fa-5x{font-size:5em}.fa-fw{width:1.28571429em;text-align:center}.fa-ul{padding-left:0;margin-left:2.14285714em;list-style-type:none}.fa-ul>li{position:relative}.fa-li{position:absolute;left:-2.14285714em;width:2.14285714em;top:.14285714em;text-align:center}.fa-li.fa-lg{left:-1.85714286em}.fa-border{padding:.2em .25em .15em;border:solid .08em #eee;border-radius:.1em}.fa-pull-left{float:left}.fa-pull-right{float:right}.fa.fa-pull-left{margin-right:.3em}.fa.fa-pull-right{margin-left:.3em}.pull-right{float:right}.pull-left{float:left}.fa.pull-left{margin-right:.3em}.fa.pull-right{margin-left:.3em}.fa-spin{-webkit-animation:fa-spin 2s infinite linear;animation:fa-spin 2s infinite linear}.fa-pulse{-webkit-animation:fa-spin 1s infinite steps(8);animation:fa-spin 1s infinite steps(8)}@-webkit-keyframes fa-spin{0%{-webkit-transform:rotate(0deg);transform:rotate(0deg)}100%{-webkit-transform:rotate(359deg);transform:rotate(359deg)}}@keyframes fa-spin{0%{-webkit-transform:rotate(0deg);transform:rotate(0deg)}100%{-webkit-transform:rotate(359deg);transform:rotate(359deg)}}.fa-rotate-90{filter:progid:DXImageTransform.Microsoft.BasicImage(rotation=1);-webkit-transform:rotate(90deg);-ms-transform:rotate(90deg);transform:rotate(90deg)}.fa-rotate-180{filter:progid:DXImageTransform.Microsoft.BasicImage(rotation=2);-webkit-transform:rotate(180deg);-ms-transform:rotate(180deg);transform:rotate(180deg)}.fa-rotate-270{filter:progid:DXImageTransform.Microsoft.BasicImage(rotation=3);-webkit-transform:rotate(270deg);-ms-transform:rotate(270deg);transform:rotate(270deg)}.fa-flip-horizontal{filter:progid:DXImageTransform.Microsoft.BasicImage(rotation=0, mirror=1);-webkit-transform:scale(-1, 1);-ms-transform:scale(-1, 1);transform:scale(-1, 1)}.fa-flip-vertical{filter:progid:DXImageTransform.Microsoft.BasicImage(rotation=2, mirror=1);-webkit-transform:scale(1, -1);-ms-transform:scale(1, -1);transform:scale(1, -1)}:root .fa-rotate-90,:root .fa-rotate-180,:root .fa-rotate-270,:root .fa-flip-horizontal,:root .fa-flip-vertical{filter:none}.fa-stack{position:relative;display:inline-block;width:2em;height:2em;line-height:2em;vertical-align:middle}.fa-stack-1x,.fa-stack-2x{position:absolute;left:0;width:100%;text-align:center}.fa-stack-1x{line-height:inherit}.fa-stack-2x{font-size:2em}.fa-inverse{color:#fff}.fa-glass:before{content:"\f000"}.fa-music:before{content:"\f001"}.fa-search:before{content:"\f002"}.fa-envelope-o:before{content:"\f003"}.fa-heart:before{content:"\f004"}.fa-star:before{content:"\f005"}.fa-star-o:before{content:"\f006"}.fa-user:before{content:"\f007"}.fa-film:before{content:"\f008"}.fa-th-large:before{content:"\f009"}.fa-th:before{content:"\f00a"}.fa-th-list:before{content:"\f00b"}.fa-check:before{content:"\f00c"}.fa-remove:before,.fa-close:before,.fa-times:before{content:"\f00d"}.fa-search-plus:before{content:"\f00e"}.fa-search-minus:before{content:"\f010"}.fa-power-off:before{content:"\f011"}.fa-signal:before{content:"\f012"}.fa-gear:before,.fa-cog:before{content:"\f013"}.fa-trash-o:before{content:"\f014"}.fa-home:before{content:"\f015"}.fa-file-o:before{content:"\f016"}.fa-clock-o:before{content:"\f017"}.fa-road:before{content:"\f018"}.fa-download:before{content:"\f019"}.fa-arrow-circle-o-down:before{content:"\f01a"}.fa-arrow-circle-o-up:before{content:"\f01b"}.fa-inbox:before{content:"\f01c"}.fa-play-circle-o:before{content:"\f01d"}.fa-rotate-right:before,.fa-repeat:before{content:"\f01e"}.fa-refresh:before{content:"\f021"}.fa-list-alt:before{content:"\f022"}.fa-lock:before{content:"\f023"}.fa-flag:before{content:"\f024"}.fa-headphones:before{content:"\f025"}.fa-volume-off:before{content:"\f026"}.fa-volume-down:before{content:"\f027"}.fa-volume-up:before{content:"\f028"}.fa-qrcode:before{content:"\f029"}.fa-barcode:before{content:"\f02a"}.fa-tag:before{content:"\f02b"}.fa-tags:before{content:"\f02c"}.fa-book:before{content:"\f02d"}.fa-bookmark:before{content:"\f02e"}.fa-print:before{content:"\f02f"}.fa-camera:before{content:"\f030"}.fa-font:before{content:"\f031"}.fa-bold:before{content:"\f032"}.fa-italic:before{content:"\f033"}.fa-text-height:before{content:"\f034"}.fa-text-width:before{content:"\f035"}.fa-align-left:before{content:"\f036"}.fa-align-center:before{content:"\f037"}.fa-align-right:before{content:"\f038"}.fa-align-justify:before{content:"\f039"}.fa-list:before{content:"\f03a"}.fa-dedent:before,.fa-outdent:before{content:"\f03b"}.fa-indent:before{content:"\f03c"}.fa-video-camera:before{content:"\f03d"}.fa-photo:before,.fa-image:before,.fa-picture-o:before{content:"\f03e"}.fa-pencil:before{content:"\f040"}.fa-map-marker:before{content:"\f041"}.fa-adjust:before{content:"\f042"}.fa-tint:before{content:"\f043"}.fa-edit:before,.fa-pencil-square-o:before{content:"\f044"}.fa-share-square-o:before{content:"\f045"}.fa-check-square-o:before{content:"\f046"}.fa-arrows:before{content:"\f047"}.fa-step-backward:before{content:"\f048"}.fa-fast-backward:before{content:"\f049"}.fa-backward:before{content:"\f04a"}.fa-play:before{content:"\f04b"}.fa-pause:before{content:"\f04c"}.fa-stop:before{content:"\f04d"}.fa-forward:before{content:"\f04e"}.fa-fast-forward:before{content:"\f050"}.fa-step-forward:before{content:"\f051"}.fa-eject:before{content:"\f052"}.fa-chevron-left:before{content:"\f053"}.fa-chevron-right:before{content:"\f054"}.fa-plus-circle:before{content:"\f055"}.fa-minus-circle:before{content:"\f056"}.fa-times-circle:before{content:"\f057"}.fa-check-circle:before{content:"\f058"}.fa-question-circle:before{content:"\f059"}.fa-info-circle:before{content:"\f05a"}.fa-crosshairs:before{content:"\f05b"}.fa-times-circle-o:before{content:"\f05c"}.fa-check-circle-o:before{content:"\f05d"}.fa-ban:before{content:"\f05e"}.fa-arrow-left:before{content:"\f060"}.fa-arrow-right:before{content:"\f061"}.fa-arrow-up:before{content:"\f062"}.fa-arrow-down:before{content:"\f063"}.fa-mail-forward:before,.fa-share:before{content:"\f064"}.fa-expand:before{content:"\f065"}.fa-compress:before{content:"\f066"}.fa-plus:before{content:"\f067"}.fa-minus:before{content:"\f068"}.fa-asterisk:before{content:"\f069"}.fa-exclamation-circle:before{content:"\f06a"}.fa-gift:before{content:"\f06b"}.fa-leaf:before{content:"\f06c"}.fa-fire:before{content:"\f06d"}.fa-eye:before{content:"\f06e"}.fa-eye-slash:before{content:"\f070"}.fa-warning:before,.fa-exclamation-triangle:before{content:"\f071"}.fa-plane:before{content:"\f072"}.fa-calendar:before{content:"\f073"}.fa-random:before{content:"\f074"}.fa-comment:before{content:"\f075"}.fa-magnet:before{content:"\f076"}.fa-chevron-up:before{content:"\f077"}.fa-chevron-down:before{content:"\f078"}.fa-retweet:before{content:"\f079"}.fa-shopping-cart:before{content:"\f07a"}.fa-folder:before{content:"\f07b"}.fa-folder-open:before{content:"\f07c"}.fa-arrows-v:before{content:"\f07d"}.fa-arrows-h:before{content:"\f07e"}.fa-bar-chart-o:before,.fa-bar-chart:before{content:"\f080"}.fa-twitter-square:before{content:"\f081"}.fa-facebook-square:before{content:"\f082"}.fa-camera-retro:before{content:"\f083"}.fa-key:before{content:"\f084"}.fa-gears:before,.fa-cogs:before{content:"\f085"}.fa-comments:before{content:"\f086"}.fa-thumbs-o-up:before{content:"\f087"}.fa-thumbs-o-down:before{content:"\f088"}.fa-star-half:before{content:"\f089"}.fa-heart-o:before{content:"\f08a"}.fa-sign-out:before{content:"\f08b"}.fa-linkedin-square:before{content:"\f08c"}.fa-thumb-tack:before{content:"\f08d"}.fa-external-link:before{content:"\f08e"}.fa-sign-in:before{content:"\f090"}.fa-trophy:before{content:"\f091"}.fa-github-square:before{content:"\f092"}.fa-upload:before{content:"\f093"}.fa-lemon-o:before{content:"\f094"}.fa-phone:before{content:"\f095"}.fa-square-o:before{content:"\f096"}.fa-bookmark-o:before{content:"\f097"}.fa-phone-square:before{content:"\f098"}.fa-twitter:before{content:"\f099"}.fa-facebook-f:before,.fa-facebook:before{content:"\f09a"}.fa-github:before{content:"\f09b"}.fa-unlock:before{content:"\f09c"}.fa-credit-card:before{content:"\f09d"}.fa-feed:before,.fa-rss:before{content:"\f09e"}.fa-hdd-o:before{content:"\f0a0"}.fa-bullhorn:before{content:"\f0a1"}.fa-bell:before{content:"\f0f3"}.fa-certificate:before{content:"\f0a3"}.fa-hand-o-right:before{content:"\f0a4"}.fa-hand-o-left:before{content:"\f0a5"}.fa-hand-o-up:before{content:"\f0a6"}.fa-hand-o-down:before{content:"\f0a7"}.fa-arrow-circle-left:before{content:"\f0a8"}.fa-arrow-circle-right:before{content:"\f0a9"}.fa-arrow-circle-up:before{content:"\f0aa"}.fa-arrow-circle-down:before{content:"\f0ab"}.fa-globe:before{content:"\f0ac"}.fa-wrench:before{content:"\f0ad"}.fa-tasks:before{content:"\f0ae"}.fa-filter:before{content:"\f0b0"}.fa-briefcase:before{content:"\f0b1"}.fa-arrows-alt:before{content:"\f0b2"}.fa-group:before,.fa-users:before{content:"\f0c0"}.fa-chain:before,.fa-link:before{content:"\f0c1"}.fa-cloud:before{content:"\f0c2"}.fa-flask:before{content:"\f0c3"}.fa-cut:before,.fa-scissors:before{content:"\f0c4"}.fa-copy:before,.fa-files-o:before{content:"\f0c5"}.fa-paperclip:before{content:"\f0c6"}.fa-save:before,.fa-floppy-o:before{content:"\f0c7"}.fa-square:before{content:"\f0c8"}.fa-navicon:before,.fa-reorder:before,.fa-bars:before{content:"\f0c9"}.fa-list-ul:before{content:"\f0ca"}.fa-list-ol:before{content:"\f0cb"}.fa-strikethrough:before{content:"\f0cc"}.fa-underline:before{content:"\f0cd"}.fa-table:before{content:"\f0ce"}.fa-magic:before{content:"\f0d0"}.fa-truck:before{content:"\f0d1"}.fa-pinterest:before{content:"\f0d2"}.fa-pinterest-square:before{content:"\f0d3"}.fa-google-plus-square:before{content:"\f0d4"}.fa-google-plus:before{content:"\f0d5"}.fa-money:before{content:"\f0d6"}.fa-caret-down:before{content:"\f0d7"}.fa-caret-up:before{content:"\f0d8"}.fa-caret-left:before{content:"\f0d9"}.fa-caret-right:before{content:"\f0da"}.fa-columns:before{content:"\f0db"}.fa-unsorted:before,.fa-sort:before{content:"\f0dc"}.fa-sort-down:before,.fa-sort-desc:before{content:"\f0dd"}.fa-sort-up:before,.fa-sort-asc:before{content:"\f0de"}.fa-envelope:before{content:"\f0e0"}.fa-linkedin:before{content:"\f0e1"}.fa-rotate-left:before,.fa-undo:before{content:"\f0e2"}.fa-legal:before,.fa-gavel:before{content:"\f0e3"}.fa-dashboard:before,.fa-tachometer:before{content:"\f0e4"}.fa-comment-o:before{content:"\f0e5"}.fa-comments-o:before{content:"\f0e6"}.fa-flash:before,.fa-bolt:before{content:"\f0e7"}.fa-sitemap:before{content:"\f0e8"}.fa-umbrella:before{content:"\f0e9"}.fa-paste:before,.fa-clipboard:before{content:"\f0ea"}.fa-lightbulb-o:before{content:"\f0eb"}.fa-exchange:before{content:"\f0ec"}.fa-cloud-download:before{content:"\f0ed"}.fa-cloud-upload:before{content:"\f0ee"}.fa-user-md:before{content:"\f0f0"}.fa-stethoscope:before{content:"\f0f1"}.fa-suitcase:before{content:"\f0f2"}.fa-bell-o:before{content:"\f0a2"}.fa-coffee:before{content:"\f0f4"}.fa-cutlery:before{content:"\f0f5"}.fa-file-text-o:before{content:"\f0f6"}.fa-building-o:before{content:"\f0f7"}.fa-hospital-o:before{content:"\f0f8"}.fa-ambulance:before{content:"\f0f9"}.fa-medkit:before{content:"\f0fa"}.fa-fighter-jet:before{content:"\f0fb"}.fa-beer:before{content:"\f0fc"}.fa-h-square:before{content:"\f0fd"}.fa-plus-square:before{content:"\f0fe"}.fa-angle-double-left:before{content:"\f100"}.fa-angle-double-right:before{content:"\f101"}.fa-angle-double-up:before{content:"\f102"}.fa-angle-double-down:before{content:"\f103"}.fa-angle-left:before{content:"\f104"}.fa-angle-right:before{content:"\f105"}.fa-angle-up:before{content:"\f106"}.fa-angle-down:before{content:"\f107"}.fa-desktop:before{content:"\f108"}.fa-laptop:before{content:"\f109"}.fa-tablet:before{content:"\f10a"}.fa-mobile-phone:before,.fa-mobile:before{content:"\f10b"}.fa-circle-o:before{content:"\f10c"}.fa-quote-left:before{content:"\f10d"}.fa-quote-right:before{content:"\f10e"}.fa-spinner:before{content:"\f110"}.fa-circle:before{content:"\f111"}.fa-mail-reply:before,.fa-reply:before{content:"\f112"}.fa-github-alt:before{content:"\f113"}.fa-folder-o:before{content:"\f114"}.fa-folder-open-o:before{content:"\f115"}.fa-smile-o:before{content:"\f118"}.fa-frown-o:before{content:"\f119"}.fa-meh-o:before{content:"\f11a"}.fa-gamepad:before{content:"\f11b"}.fa-keyboard-o:before{content:"\f11c"}.fa-flag-o:before{content:"\f11d"}.fa-flag-checkered:before{content:"\f11e"}.fa-terminal:before{content:"\f120"}.fa-code:before{content:"\f121"}.fa-mail-reply-all:before,.fa-reply-all:before{content:"\f122"}.fa-star-half-empty:before,.fa-star-half-full:before,.fa-star-half-o:before{content:"\f123"}.fa-location-arrow:before{content:"\f124"}.fa-crop:before{content:"\f125"}.fa-code-fork:before{content:"\f126"}.fa-unlink:before,.fa-chain-broken:before{content:"\f127"}.fa-question:before{content:"\f128"}.fa-info:before{content:"\f129"}.fa-exclamation:before{content:"\f12a"}.fa-superscript:before{content:"\f12b"}.fa-subscript:before{content:"\f12c"}.fa-eraser:before{content:"\f12d"}.fa-puzzle-piece:before{content:"\f12e"}.fa-microphone:before{content:"\f130"}.fa-microphone-slash:before{content:"\f131"}.fa-shield:before{content:"\f132"}.fa-calendar-o:before{content:"\f133"}.fa-fire-extinguisher:before{content:"\f134"}.fa-rocket:before{content:"\f135"}.fa-maxcdn:before{content:"\f136"}.fa-chevron-circle-left:before{content:"\f137"}.fa-chevron-circle-right:before{content:"\f138"}.fa-chevron-circle-up:before{content:"\f139"}.fa-chevron-circle-down:before{content:"\f13a"}.fa-html5:before{content:"\f13b"}.fa-css3:before{content:"\f13c"}.fa-anchor:before{content:"\f13d"}.fa-unlock-alt:before{content:"\f13e"}.fa-bullseye:before{content:"\f140"}.fa-ellipsis-h:before{content:"\f141"}.fa-ellipsis-v:before{content:"\f142"}.fa-rss-square:before{content:"\f143"}.fa-play-circle:before{content:"\f144"}.fa-ticket:before{content:"\f145"}.fa-minus-square:before{content:"\f146"}.fa-minus-square-o:before{content:"\f147"}.fa-level-up:before{content:"\f148"}.fa-level-down:before{content:"\f149"}.fa-check-square:before{content:"\f14a"}.fa-pencil-square:before{content:"\f14b"}.fa-external-link-square:before{content:"\f14c"}.fa-share-square:before{content:"\f14d"}.fa-compass:before{content:"\f14e"}.fa-toggle-down:before,.fa-caret-square-o-down:before{content:"\f150"}.fa-toggle-up:before,.fa-caret-square-o-up:before{content:"\f151"}.fa-toggle-right:before,.fa-caret-square-o-right:before{content:"\f152"}.fa-euro:before,.fa-eur:before{content:"\f153"}.fa-gbp:before{content:"\f154"}.fa-dollar:before,.fa-usd:before{content:"\f155"}.fa-rupee:before,.fa-inr:before{content:"\f156"}.fa-cny:before,.fa-rmb:before,.fa-yen:before,.fa-jpy:before{content:"\f157"}.fa-ruble:before,.fa-rouble:before,.fa-rub:before{content:"\f158"}.fa-won:before,.fa-krw:before{content:"\f159"}.fa-bitcoin:before,.fa-btc:before{content:"\f15a"}.fa-file:before{content:"\f15b"}.fa-file-text:before{content:"\f15c"}.fa-sort-alpha-asc:before{content:"\f15d"}.fa-sort-alpha-desc:before{content:"\f15e"}.fa-sort-amount-asc:before{content:"\f160"}.fa-sort-amount-desc:before{content:"\f161"}.fa-sort-numeric-asc:before{content:"\f162"}.fa-sort-numeric-desc:before{content:"\f163"}.fa-thumbs-up:before{content:"\f164"}.fa-thumbs-down:before{content:"\f165"}.fa-youtube-square:before{content:"\f166"}.fa-youtube:before{content:"\f167"}.fa-xing:before{content:"\f168"}.fa-xing-square:before{content:"\f169"}.fa-youtube-play:before{content:"\f16a"}.fa-dropbox:before{content:"\f16b"}.fa-stack-overflow:before{content:"\f16c"}.fa-instagram:before{content:"\f16d"}.fa-flickr:before{content:"\f16e"}.fa-adn:before{content:"\f170"}.fa-bitbucket:before{content:"\f171"}.fa-bitbucket-square:before{content:"\f172"}.fa-tumblr:before{content:"\f173"}.fa-tumblr-square:before{content:"\f174"}.fa-long-arrow-down:before{content:"\f175"}.fa-long-arrow-up:before{content:"\f176"}.fa-long-arrow-left:before{content:"\f177"}.fa-long-arrow-right:before{content:"\f178"}.fa-apple:before{content:"\f179"}.fa-windows:before{content:"\f17a"}.fa-android:before{content:"\f17b"}.fa-linux:before{content:"\f17c"}.fa-dribbble:before{content:"\f17d"}.fa-skype:before{content:"\f17e"}.fa-foursquare:before{content:"\f180"}.fa-trello:before{content:"\f181"}.fa-female:before{content:"\f182"}.fa-male:before{content:"\f183"}.fa-gittip:before,.fa-gratipay:before{content:"\f184"}.fa-sun-o:before{content:"\f185"}.fa-moon-o:before{content:"\f186"}.fa-archive:before{content:"\f187"}.fa-bug:before{content:"\f188"}.fa-vk:before{content:"\f189"}.fa-weibo:before{content:"\f18a"}.fa-renren:before{content:"\f18b"}.fa-pagelines:before{content:"\f18c"}.fa-stack-exchange:before{content:"\f18d"}.fa-arrow-circle-o-right:before{content:"\f18e"}.fa-arrow-circle-o-left:before{content:"\f190"}.fa-toggle-left:before,.fa-caret-square-o-left:before{content:"\f191"}.fa-dot-circle-o:before{content:"\f192"}.fa-wheelchair:before{content:"\f193"}.fa-vimeo-square:before{content:"\f194"}.fa-turkish-lira:before,.fa-try:before{content:"\f195"}.fa-plus-square-o:before{content:"\f196"}.fa-space-shuttle:before{content:"\f197"}.fa-slack:before{content:"\f198"}.fa-envelope-square:before{content:"\f199"}.fa-wordpress:before{content:"\f19a"}.fa-openid:before{content:"\f19b"}.fa-institution:before,.fa-bank:before,.fa-university:before{content:"\f19c"}.fa-mortar-board:before,.fa-graduation-cap:before{content:"\f19d"}.fa-yahoo:before{content:"\f19e"}.fa-google:before{content:"\f1a0"}.fa-reddit:before{content:"\f1a1"}.fa-reddit-square:before{content:"\f1a2"}.fa-stumbleupon-circle:before{content:"\f1a3"}.fa-stumbleupon:before{content:"\f1a4"}.fa-delicious:before{content:"\f1a5"}.fa-digg:before{content:"\f1a6"}.fa-pied-piper:before{content:"\f1a7"}.fa-pied-piper-alt:before{content:"\f1a8"}.fa-drupal:before{content:"\f1a9"}.fa-joomla:before{content:"\f1aa"}.fa-language:before{content:"\f1ab"}.fa-fax:before{content:"\f1ac"}.fa-building:before{content:"\f1ad"}.fa-child:before{content:"\f1ae"}.fa-paw:before{content:"\f1b0"}.fa-spoon:before{content:"\f1b1"}.fa-cube:before{content:"\f1b2"}.fa-cubes:before{content:"\f1b3"}.fa-behance:before{content:"\f1b4"}.fa-behance-square:before{content:"\f1b5"}.fa-steam:before{content:"\f1b6"}.fa-steam-square:before{content:"\f1b7"}.fa-recycle:before{content:"\f1b8"}.fa-automobile:before,.fa-car:before{content:"\f1b9"}.fa-cab:before,.fa-taxi:before{content:"\f1ba"}.fa-tree:before{content:"\f1bb"}.fa-spotify:before{content:"\f1bc"}.fa-deviantart:before{content:"\f1bd"}.fa-soundcloud:before{content:"\f1be"}.fa-database:before{content:"\f1c0"}.fa-file-pdf-o:before{content:"\f1c1"}.fa-file-word-o:before{content:"\f1c2"}.fa-file-excel-o:before{content:"\f1c3"}.fa-file-powerpoint-o:before{content:"\f1c4"}.fa-file-photo-o:before,.fa-file-picture-o:before,.fa-file-image-o:before{content:"\f1c5"}.fa-file-zip-o:before,.fa-file-archive-o:before{content:"\f1c6"}.fa-file-sound-o:before,.fa-file-audio-o:before{content:"\f1c7"}.fa-file-movie-o:before,.fa-file-video-o:before{content:"\f1c8"}.fa-file-code-o:before{content:"\f1c9"}.fa-vine:before{content:"\f1ca"}.fa-codepen:before{content:"\f1cb"}.fa-jsfiddle:before{content:"\f1cc"}.fa-life-bouy:before,.fa-life-buoy:before,.fa-life-saver:before,.fa-support:before,.fa-life-ring:before{content:"\f1cd"}.fa-circle-o-notch:before{content:"\f1ce"}.fa-ra:before,.fa-rebel:before{content:"\f1d0"}.fa-ge:before,.fa-empire:before{content:"\f1d1"}.fa-git-square:before{content:"\f1d2"}.fa-git:before{content:"\f1d3"}.fa-y-combinator-square:before,.fa-yc-square:before,.fa-hacker-news:before{content:"\f1d4"}.fa-tencent-weibo:before{content:"\f1d5"}.fa-qq:before{content:"\f1d6"}.fa-wechat:before,.fa-weixin:before{content:"\f1d7"}.fa-send:before,.fa-paper-plane:before{content:"\f1d8"}.fa-send-o:before,.fa-paper-plane-o:before{content:"\f1d9"}.fa-history:before{content:"\f1da"}.fa-circle-thin:before{content:"\f1db"}.fa-header:before{content:"\f1dc"}.fa-paragraph:before{content:"\f1dd"}.fa-sliders:before{content:"\f1de"}.fa-share-alt:before{content:"\f1e0"}.fa-share-alt-square:before{content:"\f1e1"}.fa-bomb:before{content:"\f1e2"}.fa-soccer-ball-o:before,.fa-futbol-o:before{content:"\f1e3"}.fa-tty:before{content:"\f1e4"}.fa-binoculars:before{content:"\f1e5"}.fa-plug:before{content:"\f1e6"}.fa-slideshare:before{content:"\f1e7"}.fa-twitch:before{content:"\f1e8"}.fa-yelp:before{content:"\f1e9"}.fa-newspaper-o:before{content:"\f1ea"}.fa-wifi:before{content:"\f1eb"}.fa-calculator:before{content:"\f1ec"}.fa-paypal:before{content:"\f1ed"}.fa-google-wallet:before{content:"\f1ee"}.fa-cc-visa:before{content:"\f1f0"}.fa-cc-mastercard:before{content:"\f1f1"}.fa-cc-discover:before{content:"\f1f2"}.fa-cc-amex:before{content:"\f1f3"}.fa-cc-paypal:before{content:"\f1f4"}.fa-cc-stripe:before{content:"\f1f5"}.fa-bell-slash:before{content:"\f1f6"}.fa-bell-slash-o:before{content:"\f1f7"}.fa-trash:before{content:"\f1f8"}.fa-copyright:before{content:"\f1f9"}.fa-at:before{content:"\f1fa"}.fa-eyedropper:before{content:"\f1fb"}.fa-paint-brush:before{content:"\f1fc"}.fa-birthday-cake:before{content:"\f1fd"}.fa-area-chart:before{content:"\f1fe"}.fa-pie-chart:before{content:"\f200"}.fa-line-chart:before{content:"\f201"}.fa-lastfm:before{content:"\f202"}.fa-lastfm-square:before{content:"\f203"}.fa-toggle-off:before{content:"\f204"}.fa-toggle-on:before{content:"\f205"}.fa-bicycle:before{content:"\f206"}.fa-bus:before{content:"\f207"}.fa-ioxhost:before{content:"\f208"}.fa-angellist:before{content:"\f209"}.fa-cc:before{content:"\f20a"}.fa-shekel:before,.fa-sheqel:before,.fa-ils:before{content:"\f20b"}.fa-meanpath:before{content:"\f20c"}.fa-buysellads:before{content:"\f20d"}.fa-connectdevelop:before{content:"\f20e"}.fa-dashcube:before{content:"\f210"}.fa-forumbee:before{content:"\f211"}.fa-leanpub:before{content:"\f212"}.fa-sellsy:before{content:"\f213"}.fa-shirtsinbulk:before{content:"\f214"}.fa-simplybuilt:before{content:"\f215"}.fa-skyatlas:before{content:"\f216"}.fa-cart-plus:before{content:"\f217"}.fa-cart-arrow-down:before{content:"\f218"}.fa-diamond:before{content:"\f219"}.fa-ship:before{content:"\f21a"}.fa-user-secret:before{content:"\f21b"}.fa-motorcycle:before{content:"\f21c"}.fa-street-view:before{content:"\f21d"}.fa-heartbeat:before{content:"\f21e"}.fa-venus:before{content:"\f221"}.fa-mars:before{content:"\f222"}.fa-mercury:before{content:"\f223"}.fa-intersex:before,.fa-transgender:before{content:"\f224"}.fa-transgender-alt:before{content:"\f225"}.fa-venus-double:before{content:"\f226"}.fa-mars-double:before{content:"\f227"}.fa-venus-mars:before{content:"\f228"}.fa-mars-stroke:before{content:"\f229"}.fa-mars-stroke-v:before{content:"\f22a"}.fa-mars-stroke-h:before{content:"\f22b"}.fa-neuter:before{content:"\f22c"}.fa-genderless:before{content:"\f22d"}.fa-facebook-official:before{content:"\f230"}.fa-pinterest-p:before{content:"\f231"}.fa-whatsapp:before{content:"\f232"}.fa-server:before{content:"\f233"}.fa-user-plus:before{content:"\f234"}.fa-user-times:before{content:"\f235"}.fa-hotel:before,.fa-bed:before{content:"\f236"}.fa-viacoin:before{content:"\f237"}.fa-train:before{content:"\f238"}.fa-subway:before{content:"\f239"}.fa-medium:before{content:"\f23a"}.fa-yc:before,.fa-y-combinator:before{content:"\f23b"}.fa-optin-monster:before{content:"\f23c"}.fa-opencart:before{content:"\f23d"}.fa-expeditedssl:before{content:"\f23e"}.fa-battery-4:before,.fa-battery-full:before{content:"\f240"}.fa-battery-3:before,.fa-battery-three-quarters:before{content:"\f241"}.fa-battery-2:before,.fa-battery-half:before{content:"\f242"}.fa-battery-1:before,.fa-battery-quarter:before{content:"\f243"}.fa-battery-0:before,.fa-battery-empty:before{content:"\f244"}.fa-mouse-pointer:before{content:"\f245"}.fa-i-cursor:before{content:"\f246"}.fa-object-group:before{content:"\f247"}.fa-object-ungroup:before{content:"\f248"}.fa-sticky-note:before{content:"\f249"}.fa-sticky-note-o:before{content:"\f24a"}.fa-cc-jcb:before{content:"\f24b"}.fa-cc-diners-club:before{content:"\f24c"}.fa-clone:before{content:"\f24d"}.fa-balance-scale:before{content:"\f24e"}.fa-hourglass-o:before{content:"\f250"}.fa-hourglass-1:before,.fa-hourglass-start:before{content:"\f251"}.fa-hourglass-2:before,.fa-hourglass-half:before{content:"\f252"}.fa-hourglass-3:before,.fa-hourglass-end:before{content:"\f253"}.fa-hourglass:before{content:"\f254"}.fa-hand-grab-o:before,.fa-hand-rock-o:before{content:"\f255"}.fa-hand-stop-o:before,.fa-hand-paper-o:before{content:"\f256"}.fa-hand-scissors-o:before{content:"\f257"}.fa-hand-lizard-o:before{content:"\f258"}.fa-hand-spock-o:before{content:"\f259"}.fa-hand-pointer-o:before{content:"\f25a"}.fa-hand-peace-o:before{content:"\f25b"}.fa-trademark:before{content:"\f25c"}.fa-registered:before{content:"\f25d"}.fa-creative-commons:before{content:"\f25e"}.fa-gg:before{content:"\f260"}.fa-gg-circle:before{content:"\f261"}.fa-tripadvisor:before{content:"\f262"}.fa-odnoklassniki:before{content:"\f263"}.fa-odnoklassniki-square:before{content:"\f264"}.fa-get-pocket:before{content:"\f265"}.fa-wikipedia-w:before{content:"\f266"}.fa-safari:before{content:"\f267"}.fa-chrome:before{content:"\f268"}.fa-firefox:before{content:"\f269"}.fa-opera:before{content:"\f26a"}.fa-internet-explorer:before{content:"\f26b"}.fa-tv:before,.fa-television:before{content:"\f26c"}.fa-contao:before{content:"\f26d"}.fa-500px:before{content:"\f26e"}.fa-amazon:before{content:"\f270"}.fa-calendar-plus-o:before{content:"\f271"}.fa-calendar-minus-o:before{content:"\f272"}.fa-calendar-times-o:before{content:"\f273"}.fa-calendar-check-o:before{content:"\f274"}.fa-industry:before{content:"\f275"}.fa-map-pin:before{content:"\f276"}.fa-map-signs:before{content:"\f277"}.fa-map-o:before{content:"\f278"}.fa-map:before{content:"\f279"}.fa-commenting:before{content:"\f27a"}.fa-commenting-o:before{content:"\f27b"}.fa-houzz:before{content:"\f27c"}.fa-vimeo:before{content:"\f27d"}.fa-black-tie:before{content:"\f27e"}.fa-fonticons:before{content:"\f280"}.fa-reddit-alien:before{content:"\f281"}.fa-edge:before{content:"\f282"}.fa-credit-card-alt:before{content:"\f283"}.fa-codiepie:before{content:"\f284"}.fa-modx:before{content:"\f285"}.fa-fort-awesome:before{content:"\f286"}.fa-usb:before{content:"\f287"}.fa-product-hunt:before{content:"\f288"}.fa-mixcloud:before{content:"\f289"}.fa-scribd:before{content:"\f28a"}.fa-pause-circle:before{content:"\f28b"}.fa-pause-circle-o:before{content:"\f28c"}.fa-stop-circle:before{content:"\f28d"}.fa-stop-circle-o:before{content:"\f28e"}.fa-shopping-bag:before{content:"\f290"}.fa-shopping-basket:before{content:"\f291"}.fa-hashtag:before{content:"\f292"}.fa-bluetooth:before{content:"\f293"}.fa-bluetooth-b:before{content:"\f294"}.fa-percent:before{content:"\f295"}
5 |
--------------------------------------------------------------------------------
/test_app/static/fonts/FontAwesome.otf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/christabor/flask_extras/f57300bc2922aa4105d1aa393351b63c86c26048/test_app/static/fonts/FontAwesome.otf
--------------------------------------------------------------------------------
/test_app/static/fonts/fontawesome-webfont.eot:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/christabor/flask_extras/f57300bc2922aa4105d1aa393351b63c86c26048/test_app/static/fonts/fontawesome-webfont.eot
--------------------------------------------------------------------------------
/test_app/static/fonts/fontawesome-webfont.ttf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/christabor/flask_extras/f57300bc2922aa4105d1aa393351b63c86c26048/test_app/static/fonts/fontawesome-webfont.ttf
--------------------------------------------------------------------------------
/test_app/static/fonts/fontawesome-webfont.woff:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/christabor/flask_extras/f57300bc2922aa4105d1aa393351b63c86c26048/test_app/static/fonts/fontawesome-webfont.woff
--------------------------------------------------------------------------------
/test_app/static/fonts/fontawesome-webfont.woff2:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/christabor/flask_extras/f57300bc2922aa4105d1aa393351b63c86c26048/test_app/static/fonts/fontawesome-webfont.woff2
--------------------------------------------------------------------------------
/test_app/static/glyphicons-halflings-regular.eot:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/christabor/flask_extras/f57300bc2922aa4105d1aa393351b63c86c26048/test_app/static/glyphicons-halflings-regular.eot
--------------------------------------------------------------------------------
/test_app/static/glyphicons-halflings-regular.ttf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/christabor/flask_extras/f57300bc2922aa4105d1aa393351b63c86c26048/test_app/static/glyphicons-halflings-regular.ttf
--------------------------------------------------------------------------------
/test_app/static/glyphicons-halflings-regular.woff:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/christabor/flask_extras/f57300bc2922aa4105d1aa393351b63c86c26048/test_app/static/glyphicons-halflings-regular.woff
--------------------------------------------------------------------------------
/test_app/static/glyphicons-halflings-regular.woff2:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/christabor/flask_extras/f57300bc2922aa4105d1aa393351b63c86c26048/test_app/static/glyphicons-halflings-regular.woff2
--------------------------------------------------------------------------------
/test_app/templates/layouts/base.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | "{{ macroname }}"
14 | Add footer and remove close buttons
27 | 28 | {{ bs.modal('someId2', 'Some html or text...', footer='Some footer text/html.', close_btns=False, title='My Modal 2', include_btn=True) }} 29 | 30 | {{ example('dict2carousel', 'Generate a carousel from a dictionary') }} 31 | 32 | {{ bs.dict2carousel( 33 | 'someId', 34 | [ 35 | {'content': 'Or left aligned
56 | 57 | {{ bs.dictlist_group_badged({'Messages': 20, 'Unread': 10, 'Read': 100, 'Starred': 34}, align='left') }} 58 | 59 | {{ example('bs3_label', 'Generate a label from a string and a label mapping') }} 60 | 61 | {% set label_map = {'dev': 'info', 'stage': 'warning', 'prod': 'danger'} %} 62 | 63 | {{ bs.bs3_label('prod', label_map) }} 64 | {{ bs.bs3_label('stage', label_map) }} 65 | {{ bs.bs3_label('dev', label_map) }} 66 | 67 | {{ example('bs3_panel', 'Generate bs3 panels for a dict of titles/body content.') }} 68 | 69 | {{ bs.bs3_panel({'Heading 1': 'Some body text... etc...', 'Heading 2': 'Some body text...'}, paneltype='info') }} 70 | 71 | {{ example('bs3_breadcrumb', 'Generate breadcrumbs from a breadcrumb object (likely flask-breadcrumbs, but anything that supports the same attributes would work.') }} 72 | 73 | {{ bs.bs3_breadcrumb([{'text': 'Home'}, {'text': 'SomePage'}]) }} 74 | 75 | 76 | {{ example('dict2labels', 'Makes a dict of `name`:`label` into bootstrap labels') }} 77 | 78 | {{ bs.dict2labels({'foo': 'danger', 'bar': 'success'}) }} 79 | {{ bs.dict2labels({'foo': 'danger', 'bar': 'success'}, aslist=False) }} 80 |Or wrap it in an html list by specifying `aslist`:
81 | 82 | {{ bs.dict2labels({'foo': 'danger', 'bar': 'success'}, aslist=True) }} 83 | 84 | {{ example('dict2tabs', 'Make a dict into a bs3 tab group with tab content. Keys are tab labels, and values are tab content.') }} 85 | 86 | {{ bs.dict2tabs({"foo": "Some content rendered from html or macro
"}) }} 87 | 88 |You can also customize styles, data-attrs, etc... see function signature for more info.
91 | 92 |You can even pass in content generated from other macros, such as the wtform_form macro:
93 | 94 | {{ 95 | bs.dict2tabs({ 96 | "tab1": wtform_form(form, classes=['well']), 97 | "tab2": wtform_form(form2, classes=['well']) 98 | }, 99 | id="MyCoolTabContainer" 100 | ) 101 | }} 102 | 103 | {{ example('dictlist2tabs', 'Same idea as above, but with a list of dicts instead. This ensures ordering of tabs is correct.') }} 104 | 105 | {{ 106 | bs.dictlist2tabs([ 107 | {"tab1": wtform_form(form, classes=['well'])}, 108 | {"tab2": wtform_form(form2, classes=['well'])} 109 | ], 110 | id="MyCoolTabContainer2" 111 | ) 112 | }} 113 | 114 | {{ example('inline_list', 'Generate from a list, an inline list with a custom divider.') }} 115 | 116 | {{ bs.inline_list(['foo', 'bar', 'baz']) }} 117 | 118 | {{ example('inline_dictlist', 'Generate from a list of dicts, an inline list with a custom divider and key/value.') }} 119 | 120 | {{ bs.inline_dictlist(dictlist, seperator='::') }} 121 | 122 | {{ example('dict2btn_group', 'Generated a button group from a dictionary of {"btn-type": "name"} dict list.') }} 123 | 124 | {{ bs.dict2btn_group({'info': 'bar', 'success': 'baz', 'default': 'foo'}, size='xs') }}Disabled links + prev/next + custom prev/next text.
146 | 147 | {{ bs.dict2_pagination(pagination, prev={'somelink': '«'}, next={'somelink': 'next'}, size='sm', disabled=[1, 2, 'prev', 'next']) }} 148 | 149 | {{ example('modal_carousel', 'Generate a carousel inside a modal with a dictionary for carousel items.') }} 150 | 151 | {{ bs.modal_carousel( 152 | [ 153 | {'content': '13 | Here is some text with {{ inline_code('some_function(2, 4)') }} in it. 14 |
15 | 16 | {{ example('code', 'Show code styling as a block and specified language options. (Syntax highlighting not provided, but classes are added for styling.)') }} 17 | 18 | {{ code('{"I": "am": "some": "json", "weee": 42}', lang='json') }} 19 | 20 | {{ example('tokenize_code', 'Show code styling as a block but wrap various tokens of code with specified data-attrs and/or classes for styling. Useful for showing log or other data output with specific delimited tokens editable/styled.') }} 21 | 22 | {{ tokenize_code( 23 | 'This is a log output stream ... FOO bar BAZ "quux" 01-01-0000 00:00:00 | GET | foobar', 24 | delimiter=' ', 25 | token_dattrs={'BAZ': {'val': 'foo'}}, 26 | token_classes={ 27 | 'FOO': ['label', 'label-info'], 28 | 'BAZ': ['label', 'label-danger'], 29 | 'GET': ['label', 'label-success'] 30 | }, 31 | replacers=['"'], 32 | use_pre=False, 33 | wrap_all=True, 34 | ) 35 | }} 36 |See docs
27 | 28 |See docs
30 | 31 |See docs
33 |Supports appending icons and left/right order:
31 | 32 | {{ mac.list2list(['Toyota', 'V2', '747', 'John Deere'], icons={'Toyota': ['fa', 'fa-car'], 'V2': ['fa', 'fa-rocket']}, icondir='left') }} 33 | {{ mac.list2list(['Toyota', 'V2', '747', 'John Deere'], icons={'Toyota': ['fa', 'fa-car'], 'V2': ['fa', 'fa-rocket']}, icondir='right') }} 34 | 35 | {{ example('dictlist2nav', 'Make a list of links with nav element. Format must be a list of dicts/tuples. Supports *one* level of nesting.') }} 36 | 37 | {{ mac.dictlist2nav(dictlist) }} 38 | 39 | {{ example('dictlist2dropdown', 'Make a select > option element. Format must be a list of dicts/tuples. Supports *one* level of nesting.') }} 40 | 41 | {{ mac.dictlist2dropdown(dictlist) }} 42 | 43 | {{ example('dictlist2checkboxes', 'Make a checkbox group, where keys are input names, and values are labels. Format must be a list of dicts/tuples.') }} 44 | 45 | {{ mac.dictlist2checkboxes(dictlist) }} 46 | 47 | 48 | {{ example('objects2table', 'Convert a list of dicts/tuples to a table.') }} 49 | 50 | {{ mac.objects2table(dictlist2) }} 51 | 52 |Custom styling
53 | 54 | {{ mac.objects2table(dictlist2, classes=['table', 'table-striped']) }} 55 | 56 |Filtering
57 | 58 | {{ mac.objects2table(dictlist2, filterkeys='name', classes=['table', 'table']) }} 59 | 60 |Also supports many other things, like custom macros *PER* header or field, giving you much more fine-grained controlled, IF you need it.
61 | 62 | {{ example('wtform_form', 'Render a wtform object, with error handling, customizable options, layouts, text and much more.') }} 63 | 64 |Options include:
65 |And more. See actual macro for more.
83 | 84 |Examples (wrapped in boxes for clarity using custom class).
85 | 86 | {{ mac.wtform_form(form, classes=['form', 'well']) }} 87 | 88 |Horizontal mode
89 | 90 | {{ mac.wtform_form(form, horizontal=True, classes=['form', 'well']) }} 91 | 92 |Horizontal/right-align mode
93 | 94 | {{ mac.wtform_form(form, align='right', horizontal=True, classes=['form', 'well']) }} 95 | 96 |Custom grouping
97 | 98 | {{ mac.wtform_form(form, 99 | classes=['form', 'well'], 100 | fieldset_groups=[ 101 | ('Important info!', ('name', 'age')), 102 | ('Not so important...', ('nickname', 'favorite_food')), 103 | ]) }} 104 | 105 | {{ example('recurse_dictlist', 'Recursively traverse a list of dictionaries and all sub-datastructures, to build out a nested UL or OL.') }} 106 | 107 | {{ mac.recurse_dictlist(recursedict) }} 108 |