Commits

Waylan Limberg committed c673444

reorganized the extensions into a seperate dir. Much cleaner looking file system IMO.

Comments (0)

Files changed (22)

         module = __import__(extension_module_name)
 
     except ImportError:
-        message(WARN,
+        try:
+            module = __import__('.'.join(['mdx', extension_module_name]), {}, {}, ['mdx'])
+        except:
+            message(WARN,
                 "Couldn't load extension '%s' from \"%s\" - continuing without."
                 % (ext_name, extension_module_name) )
-        # Return a dummy (do nothing) Extension as silent failure
-        return Extension(configs={})
+            # Return a dummy (do nothing) Extension as silent failure
+            return Extension(configs={})
 
     return module.makeExtension(configs.items())    
 

mdx/__init__.py

Empty file added.

mdx/mdx_codehilite.py

+#!/usr/bin/python
+
+"""
+CodeHilite Extension for Python-Markdown
+=======================================
+
+Adds code/syntax highlighting to standard Python-Markdown code blocks.
+
+By [Waylan Limberg](http://achinghead.com/).
+
+Project website: http://achinghead.com/markdown-wikilinks/
+Contact: waylan [at] gmail [dot] com
+ 
+License: [BSD](http://www.opensource.org/licenses/bsd-license.php)
+ 
+Version: 0.2 (April 30, 2008)
+  
+Dependencies:
+* [Python 2.3+](http://python.org/)
+* [Markdown 1.7+](http://www.freewisdom.org/projects/python-markdown/)
+* [Pygments](http://pygments.org/)
+
+"""
+
+import markdown
+
+# --------------- CONSTANTS YOU MIGHT WANT TO MODIFY -----------------
+
+try:
+    TAB_LENGTH = markdown.TAB_LENGTH
+except AttributeError:
+    TAB_LENGTH = 4
+
+
+# ------------------ The Main CodeHilite Class ----------------------
+class CodeHilite:
+    """
+    Determine language of source code, and pass it into the pygments hilighter.
+
+    Basic Usage:
+        >>> code = CodeHilite(src = text)
+        >>> html = code.hilite()
+    
+    * src: Can be a string or any object with a .readline attribute.
+      
+    * linenos: (Boolen) Turns line numbering 'on' or 'off' (off by default).
+      
+    Low Level Usage:
+        >>> code = CodeHilite()
+        >>> code.src = text      # String or anything with a .readline attribute
+        >>> code.linenos = True  # True or False; Turns line numbering on or of.
+        >>> html = code.hilite()
+    
+    """
+
+    def __init__(self, src=None, linenos = False):
+        self.src = src
+        self.lang = None
+        self.linenos = linenos
+
+    def hilite(self):
+        """
+        Pass code to the [Pygments](http://pygments.pocoo.org/) highliter with 
+        optional line numbers. The output should then be styled with css to 
+        your liking. No styles are applied by default - only styling hooks 
+        (i.e.: <span class="k">). 
+
+        returns : A string of html.
+    
+        """
+
+        self.src = self.src.strip('\n')
+        
+        self._getLang()
+
+        try:
+            from pygments import highlight
+            from pygments.lexers import get_lexer_by_name, guess_lexer, TextLexer
+            from pygments.formatters import HtmlFormatter
+        except ImportError:
+            # just escape and pass through
+            txt = self._escape(self.src)
+            '''if num:
+                txt = self._number(txt)
+            else :
+                txt = '<div class="codehilite"><pre>%s</pre></div>\n'% txt'''
+            txt = self._number(txt)    
+            return txt
+        else:
+            try:
+                lexer = get_lexer_by_name(self.lang)
+            except ValueError:
+                try:
+                    lexer = guess_lexer(self.src)
+                except ValueError:
+                    lexer = TextLexer()
+            formatter = HtmlFormatter(linenos=self.linenos, cssclass="codehilite")
+            return highlight(self.src, lexer, formatter)
+
+    def _escape(self, txt):
+        """ basic html escaping """
+        txt = txt.replace('&', '&amp;')
+        txt = txt.replace('<', '&lt;')
+        txt = txt.replace('>', '&gt;')
+        txt = txt.replace('"', '&quot;')
+        return txt
+
+    def _number(self, txt):
+        """ Use <ol> for line numbering """
+        # Fix Whitespace
+        txt = txt.replace('\t', ' '*TAB_LENGTH)
+        txt = txt.replace(" "*4, "&nbsp; &nbsp; ")
+        txt = txt.replace(" "*3, "&nbsp; &nbsp;")
+        txt = txt.replace(" "*2, "&nbsp; ")        
+        
+        # Add line numbers
+        lines = txt.splitlines()
+        txt = '<div class="codehilite"><pre><ol>\n'
+        for line in lines:
+            txt += '\t<li>%s</li>\n'% line
+        txt += '</ol></pre></div>\n'
+        return txt
+
+
+    def _getLang(self):
+        """ 
+        Determines language of a code block from shebang lines and whether said
+        line should be removed or left in place. If the sheband line contains a
+        path (even a single /) then it is assumed to be a real shebang lines and
+        left alone. However, if no path is given (e.i.: #!python or :::python) 
+        then it is assumed to be a mock shebang for language identifitation of a
+        code fragment and removed from the code block prior to processing for 
+        code highlighting. When a mock shebang (e.i: #!python) is found, line 
+        numbering is turned on. When colons are found in place of a shebang 
+        (e.i.: :::python), line numbering is left in the current state - off 
+        by default.
+        
+        """
+
+        import re
+    
+        #split text into lines
+        lines = self.src.split("\n")
+        #pull first line to examine
+        fl = lines.pop(0)
+    
+        c = re.compile(r'''
+            (?:(?:::+)|(?P<shebang>[#]!))	#shebang or 2 or more colons
+            (?P<path>(?:/\w+)*[/ ])? # zero or 1 path ending in either a / or a single space
+            (?P<lang>\w*)	# the language (a single /  or space before lang is a path)
+            ''',  re.VERBOSE)
+        # search first line for shebang
+        m = c.search(fl)
+        if m:
+            # we have a match
+            try:
+                self.lang = m.group('lang').lower()
+            except IndexError:
+                self.lang = None
+            if m.group('path'):
+                # path exists - restore first line
+                lines.insert(0, fl)
+            if m.group('shebang'):
+                # shebang exists - use line numbers
+                self.linenos = True
+        else:
+            # No match
+            lines.insert(0, fl)
+        
+        self.src = "\n".join(lines).strip("\n")
+
+
+
+# ------------------ The Markdown Extension -------------------------------
+class CodeHiliteExtention(markdown.Extension):
+    def __init__(self, configs):
+        # define default configs
+        self.config = {
+            'force_linenos' : [False, "Force line numbers - Default: False"] 
+            }
+        
+        # Override defaults with user settings
+        for key, value in configs:
+            # self.config[key][0] = value
+            self.setConfig(key, value) 
+            
+    def extendMarkdown(self, md, md_globals):
+  
+        def _hiliteCodeBlock(parent_elem, lines, inList):
+            """
+            Overrides `_processCodeBlock` method in standard Markdown class 
+            and sends code blocks to a code highlighting proccessor. The result
+            is then stored in the HtmlStash, a placeholder is inserted into
+            the dom and the remainder of the text file is processed recursively.
+
+            * parent_elem: DOM element to which the content will be added
+            * lines: a list of lines
+            * inList: a level
+            
+            returns: None
+
+            """
+
+            detabbed, theRest = md.blockGuru.detectTabbed(lines)
+            text = "\n".join(detabbed).rstrip()+"\n"
+            code = CodeHilite(text, linenos=self.config['force_linenos'][0]) 
+            placeholder = md.htmlStash.store(code.hilite(), safe=True)
+            if parent_elem.text:
+                parent_elem.text += placeholder
+            else:
+                parent_elem.text = placeholder
+            md._processSection(parent_elem, theRest, inList)
+            
+        md._processCodeBlock = _hiliteCodeBlock
+
+def makeExtension(configs={}):
+  return CodeHiliteExtention(configs=configs)
+
+#!/usr/bin/env python
+"""
+Python-Markdown Extra Extension
+===============================
+
+A compilation of various Python-Markdown extensions that imitates
+[PHP Markdown Extra](http://michelf.com/projects/php-markdown/extra/).
+
+As no-one has yet written a Definition List extension for Python-
+Markdown, definition lists are not yet supported by Extra.
+
+Note that each of the individual extensions still need to be available
+on your PYTHONPATH. This extension simply wraps them all up as a 
+convenience so that only one extension needs to be listed when
+initiating Markdown. See the documentation for each individual
+extension for specifics about that extension.
+
+In the event that one or more of the supported extensions are not 
+available for import, Markdown will simply continue without that 
+extension. If you would like to be notified of such failures,
+you may set Python-Markdown's logger level to "WARN".
+
+There may be additional extensions that are distributed with 
+Python-Markdown that are not included here in Extra. Those extensions
+are not part of PHP Markdown Extra, and therefore, not part of
+Python-Markdown Extra. If you really would like Extra to include
+additional extensions, we suggest creating your own clone of Extra
+under a differant name. You could also edit the `extensions` global 
+variable defined below, but be aware that such changes may be lost 
+when you upgrade to any future version of Python-Markdown.
+
+"""
+
+import markdown
+
+extensions = ['fenced_code',
+              'footnotes',
+              'headerid',
+              'tables',
+              'abbr',
+              ]
+              
+
+class ExtraExtension(markdown.Extension):
+    """ Add various extensions to Markdown class."""
+
+    def extendMarkdown(self, md, md_globals):
+        """ Register extension instances. """
+        md.registerExtensions(extensions, self.config)
+
+def makeExtension(configs={}):
+    return ExtraExtension(configs=dict(configs))

mdx/mdx_fenced_code.py

+#!/usr/bin/env python
+
+"""
+Fenced Code Extension for Python Markdown
+=========================================
+
+This extension adds Fenced Code Blocks to Python-Markdown.
+
+    >>> import markdown
+    >>> text = '''
+    ... A paragraph before a fenced code block:
+    ... 
+    ... ~~~
+    ... Fenced code block
+    ... ~~~
+    ... '''
+    >>> html = markdown.markdown(text, extensions=['fenced_code'])
+    >>> html
+    u'<p>A paragraph before a fenced code block:\\n</p>\\n<pre><code>Fenced code block\\n</code></pre>'
+
+Works with safe_mode also (we check this because we are using the HtmlStash):
+
+    >>> markdown.markdown(text, extensions=['fenced_code'], safe_mode='replace')
+    u'<p>A paragraph before a fenced code block:\\n</p>\\n<pre><code>Fenced code block\\n</code></pre>'
+    
+Include tilde's in a code block and wrap with blank lines:
+
+    >>> text = '''
+    ... ~~~~~~~~
+    ... 
+    ... ~~~~
+    ... 
+    ... ~~~~~~~~'''
+    >>> markdown.markdown(text, extensions=['fenced_code'])
+    u'<pre><code>\\n~~~~\\n\\n</code></pre>'
+
+Multiple blocks and language tags:
+
+    >>> text = '''
+    ... ~~~~
+    ... block one
+    ... ~~~~{.python}
+    ... 
+    ... ~~~~
+    ... <p>block two</p>
+    ... ~~~~{.html}'''
+    >>> markdown.markdown(text, extensions=['fenced_code'])
+    u'<pre><code class="python">block one\\n</code></pre>\\n\\n<pre><code class="html">&lt;p&gt;block two&lt;/p&gt;\\n</code></pre>'
+
+"""
+
+import markdown, re
+
+# Global vars
+FENCED_BLOCK_RE = re.compile( \
+    r'(?P<fence>^~{3,})[ ]*\n(?P<code>.*?)(?P=fence)[ ]*(\{\.(?P<lang>[a-zA-Z0-9_-]*)\})?[ ]*$', 
+    re.MULTILINE|re.DOTALL
+    )
+CODE_WRAP = '<pre><code%s>%s</code></pre>'
+LANG_TAG = ' class="%s"'
+
+
+class FencedCodeExtension(markdown.Extension):
+
+    def extendMarkdown(self, md, md_globals):
+        """ Add FencedBlockPreprocessor to the Markdown instance. """
+
+        FENCED_BLOCK_PREPROCESSOR = FencedBlockPreprocessor()
+        FENCED_BLOCK_PREPROCESSOR.md = md
+        md.textPreprocessors.insert(0, FENCED_BLOCK_PREPROCESSOR)
+
+
+class FencedBlockPreprocessor(markdown.TextPreprocessor):
+    
+    def run(self, text):
+        """ Match and store Fenced Code Blocks in the HtmlStash. """
+        while 1:
+            m = FENCED_BLOCK_RE.search(text)
+            if m:
+                lang = ''
+                if m.group('lang'):
+                    lang = LANG_TAG % m.group('lang')
+                code = CODE_WRAP % (lang, self._escape(m.group('code')))
+                placeholder = self.md.htmlStash.store(code, safe=True)
+                text = '%s\n%s\n%s'% (text[:m.start()], placeholder, text[m.end():])
+            else:
+                break
+        return text
+
+    def _escape(self, txt):
+        """ basic html escaping """
+        txt = txt.replace('&', '&amp;')
+        txt = txt.replace('<', '&lt;')
+        txt = txt.replace('>', '&gt;')
+        txt = txt.replace('"', '&quot;')
+        return txt
+
+
+def makeExtension(configs=None):
+    return FencedCodeExtension()
+
+
+if __name__ == "__main__":
+    import doctest
+    doctest.testmod()

mdx/mdx_footnotes.py

+"""
+========================= FOOTNOTES =================================
+
+This section adds footnote handling to markdown.  It can be used as
+an example for extending python-markdown with relatively complex
+functionality.  While in this case the extension is included inside
+the module itself, it could just as easily be added from outside the
+module.  Not that all markdown classes above are ignorant about
+footnotes.  All footnote functionality is provided separately and
+then added to the markdown instance at the run time.
+
+Footnote functionality is attached by calling extendMarkdown()
+method of FootnoteExtension.  The method also registers the
+extension to allow it's state to be reset by a call to reset()
+method.
+
+Example:
+    Footnotes[^1] have a label[^label] and a definition[^!DEF].
+
+    [^1]: This is a footnote
+    [^label]: A footnote on "label"
+    [^!DEF]: The footnote for definition
+
+"""
+
+FN_BACKLINK_TEXT = "zz1337820767766393qq"
+
+
+import re, markdown, random
+from markdown import etree
+
+class FootnoteExtension (markdown.Extension):
+
+    DEF_RE = re.compile(r'(\ ?\ ?\ ?)\[\^([^\]]*)\]:\s*(.*)')
+    SHORT_USE_RE = re.compile(r'\[\^([^\]]*)\]', re.M) # [^a]
+
+    def __init__ (self, configs) :
+
+        self.config = {'PLACE_MARKER' :
+                       ["///Footnotes Go Here///",
+                        "The text string that marks where the footnotes go"]}
+
+        for key, value in configs :
+            self.config[key][0] = value
+            
+        self.reset()
+
+    def extendMarkdown(self, md, md_globals) :
+
+        self.md = md
+
+        # Stateless extensions do not need to be registered
+        md.registerExtension(self)
+
+        # Insert a preprocessor before ReferencePreprocessor
+        index = md.preprocessors.index(md_globals['REFERENCE_PREPROCESSOR'])
+        preprocessor = FootnotePreprocessor(self)
+        preprocessor.md = md
+        md.preprocessors.insert(index, preprocessor)
+
+        # Insert an inline pattern before ImageReferencePattern
+        FOOTNOTE_RE = r'\[\^([^\]]*)\]' # blah blah [^1] blah
+        index = md.inlinePatterns.index(md_globals['IMAGE_REFERENCE_PATTERN'])
+        md.inlinePatterns.insert(index, FootnotePattern(FOOTNOTE_RE, self))
+
+        # Insert a post-processor that would actually add the footnote div
+        postprocessor = FootnotePostprocessor(self)
+        postprocessor.extension = self
+
+        md.postprocessors.append(postprocessor)
+        
+        textPostprocessor = FootnoteTextPostprocessor(self)
+
+        md.textPostprocessors.append(textPostprocessor)
+
+
+    def reset(self) :
+        # May be called by Markdown is state reset is desired
+
+        self.footnote_suffix = "-" + str(int(random.random()*1000000000))
+        self.used_footnotes={}
+        self.footnotes = {}
+
+    def findFootnotesPlaceholder(self, root):
+        
+        def finder(element):
+            for child in element:
+                if child.text:
+                    if child.text.find(self.getConfig("PLACE_MARKER")) > -1:
+                        return child, True
+                if child.tail:
+                    if child.tail.find(self.getConfig("PLACE_MARKER")) > -1:
+                        return (child, element), False
+                finder(child)
+            return None
+                
+        res = finder(root)
+        return res
+
+
+    def setFootnote(self, id, text) :
+        self.footnotes[id] = text
+
+    def makeFootnoteId(self, num) :
+        return 'fn%d%s' % (num, self.footnote_suffix)
+
+    def makeFootnoteRefId(self, num) :
+        return 'fnr%d%s' % (num, self.footnote_suffix)
+
+    def makeFootnotesDiv (self, root) :
+        """Creates the div with class='footnote' and populates it with
+           the text of the footnotes.
+
+           @returns: the footnote div as a dom element """
+
+        if not self.footnotes.keys() :
+            return None
+
+        div = etree.Element("div")
+        div.set('class', 'footnote')
+        hr = etree.SubElement(div, "hr")
+        ol = etree.SubElement(div, "ol")
+        
+
+        footnotes = [(self.used_footnotes[id], id)
+                     for id in self.footnotes.keys()]
+        footnotes.sort()
+
+        for i, id in footnotes :
+            li = etree.SubElement(ol, "li")
+            li.set("id", self.makeFootnoteId(i))
+
+            self.md._processSection(li, self.footnotes[id].split("\n"), looseList=1)
+
+            backlink = etree.Element("a")
+            backlink.set("href", "#" + self.makeFootnoteRefId(i))
+            backlink.set("class", "footnoteBackLink")
+            backlink.set("title",
+                                  "Jump back to footnote %d in the text" % i)
+            backlink.text = FN_BACKLINK_TEXT
+
+            if li.getchildren():
+                node = li[-1]
+                if node.text:
+		            li.append(backlink)
+                elif node.tag == "p":
+                    node.append(backlink)
+                else:
+                    p = etree.SubElement(li, "p")
+                    p.append(backlink)
+        div = self.md.applyInlinePatterns(etree.ElementTree(div)).getroot()
+        return div
+
+
+class FootnotePreprocessor :
+
+    def __init__ (self, footnotes) :
+        self.footnotes = footnotes
+
+    def run(self, lines) :
+
+        self.blockGuru = markdown.BlockGuru()
+        lines = self._handleFootnoteDefinitions (lines)
+
+        # Make a hash of all footnote marks in the text so that we
+        # know in what order they are supposed to appear.  (This
+        # function call doesn't really substitute anything - it's just
+        # a way to get a callback for each occurence.
+
+        text = "\n".join(lines)
+        self.footnotes.SHORT_USE_RE.sub(self.recordFootnoteUse, text)
+
+        return text.split("\n")
+
+
+    def recordFootnoteUse(self, match) :
+
+        id = match.group(1)
+        id = id.strip()
+        nextNum = len(self.footnotes.used_footnotes.keys()) + 1
+        self.footnotes.used_footnotes[id] = nextNum
+
+
+    def _handleFootnoteDefinitions(self, lines) :
+        """Recursively finds all footnote definitions in the lines.
+
+            @param lines: a list of lines of text
+            @returns: a string representing the text with footnote
+                      definitions removed """
+
+        i, id, footnote = self._findFootnoteDefinition(lines)
+
+        if id :
+
+            plain = lines[:i]
+
+            detabbed, theRest = self.blockGuru.detectTabbed(lines[i+1:])
+   
+            self.footnotes.setFootnote(id,
+                                       footnote + "\n"
+                                       + "\n".join(detabbed))
+
+            more_plain = self._handleFootnoteDefinitions(theRest)
+            return plain + [""] + more_plain
+
+        else :
+            return lines
+
+    def _findFootnoteDefinition(self, lines) :
+        """Finds the first line of a footnote definition.
+
+            @param lines: a list of lines of text
+            @returns: the index of the line containing a footnote definition """
+
+        counter = 0
+        for line in lines :
+            m = self.footnotes.DEF_RE.match(line)
+            if m :
+                return counter, m.group(2), m.group(3)
+            counter += 1
+        return counter, None, None
+
+
+class FootnotePattern (markdown.Pattern) :
+
+    def __init__ (self, pattern, footnotes) :
+
+        markdown.Pattern.__init__(self, pattern)
+        self.footnotes = footnotes
+
+    def handleMatch(self, m) :
+        sup = etree.Element("sup")
+        a = etree.SubElement(sup, "a")
+        id = m.group(2)
+        num = self.footnotes.used_footnotes[id]
+        sup.set('id', self.footnotes.makeFootnoteRefId(num))
+        a.set('href', '#' + self.footnotes.makeFootnoteId(num))
+        a.text = str(num)
+        return sup
+
+class FootnotePostprocessor (markdown.Postprocessor):
+
+    def __init__ (self, footnotes) :
+        self.footnotes = footnotes
+
+    def run(self, root):
+        footnotesDiv = self.footnotes.makeFootnotesDiv(root)
+        if footnotesDiv:
+            result = self.extension.findFootnotesPlaceholder(root)
+
+            if result:
+                node, isText = result
+                if isText:
+                    node.text = None
+                    node.getchildren().insert(0, footnotesDiv)
+                else:
+                    child, element = node
+                    ind = element.getchildren().find(child)
+                    element.getchildren().insert(ind + 1, footnotesDiv)
+                    child.tail = None
+                    
+                fnPlaceholder.parent.replaceChild(fnPlaceholder, footnotesDiv)
+            else :
+                root.append(footnotesDiv)
+
+class FootnoteTextPostprocessor (markdown.Postprocessor):
+
+    def __init__ (self, footnotes) :
+        self.footnotes = footnotes
+
+    def run(self, text) :
+        return text.replace(FN_BACKLINK_TEXT, "&#8617;")
+
+def makeExtension(configs=[]):
+    return FootnoteExtension(configs=configs)
+

mdx/mdx_headerid.py

+#!/usr/bin/python
+
+"""
+HeaderID Extension for Python-Markdown
+======================================
+
+Adds ability to set HTML IDs for headers.
+
+Basic usage:
+
+    >>> import markdown
+    >>> text = "# Some Header # {#some_id}"
+    >>> md = markdown.markdown(text, ['headerid'])
+    >>> md
+    u'<h1 id="some_id">Some Header</h1>'
+
+All header IDs are unique:
+
+    >>> text = '''
+    ... #Header
+    ... #Another Header {#header}
+    ... #Third Header {#header}'''
+    >>> md = markdown.markdown(text, ['headerid'])
+    >>> md
+    u'<h1 id="header">Header</h1>\\n\\n<h1 id="header_1">Another Header</h1>\\n\\n<h1 id="header_2">Third Header</h1>'
+
+To fit within a html template's hierarchy, set the header base level:
+
+    >>> text = '''
+    ... #Some Header
+    ... ## Next Level'''
+    >>> md = markdown.markdown(text, ['headerid(level=3)'])
+    >>> md
+    u'<h3 id="some_header">Some Header</h3>\\n\\n<h4 id="next_level">Next Level</h4>'
+
+Turn off auto generated IDs:
+
+    >>> text = '''
+    ... # Some Header
+    ... # Header with ID # { #foo }'''
+    >>> md = markdown.markdown(text, ['headerid(forceid=False)'])
+    >>> md
+    u'<h1>Some Header</h1>\\n\\n<h1 id="foo">Header with ID</h1>'
+
+Use with MetaData extension:
+
+    >>> text = '''header_level: 2
+    ... header_forceid: Off
+    ...
+    ... # A Header'''
+    >>> md = markdown.markdown(text, ['headerid', 'meta'])
+    >>> md
+    u'<h2>A Header</h2>'
+
+By [Waylan Limberg](http://achinghead.com/).
+
+Project website: http://achinghead.com/markdown-headerid/
+Contact: waylan [at] gmail [dot] com
+
+License: [BSD](http://www.opensource.org/licenses/bsd-license.php) 
+
+Version: 0.1 (May 2, 2008)
+
+Dependencies:
+* [Python 2.3+](http://python.org)
+* [Markdown 1.7+](http://www.freewisdom.org/projects/python-markdown/)
+
+"""
+
+import markdown
+from markdown import etree
+import re
+from string import ascii_lowercase, digits, punctuation
+
+ID_CHARS = ascii_lowercase + digits + '-_'
+
+HEADER_RE = re.compile(r'''^(\#{1,6})		# group(1) = string of hashes
+                           ( [^{^#]*)		# group(2) = Header text
+                           [\#]*		    # optional closing hashes (not counted)
+                           (?:[ \t]*\{[ \t]*\#([-_:a-zA-Z0-9]+)[ \t]*\})?	# group(3) = id attr''',
+                           re.VERBOSE)
+
+IDCOUNT_RE = re.compile(r'^(.*)_([0-9]+)$')
+
+class HeaderIdExtension (markdown.Extension) :
+    def __init__(self, configs):
+        # set defaults
+        self.config = {
+                'level' : ['1', 'Base level for headers.'],
+                'forceid' : ['True', 'Force all headers to have an id.'],
+                'toc_id' : ['toc', 'Set html id of wrapper div for TOC.'],
+                'toc_marker': ['///TOC///', 'Marker to identify position of TOC.']
+            }
+
+        for key, value in configs:
+            self.setConfig(key, value)
+
+
+    def extendMarkdown(self, md, md_globals) :
+
+        md.IDs = []
+        md.toc = Toc(self.getConfig('toc_id'), self.getConfig('toc_marker'))
+
+        def _processHeaderId(parent_elem, paragraph) :
+            ''' 
+            Overrides _processHeader of Markdown() and 
+            adds an 'id' to the header. 
+            '''
+            m = HEADER_RE.match(paragraph[0])
+            if m :
+                start_level, force_id = _get_meta()
+                level = len(m.group(1)) + start_level
+                if level > 6: 
+                    level = 6
+                h = etree.Element("h%d" % level)
+                parent_elem.append(h)
+                inline = etree.SubElement(h, "inline")
+                inline.text = m.group(2).strip()
+                i = ''
+                if m.group(3):
+                    i = _unique_id(m.group(3))
+                elif force_id:
+                    i = _create_id(m.group(2).strip())
+                if i:
+                    h.set('id', i)
+                    md.toc.append(i, inline.text)
+            else :
+                message(CRITICAL, "We've got a problem header!")
+        
+        md._processHeader = _processHeaderId
+
+        def _get_meta():
+            ''' Return meta data suported by this ext as a tuple '''
+            level = int(self.config['level'][0]) - 1
+            force = _str2bool(self.config['forceid'][0])
+            if hasattr(md, 'Meta'):
+                if md.Meta.has_key('header_level'):
+                    level = int(md.Meta['header_level'][0]) - 1
+                if md.Meta.has_key('header_forceid'): 
+                    force = _str2bool(md.Meta['header_forceid'][0])
+            return level, force
+
+        def _str2bool(s, default=False):
+            ''' Convert a string to a booleen value. '''
+            s = str(s)
+            if s.lower() in ['0', 'f', 'false', 'off', 'no', 'n']:
+                return False
+            elif s.lower() in ['1', 't', 'true', 'on', 'yes', 'y']:
+                    return True
+            return default
+
+        def _unique_id(id):
+            ''' Ensure ID is unique. Append '_1', '_2'... if not '''
+            while id in md.IDs:
+                m = IDCOUNT_RE.match(id)
+                if m:
+                    id = '%s_%d'% (m.group(1), int(m.group(2))+1)
+                else:
+                    id = '%s_%d'% (id, 1)
+            md.IDs.append(id)
+            return id
+
+
+        def _create_id(header):
+            ''' Return ID from Header text. '''
+            h = ''
+            for c in header.lower().replace(' ', '_'):
+                if c in ID_CHARS:
+                    h += c
+                elif c not in punctuation:
+                    h += '+'
+            return _unique_id(h)
+
+class Toc():
+    """ Store a Table of Contents from a documents Headers. """
+    def __init__(self, html_id, marker):
+        self.html_id = html_id
+        self.marker = marker
+        self.ids = []
+        self.labels = []
+
+    def append(self, id, label):
+        """ Append an item to the store. """
+        self.ids.append(id)
+        self.labels.append(label)
+
+    def render(self):
+        """ Render the TOC as HTML and return unicode. """
+        out = u'<div id="%s"><ul>\n' % self.html_id
+        for c in range(len(self.ids)):
+            out += u'<li><a href="#%s">%s</a></li>\n'%(self.ids[c], self.labels[c])
+        out += u'</ul></div>'
+        return out
+
+
+def makeExtension(configs=None) :
+    return HeaderIdExtension(configs=configs)
+
+if __name__ == "__main__":
+    import doctest
+    doctest.testmod()
+

mdx/mdx_imagelinks.py

+"""
+========================= IMAGE LINKS =================================
+
+
+Turns paragraphs like
+
+<~~~~~~~~~~~~~~~~~~~~~~~~
+dir/subdir
+dir/subdir
+dir/subdir
+~~~~~~~~~~~~~~
+dir/subdir
+dir/subdir
+dir/subdir
+~~~~~~~~~~~~~~~~~~~>
+
+Into mini-photo galleries.
+
+"""
+
+import re, markdown
+import url_manager
+
+
+IMAGE_LINK = """<a href="%s"><img src="%s" title="%s"/></a>"""
+SLIDESHOW_LINK = """<a href="%s" target="_blank">[slideshow]</a>"""
+ALBUM_LINK = """&nbsp;<a href="%s">[%s]</a>"""
+
+
+class ImageLinksExtension (markdown.Extension):
+
+    def __init__ (self) :
+        self.reset()
+
+    def extendMarkdown(self, md, md_globals) :
+
+        self.md = md
+
+        # Stateless extensions do not need to be registered
+        md.registerExtension(self)
+
+        # Insert a preprocessor before all preprocessors
+
+        preprocessor = ImageLinkPreprocessor()
+        preprocessor.md = md
+        md.preprocessors.insert(0, preprocessor)
+
+    def reset(self) :
+        # May be called by Markdown is state reset is desired
+        pass
+
+
+class ImageLinkPreprocessor (markdown.Preprocessor):
+
+    def run(self, lines) :
+
+        url = url_manager.BlogEntryUrl(url_manager.BlogUrl("all"),
+                                       "2006/08/29/the_rest_of_our")
+
+
+        all_images = []
+        blocks = []
+        in_image_block = False
+
+        new_lines = []
+        
+        for line in lines :
+
+            if line.startswith("<~~~~~~~") :
+                albums = []
+                rows = []
+                in_image_block = True
+
+            if not in_image_block :
+
+                new_lines.append(line)
+
+            else :
+
+                line = line.strip()
+                
+                if line.endswith("~~~~~~>") or not line :
+                    in_image_block = False
+                    new_block = "<div><br/><center><span class='image-links'>\n"
+
+                    album_url_hash = {}
+
+                    for row in rows :
+                        for photo_url, title in row :
+                            new_block += "&nbsp;"
+                            new_block += IMAGE_LINK % (photo_url,
+                                                       photo_url.get_thumbnail(),
+                                                       title)
+                            
+                            album_url_hash[str(photo_url.get_album())] = 1
+                        
+                    new_block += "<br/>"
+                            
+                    new_block += "</span>"
+                    new_block += SLIDESHOW_LINK % url.get_slideshow()
+
+                    album_urls = album_url_hash.keys()
+                    album_urls.sort()
+
+                    if len(album_urls) == 1 :
+                        new_block += ALBUM_LINK % (album_urls[0], "complete album")
+                    else :
+                        for i in range(len(album_urls)) :
+                            new_block += ALBUM_LINK % (album_urls[i],
+                                                       "album %d" % (i + 1) )
+                    
+                    new_lines.append(new_block + "</center><br/></div>")
+
+                elif line[1:6] == "~~~~~" :
+                    rows.append([])  # start a new row
+                else :
+                    parts = line.split()
+                    line = parts[0]
+                    title = " ".join(parts[1:])
+
+                    album, photo = line.split("/")
+                    photo_url = url.get_photo(album, photo,
+                                              len(all_images)+1)
+                    all_images.append(photo_url)                        
+                    rows[-1].append((photo_url, title))
+
+                    if not album in albums :
+                        albums.append(album)
+
+        return new_lines
+
+
+def makeExtension(configs) :
+    return ImageLinksExtension(configs)
+
+#!usr/bin/python
+
+'''
+Meta Data Extension for Python-Markdown
+==========================================
+
+This extension adds Meta Data handling to markdown.
+
+    >>> import markdown
+    >>> text = """Title: A Test Doc.
+    ... Author: Waylan Limberg
+    ...         John Doe
+    ... Blank_Data:
+    ...
+    ... The body. This is paragraph one.
+    ... """
+    >>> md = markdown.Markdown(text, ['meta'])
+    >>> md.convert()
+    u'<p>The body. This is paragraph one.\\n</p>'
+    >>> md.Meta
+    {u'blank_data': [u''], u'author': [u'Waylan Limberg', u'John Doe'], u'title': [u'A Test Doc.']}
+
+Make sure text without Meta Data still works (markdown < 1.6b returns a <p>).
+
+    >>> text = '    Some Code - not extra lines of meta data.'
+    >>> md = markdown.Markdown(text, ['meta'])
+    >>> md.convert()
+    u'<pre><code>Some Code - not extra lines of meta data.\\n</code></pre>'
+    >>> md.Meta
+    {}
+
+'''
+
+import markdown, re
+
+# Global Vars
+META_RE = re.compile(r'^[ ]{0,3}(?P<key>[A-Za-z0-9_-]+):\s*(?P<value>.*)')
+META_MORE_RE = re.compile(r'^[ ]{4,}(?P<value>.*)')
+
+class MetaExtension (markdown.Extension) :
+    def __init__(self, configs):
+        pass
+
+    def extendMarkdown(self, md, md_globals) :
+        self.md = md
+
+        # Insert meta preprocessor first
+        META_PREPROCESSOR = MetaPreprocessor()
+        META_PREPROCESSOR.md = md
+        md.preprocessors.insert(0, META_PREPROCESSOR)
+        
+class MetaPreprocessor(markdown.Preprocessor) :
+    def run(self, lines) :
+        meta = {}
+        key = None
+        while 1:
+            line = lines.pop(0)
+            if line.strip() == '':
+                break # blank line - done
+            m1 = META_RE.match(line)
+            if m1:
+                key = m1.group('key').lower().strip()
+                meta[key] = [m1.group('value').strip()]
+            else:
+                m2 = META_MORE_RE.match(line)
+                if m2 and key:
+                    # Add another line to existing key
+                    meta[key].append(m2.group('value').strip())
+                else:
+                    lines.insert(0, line)
+                    break # no meta data - done
+        self.md.Meta = meta
+        return lines
+        
+
+def makeExtension(configs=None) :
+    return MetaExtension(configs=configs)
+
+if __name__ == "__main__":
+    import doctest
+    doctest.testmod()
+import markdown
+from markdown import etree
+
+DEFAULT_URL = "http://www.freewisdom.org/projects/python-markdown/"
+DEFAULT_CREATOR = "Yuri Takhteyev"
+DEFAULT_TITLE = "Markdown in Python"
+GENERATOR = "http://www.freewisdom.org/projects/python-markdown/markdown2rss"
+
+month_map = { "Jan" : "01",
+              "Feb" : "02",
+              "March" : "03",
+              "April" : "04",
+              "May" : "05",
+              "June" : "06",
+              "July" : "07",
+              "August" : "08",
+              "September" : "09",
+              "October" : "10",
+              "November" : "11",
+              "December" : "12" }
+
+def get_time(heading):
+
+    heading = heading.split("-")[0]
+    heading = heading.strip().replace(",", " ").replace(".", " ")
+
+    month, date, year = heading.split()
+    month = month_map[month]
+
+    return rdftime(" ".join((month, date, year, "12:00:00 AM")))
+
+def rdftime(time):
+
+    time = time.replace(":", " ")
+    time = time.replace("/", " ")
+    time = time.split()
+    return "%s-%s-%sT%s:%s:%s-08:00" % (time[0], time[1], time[2],
+                                        time[3], time[4], time[5])
+
+
+def get_date(text):
+    return "date"
+
+class RssExtension (markdown.Extension):
+
+    def extendMarkdown(self, md, md_globals):
+
+        self.config = { 'URL' : [DEFAULT_URL, "Main URL"],
+                        'CREATOR' : [DEFAULT_CREATOR, "Feed creator's name"],
+                        'TITLE' : [DEFAULT_TITLE, "Feed title"] }
+
+        md.xml_mode = True
+        
+        # Insert a post-processor that would actually add the title tag
+        postprocessor = RssPostProcessor(self)
+        postprocessor.ext = self
+        md.postprocessors.append(postprocessor)
+        md.stripTopLevelTags = 0
+        md.docType = '<?xml version="1.0" encoding="utf-8"?>\n'
+
+class RssPostProcessor (markdown.Postprocessor):
+
+    def __init__(self, md):
+        
+        pass
+
+    def run (self, root):
+
+        rss = etree.Element("rss")
+        rss.set("version", "2.0")
+
+        channel = etree.SubElement(rss, "channel")
+
+        for tag, text in (("title", self.ext.getConfig("TITLE")),
+                          ("link", self.ext.getConfig("URL")),
+                          ("description", None)):
+            
+            element = etree.SubElement(channel, tag)
+            element.text = text
+
+        for child in root:
+
+
+            if child.tag in ["h1", "h2", "h3", "h4", "h5"] :
+      
+                heading = child.text.strip()
+                
+                item = etree.SubElement(channel, "item")
+  
+                link = etree.SubElement(item, "link")
+                link.text = self.ext.getConfig("URL")
+
+                title = etree.SubElement(item, "title")
+                title.text = heading
+
+                guid = ''.join([x for x in heading if x.isalnum()])
+
+                guidElem = etree.SubElement(item, "guid")
+                guidElem.text = guid
+                guidElem.set("isPermaLink", "false")
+
+            elif child.tag in ["p"] :
+                if item:
+                    description = etree.SubElement(item, "description")
+                    if len(child):
+                        content = "\n".join([etree.tostring(node)
+                                             for node in child])
+                    else:
+                        content = child.text
+                    pholder = self.stash.store("<![CDATA[ %s]]>" % content)
+                    description.text = pholder
+    
+        return rss
+
+
+def makeExtension(configs):
+
+    return RssExtension(configs)

mdx/mdx_tables.py

+#!/usr/bin/env python
+
+"""
+Table extension for Python-Markdown
+"""
+
+import markdown
+from markdown import etree
+
+class TablePattern(markdown.Pattern) :
+    def __init__ (self, md):
+        markdown.Pattern.__init__(self, r'(^|\n)\|([^\n]*)\|')
+        self.md = md
+
+    def handleMatch(self, m):
+
+        # a single line represents a row
+        tr = etree.Element('tr')
+        
+        # chunks between pipes represent cells
+
+        for t in m.group(3).split('|'): 
+     
+            if len(t) >= 2 and t.startswith('*') and t.endswith('*'):
+                # if a cell is bounded by asterisks, it is a <th>
+                td = etree.Element('th')
+                t = t[1:-1]
+            else:
+                # otherwise it is a <td>
+                td = etree.Element('td')
+            
+            # add text ot inline section, later it will be
+            # processed by core
+            inline = etree.SubElement(td, "inline")
+            inline.text = t
+                    
+            tr.append(td)
+            tr.tail = "\n"
+ 
+        return tr
+
+
+class TablePostprocessor:
+    
+    def _findElement(self, element, name):
+        result = []
+        for child in element:
+            if child.tag == name:
+                result.append(child)
+            result += self._findElement(child, name)
+        return result
+    
+    def run(self, root):
+
+        for element in self._findElement(root, "p"):
+             for child in element:
+                 if child.tail:
+                     element.tag = "table"
+                     break
+        
+                
+
+
+class TableExtension(markdown.Extension):
+    def extendMarkdown(self, md, md_globals):
+        md.inlinePatterns.insert(0, TablePattern(md))
+        md.postprocessors.append(TablePostprocessor())
+
+
+def makeExtension(configs):
+    return TableExtension(configs)
+

mdx/mdx_wikilink.py

+#!/usr/bin/env python
+
+'''
+WikiLink Extention for Python-Markdown
+======================================
+
+Converts CamelCase words to relative links.  Requires Python-Markdown 1.6+
+
+Basic usage:
+
+    >>> import markdown
+    >>> text = "Some text with a WikiLink."
+    >>> md = markdown.markdown(text, ['wikilink'])
+    >>> md
+    u'<p>Some text with a <a href="/WikiLink/" class="wikilink">WikiLink</a>.\\n</p>'
+
+To define custom settings the simple way:
+
+    >>> md = markdown.markdown(text, 
+    ...     ['wikilink(base_url=/wiki/,end_url=.html,html_class=foo)']
+    ... )
+    >>> md
+    u'<p>Some text with a <a href="/wiki/WikiLink.html" class="foo">WikiLink</a>.\\n</p>'
+    
+Custom settings the complex way:
+
+    >>> md = markdown.Markdown(
+    ...     extensions = ['wikilink'], 
+    ...     extension_configs = {'wikilink': [
+    ...                                 ('base_url', 'http://example.com/'), 
+    ...                                 ('end_url', '.html'),
+    ...                                 ('html_class', '') ]},
+    ...     safe_mode = True)
+    >>> md.convert(text)
+    u'<p>Some text with a <a href="http://example.com/WikiLink.html">WikiLink</a>.\\n</p>'
+
+Use MetaData with mdx_meta.py (Note the blank html_class in MetaData):
+
+    >>> text = """wiki_base_url: http://example.com/
+    ... wiki_end_url:   .html
+    ... wiki_html_class:
+    ...
+    ... Some text with a WikiLink."""
+    >>> md = markdown.Markdown(extensions=['meta', 'wikilink'])
+    >>> md.convert(text)
+    u'<p>Some text with a <a href="http://example.com/WikiLink.html">WikiLink</a>.\\n</p>'
+
+MetaData should not carry over to next document:
+
+    >>> md.convert("No MetaData here.")
+    u'<p>No <a href="/MetaData/" class="wikilink">MetaData</a> here.\\n</p>'
+
+From the command line:
+
+    python markdown.py -x wikilink(base_url=http://example.com/,end_url=.html,html_class=foo) src.txt
+
+By [Waylan Limberg](http://achinghead.com/).
+
+Project website: http://achinghead.com/markdown-wikilinks/
+Contact: waylan [at] gmail [dot] com
+
+License: [BSD](http://www.opensource.org/licenses/bsd-license.php) 
+
+Version: 0.6 (May 2, 2008)
+
+Dependencies:
+* [Python 2.3+](http://python.org)
+* [Markdown 1.6+](http://www.freewisdom.org/projects/python-markdown/)
+'''
+
+import markdown
+from markdown import etree
+
+class WikiLinkExtension (markdown.Extension) :
+    def __init__(self, configs):
+        # set extension defaults
+        self.config = {
+                        'base_url' : ['/', 'String to append to beginning or URL.'],
+                        'end_url' : ['/', 'String to append to end of URL.'],
+                        'html_class' : ['wikilink', 'CSS hook. Leave blank for none.']
+        }
+        
+        # Override defaults with user settings
+        for key, value in configs :
+            self.setConfig(key, value)
+        
+    def extendMarkdown(self, md, md_globals):
+        self.md = md
+    
+        # append to end of inline patterns
+        WIKILINK_RE = r'''(?P<escape>\\|\b)(?P<camelcase>([A-Z]+[a-z-_]+){2,})\b'''
+        WIKILINK_PATTERN = WikiLinks(WIKILINK_RE, self.config)
+        WIKILINK_PATTERN.md = md
+        md.inlinePatterns.append(WIKILINK_PATTERN)  
+        
+
+class WikiLinks (markdown.BasePattern) :
+    def __init__(self, pattern, config):
+        markdown.BasePattern.__init__(self, pattern)
+        self.config = config
+  
+    def handleMatch(self, m):
+        if  m.group('escape') == '\\':
+            a = m.group('camelcase')
+        else:
+            base_url, end_url, html_class = self._getMeta()
+            url = '%s%s%s'% (base_url, m.group('camelcase'), end_url)
+            label = m.group('camelcase').replace('_', ' ')
+            a = etree.Element('a')
+            a.text = label
+            a.set('href', url)
+            if html_class:
+                a.set('class', html_class)
+        return a
+
+    def _getMeta(self):
+        """ Return meta data or config data. """
+        base_url = self.config['base_url'][0]
+        end_url = self.config['end_url'][0]
+        html_class = self.config['html_class'][0]
+        if hasattr(self.md, 'Meta'):
+            if self.md.Meta.has_key('wiki_base_url'):
+                base_url = self.md.Meta['wiki_base_url'][0]
+            if self.md.Meta.has_key('wiki_end_url'):
+                end_url = self.md.Meta['wiki_end_url'][0]
+            if self.md.Meta.has_key('wiki_html_class'):
+                html_class = self.md.Meta['wiki_html_class'][0]
+        return base_url, end_url, html_class
+    
+    def type(self):
+        return "WLink"
+
+
+def makeExtension(configs=None) :
+    return WikiLinkExtension(configs=configs)
+
+if __name__ == "__main__":
+    import doctest
+    doctest.testmod()
+

mdx_codehilite.py

-#!/usr/bin/python
-
-"""
-CodeHilite Extension for Python-Markdown
-=======================================
-
-Adds code/syntax highlighting to standard Python-Markdown code blocks.
-
-By [Waylan Limberg](http://achinghead.com/).
-
-Project website: http://achinghead.com/markdown-wikilinks/
-Contact: waylan [at] gmail [dot] com
- 
-License: [BSD](http://www.opensource.org/licenses/bsd-license.php)
- 
-Version: 0.2 (April 30, 2008)
-  
-Dependencies:
-* [Python 2.3+](http://python.org/)
-* [Markdown 1.7+](http://www.freewisdom.org/projects/python-markdown/)
-* [Pygments](http://pygments.org/)
-
-"""
-
-import markdown
-
-# --------------- CONSTANTS YOU MIGHT WANT TO MODIFY -----------------
-
-try:
-    TAB_LENGTH = markdown.TAB_LENGTH
-except AttributeError:
-    TAB_LENGTH = 4
-
-
-# ------------------ The Main CodeHilite Class ----------------------
-class CodeHilite:
-    """
-    Determine language of source code, and pass it into the pygments hilighter.
-
-    Basic Usage:
-        >>> code = CodeHilite(src = text)
-        >>> html = code.hilite()
-    
-    * src: Can be a string or any object with a .readline attribute.
-      
-    * linenos: (Boolen) Turns line numbering 'on' or 'off' (off by default).
-      
-    Low Level Usage:
-        >>> code = CodeHilite()
-        >>> code.src = text      # String or anything with a .readline attribute
-        >>> code.linenos = True  # True or False; Turns line numbering on or of.
-        >>> html = code.hilite()
-    
-    """
-
-    def __init__(self, src=None, linenos = False):
-        self.src = src
-        self.lang = None
-        self.linenos = linenos
-
-    def hilite(self):
-        """
-        Pass code to the [Pygments](http://pygments.pocoo.org/) highliter with 
-        optional line numbers. The output should then be styled with css to 
-        your liking. No styles are applied by default - only styling hooks 
-        (i.e.: <span class="k">). 
-
-        returns : A string of html.
-    
-        """
-
-        self.src = self.src.strip('\n')
-        
-        self._getLang()
-
-        try:
-            from pygments import highlight
-            from pygments.lexers import get_lexer_by_name, guess_lexer, TextLexer
-            from pygments.formatters import HtmlFormatter
-        except ImportError:
-            # just escape and pass through
-            txt = self._escape(self.src)
-            '''if num:
-                txt = self._number(txt)
-            else :
-                txt = '<div class="codehilite"><pre>%s</pre></div>\n'% txt'''
-            txt = self._number(txt)    
-            return txt
-        else:
-            try:
-                lexer = get_lexer_by_name(self.lang)
-            except ValueError:
-                try:
-                    lexer = guess_lexer(self.src)
-                except ValueError:
-                    lexer = TextLexer()
-            formatter = HtmlFormatter(linenos=self.linenos, cssclass="codehilite")
-            return highlight(self.src, lexer, formatter)
-
-    def _escape(self, txt):
-        """ basic html escaping """
-        txt = txt.replace('&', '&amp;')
-        txt = txt.replace('<', '&lt;')
-        txt = txt.replace('>', '&gt;')
-        txt = txt.replace('"', '&quot;')
-        return txt
-
-    def _number(self, txt):
-        """ Use <ol> for line numbering """
-        # Fix Whitespace
-        txt = txt.replace('\t', ' '*TAB_LENGTH)
-        txt = txt.replace(" "*4, "&nbsp; &nbsp; ")
-        txt = txt.replace(" "*3, "&nbsp; &nbsp;")
-        txt = txt.replace(" "*2, "&nbsp; ")        
-        
-        # Add line numbers
-        lines = txt.splitlines()
-        txt = '<div class="codehilite"><pre><ol>\n'
-        for line in lines:
-            txt += '\t<li>%s</li>\n'% line
-        txt += '</ol></pre></div>\n'
-        return txt
-
-
-    def _getLang(self):
-        """ 
-        Determines language of a code block from shebang lines and whether said
-        line should be removed or left in place. If the sheband line contains a
-        path (even a single /) then it is assumed to be a real shebang lines and
-        left alone. However, if no path is given (e.i.: #!python or :::python) 
-        then it is assumed to be a mock shebang for language identifitation of a
-        code fragment and removed from the code block prior to processing for 
-        code highlighting. When a mock shebang (e.i: #!python) is found, line 
-        numbering is turned on. When colons are found in place of a shebang 
-        (e.i.: :::python), line numbering is left in the current state - off 
-        by default.
-        
-        """
-
-        import re
-    
-        #split text into lines
-        lines = self.src.split("\n")
-        #pull first line to examine
-        fl = lines.pop(0)
-    
-        c = re.compile(r'''
-            (?:(?:::+)|(?P<shebang>[#]!))	#shebang or 2 or more colons
-            (?P<path>(?:/\w+)*[/ ])? # zero or 1 path ending in either a / or a single space
-            (?P<lang>\w*)	# the language (a single /  or space before lang is a path)
-            ''',  re.VERBOSE)
-        # search first line for shebang
-        m = c.search(fl)
-        if m:
-            # we have a match
-            try:
-                self.lang = m.group('lang').lower()
-            except IndexError:
-                self.lang = None
-            if m.group('path'):
-                # path exists - restore first line
-                lines.insert(0, fl)
-            if m.group('shebang'):
-                # shebang exists - use line numbers
-                self.linenos = True
-        else:
-            # No match
-            lines.insert(0, fl)
-        
-        self.src = "\n".join(lines).strip("\n")
-
-
-
-# ------------------ The Markdown Extension -------------------------------
-class CodeHiliteExtention(markdown.Extension):
-    def __init__(self, configs):
-        # define default configs
-        self.config = {
-            'force_linenos' : [False, "Force line numbers - Default: False"] 
-            }
-        
-        # Override defaults with user settings
-        for key, value in configs:
-            # self.config[key][0] = value
-            self.setConfig(key, value) 
-            
-    def extendMarkdown(self, md, md_globals):
-  
-        def _hiliteCodeBlock(parent_elem, lines, inList):
-            """
-            Overrides `_processCodeBlock` method in standard Markdown class 
-            and sends code blocks to a code highlighting proccessor. The result
-            is then stored in the HtmlStash, a placeholder is inserted into
-            the dom and the remainder of the text file is processed recursively.
-
-            * parent_elem: DOM element to which the content will be added
-            * lines: a list of lines
-            * inList: a level
-            
-            returns: None
-
-            """
-
-            detabbed, theRest = md.blockGuru.detectTabbed(lines)
-            text = "\n".join(detabbed).rstrip()+"\n"
-            code = CodeHilite(text, linenos=self.config['force_linenos'][0]) 
-            placeholder = md.htmlStash.store(code.hilite(), safe=True)
-            if parent_elem.text:
-                parent_elem.text += placeholder
-            else:
-                parent_elem.text = placeholder
-            md._processSection(parent_elem, theRest, inList)
-            
-        md._processCodeBlock = _hiliteCodeBlock
-
-def makeExtension(configs={}):
-  return CodeHiliteExtention(configs=configs)
-

mdx_extra.py

-#!/usr/bin/env python
-"""
-Python-Markdown Extra Extension
-===============================
-
-A compilation of various Python-Markdown extensions that imitates
-[PHP Markdown Extra](http://michelf.com/projects/php-markdown/extra/).
-
-As no-one has yet written a Definition List extension for Python-
-Markdown, definition lists are not yet supported by Extra.
-
-Note that each of the individual extensions still need to be available
-on your PYTHONPATH. This extension simply wraps them all up as a 
-convenience so that only one extension needs to be listed when
-initiating Markdown. See the documentation for each individual
-extension for specifics about that extension.
-
-In the event that one or more of the supported extensions are not 
-available for import, Markdown will simply continue without that 
-extension. If you would like to be notified of such failures,
-you may set Python-Markdown's logger level to "WARN".
-
-There may be additional extensions that are distributed with 
-Python-Markdown that are not included here in Extra. Those extensions
-are not part of PHP Markdown Extra, and therefore, not part of
-Python-Markdown Extra. If you really would like Extra to include
-additional extensions, we suggest creating your own clone of Extra
-under a differant name. You could also edit the `extensions` global 
-variable defined below, but be aware that such changes may be lost 
-when you upgrade to any future version of Python-Markdown.
-
-"""
-
-import markdown
-
-extensions = ['fenced_code',
-              'footnotes',
-              'headerid',
-              'tables',
-              'abbr',
-              ]
-              
-
-class ExtraExtension(markdown.Extension):
-    """ Add various extensions to Markdown class."""
-
-    def extendMarkdown(self, md, md_globals):
-        """ Register extension instances. """
-        md.registerExtensions(extensions, self.config)
-
-def makeExtension(configs={}):
-    return ExtraExtension(configs=dict(configs))

mdx_fenced_code.py

-#!/usr/bin/env python
-
-"""
-Fenced Code Extension for Python Markdown
-=========================================
-
-This extension adds Fenced Code Blocks to Python-Markdown.
-
-    >>> import markdown
-    >>> text = '''
-    ... A paragraph before a fenced code block:
-    ... 
-    ... ~~~
-    ... Fenced code block
-    ... ~~~
-    ... '''
-    >>> html = markdown.markdown(text, extensions=['fenced_code'])
-    >>> html
-    u'<p>A paragraph before a fenced code block:\\n</p>\\n<pre><code>Fenced code block\\n</code></pre>'
-
-Works with safe_mode also (we check this because we are using the HtmlStash):
-
-    >>> markdown.markdown(text, extensions=['fenced_code'], safe_mode='replace')
-    u'<p>A paragraph before a fenced code block:\\n</p>\\n<pre><code>Fenced code block\\n</code></pre>'
-    
-Include tilde's in a code block and wrap with blank lines:
-
-    >>> text = '''
-    ... ~~~~~~~~
-    ... 
-    ... ~~~~
-    ... 
-    ... ~~~~~~~~'''
-    >>> markdown.markdown(text, extensions=['fenced_code'])
-    u'<pre><code>\\n~~~~\\n\\n</code></pre>'
-
-Multiple blocks and language tags:
-
-    >>> text = '''
-    ... ~~~~
-    ... block one
-    ... ~~~~{.python}
-    ... 
-    ... ~~~~
-    ... <p>block two</p>
-    ... ~~~~{.html}'''
-    >>> markdown.markdown(text, extensions=['fenced_code'])
-    u'<pre><code class="python">block one\\n</code></pre>\\n\\n<pre><code class="html">&lt;p&gt;block two&lt;/p&gt;\\n</code></pre>'
-
-"""
-
-import markdown, re
-
-# Global vars
-FENCED_BLOCK_RE = re.compile( \
-    r'(?P<fence>^~{3,})[ ]*\n(?P<code>.*?)(?P=fence)[ ]*(\{\.(?P<lang>[a-zA-Z0-9_-]*)\})?[ ]*$', 
-    re.MULTILINE|re.DOTALL
-    )
-CODE_WRAP = '<pre><code%s>%s</code></pre>'
-LANG_TAG = ' class="%s"'
-
-
-class FencedCodeExtension(markdown.Extension):
-
-    def extendMarkdown(self, md, md_globals):
-        """ Add FencedBlockPreprocessor to the Markdown instance. """
-
-        FENCED_BLOCK_PREPROCESSOR = FencedBlockPreprocessor()
-        FENCED_BLOCK_PREPROCESSOR.md = md
-        md.textPreprocessors.insert(0, FENCED_BLOCK_PREPROCESSOR)
-
-
-class FencedBlockPreprocessor(markdown.TextPreprocessor):
-    
-    def run(self, text):
-        """ Match and store Fenced Code Blocks in the HtmlStash. """
-        while 1:
-            m = FENCED_BLOCK_RE.search(text)
-            if m:
-                lang = ''
-                if m.group('lang'):
-                    lang = LANG_TAG % m.group('lang')
-                code = CODE_WRAP % (lang, self._escape(m.group('code')))
-                placeholder = self.md.htmlStash.store(code, safe=True)
-                text = '%s\n%s\n%s'% (text[:m.start()], placeholder, text[m.end():])
-            else:
-                break
-        return text
-
-    def _escape(self, txt):
-        """ basic html escaping """
-        txt = txt.replace('&', '&amp;')
-        txt = txt.replace('<', '&lt;')
-        txt = txt.replace('>', '&gt;')
-        txt = txt.replace('"', '&quot;')
-        return txt
-
-
-def makeExtension(configs=None):
-    return FencedCodeExtension()
-
-
-if __name__ == "__main__":
-    import doctest
-    doctest.testmod()

mdx_footnotes.py

-"""
-========================= FOOTNOTES =================================
-
-This section adds footnote handling to markdown.  It can be used as
-an example for extending python-markdown with relatively complex
-functionality.  While in this case the extension is included inside
-the module itself, it could just as easily be added from outside the
-module.  Not that all markdown classes above are ignorant about
-footnotes.  All footnote functionality is provided separately and
-then added to the markdown instance at the run time.
-
-Footnote functionality is attached by calling extendMarkdown()
-method of FootnoteExtension.  The method also registers the
-extension to allow it's state to be reset by a call to reset()
-method.
-
-Example:
-    Footnotes[^1] have a label[^label] and a definition[^!DEF].
-
-    [^1]: This is a footnote
-    [^label]: A footnote on "label"
-    [^!DEF]: The footnote for definition
-
-"""
-
-FN_BACKLINK_TEXT = "zz1337820767766393qq"
-
-
-import re, markdown, random
-from markdown import etree
-
-class FootnoteExtension (markdown.Extension):
-
-    DEF_RE = re.compile(r'(\ ?\ ?\ ?)\[\^([^\]]*)\]:\s*(.*)')
-    SHORT_USE_RE = re.compile(r'\[\^([^\]]*)\]', re.M) # [^a]
-
-    def __init__ (self, configs) :
-
-        self.config = {'PLACE_MARKER' :
-                       ["///Footnotes Go Here///",
-                        "The text string that marks where the footnotes go"]}
-
-        for key, value in configs :
-            self.config[key][0] = value
-            
-        self.reset()
-
-    def extendMarkdown(self, md, md_globals) :
-
-        self.md = md
-
-        # Stateless extensions do not need to be registered
-        md.registerExtension(self)
-
-        # Insert a preprocessor before ReferencePreprocessor
-        index = md.preprocessors.index(md_globals['REFERENCE_PREPROCESSOR'])
-        preprocessor = FootnotePreprocessor(self)
-        preprocessor.md = md
-        md.preprocessors.insert(index, preprocessor)
-
-        # Insert an inline pattern before ImageReferencePattern
-        FOOTNOTE_RE = r'\[\^([^\]]*)\]' # blah blah [^1] blah
-        index = md.inlinePatterns.index(md_globals['IMAGE_REFERENCE_PATTERN'])
-        md.inlinePatterns.insert(index, FootnotePattern(FOOTNOTE_RE, self))
-
-        # Insert a post-processor that would actually add the footnote div
-        postprocessor = FootnotePostprocessor(self)
-        postprocessor.extension = self
-
-        md.postprocessors.append(postprocessor)
-        
-        textPostprocessor = FootnoteTextPostprocessor(self)
-
-        md.textPostprocessors.append(textPostprocessor)
-
-
-    def reset(self) :
-        # May be called by Markdown is state reset is desired
-
-        self.footnote_suffix = "-" + str(int(random.random()*1000000000))
-        self.used_footnotes={}
-        self.footnotes = {}
-
-    def findFootnotesPlaceholder(self, root):
-        
-        def finder(element):
-            for child in element:
-                if child.text:
-                    if child.text.find(self.getConfig("PLACE_MARKER")) > -1:
-                        return child, True
-                if child.tail:
-                    if child.tail.find(self.getConfig("PLACE_MARKER")) > -1:
-                        return (child, element), False
-                finder(child)
-            return None
-                
-        res = finder(root)
-        return res
-
-
-    def setFootnote(self, id, text) :
-        self.footnotes[id] = text
-
-    def makeFootnoteId(self, num) :
-        return 'fn%d%s' % (num, self.footnote_suffix)
-
-    def makeFootnoteRefId(self, num) :
-        return 'fnr%d%s' % (num, self.footnote_suffix)
-
-    def makeFootnotesDiv (self, root) :
-        """Creates the div with class='footnote' and populates it with
-           the text of the footnotes.
-
-           @returns: the footnote div as a dom element """
-
-        if not self.footnotes.keys() :
-            return None
-
-        div = etree.Element("div")
-        div.set('class', 'footnote')
-        hr = etree.SubElement(div, "hr")
-        ol = etree.SubElement(div, "ol")
-        
-
-        footnotes = [(self.used_footnotes[id], id)
-                     for id in self.footnotes.keys()]
-        footnotes.sort()
-
-        for i, id in footnotes :
-            li = etree.SubElement(ol, "li")
-            li.set("id", self.makeFootnoteId(i))
-
-            self.md._processSection(li, self.footnotes[id].split("\n"), looseList=1)
-
-            backlink = etree.Element("a")
-            backlink.set("href", "#" + self.makeFootnoteRefId(i))
-            backlink.set("class", "footnoteBackLink")
-            backlink.set("title",
-                                  "Jump back to footnote %d in the text" % i)
-            backlink.text = FN_BACKLINK_TEXT
-
-            if li.getchildren():
-                node = li[-1]
-                if node.text:
-		            li.append(backlink)
-                elif node.tag == "p":
-                    node.append(backlink)
-                else:
-                    p = etree.SubElement(li, "p")
-                    p.append(backlink)
-        div = self.md.applyInlinePatterns(etree.ElementTree(div)).getroot()
-        return div
-
-
-class FootnotePreprocessor :
-
-    def __init__ (self, footnotes) :
-        self.footnotes = footnotes
-
-    def run(self, lines) :
-
-        self.blockGuru = markdown.BlockGuru()
-        lines = self._handleFootnoteDefinitions (lines)
-
-        # Make a hash of all footnote marks in the text so that we
-        # know in what order they are supposed to appear.  (This
-        # function call doesn't really substitute anything - it's just
-        # a way to get a callback for each occurence.
-
-        text = "\n".join(lines)
-        self.footnotes.SHORT_USE_RE.sub(self.recordFootnoteUse, text)
-
-        return text.split("\n")
-
-
-    def recordFootnoteUse(self, match) :
-
-        id = match.group(1)
-        id = id.strip()
-        nextNum = len(self.footnotes.used_footnotes.keys()) + 1
-        self.footnotes.used_footnotes[id] = nextNum
-
-
-    def _handleFootnoteDefinitions(self, lines) :
-        """Recursively finds all footnote definitions in the lines.
-
-            @param lines: a list of lines of text
-            @returns: a string representing the text with footnote
-                      definitions removed """
-
-        i, id, footnote = self._findFootnoteDefinition(lines)
-
-        if id :
-
-            plain = lines[:i]
-
-            detabbed, theRest = self.blockGuru.detectTabbed(lines[i+1:])
-   
-            self.footnotes.setFootnote(id,
-                                       footnote + "\n"
-                                       + "\n".join(detabbed))
-
-            more_plain = self._handleFootnoteDefinitions(theRest)
-            return plain + [""] + more_plain
-
-        else :
-            return lines
-
-    def _findFootnoteDefinition(self, lines) :
-        """Finds the first line of a footnote definition.
-
-            @param lines: a list of lines of text
-            @returns: the index of the line containing a footnote definition """
-
-        counter = 0
-        for line in lines :
-            m = self.footnotes.DEF_RE.match(line)
-            if m :
-                return counter, m.group(2), m.group(3)
-            counter += 1
-        return counter, None, None
-
-
-class FootnotePattern (markdown.Pattern) :
-
-    def __init__ (self, pattern, footnotes) :
-
-        markdown.Pattern.__init__(self, pattern)
-        self.footnotes = footnotes
-
-    def handleMatch(self, m) :
-        sup = etree.Element("sup")
-        a = etree.SubElement(sup, "a")
-        id = m.group(2)
-        num = self.footnotes.used_footnotes[id]
-        sup.set('id', self.footnotes.makeFootnoteRefId(num))
-        a.set('href', '#' + self.footnotes.makeFootnoteId(num))
-        a.text = str(num)
-        return sup
-
-class FootnotePostprocessor (markdown.Postprocessor):
-
-    def __init__ (self, footnotes) :
-        self.footnotes = footnotes
-
-    def run(self, root):
-        footnotesDiv = self.footnotes.makeFootnotesDiv(root)
-        if footnotesDiv:
-            result = self.extension.findFootnotesPlaceholder(root)
-
-            if result:
-                node, isText = result
-                if isText:
-                    node.text = None
-                    node.getchildren().insert(0, footnotesDiv)
-                else:
-                    child, element = node
-                    ind = element.getchildren().find(child)
-                    element.getchildren().insert(ind + 1, footnotesDiv)
-                    child.tail = None
-                    
-                fnPlaceholder.parent.replaceChild(fnPlaceholder, footnotesDiv)
-            else :
-                root.append(footnotesDiv)
-
-class FootnoteTextPostprocessor (markdown.Postprocessor):
-
-    def __init__ (self, footnotes) :
-        self.footnotes = footnotes
-
-    def run(self, text) :
-        return text.replace(FN_BACKLINK_TEXT, "&#8617;")
-
-def makeExtension(configs=[]):
-    return FootnoteExtension(configs=configs)
-

mdx_headerid.py

-#!/usr/bin/python
-
-"""
-HeaderID Extension for Python-Markdown
-======================================
-
-Adds ability to set HTML IDs for headers.
-
-Basic usage:
-
-    >>> import markdown
-    >>> text = "# Some Header # {#some_id}"
-    >>> md = markdown.markdown(text, ['headerid'])
-    >>> md
-    u'<h1 id="some_id">Some Header</h1>'
-
-All header IDs are unique:
-
-    >>> text = '''
-    ... #Header
-    ... #Another Header {#header}
-    ... #Third Header {#header}'''
-    >>> md = markdown.markdown(text, ['headerid'])
-    >>> md
-    u'<h1 id="header">Header</h1>\\n\\n<h1 id="header_1">Another Header</h1>\\n\\n<h1 id="header_2">Third Header</h1>'
-
-To fit within a html template's hierarchy, set the header base level:
-
-    >>> text = '''
-    ... #Some Header
-    ... ## Next Level'''
-    >>> md = markdown.markdown(text, ['headerid(level=3)'])
-    >>> md
-    u'<h3 id="some_header">Some Header</h3>\\n\\n<h4 id="next_level">Next Level</h4>'
-
-Turn off auto generated IDs:
-
-    >>> text = '''
-    ... # Some Header
-    ... # Header with ID # { #foo }'''
-    >>> md = markdown.markdown(text, ['headerid(forceid=False)'])
-    >>> md
-    u'<h1>Some Header</h1>\\n\\n<h1 id="foo">Header with ID</h1>'
-
-Use with MetaData extension:
-
-    >>> text = '''header_level: 2
-    ... header_forceid: Off
-    ...
-    ... # A Header'''
-    >>> md = markdown.markdown(text, ['headerid', 'meta'])
-    >>> md
-    u'<h2>A Header</h2>'
-
-By [Waylan Limberg](http://achinghead.com/).
-
-Project website: http://achinghead.com/markdown-headerid/
-Contact: waylan [at] gmail [dot] com
-
-License: [BSD](http://www.opensource.org/licenses/bsd-license.php) 
-
-Version: 0.1 (May 2, 2008)
-
-Dependencies:
-* [Python 2.3+](http://python.org)
-* [Markdown 1.7+](http://www.freewisdom.org/projects/python-markdown/)
-
-"""
-
-import markdown
-from markdown import etree
-import re
-from string import ascii_lowercase, digits, punctuation
-
-ID_CHARS = ascii_lowercase + digits + '-_'
-
-HEADER_RE = re.compile(r'''^(\#{1,6})		# group(1) = string of hashes
-                           ( [^{^#]*)		# group(2) = Header text
-                           [\#]*		    # optional closing hashes (not counted)
-                           (?:[ \t]*\{[ \t]*\#([-_:a-zA-Z0-9]+)[ \t]*\})?	# group(3) = id attr''',
-                           re.VERBOSE)
-
-IDCOUNT_RE = re.compile(r'^(.*)_([0-9]+)$')
-
-class HeaderIdExtension (markdown.Extension) :
-    def __init__(self, configs):
-        # set defaults
-        self.config = {
-                'level' : ['1', 'Base level for headers.'],
-                'forceid' : ['True', 'Force all headers to have an id.']
-            }
-
-        for key, value in configs:
-            self.setConfig(key, value)
-
-
-    def extendMarkdown(self, md, md_globals) :
-
-        md.IDs = []
-
-        def _processHeaderId(parent_elem, paragraph) :
-            ''' 
-            Overrides _processHeader of Markdown() and 
-            adds an 'id' to the header. 
-            '''
-            m = HEADER_RE.match(paragraph[0])
-            if m :
-                start_level, force_id = _get_meta()
-                level = len(m.group(1)) + start_level
-                if level > 6: 
-                    level = 6
-                h = etree.Element("h%d" % level)
-                parent_elem.append(h)