bootstrap.py 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311
  1. #!/usr/bin/env python
  2. # -*- coding: utf-8 -*-
  3. # -----------------------------------------------------------------------------
  4. # Bootstrap RST
  5. # Copyright (c) 2014, Nicolas P. Rougier
  6. # Distributed under the (new) BSD License. See LICENSE.txt for more info.
  7. # -----------------------------------------------------------------------------
  8. import sys, os, re
  9. from docutils import nodes, utils
  10. from docutils.parsers.rst.directives import images
  11. from docutils.transforms import TransformError, Transform, parts
  12. from docutils.parsers.rst import Directive, directives, states, roles
  13. from docutils.nodes import fully_normalize_name, whitespace_normalize_name
  14. from docutils.parsers.rst.roles import set_classes
  15. from docutils.io import StringOutput
  16. from docutils.core import Publisher
  17. from pelican import signals
  18. from pelican.readers import RstReader, PelicanHTMLTranslator
  19. from .roles import *
  20. from .directives import *
  21. class HTMLTranslator(PelicanHTMLTranslator):
  22. """
  23. This is a translator class for the docutils system.
  24. """
  25. def visit_h1(self, node):
  26. self.body.append('<h1>%s</h1>' % node.children[0])
  27. raise nodes.SkipNode
  28. def visit_h2(self, node):
  29. self.body.append('<h2>%s</h2>' % node.children[0])
  30. raise nodes.SkipNode
  31. def visit_h3(self, node):
  32. self.body.append('<h3>%s</h3>' % node.children[0])
  33. raise nodes.SkipNode
  34. def visit_h4(self, node):
  35. self.body.append('<h4>%s</h4>' % node.children[0])
  36. raise nodes.SkipNode
  37. def visit_h5(self, node):
  38. self.body.append('<h5>%s</h5>' % node.children[0])
  39. raise nodes.SkipNode
  40. def visit_h6(self, node):
  41. self.body.append('<h6>%s</h6>' % node.children[0])
  42. raise nodes.SkipNode
  43. def visit_label_default(self, node):
  44. self.body.append(
  45. '<span class="label label-default">%s</span>' % node.children[0])
  46. raise nodes.SkipNode
  47. def visit_label_primary(self, node):
  48. self.body.append(
  49. '<span class="label label-primary">%s</span>' % node.children[0])
  50. raise nodes.SkipNode
  51. def visit_label_success(self, node):
  52. self.body.append(
  53. '<span class="label label-success">%s</span>' % node.children[0])
  54. raise nodes.SkipNode
  55. def visit_label_info(self, node):
  56. self.body.append(
  57. '<span class="label label-info">%s</span>' % node.children[0])
  58. raise nodes.SkipNode
  59. def visit_label_warning(self, node):
  60. self.body.append(
  61. '<span class="label label-warning">%s</span>' % node.children[0])
  62. raise nodes.SkipNode
  63. def visit_label_danger(self, node):
  64. self.body.append(
  65. '<span class="label label-danger">%s</span>' % node.children[0])
  66. raise nodes.SkipNode
  67. def visit_page_row(self, node):
  68. self.body.append(self.starttag(node,'div'))
  69. def depart_page_row(self, node):
  70. self.body.append('</div>\n')
  71. def visit_page_column(self, node):
  72. self.body.append(self.starttag(node,'div'))
  73. def depart_page_column(self, node):
  74. self.body.append('</div>\n')
  75. def visit_button(self, node):
  76. btn_classes = { 'primary' : 'btn-primary', 'success' : 'btn-success',
  77. 'info' : 'btn-info', 'warning' : 'btn-warning',
  78. 'danger' : 'btn-danger', 'link' : 'btn-link',
  79. 'outline' : 'btn-outline', 'tiny' : 'btn-xs',
  80. 'small' : 'btn-sm', 'large' : 'btn-lg',
  81. 'block' : 'btn-block', 'active' : 'btn-active' }
  82. classes = 'btn '
  83. flag = False
  84. for node_class in node['classes']:
  85. if node_class in ['primary', 'success', 'warning'
  86. 'info', 'link', 'danger', 'outline']:
  87. flag = True
  88. btn_class = btn_classes.get(node_class, None)
  89. if btn_class:
  90. classes += btn_class + ' '
  91. if flag == False:
  92. classes += 'btn-default'
  93. target = node['target']
  94. properties = ''
  95. # Disabled
  96. if 'disabled' in node['classes']:
  97. if target:
  98. properties += ' disabled="disabled"'
  99. else:
  100. classes += ' disabled'
  101. # Data toggle
  102. if 'toggle' in node['classes']:
  103. classes += ' dropdown-toggle '
  104. properties += ' data-toggle="dropdown"'
  105. if target:
  106. properties += ' role="button"'
  107. anchor = '<a href="%s" class="%s" %s>' % (target,classes,properties)
  108. self.body.append(anchor)
  109. else:
  110. properties += ' type="button"'
  111. button = '<button class="%s" %s>' % (classes,properties)
  112. self.body.append(button)
  113. def depart_button(self, node):
  114. if node['target']:
  115. self.body.append('</a>\n')
  116. else:
  117. self.body.append('</button>\n')
  118. def visit_progress(self, node):
  119. prg_classes = { 'success' : 'progress-bar-success',
  120. 'info' : 'progress-bar-info',
  121. 'warning' : 'progress-bar-warning',
  122. 'danger' : 'progress-bar-danger' }
  123. label = node['label']
  124. classes = 'progress-bar'
  125. flag = False
  126. for nodeclass in node['classes']:
  127. flag = True
  128. classes += ' ' + prg_classes.get(nodeclass, '')
  129. if flag == False:
  130. classes += ' progress-bar-default'
  131. properties = 'role="progress-bar"'
  132. properties += ' aria-valuenow="%d"' % int(node['value'])
  133. properties += ' aria-valuemin="%d"' % int(node['value_min'])
  134. properties += ' aria-valuemax="%d"' % int(node['value_max'])
  135. properties += ' style="width: %d%%";' % int(node['value'])
  136. if 'active' in node['classes']:
  137. self.body.append('<div class="progress progress-striped active">')
  138. elif 'striped' in node['classes']:
  139. self.body.append('<div class="progress progress-striped">')
  140. else:
  141. self.body.append('<div class="progress">')
  142. self.body.append(
  143. '<div class="%s" %s>%s</div>' % (classes,properties,label))
  144. self.body.append('</div>')
  145. raise nodes.SkipNode
  146. def visit_alert(self, node):
  147. self.body.append(self.starttag(node, 'div', CLASS='alert'))
  148. if node.dismissable:
  149. self.body.append(
  150. u"""<button type="button" class="close" data-dismiss="alert" """
  151. u"""aria-hidden="true">×</button>""")
  152. def depart_alert(self, node):
  153. self.body.append('</div>\n')
  154. def visit_callout(self, node):
  155. self.body.append(self.starttag(node, 'div', CLASS='bs-callout'))
  156. def depart_callout(self, node):
  157. self.body.append('</div>\n')
  158. # overwritten
  159. def visit_definition_list(self, node):
  160. list_class = node.parent.get('list-class', [])
  161. list_class.append('docutils')
  162. list_class = ' '.join(list_class)
  163. self.body.append(self.starttag(node, 'dl', CLASS=list_class))
  164. # overwritten
  165. def visit_sidebar(self, node):
  166. self.body.append(self.starttag(node, 'div', CLASS='col-md-3 col-md-push-9'))
  167. self.body.append(self.starttag(node, 'div', CLASS='bs-docs-sidebar hidden-print affix-top'))
  168. self.body.append(self.starttag(node, 'div', CLASS='sidebar'))
  169. self.set_first_last(node)
  170. self.in_sidebar = True
  171. # overwritten
  172. def depart_sidebar(self, node):
  173. self.body.append('</div>\n')
  174. self.body.append('</div>\n')
  175. self.body.append('</div>\n')
  176. # Opening tag for body
  177. self.body.append(self.starttag(node, 'div', CLASS='col-md-9 col-md-pull-3'))
  178. self.in_sidebar = False
  179. # overwritten : removed compact paragraph
  180. # def visit_paragraph(self, node):
  181. # if self.should_be_compact_paragraph(node):
  182. # self.context.append('')
  183. # else:
  184. # self.body.append(self.starttag(node, 'p', ''))
  185. # self.context.append('</p>\n')
  186. # overwritten: remove border=1, replace docutils/table class
  187. def visit_table(self, node):
  188. self.context.append(self.compact_p)
  189. self.compact_p = True
  190. #classes = ' '.join(['docutils', self.settings.table_style]).strip()
  191. classes = ' '.join(['table', self.settings.table_style]).strip()
  192. self.body.append(self.starttag(node, 'table', CLASS=classes))
  193. # overwritten : removed 'container' class
  194. def visit_container(self, node):
  195. self.body.append(self.starttag(node, 'div', CLASS=''))
  196. # overwritten: get rid of <hr> tag
  197. def depart_header(self, node):
  198. start = self.context.pop()
  199. header = [self.starttag(node, 'div', CLASS='header')]
  200. header.extend(self.body[start:])
  201. header.append('\n</div>\n')
  202. self.body_prefix.extend(header)
  203. self.header.extend(header)
  204. del self.body[start:]
  205. # overwritten: get rid of <hr> tag
  206. def depart_footer(self, node):
  207. start = self.context.pop()
  208. footer = [self.starttag(node, 'div', CLASS='footer')]
  209. footer.extend(self.body[start:])
  210. footer.append('\n</div>\n')
  211. self.footer.extend(footer)
  212. self.body_suffix[:0] = footer
  213. del self.body[start:]
  214. # overwritten
  215. def depart_document(self, node):
  216. self.head_prefix.extend([self.doctype,
  217. self.head_prefix_template %
  218. {'lang': self.settings.language_code}])
  219. self.html_prolog.append(self.doctype)
  220. self.meta.insert(0, self.content_type % self.settings.output_encoding)
  221. self.head.insert(0, self.content_type % self.settings.output_encoding)
  222. if self.math_header:
  223. self.head.append(self.math_header)
  224. # skip content-type meta tag with interpolated charset value:
  225. self.html_head.extend(self.head[1:])
  226. # self.body_prefix.append(self.starttag(node, 'div', CLASS='document'))
  227. self.body_prefix.append(self.starttag(node, 'div', CLASS='container'))
  228. # self.body_suffix.insert(0, '</div>\n')
  229. self.fragment.extend(self.body) # self.fragment is the "naked" body
  230. self.html_body.extend(self.body_prefix[1:] + self.body_pre_docinfo
  231. + self.docinfo + self.body
  232. + self.body_suffix[:-1])
  233. assert not self.context, 'len(context) = %s' % len(self.context)
  234. # -----------------------------------------------------------------------------
  235. class RSTReader(RstReader):
  236. """
  237. A custom RST reader that behaves exactly like its parent class RstReader
  238. with the difference that it uses our HTMLTranslator
  239. """
  240. def _get_publisher(self, source_path):
  241. extra_params = {'initial_header_level': '2',
  242. 'syntax_highlight': 'short',
  243. 'input_encoding': 'utf-8'}
  244. user_params = self.settings.get('DOCUTILS_SETTINGS')
  245. if user_params:
  246. extra_params.update(user_params)
  247. pub = Publisher(destination_class=StringOutput)
  248. pub.set_components('standalone', 'restructuredtext', 'html')
  249. pub.writer.translator_class = HTMLTranslator
  250. pub.process_programmatic_settings(None, extra_params, None)
  251. pub.set_source(source_path=source_path)
  252. pub.publish()
  253. return pub
  254. def add_reader(readers):
  255. readers.reader_classes['rst'] = RSTReader
  256. def register():
  257. signals.readers_init.connect(add_reader)