Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

HTML5 Forms #98

Closed
wants to merge 2 commits into from
Closed
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
157 changes: 143 additions & 14 deletions web/form.py
Expand Up @@ -18,7 +18,7 @@ class Form(object):

>>> f = Form(Textbox("x"))
>>> f.render()
'<table>\n <tr><th><label for="x">x</label></th><td><input type="text" id="x" name="x"/></td></tr>\n</table>'
u'<table>\n <tr><th><label for="x">x</label></th><td><input type="text" id="x" name="x"/></td></tr>\n</table>'
"""
def __init__(self, *inputs, **kw):
self.inputs = inputs
Expand Down Expand Up @@ -187,18 +187,110 @@ class Textbox(Input):
"""Textbox input.

>>> Textbox(name='foo', value='bar').render()
'<input type="text" id="foo" value="bar" name="foo"/>'
u'<input type="text" id="foo" value="bar" name="foo"/>'
>>> Textbox(name='foo', value=0).render()
'<input type="text" id="foo" value="0" name="foo"/>'
u'<input type="text" id="foo" value="0" name="foo"/>'
"""
def get_type(self):
return 'text'


class Telephone(Input):
"""Telephone input.

See: <http://dev.w3.org/html5/spec/Overview.html#the-input-element>

>>> Telephone(name='tel', value='55512345').render()
u'<input type="tel" id="tel" value="55512345" name="tel"/>'
"""
def get_type(self):
return 'tel'


class Email(Input):
"""Email input.

See: <http://dev.w3.org/html5/spec/Overview.html#the-input-element>

>>> Email(name='email', value='me@example.org').render()
u'<input type="email" id="email" value="me@example.org" name="email"/>'

"""
def get_type(self):
return 'email'


class Search(Input):
"""Search input.

See: <http://dev.w3.org/html5/spec/Overview.html#text-state-and-search-state>

>> Search(name='search', value='Search').render()
u'<input type="search" id="search" value="Search" name="search"/>'

>>> Search(name='search', value='Search', required='required', pattern='[a-z0-9]{2,30}', placeholder='Search...').render()
u'<input name="search" pattern="[a-z0-9]{2,30}" required="required" value="Search" type="search" placeholder="Search..." id="search"/>'

"""
def get_type(self):
return 'search'


class Url(Input):
"""URL input.

See: <http://dev.w3.org/html5/spec/Overview.html#url-state>

>>> Url(name='url', value='url').render()
u'<input type="url" id="url" value="url" name="url"/>'
"""
def get_type(self):
return 'url'


class Number(Input):
"""Number input.

See: <http://dev.w3.org/html5/spec/Overview.html#number-state>

>>> Number(name='num', min='0', max='10', step='2', value='5').render()
u'<input name="num" min="0" max="10" value="5" step="2" type="number" id="num"/>'
"""
def get_type(self):
return 'number'


class Range(Input):
"""Range input.

See: <http://dev.w3.org/html5/spec/Overview.html#range-state>

>>> Range(name='range', min='0', max='10', step='2', value='5').render()
u'<input name="range" min="0" max="10" value="5" step="2" type="range" id="range"/>'
"""
def get_type(self):
return 'range'


class Color(Input):
"""Color input.

Warning: Only Opera 11 supports.

See: <http://dev.w3.org/html5/spec/Overview.html#color-state>

>>> Color(name='color').render()
u'<input type="color" id="color" name="color"/>'
"""
def get_type(self):
return 'color'


class Password(Input):
"""Password input.

>>> Password(name='password', value='secret').render()
'<input type="password" id="password" value="secret" name="password"/>'
u'<input type="password" id="password" value="secret" name="password"/>'
"""

def get_type(self):
Expand All @@ -208,21 +300,58 @@ class Textarea(Input):
"""Textarea input.

>>> Textarea(name='foo', value='bar').render()
'<textarea id="foo" name="foo">bar</textarea>'
u'<textarea id="foo" name="foo">bar</textarea>'
"""
def render(self):
attrs = self.attrs.copy()
attrs['name'] = self.name
value = net.websafe(self.value or '')
return '<textarea %s>%s</textarea>' % (attrs, value)


class Datalist(Input):
"""Datalist input.

This is currently supported by only Firefox and Opera. Use it with caucious.
Datalist cannot be used separately. It must be bound to an input.

An example can be viewed on
<http://dev.w3.org/html5/spec/Overview.html#url-state>

