1# Copyright (c) 2004 Divmod.
2# See LICENSE for details.
3
4
5"""
6Easy to use box layout model. Works cross-browser on IE 6, Safari, Opera, and Mozilla (using Evil Hack(tm)).
7Until a known bug in Mozilla is fixed, the Evil Hack will be in place. This evil hack prevents text inside of a Block from
8being selected.
9
10 - line: a horizontal box grouping
11 - block: a vertical, flowing box grouping
12
13Example::
14
15  text = "Hello lots of text " * 20
16
17  line(width='100%')[
18    box(width='50%')[text],
19    box(width='50%')[text]]
20  # Note there is a mozilla bug right now where the second, flowing percentage is calculated using only
21  # the empty space, so the second value here would have to be 100%; This module will soon add browser
22  # sniffing to detect if mozilla is being used and adjust percentages accordingly. For now, use pixel, em, or
23  # point values instead.
24
25Experimental feature: Keyword arguments to the line and box protos are converted into css styles:
26box(color='red') => <span style="color: red" />
27
28Known Mozilla bugs
29==================
30
31If you use border, padding, or margin, mozilla shows weird rendering artifacts. background color and
32background images generally appear to be safe.
33
34It doesn't appear to be possible to set the vertical-align in some cases in mozilla.
35"""
36
37from zope.interface import implements
38
39from nevow import static
40from nevow import inevow
41from nevow import tags
42
43boxStyle = tags.xml("""
44span.nevow-blocks-block {
45    display: inline-block;
46    -moz-binding: url('/mozbinding#inlineblock'); }
47
48div.nevow-blocks-line {
49    margin: 0px; padding: 0px }
50
51.expanded {
52    display: block; }
53
54.collapsed {
55    display: none; }
56
57.visibilityImage {
58    margin-right: 5px; }
59""")
60
61js = tags.xml( """
62function collapse(node, collapsedText, expandedText) {
63    for (var i = 0; i < node.childNodes.length; i++) {
64        var childNode = node.childNodes[i]
65        if (childNode.className == 'visibilityImage') {
66            var img = childNode;
67        } else if (childNode.className == 'headText') {
68            var head = childNode;
69        }
70    }
71    for (var i = 0; i < node.parentNode.childNodes.length; i++) {
72        var childNode = node.parentNode.childNodes[i]
73        if (childNode.className == 'collapsed' || childNode.className == 'expanded') {
74            var next = childNode;
75        }
76    }
77    if (next.className == 'collapsed') {
78        img.src = '/images/outline-expanded.png';
79        next.className = 'expanded';
80        head.innerHTML = expandedText;
81    } else {
82        img.src = '/images/outline-collapsed.png';
83        next.className = 'collapsed';
84        head.innerHTML = collapsedText;
85    }
86}//""")
87
88blocks_glue = [
89    tags.style(type="text/css")[ boxStyle ],
90    tags.script(type="text/javascript")[ tags.comment[js] ]]
91
92
93mozBinding = """<?xml version="1.0"?>
94
95<bindings xmlns="http://www.mozilla.org/xbl"
96          xmlns:xbl="http://www.mozilla.org/xbl"
97          xmlns:html="http://www.w3.org/1999/xhtml"
98          xmlns:xul="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul">
99
100<binding id="inlineblock">
101  <content>
102  <html:style type="text/css">
103    .nevow-inline-table { display: inline-table; }
104    .nevow-inline-table-row { display: table-row; }
105    .nevow-inline-table-cell { display: table-cell; }
106  </html:style>
107    <html:span class="nevow-inline-table">
108    <html:span class="nevow-inline-table-row">
109     <html:span class="nevow-inline-table-cell" xbl:inherits="style">
110          <children/>
111     </html:span>
112     </html:span>
113     </html:span>
114  </content>
115</binding>
116
117</bindings>
118"""
119
120blocks_child = static.Data(mozBinding, 'text/xml; name="inline-block.xml"')
121
122
123class _Blocks(object):
124    def __init__(self, tag, className):
125        self.tag = tag
126        self.className = className
127
128    def __call__(self, **kw):
129        """Make and return a new block. If height or width is specified, they may be
130        of any css-supported measurement format, such as '200px' or '50%'.
131
132        If height or width is not specified, the block will "shrink wrap".
133
134        Interesting experiment: kw arguments to __call__ are treated like css style key
135        value pairs. For example, block(color='blue', background_color='red') will translate
136        to style="color: blue; background-color: red". _ is mapped to -. Not sure if this will
137        work but it will be interesting to see if it is useful to use.
138        """
139        return self.tag(
140            _class=self.className,
141            style='; '.join(
142                [': '.join((k.replace('_', '-'), v))
143                for (k, v) in kw.items()]))
144
145
146block = _Blocks(tags.span, 'nevow-blocks-block')
147line = _Blocks(tags.div, 'nevow-blocks-line')
148
149
150class collapser(object):
151    """Render a fragment of html with a head and a body.
152    The body can be in two states, expanded and collapsed.
153    When the body is in collapsed state, it is not visible in the browser.
154    Clicking on the head area causes the visibility state of the body
155    to toggle.
156
157    TODO: This should be rewritten to check for patterns and slots so you
158    could have it use table or paragraph tags or whatever instead of a span
159    and a div, and you can omit the visibility image if desired (js would have
160    to change too)
161    """
162    implements(inevow.IRenderer)
163
164    def __init__(self, headCollapsed, headExpanded, body, collapsed=True):
165        self.headCollapsed = headCollapsed
166        self.headExpanded = headExpanded
167        self.body = body
168        if collapsed:
169            self.collapsed = 'collapsed'
170        else:
171            self.collapsed = 'expanded'
172
173    def rend(self, ctx, data):
174        return (tags.span(
175            _class="collapser-line",
176            onclick=(
177                "collapse(this, '",
178                self.headCollapsed,
179                "', '",
180                self.headExpanded,
181                "');"))[
182            tags.img(_class="visibilityImage", src="/images/outline-%s.png" % self.collapsed),
183            tags.span(_class="headText", style="color: blue; text-decoration: underline; cursor: pointer;")[
184                self.collapsed == 'collapsed' and self.headCollapsed or self.headExpanded ]
185        ],
186        tags.xml('&nbsp;'),
187        tags.div(_class=self.collapsed)[
188            self.body
189        ])
190
191