Large diffs are not rendered by default.

Large diffs are not rendered by default.

@@ -0,0 +1,20 @@
dominate/__init__.py,sha256=qZ6CLTkIM6XUGeCvrWxODTC0KwHqdGHhuPK_IOq9FN8,88
dominate/_version.py,sha256=sqVQ8fFGbnsGqRXKtmC4MtWqfJMePAbNboHtUAAf9RY,23
dominate/document.py,sha256=wKkHjVd4YINjgtDUvtkdpbav2wq29P4ANBMqbAUgWK4,2217
dominate/dom1core.py,sha256=abRAFVImeyRmdK_eLUE3Fr0LfnIGiRkJzfKZCfKJXqM,1760
dominate/dom_tag.py,sha256=SBv1VUBipybwtviXRC87hoVqgyhOnrWDjJ9GmfjOTnw,12431
dominate/tags.py,sha256=D2DNA0hKMIjxx7c7hcPxOO9STbxKv2bt6cw_LhljZ1A,28020
dominate/util.py,sha256=8c6HBl5jc20v6L9CHO-xm9pGUXw7Voc1uk__udZksIY,3923
dominate-2.1.16.dist-info/DESCRIPTION.rst,sha256=gWLL40AIFSAjK-NC9vpc2OiWiIsh4s8varhb3lkUNB0,10314
dominate-2.1.16.dist-info/METADATA,sha256=ivsXUqbHK_MTJcVJHPWQPajeOFuun8KV6sSituFPPiY,11461
dominate-2.1.16.dist-info/metadata.json,sha256=JxWYMTgjM3h_UgSmgWeNQ0ttMQB_M8hkIiXpezA3e5U,1249
dominate-2.1.16.dist-info/RECORD,,
dominate-2.1.16.dist-info/top_level.txt,sha256=lkq2NuHoGLvvjfSl6OLHyLNBidBuRCf1CbUpKhH-lYY,9
dominate-2.1.16.dist-info/WHEEL,sha256=54bVun1KfEBTJ68SHUmbxNPj80VxlQ0sHi4gZdGZXEY,92
dominate/_version.pyc,,
dominate/util.pyc,,
dominate/dom_tag.pyc,,
dominate/__init__.pyc,,
dominate/document.pyc,,
dominate/tags.pyc,,
dominate/dom1core.pyc,,
@@ -0,0 +1,5 @@
Wheel-Version: 1.0
Generator: bdist_wheel (0.24.0)
Root-Is-Purelib: true
Tag: py2-none-any

@@ -0,0 +1 @@
{"license": "LICENSE.txt", "name": "dominate", "metadata_version": "2.0", "generator": "bdist_wheel (0.24.0)", "summary": "Dominate is a Python library for creating and manipulating HTML documents using an elegant DOM API.", "version": "2.1.16", "extensions": {"python.details": {"project_urls": {"Home": "http://github.com/Knio/dominate/"}, "document_names": {"description": "DESCRIPTION.rst"}, "contacts": [{"role": "author", "email": "tom@zkpq.ca", "name": "Tom Flanagan and Jake Wharton"}]}}, "keywords": ["framework", "templating", "template", "html", "xhtml", "python", "html5"], "classifiers": ["Intended Audience :: Developers", "License :: OSI Approved :: GNU Lesser General Public License v3 (LGPLv3)", "Operating System :: OS Independent", "Programming Language :: Python", "Programming Language :: Python :: 2", "Programming Language :: Python :: 2.7", "Programming Language :: Python :: 3", "Programming Language :: Python :: 3.3", "Programming Language :: Python :: 3.4", "Programming Language :: Python :: 3.5", "Programming Language :: Python :: Implementation :: PyPy", "Topic :: Internet :: WWW/HTTP :: Dynamic Content", "Topic :: Software Development :: Libraries :: Python Modules", "Topic :: Text Processing :: Markup :: HTML"]}
@@ -0,0 +1 @@
dominate
@@ -0,0 +1,4 @@
from ._version import __version__
version = __version__

from .document import document
Binary file not shown.
@@ -0,0 +1 @@
__version__ = '2.1.16'
Binary file not shown.
@@ -0,0 +1,75 @@
__license__ = '''
This file is part of Dominate.
Dominate is free software: you can redistribute it and/or modify
it under the terms of the GNU Lesser General Public License as
published by the Free Software Foundation, either version 3 of
the License, or (at your option) any later version.
Dominate is distributed in the hope that it will be useful, but
WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General
Public License along with Dominate. If not, see
<http://www.gnu.org/licenses/>.
'''

from . import tags

try:
basestring = basestring
except NameError: # py3
basestring = str
unicode = str

class document(tags.html):
tagname = 'html'
def __init__(self, title='Dominate', doctype='<!DOCTYPE html>', request=None):
'''
Creates a new document instance. Accepts `title`, `doctype`, and `request` keyword arguments.
'''
super(document, self).__init__()
self.doctype = doctype
self.head = super(document, self).add(tags.head())
self.body = super(document, self).add(tags.body())
self.title_node = self.head.add(tags.title(title))
self._entry = self.body

def get_title(self):
return self.title_node.text

def set_title(self, title):
if isinstance(title, basestring):
self.title_node.text = title
else:
self.head.remove(self.title_node)
self.head.add(title)
self.title_node = title

title = property(get_title, set_title)

def add(self, *args):
'''
Adding tags to a document appends them to the <body>.
'''
return self._entry.add(*args)

def render(self, *args, **kwargs):
'''
Creates a <title> tag if not present and renders the DOCTYPE and tag tree.
'''
r = []

#Validates the tag tree and adds the doctype if one was set
if self.doctype:
r.append(self.doctype)
r.append('\n')
r.append(super(document, self).render(*args, **kwargs))

return u''.join(r)
__str__ = __unicode__ = render