>>> Datalist(name='list', args=[('a', 'b'), ('c', 'd')]).render()
u'<datalist id="list" name="list"><option label="a" value="b"><option label="c" value="d"></datalist>'
>>> Datalist(name='list', args=[['a', 'b'], ['c', 'd']]).render()
u'<datalist id="list" name="list"><option label="a" value="b"><option label="c" value="d"></datalist>'
>>> Datalist(name='list', args=['a', 'b', 'c', 'd']).render()
u'<datalist id="list" name="list"><option value="a"><option value="b"><option value="c"><option value="d"></datalist>'
"""
def __init__(self, name, args, *validators, **kwargs):
self.args = args
super(Datalist, self).__init__(name, *validators, **kwargs)

def render(self):
attrs = self.attrs.copy()
attrs['name'] = self.name
label_p = ''
x = '<datalist %s>' % attrs
for arg in self.args:
if isinstance(arg, (tuple, list)):
label_p = ' label="%s"' % net.websafe(arg[0])
label = net.websafe(arg[1])
else:
label = net.websafe(arg)
x += '<option%s value="%s">' % (label_p, label)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggest to close the option tag:
x += '<option%s value="%s" />' % (label_p, label)

x += '</datalist>'
return x


class Dropdown(Input):
r"""Dropdown/select input.

>>> Dropdown(name='foo', args=['a', 'b', 'c'], value='b').render()
'<select id="foo" name="foo">\n <option value="a">a</option>\n <option selected="selected" value="b">b</option>\n <option value="c">c</option>\n</select>\n'
u'<select id="foo" name="foo">\n <option value="a">a</option>\n <option selected="selected" value="b">b</option>\n <option value="c">c</option>\n</select>\n'
>>> Dropdown(name='foo', args=[('a', 'aa'), ('b', 'bb'), ('c', 'cc')], value='b').render()
'<select id="foo" name="foo">\n <option value="a">aa</option>\n <option selected="selected" value="b">bb</option>\n <option value="c">cc</option>\n</select>\n'
u'<select id="foo" name="foo">\n <option value="a">aa</option>\n <option selected="selected" value="b">bb</option>\n <option value="c">cc</option>\n</select>\n'
"""
def __init__(self, name, args, *validators, **attrs):
self.args = args
Expand Down Expand Up @@ -274,14 +403,14 @@ class Checkbox(Input):
"""Checkbox input.

>>> Checkbox('foo', value='bar', checked=True).render()
'<input checked="checked" type="checkbox" id="foo_bar" value="bar" name="foo"/>'
u'<input checked="checked" type="checkbox" id="foo_bar" value="bar" name="foo"/>'
>>> Checkbox('foo', value='bar').render()
'<input type="checkbox" id="foo_bar" value="bar" name="foo"/>'
u'<input type="checkbox" id="foo_bar" value="bar" name="foo"/>'
>>> c = Checkbox('foo', value='bar')
>>> c.validate('on')
True
>>> c.render()
'<input checked="checked" type="checkbox" id="foo_bar" value="bar" name="foo"/>'
u'<input checked="checked" type="checkbox" id="foo_bar" value="bar" name="foo"/>'
"""
def __init__(self, name, *validators, **attrs):
self.checked = attrs.pop('checked', False)
Expand Down Expand Up @@ -311,9 +440,9 @@ class Button(Input):
"""HTML Button.

>>> Button("save").render()
'<button id="save" name="save">save</button>'
u'<button id="save" name="save">save</button>'
>>> Button("action", value="save", html="<b>Save Changes</b>").render()
'<button id="action" value="save" name="action"><b>Save Changes</b></button>'
u'<button id="action" value="save" name="action"><b>Save Changes</b></button>'
"""
def __init__(self, name, *validators, **attrs):
super(Button, self).__init__(name, *validators, **attrs)
Expand All @@ -331,7 +460,7 @@ class Hidden(Input):
"""Hidden Input.

>>> Hidden(name='foo', value='bar').render()
'<input type="hidden" id="foo" value="bar" name="foo"/>'
u'<input type="hidden" id="foo" value="bar" name="foo"/>'
"""
def is_hidden(self):
return True
Expand All @@ -343,7 +472,7 @@ class File(Input):
"""File input.

>>> File(name='f').render()
'<input type="file" id="f" name="f"/>'
u'<input type="file" id="f" name="f"/>'
"""
def get_type(self):
return 'file'
Expand Down