simple_footnotes.py 4.4 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798
  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 sequence_gen(genlist):
  18. for gen in genlist:
  19. for elem in gen:
  20. yield elem
  21. def parse_for_footnotes(article_generator):
  22. for article in sequence_gen([article_generator.drafts, article_generator.articles]):
  23. if "[ref]" in article._content and "[/ref]" in article._content:
  24. content = article._content.replace("[ref]", "<x-simple-footnote>").replace("[/ref]", "</x-simple-footnote>")
  25. parser = html5lib.HTMLParser(tree=html5lib.getTreeBuilder("dom"))
  26. dom = parser.parse(content)
  27. endnotes = []
  28. count = 0
  29. for footnote in dom.getElementsByTagName("x-simple-footnote"):
  30. pn = footnote
  31. leavealone = False
  32. while pn:
  33. if pn.nodeName in RAW_FOOTNOTE_CONTAINERS:
  34. leavealone = True
  35. break
  36. pn = pn.parentNode
  37. if leavealone:
  38. continue
  39. count += 1
  40. fnid = "sf-%s-%s" % (article.slug, count)
  41. fnbackid = "%s-back" % (fnid,)
  42. endnotes.append((footnote, fnid, fnbackid))
  43. number = dom.createElement("sup")
  44. number.setAttribute("id", fnbackid)
  45. numbera = dom.createElement("a")
  46. numbera.setAttribute("href", "#%s" % fnid)
  47. numbera.setAttribute("class", "simple-footnote")
  48. numbera.appendChild(dom.createTextNode(str(count)))
  49. txt = getText(footnote, recursive=True).replace("\n", " ")
  50. numbera.setAttribute("title", txt)
  51. number.appendChild(numbera)
  52. footnote.parentNode.insertBefore(number, footnote)
  53. if endnotes:
  54. ol = dom.createElement("ol")
  55. ol.setAttribute("class", "simple-footnotes")
  56. for e, fnid, fnbackid in endnotes:
  57. li = dom.createElement("li")
  58. li.setAttribute("id", fnid)
  59. while e.firstChild:
  60. li.appendChild(e.firstChild)
  61. backlink = dom.createElement("a")
  62. backlink.setAttribute("href", "#%s" % fnbackid)
  63. backlink.setAttribute("class", "simple-footnote-back")
  64. backlink.appendChild(dom.createTextNode(u'\u21a9'))
  65. li.appendChild(dom.createTextNode(" "))
  66. li.appendChild(backlink)
  67. ol.appendChild(li)
  68. e.parentNode.removeChild(e)
  69. dom.getElementsByTagName("body")[0].appendChild(ol)
  70. s = html5lib.serializer.htmlserializer.HTMLSerializer(omit_optional_tags=False, quote_attr_values=True)
  71. output_generator = s.serialize(html5lib.treewalkers.getTreeWalker("dom")(dom.getElementsByTagName("body")[0]))
  72. article._content = "".join(list(output_generator)).replace(
  73. "<x-simple-footnote>", "[ref]").replace("</x-simple-footnote>", "[/ref]").replace(
  74. "<body>", "").replace("</body>", "")
  75. if False:
  76. count = 0
  77. endnotes = []
  78. for f in footnotes:
  79. count += 1
  80. fnstr = '<a class="simple-footnote" name="%s-%s-back" href="#%s-%s"><sup>%s</a>' % (
  81. article.slug, count, article.slug, count, count)
  82. endstr = '<li id="%s-%s">%s <a href="#%s-%s-back">&uarr;</a></li>' % (
  83. article.slug, count, f[len("[ref]"):-len("[/ref]")], article.slug, count)
  84. content = content.replace(f, fnstr)
  85. endnotes.append(endstr)
  86. content += '<h4>Footnotes</h4><ol class="simple-footnotes">%s</ul>' % ("\n".join(endnotes),)
  87. article._content = content
  88. def register():
  89. signals.article_generator_finalized.connect(parse_for_footnotes)