#!/usr/bin/env python
# -*- coding: utf-8 -*-
# -----------------------------------------------------------------------------
# Bootstrap RST
# Copyright (c) 2014, Nicolas P. Rougier
# Distributed under the (new) BSD License. See LICENSE.txt for more info.
# -----------------------------------------------------------------------------
import sys, os, re
from docutils import nodes, utils
from docutils.parsers.rst.directives import images
from docutils.transforms import TransformError, Transform, parts
from docutils.parsers.rst import Directive, directives, states, roles
from docutils.nodes import fully_normalize_name, whitespace_normalize_name
from docutils.parsers.rst.roles import set_classes
from docutils.io import StringOutput
from docutils.core import Publisher
from pelican import signals
from pelican.readers import RstReader, PelicanHTMLTranslator
from .roles import *
from .directives import *
class HTMLTranslator(PelicanHTMLTranslator):
"""
This is a translator class for the docutils system.
"""
def visit_h1(self, node):
self.body.append('
%s ' % node.children[0])
raise nodes.SkipNode
def visit_h2(self, node):
self.body.append('%s ' % node.children[0])
raise nodes.SkipNode
def visit_h3(self, node):
self.body.append('%s ' % node.children[0])
raise nodes.SkipNode
def visit_h4(self, node):
self.body.append('%s ' % node.children[0])
raise nodes.SkipNode
def visit_h5(self, node):
self.body.append('%s ' % node.children[0])
raise nodes.SkipNode
def visit_h6(self, node):
self.body.append('%s ' % node.children[0])
raise nodes.SkipNode
def visit_label_default(self, node):
self.body.append(
'%s ' % node.children[0])
raise nodes.SkipNode
def visit_label_primary(self, node):
self.body.append(
'%s ' % node.children[0])
raise nodes.SkipNode
def visit_label_success(self, node):
self.body.append(
'%s ' % node.children[0])
raise nodes.SkipNode
def visit_label_info(self, node):
self.body.append(
'%s ' % node.children[0])
raise nodes.SkipNode
def visit_label_warning(self, node):
self.body.append(
'%s ' % node.children[0])
raise nodes.SkipNode
def visit_label_danger(self, node):
self.body.append(
'%s ' % node.children[0])
raise nodes.SkipNode
def visit_page_row(self, node):
self.body.append(self.starttag(node,'div'))
def depart_page_row(self, node):
self.body.append('\n')
def visit_page_column(self, node):
self.body.append(self.starttag(node,'div'))
def depart_page_column(self, node):
self.body.append('\n')
def visit_button(self, node):
btn_classes = { 'primary' : 'btn-primary', 'success' : 'btn-success',
'info' : 'btn-info', 'warning' : 'btn-warning',
'danger' : 'btn-danger', 'link' : 'btn-link',
'outline' : 'btn-outline', 'tiny' : 'btn-xs',
'small' : 'btn-sm', 'large' : 'btn-lg',
'block' : 'btn-block', 'active' : 'btn-active' }
classes = 'btn '
flag = False
for node_class in node['classes']:
if node_class in ['primary', 'success', 'warning'
'info', 'link', 'danger', 'outline']:
flag = True
btn_class = btn_classes.get(node_class, None)
if btn_class:
classes += btn_class + ' '
if flag == False:
classes += 'btn-default'
target = node['target']
properties = ''
# Disabled
if 'disabled' in node['classes']:
if target:
properties += ' disabled="disabled"'
else:
classes += ' disabled'
# Data toggle
if 'toggle' in node['classes']:
classes += ' dropdown-toggle '
properties += ' data-toggle="dropdown"'
if target:
properties += ' role="button"'
anchor = '' % (target,classes,properties)
self.body.append(anchor)
else:
properties += ' type="button"'
button = '' % (classes,properties)
self.body.append(button)
def depart_button(self, node):
if node['target']:
self.body.append(' \n')
else:
self.body.append('\n')
def visit_progress(self, node):
prg_classes = { 'success' : 'progress-bar-success',
'info' : 'progress-bar-info',
'warning' : 'progress-bar-warning',
'danger' : 'progress-bar-danger' }
label = node['label']
classes = 'progress-bar'
flag = False
for nodeclass in node['classes']:
flag = True
classes += ' ' + prg_classes.get(nodeclass, '')
if flag == False:
classes += ' progress-bar-default'
properties = 'role="progress-bar"'
properties += ' aria-valuenow="%d"' % int(node['value'])
properties += ' aria-valuemin="%d"' % int(node['value_min'])
properties += ' aria-valuemax="%d"' % int(node['value_max'])
properties += ' style="width: %d%%";' % int(node['value'])
if 'active' in node['classes']:
self.body.append('')
elif 'striped' in node['classes']:
self.body.append('
')
else:
self.body.append('
')
self.body.append(
'
%s
' % (classes,properties,label))
self.body.append('
')
raise nodes.SkipNode
def visit_alert(self, node):
self.body.append(self.starttag(node, 'div', CLASS='alert'))
if node.dismissable:
self.body.append(
u"""
× """)
def depart_alert(self, node):
self.body.append('
\n')
def visit_callout(self, node):
self.body.append(self.starttag(node, 'div', CLASS='bs-callout'))
def depart_callout(self, node):
self.body.append('
\n')
# overwritten
def visit_definition_list(self, node):
list_class = node.parent.get('list-class', [])
list_class.append('docutils')
list_class = ' '.join(list_class)
self.body.append(self.starttag(node, 'dl', CLASS=list_class))
# overwritten
def visit_sidebar(self, node):
self.body.append(self.starttag(node, 'div', CLASS='col-md-3 col-md-push-9'))
self.body.append(self.starttag(node, 'div', CLASS='bs-docs-sidebar hidden-print affix-top'))
self.body.append(self.starttag(node, 'div', CLASS='sidebar'))
self.set_first_last(node)
self.in_sidebar = True
# overwritten
def depart_sidebar(self, node):
self.body.append('\n')
self.body.append('\n')
self.body.append('\n')
# Opening tag for body
self.body.append(self.starttag(node, 'div', CLASS='col-md-9 col-md-pull-3'))
self.in_sidebar = False
# overwritten : removed compact paragraph
# def visit_paragraph(self, node):
# if self.should_be_compact_paragraph(node):
# self.context.append('')
# else:
# self.body.append(self.starttag(node, 'p', ''))
# self.context.append('\n')
# overwritten: remove border=1, replace docutils/table class
def visit_table(self, node):
self.context.append(self.compact_p)
self.compact_p = True
#classes = ' '.join(['docutils', self.settings.table_style]).strip()
classes = ' '.join(['table', self.settings.table_style]).strip()
self.body.append(self.starttag(node, 'table', CLASS=classes))
# overwritten : removed 'container' class
def visit_container(self, node):
self.body.append(self.starttag(node, 'div', CLASS=''))
# overwritten: get rid of tag
def depart_header(self, node):
start = self.context.pop()
header = [self.starttag(node, 'div', CLASS='header')]
header.extend(self.body[start:])
header.append('\n\n')
self.body_prefix.extend(header)
self.header.extend(header)
del self.body[start:]
# overwritten: get rid of tag
def depart_footer(self, node):
start = self.context.pop()
footer = [self.starttag(node, 'div', CLASS='footer')]
footer.extend(self.body[start:])
footer.append('\n\n')
self.footer.extend(footer)
self.body_suffix[:0] = footer
del self.body[start:]
# overwritten
def depart_document(self, node):
self.head_prefix.extend([self.doctype,
self.head_prefix_template %
{'lang': self.settings.language_code}])
self.html_prolog.append(self.doctype)
self.meta.insert(0, self.content_type % self.settings.output_encoding)
self.head.insert(0, self.content_type % self.settings.output_encoding)
if self.math_header:
self.head.append(self.math_header)
# skip content-type meta tag with interpolated charset value:
self.html_head.extend(self.head[1:])
# self.body_prefix.append(self.starttag(node, 'div', CLASS='document'))
self.body_prefix.append(self.starttag(node, 'div', CLASS='container'))
# self.body_suffix.insert(0, '\n')
self.fragment.extend(self.body) # self.fragment is the "naked" body
self.html_body.extend(self.body_prefix[1:] + self.body_pre_docinfo
+ self.docinfo + self.body
+ self.body_suffix[:-1])
assert not self.context, 'len(context) = %s' % len(self.context)
# -----------------------------------------------------------------------------
class RSTReader(RstReader):
"""
A custom RST reader that behaves exactly like its parent class RstReader
with the difference that it uses our HTMLTranslator
"""
def _get_publisher(self, source_path):
extra_params = {'initial_header_level': '2',
'syntax_highlight': 'short',
'input_encoding': 'utf-8'}
user_params = self.settings.get('DOCUTILS_SETTINGS')
if user_params:
extra_params.update(user_params)
pub = Publisher(destination_class=StringOutput)
pub.set_components('standalone', 'restructuredtext', 'html')
pub.writer.translator_class = HTMLTranslator
pub.process_programmatic_settings(None, extra_params, None)
pub.set_source(source_path=source_path)
pub.publish()
return pub
def add_reader(readers):
readers.reader_classes['rst'] = RSTReader
def register():
signals.readers_init.connect(add_reader)