diff --git a/complex_footnotes/README.md b/complex_footnotes/README.md
new file mode 100644
index 0000000..d4105cc
--- /dev/null
+++ b/complex_footnotes/README.md
@@ -0,0 +1,26 @@
+Simple Footnotes
+================
+
+A Pelican plugin to add footnotes to blog posts.
+
+When writing a post or page, add a footnote like this:
+
+ Here's my written text[ref]and here is a footnote[/ref].
+
+This will appear as, roughly:
+
+Here's my written text1
+
+ 1. and here is a footnote ↩
+
+Inspired by Andrew Nacin's [Simple Footnotes WordPress plugin](http://wordpress.org/plugins/simple-footnotes/).
+
+Requirements
+============
+
+Needs html5lib, so you'll want to `pip install html5lib` before running.
+
+Should work with any content format (ReST, Markdown, whatever), because
+it looks for the `[ref]` and `[/ref]` once the conversion to HTML has happened.
+
+Stuart Langridge, http://www.kryogenix.org/, February 2014.
diff --git a/complex_footnotes/__init__.py b/complex_footnotes/__init__.py
new file mode 100644
index 0000000..2f958a8
--- /dev/null
+++ b/complex_footnotes/__init__.py
@@ -0,0 +1 @@
+from .simple_footnotes import *
diff --git a/complex_footnotes/simple_footnotes.py b/complex_footnotes/simple_footnotes.py
new file mode 100644
index 0000000..5de48b7
--- /dev/null
+++ b/complex_footnotes/simple_footnotes.py
@@ -0,0 +1,108 @@
+#!/usr/bin/env python
+# -*- coding: utf-8 -*- #
+
+from pelican import signals
+import html5lib
+import six
+
+RAW_FOOTNOTE_CONTAINERS = ["code"]
+
+
+def getText(node, recursive=False):
+ """Get all the text associated with this node.
+ With recursive == True, all text from child nodes is retrieved."""
+ L = [u'']
+ for n in node.childNodes:
+ if n.nodeType in (node.TEXT_NODE, node.CDATA_SECTION_NODE):
+ L.append(n.data)
+ else:
+ if not recursive:
+ return None
+ L.append(getText(n))
+ return u''.join(L)
+
+
+def sequence_gen(genlist):
+ for gen in genlist:
+ for elem in gen:
+ yield elem
+
+
+def parse_for_footnotes(article_or_page_generator):
+ all_content = [
+ getattr(article_or_page_generator, attr, None) \
+ for attr in [u'articles', u'drafts', u'pages']]
+ all_content = [x for x in all_content if x is not None]
+ for article in sequence_gen(all_content):
+ if u"[ref]" in article._content and u"[/ref]" in article._content:
+ content = article._content.replace(u"[ref]", u"").replace(u"[/ref]",
+ u"")
+ parser = html5lib.HTMLParser(tree=html5lib.getTreeBuilder(u"dom"))
+ dom = parser.parse(content)
+ endnotes = []
+ count = 0
+ for footnote in dom.getElementsByTagName(u"x-simple-footnote"):
+ pn = footnote
+ leavealone = False
+ while pn:
+ if pn.nodeName in RAW_FOOTNOTE_CONTAINERS:
+ leavealone = True
+ break
+ pn = pn.parentNode
+ if leavealone:
+ continue
+ count += 1
+ fnid = u"sf-%s-%s" % (article.slug, count)
+ fnbackid = u"%s-back" % (fnid,)
+ endnotes.append((footnote, fnid, fnbackid))
+ number = dom.createElement(u"sup")
+ number.setAttribute(u"id", fnbackid)
+
+ numbera = dom.createElement(u"a")
+ numbera.setAttribute(u"href", u"#%s" % fnid)
+ numbera.setAttribute(u"class", u"simple-footnote")
+ numbera.appendChild(dom.createTextNode(six.text_type(count)))
+ txt = getText(footnote, recursive=True).replace(u"\n", u" ")
+
+ footnote_container = dom.createElement(u"span")
+ footnote_container.setAttribute(u"class", u"simple-footnote-container")
+ footnote_content = dom.createElement(u"span")
+ footnote_content.appendChild(footnote.firstChild)
+ footnote_content.setAttribute(u"class", u"simple-footnote-content")
+ footnote_container.appendChild(footnote_content)
+ print(footnote.firstChild)
+
+ numbera.setAttribute(u"title", txt)
+ number.appendChild(footnote_container)
+ number.appendChild(numbera)
+
+ footnote.parentNode.insertBefore(number, footnote)
+ footnote.parentNode.insertBefore(footnote_container, footnote)
+ if endnotes:
+ ol = dom.createElement(u"ol")
+ ol.setAttribute(u"class", u"simple-footnotes")
+ for e, fnid, fnbackid in endnotes:
+ # li = dom.createElement(u"li")
+ # li.setAttribute(u"id", fnid)
+ # while e.firstChild:
+ # li.appendChild(e.firstChild)
+ # backlink = dom.createElement(u"a")
+ # backlink.setAttribute(u"href", u"#%s" % fnbackid)
+ # backlink.setAttribute(u"class", u"simple-footnote-back")
+ # backlink.appendChild(dom.createTextNode(u'\u21a9'))
+ # li.appendChild(dom.createTextNode(u" "))
+ # li.appendChild(backlink)
+ # ol.appendChild(li)
+ e.parentNode.removeChild(e)
+ dom.getElementsByTagName(u"body")[0].appendChild(ol)
+ s = html5lib.serializer.HTMLSerializer(omit_optional_tags=False, quote_attr_values='legacy')
+ output_generator = s.serialize(
+ html5lib.treewalkers.getTreeWalker(u"dom")(dom.getElementsByTagName(u"body")[0]))
+ article._content = u"".join(list(output_generator)).replace(
+ u"", u"[ref]").replace(u"", u"[/ref]").replace(
+ u"
", u"").replace(u"", u"")
+
+
+def register():
+ signals.article_generator_finalized.connect(parse_for_footnotes)
+ signals.page_generator_finalized.connect(parse_for_footnotes)
diff --git a/complex_footnotes/test_simple_footnotes.py b/complex_footnotes/test_simple_footnotes.py
new file mode 100644
index 0000000..04af1dc
--- /dev/null
+++ b/complex_footnotes/test_simple_footnotes.py
@@ -0,0 +1,33 @@
+import unittest
+from simple_footnotes import parse_for_footnotes
+
+class PseudoArticleGenerator(object):
+ articles = []
+class PseudoArticle(object):
+ _content = ""
+ slug = "article"
+
+class TestFootnotes(unittest.TestCase):
+
+ def _expect(self, input, expected_output):
+ ag = PseudoArticleGenerator()
+ art = PseudoArticle()
+ art._content = input
+ ag.articles = [art]
+ parse_for_footnotes(ag)
+ self.assertEqual(art._content, expected_output)
+
+ def test_simple(self):
+ self._expect("words[ref]footnote[/ref]end",
+ ('wordsend'
+ ''))
+
+ def test_no_footnote_inside_code(self):
+ self._expect("wordsthis is code[ref]footnote[/ref] end code
end",
+ "wordsthis is code[ref]footnote[/ref] end code
end")
+
+if __name__ == '__main__':
+ unittest.main()
\ No newline at end of file