simple_footnotes.py 4.1 KB

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