Binary file static/icons/treelist-more.png has changed
Binary file static/icons/treelist-open.png has changed
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/static/js/treelist.js Sun Dec 19 18:39:54 2010 +0200
@@ -0,0 +1,24 @@
+/**
+ * Dynamically expandable tree-form lists
+ */
+
+/* Document startup */
+function treelist_init ()
+{
+ $$('.treelist li').each(function (item_li) {
+ item_handle = item_li.down('div.item');
+
+ item_handle.on('click', function (event, element) {
+ // only target clicks directly in div, not in element inside it
+ if (!element.match('div.item'))
+ return;
+
+ if (item_li.hasClassName('more'))
+ item_li.toggleClassName('open');
+ });
+ });
+}
+
+
+Event.on(window, 'load', treelist_init);
+
--- a/static/layout.css Sat Dec 18 15:09:47 2010 +0200
+++ b/static/layout.css Sun Dec 19 18:39:54 2010 +0200
@@ -111,6 +111,8 @@
div#content
{
margin-left: 180px;
+
+ padding: 10px 50px 10px 25px;
}
/* Full-width Footer at bottom of page */
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/static/treelist.css Sun Dec 19 18:39:54 2010 +0200
@@ -0,0 +1,85 @@
+/*
+ * Structured tree-form lists
+ */
+
+ul.treelist,
+.treelist ul
+{
+ list-style-type: none;
+
+ padding: 0px;
+
+ /* Lists of items are vertically separated, visually more so than plain list items */
+ margin: 10px 0px;
+}
+
+.treelist li
+{
+ /* Items in a list are vertically separated */
+ margin-top: 5px;
+}
+
+.treelist li ul
+{
+ /* A nested list is indented */
+ padding-left: 2em;
+}
+
+.treelist li > div
+{
+ /* The handle fits to the left of the text block */
+ padding-left: 28px;
+
+ /* The item description need to be full-width */
+ width: 50%;
+}
+
+.treelist li > div a
+{
+ /* The text inside the li consitutes the clickable part */
+ display: block;
+
+ /* The contents are adequately spaced */
+ padding: 10px;
+}
+
+/*
+ * Treelist item states
+ */
+.treelist li > ul
+{
+ /* Nested lists are closed per default */
+ display: none;
+}
+
+.treelist li.open > ul
+{
+ /* The sub-list inside an open item is displayed */
+ display: block;
+}
+
+.treelist li > div a
+{
+ /* A normal treelist item does not look special */
+ background-color: #ffffff;
+ border: thin solid #e8e8e8;
+}
+
+.treelist li.more > div
+{
+ background: transparent url(icons/treelist-more.png) no-repeat center left;
+}
+
+.treelist li.open > div
+{
+ background: transparent url(icons/treelist-open.png) no-repeat center left;
+}
+
+/*
+ * Checkbox styles
+ */
+.treelist li > div input[type=checkbox]
+{
+ background: #ffffff;
+ border: 1px solid green;
+}
--- a/svv/wsgi.py Sat Dec 18 15:09:47 2010 +0200
+++ b/svv/wsgi.py Sun Dec 19 18:39:54 2010 +0200
@@ -10,7 +10,9 @@
class AppHandler (object):
"""
- Per-request handler context
+ Per-request handler type, containing the request context and implementing the response.
+
+ Works on a generic level, i.e. WSGI/Werkzeug URL-mapped request in, response out.
"""
# default content type for response
@@ -28,30 +30,32 @@
Handle request that was mapped to ourselves via the URL routing, using given dict of values from URL.
"""
- # render
- html = unicode(self.render(**url_values))
-
- # XXX: unicode
- return Response(html, mimetype='text/html')
-
- def render (self, **args) :
- """
- Handling a GET request, returning the proper response HTML.
- """
-
raise NotImplementedError()
import html
from html import tags
-class Index (AppHandler) :
- def render (self) :
+class PageHandler (AppHandler) :
+ """
+ Specialized AppHandler for normal HTML page views.
+
+ Renders the layout template and HTML.
+ """
+
+ def respond (self, url_values) :
title = "Index"
- css = ["/static/layout.css", "/static/style.css"]
+ css = ["/static/layout.css", "/static/style.css", "/static/treelist.css"]
+ scripts = [
+ "/static/js/prototype.js",
+ "/static/js/scriptaculous/scriptaculous.js",
+ "/static/js/treelist.js"
+ ]
head = (
tags.title(title),
(tags.link(rel='Stylesheet', type="text/css", href=src) for src in css),
+ # XXX: script can't self-close >_<
+ (tags.script(src=src)(" ") for src in scripts),
)
header = ("Foo List")
@@ -59,10 +63,9 @@
menu = [tags.ul(tags.li(tags.a(href='#')(name)) for name in ['Menu A', 'Menu B', 'Menu C'])]
footer = ("Copyright?")
- content = (
- tags.ul(tags.li("Item #%d" % i) for i in xrange(10))
- )
-
+ # render page HTML
+ content = self.render(**url_values)
+
layout = (
tags.div(id='header')(header),
tags.div(id='container')(
@@ -73,7 +76,67 @@
tags.div(id='footer')(footer),
)
- return html.document(head, layout)
+ # perform the actual rendering (run generators etc.)
+ html_text = unicode(html.document(head, layout))
+
+ # response object
+ # XXX: unicode?
+ return Response(html_text, mimetype='text/html')
+
+ def render (self, **args) :
+ """
+ Render and return HTML for page content.
+
+ XXX: must be HTML object, support just text?
+ """
+
+ raise NotImplementedError()
+
+class Index (PageHandler) :
+ DATA = (
+ (100, "Top A", []),
+ (200, "Top B", [
+ (210, "Mid BA", [
+ (211, "Sub BAA", []),
+ (212, "Sub BAB", []),
+ ]),
+ (220, "Mid BB", []),
+ ]),
+ (300, "Top C", [
+ (310, "Mid CA", []),
+ (320, "Mid CB", []),
+ ]),
+ )
+
+ def render (self) :
+ # build data set
+ data = self.DATA
+
+ def render_item (id, text, children) :
+ return tags.li(
+ tags.div(class_='item')(
+ # item text
+ tags.a(tags.input(type='checkbox', name_=id, checked=(
+ 'checked' if self.request.form.get(str(id)) else None
+ )), text, class_='text'),
+ ),
+
+ # children
+ tags.ul(render_item(*item) for item in children if item) if children else None,
+
+ #
+ class_=('more open' if children else None),
+ )
+
+ # render it
+ return (
+ tags.h3("Item list"),
+ tags.form(action='.', method='post')(
+ tags.ul(class_='treelist')(render_item(*item) for item in data),
+ tags.input(type='submit'),
+ ),
+ )
+
class WSGIApp (object) :
"""