1 # Author: Aleksey Gurtovoy
2 # Contact: agurtovoy@meta-comm.com
5 # Copyright: This module has been placed in the public domain.
7 from docutils
import writers
8 from docutils
.writers
import html4css1
17 class Writer(writers
.Writer
):
19 settings_spec
= html4css1
.Writer
.settings_spec
+ (
20 'HTML/Frames-Specific Options',
21 """The HTML --stylesheet option's default is set to """
26 settings_default_overrides
= { 'stylesheet': 'frames.css' }
27 relative_path_settings
= ('stylesheet_path',)
28 config_section
= 'html4frames writer'
29 config_section_dependencies
= ('writers',)
32 self
.__super
= writers
.Writer
33 self
.__super
.__init
__(self
)
34 self
.translator
= frame_pages_translator
36 def translate(self
, document
, page_files_dir
, extension
):
37 visitor
= self
.translator(document
, page_files_dir
, extension
)
38 document
.walkabout(visitor
)
39 self
.output
= None # needed by writers.Writer
40 return visitor
.result()
42 def write(self
, document
, destination
):
43 self
.language
= docutils
.languages
.get_language(
44 document
.settings
.language_code
)
46 index_file_path
= destination
.destination_path
47 index_file_name
= os
.path
.basename(index_file_path
)
48 (page_files_dir
, extension
) = os
.path
.splitext(index_file_name
)
49 chunk_files_path
= os
.path
.join(
50 os
.path
.dirname(index_file_path
)
54 (index_text
, pages
) = self
.translate(document
, page_files_dir
, extension
)
56 if len(pages
) > 0 and not os
.path
.exists(chunk_files_path
):
57 os
.mkdir(chunk_files_path
)
59 destination
.write(index_text
)
61 for c
in pages
.keys():
62 chunk_file
= self
.make_chunk_output(
64 , os
.path
.join(chunk_files_path
,c
+ extension
)
67 chunk_file
.write(pages
[c
])
69 def make_chunk_output(self
, destination
, destination_path
):
70 return docutils
.io
.FileOutput(
71 destination_path
=destination_path
72 , encoding
=destination
.encoding
73 , error_handler
=destination
.error_handler
77 class frame_pages_translator(docutils
.nodes
.NodeVisitor
):
79 page_translator
= html4css1
.HTMLTranslator
83 nav_bar_separator
= '<hr class="navigation-bar-separator">\n'
84 re_href
= re
.compile('(href=")(.*?)(")')
86 def __init__(self
, document
, page_files_dir
, extension
):
87 self
.__super
= docutils
.nodes
.NodeVisitor
88 self
.__super
.__init
__(self
, document
)
90 self
.section_level
= 0
91 self
.max_chunk_level
= 0
92 self
.settings
= document
.settings
93 self
.reporter
= document
.reporter
94 self
.visitors
= [self
.__page
_translator
(document
, self
.docframe
)]
95 self
.toc_nav_builder
= None
96 self
.page_files_dir
= page_files_dir
97 self
.extension
= extension
100 self
.tocframe_page
= _toc_entry(id='%s_toc' % self
.page_files_dir
)
101 self
.home_page
= _toc_entry(id='%s_home' % self
.page_files_dir
)
102 self
.toc
= self
.home_page
103 self
.in_home_page
= 0
105 def active_visitor(self
):
106 return self
.visitors
[-1]
109 self
.pages
[self
.home_page
.id] = self
.visitors
[0].astext()
110 html_spec
= self
.visitors
[0]
111 index_page
= ''.join(
112 html_spec
.head_prefix
+ html_spec
.head
+ html_spec
.stylesheet
113 + ['</head>\n<frameset cols="%d%%,%d%%">\n'
114 % (self
.tocframe_width
, 100 - self
.tocframe_width
)]
115 + ['<frame name="%s" src="%s" scrolling="auto">\n'
116 % (self
.tocframe
, self
.__chunk
_ref
(self
.tocframe_page
.id, 1))]
117 + ['<frame name="%s" src="%s" scrolling="auto">\n'
118 % (self
.docframe
, self
.__chunk
_ref
(self
.home_page
.id, 1))]
119 + ['</frameset>\n</html>\n']
122 return (index_page
, self
.pages
)
125 def visit_section(self
, node
):
126 self
.section_level
+= 1
127 if self
.section_level
<= self
.max_chunk_level
:
128 section_id
= node
.get('id')
129 if self
.page_subtoc
.has_key(section_id
):
130 node
.append(self
.page_subtoc
[section_id
])
132 self
.visitors
.append(self
.__page
_translator
(
133 self
.__node
_to
_document
(node
)
137 self
.toc
= self
.toc
.next
138 self
.active_visitor().nav_bar
= self
.__nav
_bar
(self
.toc
)
139 self
.active_visitor().body
.append(self
.active_visitor().nav_bar
)
140 self
.active_visitor().body
.append(self
.nav_bar_separator
)
142 self
.active_visitor().visit_section(node
)
144 def depart_section(self
, node
):
145 self
.active_visitor().depart_section(node
)
146 if self
.section_level
<= self
.max_chunk_level
:
147 self
.active_visitor().body
.append(self
.nav_bar_separator
)
148 self
.active_visitor().body
.append(self
.active_visitor().nav_bar
)
149 visitor
= self
.visitors
.pop()
150 self
.pages
[node
.get('id')] = visitor
.astext()
152 self
.section_level
-= 1
155 def visit_topic(self
, node
):
156 if node
.get('class') == 'contents' and not self
.in_home_page
:
157 self
.toc_nav_builder
= _toc_nav_builder(home
=self
.home_page
)
158 self
.visitors
.append(self
.__page
_translator
(
159 self
.__node
_to
_document
(node
)
163 self
.active_visitor().visit_topic(node
)
165 def depart_topic(self
, node
):
166 if node
.get('class') == 'contents' and not self
.in_home_page
:
167 self
.toc_nav_builder
= None
168 tocframe_visitor
= self
.visitors
.pop()
169 self
.pages
[self
.tocframe_page
.id] = tocframe_visitor
.astext()
170 self
.in_home_page
= 1
171 home_page_toc
= self
.page_subtoc
[self
.home_page
.id]
172 home_page_toc
.walkabout(self
)
173 self
.in_home_page
= 0
175 self
.active_visitor().depart_topic(node
)
178 def visit_bullet_list(self
, node
):
179 if self
.__is
_in
_main
_toc
():
180 section_id
= self
.toc_nav_builder
.last_visited
.id
181 self
.page_subtoc
[section_id
] = self
.__subtoc
(node
)
182 self
.toc_nav_builder
.subsection()
183 self
.section_level
+= 1
184 if self
.section_level
> self
.max_chunk_level
:
185 self
.max_chunk_level
= self
.section_level
187 self
.active_visitor().visit_bullet_list(node
)
189 def depart_bullet_list(self
, node
):
190 self
.active_visitor().depart_bullet_list(node
)
191 if self
.__is
_in
_main
_toc
():
192 self
.toc_nav_builder
.up()
193 self
.section_level
-= 1
196 def visit_reference(self
, node
):
197 self
.__adjust
_node
_uri
(node
, 'refuri')
198 self
.active_visitor().visit_reference(node
)
199 if node
.has_key('refuri'):
200 self
.active_visitor().body
[-1] = self
.__add
_target
_attr
(
201 self
.active_visitor().body
[-1]
205 if node
.has_key('refid'):
206 section_id
= node
.get('refid')
207 self
.active_visitor().body
[-1] = self
.__replace
_href
(
208 self
.__chunk
_ref
(section_id
)
209 , self
.active_visitor().body
[-1]
212 if self
.__is
_in
_main
_toc
():
213 self
.toc_nav_builder
.along(section_id
)
216 def depart_reference(self
, node
):
217 self
.active_visitor().depart_reference(node
)
220 def visit_image(self
, node
):
221 self
.__adjust
_node
_uri
(node
, 'uri')
222 self
.active_visitor().visit_image(node
)
224 def depart_image(self
, node
):
225 self
.active_visitor().depart_image(node
)
228 def visit_title(self
, node
):
229 self
.active_visitor().visit_title(node
)
230 if node
.has_key('refid'):
231 self
.active_visitor().body
[-1] = self
.__replace
_href
(
232 '%s#%s' % ( self
.__chunk
_ref
(self
.toc
.up
.id), node
.get('refid') )
233 , self
.active_visitor().body
[-1]
236 def depart_title(self
, node
):
237 self
.active_visitor().depart_title(node
)
240 def __page_translator(self
, document
, frame
):
241 result
= self
.page_translator(document
)
242 result
.body_prefix
= ['</head>\n<body class="%s">\n' % frame
]
245 def __subtoc(self
, node
):
246 def _auto_toc_filter(node
,root
=node
):
247 return node
!= root \
248 and isinstance(node
, docutils
.nodes
.bullet_list
)
250 node
['id'] = 'outline'
251 if node
.get('class', '') == '': node
['class'] = 'toc'
253 self
.__node
_to
_document
(node
)
258 def __add_target_attr(self
, href
, target
):
259 return self
.re_href
.sub(r
'\1\2\3 target="%s"' % target
, href
)
261 def __replace_href(self
, new
, old
):
262 if not self
.__is
_in
_main
_toc
():
263 return self
.re_href
.sub(r
'\1%s\3' % new
, old
)
265 return self
.re_href
.sub(
266 r
'\1%s\3 target="%s"' % (new
, self
.docframe
)
270 def __nav_bar(self
, toc_node
):
271 return '<span class="navigation-bar">%s</span>\n' \
272 % '<span class="navigation-group-separator"> | </span>'.join([
273 self
.__nav
_group
(['Prev', 'Next'], [toc_node
.prev
, toc_node
.next
])
274 , self
.__nav
_group
(['Back', 'Along'], [toc_node
.back
, toc_node
.along
])
275 , self
.__nav
_group
(['Up', 'Home'], [toc_node
.up
, self
.home_page
])
278 def __nav_group(self
, labels
, nodes
):
279 return '<span class="navigation-group">%s</span>' % ' '.join(
280 map(lambda l
,n
: self
.__toc
_node
_link
(l
, n
), labels
, nodes
)
284 def __node_to_document(self
, node
):
285 node
.settings
= self
.settings
286 node
.reporter
= self
.reporter
289 def __adjust_node_uri(self
, node
, attr
):
290 if node
.has_key(attr
):
292 if _is_uri_relative(src_uri
):
293 node
[attr
] = '../%s' % src_uri
295 def __toc_node_link(self
, name
, toc_node
):
296 if not toc_node
: return name
297 return '<a href="%s" class="navigation-link">%s</a>' % (
298 self
.__chunk
_ref
(toc_node
.id)
303 def __chunk_ref(self
, chunk_name
, from_index
=0):
305 return './%s/%s%s' % (self
.page_files_dir
, chunk_name
, self
.extension
)
307 return './%s%s' % (chunk_name
, self
.extension
)
309 def __is_in_main_toc(self
):
310 return self
.toc_nav_builder
313 def _setup_forwarding(visitor
):
314 for name
in docutils
.nodes
.node_class_names
:
315 if not getattr(visitor
, 'visit_' + name
, None):
316 def forward_visit(self
, node
, name
=name
):
317 getattr(self
.active_visitor(), 'visit_' + name
)(node
)
318 def forward_depart(self
, node
, name
=name
):
319 getattr(self
.active_visitor(), 'depart_' + name
)(node
)
321 setattr(visitor
, 'visit_' + name
, forward_visit
)
322 setattr(visitor
, 'depart_' + name
, forward_depart
)
324 _setup_forwarding(frame_pages_translator
)
327 def _filter_tree(document
, filter):
329 class filter_tree_copy_visitor(docutils
.nodes
.TreeCopyVisitor
):
331 def __init__(self
, document
, filter):
332 self
.__super
= docutils
.nodes
.TreeCopyVisitor
333 self
.__super
.__init
__(self
, document
)
336 def default_visit(self
, node
):
337 if self
.filter(node
):
338 raise docutils
.nodes
.SkipNode()
340 self
.__super
.default_visit(self
, node
)
342 visitor
= filter_tree_copy_visitor(document
, filter)
343 document
.walkabout(visitor
)
344 return visitor
.get_tree_copy()
347 def _is_uri_relative(uri
):
348 (scheme
, location
, path
, query
, fragment
) = urlparse
.urlsplit(uri
)
349 return len(scheme
) == 0 and len(location
) == 0
352 class _toc_nav_builder
:
354 def __init__(self
, home
):
355 self
.last_visited
= self
.last_sibling
= home
357 def subsection(self
):
358 parent
= self
.last_visited
359 self
.last_visited
= _toc_entry(id=None)
360 self
.last_visited
.up
= self
.last_visited
.prev
= parent
361 parent
.next
= self
.last_visited
362 self
.last_sibling
= self
.last_visited
364 def along(self
, section_id
):
365 last
= self
.last_visited
367 self
.last_visited
= _toc_entry(id=section_id
)
368 self
.last_visited
.prev
= last
369 self
.last_visited
.back
= self
.last_sibling
370 self
.last_visited
.up
= self
.last_sibling
.up
371 last
.next
= self
.last_sibling
.along
= self
.last_visited
372 self
.last_sibling
= self
.last_visited
374 self
.last_visited
.id = section_id
377 self
.last_sibling
= self
.last_sibling
.up
381 def __init__(self
, id):
383 self
.prev
= self
.next
= self
.back
= self
.along
= self
.up
= None