def __repr__(self):
return '<dominate.document "%s">' % self.title
Binary file not shown.
@@ -0,0 +1,68 @@
__license__ = '''
This file is part of Dominate.
Dominate is free software: you can redistribute it and/or modify
it under the terms of the GNU Lesser General Public License as
published by the Free Software Foundation, either version 3 of
the License, or (at your option) any later version.
Dominate is distributed in the hope that it will be useful, but
WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General
Public License along with Dominate. If not, see
<http://www.gnu.org/licenses/>.
'''

try:
basestring = basestring
except NameError: # py3
basestring = str
unicode = str


class dom1core(object):
'''
Implements the Document Object Model (Core) Level 1
http://www.w3.org/TR/1998/REC-DOM-Level-1-19981001/
http://www.w3.org/TR/1998/REC-DOM-Level-1-19981001/level-one-core.html
'''
@property
def parentNode(self):
'''
DOM API: Returns the parent tag of the current element.
'''
return self.parent

def getElementById(self, id):
'''
DOM API: Returns single element with matching id value.
'''
results = self.get(id=id)
if len(results) > 1:
raise ValueError('Multiple tags with id "%s".' % id)
elif results:
return results[0]
else:
return None

def getElementsByTagName(self, name):
'''
DOM API: Returns all tags that match name.
'''
if isinstance(name, basestring):
return self.get(name.lower())
else:
return None

def appendChild(self, obj):
'''
DOM API: Add an item to the end of the children list.
'''
self.add(obj)
return self


Binary file not shown.

Large diffs are not rendered by default.

Binary file not shown.

Large diffs are not rendered by default.

Binary file not shown.
@@ -0,0 +1,169 @@
'''
Utility classes for creating dynamic html documents
'''

__license__ = '''
This file is part of Dominate.
Dominate is free software: you can redistribute it and/or modify
it under the terms of the GNU Lesser General Public License as
published by the Free Software Foundation, either version 3 of
the License, or (at your option) any later version.
Dominate is distributed in the hope that it will be useful, but
WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General
Public License along with Dominate. If not, see
<http://www.gnu.org/licenses/>.
'''

import re
from .dom_tag import dom_tag


try:
basestring = basestring
except NameError:
basestring = str
unichr = chr

def include(f):
'''
includes the contents of a file on disk.
takes a filename
'''
fl = open(f, 'r')
data = fl.read()
fl.close()
return raw(data)


def system(cmd, data=None):
'''
pipes the output of a program
'''
import subprocess
s = subprocess.Popen(cmd, shell=True, stdout=subprocess.PIPE, stdin=subprocess.PIPE)
out, err = s.communicate(data)
return out.decode('utf8')


def escape(data, quote=True): # stoled from std lib cgi
'''
Escapes special characters into their html entities
Replace special characters "&", "<" and ">" to HTML-safe sequences.
If the optional flag quote is true, the quotation mark character (")
is also translated.
This is used to escape content that appears in the body of an HTML cocument
'''
data = data.replace("&", "&amp;") # Must be done first!
data = data.replace("<", "&lt;")
data = data.replace(">", "&gt;")
if quote:
data = data.replace('"', "&quot;")
return data


_unescape = {
'quot': 34,
'amp': 38,
'lt': 60,
'gt': 62,
'nbsp': 32,
# more here
# http://www.w3.org/TR/html4/sgml/entities.html
'yuml': 255,
}


def unescape(data):
'''
unescapes html entities. the opposite of escape.
'''
cc = re.compile('&(?:(?:#(\d+))|([^;]+));')

result = []
m = cc.search(data)
while m:
result.append(data[0:m.start()])
d = m.group(1)
if d:
d = int(d)
result.append(unichr(d))
else:
d = _unescape.get(m.group(2), ord('?'))
result.append(unichr(d))

data = data[m.end():]
m = cc.search(data)

result.append(data)
return ''.join(result)


_reserved = ";/?:@&=+$, "
_replace_map = dict((c, '%%%2X' % ord(c)) for c in _reserved)


def url_escape(data):
return ''.join(_replace_map.get(c, c) for c in data)


def url_unescape(data):
return re.sub('%([0-9a-fA-F]{2})',
lambda m: unichr(int(m.group(1), 16)), data)


class lazy(dom_tag):
'''
delays function execution until rendered
'''
def __new__(_cls, *args, **kwargs):
'''
Need to reset this special method or else
dom_tag will think it's being used as a dectorator.
This means lazy() can't be used as a dectorator, but
thinking about when you might want that just confuses me.
'''
return object.__new__(_cls)

def __init__(self, func, *args, **kwargs):
super(lazy, self).__init__()
self.func = func
self.args = args
self.kwargs = kwargs


def _render(self, rendered, indent=1, inline=False):
r = self.func(*self.args, **self.kwargs)
rendered.append(str(r))


# TODO rename this to raw?
class text(dom_tag):
'''
Just a string. useful for inside context managers
'''
is_pretty = False

def __init__(self, _text, escape=True):
super(text, self).__init__()
if escape:
self.text = globals()['escape'](_text)
else:
self.text = _text

def _render(self, rendered, indent, inline):
rendered.append(self.text)
return rendered

def raw(s):
'''
Inserts a raw string into the DOM. Unsafe.
'''
return text(s, escape=False)
Binary file not shown.
@@ -0,0 +1,193 @@
#!/usr/bin/env python
# coding=utf8

import re

from flask import Blueprint, current_app, url_for

try:
from wtforms.fields import HiddenField
except ImportError:
def is_hidden_field_filter(field):
raise RuntimeError('WTForms is not installed.')
else:
def is_hidden_field_filter(field):
return isinstance(field, HiddenField)

from .forms import render_form


__version__ = '3.3.5.7'
BOOTSTRAP_VERSION = re.sub(r'^(\d+\.\d+\.\d+).*', r'\1', __version__)
JQUERY_VERSION = '1.11.3'
HTML5SHIV_VERSION = '3.7.2'
RESPONDJS_VERSION = '1.4.2'


