text: # -*- coding: utf-8 -*-
  
  # Copyright © 2012-2015 Roberto Alsina and others.
  
  # Permission is hereby granted, free of charge, to any
  # person obtaining a copy of this software and associated
  # documentation files (the "Software"), to deal in the
  # Software without restriction, including without limitation
  # the rights to use, copy, modify, merge, publish,
  # distribute, sublicense, and/or sell copies of the
  # Software, and to permit persons to whom the Software is
  # furnished to do so, subject to the following conditions:
  #
  # The above copyright notice and this permission notice
  # shall be included in all copies or substantial portions of
  # the Software.
  #
  # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY
  # KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE
  # WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR
  # PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS
  # OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR
  # OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
  # OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
  # SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
  
  from __future__ import unicode_literals
  import io
  import os
  import re
  
  try:
      import docutils.core
      import docutils.nodes
      import docutils.utils
      import docutils.io
      import docutils.readers.standalone
      has_docutils = True
  except ImportError:
      has_docutils = False
  
  try:
      import rst2html5
      has_rst2html5 = True
  except ImportError:
      has_rst2html5 = False
  
  from nikola.plugin_categories import PageCompiler
  from nikola.utils import get_logger, makedirs, req_missing, write_metadata
  
  
  class CompileRestHTML5(PageCompiler):
      """Compile reSt into HTML."""
  
      name = "rest_html5"
      demote_headers = True
      logger = None
  
      def _read_extra_deps(self, post):
          """Reads contents of .dep file and returns them as a list"""
          dep_path = post.base_path + '.dep'
          if os.path.isfile(dep_path):
              with io.open(dep_path, 'r+', encoding='utf8') as depf:
                  deps = [l.strip() for l in depf.readlines()]
                  return deps
          return []
  
      def register_extra_dependencies(self, post):
          """Adds dependency to post object to check .dep file."""
          post.add_dependency(lambda: self._read_extra_deps(post), 'fragment')
  
      def compile_html(self, source, dest, is_two_file=True):
          """Compile reSt into HTML."""
  
          if not has_docutils:
              req_missing(['docutils'], 'build this site (compile reStructuredText)')
          if not has_rst2html5:
              req_missing(['rst2html5'], 'build this site (compile reStructuredText into HTML5)')
          makedirs(os.path.dirname(dest))
          error_level = 100
          with io.open(dest, "w+", encoding="utf8") as out_file:
              with io.open(source, "r", encoding="utf8") as in_file:
                  data = in_file.read()
                  add_ln = 0
                  if not is_two_file:
                      spl = re.split('(\n\n|\r\n\r\n)', data, maxsplit=1)
                      data = spl[-1]
                      if len(spl) != 1:
                          # If errors occur, this will be added to the line
                          # number reported by docutils so the line number
                          # matches the actual line number (off by 7 with default
                          # metadata, could be more or less depending on the post
                          # author).
                          add_ln = len(spl[0].splitlines()) + 1
  
                  default_template_path = os.path.join(os.path.dirname(__file__), 'template.txt')
                  output, error_level, deps = rst2html(
                      data, settings_overrides={
                          'initial_header_level': 0,
                          'record_dependencies': True,
                          'stylesheet_path': None,
                          'link_stylesheet': True,
                          'syntax_highlight': 'short',
                          'math_output': 'mathjax',
                          'template': default_template_path,
                      }, logger=self.logger, source_path=source, l_add_ln=add_ln)
                  out_file.write(output)
              deps_path = dest + '.dep'
              if deps.list:
                  with io.open(deps_path, "w+", encoding="utf8") as deps_file:
                      deps_file.write('\n'.join(deps.list))
              else:
                  if os.path.isfile(deps_path):
                      os.unlink(deps_path)
          if error_level < 3:
              return True
          else:
              return False
  
      def create_post(self, path, **kw):
          content = kw.pop('content', None)
          onefile = kw.pop('onefile', False)
          # is_page is not used by create_post as of now.
          kw.pop('is_page', False)
          metadata = {}
          metadata.update(self.default_metadata)
          metadata.update(kw)
          makedirs(os.path.dirname(path))
          if not content.endswith('\n'):
              content += '\n'
          with io.open(path, "w+", encoding="utf8") as fd:
              if onefile:
                  fd.write(write_metadata(metadata))
                  fd.write('\n')
              fd.write(content)
  
      def set_site(self, site):
          self.config_dependencies = []
          for plugin_info in site.plugin_manager.getPluginsOfCategory("RestExtension"):
              if plugin_info.name in site.config['DISABLED_PLUGINS']:
                  site.plugin_manager.removePluginFromCategory(plugin_info, "RestExtension")
                  continue
  
              site.plugin_manager.activatePluginByName(plugin_info.name)
              self.config_dependencies.append(plugin_info.name)
              plugin_info.plugin_object.set_site(site)
              plugin_info.plugin_object.short_help = plugin_info.description
  
          self.logger = get_logger('compile_rest', site.loghandlers)
          if not site.debug:
              self.logger.level = 4
  
          return super(CompileRestHTML5, self).set_site(site)
  
  
  def get_observer(settings):
      """Return an observer for the docutils Reporter."""
      def observer(msg):
          """Report docutils/rest messages to a Nikola user.
  
          Error code mapping:
  
          +------+---------+------+----------+
          | dNUM |   dNAME | lNUM |    lNAME |    d = docutils, l = logbook
          +------+---------+------+----------+
          |    0 |   DEBUG |    1 |    DEBUG |
          |    1 |    INFO |    2 |     INFO |
          |    2 | WARNING |    4 |  WARNING |
          |    3 |   ERROR |    5 |    ERROR |
          |    4 |  SEVERE |    6 | CRITICAL |
          +------+---------+------+----------+
          """
          errormap = {0: 1, 1: 2, 2: 4, 3: 5, 4: 6}
          text = docutils.nodes.Element.astext(msg)
          line = msg['line'] + settings['add_ln'] if 'line' in msg else 0
          out = '[{source}{colon}{line}] {text}'.format(
              source=settings['source'], colon=(':' if line else ''),
              line=line, text=text)
          settings['logger'].log(errormap[msg['level']], out)
  
      return observer
  
  
  class NikolaReader(docutils.readers.standalone.Reader):
  
      def new_document(self):
          """Create and return a new empty document tree (root node)."""
          document = docutils.utils.new_document(self.source.source_path, self.settings)
          document.reporter.stream = False
          document.reporter.attach_observer(get_observer(self.l_settings))
          return document
  
  
  def add_node(node, visit_function=None, depart_function=None):
      """
      Register a Docutils node class.
      This function is completely optional. It is a same concept as
      `Sphinx add_node function <http://sphinx-doc.org/ext/appapi.html#sphinx.application.Sphinx.add_node>`_.
  
      For example::
  
          class Plugin(RestExtension):
  
              name = "rest_math"
  
              def set_site(self, site):
                  self.site = site
                  directives.register_directive('math', MathDirective)
                  add_node(MathBlock, visit_Math, depart_Math)
                  return super(Plugin, self).set_site(site)
  
          class MathDirective(Directive):
              def run(self):
                  node = MathBlock()
                  return [node]
  
          class Math(docutils.nodes.Element): pass
  
          def visit_Math(self, node):
              self.body.append(self.starttag(node, 'math'))
  
          def depart_Math(self, node):
              self.body.append('</math>')
  
      For full example, you can refer to `Microdata plugin <http://plugins.getnikola.com/#microdata>`_
      """
      docutils.nodes._add_node_class_names([node.__name__])
      if visit_function:
          setattr(rst2html5.HTML5Translator, 'visit_' + node.__name__, visit_function)
      if depart_function:
          setattr(rst2html5.HTML5Translator, 'depart_' + node.__name__, depart_function)
  
  
  def rst2html(source, source_path=None, source_class=docutils.io.StringInput,
               destination_path=None, reader=None,
               parser=None, parser_name='restructuredtext', writer=None,
               writer_name='html5', settings=None, settings_spec=None,
               settings_overrides=None, config_section=None,
               enable_exit_status=None, logger=None, l_add_ln=0):
      """
      Set up & run a `Publisher`, and return a dictionary of document parts.
      Dictionary keys are the names of parts, and values are Unicode strings;
      encoding is up to the client.  For programmatic use with string I/O.
  
      For encoded string input, be sure to set the 'input_encoding' setting to
      the desired encoding.  Set it to 'unicode' for unencoded Unicode string
      input.  Here's how::
  
          publish_parts(..., settings_overrides={'input_encoding': 'unicode'})
  
      Parameters: see `publish_programmatically`.
  
      WARNING: `reader` should be None (or NikolaReader()) if you want Nikola to report
               reStructuredText syntax errors.
      """
      if reader is None:
          reader = NikolaReader()
          # For our custom logging, we have special needs and special settings we
          # specify here.
          # logger    a logger from Nikola
          # source   source filename (docutils gets a string)
          # add_ln   amount of metadata lines (see comment in compile_html above)
          reader.l_settings = {'logger': logger, 'source': source_path,
                               'add_ln': l_add_ln}
  
      if writer is None:
          writer = rst2html5.HTML5Writer()
  
      pub = docutils.core.Publisher(reader, parser, writer, settings=settings,
                                    source_class=source_class,
                                    destination_class=docutils.io.StringOutput)
      pub.set_components(None, parser_name, writer_name)
      pub.process_programmatic_settings(
          settings_spec, settings_overrides, config_section)
      pub.set_source(source, None)
      pub.settings._nikola_source_path = source_path
      pub.set_destination(None, destination_path)
      pub.publish(enable_exit_status=enable_exit_status)
  
      return pub.writer.parts['body'], pub.document.reporter.max_level, pub.settings.record_dependencies
  