simple_footnotes.py 3.9 KB

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