class CDN(object):
"""Base class for CDN objects."""
def get_resource_url(self, filename):
"""Return resource url for filename."""
raise NotImplementedError


class StaticCDN(object):
"""A CDN that serves content from the local application.
:param static_endpoint: Endpoint to use.
:param rev: If ``True``, honor ``BOOTSTRAP_QUERYSTRING_REVVING``.
"""
def __init__(self, static_endpoint='static', rev=False):
self.static_endpoint = static_endpoint
self.rev = rev

def get_resource_url(self, filename):
extra_args = {}

if self.rev and current_app.config['BOOTSTRAP_QUERYSTRING_REVVING']:
extra_args['bootstrap'] = __version__

return url_for(self.static_endpoint, filename=filename, **extra_args)


class WebCDN(object):
"""Serves files from the Web.
:param baseurl: The baseurl. Filenames are simply appended to this URL.
"""
def __init__(self, baseurl):
self.baseurl = baseurl

def get_resource_url(self, filename):
return self.baseurl + filename


class ConditionalCDN(object):
"""Serves files from one CDN or another, depending on whether a
configuration value is set.
:param confvar: Configuration variable to use.
:param primary: CDN to use if the configuration variable is ``True``.
:param fallback: CDN to use otherwise.
"""
def __init__(self, confvar, primary, fallback):
self.confvar = confvar
self.primary = primary
self.fallback = fallback

def get_resource_url(self, filename):
if current_app.config[self.confvar]:
return self.primary.get_resource_url(filename)
return self.fallback.get_resource_url(filename)


def bootstrap_find_resource(filename, cdn, use_minified=None, local=True):
"""Resource finding function, also available in templates.
Tries to find a resource, will force SSL depending on
``BOOTSTRAP_CDN_FORCE_SSL`` settings.
:param filename: File to find a URL for.
:param cdn: Name of the CDN to use.
:param use_minified': If set to ``True``/``False``, use/don't use
minified. If ``None``, honors
``BOOTSTRAP_USE_MINIFIED``.
:param local: If ``True``, uses the ``local``-CDN when
``BOOTSTRAP_SERVE_LOCAL`` is enabled. If ``False``, uses
the ``static``-CDN instead.
:return: A URL.
"""
config = current_app.config

if None == use_minified:
use_minified = config['BOOTSTRAP_USE_MINIFIED']

if use_minified:
filename = '%s.min.%s' % tuple(filename.rsplit('.', 1))

cdns = current_app.extensions['bootstrap']['cdns']
resource_url = cdns[cdn].get_resource_url(filename)

if resource_url.startswith('//') and config['BOOTSTRAP_CDN_FORCE_SSL']:
resource_url = 'https:%s' % resource_url

return resource_url


class Bootstrap(object):
def __init__(self, app=None):
if app is not None:
self.init_app(app)

def init_app(self, app):
app.config.setdefault('BOOTSTRAP_USE_MINIFIED', True)
app.config.setdefault('BOOTSTRAP_CDN_FORCE_SSL', False)

app.config.setdefault('BOOTSTRAP_QUERYSTRING_REVVING', True)
app.config.setdefault('BOOTSTRAP_SERVE_LOCAL', False)

app.config.setdefault('BOOTSTRAP_LOCAL_SUBDOMAIN', None)

blueprint = Blueprint(
'bootstrap',
__name__,
template_folder='templates',
static_folder='static',
static_url_path=app.static_url_path + '/bootstrap',
subdomain=app.config['BOOTSTRAP_LOCAL_SUBDOMAIN'])

# add the form rendering template filter
blueprint.add_app_template_filter(render_form)

app.register_blueprint(blueprint)

app.jinja_env.globals['bootstrap_is_hidden_field'] =\
is_hidden_field_filter
app.jinja_env.globals['bootstrap_find_resource'] =\
bootstrap_find_resource

if not hasattr(app, 'extensions'):
app.extensions = {}

local = StaticCDN('bootstrap.static', rev=True)
static = StaticCDN()

def lwrap(cdn, primary=static):
return ConditionalCDN('BOOTSTRAP_SERVE_LOCAL', primary, cdn)

bootstrap = lwrap(
WebCDN('//cdnjs.cloudflare.com/ajax/libs/twitter-bootstrap/%s/'
% BOOTSTRAP_VERSION),
local)

jquery = lwrap(
WebCDN('//cdnjs.cloudflare.com/ajax/libs/jquery/%s/'
% JQUERY_VERSION),
local)

html5shiv = lwrap(
WebCDN('//cdnjs.cloudflare.com/ajax/libs/html5shiv/%s/'
% HTML5SHIV_VERSION))

respondjs = lwrap(
WebCDN('//cdnjs.cloudflare.com/ajax/libs/respond.js/%s/'
% RESPONDJS_VERSION))

app.extensions['bootstrap'] = {
'cdns': {
'local': local,
'static': static,
'bootstrap': bootstrap,
'jquery': jquery,
'html5shiv': html5shiv,
'respond.js': respondjs,
},
}

# setup support for flask-nav
renderers = app.extensions.setdefault('nav_renderers', {})
renderer_name = (__name__ + '.nav', 'BootstrapRenderer')
renderers['bootstrap'] = renderer_name

# make bootstrap the default renderer
renderers[None] = renderer_name
Binary file not shown.
@@ -0,0 +1,149 @@
from dominate import tags
from dominate.util import raw
from flask import current_app
from markupsafe import Markup
from visitor import Visitor


def render_form(form, **kwargs):
r = WTFormsRenderer(**kwargs)

return Markup(r.visit(form))


