not thinking in nested parentheticals vs. installing a footnote plugin
[Ultimately_Untrue_Thought.git] / plugins / simple_footnotes.py
1 #!/usr/bin/env python
2 # -*- coding: utf-8 -*- #
3
4 # copied from getpelican/pelican-plugins@f15f42c6dba9
5
6 # with edits to add an <hr> and "Notes" header
7
8 from pelican import signals
9 import html5lib
10 import six
11
12 RAW_FOOTNOTE_CONTAINERS = ["code"]
13
14
15 def getText(node, recursive=False):
16     """Get all the text associated with this node.
17        With recursive == True, all text from child nodes is retrieved."""
18     L = [u'']
19     for n in node.childNodes:
20         if n.nodeType in (node.TEXT_NODE, node.CDATA_SECTION_NODE):
21             L.append(n.data)
22         else:
23             if not recursive:
24                 return None
25         L.append(getText(n))
26     return u''.join(L)
27
28
29 def sequence_gen(genlist):
30     for gen in genlist:
31         for elem in gen:
32             yield elem
33
34
35 def parse_for_footnotes(article_or_page_generator):
36     all_content = [
37         getattr(article_or_page_generator, attr, None) \
38         for attr in [u'articles', u'drafts', u'pages']]
39     all_content = [x for x in all_content if x is not None]
40     for article in sequence_gen(all_content):
41         if u"[ref]" in article._content and u"[/ref]" in article._content:
42             content = article._content.replace(u"[ref]", u"<x-simple-footnote>").replace(u"[/ref]",
43                                                                                          u"</x-simple-footnote>")
44             parser = html5lib.HTMLParser(tree=html5lib.getTreeBuilder(u"dom"))
45             dom = parser.parse(content)
46             endnotes = []
47             count = 0
48             for footnote in dom.getElementsByTagName(u"x-simple-footnote"):
49                 pn = footnote
50                 leavealone = False
51                 while pn:
52                     if pn.nodeName in RAW_FOOTNOTE_CONTAINERS:
53                         leavealone = True
54                         break
55                     pn = pn.parentNode
56                 if leavealone:
57                     continue
58                 count += 1
59                 fnid = u"sf-%s-%s" % (article.slug, count)
60                 fnbackid = u"%s-back" % (fnid,)
61                 endnotes.append((footnote, fnid, fnbackid))
62                 number = dom.createElement(u"sup")
63                 number.setAttribute(u"id", fnbackid)
64                 numbera = dom.createElement(u"a")
65                 numbera.setAttribute(u"href", u"#%s" % fnid)
66                 numbera.setAttribute(u"class", u"simple-footnote")
67                 numbera.appendChild(dom.createTextNode(six.text_type(count)))
68                 txt = getText(footnote, recursive=True).replace(u"\n", u" ")
69                 numbera.setAttribute(u"title", txt)
70                 number.appendChild(numbera)
71                 footnote.parentNode.insertBefore(number, footnote)
72             if endnotes:
73                 ol = dom.createElement(u"ol")
74                 ol.setAttribute(u"class", u"simple-footnotes")
75                 for e, fnid, fnbackid in endnotes:
76                     li = dom.createElement(u"li")
77                     li.setAttribute(u"id", fnid)
78                     while e.firstChild:
79                         li.appendChild(e.firstChild)
80                     backlink = dom.createElement(u"a")
81                     backlink.setAttribute(u"href", u"#%s" % fnbackid)
82                     backlink.setAttribute(u"class", u"simple-footnote-back")
83                     backlink.appendChild(dom.createTextNode(u'\u21a9'))
84                     li.appendChild(dom.createTextNode(u" "))
85                     li.appendChild(backlink)
86                     ol.appendChild(li)
87                     e.parentNode.removeChild(e)
88                 body = dom.getElementsByTagName(u"body")[0]
89                 body.appendChild(dom.createElement("hr"))
90                 notes_header = dom.createElement("p")
91                 notes_header.setAttribute("id", "notes-header")
92                 notes_header.appendChild(dom.createTextNode("Notes"))
93                 body.appendChild(notes_header)
94                 body.appendChild(ol)
95                 s = html5lib.serializer.HTMLSerializer(omit_optional_tags=False, quote_attr_values='legacy')
96                 output_generator = s.serialize(
97                     html5lib.treewalkers.getTreeWalker(u"dom")(dom.getElementsByTagName(u"body")[0]))
98                 article._content = u"".join(list(output_generator)).replace(
99                     u"<x-simple-footnote>", u"[ref]").replace(u"</x-simple-footnote>", u"[/ref]").replace(
100                     u"<body>", u"").replace(u"</body>", u"")
101
102
103 def register():
104     signals.article_generator_finalized.connect(parse_for_footnotes)
105     signals.page_generator_finalized.connect(parse_for_footnotes)