simple_footnotes.py 4.2 KB

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