class WTFormsRenderer(Visitor):
def __init__(self,
action='',
id=None,
method='post',
extra_classes=[],
role='form',
enctype=None):
self.action = action
self.form_id = id
self.method = method
self.extra_classes = extra_classes
self.role = role
self.enctype = enctype

def _visited_file_field(self):
if self._real_enctype is None:
self._real_enctype = u'multipart/form-data'

def _get_wrap(self, node, classes='form-group'):
# add required class, which strictly speaking isn't bootstrap, but
# a common enough customization
if node.flags.required:
classes += ' required'

div = tags.div(_class=classes)
if current_app.debug:
div.add(tags.comment(' Field: {} ({}) '.format(
node.name, node.__class__.__name__)))

return div

def _wrapped_input(self, node,
type='text',
classes=['form-control'], **kwargs):
wrap = self._get_wrap(node)
wrap.add(tags.label(node.label.text, _for=node.id))
wrap.add(tags.input(type=type, _class=' '.join(classes), **kwargs))

return wrap

def visit_BooleanField(self, node):
wrap = self._get_wrap(node, classes='checkbox')

label = wrap.add(tags.label(_for=node.id))
label.add(tags.input(type='checkbox'))
label.add(node.label.text)

return wrap

def visit_DateField(self, node):
return self._wrapped_input(node, 'date')

def visit_DateTimeField(self, node):
return self._wrapped_input(node, 'datetime-local')

def visit_DecimalField(self, node):
# FIXME: if range-validator is present, add limits?
return self._wrapped_input(node, 'number')

def visit_EmailField(self, node):
# note: WTForms does not actually have an EmailField, this function
# is called by visit_TextField based on which validators are enabled
return self._wrapped_input(node, 'email')

def visit_Field(self, node):
# FIXME: add error class

wrap = self._get_wrap(node)

# add the label
wrap.add(tags.label(node.label.text, _for=node.id))
wrap.add(raw(node()))

if node.description:
wrap.add(tags.p(node.description, _class='help-block'))

return wrap

def visit_FileField(self, node):
self._visited_file_field()
return self._wrapped_input(node, 'file', classes=[])

def visit_FloatField(self, node):
# FIXME: if range-validator is present, add limits?
return self._wrapped_input(node, 'number')

def visit_Form(self, node):
form = tags.form(_class=' '.join(['form'] + self.extra_classes))

if self.action:
form['action'] = self.action

if self.form_id:
form['id'] = self.form_id

if self.method:
form['method'] = self.method

# prepare enctype, this will be auto-updated by file fields if
# necessary
self._real_enctype = self.enctype

# render fields
for field in node:
elem = self.visit(field)
form.add(elem)

if self._real_enctype:
form['enctype'] = self._real_enctype

return form

def visit_HiddenField(self, node):
return raw(node())

def visit_IntegerField(self, node):
# FIXME: if range-validator is present, add limits?
return self._wrapped_input(node, 'number', step=1)

def visit_PasswordField(self, node):
return self._wrapped_input(node, 'password')

def visit_SubmitField(self, node):
button = tags.button(node.label.text,
_class='btn btn-default',
type='submit')
return button

def visit_TextField(self, node):
for v in node.validators:
if v.__class__.__name__ == 'Email':
# render email fields differently
return self.visit_EmailField(node)

return self._wrapped_input(node, 'text')
Binary file not shown.
@@ -0,0 +1,102 @@
from hashlib import sha1
from dominate import tags
from visitor import Visitor


class BootstrapRenderer(Visitor):
def __init__(self, html5=True, id=None):
self.html5 = html5
self._in_dropdown = False
self.id = id

def visit_Navbar(self, node):
# create a navbar id that is somewhat fixed, but do not leak any
# information about memory contents to the outside
node_id = self.id or sha1(str(id(node)).encode()).hexdigest()

root = tags.nav() if self.html5 else tags.div(role='navigation')
root['class'] = 'navbar navbar-default'

cont = root.add(tags.div(_class='container-fluid'))

# collapse button
header = cont.add(tags.div(_class='navbar-header'))
btn = header.add(tags.button())
btn['type'] = 'button'
btn['class'] = 'navbar-toggle collapsed'
btn['data-toggle'] = 'collapse'
btn['data-target'] = '#' + node_id
btn['aria-expanded'] = 'false'
btn['aria-controls'] = 'navbar'

btn.add(tags.span('Toggle navigation', _class='sr-only'))
btn.add(tags.span(_class='icon-bar'))
btn.add(tags.span(_class='icon-bar'))
btn.add(tags.span(_class='icon-bar'))

# title may also have a 'get_url()' method, in which case we render
# a brand-link
if node.title is not None:
if hasattr(node.title, 'get_url'):
header.add(tags.a(node.title.text, _class='navbar-brand',
href=node.title.get_url()))
else:
header.add(tags.span(node.title, _class='navbar-brand'))

bar = cont.add(tags.div(
_class='navbar-collapse collapse',
id=node_id,
))
bar_list = bar.add(tags.ul(_class='nav navbar-nav'))

for item in node.items:
bar_list.add(self.visit(item))

return root

def visit_Text(self, node):
if not self._in_dropdown:
return tags.p(node.text, _class='navbar-text')
return tags.li(node.text, _class='dropdown-header')

def visit_Link(self, node):
item = tags.li()
item.add(tags.a(node.text, href=node.get_url()))

return item

def visit_Separator(self, node):
if not self._in_dropdown:
raise RuntimeError('Cannot render separator outside Subgroup.')
return tags.li(role='separator', _class='divider')

def visit_Subgroup(self, node):
if not self._in_dropdown:
li = tags.li(_class='dropdown')
if node.active:
li['class'] = 'active'
a = li.add(tags.a(node.title, href='#', _class='dropdown-toggle'))
a['data-toggle'] = 'dropdown'
a['role'] = 'button'
a['aria-haspopup'] = 'true'
a['aria-expanded'] = 'false'
a.add(tags.span(_class='caret'))

