[CHG] initial port to new theme
This commit is contained in:
@@ -0,0 +1,663 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
import os.path
|
||||
import posixpath
|
||||
import re
|
||||
import urllib
|
||||
|
||||
from docutils import nodes
|
||||
from sphinx import addnodes, util
|
||||
from sphinx.locale import admonitionlabels
|
||||
|
||||
|
||||
def _parents(node):
|
||||
while node.parent:
|
||||
node = node.parent
|
||||
yield node
|
||||
|
||||
class BootstrapTranslator(nodes.NodeVisitor, object):
|
||||
head_prefix = 'head_prefix'
|
||||
head = 'head'
|
||||
stylesheet = 'stylesheet'
|
||||
body_prefix = 'body_prefix'
|
||||
body_pre_docinfo = 'body_pre_docinfo'
|
||||
docinfo = 'docinfo'
|
||||
body_suffix = 'body_suffix'
|
||||
subtitle = 'subtitle'
|
||||
header = 'header'
|
||||
footer = 'footer'
|
||||
html_prolog = 'html_prolog'
|
||||
html_head = 'html_head'
|
||||
html_title = 'html_title'
|
||||
html_subtitle = 'html_subtitle'
|
||||
|
||||
# <meta> tags
|
||||
meta = [
|
||||
'<meta http-equiv="X-UA-Compatible" content="IE=edge">',
|
||||
'<meta name="viewport" content="width=device-width, initial-scale=1">'
|
||||
]
|
||||
|
||||
def __init__(self, builder, document):
|
||||
super(BootstrapTranslator, self).__init__(document)
|
||||
self.builder = builder
|
||||
self.body = []
|
||||
self.fragment = self.body
|
||||
self.html_body = self.body
|
||||
# document title
|
||||
self.title = []
|
||||
self.start_document_title = 0
|
||||
self.first_title = False
|
||||
|
||||
self.context = []
|
||||
self.section_level = 0
|
||||
|
||||
self.highlightlang = self.highlightlang_base = self.builder.config.highlight_language
|
||||
self.highlightopts = getattr(builder.config, 'highlight_options', {})
|
||||
|
||||
self.first_param = 1
|
||||
self.optional_param_level = 0
|
||||
self.required_params_left = 0
|
||||
self.param_separator = ','
|
||||
|
||||
def encode(self, text):
|
||||
return unicode(text).translate({
|
||||
ord('&'): u'&',
|
||||
ord('<'): u'<',
|
||||
ord('"'): u'"',
|
||||
ord('>'): u'>',
|
||||
0xa0: u' '
|
||||
})
|
||||
|
||||
def starttag(self, node, tagname, **attributes):
|
||||
tagname = unicode(tagname).lower()
|
||||
|
||||
# extract generic attributes
|
||||
attrs = {name.lower(): value for name, value in attributes.iteritems()}
|
||||
attrs.update(
|
||||
(name, value) for name, value in node.attributes.iteritems()
|
||||
if name.startswith('data-')
|
||||
)
|
||||
|
||||
prefix = []
|
||||
postfix = []
|
||||
|
||||
# handle possibly multiple ids
|
||||
assert 'id' not in attrs, "starttag can't be passed a single id attribute, use a list of ids"
|
||||
ids = node.get('ids', []) + attrs.pop('ids', [])
|
||||
if ids:
|
||||
_ids = iter(ids)
|
||||
attrs['id'] = next(_ids)
|
||||
postfix.extend(u'<i id="{}"></i>'.format(_id) for _id in _ids)
|
||||
|
||||
# set CSS class
|
||||
classes = set(node.get('classes', []) + attrs.pop('class', '').split())
|
||||
if classes:
|
||||
attrs['class'] = u' '.join(classes)
|
||||
|
||||
return u'{prefix}<{tag} {attrs}>{postfix}'.format(
|
||||
prefix=u''.join(prefix),
|
||||
tag=tagname,
|
||||
attrs=u' '.join(u'{}="{}"'.format(name, self.attval(value))
|
||||
for name, value in attrs.iteritems()),
|
||||
postfix=u''.join(postfix),
|
||||
)
|
||||
# only "space characters" SPACE, CHARACTER TABULATION, LINE FEED,
|
||||
# FORM FEED and CARRIAGE RETURN should be collapsed, not al White_Space
|
||||
def attval(self, value, whitespace=re.compile(u'[ \t\n\f\r]')):
|
||||
return self.encode(whitespace.sub(u' ', unicode(value)))
|
||||
|
||||
def astext(self):
|
||||
return u''.join(self.body)
|
||||
|
||||
def unknown_visit(self, node):
|
||||
print "unknown node", node.__class__.__name__
|
||||
self.body.append(u'[UNKNOWN NODE {}]'.format(node.__class__.__name__))
|
||||
raise nodes.SkipNode
|
||||
|
||||
def visit_highlightlang(self, node):
|
||||
self.highlightlang = node['lang']
|
||||
def depart_highlightlang(self, node):
|
||||
pass
|
||||
|
||||
def visit_document(self, node):
|
||||
self.first_title = True
|
||||
def depart_document(self, node):
|
||||
pass
|
||||
|
||||
def visit_section(self, node):
|
||||
# close "parent" or preceding section, unless this is the opening of
|
||||
# the first section
|
||||
if self.section_level:
|
||||
self.body.append(u'</section>')
|
||||
self.section_level += 1
|
||||
|
||||
self.body.append(self.starttag(node, 'section'))
|
||||
def depart_section(self, node):
|
||||
self.section_level -= 1
|
||||
# close last section of document
|
||||
if not self.section_level:
|
||||
self.body.append(u'</section>')
|
||||
|
||||
def is_compact_paragraph(self, node):
|
||||
parent = node.parent
|
||||
if isinstance(parent, (nodes.document, nodes.compound,
|
||||
addnodes.desc_content,
|
||||
addnodes.versionmodified)):
|
||||
# Never compact paragraphs in document or compound.
|
||||
return False
|
||||
|
||||
for key, value in node.attlist():
|
||||
# we can ignore a few specific classes, all other non-default
|
||||
# attributes require that a <p> node remains
|
||||
if key != 'classes' or value not in ([], ['first'], ['last'], ['first', 'last']):
|
||||
return False
|
||||
|
||||
first = isinstance(node.parent[0], nodes.label)
|
||||
for child in parent.children[first:]:
|
||||
# only first paragraph can be compact
|
||||
if isinstance(child, nodes.Invisible):
|
||||
continue
|
||||
if child is node:
|
||||
break
|
||||
return False
|
||||
parent_length = len([
|
||||
1 for n in parent
|
||||
if not isinstance(n, (nodes.Invisible, nodes.label))
|
||||
])
|
||||
return parent_length == 1
|
||||
|
||||
def visit_paragraph(self, node):
|
||||
if self.is_compact_paragraph(node):
|
||||
self.context.append(u'')
|
||||
return
|
||||
self.body.append(self.starttag(node, 'p'))
|
||||
self.context.append(u'</p>')
|
||||
def depart_paragraph(self, node):
|
||||
self.body.append(self.context.pop())
|
||||
def visit_compact_paragraph(self, node):
|
||||
pass
|
||||
def depart_compact_paragraph(self, node):
|
||||
pass
|
||||
|
||||
def visit_literal_block(self, node):
|
||||
if node.rawsource != node.astext():
|
||||
# most probably a parsed-literal block -- don't highlight
|
||||
self.body.append(self.starttag(node, 'pre'))
|
||||
return
|
||||
lang = self.highlightlang
|
||||
highlight_args = node.get('highlight_args', {})
|
||||
if 'language' in node:
|
||||
# code-block directives
|
||||
lang = node['language']
|
||||
highlight_args['force'] = True
|
||||
linenos = node.get('linenos', False)
|
||||
if lang is self.highlightlang_base:
|
||||
# only pass highlighter options for original language
|
||||
opts = self.highlightopts
|
||||
else:
|
||||
opts = {}
|
||||
|
||||
def warner(msg):
|
||||
self.builder.warn(msg, (self.builder.current_docname, node.line))
|
||||
highlighted = self.builder.highlighter.highlight_block(
|
||||
node.rawsource, lang, opts=opts, warn=warner, linenos=linenos,
|
||||
**highlight_args)
|
||||
self.body.append(self.starttag(node, 'div', CLASS='highlight-%s' % lang))
|
||||
self.body.append(highlighted)
|
||||
self.body.append(u'</div>\n')
|
||||
raise nodes.SkipNode
|
||||
def depart_literal_block(self, node):
|
||||
self.body.append(u'</pre>')
|
||||
|
||||
def visit_bullet_list(self, node):
|
||||
self.body.append(self.starttag(node, 'ul'))
|
||||
def depart_bullet_list(self, node):
|
||||
self.body.append(u'</ul>')
|
||||
def visit_enumerated_list(self, node):
|
||||
self.body.append(self.starttag(node, 'ol'))
|
||||
def depart_enumerated_list(self, node):
|
||||
self.body.append(u'</ol>')
|
||||
def visit_list_item(self, node):
|
||||
self.body.append(self.starttag(node, 'li'))
|
||||
def depart_list_item(self, node):
|
||||
self.body.append(u'</li>')
|
||||
def visit_definition_list(self, node):
|
||||
self.body.append(self.starttag(node, 'dl'))
|
||||
def depart_definition_list(self, node):
|
||||
self.body.append(u'</dl>')
|
||||
def visit_definition_list_item(self, node):
|
||||
pass
|
||||
def depart_definition_list_item(self, node):
|
||||
pass
|
||||
def visit_term(self, node):
|
||||
self.body.append(self.starttag(node, 'dt'))
|
||||
def depart_term(self, node):
|
||||
self.body.append(u'</dt>')
|
||||
def visit_termsep(self, node):
|
||||
self.body.append(self.starttag(node, 'br'))
|
||||
raise nodes.SkipNode
|
||||
def visit_definition(self, node):
|
||||
self.body.append(self.starttag(node, 'dd'))
|
||||
def depart_definition(self, node):
|
||||
self.body.append(u'</dd>')
|
||||
|
||||
def visit_admonition(self, node, type=None):
|
||||
clss = {
|
||||
# ???: 'alert-success',
|
||||
|
||||
'note': 'alert-info',
|
||||
'hint': 'alert-info',
|
||||
'tip': 'alert-info',
|
||||
'seealso': 'alert-info',
|
||||
|
||||
'warning': 'alert-warning',
|
||||
'attention': 'alert-warning',
|
||||
'caution': 'alert-warning',
|
||||
'important': 'alert-warning',
|
||||
|
||||
'danger': 'alert-danger',
|
||||
'error': 'alert-danger',
|
||||
|
||||
'exercise': 'alert-exercise',
|
||||
}
|
||||
self.body.append(self.starttag(node, 'div', role='alert', CLASS='alert {}'.format(
|
||||
clss.get(type, '')
|
||||
)))
|
||||
if 'alert-dismissible' in node.get('classes', []):
|
||||
self.body.append(
|
||||
u'<button type="button" class="close" data-dismiss="alert" aria-label="Close">'
|
||||
u'<span aria-hidden="true">×</span>'
|
||||
u'</button>')
|
||||
if type:
|
||||
node.insert(0, nodes.title(type, admonitionlabels[type]))
|
||||
def depart_admonition(self, node):
|
||||
self.body.append(u'</div>')
|
||||
visit_note = lambda self, node: self.visit_admonition(node, 'note')
|
||||
visit_warning = lambda self, node: self.visit_admonition(node, 'warning')
|
||||
visit_attention = lambda self, node: self.visit_admonition(node, 'attention')
|
||||
visit_caution = lambda self, node: self.visit_admonition(node, 'caution')
|
||||
visit_danger = lambda self, node: self.visit_admonition(node, 'danger')
|
||||
visit_error = lambda self, node: self.visit_admonition(node, 'error')
|
||||
visit_hint = lambda self, node: self.visit_admonition(node, 'hint')
|
||||
visit_important = lambda self, node: self.visit_admonition(node, 'important')
|
||||
visit_tip = lambda self, node: self.visit_admonition(node, 'tip')
|
||||
visit_exercise = lambda self, node: self.visit_admonition(node, 'exercise')
|
||||
visit_seealso = lambda self, node: self.visit_admonition(node, 'seealso')
|
||||
depart_note = depart_admonition
|
||||
depart_warning = depart_admonition
|
||||
depart_attention = depart_admonition
|
||||
depart_caution = depart_admonition
|
||||
depart_danger = depart_admonition
|
||||
depart_error = depart_admonition
|
||||
depart_hint = depart_admonition
|
||||
depart_important = depart_admonition
|
||||
depart_tip = depart_admonition
|
||||
depart_exercise = depart_admonition
|
||||
depart_seealso = depart_admonition
|
||||
def visit_versionmodified(self, node):
|
||||
self.body.append(self.starttag(node, 'div', CLASS=node['type']))
|
||||
def depart_versionmodified(self, node):
|
||||
self.body.append(u'</div>')
|
||||
|
||||
def visit_title(self, node):
|
||||
parent = node.parent
|
||||
closing = u'</p>'
|
||||
if isinstance(parent, nodes.Admonition):
|
||||
self.body.append(self.starttag(node, 'p', CLASS='alert-title'))
|
||||
elif isinstance(node.parent, nodes.document):
|
||||
self.body.append(self.starttag(node, 'h1'))
|
||||
closing = u'</h1>'
|
||||
self.start_document_title = len(self.body)
|
||||
else:
|
||||
assert isinstance(parent, nodes.section), "expected a section node as parent to the title, found {}".format(parent)
|
||||
if self.first_title:
|
||||
self.first_title = False
|
||||
raise nodes.SkipNode()
|
||||
nodename = 'h{}'.format(self.section_level)
|
||||
self.body.append(self.starttag(node, nodename))
|
||||
closing = u'</{}>'.format(nodename)
|
||||
self.context.append(closing)
|
||||
def depart_title(self, node):
|
||||
self.body.append(self.context.pop())
|
||||
if self.start_document_title:
|
||||
self.title = self.body[self.start_document_title:-1]
|
||||
self.start_document_title = 0
|
||||
del self.body[:]
|
||||
|
||||
# the rubric should be a smaller heading than the current section, up to
|
||||
# h6... maybe "h7" should be a ``p`` instead?
|
||||
def visit_rubric(self, node):
|
||||
self.body.append(self.starttag(node, 'h{}'.format(min(self.section_level + 1, 6))))
|
||||
def depart_rubric(self, node):
|
||||
self.body.append(u'</h{}>'.format(min(self.section_level + 1, 6)))
|
||||
|
||||
def visit_block_quote(self, node):
|
||||
self.body.append(self.starttag(node, 'blockquote'))
|
||||
def depart_block_quote(self, node):
|
||||
self.body.append(u'</blockquote>')
|
||||
def visit_attribution(self, node):
|
||||
self.body.append(self.starttag(node, 'footer'))
|
||||
def depart_attribution(self, node):
|
||||
self.body.append(u'</footer>')
|
||||
|
||||
def visit_container(self, node):
|
||||
self.body.append(self.starttag(node, 'div'))
|
||||
def depart_container(self, node):
|
||||
self.body.append(u'</div>')
|
||||
def visit_compound(self, node):
|
||||
self.body.append(self.starttag(node, 'div'))
|
||||
def depart_compound(self, node):
|
||||
self.body.append(u'</div>')
|
||||
|
||||
def visit_image(self, node):
|
||||
uri = node['uri']
|
||||
if uri in self.builder.images:
|
||||
uri = posixpath.join(self.builder.imgpath,
|
||||
self.builder.images[uri])
|
||||
attrs = {'src': uri, 'class': 'img-responsive'}
|
||||
if 'alt' in node:
|
||||
attrs['alt'] = node['alt']
|
||||
# todo: explicit width/height/scale?
|
||||
self.body.append(self.starttag(node, 'img', **attrs))
|
||||
def depart_image(self, node): pass
|
||||
def visit_figure(self, node):
|
||||
self.body.append(self.starttag(node, 'div'))
|
||||
def depart_figure(self, node):
|
||||
self.body.append(u'</div>')
|
||||
def visit_caption(self, node):
|
||||
# first paragraph of figure content
|
||||
self.body.append(self.starttag(node, 'h4'))
|
||||
def depart_caption(self, node):
|
||||
self.body.append(u'</h4>')
|
||||
def visit_legend(self, node): pass
|
||||
def depart_legend(self, node): pass
|
||||
|
||||
def visit_line(self, node):
|
||||
self.body.append(self.starttag(node, 'div', CLASS='line'))
|
||||
# ensure the line still takes the room it needs
|
||||
if not len(node): self.body.append(u'<br />')
|
||||
def depart_line(self, node):
|
||||
self.body.append(u'</div>')
|
||||
|
||||
def visit_line_block(self, node):
|
||||
self.body.append(self.starttag(node, 'div', CLASS='line-block'))
|
||||
def depart_line_block(self, node):
|
||||
self.body.append(u'</div>')
|
||||
|
||||
def visit_table(self, node):
|
||||
self.body.append(self.starttag(node, 'table', CLASS='table'))
|
||||
def depart_table(self, node):
|
||||
self.body.append(u'</table>')
|
||||
def visit_tgroup(self, node): pass
|
||||
def depart_tgroup(self, node): pass
|
||||
def visit_colspec(self, node): raise nodes.SkipNode
|
||||
def visit_thead(self, node):
|
||||
self.body.append(self.starttag(node, 'thead'))
|
||||
def depart_thead(self, node):
|
||||
self.body.append(u'</thead>')
|
||||
def visit_tbody(self, node):
|
||||
self.body.append(self.starttag(node, 'tbody'))
|
||||
def depart_tbody(self, node):
|
||||
self.body.append(u'</tbody>')
|
||||
def visit_row(self, node):
|
||||
self.body.append(self.starttag(node, 'tr'))
|
||||
def depart_row(self, node):
|
||||
self.body.append(u'</tr>')
|
||||
def visit_entry(self, node):
|
||||
if isinstance(node.parent.parent, nodes.thead):
|
||||
tagname = 'th'
|
||||
else:
|
||||
tagname = 'td'
|
||||
self.body.append(self.starttag(node, tagname))
|
||||
self.context.append(tagname)
|
||||
def depart_entry(self, node):
|
||||
self.body.append(u'</{}>'.format(self.context.pop()))
|
||||
|
||||
def visit_Text(self, node):
|
||||
self.body.append(self.encode(node.astext()))
|
||||
def depart_Text(self, node):
|
||||
pass
|
||||
def visit_literal(self, node):
|
||||
self.body.append(self.starttag(node, 'code'))
|
||||
def depart_literal(self, node):
|
||||
self.body.append(u'</code>')
|
||||
visit_literal_emphasis = visit_literal
|
||||
depart_literal_emphasis = depart_literal
|
||||
def visit_emphasis(self, node):
|
||||
self.body.append(self.starttag(node, 'em'))
|
||||
def depart_emphasis(self, node):
|
||||
self.body.append(u'</em>')
|
||||
def visit_strong(self, node):
|
||||
self.body.append(self.starttag(node, 'strong'))
|
||||
def depart_strong(self, node):
|
||||
self.body.append(u'</strong>')
|
||||
visit_literal_strong = visit_strong
|
||||
depart_literal_strong = depart_strong
|
||||
def visit_inline(self, node):
|
||||
self.body.append(self.starttag(node, 'span'))
|
||||
def depart_inline(self, node):
|
||||
self.body.append(u'</span>')
|
||||
def visit_abbreviation(self, node):
|
||||
attrs = {}
|
||||
if 'explanation' in node:
|
||||
attrs['title'] = node['explanation']
|
||||
self.body.append(self.starttag(node, 'abbr', **attrs))
|
||||
def depart_abbreviation(self, node):
|
||||
self.body.append(u'</abbr>')
|
||||
|
||||
def visit_reference(self, node):
|
||||
attrs = {
|
||||
'class': 'reference',
|
||||
'href': node['refuri'] if 'refuri' in node else '#' + node['refid']
|
||||
}
|
||||
attrs['class'] += ' internal' if (node.get('internal') or 'refuri' not in node) else ' external'
|
||||
if any(isinstance(ancestor, nodes.Admonition) for ancestor in _parents(node)):
|
||||
attrs['class'] += ' alert-link'
|
||||
|
||||
if 'reftitle' in node:
|
||||
attrs['title'] = node['reftitle']
|
||||
|
||||
self.body.append(self.starttag(node, 'a', **attrs))
|
||||
def depart_reference(self, node):
|
||||
self.body.append(u'</a>')
|
||||
def visit_target(self, node): pass
|
||||
def depart_target(self, node): pass
|
||||
def visit_footnote(self, node):
|
||||
self.body.append(self.starttag(node, 'div', CLASS='footnote'))
|
||||
self.footnote_backrefs(node)
|
||||
def depart_footnote(self, node):
|
||||
self.body.append(u'</div>')
|
||||
def visit_footnote_reference(self, node):
|
||||
self.body.append(self.starttag(
|
||||
node, 'a', href='#' + node['refid'], CLASS="footnote-ref"))
|
||||
def depart_footnote_reference(self, node):
|
||||
self.body.append(u'</a>')
|
||||
def visit_label(self, node):
|
||||
self.body.append(self.starttag(node, 'span', CLASS='footnote-label'))
|
||||
self.body.append(u'%s[' % self.context.pop())
|
||||
def depart_label(self, node):
|
||||
# Context added in footnote_backrefs.
|
||||
self.body.append(u']%s</span> %s' % (self.context.pop(), self.context.pop()))
|
||||
def footnote_backrefs(self, node):
|
||||
# should store following data on context stack (in that order since
|
||||
# they'll be popped so LIFO)
|
||||
#
|
||||
# * outside (after) label
|
||||
# * after label text
|
||||
# * before label text
|
||||
backrefs = node['backrefs']
|
||||
if not backrefs:
|
||||
self.context.extend(['', '', ''])
|
||||
elif len(backrefs) == 1:
|
||||
self.context.extend([
|
||||
'',
|
||||
'</a>',
|
||||
'<a class="footnote-backref" href="#%s">' % backrefs[0]
|
||||
])
|
||||
else:
|
||||
backlinks = (
|
||||
'<a class="footnote-backref" href="#%s">%s</a>' % (backref, i)
|
||||
for i, backref in enumerate(backrefs, start=1)
|
||||
)
|
||||
self.context.extend([
|
||||
'<em class="footnote-backrefs">(%s)</em> ' % ', '.join(backlinks),
|
||||
'',
|
||||
''
|
||||
])
|
||||
|
||||
def visit_desc(self, node):
|
||||
self.body.append(self.starttag(node, 'section', CLASS='code-' + node['objtype']))
|
||||
def depart_desc(self, node):
|
||||
self.body.append(u'</section>')
|
||||
def visit_desc_signature(self, node):
|
||||
self.body.append(self.starttag(node, 'h6'))
|
||||
self.body.append(u'<code>')
|
||||
def depart_desc_signature(self, node):
|
||||
self.body.append(u'</code>')
|
||||
self.body.append(u'</h6>')
|
||||
def visit_desc_addname(self, node): pass
|
||||
def depart_desc_addname(self, node): pass
|
||||
def visit_desc_type(self, node): pass
|
||||
def depart_desc_type(self, node): pass
|
||||
def visit_desc_returns(self, node):
|
||||
self.body.append(u' → ')
|
||||
def depart_desc_returns(self, node):
|
||||
pass
|
||||
def visit_desc_name(self, node): pass
|
||||
def depart_desc_name(self, node): pass
|
||||
def visit_desc_parameterlist(self, node):
|
||||
self.body.append(u'(')
|
||||
self.first_param = True
|
||||
self.optional_param_level = 0
|
||||
# How many required parameters are left.
|
||||
self.required_params_left = sum(isinstance(c, addnodes.desc_parameter) for c in node.children)
|
||||
self.param_separator = node.child_text_separator
|
||||
def depart_desc_parameterlist(self, node):
|
||||
self.body.append(u')')
|
||||
# If required parameters are still to come, then put the comma after
|
||||
# the parameter. Otherwise, put the comma before. This ensures that
|
||||
# signatures like the following render correctly (see issue #1001):
|
||||
#
|
||||
# foo([a, ]b, c[, d])
|
||||
#
|
||||
def visit_desc_parameter(self, node):
|
||||
if self.first_param:
|
||||
self.first_param = 0
|
||||
elif not self.required_params_left:
|
||||
self.body.append(self.param_separator)
|
||||
if self.optional_param_level == 0:
|
||||
self.required_params_left -= 1
|
||||
if 'noemph' not in node: self.body.append(u'<em>')
|
||||
def depart_desc_parameter(self, node):
|
||||
if 'noemph' not in node: self.body.append(u'</em>')
|
||||
if self.required_params_left:
|
||||
self.body.append(self.param_separator)
|
||||
def visit_desc_optional(self, node):
|
||||
self.optional_param_level += 1
|
||||
self.body.append(u'[')
|
||||
def depart_desc_optional(self, node):
|
||||
self.optional_param_level -= 1
|
||||
self.body.append(u']')
|
||||
def visit_desc_annotation(self, node):
|
||||
self.body.append(self.starttag(node, 'em'))
|
||||
def depart_desc_annotation(self, node):
|
||||
self.body.append(u'</em>')
|
||||
def visit_desc_content(self, node): pass
|
||||
def depart_desc_content(self, node): pass
|
||||
def visit_field_list(self, node):
|
||||
self.body.append(self.starttag(node, 'div', CLASS='code-fields'))
|
||||
def depart_field_list(self, node):
|
||||
self.body.append(u'</div>')
|
||||
def visit_field(self, node):
|
||||
self.body.append(self.starttag(node, 'div', CLASS='code-field'))
|
||||
def depart_field(self, node):
|
||||
self.body.append(u'</div>')
|
||||
def visit_field_name(self, node):
|
||||
self.body.append(self.starttag(node, 'div', CLASS='code-field-name'))
|
||||
def depart_field_name(self, node):
|
||||
self.body.append(u'</div>')
|
||||
def visit_field_body(self, node):
|
||||
self.body.append(self.starttag(node, 'div', CLASS='code-field-body'))
|
||||
def depart_field_body(self, node):
|
||||
self.body.append(u'</div>')
|
||||
|
||||
def visit_glossary(self, node): pass
|
||||
def depart_glossary(self, node): pass
|
||||
|
||||
def visit_comment(self, node): raise nodes.SkipNode
|
||||
def visit_toctree(self, node):
|
||||
# div class=row {{ section_type }}
|
||||
# h2 class=col-sm-12
|
||||
# {{ section title }}
|
||||
# div class=col-sm-6 col-md-3
|
||||
# figure class=card
|
||||
# a href=current_link style=background-image: document-image-attribute class=card-img
|
||||
# figcaption
|
||||
# {{ card title }}
|
||||
env = self.builder.env
|
||||
conf = self.builder.app.config
|
||||
for title, ref in ((e[0], e[1]) for e in node['entries']):
|
||||
# external URL, no toc, can't recurse into
|
||||
if ref not in env.tocs:
|
||||
continue
|
||||
toc = env.tocs[ref].traverse(addnodes.toctree)
|
||||
|
||||
classes = env.metadata[ref].get('types', 'tutorials')
|
||||
classes += ' toc-single-entry' if not toc else ' toc-section'
|
||||
self.body.append(self.starttag(node, 'div', CLASS="row " + classes))
|
||||
self.body.append(u'<h2 class="col-sm-12">')
|
||||
self.body.append(title if title else util.nodes.clean_astext(env.titles[ref]))
|
||||
self.body.append(u'</h2>')
|
||||
|
||||
entries = [(title, ref)] if not toc else ((e[0], e[1]) for e in toc[0]['entries'])
|
||||
for subtitle, subref in entries:
|
||||
baseuri = self.builder.get_target_uri(node['parent'])
|
||||
|
||||
if subref in env.metadata:
|
||||
cover = env.metadata[subref].get('banner', conf.odoo_cover_default)
|
||||
elif subref in conf.odoo_cover_external:
|
||||
cover = conf.odoo_cover_external[subref]
|
||||
else:
|
||||
cover = conf.odoo_cover_default_external
|
||||
|
||||
if cover:
|
||||
banner = '_static/' + cover
|
||||
base, ext = os.path.splitext(banner)
|
||||
small = "{}.small{}".format(base, ext)
|
||||
if os.path.isfile(urllib.url2pathname(small)):
|
||||
banner = small
|
||||
style = u"background-image: url('{}')".format(
|
||||
util.relative_uri(baseuri, banner) or '#')
|
||||
else:
|
||||
style = u''
|
||||
|
||||
self.body.append(u"""
|
||||
<div class="col-sm-6 col-md-3">
|
||||
<figure class="card">
|
||||
<a href="{link}" class="card-img">
|
||||
<span style="{style}"></span>
|
||||
<figcaption>{title}</figcaption>
|
||||
</a>
|
||||
</figure>
|
||||
</div>
|
||||
""".format(
|
||||
link=subref if util.url_re.match(subref) else util.relative_uri(
|
||||
baseuri, self.builder.get_target_uri(subref)),
|
||||
style=style,
|
||||
title=subtitle if subtitle else util.nodes.clean_astext(env.titles[subref]),
|
||||
))
|
||||
|
||||
self.body.append(u'</div>')
|
||||
raise nodes.SkipNode
|
||||
|
||||
def visit_index(self, node): raise nodes.SkipNode
|
||||
|
||||
def visit_raw(self, node):
|
||||
if 'html' in node.get('format', '').split():
|
||||
t = 'span' if isinstance(node.parent, nodes.TextElement) else 'div'
|
||||
if node['classes']:
|
||||
self.body.append(self.starttag(node, t))
|
||||
self.body.append(node.astext())
|
||||
if node['classes']:
|
||||
self.body.append('</%s>' % t)
|
||||
# Keep non-HTML raw text out of output:
|
||||
raise nodes.SkipNode
|
||||
Reference in New Issue
Block a user