removed obsolete issues (many of them fixed with AE)
[docutils.git] / sandbox / agurtovoy / html_frames / writers / html4frames.py
blob78031436a88b850fdfc5cff2d8a05b2bf81cf4b0
1 # Author: Aleksey Gurtovoy
2 # Contact: agurtovoy@meta-comm.com
3 # Revision: $Revision$
4 # Date: $Date$
5 # Copyright: This module has been placed in the public domain.
7 from docutils import writers
8 from docutils.writers import html4css1
9 import docutils.nodes
11 import urlparse
12 import os.path
13 import os
14 import re
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 """
22 '"frames.css".',
26 settings_default_overrides = { 'stylesheet': 'frames.css' }
27 relative_path_settings = ('stylesheet_path',)
28 config_section = 'html4frames writer'
29 config_section_dependencies = ('writers',)
31 def __init__(self):
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)
51 , page_files_dir
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(
63 destination
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
80 tocframe = 'tocframe'
81 docframe = 'docframe'
82 tocframe_width = 30
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
98 self.pages = {}
99 self.page_subtoc = {}
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]
108 def result(self):
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)
134 , self.docframe
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)
160 , self.tocframe
162 else:
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
174 else:
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]
202 , '_top'
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]
243 return result
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'
252 return _filter_tree(
253 self.__node_to_document(node)
254 , _auto_toc_filter
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)
264 else:
265 return self.re_href.sub(
266 r'\1%s\3 target="%s"' % (new, self.docframe)
267 , old
270 def __nav_bar(self, toc_node):
271 return '<span class="navigation-bar">%s</span>\n' \
272 % '<span class="navigation-group-separator">&nbsp;|&nbsp;</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>' % '&nbsp;'.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
287 return node
289 def __adjust_node_uri(self, node, attr):
290 if node.has_key(attr):
291 src_uri = node[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)
299 , name
303 def __chunk_ref(self, chunk_name, from_index=0):
304 if from_index:
305 return './%s/%s%s' % (self.page_files_dir, chunk_name, self.extension)
306 else:
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)
334 self.filter = filter
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
366 if last.id:
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
373 else:
374 self.last_visited.id = section_id
376 def up(self):
377 self.last_sibling = self.last_sibling.up
380 class _toc_entry:
381 def __init__(self, id):
382 self.id = id
383 self.prev = self.next = self.back = self.along = self.up = None