ul = li.add(tags.ul(_class='dropdown-menu'))

self._in_dropdown = True
for item in node.items:
ul.add(self.visit(item))
self._in_dropdown = False

return li
else:
raise RuntimeError('Cannot render nested Subgroups')

def visit_View(self, node):
item = tags.li()
item.add(tags.a(node.text, href=node.get_url(), title=node.text))
if node.active:
item['class'] = 'active'

return item
Binary file not shown.

Large diffs are not rendered by default.

Large diffs are not rendered by default.

Large diffs are not rendered by default.

Large diffs are not rendered by default.

Large diffs are not rendered by default.

Large diffs are not rendered by default.

Binary file not shown.
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file not shown.
Binary file not shown.
Binary file not shown.

Large diffs are not rendered by default.

Large diffs are not rendered by default.

Large diffs are not rendered by default.

Large diffs are not rendered by default.

Large diffs are not rendered by default.

@@ -0,0 +1,13 @@
// This file is autogenerated via the `commonjs` Grunt task. You can require() this file in a CommonJS environment.
require('../../js/transition.js')
require('../../js/alert.js')
require('../../js/button.js')
require('../../js/carousel.js')
require('../../js/collapse.js')
require('../../js/dropdown.js')
require('../../js/modal.js')
require('../../js/tooltip.js')
require('../../js/popover.js')
require('../../js/scrollspy.js')
require('../../js/tab.js')
require('../../js/affix.js')
@@ -0,0 +1,34 @@
{% block doc -%}
<!DOCTYPE html>
<html{% block html_attribs %}{% endblock html_attribs %}>
{%- block html %}
<head>
{%- block head %}
<title>{% block title %}{% endblock title %}</title>

{%- block metas %}
<meta name="viewport" content="width=device-width, initial-scale=1.0">
{%- endblock metas %}

{%- block styles %}
<!-- Bootstrap -->
<link href="{{bootstrap_find_resource('css/bootstrap.css', cdn='bootstrap')}}" rel="stylesheet">
{%- endblock styles %}
{%- endblock head %}
</head>
<body{% block body_attribs %}{% endblock body_attribs %}>
{% block body -%}
{% block navbar %}
{%- endblock navbar %}
{% block content -%}
{%- endblock content %}

{% block scripts %}
<script src="{{bootstrap_find_resource('jquery.js', cdn='jquery')}}"></script>
<script src="{{bootstrap_find_resource('js/bootstrap.js', cdn='bootstrap')}}"></script>
{%- endblock scripts %}
{%- endblock body %}
</body>
{%- endblock html %}
</html>
{% endblock doc -%}
@@ -0,0 +1,6 @@
{% macro ie8() %}
<!--[if lt IE 9]>
<script src="{{bootstrap_find_resource('html5shiv.js', cdn='html5shiv', local=False)}}"></script>
<script src="{{bootstrap_find_resource('respond.js', cdn='respond.js', local=False)}}"></script>
<![endif]-->
{% endmacro %}
@@ -0,0 +1,38 @@
{% macro analytics(account) -%}
<script type="text/javascript">
var _gaq = _gaq || [];
_gaq.push(['_setAccount', '{{account}}']);
_gaq.push(['_trackPageview']);

(function() {
var ga = document.createElement('script'); ga.type = 'text/javascript'; ga.async = true;
ga.src = ('https:' == document.location.protocol ? 'https://ssl' : 'http://www') + '.google-analytics.com/ga.js';
var s = document.getElementsByTagName('script')[0]; s.parentNode.insertBefore(ga, s);
})();
</script>
{% endmacro %}

