1 # -*- coding: utf-8 -*-
 
   3 # This file is part of Librarian, licensed under GNU Affero GPLv3 or later.
 
   4 # Copyright © Fundacja Nowoczesna Polska. See NOTICE for more information.
 
   8 from librarian.formats import Format
 
   9 from librarian.output import OutputFile
 
  10 from librarian.renderers import Register, TreeRenderer
 
  11 from librarian.utils import Context, get_resource
 
  12 from librarian import core, VIDEO_PROVIDERS
 
  15 class HtmlFormat(Format):
 
  19     renderers = Register()
 
  21     def __init__(self, doc, standalone=False):
 
  22         super(HtmlFormat, self).__init__(doc)
 
  23         self.standalone = standalone
 
  25     def build(self, files_path=None):
 
  27             tmpl = get_resource("formats/html/res/html_standalone.html")
 
  29             tmpl = get_resource("formats/html/res/html.html")
 
  32         ctx = Context(format=self)
 
  33         ctx.files_path = files_path
 
  36         ctx.footnotes = Footnotes()
 
  39             t.find('head/title').text = u"%s (%s)" % (self.doc.meta.title(), self.doc.meta.author())
 
  41         t.find('.//div[@id="content"]').extend(
 
  42             self.render(self.doc.edoc.getroot(), ctx))
 
  43         # t.find('.//div[@id="toc"]').append(ctx.toc.render())
 
  44         t.find('.//div[@id="footnotes"]').extend(ctx.footnotes.output)
 
  46         return OutputFile.from_string(etree.tostring(
 
  47             t, encoding='utf-8', method="html"))
 
  49     def render(self, element, ctx):
 
  50         return self.renderers.get_for(element).render(element, ctx)
 
  55 class NaturalText(TreeRenderer):
 
  56     def render_text(self, text, ctx):
 
  57         root, inner = self.text_container()
 
  58         chunks = re.split('(?<=\s\w) ', text)
 
  59         inner.text = chunks[0]
 
  60         for chunk in chunks[1:]:
 
  61             x = etree.Entity("nbsp")
 
  67 class LiteralText(TreeRenderer):
 
  71 class Silent(TreeRenderer):
 
  72     def render_text(self, text, ctx):
 
  73         root, inner = self.text_container()
 
  77 class Footnotes(object):
 
  80         self.output = etree.Element("_")
 
  82     def append(self, item):
 
  86             href="#footnote-anchor-%d" % self.counter,
 
  87             id="footnote-%d" % self.counter,
 
  88             style="float:left;margin-right:1em")
 
  89         e.text = "[%d]" % self.counter
 
  92         self.output.extend(item)
 
  93         anchor = etree.Element(
 
  95             id="footnote-anchor-%d" % self.counter,
 
  96             href="#footnote-%d" % self.counter)
 
  97         anchor.text = "[%d]" % self.counter
 
 106     def add(self, title, level=0):
 
 108         self.items.append((level, title, self.counter))
 
 112         out = etree.Element("ul", id="toc")
 
 115         for level, title, counter in self.items:
 
 116             while level > curr_level:
 
 117                 ins = etree.Element("ul")
 
 121             while level < curr_level:
 
 122                 cursor = cursor.getparent()
 
 124             ins = etree.Element("li")
 
 125             ins.append(etree.Element("a", href="#sect%d" % counter))
 
 133 HtmlFormat.renderers.register(core.Aside, None, NaturalText('aside'))
 
 134 HtmlFormat.renderers.register(core.Aside, 'comment', Silent())
 
 137 class AsideFootnote(NaturalText):
 
 138     def render(self, element, ctx):
 
 139         output = super(AsideFootnote, self).render(element, ctx)
 
 140         anchor = ctx.footnotes.append(output)
 
 141         root, inner = self.container()
 
 144 HtmlFormat.renderers.register(core.Aside, 'footnote', AsideFootnote())
 
 147 class Header(NaturalText):
 
 148     def render(self, element, ctx):
 
 149         root = super(Header, self).render(element, ctx)
 
 150         if ctx.toc_level == 1:
 
 151             d = etree.SubElement(root, 'div', {'class': "page-header"})
 
 156                 d = etree.SubElement(
 
 157                     root[0], 'a', {'id': root[0].text, 'style': 'pointer: hand; color:#ddd; font-size:.8em'})
 
 162 HtmlFormat.renderers.register(core.Header, None, Header('h1'))
 
 165 HtmlFormat.renderers.register(core.Div, None, NaturalText('div'))
 
 168 class DivDefined(NaturalText):
 
 169     def render(self, element, ctx):
 
 170         output = super(DivDefined, self).render(element, ctx)
 
 171         output[0].text = (output[0].text or '') + ':'
 
 172         output[0].attrib['id'] = output[0].text  # not so cool?
 
 175 HtmlFormat.renderers.register(core.Div, 'defined', DivDefined('dt', {'style': 'display: inline-block'}))
 
 178 class DivImage(NaturalText):
 
 179     def render(self, element, ctx):
 
 180         output = super(DivImage, self).render(element, ctx)
 
 181         src = element.attrib.get('src', '')
 
 182         if src.startswith('file://'):
 
 183             src = ctx.files_path + src[7:]
 
 184         output[0].attrib['src'] = src
 
 185         output[0].attrib['style'] = 'display: block; width: 60%; margin: 3em auto'
 
 188 HtmlFormat.renderers.register(core.Div, 'img', DivImage('img'))
 
 191 class DivVideo(NaturalText):
 
 192     def render(self, element, ctx):
 
 193         output = super(DivVideo, self).render(element, ctx)
 
 194         provider = element.attrib.get('provider', '')
 
 195         video_id = element.attrib.get('videoid', '')
 
 199             'src': VIDEO_PROVIDERS[provider]['embed'] % video_id,
 
 201             'allowfullscreen': '',
 
 203         for attrib, value in attribs.iteritems():
 
 204             output[0].attrib[attrib] = value
 
 207 HtmlFormat.renderers.register(core.Div, 'video', DivVideo('iframe'))
 
 209 HtmlFormat.renderers.register(core.Div, 'item', NaturalText('li'))
 
 210 HtmlFormat.renderers.register(core.Span, 'item', NaturalText('li'))
 
 211 HtmlFormat.renderers.register(core.Div, 'list', NaturalText('ul'))
 
 212 HtmlFormat.renderers.register(core.Div, 'list.enum', NaturalText('ol'))
 
 215 class DivListDefinitions(NaturalText):
 
 216     def render(self, element, ctx):
 
 217         output = super(DivListDefinitions, self).render(element, ctx)
 
 218         # if ctx.toc_level > 2:
 
 219         #     output[0].attrib['style'] = 'float: right'
 
 222 HtmlFormat.renderers.register(core.Div, 'list.definitions', DivListDefinitions('ul'))
 
 223 HtmlFormat.renderers.register(core.Div, 'p', NaturalText('p'))
 
 226 class Section(NaturalText):
 
 227     def subcontext(self, element, ctx):
 
 228         return Context(ctx, toc_level=ctx.toc_level + 1)
 
 230     def render(self, element, ctx):
 
 231         counter = ctx.toc.add(element.meta.title(), ctx.toc_level)
 
 232         root = super(Section, self).render(element, ctx)
 
 233         root[0].set("id", "sect%d" % counter)
 
 235 HtmlFormat.renderers.register(core.Section, None, Section('section'))
 
 238 HtmlFormat.renderers.register(core.Span, None, NaturalText('span'))
 
 239 HtmlFormat.renderers.register(core.Span, 'cite', NaturalText('cite'))
 
 240 HtmlFormat.renderers.register(core.Span, 'cite.code', LiteralText('code'))
 
 241 HtmlFormat.renderers.register(core.Span, 'emph', NaturalText('em'))
 
 242 HtmlFormat.renderers.register(core.Span, 'emp', NaturalText('strong'))
 
 245 class SpanUri(LiteralText):
 
 246     def render(self, element, ctx):
 
 247         root = super(SpanUri, self).render(element, ctx)
 
 248         root[0].attrib['href'] = element.text
 
 250 HtmlFormat.renderers.register(core.Span, 'uri', SpanUri('a'))
 
 253 class SpanLink(LiteralText):
 
 254     def render(self, element, ctx):
 
 255         root = super(SpanLink, self).render(element, ctx)
 
 256         src = element.attrib.get('href', '')
 
 257         if src.startswith('file://'):
 
 258             src = ctx.files_path + src[7:]
 
 259         root[0].attrib['href'] = src
 
 261 HtmlFormat.renderers.register(core.Span, 'link', SpanLink('a'))