{% macro uanalytics(id, options='auto', domain=None) %}
{# The uanalytics macro currently contains a hack to support legacy code.
The old signature was ``uanalytics(id, domain)`` when domain was a required
parameter that was passed on to the ga() function.

To preserve old behavior, if options is not a dictionary, it is passed on
unchanged. The ``domain`` parameter is added to not break calls with named
parameters, it will override any other value for options.

More modern code can simply pass any desired option to the analytics
function as desired.
#}
{%- if domain != None %}
{%- set options = domain %}
{%- endif %}
<script>
(function(i,s,o,g,r,a,m){i['GoogleAnalyticsObject']=r;i[r]=i[r]||function(){
(i[r].q=i[r].q||[]).push(arguments)},i[r].l=1*new Date();a=s.createElement(o),
m=s.getElementsByTagName(o)[0];a.async=1;a.src=g;m.parentNode.insertBefore(a,m)
})(window,document,'script','//www.google-analytics.com/analytics.js','ga');
ga('create', {{id|tojson|safe}}, {{options|tojson|safe}});
ga('send', 'pageview');
</script>
{% endmacro %}
@@ -0,0 +1,52 @@
{% macro _arg_url_for(endpoint, base) %}
{# calls url_for() with a given endpoint and **base as the parameters,
additionally passing on all keyword_arguments (may overwrite existing ones)
#}
{%- with kargs = base.copy(),
_ = kargs.update(kwargs) -%}
{{url_for(endpoint, **kargs)}}
{%- endwith %}
{%- endmacro %}

{% macro render_pagination(pagination,
endpoint=None,
prev=('&laquo;')|safe,
next=('&raquo;')|safe,
size=None,
ellipses='…',
args={}
)
-%}
{# poor man's "do": #}
{% with url_args = {},
_ = url_args.update(request.view_args if not endpoint else {}),
__ = url_args.update(request.args if not endpoint else {}),
___ = url_args.update(args) %}
{% with endpoint = endpoint or request.endpoint %}
<nav>
<ul class="pagination{% if size %} pagination-{{size}}{% endif %}"{{kwargs|xmlattr}}>
{# prev and next are only show if a symbol has been passed. #}
{% if prev != None -%}
<li{% if not pagination.has_prev %} class="disabled"{% endif %}><a href="{{_arg_url_for(endpoint, url_args, page=pagination.prev_num) if pagination.has_prev else '#'}}">{{prev}}</li></a>
{%- endif -%}

{%- for page in pagination.iter_pages() %}
{% if page %}
{% if page != pagination.page %}
<li><a href="{{_arg_url_for(endpoint, url_args, page=page)}}">{{page}}</a></li>
{% else %}
<li class="active"><a href="#">{{page}} <span class="sr-only">(current)</span></a></li>
{% endif %}
{% elif ellipses != None %}
<li class="disabled"><a href="#">{{ellipses}}</a></li>
{% endif %}
{%- endfor %}

{% if next != None -%}
<li{% if not pagination.has_next %} class="disabled"{% endif %}><a href="{{_arg_url_for(endpoint, url_args, page=pagination.next_num) if pagination.has_next else '#'}}">{{next}}</li></a>
{%- endif -%}
</ul>
</nav>
{% endwith %}
{% endwith %}
{% endmacro %}
@@ -0,0 +1,45 @@
{% macro flashed_messages(messages=None, container=True, transform={
'critical': 'danger',
'error': 'danger',
'info': 'info',
'warning': 'warning',
'debug': 'info',
'notset': 'info',
'message': 'info',
}, default_category=None, dismissible=False) -%}
{% with messages = messages or get_flashed_messages(with_categories=True) -%}
{% if messages -%} {# don't output anything if there are no messages #}

{% if container -%}
<!-- begin message block -->
<div class="container flashed-messages">
<div class="row">
<div class="col-md-12">
{% endif -%}

{% for cat, msg in messages %} <div class="alert alert-{{transform.get(cat.lower(), default_category or cat)}}{% if dismissible %} alert-dismissible{% endif %}" role="alert">
{% if dismissible %} <button type="button" class="close" data-dismiss="alert" aria-label="Close"><span aria-hidden="true">&times;</span></button>{% endif %}
{{msg}}
</div>
{%- endfor -%}

{% if container %}
</div>
</div>
</div>
<!-- end message block -->
{% endif -%}

{% endif -%}
{% endwith -%}
{% endmacro -%}


{% macro icon(type=None, extra_classes=[]) -%}
<span{{ ({'class': (['glyphicon', 'glyphicon-' + type] + extra_classes)|join(' ')})|xmlattr}}{{kwargs|xmlattr}}></span>
{%- endmacro %}


{% macro form_button(url, content, method='post', class='btn-link') -%}
<form style="display: inline;" action='{{url}}' method='{{method}}'><button class="{{class|safe}}">{{content}}</button></form>
{%- endmacro %}
@@ -0,0 +1,211 @@
{% macro form_errors(form, hiddens=True) %}
{%- if form.errors %}
{%- for fieldname, errors in form.errors.items() %}
{%- if bootstrap_is_hidden_field(form[fieldname]) and hiddens or
not bootstrap_is_hidden_field(form[fieldname]) and hiddens != 'only' %}
{%- for error in errors %}
<p class="error">{{error}}</p>
{%- endfor %}
{%- endif %}
{%- endfor %}
{%- endif %}
{%- endmacro %}

{% macro _hz_form_wrap(horizontal_columns, form_type, add_group=False, required=False) %}
{% if form_type == "horizontal" %}
{% if add_group %}<div class="form-group{% if required %} required{% endif %}">{% endif %}
<div class="col-{{horizontal_columns[0]}}-offset-{{horizontal_columns[1]}}
col-{{horizontal_columns[0]}}-{{horizontal_columns[2]}}
">
{% endif %}
{{caller()}}

{% if form_type == "horizontal" %}
{% if add_group %}</div>{% endif %}
</div>
{% endif %}
{% endmacro %}

{% macro form_field(field,
form_type="basic",
horizontal_columns=('lg', 2, 10),
button_map={}) %}

{# this is a workaround hack for the more straightforward-code of just passing required=required parameter. older versions of wtforms do not have
the necessary fix for required=False attributes, but will also not set the required flag in the first place. we skirt the issue using the code below #}
{% if field.flags.required and not required in kwargs %}
{% set kwargs = dict(required=True, **kwargs) %}
{% endif %}

{% if field.widget.input_type == 'checkbox' %}
{% call _hz_form_wrap(horizontal_columns, form_type, True, required=required) %}
<div class="checkbox">
<label>
{{field()|safe}} {{field.label.text|safe}}
</label>
</div>
{% endcall %}
{%- elif field.type == 'RadioField' -%}
{# note: A cleaner solution would be rendering depending on the widget,
this is just a hack for now, until I can think of something better #}
{% call _hz_form_wrap(horizontal_columns, form_type, True, required=required) %}
{% for item in field -%}
<div class="radio">
<label>
{{item|safe}} {{item.label.text|safe}}
</label>
</div>
{% endfor %}
{% endcall %}
{%- elif field.type == 'SubmitField' -%}
{# deal with jinja scoping issues? #}
{% set field_kwargs = kwargs %}

{# note: same issue as above - should check widget, not field type #}
{% call _hz_form_wrap(horizontal_columns, form_type, True, required=required) %}
{{field(class='btn btn-%s' % button_map.get(field.name, 'default'),
**field_kwargs)}}
{% endcall %}
{%- elif field.type == 'FormField' -%}
{# note: FormFields are tricky to get right and complex setups requiring
these are probably beyond the scope of what this macro tries to do.
the code below ensures that things don't break horribly if we run into
one, but does not try too hard to get things pretty. #}
<fieldset>
<legend>{{field.label}}</legend>
{%- for subfield in field %}
{% if not bootstrap_is_hidden_field(subfield) -%}
{{ form_field(subfield,
form_type=form_type,
horizontal_columns=horizontal_columns,
button_map=button_map) }}
{%- endif %}
{%- endfor %}
</fieldset>
{% else -%}
<div class="form-group {% if field.errors %} has-error{% endif -%}
{%- if field.flags.required %} required{% endif -%}
">
{%- if form_type == "inline" %}
{{field.label(class="sr-only")|safe}}
{% if field.type == 'FileField' %}
{{field(**kwargs)|safe}}
{% else %}
{{field(class="form-control", **kwargs)|safe}}
{% endif %}
{% elif form_type == "horizontal" %}
{{field.label(class="control-label " + (
" col-%s-%s" % horizontal_columns[0:2]
))|safe}}
<div class=" col-{{horizontal_columns[0]}}-{{horizontal_columns[2]}}">
{% if field.type == 'FileField' %}
{{field(**kwargs)|safe}}
{% else %}
{{field(class="form-control", **kwargs)|safe}}
{% endif %}
</div>
{%- if field.errors %}
{%- for error in field.errors %}
{% call _hz_form_wrap(horizontal_columns, form_type, required=required) %}
<p class="help-block">{{error}}</p>
{% endcall %}
{%- endfor %}
{%- elif field.description -%}
{% call _hz_form_wrap(horizontal_columns, form_type, required=required) %}
<p class="help-block">{{field.description|safe}}</p>
{% endcall %}
{%- endif %}
{%- else -%}
{{field.label(class="control-label")|safe}}
{% if field.type == 'FileField' %}
{{field(**kwargs)|safe}}
{% else %}
{{field(class="form-control", **kwargs)|safe}}
{% endif %}

{%- if field.errors %}
{%- for error in field.errors %}
<p class="help-block">{{error}}</p>
{%- endfor %}
{%- elif field.description -%}
<p class="help-block">{{field.description|safe}}</p>
{%- endif %}
{%- endif %}
</div>
{% endif %}
{% endmacro %}

{# valid form types are "basic", "inline" and "horizontal" #}
{% macro quick_form(form,
action="",
method="post",
extra_classes=None,
role="form",
form_type="basic",
horizontal_columns=('lg', 2, 10),
enctype=None,
button_map={},
id="") %}
{#-
action="" is what we want, from http://www.ietf.org/rfc/rfc2396.txt:

4.2. Same-document References

A URI reference that does not contain a URI is a reference to the
current document. In other words, an empty URI reference within a
document is interpreted as a reference to the start of that document,
and a reference containing only a fragment identifier is a reference
to the identified fragment of that document. Traversal of such a
reference should not result in an additional retrieval action.
However, if the URI reference occurs in a context that is always
intended to result in a new request, as in the case of HTML's FORM
element, then an empty URI reference represents the base URI of the
current document and should be replaced by that URI when transformed
into a request.

-#}
{#- if any file fields are inside the form and enctype is automatic, adjust
if file fields are found. could really use the equalto test of jinja2
here, but latter is not available until 2.8

warning: the code below is guaranteed to make you cry =(
#}
{%- set _enctype = [] %}
{%- if enctype is none -%}
{%- for field in form %}
{%- if field.type == 'FileField' %}
{#- for loops come with a fairly watertight scope, so this list-hack is
used to be able to set values outside of it #}
{%- set _ = _enctype.append('multipart/form-data') -%}
{%- endif %}
{%- endfor %}
{%- else %}
{% set _ = _enctype.append(enctype) %}
{%- endif %}
<form
{%- if action != None %} action="{{action}}"{% endif -%}
{%- if id %} id="{{id}}"{% endif -%}
{%- if method %} method="{{method}}"{% endif %}
class="form
{%- if extra_classes %} {{extra_classes}}{% endif -%}
{%- if form_type == "horizontal" %} form-horizontal
{%- elif form_type == "inline" %} form-inline
{%- endif -%}
"
{%- if _enctype[0] %} enctype="{{_enctype[0]}}"{% endif -%}
{%- if role %} role="{{role}}"{% endif -%}
>
{{ form.hidden_tag() }}
{{ form_errors(form, hiddens='only') }}

{%- for field in form %}
{% if not bootstrap_is_hidden_field(field) -%}
{{ form_field(field,
form_type=form_type,
horizontal_columns=horizontal_columns,
button_map=button_map) }}
{%- endif %}
{%- endfor %}

</form>
{%- endmacro %}
@@ -0,0 +1,93 @@
visitor
=======

A tiny library to facilitate `visitor
<https://en.wikipedia.org/wiki/Visitor_pattern>`_ implementation in Python
(which are slightly peculiar due to dynamic typing). In fact, it is so small,
you may just be better off copy & pasting the source straight into your
project...


Example use
-----------

A simple JSON-encoder:

.. code-block:: python
from visitor import Visitor
class JSONEncoder(Visitor):
def __init__(self):
self.indent = 0
def escape_str(self, s):
# note: this is not a good escape function, do not use this in
# production!
s = s.replace('\\', '\\\\')
s = s.replace('"', '\\"')
return '"' + s + '"'
def visit_list(self, node):
self.indent += 1
s = '[\n' + ' ' * self.indent
s += (',\n' + ' ' * self.indent).join(self.visit(item)
for item in node)
self.indent -= 1
s += '\n' + ' ' * self.indent + ']'
return s
def visit_str(self, node):
return self.escape_str(node)
def visit_int(self, node):
return str(node)
def visit_bool(self, node):
return 'true' if node else 'false'
def visit_dict(self, node):
self.indent += 1
s = '{\n' + ' ' * self.indent
s += (',\n' + ' ' * self.indent).join(
'{}: {}'.format(self.escape_str(key), self.visit(value))
for key, value in sorted(node.items())
)
self.indent -= 1
s += '\n' + ' ' * self.indent + '}'
return s
data = [
'List', 'of', 42, 'items', True, {
'sub1': 'some string',
'sub2': {
'sub2sub1': False,
'sub2sub2': 123,
}
}
]
print(JSONEncoder().visit(data))
Output::

[
"List",
"of",
42,
"items",
true,
{
"sub1": "some string",
"sub2": {
"sub2sub1": false,
"sub2sub2": 123
}
}
]


@@ -0,0 +1,105 @@
Metadata-Version: 2.0
Name: visitor
Version: 0.1.2
Summary: A tiny pythonic visitor implementation.
Home-page: http://github.com/mbr/visitor
Author: Marc Brinkmann
Author-email: git@marcbrinkmann.de
License: MIT
Platform: UNKNOWN
Classifier: Programming Language :: Python :: 2
Classifier: Programming Language :: Python :: 3

visitor
=======

A tiny library to facilitate `visitor
<https://en.wikipedia.org/wiki/Visitor_pattern>`_ implementation in Python
(which are slightly peculiar due to dynamic typing). In fact, it is so small,
you may just be better off copy & pasting the source straight into your
project...


Example use
-----------

A simple JSON-encoder:

.. code-block:: python

from visitor import Visitor


class JSONEncoder(Visitor):
def __init__(self):
self.indent = 0

def escape_str(self, s):
# note: this is not a good escape function, do not use this in
# production!
s = s.replace('\\', '\\\\')
s = s.replace('"', '\\"')
return '"' + s + '"'

def visit_list(self, node):
self.indent += 1
s = '[\n' + ' ' * self.indent
s += (',\n' + ' ' * self.indent).join(self.visit(item)
for item in node)
self.indent -= 1
s += '\n' + ' ' * self.indent + ']'
return s

def visit_str(self, node):
return self.escape_str(node)

def visit_int(self, node):
return str(node)

def visit_bool(self, node):
return 'true' if node else 'false'

def visit_dict(self, node):
self.indent += 1
s = '{\n' + ' ' * self.indent
s += (',\n' + ' ' * self.indent).join(
'{}: {}'.format(self.escape_str(key), self.visit(value))
for key, value in sorted(node.items())
)
self.indent -= 1
s += '\n' + ' ' * self.indent + '}'
return s


data = [
'List', 'of', 42, 'items', True, {
'sub1': 'some string',
'sub2': {
'sub2sub1': False,
'sub2sub2': 123,
}
}
]

print(JSONEncoder().visit(data))



Output::

[
"List",
"of",
42,
"items",
true,
{
"sub1": "some string",
"sub2": {
"sub2sub1": false,
"sub2sub2": 123
}
}
]


@@ -0,0 +1,8 @@
visitor/__init__.py,sha256=2h5sJeSdoIF0uLLl-mryYjsHQaDMvynZNOeQGX8UoVs,2059
visitor-0.1.2.dist-info/DESCRIPTION.rst,sha256=3HtZDldUOheikNO-yOgQ2zcDFzmt_sjZgo0kKzMEd9c,2178
visitor-0.1.2.dist-info/METADATA,sha256=z6DwXQ3Cx05XhJC7rE_4l5L4e6w9p50Zyq6u_plaP3Y,2505
visitor-0.1.2.dist-info/metadata.json,sha256=0G6YagIFCkwyzKE62Nmn_pkF7gM2lVuGFOsVuiPwb14,512
visitor-0.1.2.dist-info/RECORD,,
visitor-0.1.2.dist-info/top_level.txt,sha256=YzMUOuikZNH_JrhCf_4ZZFsbtWgkdMoKlTJ4D0CvfP4,8
visitor-0.1.2.dist-info/WHEEL,sha256=54bVun1KfEBTJ68SHUmbxNPj80VxlQ0sHi4gZdGZXEY,92
visitor/__init__.pyc,,
@@ -0,0 +1,5 @@
Wheel-Version: 1.0
Generator: bdist_wheel (0.24.0)
Root-Is-Purelib: true
Tag: py2-none-any

@@ -0,0 +1 @@
{"license": "MIT", "name": "visitor", "metadata_version": "2.0", "generator": "bdist_wheel (0.24.0)", "summary": "A tiny pythonic visitor implementation.", "version": "0.1.2", "extensions": {"python.details": {"project_urls": {"Home": "http://github.com/mbr/visitor"}, "document_names": {"description": "DESCRIPTION.rst"}, "contacts": [{"role": "author", "email": "git@marcbrinkmann.de", "name": "Marc Brinkmann"}]}}, "classifiers": ["Programming Language :: Python :: 2", "Programming Language :: Python :: 3"]}
@@ -0,0 +1 @@
visitor
@@ -0,0 +1,47 @@
# Copyright (c) 2015 Marc Brinkmann

# Permission is hereby granted, free of charge, to any person obtaining a
# copy of this software and associated documentation files (the "Software"),
# to deal in the Software without restriction, including without limitation
# the rights to use, copy, modify, merge, publish, distribute, sublicense,
# and/or sell copies of the Software, and to permit persons to whom the
# Software is furnished to do so, subject to the following conditions:

# The above copyright notice and this permission notice shall be included in
# all copies or substantial portions of the Software.

# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
# FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
# DEALINGS IN THE SOFTWARE.


class Visitor(object):
"""Base class for visitors."""

def visit(self, node):
"""Visit a node.
Calls ``visit_CLASSNAME`` on itself passing ``node``, where
``CLASSNAME`` is the node's class. If the visitor does not implement an
appropriate visitation method, will go up the
`MRO <https://www.python.org/download/releases/2.3/mro/>`_ until a
match is found.
If the search exhausts all classes of node, raises a
:class:`~exceptions.NotImplementedError`.
:param node: The node to visit.
:return: The return value of the called visitation function.
"""
for cls in type(node).mro():
meth = getattr(self, 'visit_' + cls.__name__, None)
if meth is None:
continue
return meth(node)

raise NotImplementedError('No visitation method visit_{}'
.format(node.__class__.__name__))
Binary file not shown.