Tizen 2.1 base
authorJinkun Jang <jinkun.jang@samsung.com>
Tue, 12 Mar 2013 17:21:26 +0000 (02:21 +0900)
committerHyunjee Kim <hj0426.kim@samsung.com>
Tue, 31 Mar 2020 04:06:24 +0000 (13:06 +0900)
Signed-off-by: Hyunjee Kim <hj0426.kim@samsung.com>
57 files changed:
MANIFEST [new file with mode: 0644]
PKG-INFO [new file with mode: 0644]
bin/markdown [new file with mode: 0755]
docs/AUTHORS [new file with mode: 0644]
docs/CHANGE_LOG [new file with mode: 0644]
docs/INSTALL [new file with mode: 0644]
docs/LICENSE [new file with mode: 0644]
docs/README [new file with mode: 0644]
docs/README.html [new file with mode: 0644]
docs/command_line.txt [new file with mode: 0644]
docs/extensions/Abbreviations.txt [new file with mode: 0644]
docs/extensions/CodeHilite.txt [new file with mode: 0644]
docs/extensions/Definition_Lists.txt [new file with mode: 0644]
docs/extensions/Fenced_Code_Blocks.txt [new file with mode: 0644]
docs/extensions/HTML_Tidy.txt [new file with mode: 0644]
docs/extensions/HeaderId.txt [new file with mode: 0644]
docs/extensions/ImageLinks.txt [new file with mode: 0644]
docs/extensions/Meta-Data.txt [new file with mode: 0644]
docs/extensions/RSS.txt [new file with mode: 0644]
docs/extensions/Tables.txt [new file with mode: 0644]
docs/extensions/Tables_of_Contents.txt [new file with mode: 0644]
docs/extensions/WikiLinks.txt [new file with mode: 0644]
docs/extensions/extra.txt [new file with mode: 0644]
docs/extensions/footnotes.txt [new file with mode: 0644]
docs/extensions/index.txt [new file with mode: 0644]
docs/release-2.0.1.txt [new file with mode: 0644]
docs/release-2.0.2.txt [new file with mode: 0644]
docs/release-2.0.txt [new file with mode: 0644]
docs/using_as_module.txt [new file with mode: 0644]
docs/writing_extensions.txt [new file with mode: 0644]
markdown/__init__.py [new file with mode: 0644]
markdown/blockparser.py [new file with mode: 0644]
markdown/blockprocessors.py [new file with mode: 0644]
markdown/commandline.py [new file with mode: 0644]
markdown/etree_loader.py [new file with mode: 0644]
markdown/extensions/__init__.py [new file with mode: 0644]
markdown/extensions/abbr.py [new file with mode: 0644]
markdown/extensions/codehilite.py [new file with mode: 0644]
markdown/extensions/def_list.py [new file with mode: 0644]
markdown/extensions/extra.py [new file with mode: 0644]
markdown/extensions/fenced_code.py [new file with mode: 0644]
markdown/extensions/footnotes.py [new file with mode: 0644]
markdown/extensions/headerid.py [new file with mode: 0644]
markdown/extensions/html_tidy.py [new file with mode: 0644]
markdown/extensions/imagelinks.py [new file with mode: 0644]
markdown/extensions/meta.py [new file with mode: 0644]
markdown/extensions/rss.py [new file with mode: 0644]
markdown/extensions/tables.py [new file with mode: 0644]
markdown/extensions/toc.py [new file with mode: 0644]
markdown/extensions/wikilinks.py [new file with mode: 0644]
markdown/html4.py [new file with mode: 0644]
markdown/inlinepatterns.py [new file with mode: 0644]
markdown/odict.py [new file with mode: 0644]
markdown/postprocessors.py [new file with mode: 0644]
markdown/preprocessors.py [new file with mode: 0644]
markdown/treeprocessors.py [new file with mode: 0644]
setup.py [new file with mode: 0755]

diff --git a/MANIFEST b/MANIFEST
new file mode 100644 (file)
index 0000000..b30a00c
--- /dev/null
+++ b/MANIFEST
@@ -0,0 +1,56 @@
+MANIFEST
+setup.py
+bin/markdown
+docs/AUTHORS
+docs/CHANGE_LOG
+docs/INSTALL
+docs/LICENSE
+docs/README
+docs/README.html
+docs/command_line.txt
+docs/release-2.0.1.txt
+docs/release-2.0.2.txt
+docs/release-2.0.txt
+docs/using_as_module.txt
+docs/writing_extensions.txt
+docs/extensions/Abbreviations.txt
+docs/extensions/CodeHilite.txt
+docs/extensions/Definition_Lists.txt
+docs/extensions/Fenced_Code_Blocks.txt
+docs/extensions/HTML_Tidy.txt
+docs/extensions/HeaderId.txt
+docs/extensions/ImageLinks.txt
+docs/extensions/Meta-Data.txt
+docs/extensions/RSS.txt
+docs/extensions/Tables.txt
+docs/extensions/Tables_of_Contents.txt
+docs/extensions/WikiLinks.txt
+docs/extensions/extra.txt
+docs/extensions/footnotes.txt
+docs/extensions/index.txt
+markdown/__init__.py
+markdown/blockparser.py
+markdown/blockprocessors.py
+markdown/commandline.py
+markdown/etree_loader.py
+markdown/html4.py
+markdown/inlinepatterns.py
+markdown/odict.py
+markdown/postprocessors.py
+markdown/preprocessors.py
+markdown/treeprocessors.py
+markdown/extensions/__init__.py
+markdown/extensions/abbr.py
+markdown/extensions/codehilite.py
+markdown/extensions/def_list.py
+markdown/extensions/extra.py
+markdown/extensions/fenced_code.py
+markdown/extensions/footnotes.py
+markdown/extensions/headerid.py
+markdown/extensions/html_tidy.py
+markdown/extensions/imagelinks.py
+markdown/extensions/meta.py
+markdown/extensions/rss.py
+markdown/extensions/tables.py
+markdown/extensions/toc.py
+markdown/extensions/wikilinks.py
diff --git a/PKG-INFO b/PKG-INFO
new file mode 100644 (file)
index 0000000..0b8c489
--- /dev/null
+++ b/PKG-INFO
@@ -0,0 +1,29 @@
+Metadata-Version: 1.0
+Name: Markdown
+Version: 2.0.3
+Summary: Python implementation of Markdown.
+Home-page: http://www.freewisdom.org/projects/python-markdown
+Author: Waylan Limberg
+Author-email: waylan [at] gmail.com
+License: BSD License
+Download-URL: http://pypi.python.org/packages/source/M/Markdown/Markdown-2.0.3.tar.gz
+Description: UNKNOWN
+Platform: UNKNOWN
+Classifier: Development Status :: 5 - Production/Stable
+Classifier: License :: OSI Approved :: BSD License
+Classifier: Operating System :: OS Independent
+Classifier: Programming Language :: Python
+Classifier: Programming Language :: Python :: 2
+Classifier: Programming Language :: Python :: 2.3
+Classifier: Programming Language :: Python :: 2.4
+Classifier: Programming Language :: Python :: 2.5
+Classifier: Programming Language :: Python :: 2.6
+Classifier: Programming Language :: Python :: 3
+Classifier: Programming Language :: Python :: 3.0
+Classifier: Topic :: Communications :: Email :: Filters
+Classifier: Topic :: Internet :: WWW/HTTP :: Dynamic Content :: CGI Tools/Libraries
+Classifier: Topic :: Internet :: WWW/HTTP :: Site Management
+Classifier: Topic :: Software Development :: Documentation
+Classifier: Topic :: Software Development :: Libraries :: Python Modules
+Classifier: Topic :: Text Processing :: Filters
+Classifier: Topic :: Text Processing :: Markup :: HTML
diff --git a/bin/markdown b/bin/markdown
new file mode 100755 (executable)
index 0000000..8d04cc9
--- /dev/null
@@ -0,0 +1,42 @@
+#!/usr/bin/env python
+"""
+Python Markdown, the Command Line Script
+========================================
+
+This is the command line script for Python Markdown.
+
+Basic use from the command line:
+
+    markdown source.txt > destination.html
+
+Run "markdown --help" to see more options.
+
+See markdown/__init__.py for information on using Python Markdown as a module.
+
+## Authors and License
+
+Started by [Manfred Stienstra](http://www.dwerg.net/).  Continued and
+maintained  by [Yuri Takhteyev](http://www.freewisdom.org), [Waylan
+Limberg](http://achinghead.com/) and [Artem Yunusov](http://blog.splyer.com).
+
+Contact: markdown@freewisdom.org
+
+Copyright 2007, 2008 The Python Markdown Project (v. 1.7 and later)
+Copyright 200? Django Software Foundation (OrderedDict implementation)
+Copyright 2004, 2005, 2006 Yuri Takhteyev (v. 0.2-1.6b)
+Copyright 2004 Manfred Stienstra (the original version)
+
+License: BSD (see docs/LICENSE for details).
+"""
+
+import logging
+from markdown import COMMAND_LINE_LOGGING_LEVEL
+from markdown import commandline
+
+# Setup a logger manually for compatibility with Python 2.3
+logger = logging.getLogger('MARKDOWN')
+logger.setLevel(COMMAND_LINE_LOGGING_LEVEL)
+logger.addHandler(logging.StreamHandler())
+
+if __name__ == '__main__':
+    commandline.run()
diff --git a/docs/AUTHORS b/docs/AUTHORS
new file mode 100644 (file)
index 0000000..2843b56
--- /dev/null
@@ -0,0 +1,44 @@
+Primary Authors
+===============
+
+Yuri Takteyev <http://freewisdom.org/>, who has written much of the current code
+while procrastingating his Ph.D.
+
+Waylan Limberg <http://achinghead.com/>, who has written most of the available 
+extensions and later was asked to join Yuri, fixing nummrious bugs, adding
+documentation and making general improvements to the existing codebase,
+included a complete refactor of the core.
+
+Artem Yunusov, who as part of a 2008 GSoC project, has refactored inline 
+patterns, replaced the NanoDOM with ElementTree support and made various other 
+improvements.
+
+Manfed Stienstra <http://www.dwerg.net/>, who wrote the original version of 
+the script and is responsible for various parts of the existing codebase.
+
+David Wolever, who refactored the extension API and made other improvements
+as he helped to integrate Markdown into Dr.Project.
+
+Other Contributors
+==================
+
+The incomplete list of individuals below have provided patches or otherwise 
+contributed to the project in various ways. We would like to thank everyone 
+who has contributed to the progect in any way.
+
+Eric Abrahamsen
+Jeff Balogh
+Sergej Chodarev
+Chris Clark
+Tiago Cogumbreiro
+Kjell Magne Fauske
+G. Clark Haynes
+Daniel Krech
+Steward Midwinter
+Jack Miller
+Neale Pickett
+Paul Stansifer
+John Szakmeister
+Malcolm Tredinnick
+Ben Wilson
+and many others who helped by reporting bugs
diff --git a/docs/CHANGE_LOG b/docs/CHANGE_LOG
new file mode 100644 (file)
index 0000000..e005ff8
--- /dev/null
@@ -0,0 +1,180 @@
+PYTHON MARKDOWN CHANGELOG
+=========================
+
+Sept 28, 2009: Released version 2.0.2-Final.
+
+May 20, 2009: Released version 2.0.1-Final.
+
+Mar 30, 2009: Released version 2.0-Final.
+
+Mar 8, 2009: Release Candidate 2.0-rc-1.
+
+Feb 2009: Added support for multi-level lists to new Blockprocessors.
+
+Jan 2009: Added HTML 4 output as an option (thanks Eric Abrahamsen)
+
+Nov 2008: Added Definistion List ext. Replaced old core with BlockProcessors.
+Broken up into multiple files.
+
+Oct 2008: Changed logging behavior to work better with other systems. 
+Refactored tree tarversing. Added treap implementation, then replaced with 
+OrderedDEict. Renamed various processors to better reflect what they actually
+do. Refactored footnote ext to match php Extra's output.
+
+Sept 2008: Moved prettifyTree to a Postprocessor, replaced wikilink ext
+with wikilinks (note the s) ext (uses bracketed links instead of CamelCase)
+and various bug fixes.
+
+August 18 2008: Reorganized directory structure. Added a 'docs' dir
+and moved all extensions into a 'markdown-extensions' package.
+Added additional documentation and a few bug fixes. (v2.0-beta)
+
+August 4 2008: Updated included extensions to ElementTree. Added a 
+seperate commanline script. (v2.0-alpha)
+
+July 2008: Switched from home-grown NanoDOM to ElementTree and
+various related bugs (thanks Artem Yunusov).
+
+June 2008: Fixed issues with nested inline patterns and cleaned 
+up testing framework (thanks Artem Yunusov).
+
+May 2008: Added a number of additional extensions to the
+distribution and other minor changes. Moved repo to git from svn.
+
+Mar 2008: Refactored extension api to accept either an 
+extension name (as a string) or an instance of an extension
+(Thanks David Wolever). Fixed various bugs and added doc strings.
+
+Feb 2008: Various bugfixes mostly regarding extensions.
+
+Feb 18, 2008: Version 1.7.
+
+Feb 13, 2008: A little code cleanup and better documentation
+and inheritance for pre/post proccessors.
+
+Feb 9, 2008: Doublequotes no longer html escaped and rawhtml
+honors <?foo>, <@foo>, and <%foo> for those who run markdown on
+template syntax.
+
+Dec 12, 2007: Updated docs. Removed encoding arg from Markdown
+and markdown as per list discussion. Clean up in prep for 1.7.
+
+Nov 29, 2007: Added support for images inside links. Also fixed
+a few bugs in the footnote extension.
+
+Nov 19, 2007: `message` now uses python's logging module. Also removed 
+limit imposed by recursion in _process_section(). You can now parse as 
+long of a document as your memory can handle.
+
+Nov 5, 2007: Moved safe_mode code to a textPostprocessor and added 
+escaping option.
+
+Nov 3, 2007: Fixed convert method to accept empty strings.
+
+Oct 30, 2007: Fixed BOM removal (thanks Malcolm Tredinnick). Fixed 
+infinite loop in bracket regex for inline links.
+
+Oct 11, 2007: LineBreaks is now an inlinePattern. Fixed HR in 
+blockquotes. Refactored _processSection method (see tracker #1793419).
+
+Oct 9, 2007: Added textPreprocessor (from 1.6b).
+
+Oct 8, 2008: Fixed Lazy Blockquote. Fixed code block on first line. 
+Fixed empty inline image link.
+
+Oct 7, 2007: Limit recursion on inlinePatterns. Added a 'safe' tag 
+to htmlStash.
+
+March 18, 2007: Fixed or merged a bunch of minor bugs, including
+multi-line comments and markup inside links. (Tracker #s: 1683066,
+1671153, 1661751, 1627935, 1544371, 1458139.) -> v. 1.6b
+
+Oct 10, 2006: Fixed a bug that caused some text to be lost after
+comments.  Added "safe mode" (user's html tags are removed).
+
+Sept 6, 2006: Added exception for PHP tags when handling html blocks.
+
+August 7, 2006: Incorporated Sergej Chodarev's patch to fix a problem
+with ampersand normalization and html blocks.
+
+July 10, 2006: Switched to using optparse.  Added proper support for
+unicode.
+
+July 9, 2006: Fixed the <!--@address.com> problem (Tracker #1501354).  
+
+May 18, 2006: Stopped catching unquoted titles in reference links.
+Stopped creating blank headers.
+
+May 15, 2006: A bug with lists, recursion on block-level elements,
+run-in headers, spaces before headers, unicode input (thanks to Aaron
+Swartz). Sourceforge tracker #s: 1489313, 1489312, 1489311, 1488370,
+1485178, 1485176. (v. 1.5)
+
+Mar. 24, 2006: Switched to a not-so-recursive algorithm with
+_handleInline.  (Version 1.4)
+
+Mar. 15, 2006: Replaced some instance variables with class variables
+(a patch from Stelios Xanthakis).  Chris Clark's new regexps that do
+not trigger midword underlining.
+
+Feb. 28, 2006: Clean-up and command-line handling by Stewart
+Midwinter. (Version 1.3)
+
+Feb. 24, 2006: Fixed a bug with the last line of the list appearing
+again as a separate paragraph.  Incorporated Chris Clark's "mailto"
+patch.  Added support for <br /> at the end of lines ending in two or
+more spaces.  Fixed a crashing bug when using ImageReferencePattern.
+Added several utility methods to Nanodom.  (Version 1.2)
+
+Jan. 31, 2006: Added "hr" and "hr/" to BLOCK_LEVEL_ELEMENTS and
+changed <hr/> to <hr />.  (Thanks to Sergej Chodarev.)
+
+Nov. 26, 2005: Fixed a bug with certain tabbed lines inside lists
+getting wrapped in <pre><code>.  (v. 1.1)
+
+Nov. 19, 2005: Made "<!...", "<?...", etc. behave like block-level
+HTML tags.
+
+Nov. 14, 2005: Added entity code and email autolink fix by Tiago
+Cogumbreiro.  Fixed some small issues with backticks to get 100%
+compliance with John's test suite.  (v. 1.0)
+
+Nov. 7, 2005: Added an unlink method for documents to aid with memory
+collection (per Doug Sauder's suggestion).
+
+Oct. 29, 2005: Restricted a set of html tags that get treated as
+block-level elements.
+
+Sept. 18, 2005: Refactored the whole script to make it easier to
+customize it and made footnote functionality into an extension.
+(v. 0.9)
+
+Sept. 5, 2005: Fixed a bug with multi-paragraph footnotes.  Added
+attribute support.
+
+Sept. 1, 2005: Changed the way headers are handled to allow inline
+syntax in headers (e.g. links) and got the lists to use p-tags
+correctly (v. 0.8)
+
+Aug. 29, 2005: Added flexible tabs, fixed a few small issues, added
+basic support for footnotes.  Got rid of xml.dom.minidom and added
+pretty-printing. (v. 0.7)
+
+Aug. 13, 2005: Fixed a number of small bugs in order to conform to the
+test suite.  (v. 0.6)
+
+Aug. 11, 2005: Added support for inline html and entities, inline
+images, autolinks, underscore emphasis. Cleaned up and refactored the
+code, added some more comments.
+
+Feb. 19, 2005: Rewrote the handling of high-level elements to allow
+multi-line list items and all sorts of nesting.
+
+Feb. 3, 2005: Reference-style links, single-line lists, backticks,
+escape, emphasis in the beginning of the paragraph.
+
+Nov. 2004: Added links, blockquotes, html blocks to Manfred
+Stienstra's code
+
+Apr. 2004: Manfred's version at http://www.dwerg.net/projects/markdown/
+
diff --git a/docs/INSTALL b/docs/INSTALL
new file mode 100644 (file)
index 0000000..d8feade
--- /dev/null
@@ -0,0 +1,73 @@
+Installing Python-Markdown
+==========================
+
+Checking Dependencies
+---------------------
+
+Python-Markdown requires the ElementTree module to be installed. In Python2.5+ 
+ElementTree is included as part of the standard library. For earlier versions 
+of Python, open a Python shell and type the following:
+
+    >>> import cElementTree
+    >>> import ElementTree
+
+If at least one of those does not generate any errors, then you have a working
+copy of ElementTree installed on your system. As cElementTree is faster, you
+may want to install that if you don't already have it and it's available for 
+your system.
+
+See <http://effbot.org/zone/element-index.htm> for more information or to 
+download the latest version of ElementTree.
+
+The East Way
+------------
+
+The simplest way to install Python-Markdown is by using SetupTools. As and
+Admin/Root user on your system do:
+
+    easy_install ElementTree
+    easy_install Markdown
+
+That's it, your done.
+
+Installing on Windows
+---------------------
+
+Download the Windows installer (.exe) from PyPI: 
+<http://pypi.python.org/pypi/Markdown>
+
+Double-click the file and follow the instructions.
+
+If you prefer to manually install Python-Markdown in Windows, download the
+Zip file, unzip it, and on the command line in the directory you unzipped to:
+
+    python setup.py install
+
+If you plan to use the provided command line script, you need to make sure your
+script directory is on your system path. On a typical Python install of Windows
+the Scripts directory is `C:\Python25\Scripts\`. Adjust according to your 
+system and add that to your system path.
+
+Installing on *nix Systems
+--------------------------
+
+From the command line do the following:
+
+    wget http://pypi.python.org/packages/source/M/Markdown/Markdown-2.0.tar.gz
+    tar xvzf Markdown-2.0.tar.gz
+    cd markdown-2.0/
+    sudo python setup.py install
+
+Using the Git Repository
+------------------------
+
+If your the type that like to live on the edge, you may want to keep up with 
+the latest additions and bug fixes in the repository between releases.
+Python-Markdown is maintained in a Git repository on Gitorious.org. To
+get a copy of Python-Markdown from the repository do the following from the
+command line:
+
+    git clone git://gitorious.org/python-markdown/mainline.git python-markdown
+    cd python-markdown
+    python setup.py install
+
diff --git a/docs/LICENSE b/docs/LICENSE
new file mode 100644 (file)
index 0000000..4cd8b14
--- /dev/null
@@ -0,0 +1,30 @@
+Copyright 2007, 2008 The Python Markdown Project (v. 1.7 and later)  
+Copyright 2004, 2005, 2006 Yuri Takhteyev (v. 0.2-1.6b)  
+Copyright 2004 Manfred Stienstra (the original version)  
+
+All rights reserved.
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions are met:
+    
+*   Redistributions of source code must retain the above copyright
+    notice, this list of conditions and the following disclaimer.
+*   Redistributions in binary form must reproduce the above copyright
+    notice, this list of conditions and the following disclaimer in the
+    documentation and/or other materials provided with the distribution.
+*   Neither the name of the <organization> nor the
+    names of its contributors may be used to endorse or promote products
+    derived from this software without specific prior written permission.
+
+THIS SOFTWARE IS PROVIDED BY THE PYTHON MARKDOWN PROJECT ''AS IS'' AND ANY
+EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+DISCLAIMED. IN NO EVENT SHALL ANY CONTRIBUTORS TO THE PYTHON MARKDOWN PROJECT
+BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+POSSIBILITY OF SUCH DAMAGE.
+
diff --git a/docs/README b/docs/README
new file mode 100644 (file)
index 0000000..d19a1ea
--- /dev/null
@@ -0,0 +1,30 @@
+[Python-Markdown][]
+===================
+
+This is a Python implementation of John Gruber's [Markdown][]. 
+It is almost completely compliant with the reference implementation,
+though there are a few known issues. See [Features][] for information 
+on what exactly is supported and what is not. Additional features are 
+supported by the [Available Extensions][].
+
+[Python-Markdown]: http://freewisdom.org/projects/python-markdown
+[Markdown]: http://daringfireball.net/projects/markdown/
+[Features]: http://www.freewisdom.org/projects/python-markdown/Features
+[Available Extensions]: http://www.freewisdom.org/projects/python-markdown/Available_Extensions
+
+
+Documentation
+-------------
+
+Installation and usage documentation is available in the `docs/` directory
+of the distribution and on the project website at 
+<http://freewisdom.org/projects/python-markdown>.
+
+Support
+-------
+
+You may ask for help and discuss various other issues on the [mailing list][] and report bugs on the [bug tracker][].
+
+[mailing list]: http://lists.sourceforge.net/lists/listinfo/python-markdown-discuss
+[bug tracker]: http://www.freewisdom.org/projects/python-markdown/Tickets 
+
diff --git a/docs/README.html b/docs/README.html
new file mode 100644 (file)
index 0000000..49e3b07
--- /dev/null
@@ -0,0 +1,12 @@
+<h1><a href="http://freewisdom.org/projects/python-markdown">Python-Markdown</a></h1>
+<p>This is a Python implementation of John Gruber's <a href="http://daringfireball.net/projects/markdown/">Markdown</a>. 
+It is almost completely compliant with the reference implementation,
+though there are a few known issues. See <a href="http://www.freewisdom.org/projects/python-markdown/Features">Features</a> for information 
+on what exactly is supported and what is not. Additional features are 
+supported by the <a href="http://www.freewisdom.org/projects/python-markdown/Available_Extensions">Available Extensions</a>.</p>
+<h2>Documentation</h2>
+<p>Installation and usage documentation is available in the <code>docs/</code> directory
+of the distribution and on the project website at 
+<a href="http://freewisdom.org/projects/python-markdown">http://freewisdom.org/projects/python-markdown</a>.</p>
+<h2>Support</h2>
+<p>You may ask for help and discuss various other issues on the <a href="http://lists.sourceforge.net/lists/listinfo/python-markdown-discuss">mailing list</a> and report bugs on the <a href="http://www.freewisdom.org/projects/python-markdown/Tickets">bug tracker</a>.</p>
\ No newline at end of file
diff --git a/docs/command_line.txt b/docs/command_line.txt
new file mode 100644 (file)
index 0000000..d0134ea
--- /dev/null
@@ -0,0 +1,98 @@
+Using Python-Markdown on the Command Line
+=========================================
+
+While Python-Markdown is primarily a python library, a command line script is 
+included as well. While there are many other command line implementations 
+of Markdown, you may not have them installed, or you may prefer to use 
+Python-Markdown's various extensions.
+
+Setup
+-----
+
+Generally, you may simply call the ``markdown`` file from the command 
+line. However, if you have fully installed Markdown (``setup.py install`` or 
+``easy_install``), then the ``markdown`` script will have been copied to
+you Python "Scripts" directory. Different systems require different methods to
+ensure that any files in the Python "Scripts" directory are on your system 
+path.
+
+* **Windows**:
+
+    Assuming a default install on Windows, your "Scripts" directory is most
+    likely something like ``C:\\Python25\Scripts``. Verify the location of
+    your "Scripts" directory and add it to you system path.
+
+    Calling ``markdown`` from th ecommand line will call the wrapper batch file
+    ``markdown.bat`` in the "Scripts" directory created during install.
+
+* **Linux**:
+
+    As each Linux distribution is different and we can't possibly document all 
+    of them here, we'll provide a few helpful pointers:
+
+    * Some systems will automatically install the script on your path. Try it 
+      and see if it works. Just run ``markdown`` from the command line.
+
+    * Other systems may maintain a separate "Scripts" directory which you
+      need to add to your path. Find it (check with your distribution) and
+      either add it to your path or make a symbolic link to it from your path.
+
+    * If you are sure ``markdown`` is on your path, but it still isn't being
+      found, check the permissions of the file and make sure it is executable.
+
+    As an alternative, you could just ``cd`` into the directory which contains
+    the source distribution, and run it from there. However, remember that your
+    markdown text files will not likely be in that directory, so it is much more 
+    convenient to have ``markdown`` on your path.
+
+The Basics
+----------
+
+To use ``markdown`` from the command line, run it as 
+
+    $ markdown input_file.txt
+
+or 
+
+    $ markdown input_file.txt > output_file.html
+
+More Options
+------------
+
+If you are using Python 2.3 or higher, you can also use advanced
+command line options to specify encoding or to run extensions.
+
+    $ markdown --help
+    Usage: markdown INPUTFILE [options]
+
+    Options:
+      -h, --help            show this help message and exit
+      -f OUTPUT_FILE, --file=OUTPUT_FILE
+                            write output to OUTPUT_FILE
+      -e ENCODING, --encoding=ENCODING
+                            encoding for input and output files
+      -q, --quiet           suppress all messages
+      -v, --verbose         print info messages
+      -s SAFE_MODE, --safe=SAFE_MODE
+                            safe mode ('replace', 'remove' or 'escape'  user's
+                            HTML tag)
+      -o OUTPUT_FORMAT, --output_format=OUTPUT_FORMAT
+                            Format of output. One of 'xhtml1' (default) or
+                            'html4'.
+      --noisy               print debug messages
+      -x EXTENSION, --extension=EXTENSION
+                            load extension EXTENSION
+
+Using Extensions
+----------------
+
+For an extension to be ran this way it must be provided in a module
+which should be in your python path (see [[writing_extensions]] for details). 
+It can then be invoked by the name of that module:
+
+    $ markdown -x footnotes text_with_footnotes.txt > output.html
+
+If the extension supports config options, you can pass them in as well:
+
+    $ markdown -x "footnotes(PLACE_MARKER=~~~~~~~~)" input.txt
+
diff --git a/docs/extensions/Abbreviations.txt b/docs/extensions/Abbreviations.txt
new file mode 100644 (file)
index 0000000..fa54d3c
--- /dev/null
@@ -0,0 +1,53 @@
+Abbreviations
+-------------
+
+Summary
+-------
+
+The Markdown Abbreviation Extension adds the ability to define abbreviations. 
+Specifically, any defined abbreviation is wrapped in  an `<abbr>` tag.
+
+The Abbreviation extension is included in the standard Markdown library.
+
+Syntax
+------
+
+Abbreviations are defined using the syntax established in 
+[PHP Markdown Extra][php].
+
+[php]: http://www.michelf.com/projects/php-markdown/extra/#abbr
+
+Thus, the following text (taken from the above referenced PHP documentation):
+
+    The HTML specification 
+    is maintained by the W3C.
+        
+    *[HTML]: Hyper Text Markup Language
+    *[W3C]:  World Wide Web Consortium
+
+will be rendered like so:
+
+    <p>The <abbr title="Hyper Text Markup Language">HTML</abbr> specification 
+    is maintained by the <abbr title="World Wide Web Consortium">W3C</abbr>.</p>
+
+Usage
+-----
+
+From the Python interpreter:
+
+    >>> import markdown
+    >>> text = """
+    ... Some text with an ABBR.
+    ...
+    ... *[ABBR]: Abbreviation
+    ... """
+    >>> html = markdown.markdown(text, ['abbr'])
+
+To use with other extensions, just add them to the list, like this:
+
+    >>> html = markdown.markdown(text, ['abbr', 'footnotes'])
+
+Abbreviations can also be called from the command line using Markdown's `-x` 
+parameter, like so:
+
+    markdown.py -x abbr source.txt > output.html
diff --git a/docs/extensions/CodeHilite.txt b/docs/extensions/CodeHilite.txt
new file mode 100644 (file)
index 0000000..482ad60
--- /dev/null
@@ -0,0 +1,113 @@
+CodeHilite
+==========
+
+Summary
+-------
+
+The CodeHilite Extension adds code/syntax highlighting to standard 
+Python-Markdown code blocks using [Pygments][].
+
+[Python-Markdown]: http://www.freewisdom.org/projects/python-markdown/
+[Pygments]: http://pygments.org/
+
+This extension is included in the  Markdown library.
+
+Setup
+-----
+
+You will also need to [download][dl] and install the Pygments package on your 
+`PYTHONPATH`. You will need to determine the appropriate CSS classes and create
+appropriate rules for them, which are either defined in or linked from the 
+header of your HTML templates. See the excellent [documentation][] for more 
+details. If no language is defined, Pygments will attempt to guess the 
+language. When that fails, the code block will display as un-highlighted code.
+
+[dl]: http://pygments.org/download/
+[documentation]: http://pygments.org/docs
+
+**Note:** The css and/or javascript is not included as part of this extension 
+but shall always be provided by the end user.
+
+Syntax
+------
+
+The CodeHilite Extension follows the same [syntax][] as regular Markdown code 
+blocks, with one exception. The hiliter needs to know what language to use for 
+the code block. There are three ways to tell the hiliter what language the code 
+block contains and each one has a different result.
+
+[syntax]: http://daringfireball.net/projects/markdown/syntax#precode
+
+###SheBang (with path)
+
+If the first line of the codeblock contains a shebang, the language is derived 
+from that and line numbers are used.
+
+        #!/usr/bin/python
+        # Code goes here ...
+
+Will result in:
+
+    #!/usr/bin/python
+    # Code goes here ...
+
+
+###SheBang (no path)
+
+If the first line contains a shebang, but the shebang line does not contain a 
+path (a single `/` or even a space), then that line is removed from the code 
+block before processing. Line numbers are used.
+
+        #!python
+        # Code goes here ...
+
+Will result in:
+
+    # Code goes here ...
+
+####Colons
+
+If the first line begins with three or more colons, the text following the 
+colons identifies the language. The first line is removed from the code block 
+before processing and line numbers are not used.
+
+        :::python
+        # Code goes here ...
+
+Will result in:
+
+    # Code goes here ...
+
+###When No Language is Defined
+
+CodeHilite is completely backward compatible so that if a code block is 
+encountered that does not define a language, the block is simple wrapped in 
+`<pre>` tags and output. Note: one exception would be that the Pygments 
+highlighting engine will try to guess the language. Upon failure, the same 
+behavior will happen as described here.
+
+        # Code goes here ...
+
+Will result in:
+
+    # Code goes here ...
+
+Lets see the source for that:
+
+    <div class="codehilite" ><pre><code># Code goes here ...
+    </code></pre></div>
+
+Usage
+-----
+
+From the Python interpreter:
+
+    >>> html = markdown.markdown(text, ['codehilite'])
+
+If you want every code block to have line numbers, even when using colons 
+(`:::`) for language identification, the setting `force_linenos` is available 
+to do so.
+
+    >>> html = markdown.markdown(text, 
+    ...     ['codehilite(force_linenos=True)']
+    ... )
diff --git a/docs/extensions/Definition_Lists.txt b/docs/extensions/Definition_Lists.txt
new file mode 100644 (file)
index 0000000..983070d
--- /dev/null
@@ -0,0 +1,55 @@
+Definition Lists
+----------------
+
+Summary
+-------
+
+The Definition List Extension adds the ability to create definition list in
+Markdown documents.
+
+This extension is included in the standard Markdown library.
+
+Syntax
+------
+
+Definition lists are defined using the syntax established in 
+[PHP Markdown Extra][php].
+
+[php]: http://www.michelf.com/projects/php-markdown/extra/#def-list
+
+Thus, the following text (taken from the above referenced PHP documentation):
+
+    Apple
+    :   Pomaceous fruit of plants of the genus Malus in 
+        the family Rosaceae.
+
+    Orange
+    :   The fruit of an evergreen tree of the genus Citrus.
+
+will be rendered like so:
+
+    <dl>
+    <dt>Apple</dt>
+    <dd>Pomaceous fruit of plants of the genus Malus in 
+    the family Rosaceae.</dd>
+
+    <dt>Orange</dt>
+    <dd>The fruit of an evergreen tree of the genus Citrus.</dd>
+    </dl>
+
+
+Usage
+-----
+
+From the Python interpreter:
+
+    >>> html = markdown.markdown(text, ['def_list'])
+
+To use with other extensions, just add them to the list, like this:
+
+    >>> html = markdown.markdown(text, ['def_list', 'footnotes'])
+
+The extension can also be called from the command line using Markdown's `-x` 
+parameter:
+
+    markdown.py -x def_list source.txt > output.html
diff --git a/docs/extensions/Fenced_Code_Blocks.txt b/docs/extensions/Fenced_Code_Blocks.txt
new file mode 100644 (file)
index 0000000..6b1ba76
--- /dev/null
@@ -0,0 +1,63 @@
+Fenced Code Blocks
+==================
+
+Summary
+-------
+
+This extension adds a secondary way to define code blocks which overcomes a few
+limitations of the indented code blocks.
+
+This extension is included in the standard Markdown library.
+
+Syntax
+------
+
+Fenced Code Blocks are defined using the syntax established in 
+[PHP Markdown Extra][php].
+
+[php]: http://www.michelf.com/projects/php-markdown/extra/#fenced-code-blocks
+
+Thus, the following text (taken from the above referenced PHP documentation):
+
+    This is a paragraph introducing:
+
+    ~~~~~~~~~~~~~~~~~~~~
+    a one-line code block
+    ~~~~~~~~~~~~~~~~~~~~
+
+Fenced code blocks can have a blank line as the first  and/or last line of a 
+code block and they can also come immediately after a list item without becoming
+part of the list.
+
+In addition to PHP Extra's syntax, you can define the language of the code 
+block for use by syntax highlighters etc. The language will be assigned as a 
+class attribute of the ``<code>`` element in the output. Therefore, you should 
+define the language as you would a css class - ``.language``. For consistency 
+with other markdown syntax, the language can *optionally* be wrapped in curly 
+brackets:
+
+    ~~~~{.python}
+    # python code
+    ~~~~
+
+    ~~~~.html
+    <p>HTML Document</p>
+    ~~~~
+
+The above will output:
+
+    <pre><code class="python"># python code
+    </code></pre>
+    
+    <pre><code class="html">&lt;p&gt;HTML Document&lt;/p&gt;
+    </code></pre>
+
+Usage
+-----
+
+From the Python interpreter:
+
+    >>> html = markdown.markdown(text, ['fenced_code'])
+
+
+
diff --git a/docs/extensions/HTML_Tidy.txt b/docs/extensions/HTML_Tidy.txt
new file mode 100644 (file)
index 0000000..52f991f
--- /dev/null
@@ -0,0 +1,27 @@
+HTML Tidy
+=========
+
+Runs [HTML Tidy][] on the output of Python-Markdown using the [uTidylib][]
+Python wrapper. Both libtidy and uTidylib must be installed on your system.
+
+This extension is available in the standard Markdown library since version 2.0.
+
+[HTML Tidy]: http://tidy.sourceforge.net/
+[uTidylib]: http://utidylib.berlios.de/
+
+Note than any Tidy [options][] can be passed in as extension configs. So, 
+for example, to output HTML rather than XHTML, set ``output_xhtml=0``. To
+indent the output, set ``indent=auto`` and to have Tidy wrap the output in 
+``<html>`` and ``<body>`` tags, set ``show_body_only=0``. See Tidy's 
+[options][] for a full list of the available options. The defaults are set to 
+most closely match Markdowns defaults with the exception that you get much
+better pretty-printing.
+
+[options]: http://tidy.sourceforge.net/docs/quickref.html
+
+Note that options set in this extension will override most any other settings
+passed on to Markdown (such as "output_format"). Unlike Markdown, this extension
+will also treat raw HTML no different than that output by Markdown. In other 
+words, it may munge a document authors carefully crafted HTML. Of course, it
+may also transform poorly formed raw HTML into nice, valid HTML. Take these
+things into consideration when electing to use this extension.
diff --git a/docs/extensions/HeaderId.txt b/docs/extensions/HeaderId.txt
new file mode 100644 (file)
index 0000000..efd1eb8
--- /dev/null
@@ -0,0 +1,104 @@
+HeaderId
+========
+
+Summary
+-------
+
+An extension to Python-Markdown that adds an 'id' attribute to HTML header 
+elements (h1-h6) in markdown's output.
+
+This extension is included in the standard Markdown library.
+
+Syntax
+------
+
+The basic syntax follows [PHP Markdown Extra][]'s implementation:
+
+[PHP Markdown Extra]: http://michelf.com/projects/php-markdown/extra/#header-id
+
+    Header 1            {#header1}
+    ========
+
+    ## Header 2 ##      {#header2}
+
+will result in the following HTML:
+
+    <h1 id="header1">Header 1</h1>
+
+    <h2 id="header2">Header 2</h2>
+
+However, there is much more that this extension does.
+
+By default, all headers will automatically have unique "id" attributes 
+generated based upon the text of the header (See below to turn this off). 
+Note this example in which all three headers would have the same "id":
+
+    #Header
+    #Another Header {#header}
+    #Header
+
+Results in:
+
+    <h1 id="header">Header</h1>
+    <h1 id="header_1">Another Header</h1>
+    <h1 id="header_2">Third Header</h1>
+
+Configuring the Output
+----------------------
+
+The HeaderId extension has two configuration settings:
+
+* **level**: Base level for headers.
+
+    Default: `1`
+
+* **forceid**: Force all headers to have an id.
+
+    Default: `True`
+
+The `level` setting allows you to automatically adjust the header levels to fit
+within the hierarchy of your html templates. For example, the markdown text for
+this page should not contain any headers higher than level 3 (`<h3>`). 
+Therefore, do the following:
+
+    >>>  text = '''
+    ... #Some Header
+    ... ## Next Level'''
+    >>> html = markdown.markdown(text, ['headerid(level=3)'])
+    >>> print html
+    <h3 id="some_header">Some Header</h3>
+    <h4 id="next_level">Next Level</h4>'
+
+The `forceid` setting turns on or off the automatically generated ids for 
+headers that do not have one explicitly defined.
+
+    >>> text = '''
+    ... # Some Header
+    ... # Header with ID # { #foo }'''
+    >>> html = markdown.markdown(text, ['headerid(forceid=False)'])
+    >>> print html
+    <h1>Some Header</h1>
+    <h1 id="foo">Header with ID</h1>
+
+Using with Meta-Data
+--------------------
+
+The HeaderId Extension also supports the [[Meta-Data]] Extension. Please see the documentation for that extension for specifics. The supported meta-data keywords are:
+
+* `header_level`
+* `header_forceid`
+
+When used, the meta-data will override the settings provided through the  
+`extension_configs` interface. 
+
+This document:
+
+    header_level: 2
+    header_forceid: Off
+
+    # A Header
+
+
+Will result in the following output:
+
+    <h2>A Header</h2>
diff --git a/docs/extensions/ImageLinks.txt b/docs/extensions/ImageLinks.txt
new file mode 100644 (file)
index 0000000..db4f99f
--- /dev/null
@@ -0,0 +1,27 @@
+ImageLinks
+==========
+
+Summary
+-------
+
+ImageLinks is a Python-Markdown extension that provides a mechanism for 
+defining mini-photo galleries within a markdown document.
+
+This extension is part of the Markdown library since 2.0.
+
+Syntax
+------
+
+Turns paragraphs like
+
+    <~~~~~~~~~~~~~~~~~~~~~~~~
+    dir/subdir
+    dir/subdir
+    dir/subdir
+    ~~~~~~~~~~~~~~
+    dir/subdir
+    dir/subdir
+    dir/subdir
+    ~~~~~~~~~~~~~~~~~~~>
+
+Into mini-photo galleries.
diff --git a/docs/extensions/Meta-Data.txt b/docs/extensions/Meta-Data.txt
new file mode 100644 (file)
index 0000000..982ea67
--- /dev/null
@@ -0,0 +1,88 @@
+Meta-Data
+=========
+
+Summary
+-------
+
+An extension to Python-Markdown that adds a syntax for defining meta-data about
+a document. The Meta-Data extension is inspired by and follows the syntax of 
+[MultiMarkdown][]. Currently, this extension does not use the meta-data in any 
+way, but simply provides it as a `Meta` attribute of a markdown instance for 
+use by other extensions or directly by your python code.
+
+[MultiMarkdown]: http://fletcherpenney.net/MultiMarkdown_Syntax_Guide#metadata
+
+This extension has been a part of the Markdown library since 2.0.
+
+Syntax
+------
+
+Meta-data consists of a series of keywords and values defined at the beginning 
+of a markdown document like this:
+
+    Title:   My Document
+    Summary: A brief description of my document.
+    Authors: Waylan Limberg
+             John Doe
+    Date:    October 2, 2007
+    blank-value: 
+    base_url: http://example.com
+
+    This is the first paragraph of the document.
+
+The keywords are case-insensitive and may consist of letters, numbers, 
+underscores and dashes and must end with a colon. The values consist of 
+anything following the colon on the line and may even be blank. If a line is 
+indented 4 or more spaces, that line is assumed to be an additional line of the
+value for the previous keyword. A keyword may have as many lines as desired. 
+The first blank line ends all meta-data for the document. Therefore, the first 
+line of a document must not be blank. All meta-data is stripped from the 
+document prior to any further processing by markdown.
+
+Accessing the Meta-Data
+-----------------------
+
+The meta-data is made available as a python Dict in the `Meta` attribute of an 
+instance of the Markdown class. For example, using the above document:
+
+    >>> md = markdown.Markdown(extensions = ['meta'])
+    >>> html = md.convert(text)
+    >>> # Meta-data has been stripped from output
+    >>> print html
+    <p>This is the first paragraph of the document.</p>
+
+    >>> # View meta-data
+    >>> print md.Meta
+    {
+    'title' : ['My Document'],
+    'summary' : ['A brief description of my document.'],
+    'authors' : ['Waylan Limberg', 'John Doe'],
+    'date' : ['October 2, 2007'],
+    'blank-value' : [''],
+    'base_url' : ['http://example.com']
+    }
+
+Note that the keys are all lowercase and the values consist of a list of 
+strings where each item is one line for that key. This way, one could preserve 
+line breaks if desired. Or the items could be joined where appropriate. No 
+assumptions are made regarding the data. It is simply passed as found to the 
+`Meta` attribute.
+
+Perhaps the meta-data could be passed into a template system, or used by 
+various markdown extensions. The possibilities are left to the imagination of 
+the developer.
+
+Compatible Extensions
+---------------------
+
+The following are extensions currently known to work with the Meta-Data 
+Extension and the keywords they are known to support:
+
+* [[HeaderId]]
+    * `header_level`
+    * `header_forceid`
+* [[WikiLinks]]
+    * `wiki_base_url`
+    * `wiki_end_url`
+    * `wiki_html_class`
+
diff --git a/docs/extensions/RSS.txt b/docs/extensions/RSS.txt
new file mode 100644 (file)
index 0000000..f2ecf0c
--- /dev/null
@@ -0,0 +1,35 @@
+RSS
+===
+
+Summary
+-------
+
+An extension to Python-Markdown that outputs a markdown document as RSS. This 
+extension has been included with Python-Markdown since 1.7 and should be 
+available to anyone who has a typical install of Python-Markdown.
+
+Usage
+-----
+
+From the Python interpreter:
+
+    >>> import markdown
+    >>> text = "Some markdown document."
+    >>> rss = markdown.markdown(text, ['rss'])
+
+Configuring the Output
+----------------------
+
+An RSS document includes some data about the document (URI, author, title) that
+will likely need to be configured for your needs. Therefore, three configuration
+options are available:
+
+* **URL** : The Main URL for the document.
+* **CREATOR** : The Feed creator's name.
+* **TITLE** : The title for the feed.
+
+An example:
+
+    >>> rss = markdown.markdown(text, extensions = \
+    ...        ['rss(URL=http://example.com,CREATOR=JOHN DOE,TITLE=My Document)']
+    ... )
diff --git a/docs/extensions/Tables.txt b/docs/extensions/Tables.txt
new file mode 100644 (file)
index 0000000..63d6849
--- /dev/null
@@ -0,0 +1,53 @@
+Tables
+----------------
+
+Summary
+-------
+
+The Table Extension adds the ability to create tables in Markdown documents.
+
+This extension is included in the standard Markdown library.
+
+Syntax
+------
+
+Tables are defined using the syntax established in [PHP Markdown Extra][php].
+
+[php]: http://www.michelf.com/projects/php-markdown/extra/#table
+
+Thus, the following text (taken from the above referenced PHP documentation):
+
+First Header  | Second Header
+------------- | -------------
+Content Cell  | Content Cell
+Content Cell  | Content Cell
+
+will be rendered as:
+
+<table>
+<thead>
+<tr>
+<th>First Header</th>
+<th>Second Header</th>
+</tr>
+</thead>
+<tbody>
+<tr>
+<td>Content Cell</td>
+<td>Content Cell</td>
+
+</tr>
+<tr>
+<td>Content Cell</td>
+<td>Content Cell</td>
+</tr>
+</tbody>
+</table>
+
+Usage
+-----
+
+From the Python interpreter:
+
+    >>> html = markdown.markdown(text, ['tables'])
+
diff --git a/docs/extensions/Tables_of_Contents.txt b/docs/extensions/Tables_of_Contents.txt
new file mode 100644 (file)
index 0000000..032c25c
--- /dev/null
@@ -0,0 +1,50 @@
+Table of Contents
+=================
+
+Summary
+-------
+
+Adds a Table of Contents to a Markdown document.
+
+This extension is included with the Markdown library since version 2.0.
+
+Syntax
+------
+
+Place a marker in the document where you would like the table of contents to
+appear. Then, a nested list of all the headers in the document will replace the
+marker. The marker defaults to ``[TOC]`` so the following document:
+
+    [TOC]
+
+    # Header 1
+
+    ## Header 2
+
+would generate the following output:
+
+    <div class="toc">
+      <ul>
+        <li><a href="#header-1">Header 1</a></li>
+          <ul>
+            <li><a href="#header-2">Header 2</a></li>
+          </ul>
+      </ul>
+    </div>
+    <h1 id="header-1">Header 1</h1>
+    <h1 id="header-2">Header 2</h1>
+
+Configuration Options
+---------------------
+
+The following options are provided to configure the output:
+
+* **marker**: Text to find and replace with the Table of Contents. Defaults
+  to ``[TOC]``.
+* **slugify**: Callable to generate anchors based on header text. Defaults to a
+  built in ``slugify`` method. The callable must accept one argument which 
+  contains the text content of the header and return a string which will be 
+  used as the anchor text.
+* **title**: Title to insert in TOC ``<div>``. Defaults to ``None``.
+* **anchorlink**: Set to ``True`` to have the headers link to themselves. 
+  Default is ``False``.
diff --git a/docs/extensions/WikiLinks.txt b/docs/extensions/WikiLinks.txt
new file mode 100644 (file)
index 0000000..8bbead5
--- /dev/null
@@ -0,0 +1,144 @@
+WikiLinks
+=========
+
+Summary
+-------
+
+An extension to Python-Markdown that adds [WikiLinks][]. Specifically, any 
+``[[bracketed]]`` word is converted to a link.
+
+[WikiLinks]: http://en.wikipedia.org/wiki/Wikilink
+
+This extension has been included in the Markdown library since 2.0.
+
+Syntax
+------
+
+A ``[[bracketed]]`` word is any combination of  upper or lower case letters,
+number, dashes, underscores and spaces surrounded by double brackets. Therefore 
+
+    [[Bracketed]]
+
+Would produce the following html:
+
+    <a href="/Bracketed/" class="wikilink">Bracketed</a>
+
+Note that wikilinks are automatically assigned `class="wikilink"` making it 
+easy to style wikilinks differently from other links on a page if one so 
+desires. See below for ways to alter the class.
+
+You should also note that when a space is used, the space is converted to an
+underscore in the link but left as-is in the label. Perhaps an example 
+would illustrate this best:
+
+    [[Wiki Link]]
+
+Becomes
+
+    <a href="/Wiki_Link/" class="wikilink">Wiki Link</a>
+
+Usage
+-----
+
+From the Python interpreter:
+
+    >>> text = "Some text with a [[WikiLink]]."
+    >>> html = markdown.markdown(text, ['wikilink'])
+
+The default behavior is to point each link to the document root of the current 
+domain and close with a trailing slash. Additionally, each link is assigned to 
+the html class `wikilink`. This may not always be desirable. Therefore, one can
+customize that behavior within Python code. Three settings are provided to 
+change the default behavior:
+
+1. **base_url**: String to append to beginning of URL. 
+
+    Default: `'/'`
+
+2. **end_url**: String to append to end of URL.
+
+    Default: `'/'`
+
+3. **html_class**: CSS hook. Leave blank for none.
+
+    Default: `'wikilink'`
+
+4. **build_url**: Callable which formats the URL from it's parts.
+
+For an example, let us suppose links should always point to the subdirectory 
+`/wiki/` and end with `.html`
+
+    >>> html = markdown.markdown(text, 
+    ...     ['wikilink(base_url=/wiki/,end_url=.html)']
+    ... )
+
+The above would result in the following link for `[[WikiLink]]`.
+
+    <a href="/wiki/WikiLink.html" class="wikilink">WikiLink</a>
+
+If you want to do more that just alter the base and/or end of the URL, you 
+could also pass in a callable which must accept three arguments (``label``, 
+``base``, and ``end``). The callable must return the URL in it's entirety.
+
+    def my_url_builder(label, base, end):
+        # do stuff
+        return url
+
+    md = markdown.Markdown(
+            extensions=['wikilinks],
+            extension_configs={'wikilinks' : [('build_url', my_url_builder)]}
+    )
+
+
+The option is also provided to change or remove the class attribute.
+
+    >>> html = markdown.markdown(text, 
+    ...     ['wikilink(base_url=myclass)']
+    ... )
+
+Would cause all wikilinks to be assigned to the class `myclass`.
+
+    <a href="/WikiLink/" class="myclass">WikiLink</a>
+
+The same options can be used on the command line as well:
+
+    python markdown.py -x wikilink(base_url=http://example.com/,end_url=.html,html_class=foo) src.txt
+
+Some may prefer the more complex format when calling the `Markdown` class directly:
+
+    >>> md = markdown.Markdown( 
+    ...     extensions = ['wikilink'], 
+    ...     extension_configs = {'wikilink': [
+    ...                                 ('base_url', 'http://example.com/'), 
+    ...                                 ('end_url', '.html'),
+    ...                                 ('html_class', '') ]},
+    ...     safe_mode = True
+    ... )
+    >>> html = md.convert(text)
+
+Using with Meta-Data
+--------------------
+
+The WikiLink Extension also supports the [[Meta-Data]] Extension. Please see 
+the documentation for that extension for specifics. The supported meta-data 
+keywords are:
+
+* `wiki_base_url`
+* `wiki_end_url`
+* `wiki_html_class`
+
+When used, the meta-data will override the settings provided through the  
+`extension_configs` interface. 
+
+This document:
+
+    wiki_base_url: http://example.com/
+    wiki_end_url:  .html
+    wiki_html_class: 
+
+    A [[WikiLink]] in the first paragraph.
+
+would result in the following output (notice the blank `wiki_html_class`):
+
+    <p>A <a href="http://example.com/WikiLink.html">WikiLink</a> in the first paragraph.</p>
+
diff --git a/docs/extensions/extra.txt b/docs/extensions/extra.txt
new file mode 100644 (file)
index 0000000..817d58f
--- /dev/null
@@ -0,0 +1,43 @@
+Python-Markdown Extra
+=====================
+
+Summary
+-------
+
+A compilation of various Python-Markdown extensions that (mostly) imitates
+[PHP Markdown Extra](http://michelf.com/projects/php-markdown/extra/).
+
+The supported extensions include:
+
+* [[Abbreviations]]
+* [[Definition_Lists]]
+* [[Fenced_Code_Blocks]]
+* [[Footnotes]]
+* [[HeaderId]]
+* [[Tables]]
+
+See each individual extension for syntax documentation. Extra and all it's 
+supported extensions are included in the standard Markdown library.
+
+Usage
+-----
+
+From the Python interpreter:
+
+    >>> import markdown
+    >>> html = markdown.markdown(text, ['extra'])
+
+In the unlikely 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 different name (see [[Writing Extensions]]). You could also 
+edit the `extensions` global variable defined in the source, but be 
+aware that such changes may be lost when you upgrade to any future 
+version of Python-Markdown.
diff --git a/docs/extensions/footnotes.txt b/docs/extensions/footnotes.txt
new file mode 100644 (file)
index 0000000..7188f44
--- /dev/null
@@ -0,0 +1,62 @@
+Footnotes
+=========
+
+Summary
+-------
+
+An extension to Python-Markdown that adds footnote syntax. This extension has 
+been included with Python-Markdown since 1.7 and should be available to anyone 
+who has a typical install of Python-Markdown.
+
+Syntax
+------
+
+Python-Markdown's Footnote syntax follows the generally accepted syntax of the 
+Markdown community at large and almost exactly matches [PHP Markdown Extra][]'s
+implementation of footnotes. The only differences involve a few subtleties in 
+the output.
+
+[PHP Markdown Extra]: http://michelf.com/projects/php-markdown/extra/#footnotes
+
+Example:
+
+    Footnotes[^1] have a label[^label] and a definition[^!DEF].
+
+    [^1]: This is a footnote
+    [^label]: A footnote on "label"
+    [^!DEF]: The definition of a footnote.
+
+A footnote definition may contain multiple lines, paragraphs, code blocks, 
+blockquotes and most any other markdown syntax. The additional line simply 
+must be indented at least an additional four spaces.
+
+    [^1]: The first paragraph of the definition.
+
+        Paragraph two of the definition.
+
+        > A blockquote with
+        > multiple lines.
+
+            a code block
+
+        A final paragraph.
+
+By default, the footnote definitions are placed at the end of the resulting 
+HTML document. However, you may want the footnotes in another location within 
+the document. Simply place the following text at that location within your 
+markdown document (See how to configure this text below):
+
+    ///Footnotes Go Here///
+
+Usage
+-----
+
+From the Python interpreter:
+
+    >>> html = markdown.markdown(text, ['footnotes'])
+
+To configure the place marker for footnote definitions (just be sure not to 
+use any existing markdown syntax):
+
+    >>> html = markdown.markdown(text, ['footnotes(PLACE_MARKER=+++my marker+++)'])
+
diff --git a/docs/extensions/index.txt b/docs/extensions/index.txt
new file mode 100644 (file)
index 0000000..71d857c
--- /dev/null
@@ -0,0 +1,44 @@
+Available Extensions
+====================
+
+Officially Supported Extensions
+-------------------------------
+
+These extensions are included with (at least) the most recent release and are 
+officially supported by the Python-Markdown developers. Any documentation is 
+maintained here and all bug reports should be made to the project. If you 
+have a typical install of Python-Markdown, these extensions are already 
+available to you.
+
+* [[Extra]]
+    * [[Abbreviations]]
+    * [[Definition_Lists]]
+    * [[Fenced_Code_Blocks]]
+    * [[Footnotes]]
+    * [[HeaderId]]
+    * [[Tables]]
+* [[CodeHilite]]
+* [[HTML_Tidy]]
+* [[ImageLinks]]
+* [[Meta-Data]]
+* [[RSS]]
+* [[Table_of_Contents]]
+* [[WikiLinks]]
+
+Unofficially Supported Extensions 
+---------------------------------
+
+These extensions have not yet been included in any official Python-Markdown 
+release. However, the code is maintained in the projects 
+[mainline git repository](http://gitorious.org/projects/python-markdown/repos/mainline) 
+by the Python-Markdown developers and the official documentation is maintained 
+here. All bug reports should be made to the project. It is anticipated that 
+these extensions will be included with some future official release, at which 
+time they will be moved to the above list of official extensions.
+
+* [[Legacy]]
+
+
+
+
+
diff --git a/docs/release-2.0.1.txt b/docs/release-2.0.1.txt
new file mode 100644 (file)
index 0000000..e5946b2
--- /dev/null
@@ -0,0 +1,16 @@
+Python-Markdown 2.0.1 Release Notes
+===================================
+
+Python-Markdown 2.0.1 is a bug-fix release. No new features have been added.
+Most notably, various issues with the command line script have been fixed.
+There have also been a few fixes for minor parsing bugs in some edge cases.
+For a full list of changes, see the git log.
+
+Backwards-incompatible Changes
+------------------------------
+
+Due to various complications in how Python handles command line scripts in 
+differance systems and with differant installation tools, we were forced to 
+rename the commandline script to ``markdown`` (no ".py"). A matching batch
+script will get installed on Windows. Any shell scripts which call 
+``markdown.py`` will need to be altered to call ``markdown`` instead.
diff --git a/docs/release-2.0.2.txt b/docs/release-2.0.2.txt
new file mode 100644 (file)
index 0000000..8ae9a3d
--- /dev/null
@@ -0,0 +1,9 @@
+Python-Markdown 2.0.2 Release Notes
+===================================
+
+Python-Markdown 2.0.2 is a bug-fix release. No new features have been added.
+Most notably, the setup script has been updated to include a dependency on 
+ElementTree on older versions of Python (< 2.5). There have also been a few 
+fixes for minor parsing bugs in some edge cases. For a full list of changes, 
+see the git log.
+
diff --git a/docs/release-2.0.txt b/docs/release-2.0.txt
new file mode 100644 (file)
index 0000000..b1f71ad
--- /dev/null
@@ -0,0 +1,67 @@
+Python-Markdown 2.0 Release Notes
+=================================
+
+We are happy to release Python-Markdown 2.0, which has been over a year in the 
+making. We have rewritten significant portions of the code, dramatically 
+extending the extension API, increased performance, and added numerous 
+extensions to the distribution (including an extension that mimics PHP Markdown
+Extra), all while maintaining backward compatibility with the end user API in
+version 1.7.
+
+Python-Markdown supports Python versions 2.3, 2.4, 2.5, and 2.6. We've even 
+released a version converted to Python 3.0!
+
+Backwards-incompatible Changes
+------------------------------
+
+While Python-Markdown has experienced numerous internal changes, those changes 
+should only affect extension authors. If you have not written your own 
+extensions, then you should not need to make any changes to your code. 
+However, you may want to ensure that any third party extensions you are using
+are compatible with the new API.
+
+The new extension API is fully documented in [[writing_extensions]]. Below is a
+summary of the significant changes:
+
+* The old home-grown NanoDOM has been replaced with ElementTree. Therefore all
+  extensions must use ElementTree rather than the old NanoDOM.
+* The various processors and patterns are now stored with OrderedDicts rather 
+  than lists. Any code adding processors and/or patterns into Python-Markdown 
+  will need to be adjusted to use the new API using OrderedDicts.
+* The various types of processors available have been either combined, added, 
+  or removed. Ensure that your processors match the currently supported types.
+
+What's New in Python-Markdown 2.0
+---------------------------------
+
+Thanks to the work of Artem Yunusov as part of GSoC 2008, Python-Markdown uses
+ElementTree internally to build the (X)HTML document from markdown source text.
+This has resolved various issues with the older home-grown NanoDOM and made
+notable increases in performance.
+
+Artem also refactored the Inline Patterns to better support nested patterns 
+which has resolved many inconsistencies in Python-Markdown's parsing of the 
+markdown syntax.
+
+The core parser had been completely rewritten, increasing performance and, for 
+the first time, making it possible to override/add/change the way block level
+content is parsed.
+
+Python-Markdown now parses markdown source text more closely to the other 
+popular implementations (Perl, PHP, etc.) than it ever has before. With the
+exception of a few minor insignificant differences, any difference should be
+considered a bug, rather than a limitation of the parser.
+
+The option to return HTML4 output as apposed to XHTML has been added. In 
+addition, extensions should be able to easily add additional output formats.
+
+As part of implementing markdown in the Dr. Project project (a Trac fork), among
+other things, David Wolever refactored the "extension" keyword so that it
+accepts either the extension names as strings or instances of extensions. This
+makes it possible to include multiple extensions in a single module.
+
+Numerous extensions are included in the distribution by default. See
+[[available_extensions]] for a complete list.
+
+See the [[change_log]] for a full list of changes.
+
diff --git a/docs/using_as_module.txt b/docs/using_as_module.txt
new file mode 100644 (file)
index 0000000..130d0a7
--- /dev/null
@@ -0,0 +1,150 @@
+Using Markdown as Python Library
+================================
+
+First and foremost, Python-Markdown is intended to be a python library module
+used by various projects to convert Markdown syntax into HTML.
+
+The Basics
+----------
+
+To use markdown as a module:
+
+    import markdown
+    html = markdown.markdown(your_text_string)
+
+Encoded Text
+------------
+
+Note that ``markdown()`` expects **Unicode** as input (although a simple ASCII 
+string should work) and returns output as Unicode.  Do not pass encoded strings to it!
+If your input is encoded, e.g. as UTF-8, it is your responsibility to decode 
+it.  E.g.:
+
+    input_file = codecs.open("some_file.txt", mode="r", encoding="utf-8")
+    text = input_file.read()
+    html = markdown.markdown(text, extensions)
+
+If you later want to write it to disk, you should encode it yourself:
+
+    output_file = codecs.open("some_file.html", "w", encoding="utf-8")
+    output_file.write(html)
+
+More Options
+------------
+
+If you want to pass more options, you can create an instance of the ``Markdown``
+class yourself and then use ``convert()`` to generate HTML:
+
+    import markdown
+    md = markdown.Markdown(
+            extensions=['footnotes'], 
+            extension_configs= {'footnotes' : ('PLACE_MARKER','~~~~~~~~')},
+            safe_mode=True,
+            output_format='html4'
+    )
+    return md.convert(some_text)
+
+You should also use this method if you want to process multiple strings:
+
+    md = markdown.Markdown()
+    html1 = md.convert(text1)
+    html2 = md.convert(text2)
+
+Working with Files
+------------------
+
+While the Markdown class is only intended to work with Unicode text, some
+encoding/decoding is required for the command line features. These functions 
+and methods are only intended to fit the common use case.
+
+The ``Markdown`` class has the method ``convertFile`` which reads in a file and
+writes out to a file-like-object:
+
+    md = markdown.Markdown()
+    md.convertFile(input="in.txt", output="out.html", encoding="utf-8")
+
+The markdown module also includes a shortcut function ``markdownFromFile`` that
+wraps the above method.
+
+    markdown.markdownFromFile(input="in.txt", 
+                              output="out.html", 
+                              extensions=[],
+                              encoding="utf-8",
+                              safe=False)
+
+In either case, if the ``output`` keyword is passed a file name (i.e.: 
+``output="out.html"``), it will try to write to a file by that name. If
+``output`` is passed a file-like-object (i.e. ``output=StringIO.StringIO()``),
+it will attempt to write out to that object. Finally, if ``output`` is 
+set to ``None``, it will write to ``stdout``.
+
+Using Extensions
+----------------
+
+One of the parameters that you can pass is a list of Extensions. Extensions 
+must be available as python modules either within the ``markdown.extensions``
+package or on your PYTHONPATH with names starting with `mdx_`, followed by the 
+name of the extension.  Thus, ``extensions=['footnotes']`` will first look for 
+the module ``markdown.extensions.footnotes``, then a module named 
+``mdx_footnotes``.   See the documentation specific to the extension you are 
+using for help in specifying configuration settings for that extension.
+
+Note that some extensions may need their state reset between each call to 
+``convert``:
+
+    html1 = md.convert(text1)
+    md.reset()
+    html2 = md.convert(text2)
+
+Safe Mode
+---------
+
+If you are using Markdown on a web system which will transform text provided 
+by untrusted users, you may want to use the "safe_mode" option which ensures 
+that the user's HTML tags are either replaced, removed or escaped. (They can 
+still create links using Markdown syntax.)
+
+* To replace HTML, set ``safe_mode="replace"`` (``safe_mode=True`` still works 
+    for backward compatibility with older versions). The HTML will be replaced 
+    with the text defined in ``markdown.HTML_REMOVED_TEXT`` which defaults to 
+    ``[HTML_REMOVED]``. To replace the HTML with something else:
+
+        markdown.HTML_REMOVED_TEXT = "--RAW HTML IS NOT ALLOWED--"
+        md = markdown.Markdown(safe_mode="replace")
+
+    **Note**: You could edit the value of ``HTML_REMOVED_TEXT`` directly in 
+    markdown/__init__.py but you will need to remember to do so every time you 
+    upgrade to a newer version of Markdown. Therefore, this is not recommended.
+
+* To remove HTML, set ``safe_mode="remove"``. Any raw HTML will be completely 
+    stripped from the text with no warning to the author.
+
+* To escape HTML, set ``safe_mode="escape"``. The HTML will be escaped and 
+    included in the document.
+
+Output Formats
+--------------
+
+If Markdown is outputing (X)HTML as part of a web page, most likely you will
+want the output to match the (X)HTML version used by the rest of your page/site.
+Currently, Markdown offers two output formats out of the box; "HTML4" and 
+"XHTML1" (the default) . Markdown will also accept the formats "HTML" and 
+"XHTML" which currently map to "HTML4" and "XHTML" respectively. However, 
+you should use the more explicit keys as the general keys may change in the 
+future if it makes sense at that time. The keys can either be lowercase or 
+uppercase.
+
+To set the output format do:
+
+    html = markdown.markdown(text, output_format='html4')
+
+Or, when using the Markdown class:
+
+    md = markdown.Markdown(output_format='html4')
+    html = md.convert(text)
+
+Note that the output format is only set once for the class and cannot be 
+specified each time ``convert()`` is called. If you really must change the
+output format for the class, you can use the ``set_output_format`` method:
+
+    md.set_output_format('xhtml1')
diff --git a/docs/writing_extensions.txt b/docs/writing_extensions.txt
new file mode 100644 (file)
index 0000000..3aad74a
--- /dev/null
@@ -0,0 +1,594 @@
+Writing Extensions for Python-Markdown
+======================================
+
+Overview
+--------
+
+Python-Markdown includes an API for extension writers to plug their own 
+custom functionality and/or syntax into the parser. There are preprocessors
+which allow you to alter the source before it is passed to the parser, 
+inline patterns which allow you to add, remove or override the syntax of
+any inline elements, and postprocessors which allow munging of the
+output of the parser before it is returned. If you really want to dive in, 
+there are also blockprocessors which are part of the core BlockParser.
+
+As the parser builds an [ElementTree][] object which is later rendered 
+as Unicode text, there are also some helpers provided to ease manipulation of 
+the tree. Each part of the API is discussed in its respective section below. 
+Additionaly, reading the source of some [[Available Extensions]] may be helpful.
+For example, the [[Footnotes]] extension uses most of the features documented 
+here.
+
+* [Preprocessors][]
+* [InlinePatterns][]
+* [Treeprocessors][] 
+* [Postprocessors][]
+* [BlockParser][]
+* [Working with the ElementTree][]
+* [Integrating your code into Markdown][]
+    * [extendMarkdown][]
+    * [OrderedDict][]
+    * [registerExtension][]
+    * [Config Settings][]
+    * [makeExtension][]
+
+<h3 id="preprocessors">Preprocessors</h3>
+
+Preprocessors munge the source text before it is passed into the Markdown 
+core. This is an excellent place to clean up bad syntax, extract things the 
+parser may otherwise choke on and perhaps even store it for later retrieval.
+
+Preprocessors should inherit from ``markdown.preprocessors.Preprocessor`` and 
+implement a ``run`` method with one argument ``lines``. The ``run`` method of 
+each Preprocessor will be passed the entire source text as a list of Unicode 
+strings. Each string will contain one line of text. The ``run`` method should 
+return either that list, or an altered list of Unicode strings.
+
+A pseudo example:
+
+    class MyPreprocessor(markdown.preprocessors.Preprocessor):
+        def run(self, lines):
+            new_lines = []
+            for line in lines:
+                m = MYREGEX.match(line)
+                if m:
+                    # do stuff
+                else:
+                    new_lines.append(line)
+            return new_lines
+
+<h3 id="inlinepatterns">Inline Patterns</h3>
+
+Inline Patterns implement the inline HTML element syntax for Markdown such as
+``*emphasis*`` or ``[links](http://example.com)``. Pattern objects should be 
+instances of classes that inherit from ``markdown.inlinepatterns.Pattern`` or 
+one of its children. Each pattern object uses a single regular expression and 
+must have the following methods:
+
+* **``getCompiledRegExp()``**: 
+
+    Returns a compiled regular expression.
+
+* **``handleMatch(m)``**: 
+
+    Accepts a match object and returns an ElementTree element of a plain 
+    Unicode string.
+
+Note that any regular expression returned by ``getCompiledRegExp`` must capture
+the whole block. Therefore, they should all start with ``r'^(.*?)'`` and end
+with ``r'(.*?)!'``. When using the default ``getCompiledRegExp()`` method 
+provided in the ``Pattern`` you can pass in a regular expression without that 
+and ``getCompiledRegExp`` will wrap your expression for you. This means that 
+the first group of your match will be ``m.group(2)`` as ``m.group(1)`` will 
+match everything before the pattern.
+
+For an example, consider this simplified emphasis pattern:
+
+    class EmphasisPattern(markdown.inlinepatterns.Pattern):
+        def handleMatch(self, m):
+            el = markdown.etree.Element('em')
+            el.text = m.group(3)
+            return el
+
+As discussed in [Integrating Your Code Into Markdown][], an instance of this
+class will need to be provided to Markdown. That instance would be created
+like so:
+
+    # an oversimplified regex
+    MYPATTERN = r'\*([^*]+)\*'
+    # pass in pattern and create instance
+    emphasis = EmphasisPattern(MYPATTERN)
+
+Actually it would not be necessary to create that pattern (and not just because
+a more sophisticated emphasis pattern already exists in Markdown). The fact is,
+that example pattern is not very DRY. A pattern for `**strong**` text would
+be almost identical, with the exception that it would create a 'strong' element.
+Therefore, Markdown provides a number of generic pattern classes that can 
+provide some common functionality. For example, both emphasis and strong are
+implemented with separate instances of the ``SimpleTagPettern`` listed below. 
+Feel free to use or extend any of these Pattern classes.
+
+**Generic Pattern Classes**
+
+* **``SimpleTextPattern(pattern)``**:
+
+    Returns simple text of ``group(2)`` of a ``pattern``.
+
+* **``SimpleTagPattern(pattern, tag)``**:
+
+    Returns an element of type "`tag`" with a text attribute of ``group(3)``
+    of a ``pattern``. ``tag`` should be a string of a HTML element (i.e.: 'em').
+
+* **``SubstituteTagPattern(pattern, tag)``**:
+
+    Returns an element of type "`tag`" with no children or text (i.e.: 'br').
+
+There may be other Pattern classes in the Markdown source that you could extend
+or use as well. Read through the source and see if there is anything you can 
+use. You might even get a few ideas for different approaches to your specific
+situation.
+
+<h3 id="treeprocessors">Treeprocessors</h3>
+
+Treeprocessors manipulate an ElemenTree object after it has passed through the
+core BlockParser. This is where additional manipulation of the tree takes
+place. Additionally, the InlineProcessor is a Treeprocessor which steps through
+the tree and runs the InlinePatterns on the text of each Element in the tree.
+
+A Treeprocessor should inherit from ``markdown.treeprocessors.Treeprocessor``,
+over-ride the ``run`` method which takes one argument ``root`` (an Elementree 
+object) and returns either that root element or a modified root element.
+
+A pseudo example:
+
+    class MyTreeprocessor(markdown.treeprocessors.Treeprocessor):
+        def run(self, root):
+            #do stuff
+            return my_modified_root
+
+For specifics on manipulating the ElementTree, see 
+[Working with the ElementTree][] below.
+
+<h3 id="postprocessors">Postprocessors</h3>
+
+Postprocessors manipulate the document after the ElementTree has been 
+serialized into a string. Postprocessors should be used to work with the
+text just before output.
+
+A Postprocessor should inherit from ``markdown.postprocessors.Postprocessor`` 
+and over-ride the ``run`` method which takes one argument ``text`` and returns 
+a Unicode string.
+
+Postprocessors are run after the ElementTree has been serialized back into 
+Unicode text.  For example, this may be an appropriate place to add a table of 
+contents to a document:
+
+    class TocPostprocessor(markdown.postprocessors.Postprocessor):
+        def run(self, text):
+            return MYMARKERRE.sub(MyToc, text)
+
+<h3 id="blockparser">BlockParser</h3>
+
+Sometimes, pre/tree/postprocessors and Inline Patterns aren't going to do what 
+you need. Perhaps you want a new type of block type that needs to be integrated 
+into the core parsing. In such a situation, you can add/change/remove 
+functionality of the core ``BlockParser``. The BlockParser is composed of a
+number of Blockproccessors. The BlockParser steps through each block of text
+(split by blank lines) and passes each block to the appropriate Blockprocessor.
+That Blockprocessor parses the block and adds it to the ElementTree. The
+[[Definition Lists]] extension would be a good example of an extension that
+adds/modifies Blockprocessors.
+
+A Blockprocessor should inherit from ``markdown.blockprocessors.BlockProcessor``
+and implement both the ``test`` and ``run`` methods.
+
+The ``test`` method is used by BlockParser to identify the type of block.
+Therefore the ``test`` method must return a boolean value. If the test returns
+``True``, then the BlockParser will call that Blockprocessor's ``run`` method.
+If it returns ``False``, the BlockParser will move on to the next 
+BlockProcessor.
+
+The **``test``** method takes two arguments:
+
+* **``parent``**: The parent etree Element of the block. This can be useful as
+  the block may need to be treated differently if it is inside a list, for
+  example.
+
+* **``block``**: A string of the current block of text. The test may be a 
+  simple string method (such as ``block.startswith(some_text)``) or a complex 
+  regular expression.
+
+The **``run``** method takes two arguments:
+
+* **``parent``**: A pointer to the parent etree Element of the block. The run 
+  method will most likely attach additional nodes to this parent. Note that
+  nothing is returned by the method. The Elementree object is altered in place.
+
+* **``blocks``**: A list of all remaining blocks of the document. Your run 
+  method must remove (pop) the first block from the list (which it altered in
+  place - not returned) and parse that block. You may find that a block of text
+  legitimately contains multiple block types. Therefore, after processing the 
+  first type, your processor can insert the remaining text into the beginning
+  of the ``blocks`` list for future parsing.
+
+Please be aware that a single block can span multiple text blocks. For example,
+The official Markdown syntax rules state that a blank line does not end a
+Code Block. If the next block of text is also indented, then it is part of
+the previous block. Therefore, the BlockParser was specifically designed to 
+address these types of situations. If you notice the ``CodeBlockProcessor``,
+in the core, you will note that it checks the last child of the ``parent``.
+If the last child is a code block (``<pre><code>...</code></pre>``), then it
+appends that block to the previous code block rather than creating a new 
+code block.
+
+Each BlockProcessor has the following utility methods available:
+
+* **``lastChild(parent)``**: 
+
+    Returns the last child of the given etree Element or ``None`` if it had no 
+    children.
+
+* **``detab(text)``**: 
+
+    Removes one level of indent (four spaces by default) from the front of each
+    line of the given text string.
+
+* **``looseDetab(text, level)``**: 
+
+    Removes "level" levels of indent (defaults to 1) from the front of each line 
+    of the given text string. However, this methods allows secondary lines to 
+    not be indented as does some parts of the Markdown syntax.
+
+Each BlockProcessor also has a pointer to the containing BlockParser instance at
+``self.parser``, which can be used to check or alter the state of the parser.
+The BlockParser tracks it's state in a stack at ``parser.state``. The state
+stack is an instance of the ``State`` class.
+
+**``State``** is a subclass of ``list`` and has the additional methods:
+
+* **``set(state)``**: 
+
+    Set a new state to string ``state``. The new state is appended to the end 
+    of the stack.
+
+* **``reset()``**: 
+
+    Step back one step in the stack. The last state at the end is removed from 
+    the stack.
+
+* **``isstate(state)``**: 
+
+    Test that the top (current) level of the stack is of the given string 
+    ``state``.
+
+Note that to ensure that the state stack doesn't become corrupted, each time a
+state is set for a block, that state *must* be reset when the parser finishes
+parsing that block.
+
+An instance of the **``BlockParser``** is found at ``Markdown.parser``.
+``BlockParser`` has the following methods:
+
+* **``parseDocument(lines)``**: 
+
+    Given a list of lines, an ElementTree object is returned. This should be 
+    passed an entire document and is the only method the ``Markdown`` class 
+    calls directly.
+
+* **``parseChunk(parent, text)``**: 
+
+    Parses a chunk of markdown text composed of multiple blocks and attaches 
+    those blocks to the ``parent`` Element. The ``parent`` is altered in place 
+    and nothing is returned. Extensions would most likely use this method for 
+    block parsing.
+
+* **``parseBlocks(parent, blocks)``**: 
+
+    Parses a list of blocks of text and attaches those blocks to the ``parent``
+    Element. The ``parent`` is altered in place and nothing is returned. This 
+    method will generally only be used internally to recursively parse nested 
+    blocks of text.
+
+While is is not recommended, an extension could subclass or completely replace
+the ``BlockParser``. The new class would have to provide the same public API.
+However, be aware that other extensions may expect the core parser provided
+and will not work with such a drastically different parser.
+
+<h3 id="working_with_et">Working with the ElementTree</h3>
+
+As mentioned, the Markdown parser converts a source document to an 
+[ElementTree][] object before serializing that back to Unicode text. 
+Markdown has provided some helpers to ease that manipulation within the context 
+of the Markdown module.
+
+First, to get access to the ElementTree module import ElementTree from 
+``markdown`` rather than importing it directly. This will ensure you are using 
+the same version of ElementTree as markdown. The module is named ``etree`` 
+within Markdown.
+
+    from markdown import etree
+    
+``markdown.etree`` tries to import ElementTree from any known location, first 
+as a standard library module (from ``xml.etree`` in Python 2.5), then as a third
+party package (``Elementree``). In each instance, ``cElementTree`` is tried 
+first, then ``ElementTree`` if the faster C implementation is not available on 
+your system.
+
+Sometimes you may want text inserted into an element to be parsed by 
+[InlinePatterns][]. In such a situation, simply insert the text as you normally
+would and the text will be automatically run through the InlinePatterns. 
+However, if you do *not* want some text to be parsed by InlinePatterns,
+then insert the text as an ``AtomicString``.
+
+    some_element.text = markdown.AtomicString(some_text)
+
+Here's a basic example which creates an HTML table (note that the contents of 
+the second cell (``td2``) will be run through InlinePatterns latter):
+
+    table = etree.Element("table") 
+    table.set("cellpadding", "2")                      # Set cellpadding to 2
+    tr = etree.SubElement(table, "tr")                 # Add child tr to table
+    td1 = etree.SubElement(tr, "td")                   # Add child td1 to tr
+    td1.text = markdown.AtomicString("Cell content")   # Add plain text content
+    td2 = etree.SubElement(tr, "td")                   # Add second td to tr
+    td2.text = "*text* with **inline** formatting."    # Add markup text
+    table.tail = "Text after table"                    # Add text after table
+
+You can also manipulate an existing tree. Consider the following example which 
+adds a ``class`` attribute to ``<a>`` elements:
+
+       def set_link_class(self, element):
+               for child in element: 
+                   if child.tag == "a":
+                child.set("class", "myclass") #set the class attribute
+            set_link_class(child) # run recursively on children
+
+For more information about working with ElementTree see the ElementTree
+[Documentation](http://effbot.org/zone/element-index.htm) 
+([Python Docs](http://docs.python.org/lib/module-xml.etree.ElementTree.html)).
+
+<h3 id="integrating_into_markdown">Integrating Your Code Into Markdown</h3>
+
+Once you have the various pieces of your extension built, you need to tell 
+Markdown about them and ensure that they are run in the proper sequence. 
+Markdown accepts a ``Extension`` instance for each extension. Therefore, you
+will need to define a class that extends ``markdown.Extension`` and over-rides
+the ``extendMarkdown`` method. Within this class you will manage configuration 
+options for your extension and attach the various processors and patterns to 
+the Markdown instance. 
+
+It is important to note that the order of the various processors and patterns 
+matters. For example, if we replace ``http://...`` links with <a> elements, and 
+*then* try to deal with  inline html, we will end up with a mess. Therefore, 
+the various types of processors and patterns are stored within an instance of 
+the Markdown class in [OrderedDict][]s. Your ``Extension`` class will need to 
+manipulate those OrderedDicts appropriately. You may insert instances of your 
+processors and patterns into the appropriate location in an OrderedDict, remove
+a built-in instance, or replace a built-in instance with your own.
+
+<h4 id="extendmarkdown">extendMarkdown</h4>
+
+The ``extendMarkdown`` method of a ``markdown.Extension`` class accepts two 
+arguments:
+
+* **``md``**:
+
+    A pointer to the instance of the Markdown class. You should use this to 
+    access the [OrderedDict][]s of processors and patterns. They are found 
+    under the following attributes:
+
+    * ``md.preprocessors``
+    * ``md.inlinePatterns``
+    * ``md.parser.blockprocessors``
+    * ``md.treepreprocessors``
+    * ``md.postprocessors``
+
+    Some other things you may want to access in the markdown instance are:
+
+    * ``md.htmlStash``
+    * ``md.output_formats``
+    * ``md.set_output_format()``
+    * ``md.registerExtension()``
+
+* **``md_globals``**:
+
+    Contains all the various global variables within the markdown module.
+
+Of course, with access to those items, theoretically you have the option to 
+changing anything through various [monkey_patching][] techniques. However, you 
+should be aware that the various undocumented or private parts of markdown 
+may change without notice and your monkey_patches may break with a new release.
+Therefore, what you really should be doing is inserting processors and patterns
+into the markdown pipeline. Consider yourself warned.
+
+[monkey_patching]: http://en.wikipedia.org/wiki/Monkey_patch
+
+A simple example:
+
+    class MyExtension(markdown.Extension):
+        def extendMarkdown(self, md, md_globals):
+            # Insert instance of 'mypattern' before 'references' pattern
+            md.inlinePatterns.add('mypattern', MyPattern(md), '<references')
+
+<h4 id="ordereddict">OrderedDict</h4>
+
+An OrderedDict is a dictionary like object that retains the order of it's
+items. The items are ordered in the order in which they were appended to
+the OrderedDict. However, an item can also be inserted into the OrderedDict
+in a specific location in relation to the existing items.
+
+Think of OrderedDict as a combination of a list and a dictionary as it has 
+methods common to both. For example, you can get and set items using the 
+``od[key] = value`` syntax and the methods ``keys()``, ``values()``, and 
+``items()`` work as expected with the keys, values and items returned in the 
+proper order. At the same time, you can use ``insert()``, ``append()``, and 
+``index()`` as you would with a list.
+
+Generally speaking, within Markdown extensions you will be using the special 
+helper method ``add()`` to add additional items to an existing OrderedDict. 
+
+The ``add()`` method accepts three arguments:
+
+* **``key``**: A string. The key is used for later reference to the item.
+
+* **``value``**: The object instance stored in this item.
+
+* **``location``**: Optional. The items location in relation to other items. 
+
+    Note that the location can consist of a few different values:
+
+    * The special strings ``"_begin"`` and ``"_end"`` insert that item at the 
+      beginning or end of the OrderedDict respectively. 
+    
+    * A less-than sign (``<``) followed by an existing key (i.e.: 
+      ``"<somekey"``) inserts that item before the existing key.
+    
+    * A greater-than sign (``>``) followed by an existing key (i.e.: 
+      ``">somekey"``) inserts that item after the existing key. 
+
+Consider the following example:
+
+    >>> import markdown
+    >>> od = markdown.OrderedDict()
+    >>> od['one'] =  1           # The same as: od.add('one', 1, '_begin')
+    >>> od['three'] = 3          # The same as: od.add('three', 3, '>one')
+    >>> od['four'] = 4           # The same as: od.add('four', 4, '_end')
+    >>> od.items()
+    [("one", 1), ("three", 3), ("four", 4)]
+
+Note that when building an OrderedDict in order, the extra features of the
+``add`` method offer no real value and are not necessary. However, when 
+manipulating an existing OrderedDict, ``add`` can be very helpful. So let's 
+insert another item into the OrderedDict.
+
+    >>> od.add('two', 2, '>one')         # Insert after 'one'
+    >>> od.values()
+    [1, 2, 3, 4]
+
+Now let's insert another item.
+
+    >>> od.add('twohalf', 2.5, '<three') # Insert before 'three'
+    >>> od.keys()
+    ["one", "two", "twohalf", "three", "four"]
+
+Note that we also could have set the location of "twohalf" to be 'after two'
+(i.e.: ``'>two'``). However, it's unlikely that you will have control over the 
+order in which extensions will be loaded, and this could affect the final 
+sorted order of an OrderedDict. For example, suppose an extension adding 
+'twohalf' in the above examples was loaded before a separate  extension which 
+adds 'two'. You may need to take this into consideration when adding your 
+extension components to the various markdown OrderedDicts.
+
+Once an OrderedDict is created, the items are available via key:
+
+    MyNode = od['somekey']
+
+Therefore, to delete an existing item:
+
+    del od['somekey']
+
+To change the value of an existing item (leaving location unchanged):
+
+    od['somekey'] = MyNewObject()
+
+To change the location of an existing item:
+
+    t.link('somekey', '<otherkey')
+
+<h4 id="registerextension">registerExtension</h4>
+
+Some extensions may need to have their state reset between multiple runs of the
+Markdown class. For example, consider the following use of the [[Footnotes]] 
+extension:
+
+    md = markdown.Markdown(extensions=['footnotes'])
+    html1 = md.convert(text_with_footnote)
+    md.reset()
+    html2 = md.convert(text_without_footnote)
+
+Without calling ``reset``, the footnote definitions from the first document will
+be inserted into the second document as they are still stored within the class
+instance. Therefore the ``Extension`` class needs to define a ``reset`` method
+that will reset the state of the extension (i.e.: ``self.footnotes = {}``).
+However, as many extensions do not have a need for ``reset``, ``reset`` is only
+called on extensions that are registered.
+
+To register an extension, call ``md.registerExtension`` from within your 
+``extendMarkdown`` method:
+
+
+    def extendMarkdown(self, md, md_globals):
+        md.registerExtension(self)
+        # insert processors and patterns here
+
+Then, each time ``reset`` is called on the Markdown instance, the ``reset`` 
+method of each registered extension will be called as well. You should also
+note that ``reset`` will be called on each registered extension after it is
+initialized the first time. Keep that in mind when over-riding the extension's
+``reset`` method.
+
+<h4 id="configsettings">Config Settings</h4>
+
+If an extension uses any parameters that the user may want to change,
+those parameters should be stored in ``self.config`` of your 
+``markdown.Extension`` class in the following format:
+
+    self.config = {parameter_1_name : [value1, description1],
+                   parameter_2_name : [value2, description2] }
+
+When stored this way the config parameters can be over-ridden from the
+command line or at the time Markdown is initiated:
+
+    markdown.py -x myextension(SOME_PARAM=2) inputfile.txt > output.txt
+
+Note that parameters should always be assumed to be set to string
+values, and should be converted at run time. For example:
+
+    i = int(self.getConfig("SOME_PARAM"))
+
+<h4 id="makeextension">makeExtension</h4>
+
+Each extension should ideally be placed in its own module starting
+with the  ``mdx_`` prefix (e.g. ``mdx_footnotes.py``).  The module must
+provide a module-level function called ``makeExtension`` that takes
+an optional parameter consisting of a dictionary of configuration over-rides 
+and returns an instance of the extension.  An example from the footnote 
+extension:
+
+    def makeExtension(configs=None) :
+        return FootnoteExtension(configs=configs)
+
+By following the above example, when Markdown is passed the name of your 
+extension as a string (i.e.: ``'footnotes'``), it will automatically import
+the module and call the ``makeExtension`` function initiating your extension.
+
+You may have noted that the extensions packaged with Python-Markdown do not
+use the ``mdx_`` prefix in their module names. This is because they are all
+part of the ``markdown.extensions`` package. Markdown will first try to import
+from ``markdown.extensions.extname`` and upon failure, ``mdx_extname``. If both
+fail, Markdown will continue without the extension.
+
+However, Markdown will also accept an already existing instance of an extension.
+For example:
+
+    import markdown
+    import myextension
+    configs = {...}
+    myext = myextension.MyExtension(configs=configs)
+    md = markdown.Markdown(extensions=[myext])
+
+This is useful if you need to implement a large number of extensions with more
+than one residing in a module.
+
+[Preprocessors]: #preprocessors
+[InlinePatterns]: #inlinepatterns
+[Treeprocessors]: #treeprocessors
+[Postprocessors]: #postprocessors
+[BlockParser]: #blockparser
+[Working with the ElementTree]: #working_with_et
+[Integrating your code into Markdown]: #integrating_into_markdown
+[extendMarkdown]: #extendmarkdown
+[OrderedDict]: #ordereddict
+[registerExtension]: #registerextension
+[Config Settings]: #configsettings
+[makeExtension]: #makeextension
+[ElementTree]: http://effbot.org/zone/element-index.htm
diff --git a/markdown/__init__.py b/markdown/__init__.py
new file mode 100644 (file)
index 0000000..bd52113
--- /dev/null
@@ -0,0 +1,614 @@
+"""
+Python Markdown
+===============
+
+Python Markdown converts Markdown to HTML and can be used as a library or
+called from the command line.
+
+## Basic usage as a module:
+
+    import markdown
+    md = Markdown()
+    html = md.convert(your_text_string)
+
+## Basic use from the command line:
+
+    markdown source.txt > destination.html
+
+Run "markdown --help" to see more options.
+
+## Extensions
+
+See <http://www.freewisdom.org/projects/python-markdown/> for more
+information and instructions on how to extend the functionality of
+Python Markdown.  Read that before you try modifying this file.
+
+## Authors and License
+
+Started by [Manfred Stienstra](http://www.dwerg.net/).  Continued and
+maintained  by [Yuri Takhteyev](http://www.freewisdom.org), [Waylan
+Limberg](http://achinghead.com/) and [Artem Yunusov](http://blog.splyer.com).
+
+Contact: markdown@freewisdom.org
+
+Copyright 2007, 2008 The Python Markdown Project (v. 1.7 and later)
+Copyright 200? Django Software Foundation (OrderedDict implementation)
+Copyright 2004, 2005, 2006 Yuri Takhteyev (v. 0.2-1.6b)
+Copyright 2004 Manfred Stienstra (the original version)
+
+License: BSD (see docs/LICENSE for details).
+"""
+
+version = "2.0.3"
+version_info = (2,0,3, "Final")
+
+import re
+import codecs
+import sys
+import warnings
+import logging
+from logging import DEBUG, INFO, WARN, ERROR, CRITICAL
+
+
+"""
+CONSTANTS
+=============================================================================
+"""
+
+"""
+Constants you might want to modify
+-----------------------------------------------------------------------------
+"""
+
+# default logging level for command-line use
+COMMAND_LINE_LOGGING_LEVEL = CRITICAL
+TAB_LENGTH = 4               # expand tabs to this many spaces
+ENABLE_ATTRIBUTES = True     # @id = xyz -> <... id="xyz">
+SMART_EMPHASIS = True        # this_or_that does not become this<i>or</i>that
+DEFAULT_OUTPUT_FORMAT = 'xhtml1'     # xhtml or html4 output
+HTML_REMOVED_TEXT = "[HTML_REMOVED]" # text used instead of HTML in safe mode
+BLOCK_LEVEL_ELEMENTS = re.compile("p|div|h[1-6]|blockquote|pre|table|dl|ol|ul"
+                                  "|script|noscript|form|fieldset|iframe|math"
+                                  "|ins|del|hr|hr/|style|li|dt|dd|thead|tbody"
+                                  "|tr|th|td")
+DOC_TAG = "div"     # Element used to wrap document - later removed
+
+# Placeholders
+STX = u'\u0002'  # Use STX ("Start of text") for start-of-placeholder
+ETX = u'\u0003'  # Use ETX ("End of text") for end-of-placeholder
+INLINE_PLACEHOLDER_PREFIX = STX+"klzzwxh:"
+INLINE_PLACEHOLDER = INLINE_PLACEHOLDER_PREFIX + "%s" + ETX
+AMP_SUBSTITUTE = STX+"amp"+ETX
+
+
+"""
+Constants you probably do not need to change
+-----------------------------------------------------------------------------
+"""
+
+RTL_BIDI_RANGES = ( (u'\u0590', u'\u07FF'),
+                     # Hebrew (0590-05FF), Arabic (0600-06FF),
+                     # Syriac (0700-074F), Arabic supplement (0750-077F),
+                     # Thaana (0780-07BF), Nko (07C0-07FF).
+                    (u'\u2D30', u'\u2D7F'), # Tifinagh
+                    )
+
+
+"""
+AUXILIARY GLOBAL FUNCTIONS
+=============================================================================
+"""
+
+
+def message(level, text):
+    """ A wrapper method for logging debug messages. """
+    logger =  logging.getLogger('MARKDOWN')
+    if logger.handlers:
+        # The logger is configured
+        logger.log(level, text)
+        if level > WARN:
+            sys.exit(0)
+    elif level > WARN:
+        raise MarkdownException, text
+    else:
+        warnings.warn(text, MarkdownWarning)
+
+
+def isBlockLevel(tag):
+    """Check if the tag is a block level HTML tag."""
+    return BLOCK_LEVEL_ELEMENTS.match(tag)
+
+"""
+MISC AUXILIARY CLASSES
+=============================================================================
+"""
+
+class AtomicString(unicode):
+    """A string which should not be further processed."""
+    pass
+
+
+class MarkdownException(Exception):
+    """ A Markdown Exception. """
+    pass
+
+
+class MarkdownWarning(Warning):
+    """ A Markdown Warning. """
+    pass
+
+
+"""
+OVERALL DESIGN
+=============================================================================
+
+Markdown processing takes place in four steps:
+
+1. A bunch of "preprocessors" munge the input text.
+2. BlockParser() parses the high-level structural elements of the
+   pre-processed text into an ElementTree.
+3. A bunch of "treeprocessors" are run against the ElementTree. One such
+   treeprocessor runs InlinePatterns against the ElementTree, detecting inline
+   markup.
+4. Some post-processors are run against the text after the ElementTree has
+   been serialized into text.
+5. The output is written to a string.
+
+Those steps are put together by the Markdown() class.
+
+"""
+
+import preprocessors
+import blockprocessors
+import treeprocessors
+import inlinepatterns
+import postprocessors
+import blockparser
+import etree_loader
+import odict
+
+# Extensions should use "markdown.etree" instead of "etree" (or do `from
+# markdown import etree`).  Do not import it by yourself.
+
+etree = etree_loader.importETree()
+
+# Adds the ability to output html4
+import html4
+
+
+class Markdown:
+    """Convert Markdown to HTML."""
+
+    def __init__(self,
+                 extensions=[],
+                 extension_configs={},
+                 safe_mode = False, 
+                 output_format=DEFAULT_OUTPUT_FORMAT):
+        """
+        Creates a new Markdown instance.
+
+        Keyword arguments:
+
+        * extensions: A list of extensions.
+           If they are of type string, the module mdx_name.py will be loaded.
+           If they are a subclass of markdown.Extension, they will be used
+           as-is.
+        * extension-configs: Configuration setting for extensions.
+        * safe_mode: Disallow raw html. One of "remove", "replace" or "escape".
+        * output_format: Format of output. Supported formats are:
+            * "xhtml1": Outputs XHTML 1.x. Default.
+            * "xhtml": Outputs latest supported version of XHTML (currently XHTML 1.1).
+            * "html4": Outputs HTML 4
+            * "html": Outputs latest supported version of HTML (currently HTML 4).
+            Note that it is suggested that the more specific formats ("xhtml1" 
+            and "html4") be used as "xhtml" or "html" may change in the future
+            if it makes sense at that time. 
+
+        """
+        
+        self.safeMode = safe_mode
+        self.registeredExtensions = []
+        self.docType = ""
+        self.stripTopLevelTags = True
+
+        # Preprocessors
+        self.preprocessors = odict.OrderedDict()
+        self.preprocessors["html_block"] = \
+                preprocessors.HtmlBlockPreprocessor(self)
+        self.preprocessors["reference"] = \
+                preprocessors.ReferencePreprocessor(self)
+        # footnote preprocessor will be inserted with "<reference"
+
+        # Block processors - ran by the parser
+        self.parser = blockparser.BlockParser()
+        self.parser.blockprocessors['empty'] = \
+                blockprocessors.EmptyBlockProcessor(self.parser)
+        self.parser.blockprocessors['indent'] = \
+                blockprocessors.ListIndentProcessor(self.parser)
+        self.parser.blockprocessors['code'] = \
+                blockprocessors.CodeBlockProcessor(self.parser)
+        self.parser.blockprocessors['hashheader'] = \
+                blockprocessors.HashHeaderProcessor(self.parser)
+        self.parser.blockprocessors['setextheader'] = \
+                blockprocessors.SetextHeaderProcessor(self.parser)
+        self.parser.blockprocessors['hr'] = \
+                blockprocessors.HRProcessor(self.parser)
+        self.parser.blockprocessors['olist'] = \
+                blockprocessors.OListProcessor(self.parser)
+        self.parser.blockprocessors['ulist'] = \
+                blockprocessors.UListProcessor(self.parser)
+        self.parser.blockprocessors['quote'] = \
+                blockprocessors.BlockQuoteProcessor(self.parser)
+        self.parser.blockprocessors['paragraph'] = \
+                blockprocessors.ParagraphProcessor(self.parser)
+
+
+        #self.prePatterns = []
+
+        # Inline patterns - Run on the tree
+        self.inlinePatterns = odict.OrderedDict()
+        self.inlinePatterns["backtick"] = \
+                inlinepatterns.BacktickPattern(inlinepatterns.BACKTICK_RE)
+        self.inlinePatterns["escape"] = \
+                inlinepatterns.SimpleTextPattern(inlinepatterns.ESCAPE_RE)
+        self.inlinePatterns["reference"] = \
+            inlinepatterns.ReferencePattern(inlinepatterns.REFERENCE_RE, self)
+        self.inlinePatterns["link"] = \
+                inlinepatterns.LinkPattern(inlinepatterns.LINK_RE, self)
+        self.inlinePatterns["image_link"] = \
+                inlinepatterns.ImagePattern(inlinepatterns.IMAGE_LINK_RE, self)
+        self.inlinePatterns["image_reference"] = \
+            inlinepatterns.ImageReferencePattern(inlinepatterns.IMAGE_REFERENCE_RE, self)
+        self.inlinePatterns["autolink"] = \
+            inlinepatterns.AutolinkPattern(inlinepatterns.AUTOLINK_RE, self)
+        self.inlinePatterns["automail"] = \
+            inlinepatterns.AutomailPattern(inlinepatterns.AUTOMAIL_RE, self)
+        self.inlinePatterns["linebreak2"] = \
+            inlinepatterns.SubstituteTagPattern(inlinepatterns.LINE_BREAK_2_RE, 'br')
+        self.inlinePatterns["linebreak"] = \
+            inlinepatterns.SubstituteTagPattern(inlinepatterns.LINE_BREAK_RE, 'br')
+        self.inlinePatterns["html"] = \
+                inlinepatterns.HtmlPattern(inlinepatterns.HTML_RE, self)
+        self.inlinePatterns["entity"] = \
+                inlinepatterns.HtmlPattern(inlinepatterns.ENTITY_RE, self)
+        self.inlinePatterns["not_strong"] = \
+                inlinepatterns.SimpleTextPattern(inlinepatterns.NOT_STRONG_RE)
+        self.inlinePatterns["strong_em"] = \
+            inlinepatterns.DoubleTagPattern(inlinepatterns.STRONG_EM_RE, 'strong,em')
+        self.inlinePatterns["strong"] = \
+            inlinepatterns.SimpleTagPattern(inlinepatterns.STRONG_RE, 'strong')
+        self.inlinePatterns["emphasis"] = \
+            inlinepatterns.SimpleTagPattern(inlinepatterns.EMPHASIS_RE, 'em')
+        self.inlinePatterns["emphasis2"] = \
+            inlinepatterns.SimpleTagPattern(inlinepatterns.EMPHASIS_2_RE, 'em')
+        # The order of the handlers matters!!!
+
+
+        # Tree processors - run once we have a basic parse.
+        self.treeprocessors = odict.OrderedDict()
+        self.treeprocessors["inline"] = treeprocessors.InlineProcessor(self)
+        self.treeprocessors["prettify"] = \
+                treeprocessors.PrettifyTreeprocessor(self)
+
+        # Postprocessors - finishing touches.
+        self.postprocessors = odict.OrderedDict()
+        self.postprocessors["raw_html"] = \
+                postprocessors.RawHtmlPostprocessor(self)
+        self.postprocessors["amp_substitute"] = \
+                postprocessors.AndSubstitutePostprocessor()
+        # footnote postprocessor will be inserted with ">amp_substitute"
+
+        # Map format keys to serializers
+        self.output_formats = {
+            'html'  : html4.to_html_string, 
+            'html4' : html4.to_html_string,
+            'xhtml' : etree.tostring, 
+            'xhtml1': etree.tostring,
+        }
+
+        self.references = {}
+        self.htmlStash = preprocessors.HtmlStash()
+        self.registerExtensions(extensions = extensions,
+                                configs = extension_configs)
+        self.set_output_format(output_format)
+        self.reset()
+
+    def registerExtensions(self, extensions, configs):
+        """
+        Register extensions with this instance of Markdown.
+
+        Keyword aurguments:
+
+        * extensions: A list of extensions, which can either
+           be strings or objects.  See the docstring on Markdown.
+        * configs: A dictionary mapping module names to config options.
+
+        """
+        for ext in extensions:
+            if isinstance(ext, basestring):
+                ext = load_extension(ext, configs.get(ext, []))
+            if isinstance(ext, Extension):
+                try:
+                    ext.extendMarkdown(self, globals())
+                except NotImplementedError, e:
+                    message(ERROR, e)
+            else:
+                message(ERROR, 'Extension "%s.%s" must be of type: "markdown.Extension".' \
+                    % (ext.__class__.__module__, ext.__class__.__name__))
+
+    def registerExtension(self, extension):
+        """ This gets called by the extension """
+        self.registeredExtensions.append(extension)
+
+    def reset(self):
+        """
+        Resets all state variables so that we can start with a new text.
+        """
+        self.htmlStash.reset()
+        self.references.clear()
+
+        for extension in self.registeredExtensions:
+            extension.reset()
+
+    def set_output_format(self, format):
+        """ Set the output format for the class instance. """
+        try:
+            self.serializer = self.output_formats[format.lower()]
+        except KeyError:
+            message(CRITICAL, 'Invalid Output Format: "%s". Use one of %s.' \
+                               % (format, self.output_formats.keys()))
+
+    def convert(self, source):
+        """
+        Convert markdown to serialized XHTML or HTML.
+
+        Keyword arguments:
+
+        * source: Source text as a Unicode string.
+
+        """
+
+        # Fixup the source text
+        if not source.strip():
+            return u""  # a blank unicode string
+        try:
+            source = unicode(source)
+        except UnicodeDecodeError:
+            message(CRITICAL, 'UnicodeDecodeError: Markdown only accepts unicode or ascii input.')
+            return u""
+
+        source = source.replace(STX, "").replace(ETX, "")
+        source = source.replace("\r\n", "\n").replace("\r", "\n") + "\n\n"
+        source = re.sub(r'\n\s+\n', '\n\n', source)
+        source = source.expandtabs(TAB_LENGTH)
+
+        # Split into lines and run the line preprocessors.
+        self.lines = source.split("\n")
+        for prep in self.preprocessors.values():
+            self.lines = prep.run(self.lines)
+
+        # Parse the high-level elements.
+        root = self.parser.parseDocument(self.lines).getroot()
+
+        # Run the tree-processors
+        for treeprocessor in self.treeprocessors.values():
+            newRoot = treeprocessor.run(root)
+            if newRoot:
+                root = newRoot
+
+        # Serialize _properly_.  Strip top-level tags.
+        output, length = codecs.utf_8_decode(self.serializer(root, encoding="utf-8"))
+        if self.stripTopLevelTags:
+            try:
+                start = output.index('<%s>'%DOC_TAG)+len(DOC_TAG)+2
+                end = output.rindex('</%s>'%DOC_TAG)
+                output = output[start:end].strip()
+            except ValueError:
+                if output.strip().endswith('<%s />'%DOC_TAG):
+                    # We have an empty document
+                    output = ''
+                else:
+                    # We have a serious problem
+                    message(CRITICAL, 'Failed to strip top level tags.')
+
+        # Run the text post-processors
+        for pp in self.postprocessors.values():
+            output = pp.run(output)
+
+        return output.strip()
+
+    def convertFile(self, input=None, output=None, encoding=None):
+        """Converts a markdown file and returns the HTML as a unicode string.
+
+        Decodes the file using the provided encoding (defaults to utf-8),
+        passes the file content to markdown, and outputs the html to either
+        the provided stream or the file with provided name, using the same
+        encoding as the source file.
+
+        **Note:** This is the only place that decoding and encoding of unicode
+        takes place in Python-Markdown.  (All other code is unicode-in /
+        unicode-out.)
+
+        Keyword arguments:
+
+        * input: Name of source text file.
+        * output: Name of output file. Writes to stdout if `None`.
+        * encoding: Encoding of input and output files. Defaults to utf-8.
+
+        """
+
+        encoding = encoding or "utf-8"
+
+        # Read the source
+        input_file = codecs.open(input, mode="r", encoding=encoding)
+        text = input_file.read()
+        input_file.close()
+        text = text.lstrip(u'\ufeff') # remove the byte-order mark
+
+        # Convert
+        html = self.convert(text)
+
+        # Write to file or stdout
+        if isinstance(output, (str, unicode)):
+            output_file = codecs.open(output, "w", encoding=encoding)
+            output_file.write(html)
+            output_file.close()
+        else:
+            output.write(html.encode(encoding))
+
+
+"""
+Extensions
+-----------------------------------------------------------------------------
+"""
+
+class Extension:
+    """ Base class for extensions to subclass. """
+    def __init__(self, configs = {}):
+        """Create an instance of an Extention.
+
+        Keyword arguments:
+
+        * configs: A dict of configuration setting used by an Extension.
+        """
+        self.config = configs
+
+    def getConfig(self, key):
+        """ Return a setting for the given key or an empty string. """
+        if key in self.config:
+            return self.config[key][0]
+        else:
+            return ""
+
+    def getConfigInfo(self):
+        """ Return all config settings as a list of tuples. """
+        return [(key, self.config[key][1]) for key in self.config.keys()]
+
+    def setConfig(self, key, value):
+        """ Set a config setting for `key` with the given `value`. """
+        self.config[key][0] = value
+
+    def extendMarkdown(self, md, md_globals):
+        """
+        Add the various proccesors and patterns to the Markdown Instance.
+
+        This method must be overriden by every extension.
+
+        Keyword arguments:
+
+        * md: The Markdown instance.
+
+        * md_globals: Global variables in the markdown module namespace.
+
+        """
+        raise NotImplementedError, 'Extension "%s.%s" must define an "extendMarkdown"' \
+            'method.' % (self.__class__.__module__, self.__class__.__name__)
+
+
+def load_extension(ext_name, configs = []):
+    """Load extension by name, then return the module.
+
+    The extension name may contain arguments as part of the string in the
+    following format: "extname(key1=value1,key2=value2)"
+
+    """
+
+    # Parse extensions config params (ignore the order)
+    configs = dict(configs)
+    pos = ext_name.find("(") # find the first "("
+    if pos > 0:
+        ext_args = ext_name[pos+1:-1]
+        ext_name = ext_name[:pos]
+        pairs = [x.split("=") for x in ext_args.split(",")]
+        configs.update([(x.strip(), y.strip()) for (x, y) in pairs])
+
+    # Setup the module names
+    ext_module = 'markdown.extensions'
+    module_name_new_style = '.'.join([ext_module, ext_name])
+    module_name_old_style = '_'.join(['mdx', ext_name])
+
+    # Try loading the extention first from one place, then another
+    try: # New style (markdown.extensons.<extension>)
+        module = __import__(module_name_new_style, {}, {}, [ext_module])
+    except ImportError:
+        try: # Old style (mdx.<extension>)
+            module = __import__(module_name_old_style)
+        except ImportError:
+           message(WARN, "Failed loading extension '%s' from '%s' or '%s'"
+               % (ext_name, module_name_new_style, module_name_old_style))
+           # Return None so we don't try to initiate none-existant extension
+           return None
+
+    # If the module is loaded successfully, we expect it to define a
+    # function called makeExtension()
+    try:
+        return module.makeExtension(configs.items())
+    except AttributeError:
+        message(CRITICAL, "Failed to initiate extension '%s'" % ext_name)
+
+
+def load_extensions(ext_names):
+    """Loads multiple extensions"""
+    extensions = []
+    for ext_name in ext_names:
+        extension = load_extension(ext_name)
+        if extension:
+            extensions.append(extension)
+    return extensions
+
+
+"""
+EXPORTED FUNCTIONS
+=============================================================================
+
+Those are the two functions we really mean to export: markdown() and
+markdownFromFile().
+"""
+
+def markdown(text,
+             extensions = [],
+             safe_mode = False,
+             output_format = DEFAULT_OUTPUT_FORMAT):
+    """Convert a markdown string to HTML and return HTML as a unicode string.
+
+    This is a shortcut function for `Markdown` class to cover the most
+    basic use case.  It initializes an instance of Markdown, loads the
+    necessary extensions and runs the parser on the given text.
+
+    Keyword arguments:
+
+    * text: Markdown formatted text as Unicode or ASCII string.
+    * extensions: A list of extensions or extension names (may contain config args).
+    * safe_mode: Disallow raw html.  One of "remove", "replace" or "escape".
+    * output_format: Format of output. Supported formats are:
+        * "xhtml1": Outputs XHTML 1.x. Default.
+        * "xhtml": Outputs latest supported version of XHTML (currently XHTML 1.1).
+        * "html4": Outputs HTML 4
+        * "html": Outputs latest supported version of HTML (currently HTML 4).
+        Note that it is suggested that the more specific formats ("xhtml1" 
+        and "html4") be used as "xhtml" or "html" may change in the future
+        if it makes sense at that time. 
+
+    Returns: An HTML document as a string.
+
+    """
+    md = Markdown(extensions=load_extensions(extensions),
+                  safe_mode=safe_mode, 
+                  output_format=output_format)
+    return md.convert(text)
+
+
+def markdownFromFile(input = None,
+                     output = None,
+                     extensions = [],
+                     encoding = None,
+                     safe_mode = False,
+                     output_format = DEFAULT_OUTPUT_FORMAT):
+    """Read markdown code from a file and write it to a file or a stream."""
+    md = Markdown(extensions=load_extensions(extensions), 
+                  safe_mode=safe_mode,
+                  output_format=output_format)
+    md.convertFile(input, output, encoding)
+
+
+
diff --git a/markdown/blockparser.py b/markdown/blockparser.py
new file mode 100644 (file)
index 0000000..e18b338
--- /dev/null
@@ -0,0 +1,95 @@
+
+import markdown
+
+class State(list):
+    """ Track the current and nested state of the parser. 
+    
+    This utility class is used to track the state of the BlockParser and 
+    support multiple levels if nesting. It's just a simple API wrapped around
+    a list. Each time a state is set, that state is appended to the end of the
+    list. Each time a state is reset, that state is removed from the end of
+    the list.
+
+    Therefore, each time a state is set for a nested block, that state must be 
+    reset when we back out of that level of nesting or the state could be
+    corrupted.
+
+    While all the methods of a list object are available, only the three
+    defined below need be used.
+
+    """
+
+    def set(self, state):
+        """ Set a new state. """
+        self.append(state)
+
+    def reset(self):
+        """ Step back one step in nested state. """
+        self.pop()
+
+    def isstate(self, state):
+        """ Test that top (current) level is of given state. """
+        if len(self):
+            return self[-1] == state
+        else:
+            return False
+
+class BlockParser:
+    """ Parse Markdown blocks into an ElementTree object. 
+    
+    A wrapper class that stitches the various BlockProcessors together,
+    looping through them and creating an ElementTree object.
+    """
+
+    def __init__(self):
+        self.blockprocessors = markdown.odict.OrderedDict()
+        self.state = State()
+
+    def parseDocument(self, lines):
+        """ Parse a markdown document into an ElementTree. 
+        
+        Given a list of lines, an ElementTree object (not just a parent Element)
+        is created and the root element is passed to the parser as the parent.
+        The ElementTree object is returned.
+        
+        This should only be called on an entire document, not pieces.
+
+        """
+        # Create a ElementTree from the lines
+        self.root = markdown.etree.Element(markdown.DOC_TAG)
+        self.parseChunk(self.root, '\n'.join(lines))
+        return markdown.etree.ElementTree(self.root)
+
+    def parseChunk(self, parent, text):
+        """ Parse a chunk of markdown text and attach to given etree node. 
+        
+        While the ``text`` argument is generally assumed to contain multiple
+        blocks which will be split on blank lines, it could contain only one
+        block. Generally, this method would be called by extensions when
+        block parsing is required. 
+        
+        The ``parent`` etree Element passed in is altered in place. 
+        Nothing is returned.
+
+        """
+        self.parseBlocks(parent, text.split('\n\n'))
+
+    def parseBlocks(self, parent, blocks):
+        """ Process blocks of markdown text and attach to given etree node. 
+        
+        Given a list of ``blocks``, each blockprocessor is stepped through
+        until there are no blocks left. While an extension could potentially
+        call this method directly, it's generally expected to be used internally.
+
+        This is a public method as an extension may need to add/alter additional
+        BlockProcessors which call this method to recursively parse a nested
+        block.
+
+        """
+        while blocks:
+           for processor in self.blockprocessors.values():
+               if processor.test(parent, blocks[0]):
+                   processor.run(parent, blocks)
+                   break
+
+
diff --git a/markdown/blockprocessors.py b/markdown/blockprocessors.py
new file mode 100644 (file)
index 0000000..7d3b137
--- /dev/null
@@ -0,0 +1,460 @@
+"""
+CORE MARKDOWN BLOCKPARSER
+=============================================================================
+
+This parser handles basic parsing of Markdown blocks.  It doesn't concern itself
+with inline elements such as **bold** or *italics*, but rather just catches 
+blocks, lists, quotes, etc.
+
+The BlockParser is made up of a bunch of BlockProssors, each handling a 
+different type of block. Extensions may add/replace/remove BlockProcessors
+as they need to alter how markdown blocks are parsed.
+
+"""
+
+import re
+import markdown
+
+class BlockProcessor:
+    """ Base class for block processors. 
+    
+    Each subclass will provide the methods below to work with the source and
+    tree. Each processor will need to define it's own ``test`` and ``run``
+    methods. The ``test`` method should return True or False, to indicate
+    whether the current block should be processed by this processor. If the
+    test passes, the parser will call the processors ``run`` method.
+
+    """
+
+    def __init__(self, parser=None):
+        self.parser = parser
+
+    def lastChild(self, parent):
+        """ Return the last child of an etree element. """
+        if len(parent):
+            return parent[-1]
+        else:
+            return None
+
+    def detab(self, text):
+        """ Remove a tab from the front of each line of the given text. """
+        newtext = []
+        lines = text.split('\n')
+        for line in lines:
+            if line.startswith(' '*markdown.TAB_LENGTH):
+                newtext.append(line[markdown.TAB_LENGTH:])
+            elif not line.strip():
+                newtext.append('')
+            else:
+                break
+        return '\n'.join(newtext), '\n'.join(lines[len(newtext):])
+
+    def looseDetab(self, text, level=1):
+        """ Remove a tab from front of lines but allowing dedented lines. """
+        lines = text.split('\n')
+        for i in range(len(lines)):
+            if lines[i].startswith(' '*markdown.TAB_LENGTH*level):
+                lines[i] = lines[i][markdown.TAB_LENGTH*level:]
+        return '\n'.join(lines)
+
+    def test(self, parent, block):
+        """ Test for block type. Must be overridden by subclasses. 
+        
+        As the parser loops through processors, it will call the ``test`` method
+        on each to determine if the given block of text is of that type. This
+        method must return a boolean ``True`` or ``False``. The actual method of
+        testing is left to the needs of that particular block type. It could 
+        be as simple as ``block.startswith(some_string)`` or a complex regular
+        expression. As the block type may be different depending on the parent
+        of the block (i.e. inside a list), the parent etree element is also 
+        provided and may be used as part of the test.
+
+        Keywords:
+        
+        * ``parent``: A etree element which will be the parent of the block.
+        * ``block``: A block of text from the source which has been split at 
+            blank lines.
+        """
+        pass
+
+    def run(self, parent, blocks):
+        """ Run processor. Must be overridden by subclasses. 
+        
+        When the parser determines the appropriate type of a block, the parser
+        will call the corresponding processor's ``run`` method. This method
+        should parse the individual lines of the block and append them to
+        the etree. 
+
+        Note that both the ``parent`` and ``etree`` keywords are pointers
+        to instances of the objects which should be edited in place. Each
+        processor must make changes to the existing objects as there is no
+        mechanism to return new/different objects to replace them.
+
+        This means that this method should be adding SubElements or adding text
+        to the parent, and should remove (``pop``) or add (``insert``) items to
+        the list of blocks.
+
+        Keywords:
+
+        * ``parent``: A etree element which is the parent of the current block.
+        * ``blocks``: A list of all remaining blocks of the document.
+        """
+        pass
+
+
+class ListIndentProcessor(BlockProcessor):
+    """ Process children of list items. 
+    
+    Example:
+        * a list item
+            process this part
+
+            or this part
+
+    """
+
+    INDENT_RE = re.compile(r'^(([ ]{%s})+)'% markdown.TAB_LENGTH)
+    ITEM_TYPES = ['li']
+    LIST_TYPES = ['ul', 'ol']
+
+    def test(self, parent, block):
+        return block.startswith(' '*markdown.TAB_LENGTH) and \
+                not self.parser.state.isstate('detabbed') and  \
+                (parent.tag in self.ITEM_TYPES or \
+                    (len(parent) and parent[-1] and \
+                        (parent[-1].tag in self.LIST_TYPES)
+                    )
+                )
+
+    def run(self, parent, blocks):
+        block = blocks.pop(0)
+        level, sibling = self.get_level(parent, block)
+        block = self.looseDetab(block, level)
+
+        self.parser.state.set('detabbed')
+        if parent.tag in self.ITEM_TYPES:
+            # The parent is already a li. Just parse the child block.
+            self.parser.parseBlocks(parent, [block])
+        elif sibling.tag in self.ITEM_TYPES:
+            # The sibling is a li. Use it as parent.
+            self.parser.parseBlocks(sibling, [block])
+        elif len(sibling) and sibling[-1].tag in self.ITEM_TYPES:
+            # The parent is a list (``ol`` or ``ul``) which has children.
+            # Assume the last child li is the parent of this block.
+            if sibling[-1].text:
+                # If the parent li has text, that text needs to be moved to a p
+                block = '%s\n\n%s' % (sibling[-1].text, block)
+                sibling[-1].text = ''
+            self.parser.parseChunk(sibling[-1], block)
+        else:
+            self.create_item(sibling, block)
+        self.parser.state.reset()
+
+    def create_item(self, parent, block):
+        """ Create a new li and parse the block with it as the parent. """
+        li = markdown.etree.SubElement(parent, 'li')
+        self.parser.parseBlocks(li, [block])
+    def get_level(self, parent, block):
+        """ Get level of indent based on list level. """
+        # Get indent level
+        m = self.INDENT_RE.match(block)
+        if m:
+            indent_level = len(m.group(1))/markdown.TAB_LENGTH
+        else:
+            indent_level = 0
+        if self.parser.state.isstate('list'):
+            # We're in a tightlist - so we already are at correct parent.
+            level = 1
+        else:
+            # We're in a looselist - so we need to find parent.
+            level = 0
+        # Step through children of tree to find matching indent level.
+        while indent_level > level:
+            child = self.lastChild(parent)
+            if child and (child.tag in self.LIST_TYPES or child.tag in self.ITEM_TYPES):
+                if child.tag in self.LIST_TYPES:
+                    level += 1
+                parent = child
+            else:
+                # No more child levels. If we're short of indent_level,
+                # we have a code block. So we stop here.
+                break
+        return level, parent
+
+
+class CodeBlockProcessor(BlockProcessor):
+    """ Process code blocks. """
+
+    def test(self, parent, block):
+        return block.startswith(' '*markdown.TAB_LENGTH)
+    
+    def run(self, parent, blocks):
+        sibling = self.lastChild(parent)
+        block = blocks.pop(0)
+        theRest = ''
+        if sibling and sibling.tag == "pre" and len(sibling) \
+                    and sibling[0].tag == "code":
+            # The previous block was a code block. As blank lines do not start
+            # new code blocks, append this block to the previous, adding back
+            # linebreaks removed from the split into a list.
+            code = sibling[0]
+            block, theRest = self.detab(block)
+            code.text = markdown.AtomicString('%s\n%s\n' % (code.text, block.rstrip()))
+        else:
+            # This is a new codeblock. Create the elements and insert text.
+            pre = markdown.etree.SubElement(parent, 'pre')
+            code = markdown.etree.SubElement(pre, 'code')
+            block, theRest = self.detab(block)
+            code.text = markdown.AtomicString('%s\n' % block.rstrip())
+        if theRest:
+            # This block contained unindented line(s) after the first indented 
+            # line. Insert these lines as the first block of the master blocks
+            # list for future processing.
+            blocks.insert(0, theRest)
+
+
+class BlockQuoteProcessor(BlockProcessor):
+
+    RE = re.compile(r'(^|\n)[ ]{0,3}>[ ]?(.*)')
+
+    def test(self, parent, block):
+        return bool(self.RE.search(block))
+
+    def run(self, parent, blocks):
+        block = blocks.pop(0)
+        m = self.RE.search(block)
+        if m:
+            before = block[:m.start()] # Lines before blockquote
+            # Pass lines before blockquote in recursively for parsing forst.
+            self.parser.parseBlocks(parent, [before])
+            # Remove ``> `` from begining of each line.
+            block = '\n'.join([self.clean(line) for line in 
+                            block[m.start():].split('\n')])
+        sibling = self.lastChild(parent)
+        if sibling and sibling.tag == "blockquote":
+            # Previous block was a blockquote so set that as this blocks parent
+            quote = sibling
+        else:
+            # This is a new blockquote. Create a new parent element.
+            quote = markdown.etree.SubElement(parent, 'blockquote')
+        # Recursively parse block with blockquote as parent.
+        self.parser.parseChunk(quote, block)
+
+    def clean(self, line):
+        """ Remove ``>`` from beginning of a line. """
+        m = self.RE.match(line)
+        if line.strip() == ">":
+            return ""
+        elif m:
+            return m.group(2)
+        else:
+            return line
+
+class OListProcessor(BlockProcessor):
+    """ Process ordered list blocks. """
+
+    TAG = 'ol'
+    # Detect an item (``1. item``). ``group(1)`` contains contents of item.
+    RE = re.compile(r'^[ ]{0,3}\d+\.[ ]+(.*)')
+    # Detect items on secondary lines. they can be of either list type.
+    CHILD_RE = re.compile(r'^[ ]{0,3}((\d+\.)|[*+-])[ ]+(.*)')
+    # Detect indented (nested) items of either type
+    INDENT_RE = re.compile(r'^[ ]{4,7}((\d+\.)|[*+-])[ ]+.*')
+
+    def test(self, parent, block):
+        return bool(self.RE.match(block))
+
+    def run(self, parent, blocks):
+        # Check fr multiple items in one block.
+        items = self.get_items(blocks.pop(0))
+        sibling = self.lastChild(parent)
+        if sibling and sibling.tag in ['ol', 'ul']:
+            # Previous block was a list item, so set that as parent
+            lst = sibling
+            # make sure previous item is in a p.
+            if len(lst) and lst[-1].text and not len(lst[-1]):
+                p = markdown.etree.SubElement(lst[-1], 'p')
+                p.text = lst[-1].text
+                lst[-1].text = ''
+            # parse first block differently as it gets wrapped in a p.
+            li = markdown.etree.SubElement(lst, 'li')
+            self.parser.state.set('looselist')
+            firstitem = items.pop(0)
+            self.parser.parseBlocks(li, [firstitem])
+            self.parser.state.reset()
+        else:
+            # This is a new list so create parent with appropriate tag.
+            lst = markdown.etree.SubElement(parent, self.TAG)
+        self.parser.state.set('list')
+        # Loop through items in block, recursively parsing each with the
+        # appropriate parent.
+        for item in items:
+            if item.startswith(' '*markdown.TAB_LENGTH):
+                # Item is indented. Parse with last item as parent
+                self.parser.parseBlocks(lst[-1], [item])
+            else:
+                # New item. Create li and parse with it as parent
+                li = markdown.etree.SubElement(lst, 'li')
+                self.parser.parseBlocks(li, [item])
+        self.parser.state.reset()
+
+    def get_items(self, block):
+        """ Break a block into list items. """
+        items = []
+        for line in block.split('\n'):
+            m = self.CHILD_RE.match(line)
+            if m:
+                # This is a new item. Append
+                items.append(m.group(3))
+            elif self.INDENT_RE.match(line):
+                # This is an indented (possibly nested) item.
+                if items[-1].startswith(' '*markdown.TAB_LENGTH):
+                    # Previous item was indented. Append to that item.
+                    items[-1] = '%s\n%s' % (items[-1], line)
+                else:
+                    items.append(line)
+            else:
+                # This is another line of previous item. Append to that item.
+                items[-1] = '%s\n%s' % (items[-1], line)
+        return items
+
+
+class UListProcessor(OListProcessor):
+    """ Process unordered list blocks. """
+
+    TAG = 'ul'
+    RE = re.compile(r'^[ ]{0,3}[*+-][ ]+(.*)')
+
+
+class HashHeaderProcessor(BlockProcessor):
+    """ Process Hash Headers. """
+
+    # Detect a header at start of any line in block
+    RE = re.compile(r'(^|\n)(?P<level>#{1,6})(?P<header>.*?)#*(\n|$)')
+
+    def test(self, parent, block):
+        return bool(self.RE.search(block))
+
+    def run(self, parent, blocks):
+        block = blocks.pop(0)
+        m = self.RE.search(block)
+        if m:
+            before = block[:m.start()] # All lines before header
+            after = block[m.end():]    # All lines after header
+            if before:
+                # As the header was not the first line of the block and the
+                # lines before the header must be parsed first,
+                # recursively parse this lines as a block.
+                self.parser.parseBlocks(parent, [before])
+            # Create header using named groups from RE
+            h = markdown.etree.SubElement(parent, 'h%d' % len(m.group('level')))
+            h.text = m.group('header').strip()
+            if after:
+                # Insert remaining lines as first block for future parsing.
+                blocks.insert(0, after)
+        else:
+            # This should never happen, but just in case...
+            message(CRITICAL, "We've got a problem header!")
+
+
+class SetextHeaderProcessor(BlockProcessor):
+    """ Process Setext-style Headers. """
+
+    # Detect Setext-style header. Must be first 2 lines of block.
+    RE = re.compile(r'^.*?\n[=-]{3,}', re.MULTILINE)
+
+    def test(self, parent, block):
+        return bool(self.RE.match(block))
+
+    def run(self, parent, blocks):
+        lines = blocks.pop(0).split('\n')
+        # Determine level. ``=`` is 1 and ``-`` is 2.
+        if lines[1].startswith('='):
+            level = 1
+        else:
+            level = 2
+        h = markdown.etree.SubElement(parent, 'h%d' % level)
+        h.text = lines[0].strip()
+        if len(lines) > 2:
+            # Block contains additional lines. Add to  master blocks for later.
+            blocks.insert(0, '\n'.join(lines[2:]))
+
+
+class HRProcessor(BlockProcessor):
+    """ Process Horizontal Rules. """
+
+    RE = r'[ ]{0,3}(?P<ch>[*_-])[ ]?((?P=ch)[ ]?){2,}[ ]*'
+    # Detect hr on any line of a block.
+    SEARCH_RE = re.compile(r'(^|\n)%s(\n|$)' % RE)
+    # Match a hr on a single line of text.
+    MATCH_RE = re.compile(r'^%s$' % RE)
+
+    def test(self, parent, block):
+        return bool(self.SEARCH_RE.search(block))
+
+    def run(self, parent, blocks):
+        lines = blocks.pop(0).split('\n')
+        prelines = []
+        # Check for lines in block before hr.
+        for line in lines:
+            m = self.MATCH_RE.match(line)
+            if m:
+                break
+            else:
+                prelines.append(line)
+        if len(prelines):
+            # Recursively parse lines before hr so they get parsed first.
+            self.parser.parseBlocks(parent, ['\n'.join(prelines)])
+        # create hr
+        hr = markdown.etree.SubElement(parent, 'hr')
+        # check for lines in block after hr.
+        lines = lines[len(prelines)+1:]
+        if len(lines):
+            # Add lines after hr to master blocks for later parsing.
+            blocks.insert(0, '\n'.join(lines))
+
+
+class EmptyBlockProcessor(BlockProcessor):
+    """ Process blocks and start with an empty line. """
+
+    # Detect a block that only contains whitespace 
+    # or only whitespace on the first line.
+    RE = re.compile(r'^\s*\n')
+
+    def test(self, parent, block):
+        return bool(self.RE.match(block))
+
+    def run(self, parent, blocks):
+        block = blocks.pop(0)
+        m = self.RE.match(block)
+        if m:
+            # Add remaining line to master blocks for later.
+            blocks.insert(0, block[m.end():])
+            sibling = self.lastChild(parent)
+            if sibling and sibling.tag == 'pre' and sibling[0] and \
+                    sibling[0].tag == 'code':
+                # Last block is a codeblock. Append to preserve whitespace.
+                sibling[0].text = markdown.AtomicString('%s/n/n/n' % sibling[0].text )
+
+
+class ParagraphProcessor(BlockProcessor):
+    """ Process Paragraph blocks. """
+
+    def test(self, parent, block):
+        return True
+
+    def run(self, parent, blocks):
+        block = blocks.pop(0)
+        if block.strip():
+            # Not a blank block. Add to parent, otherwise throw it away.
+            if self.parser.state.isstate('list'):
+                # The parent is a tight-list. Append to parent.text
+                if parent.text:
+                    parent.text = '%s\n%s' % (parent.text, block)
+                else:
+                    parent.text = block.lstrip()
+            else:
+                # Create a regular paragraph
+                p = markdown.etree.SubElement(parent, 'p')
+                p.text = block.lstrip()
diff --git a/markdown/commandline.py b/markdown/commandline.py
new file mode 100644 (file)
index 0000000..1eedc6d
--- /dev/null
@@ -0,0 +1,96 @@
+"""
+COMMAND-LINE SPECIFIC STUFF
+=============================================================================
+
+The rest of the code is specifically for handling the case where Python
+Markdown is called from the command line.
+"""
+
+import markdown
+import sys
+import logging
+from logging import DEBUG, INFO, WARN, ERROR, CRITICAL
+
+EXECUTABLE_NAME_FOR_USAGE = "python markdown.py"
+""" The name used in the usage statement displayed for python versions < 2.3.
+(With python 2.3 and higher the usage statement is generated by optparse
+and uses the actual name of the executable called.) """
+
+OPTPARSE_WARNING = """
+Python 2.3 or higher required for advanced command line options.
+For lower versions of Python use:
+
+      %s INPUT_FILE > OUTPUT_FILE
+
+""" % EXECUTABLE_NAME_FOR_USAGE
+
+def parse_options():
+    """
+    Define and parse `optparse` options for command-line usage.
+    """
+
+    try:
+        optparse = __import__("optparse")
+    except:
+        if len(sys.argv) == 2:
+            return {'input': sys.argv[1],
+                    'output': None,
+                    'safe': False,
+                    'extensions': [],
+                    'encoding': None }, CRITICAL
+        else:
+            print OPTPARSE_WARNING
+            return None, None
+
+    parser = optparse.OptionParser(usage="%prog INPUTFILE [options]")
+    parser.add_option("-f", "--file", dest="filename", default=sys.stdout,
+                      help="write output to OUTPUT_FILE",
+                      metavar="OUTPUT_FILE")
+    parser.add_option("-e", "--encoding", dest="encoding",
+                      help="encoding for input and output files",)
+    parser.add_option("-q", "--quiet", default = CRITICAL,
+                      action="store_const", const=CRITICAL+10, dest="verbose",
+                      help="suppress all messages")
+    parser.add_option("-v", "--verbose",
+                      action="store_const", const=INFO, dest="verbose",
+                      help="print info messages")
+    parser.add_option("-s", "--safe", dest="safe", default=False,
+                      metavar="SAFE_MODE",
+                      help="safe mode ('replace', 'remove' or 'escape'  user's HTML tag)")
+    parser.add_option("-o", "--output_format", dest="output_format", 
+                      default='xhtml1', metavar="OUTPUT_FORMAT",
+                      help="Format of output. One of 'xhtml1' (default) or 'html4'.")
+    parser.add_option("--noisy",
+                      action="store_const", const=DEBUG, dest="verbose",
+                      help="print debug messages")
+    parser.add_option("-x", "--extension", action="append", dest="extensions",
+                      help = "load extension EXTENSION", metavar="EXTENSION")
+
+    (options, args) = parser.parse_args()
+
+    if not len(args) == 1:
+        parser.print_help()
+        return None, None
+    else:
+        input_file = args[0]
+
+    if not options.extensions:
+        options.extensions = []
+
+    return {'input': input_file,
+            'output': options.filename,
+            'safe_mode': options.safe,
+            'extensions': options.extensions,
+            'encoding': options.encoding,
+            'output_format': options.output_format}, options.verbose
+
+def run():
+    """Run Markdown from the command line."""
+
+    # Parse options and adjust logging level if necessary
+    options, logging_level = parse_options()
+    if not options: sys.exit(0)
+    if logging_level: logging.getLogger('MARKDOWN').setLevel(logging_level)
+
+    # Run
+    markdown.markdownFromFile(**options)
diff --git a/markdown/etree_loader.py b/markdown/etree_loader.py
new file mode 100644 (file)
index 0000000..e2599b2
--- /dev/null
@@ -0,0 +1,33 @@
+
+from markdown import message, CRITICAL
+import sys
+
+## Import
+def importETree():
+    """Import the best implementation of ElementTree, return a module object."""
+    etree_in_c = None
+    try: # Is it Python 2.5+ with C implemenation of ElementTree installed?
+        import xml.etree.cElementTree as etree_in_c
+    except ImportError:
+        try: # Is it Python 2.5+ with Python implementation of ElementTree?
+            import xml.etree.ElementTree as etree
+        except ImportError:
+            try: # An earlier version of Python with cElementTree installed?
+                import cElementTree as etree_in_c
+            except ImportError:
+                try: # An earlier version of Python with Python ElementTree?
+                    import elementtree.ElementTree as etree
+                except ImportError:
+                    message(CRITICAL, "Failed to import ElementTree")
+                    sys.exit(1)
+    if etree_in_c and etree_in_c.VERSION < "1.0":
+        message(CRITICAL, "For cElementTree version 1.0 or higher is required.")
+        sys.exit(1)
+    elif etree_in_c :
+        return etree_in_c
+    elif etree.VERSION < "1.1":
+        message(CRITICAL, "For ElementTree version 1.1 or higher is required")
+        sys.exit(1)
+    else :
+        return etree
+
diff --git a/markdown/extensions/__init__.py b/markdown/extensions/__init__.py
new file mode 100644 (file)
index 0000000..e69de29
diff --git a/markdown/extensions/abbr.py b/markdown/extensions/abbr.py
new file mode 100644 (file)
index 0000000..783220e
--- /dev/null
@@ -0,0 +1,95 @@
+'''
+Abbreviation Extension for Python-Markdown
+==========================================
+
+This extension adds abbreviation handling to Python-Markdown.
+
+Simple Usage:
+
+    >>> import markdown
+    >>> text = """
+    ... Some text with an ABBR and a REF. Ignore REFERENCE and ref.
+    ...
+    ... *[ABBR]: Abbreviation
+    ... *[REF]: Abbreviation Reference
+    ... """
+    >>> markdown.markdown(text, ['abbr'])
+    u'<p>Some text with an <abbr title="Abbreviation">ABBR</abbr> and a <abbr title="Abbreviation Reference">REF</abbr>. Ignore REFERENCE and ref.</p>'
+
+Copyright 2007-2008
+* [Waylan Limberg](http://achinghead.com/)
+* [Seemant Kulleen](http://www.kulleen.org/)
+       
+
+'''
+
+import markdown, re
+from markdown import etree
+
+# Global Vars
+ABBR_REF_RE = re.compile(r'[*]\[(?P<abbr>[^\]]*)\][ ]?:\s*(?P<title>.*)')
+
+class AbbrExtension(markdown.Extension):
+    """ Abbreviation Extension for Python-Markdown. """
+
+    def extendMarkdown(self, md, md_globals):
+        """ Insert AbbrPreprocessor before ReferencePreprocessor. """
+        md.preprocessors.add('abbr', AbbrPreprocessor(md), '<reference')
+        
+           
+class AbbrPreprocessor(markdown.preprocessors.Preprocessor):
+    """ Abbreviation Preprocessor - parse text for abbr references. """
+
+    def run(self, lines):
+        '''
+        Find and remove all Abbreviation references from the text.
+        Each reference is set as a new AbbrPattern in the markdown instance.
+        
+        '''
+        new_text = []
+        for line in lines:
+            m = ABBR_REF_RE.match(line)
+            if m:
+                abbr = m.group('abbr').strip()
+                title = m.group('title').strip()
+                self.markdown.inlinePatterns['abbr-%s'%abbr] = \
+                    AbbrPattern(self._generate_pattern(abbr), title)
+            else:
+                new_text.append(line)
+        return new_text
+    
+    def _generate_pattern(self, text):
+        '''
+        Given a string, returns an regex pattern to match that string. 
+        
+        'HTML' -> r'(?P<abbr>[H][T][M][L])' 
+        
+        Note: we force each char as a literal match (in brackets) as we don't 
+        know what they will be beforehand.
+
+        '''
+        chars = list(text)
+        for i in range(len(chars)):
+            chars[i] = r'[%s]' % chars[i]
+        return r'(?P<abbr>\b%s\b)' % (r''.join(chars))
+
+
+class AbbrPattern(markdown.inlinepatterns.Pattern):
+    """ Abbreviation inline pattern. """
+
+    def __init__(self, pattern, title):
+        markdown.inlinepatterns.Pattern.__init__(self, pattern)
+        self.title = title
+
+    def handleMatch(self, m):
+        abbr = etree.Element('abbr')
+        abbr.text = m.group('abbr')
+        abbr.set('title', self.title)
+        return abbr
+
+def makeExtension(configs=None):
+    return AbbrExtension(configs=configs)
+
+if __name__ == "__main__":
+    import doctest
+    doctest.testmod()
diff --git a/markdown/extensions/codehilite.py b/markdown/extensions/codehilite.py
new file mode 100644 (file)
index 0000000..c5d496b
--- /dev/null
@@ -0,0 +1,224 @@
+#!/usr/bin/python
+
+"""
+CodeHilite Extension for Python-Markdown
+========================================
+
+Adds code/syntax highlighting to standard Python-Markdown code blocks.
+
+Copyright 2006-2008 [Waylan Limberg](http://achinghead.com/).
+
+Project website: <http://www.freewisdom.org/project/python-markdown/CodeHilite>
+Contact: markdown@freewisdom.org
+License: BSD (see ../docs/LICENSE for details)
+  
+Dependencies:
+* [Python 2.3+](http://python.org/)
+* [Markdown 2.0+](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 = 'some text')
+        >>> html = code.hilite()
+    
+    * src: Source string or any object with a .readline attribute.
+      
+    * linenos: (Boolen) Turn line numbering 'on' or 'off' (off by default).
+
+    * css_class: Set class name of wrapper div ('codehilite' by default).
+      
+    Low Level Usage:
+        >>> code = CodeHilite()
+        >>> code.src = 'some text' # String or anything with a .readline attr.
+        >>> code.linenos = True  # True or False; Turns line numbering on or of.
+        >>> html = code.hilite()
+    
+    """
+
+    def __init__(self, src=None, linenos=False, css_class="codehilite"):
+        self.src = src
+        self.lang = None
+        self.linenos = linenos
+        self.css_class = css_class
+
+    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 self.linenos:
+                txt = self._number(txt)
+            else :
+                txt = '<div class="%s"><pre>%s</pre></div>\n'% \
+                        (self.css_class, 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=self.css_class)
+            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 
+            (?P<lang>[\w+-]*)               # The language 
+            ''',  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 HiliteTreeprocessor(markdown.treeprocessors.Treeprocessor):
+    """ Hilight source code in code blocks. """
+
+    def run(self, root):
+        """ Find code blocks and store in htmlStash. """
+        blocks = root.getiterator('pre')
+        for block in blocks:
+            children = block.getchildren()
+            if len(children) == 1 and children[0].tag == 'code':
+                code = CodeHilite(children[0].text, 
+                            linenos=self.config['force_linenos'][0],
+                            css_class=self.config['css_class'][0])
+                placeholder = self.markdown.htmlStash.store(code.hilite(), 
+                                                            safe=True)
+                # Clear codeblock in etree instance
+                block.clear()
+                # Change to p element which will later 
+                # be removed when inserting raw html
+                block.tag = 'p'
+                block.text = placeholder
+
+
+class CodeHiliteExtension(markdown.Extension):
+    """ Add source code hilighting to markdown codeblocks. """
+
+    def __init__(self, configs):
+        # define default configs
+        self.config = {
+            'force_linenos' : [False, "Force line numbers - Default: False"],
+            'css_class' : ["codehilite", 
+                           "Set class name for wrapper <div> - Default: codehilite"],
+            }
+        
+        # Override defaults with user settings
+        for key, value in configs:
+            self.setConfig(key, value) 
+
+    def extendMarkdown(self, md, md_globals):
+        """ Add HilitePostprocessor to Markdown instance. """
+        hiliter = HiliteTreeprocessor(md)
+        hiliter.config = self.config
+        md.treeprocessors.add("hilite", hiliter, "_begin") 
+
+
+def makeExtension(configs={}):
+  return CodeHiliteExtension(configs=configs)
+
diff --git a/markdown/extensions/def_list.py b/markdown/extensions/def_list.py
new file mode 100644 (file)
index 0000000..73a1c85
--- /dev/null
@@ -0,0 +1,104 @@
+#!/usr/bin/env Python
+"""
+Definition List Extension for Python-Markdown
+=============================================
+
+Added parsing of Definition Lists to Python-Markdown.
+
+A simple example:
+
+    Apple
+    :   Pomaceous fruit of plants of the genus Malus in 
+        the family Rosaceae.
+    :   An american computer company.
+
+    Orange
+    :   The fruit of an evergreen tree of the genus Citrus.
+
+Copyright 2008 - [Waylan Limberg](http://achinghead.com)
+
+"""
+
+import markdown, re
+from markdown import etree
+
+
+class DefListProcessor(markdown.blockprocessors.BlockProcessor):
+    """ Process Definition Lists. """
+
+    RE = re.compile(r'(^|\n)[ ]{0,3}:[ ]{1,3}(.*?)(\n|$)')
+
+    def test(self, parent, block):
+        return bool(self.RE.search(block))
+
+    def run(self, parent, blocks):
+        block = blocks.pop(0)
+        m = self.RE.search(block)
+        terms = [l.strip() for l in block[:m.start()].split('\n') if l.strip()]
+        d, theRest = self.detab(block[m.end():])
+        if d:
+            d = '%s\n%s' % (m.group(2), d)
+        else:
+            d = m.group(2)
+        #import ipdb; ipdb.set_trace()
+        sibling = self.lastChild(parent)
+        if not terms and sibling.tag == 'p':
+            # The previous paragraph contains the terms
+            state = 'looselist'
+            terms = sibling.text.split('\n')
+            parent.remove(sibling)
+            # Aquire new sibling
+            sibling = self.lastChild(parent)
+        else:
+            state = 'list'
+
+        if sibling and sibling.tag == 'dl':
+            # This is another item on an existing list
+            dl = sibling
+            if len(dl) and dl[-1].tag == 'dd' and len(dl[-1]):
+                state = 'looselist'
+        else:
+            # This is a new list
+            dl = etree.SubElement(parent, 'dl')
+        # Add terms
+        for term in terms:
+            dt = etree.SubElement(dl, 'dt')
+            dt.text = term
+        # Add definition
+        self.parser.state.set(state)
+        dd = etree.SubElement(dl, 'dd')
+        self.parser.parseBlocks(dd, [d])
+        self.parser.state.reset()
+
+        if theRest:
+            blocks.insert(0, theRest)
+
+class DefListIndentProcessor(markdown.blockprocessors.ListIndentProcessor):
+    """ Process indented children of definition list items. """
+
+    ITEM_TYPES = ['dd']
+    LIST_TYPES = ['dl']
+
+    def create_item(parent, block):
+        """ Create a new dd and parse the block with it as the parent. """
+        dd = markdown.etree.SubElement(parent, 'dd')
+        self.parser.parseBlocks(dd, [block])
+
+
+class DefListExtension(markdown.Extension):
+    """ Add definition lists to Markdown. """
+
+    def extendMarkdown(self, md, md_globals):
+        """ Add an instance of DefListProcessor to BlockParser. """
+        md.parser.blockprocessors.add('defindent',
+                                      DefListIndentProcessor(md.parser),
+                                      '>indent')
+        md.parser.blockprocessors.add('deflist', 
+                                      DefListProcessor(md.parser),
+                                      '>ulist')
+
+
+def makeExtension(configs={}):
+    return DefListExtension(configs=configs)
+
diff --git a/markdown/extensions/extra.py b/markdown/extensions/extra.py
new file mode 100644 (file)
index 0000000..4a2ffbf
--- /dev/null
@@ -0,0 +1,49 @@
+#!/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/).
+
+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 issue a warning and simply continue 
+without that extension. 
+
+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',
+              'def_list',
+              '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))
diff --git a/markdown/extensions/fenced_code.py b/markdown/extensions/fenced_code.py
new file mode 100644 (file)
index 0000000..307b1dc
--- /dev/null
@@ -0,0 +1,117 @@
+#!/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:</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:</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 = '''
+    ... ~~~~{.python}
+    ... block one
+    ... ~~~~
+    ... 
+    ... ~~~~.html
+    ... <p>block two</p>
+    ... ~~~~'''
+    >>> 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>'
+
+Copyright 2007-2008 [Waylan Limberg](http://achinghead.com/).
+
+Project website: <http://www.freewisdom.org/project/python-markdown/Fenced__Code__Blocks>
+Contact: markdown@freewisdom.org
+
+License: BSD (see ../docs/LICENSE for details) 
+
+Dependencies:
+* [Python 2.3+](http://python.org)
+* [Markdown 2.0+](http://www.freewisdom.org/projects/python-markdown/)
+
+"""
+
+import markdown, re
+
+# Global vars
+FENCED_BLOCK_RE = re.compile( \
+    r'(?P<fence>^~{3,})[ ]*(\{?\.(?P<lang>[a-zA-Z0-9_-]*)\}?)?[ ]*\n(?P<code>.*?)(?P=fence)[ ]*$', 
+    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. """
+
+        md.preprocessors.add('fenced_code_block', 
+                                 FencedBlockPreprocessor(md), 
+                                 "_begin")
+
+
+class FencedBlockPreprocessor(markdown.preprocessors.Preprocessor):
+    
+    def run(self, lines):
+        """ Match and store Fenced Code Blocks in the HtmlStash. """
+        text = "\n".join(lines)
+        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.markdown.htmlStash.store(code, safe=True)
+                text = '%s\n%s\n%s'% (text[:m.start()], placeholder, text[m.end():])
+            else:
+                break
+        return text.split("\n")
+
+    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()
diff --git a/markdown/extensions/footnotes.py b/markdown/extensions/footnotes.py
new file mode 100644 (file)
index 0000000..e1a9cda
--- /dev/null
@@ -0,0 +1,307 @@
+"""
+========================= 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
+
+"""
+
+import re, markdown
+from markdown import etree
+
+FN_BACKLINK_TEXT = "zz1337820767766393qq"
+NBSP_PLACEHOLDER =  "qq3936677670287331zz"
+DEF_RE = re.compile(r'(\ ?\ ?\ ?)\[\^([^\]]*)\]:\s*(.*)')
+TABBED_RE = re.compile(r'((\t)|(    ))(.*)')
+
+class FootnoteExtension(markdown.Extension):
+    """ Footnote Extension. """
+
+    def __init__ (self, configs):
+        """ Setup configs. """
+        self.config = {'PLACE_MARKER':
+                       ["///Footnotes Go Here///",
+                        "The text string that marks where the footnotes go"],
+                       'UNIQUE_IDS':
+                       [False,
+                        "Avoid name collisions across "
+                        "multiple calls to reset()."]}
+
+        for key, value in configs:
+            self.config[key][0] = value
+
+        # In multiple invocations, emit links that don't get tangled.
+        self.unique_prefix = 0
+
+        self.reset()
+
+    def extendMarkdown(self, md, md_globals):
+        """ Add pieces to Markdown. """
+        md.registerExtension(self)
+        self.parser = md.parser
+        # Insert a preprocessor before ReferencePreprocessor
+        md.preprocessors.add("footnote", FootnotePreprocessor(self),
+                             "<reference")
+        # Insert an inline pattern before ImageReferencePattern
+        FOOTNOTE_RE = r'\[\^([^\]]*)\]' # blah blah [^1] blah
+        md.inlinePatterns.add("footnote", FootnotePattern(FOOTNOTE_RE, self),
+                              "<reference")
+        # Insert a tree-processor that would actually add the footnote div
+        # This must be before the inline treeprocessor so inline patterns
+        # run on the contents of the div.
+        md.treeprocessors.add("footnote", FootnoteTreeprocessor(self),
+                                 "<inline")
+        # Insert a postprocessor after amp_substitute oricessor
+        md.postprocessors.add("footnote", FootnotePostprocessor(self),
+                                  ">amp_substitute")
+
+    def reset(self):
+        """ Clear the footnotes on reset, and prepare for a distinct document. """
+        self.footnotes = markdown.odict.OrderedDict()
+        self.unique_prefix += 1
+
+    def findFootnotesPlaceholder(self, root):
+        """ Return ElementTree Element that contains Footnote placeholder. """
+        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):
+        """ Store a footnote for later retrieval. """
+        self.footnotes[id] = text
+
+    def makeFootnoteId(self, id):
+        """ Return footnote link id. """
+        if self.getConfig("UNIQUE_IDS"):
+            return 'fn:%d-%s' % (self.unique_prefix, id)
+        else:
+            return 'fn:%s' % id
+
+    def makeFootnoteRefId(self, id):
+        """ Return footnote back-link id. """
+        if self.getConfig("UNIQUE_IDS"):
+            return 'fnref:%d-%s' % (self.unique_prefix, id)
+        else:
+            return 'fnref:%s' % id
+
+    def makeFootnotesDiv(self, root):
+        """ Return div of footnotes as et 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")
+
+        for id in self.footnotes.keys():
+            li = etree.SubElement(ol, "li")
+            li.set("id", self.makeFootnoteId(id))
+            self.parser.parseChunk(li, self.footnotes[id])
+            backlink = etree.Element("a")
+            backlink.set("href", "#" + self.makeFootnoteRefId(id))
+            backlink.set("rev", "footnote")
+            backlink.set("title", "Jump back to footnote %d in the text" % \
+                            (self.footnotes.index(id)+1))
+            backlink.text = FN_BACKLINK_TEXT
+
+            if li.getchildren():
+                node = li[-1]
+                if node.tag == "p":
+                    node.text = node.text + NBSP_PLACEHOLDER
+                    node.append(backlink)
+                else:
+                    p = etree.SubElement(li, "p")
+                    p.append(backlink)
+        return div
+
+
+class FootnotePreprocessor(markdown.preprocessors.Preprocessor):
+    """ Find all footnote references and store for later use. """
+
+    def __init__ (self, footnotes):
+        self.footnotes = footnotes
+
+    def run(self, lines):
+        lines = self._handleFootnoteDefinitions(lines)
+        text = "\n".join(lines)
+        return text.split("\n")
+
+    def _handleFootnoteDefinitions(self, lines):
+        """
+        Recursively find all footnote definitions in lines.
+
+        Keywords:
+
+        * lines: A list of lines of text
+        
+        Return: A list of lines with footnote definitions removed.
+        
+        """
+        i, id, footnote = self._findFootnoteDefinition(lines)
+
+        if id :
+            plain = lines[:i]
+            detabbed, theRest = self.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):
+        """
+        Find the parts of a footnote definition.
+
+        Keywords:
+
+        * lines: A list of lines of text.
+
+        Return: A three item tuple containing the index of the first line of a
+        footnote definition, the id of the definition and the body of the 
+        definition.
+        
+        """
+        counter = 0
+        for line in lines:
+            m = DEF_RE.match(line)
+            if m:
+                return counter, m.group(2), m.group(3)
+            counter += 1
+        return counter, None, None
+
+    def detectTabbed(self, lines):
+        """ Find indented text and remove indent before further proccesing.
+
+        Keyword arguments:
+
+        * lines: an array of strings
+
+        Returns: a list of post processed items and the unused
+        remainder of the original list
+
+        """
+        items = []
+        item = -1
+        i = 0 # to keep track of where we are
+
+        def detab(line):
+            match = TABBED_RE.match(line)
+            if match:
+               return match.group(4)
+
+        for line in lines:
+            if line.strip(): # Non-blank line
+                line = detab(line)
+                if line:
+                    items.append(line)
+                    i += 1
+                    continue
+                else:
+                    return items, lines[i:]
+
+            else: # Blank line: _maybe_ we are done.
+                i += 1 # advance
+
+                # Find the next non-blank line
+                for j in range(i, len(lines)):
+                    if lines[j].strip():
+                        next_line = lines[j]; break
+                else:
+                    break # There is no more text; we are done.
+
+                # Check if the next non-blank line is tabbed
+                if detab(next_line): # Yes, more work to do.
+                    items.append("")
+                    continue
+                else:
+                    break # No, we are done.
+        else:
+            i += 1
+
+        return items, lines[i:]
+
+
+class FootnotePattern(markdown.inlinepatterns.Pattern):
+    """ InlinePattern for footnote markers in a document's body text. """
+
+    def __init__(self, pattern, footnotes):
+        markdown.inlinepatterns.Pattern.__init__(self, pattern)
+        self.footnotes = footnotes
+
+    def handleMatch(self, m):
+        sup = etree.Element("sup")
+        a = etree.SubElement(sup, "a")
+        id = m.group(2)
+        sup.set('id', self.footnotes.makeFootnoteRefId(id))
+        a.set('href', '#' + self.footnotes.makeFootnoteId(id))
+        a.set('rel', 'footnote')
+        a.text = str(self.footnotes.footnotes.index(id) + 1)
+        return sup
+
+
+class FootnoteTreeprocessor(markdown.treeprocessors.Treeprocessor):
+    """ Build and append footnote div to end of document. """
+
+    def __init__ (self, footnotes):
+        self.footnotes = footnotes
+
+    def run(self, root):
+        footnotesDiv = self.footnotes.makeFootnotesDiv(root)
+        if footnotesDiv:
+            result = self.footnotes.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 FootnotePostprocessor(markdown.postprocessors.Postprocessor):
+    """ Replace placeholders with html entities. """
+
+    def run(self, text):
+        text = text.replace(FN_BACKLINK_TEXT, "&#8617;")
+        return text.replace(NBSP_PLACEHOLDER, "&#160;")
+
+def makeExtension(configs=[]):
+    """ Return an instance of the FootnoteExtension """
+    return FootnoteExtension(configs=configs)
+
diff --git a/markdown/extensions/headerid.py b/markdown/extensions/headerid.py
new file mode 100644 (file)
index 0000000..f70a7a9
--- /dev/null
@@ -0,0 +1,195 @@
+#!/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<h1 id="header_1">Another Header</h1>\\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<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<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>'
+
+Copyright 2007-2008 [Waylan Limberg](http://achinghead.com/).
+
+Project website: <http://www.freewisdom.org/project/python-markdown/HeaderId>
+Contact: markdown@freewisdom.org
+
+License: BSD (see ../docs/LICENSE for details) 
+
+Dependencies:
+* [Python 2.3+](http://python.org)
+* [Markdown 2.0+](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 + '-_'
+IDCOUNT_RE = re.compile(r'^(.*)_([0-9]+)$')
+
+
+class HeaderIdProcessor(markdown.blockprocessors.BlockProcessor):
+    """ Replacement BlockProcessor for Header IDs. """
+
+    # Detect a header at start of any line in block
+    RE = re.compile(r"""(^|\n)
+                        (?P<level>\#{1,6})  # group('level') = string of hashes
+                        (?P<header>.*?)     # group('header') = Header text
+                        \#*                 # optional closing hashes
+                        (?:[ \t]*\{[ \t]*\#(?P<id>[-_:a-zA-Z0-9]+)[ \t]*\})?
+                        (\n|$)              #  ^^ group('id') = id attribute
+                     """,
+                     re.VERBOSE)
+
+    IDs = []
+
+    def test(self, parent, block):
+        return bool(self.RE.search(block))
+
+    def run(self, parent, blocks):
+        block = blocks.pop(0)
+        m = self.RE.search(block)
+        if m:
+            before = block[:m.start()] # All lines before header
+            after = block[m.end():]    # All lines after header
+            if before:
+                # As the header was not the first line of the block and the
+                # lines before the header must be parsed first,
+                # recursively parse this lines as a block.
+                self.parser.parseBlocks(parent, [before])
+            # Create header using named groups from RE
+            start_level, force_id = self._get_meta()
+            level = len(m.group('level')) + start_level
+            if level > 6: 
+                level = 6
+            h = markdown.etree.SubElement(parent, 'h%d' % level)
+            h.text = m.group('header').strip()
+            if m.group('id'):
+                h.set('id', self._unique_id(m.group('id')))
+            elif force_id:
+                h.set('id', self._create_id(m.group('header').strip()))
+            if after:
+                # Insert remaining lines as first block for future parsing.
+                blocks.insert(0, after)
+        else:
+            # This should never happen, but just in case...
+            message(CRITICAL, "We've got a problem header!")
+
+    def _get_meta(self):
+        """ Return meta data suported by this ext as a tuple """
+        level = int(self.config['level'][0]) - 1
+        force = self._str2bool(self.config['forceid'][0])
+        if hasattr(self.md, 'Meta'):
+            if self.md.Meta.has_key('header_level'):
+                level = int(self.md.Meta['header_level'][0]) - 1
+            if self.md.Meta.has_key('header_forceid'): 
+                force = self._str2bool(self.md.Meta['header_forceid'][0])
+        return level, force
+
+    def _str2bool(self, 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(self, id):
+        """ Ensure ID is unique. Append '_1', '_2'... if not """
+        while id in self.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)
+        self.IDs.append(id)
+        return id
+
+    def _create_id(self, 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 self._unique_id(h)
+
+
+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.registerExtension(self)
+        self.processor = HeaderIdProcessor(md.parser)
+        self.processor.md = md
+        self.processor.config = self.config
+        # Replace existing hasheader in place.
+        md.parser.blockprocessors['hashheader'] = self.processor
+
+    def reset(self):
+        self.processor.IDs = []
+
+
+def makeExtension(configs=None):
+    return HeaderIdExtension(configs=configs)
+
+if __name__ == "__main__":
+    import doctest
+    doctest.testmod()
+
diff --git a/markdown/extensions/html_tidy.py b/markdown/extensions/html_tidy.py
new file mode 100644 (file)
index 0000000..5105e33
--- /dev/null
@@ -0,0 +1,62 @@
+#!/usr/bin/env python
+
+"""
+HTML Tidy Extension for Python-Markdown
+=======================================
+
+Runs [HTML Tidy][] on the output of Python-Markdown using the [uTidylib][] 
+Python wrapper. Both libtidy and uTidylib must be installed on your system.
+
+Note than any Tidy [options][] can be passed in as extension configs. So, 
+for example, to output HTML rather than XHTML, set ``output_xhtml=0``. To
+indent the output, set ``indent=auto`` and to have Tidy wrap the output in 
+``<html>`` and ``<body>`` tags, set ``show_body_only=0``.
+
+[HTML Tidy]: http://tidy.sourceforge.net/
+[uTidylib]: http://utidylib.berlios.de/
+[options]: http://tidy.sourceforge.net/docs/quickref.html
+
+Copyright (c)2008 [Waylan Limberg](http://achinghead.com)
+
+License: [BSD](http://www.opensource.org/licenses/bsd-license.php) 
+
+Dependencies:
+* [Python2.3+](http://python.org)
+* [Markdown 2.0+](http://www.freewisdom.org/projects/python-markdown/)
+* [HTML Tidy](http://utidylib.berlios.de/)
+* [uTidylib](http://utidylib.berlios.de/)
+
+"""
+
+import markdown
+import tidy
+
+class TidyExtension(markdown.Extension):
+
+    def __init__(self, configs):
+        # Set defaults to match typical markdown behavior.
+        self.config = dict(output_xhtml=1,
+                           show_body_only=1,
+                          )
+        # Merge in user defined configs overriding any present if nessecary.
+        for c in configs:
+            self.config[c[0]] = c[1]
+
+    def extendMarkdown(self, md, md_globals):
+        # Save options to markdown instance
+        md.tidy_options = self.config
+        # Add TidyProcessor to postprocessors
+        md.postprocessors['tidy'] = TidyProcessor(md)
+
+
+class TidyProcessor(markdown.postprocessors.Postprocessor):
+
+    def run(self, text):
+        # Pass text to Tidy. As Tidy does not accept unicode we need to encode
+        # it and decode its return value.
+        return unicode(tidy.parseString(text.encode('utf-8'), 
+                                        **self.markdown.tidy_options)) 
+
+
+def makeExtension(configs=None):
+    return TidyExtension(configs=configs)
diff --git a/markdown/extensions/imagelinks.py b/markdown/extensions/imagelinks.py
new file mode 100644 (file)
index 0000000..ee0b708
--- /dev/null
@@ -0,0 +1,119 @@
+"""
+========================= 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 extendMarkdown(self, md, md_globals):
+
+        md.preprocessors.add("imagelink", ImageLinkPreprocessor(md), "_begin")
+
+
+class ImageLinkPreprocessor(markdown.preprocessors.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)
+
diff --git a/markdown/extensions/meta.py b/markdown/extensions/meta.py
new file mode 100644 (file)
index 0000000..1b555b2
--- /dev/null
@@ -0,0 +1,90 @@
+#!usr/bin/python
+
+"""
+Meta Data Extension for Python-Markdown
+=======================================
+
+This extension adds Meta Data handling to markdown.
+
+Basic Usage:
+
+    >>> import markdown
+    >>> text = '''Title: A Test Doc.
+    ... Author: Waylan Limberg
+    ...         John Doe
+    ... Blank_Data:
+    ...
+    ... The body. This is paragraph one.
+    ... '''
+    >>> md = markdown.Markdown(['meta'])
+    >>> md.convert(text)
+    u'<p>The body. This is paragraph one.</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(['meta'])
+    >>> md.convert(text)
+    u'<pre><code>Some Code - not extra lines of meta data.\\n</code></pre>'
+    >>> md.Meta
+    {}
+
+Copyright 2007-2008 [Waylan Limberg](http://achinghead.com).
+
+Project website: <http://www.freewisdom.org/project/python-markdown/Meta-Data>
+Contact: markdown@freewisdom.org
+
+License: BSD (see ../docs/LICENSE for details)
+
+"""
+
+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):
+    """ Meta-Data extension for Python-Markdown. """
+
+    def extendMarkdown(self, md, md_globals):
+        """ Add MetaPreprocessor to Markdown instance. """
+
+        md.preprocessors.add("meta", MetaPreprocessor(md), "_begin")
+
+
+class MetaPreprocessor(markdown.preprocessors.Preprocessor):
+    """ Get Meta-Data. """
+
+    def run(self, lines):
+        """ Parse Meta-Data and store in Markdown.Meta. """
+        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.markdown.Meta = meta
+        return lines
+        
+
+def makeExtension(configs={}):
+    return MetaExtension(configs=configs)
+
+if __name__ == "__main__":
+    import doctest
+    doctest.testmod()
diff --git a/markdown/extensions/rss.py b/markdown/extensions/rss.py
new file mode 100644 (file)
index 0000000..1274da2
--- /dev/null
@@ -0,0 +1,114 @@
+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 tree-processor that would actually add the title tag
+        treeprocessor = RssTreeProcessor(md)
+        treeprocessor.ext = self
+        md.treeprocessors['rss'] = treeprocessor
+        md.stripTopLevelTags = 0
+        md.docType = '<?xml version="1.0" encoding="utf-8"?>\n'
+
+class RssTreeProcessor(markdown.treeprocessors.Treeprocessor):
+
+    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"]:
+                try:
+                    description = etree.SubElement(item, "description")
+                except UnboundLocalError:
+                    # Item not defined - moving on
+                    pass
+                else:
+                    if len(child):
+                        content = "\n".join([etree.tostring(node)
+                                             for node in child])
+                    else:
+                        content = child.text
+                    pholder = self.markdown.htmlStash.store(
+                                                "<![CDATA[ %s]]>" % content)
+                    description.text = pholder
+    
+        return rss
+
+
+def makeExtension(configs):
+
+    return RssExtension(configs)
diff --git a/markdown/extensions/tables.py b/markdown/extensions/tables.py
new file mode 100644 (file)
index 0000000..1d3c920
--- /dev/null
@@ -0,0 +1,97 @@
+#!/usr/bin/env Python
+"""
+Tables Extension for Python-Markdown
+====================================
+
+Added parsing of tables to Python-Markdown.
+
+A simple example:
+
+    First Header  | Second Header
+    ------------- | -------------
+    Content Cell  | Content Cell
+    Content Cell  | Content Cell
+
+Copyright 2009 - [Waylan Limberg](http://achinghead.com)
+"""
+import markdown
+from markdown import etree
+
+
+class TableProcessor(markdown.blockprocessors.BlockProcessor):
+    """ Process Tables. """
+
+    def test(self, parent, block):
+        rows = block.split('\n')
+        return (len(rows) > 2 and '|' in rows[0] and 
+                '|' in rows[1] and '-' in rows[1] and 
+                rows[1][0] in ['|', ':', '-'])
+
+    def run(self, parent, blocks):
+        """ Parse a table block and build table. """
+        block = blocks.pop(0).split('\n')
+        header = block[:2]
+        rows = block[2:]
+        # Get format type (bordered by pipes or not)
+        border = False
+        if header[0].startswith('|'):
+            border = True
+        # Get alignment of columns
+        align = []
+        for c in self._split_row(header[1], border):
+            if c.startswith(':') and c.endswith(':'):
+                align.append('center')
+            elif c.startswith(':'):
+                align.append('left')
+            elif c.endswith(':'):
+                align.append('right')
+            else:
+                align.append(None)
+        # Build table
+        table = etree.SubElement(parent, 'table')
+        thead = etree.SubElement(table, 'thead')
+        self._build_row(header[0], thead, align, border)
+        tbody = etree.SubElement(table, 'tbody')
+        for row in rows:
+            self._build_row(row, tbody, align, border)
+
+    def _build_row(self, row, parent, align, border):
+        """ Given a row of text, build table cells. """
+        tr = etree.SubElement(parent, 'tr')
+        tag = 'td'
+        if parent.tag == 'thead':
+            tag = 'th'
+        cells = self._split_row(row, border)
+        # We use align here rather than cells to ensure every row 
+        # contains the same number of columns.
+        for i, a in enumerate(align):
+            c = etree.SubElement(tr, tag)
+            try:
+                c.text = cells[i].strip()
+            except IndexError:
+                c.text = ""
+            if a:
+                c.set('align', a)
+
+    def _split_row(self, row, border):
+        """ split a row of text into list of cells. """
+        if border:
+            if row.startswith('|'):
+                row = row[1:]
+            if row.endswith('|'):
+                row = row[:-1]
+        return row.split('|')
+
+
+class TableExtension(markdown.Extension):
+    """ Add tables to Markdown. """
+
+    def extendMarkdown(self, md, md_globals):
+        """ Add an instance of TableProcessor to BlockParser. """
+        md.parser.blockprocessors.add('table', 
+                                      TableProcessor(md.parser),
+                                      '<hashheader')
+
+
+def makeExtension(configs={}):
+    return TableExtension(configs=configs)
diff --git a/markdown/extensions/toc.py b/markdown/extensions/toc.py
new file mode 100644 (file)
index 0000000..1d9489c
--- /dev/null
@@ -0,0 +1,136 @@
+"""
+Table of Contents Extension for Python-Markdown
+* * *
+
+(c) 2008 [Jack Miller](http://codezen.org)
+
+Dependencies:
+* [Markdown 2.0+](http://www.freewisdom.org/projects/python-markdown/)
+
+"""
+import markdown
+from markdown import etree
+import re
+
+class TocTreeprocessor(markdown.treeprocessors.Treeprocessor):
+    # Iterator wrapper to get parent and child all at once
+    def iterparent(self, root):
+        for parent in root.getiterator():
+            for child in parent:
+                yield parent, child
+
+    def run(self, doc):
+        div = etree.Element("div")
+        div.attrib["class"] = "toc"
+        last_li = None
+
+        # Add title to the div
+        if self.config["title"][0]:
+            header = etree.SubElement(div, "span")
+            header.attrib["class"] = "toctitle"
+            header.text = self.config["title"][0]
+
+        level = 0
+        list_stack=[div]
+        header_rgx = re.compile("[Hh][123456]")
+
+        # Get a list of id attributes
+        used_ids = []
+        for c in doc.getiterator():
+            if "id" in c.attrib:
+                used_ids.append(c.attrib["id"])
+
+        for (p, c) in self.iterparent(doc):
+            if not c.text:
+                continue
+
+            # To keep the output from screwing up the
+            # validation by putting a <div> inside of a <p>
+            # we actually replace the <p> in its entirety.
+            # We do not allow the marker inside a header as that
+            # would causes an enless loop of placing a new TOC 
+            # inside previously generated TOC.
+
+            if c.text.find(self.config["marker"][0]) > -1 and not header_rgx.match(c.tag):
+                for i in range(len(p)):
+                    if p[i] == c:
+                        p[i] = div
+                        break
+                    
+            if header_rgx.match(c.tag):
+                tag_level = int(c.tag[-1])
+                
+                while tag_level < level:
+                    list_stack.pop()
+                    level -= 1
+
+                if tag_level > level:
+                    newlist = etree.Element("ul")
+                    if last_li:
+                        last_li.append(newlist)
+                    else:
+                        list_stack[-1].append(newlist)
+                    list_stack.append(newlist)
+                    level += 1
+
+                # Do not override pre-existing ids 
+                if not "id" in c.attrib:
+                    id = self.config["slugify"][0](c.text)
+                    if id in used_ids:
+                        ctr = 1
+                        while "%s_%d" % (id, ctr) in used_ids:
+                            ctr += 1
+                        id = "%s_%d" % (id, ctr)
+                    used_ids.append(id)
+                    c.attrib["id"] = id
+                else:
+                    id = c.attrib["id"]
+
+                # List item link, to be inserted into the toc div
+                last_li = etree.Element("li")
+                link = etree.SubElement(last_li, "a")
+                link.text = c.text
+                link.attrib["href"] = '#' + id
+
+                if int(self.config["anchorlink"][0]):
+                    anchor = etree.SubElement(c, "a")
+                    anchor.text = c.text
+                    anchor.attrib["href"] = "#" + id
+                    anchor.attrib["class"] = "toclink"
+                    c.text = ""
+
+                list_stack[-1].append(last_li)
+
+class TocExtension(markdown.Extension):
+    def __init__(self, configs):
+        self.config = { "marker" : ["[TOC]", 
+                            "Text to find and replace with Table of Contents -"
+                            "Defaults to \"[TOC]\""],
+                        "slugify" : [self.slugify,
+                            "Function to generate anchors based on header text-"
+                            "Defaults to a built in slugify function."],
+                        "title" : [None,
+                            "Title to insert into TOC <div> - "
+                            "Defaults to None"],
+                        "anchorlink" : [0,
+                            "1 if header should be a self link"
+                            "Defaults to 0"]}
+
+        for key, value in configs:
+            self.setConfig(key, value)
+
+    # This is exactly the same as Django's slugify
+    def slugify(self, value):
+        """ Slugify a string, to make it URL friendly. """
+        import unicodedata
+        value = unicodedata.normalize('NFKD', value).encode('ascii', 'ignore')
+        value = unicode(re.sub('[^\w\s-]', '', value).strip().lower())
+        return re.sub('[-\s]+','-',value)
+
+    def extendMarkdown(self, md, md_globals):
+        tocext = TocTreeprocessor(md)
+        tocext.config = self.config
+        md.treeprocessors.add("toc", tocext, "_begin")
+       
+def makeExtension(configs={}):
+    return TocExtension(configs=configs)
diff --git a/markdown/extensions/wikilinks.py b/markdown/extensions/wikilinks.py
new file mode 100644 (file)
index 0000000..df44e1c
--- /dev/null
@@ -0,0 +1,155 @@
+#!/usr/bin/env python
+
+'''
+WikiLinks Extension for Python-Markdown
+======================================
+
+Converts [[WikiLinks]] to relative links.  Requires Python-Markdown 2.0+
+
+Basic usage:
+
+    >>> import markdown
+    >>> text = "Some text with a [[WikiLink]]."
+    >>> html = markdown.markdown(text, ['wikilinks'])
+    >>> html
+    u'<p>Some text with a <a class="wikilink" href="/WikiLink/">WikiLink</a>.</p>'
+
+Whitespace behavior:
+
+    >>> markdown.markdown('[[ foo bar_baz ]]', ['wikilinks'])
+    u'<p><a class="wikilink" href="/foo_bar_baz/">foo bar_baz</a></p>'
+    >>> markdown.markdown('foo [[ ]] bar', ['wikilinks'])
+    u'<p>foo  bar</p>'
+
+To define custom settings the simple way:
+
+    >>> markdown.markdown(text, 
+    ...     ['wikilinks(base_url=/wiki/,end_url=.html,html_class=foo)']
+    ... )
+    u'<p>Some text with a <a class="foo" href="/wiki/WikiLink.html">WikiLink</a>.</p>'
+    
+Custom settings the complex way:
+
+    >>> md = markdown.Markdown(
+    ...     extensions = ['wikilinks'], 
+    ...     extension_configs = {'wikilinks': [
+    ...                                 ('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>.</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', 'wikilinks'])
+    >>> md.convert(text)
+    u'<p>Some text with a <a href="http://example.com/WikiLink.html">WikiLink</a>.</p>'
+
+MetaData should not carry over to next document:
+
+    >>> md.convert("No [[MetaData]] here.")
+    u'<p>No <a class="wikilink" href="/MetaData/">MetaData</a> here.</p>'
+
+Define a custom URL builder:
+
+    >>> def my_url_builder(label, base, end):
+    ...     return '/bar/'
+    >>> md = markdown.Markdown(extensions=['wikilinks'], 
+    ...         extension_configs={'wikilinks' : [('build_url', my_url_builder)]})
+    >>> md.convert('[[foo]]')
+    u'<p><a class="wikilink" href="/bar/">foo</a></p>'
+
+From the command line:
+
+    python markdown.py -x wikilinks(base_url=http://example.com/,end_url=.html,html_class=foo) src.txt
+
+By [Waylan Limberg](http://achinghead.com/).
+
+License: [BSD](http://www.opensource.org/licenses/bsd-license.php) 
+
+Dependencies:
+* [Python 2.3+](http://python.org)
+* [Markdown 2.0+](http://www.freewisdom.org/projects/python-markdown/)
+'''
+
+import markdown
+import re
+
+def build_url(label, base, end):
+    """ Build a url from the label, a base, and an end. """
+    clean_label = re.sub(r'([ ]+_)|(_[ ]+)|([ ]+)', '_', label)
+    return '%s%s%s'% (base, clean_label, end)
+
+
+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.'],
+                        'build_url' : [build_url, 'Callable formats URL from label.'],
+        }
+        
+        # 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'\[\[([A-Za-z0-9_ -]+)\]\]'
+        wikilinkPattern = WikiLinks(WIKILINK_RE, self.config)
+        wikilinkPattern.md = md
+        md.inlinePatterns.add('wikilink', wikilinkPattern, "<not_strong")
+
+
+class WikiLinks(markdown.inlinepatterns.Pattern):
+    def __init__(self, pattern, config):
+        markdown.inlinepatterns.Pattern.__init__(self, pattern)
+        self.config = config
+  
+    def handleMatch(self, m):
+        if m.group(2).strip():
+            base_url, end_url, html_class = self._getMeta()
+            label = m.group(2).strip()
+            url = self.config['build_url'][0](label, base_url, end_url)
+            a = markdown.etree.Element('a')
+            a.text = label 
+            a.set('href', url)
+            if html_class:
+                a.set('class', html_class)
+        else:
+            a = ''
+        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 makeExtension(configs=None) :
+    return WikiLinkExtension(configs=configs)
+
+
+if __name__ == "__main__":
+    import doctest
+    doctest.testmod()
+
diff --git a/markdown/html4.py b/markdown/html4.py
new file mode 100644 (file)
index 0000000..08f241d
--- /dev/null
@@ -0,0 +1,274 @@
+# markdown/html4.py
+#
+# Add html4 serialization to older versions of Elementree
+# Taken from ElementTree 1.3 preview with slight modifications
+#
+# Copyright (c) 1999-2007 by Fredrik Lundh.  All rights reserved.
+#
+# fredrik@pythonware.com
+# http://www.pythonware.com
+#
+# --------------------------------------------------------------------
+# The ElementTree toolkit is
+#
+# Copyright (c) 1999-2007 by Fredrik Lundh
+#
+# By obtaining, using, and/or copying this software and/or its
+# associated documentation, you agree that you have read, understood,
+# and will comply with the following terms and conditions:
+#
+# Permission to use, copy, modify, and distribute this software and
+# its associated documentation for any purpose and without fee is
+# hereby granted, provided that the above copyright notice appears in
+# all copies, and that both that copyright notice and this permission
+# notice appear in supporting documentation, and that the name of
+# Secret Labs AB or the author not be used in advertising or publicity
+# pertaining to distribution of the software without specific, written
+# prior permission.
+#
+# SECRET LABS AB AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD
+# TO THIS SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANT-
+# ABILITY AND FITNESS.  IN NO EVENT SHALL SECRET LABS AB OR THE AUTHOR
+# BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY
+# DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS,
+# WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS
+# ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE
+# OF THIS SOFTWARE.
+# --------------------------------------------------------------------
+
+
+import markdown
+ElementTree = markdown.etree.ElementTree
+QName = markdown.etree.QName
+Comment = markdown.etree.Comment
+PI = markdown.etree.PI
+ProcessingInstruction = markdown.etree.ProcessingInstruction
+
+HTML_EMPTY = ("area", "base", "basefont", "br", "col", "frame", "hr",
+              "img", "input", "isindex", "link", "meta" "param")
+
+try:
+    HTML_EMPTY = set(HTML_EMPTY)
+except NameError:
+    pass
+
+_namespace_map = {
+    # "well-known" namespace prefixes
+    "http://www.w3.org/XML/1998/namespace": "xml",
+    "http://www.w3.org/1999/xhtml": "html",
+    "http://www.w3.org/1999/02/22-rdf-syntax-ns#": "rdf",
+    "http://schemas.xmlsoap.org/wsdl/": "wsdl",
+    # xml schema
+    "http://www.w3.org/2001/XMLSchema": "xs",
+    "http://www.w3.org/2001/XMLSchema-instance": "xsi",
+    # dublic core
+    "http://purl.org/dc/elements/1.1/": "dc",
+}
+
+
+def _raise_serialization_error(text):
+    raise TypeError(
+        "cannot serialize %r (type %s)" % (text, type(text).__name__)
+        )
+
+def _encode(text, encoding):
+    try:
+        return text.encode(encoding, "xmlcharrefreplace")
+    except (TypeError, AttributeError):
+        _raise_serialization_error(text)
+
+def _escape_cdata(text, encoding):
+    # escape character data
+    try:
+        # it's worth avoiding do-nothing calls for strings that are
+        # shorter than 500 character, or so.  assume that's, by far,
+        # the most common case in most applications.
+        if "&" in text:
+            text = text.replace("&", "&amp;")
+        if "<" in text:
+            text = text.replace("<", "&lt;")
+        if ">" in text:
+            text = text.replace(">", "&gt;")
+        return text.encode(encoding, "xmlcharrefreplace")
+    except (TypeError, AttributeError):
+        _raise_serialization_error(text)
+
+
+def _escape_attrib(text, encoding):
+    # escape attribute value
+    try:
+        if "&" in text:
+            text = text.replace("&", "&amp;")
+        if "<" in text:
+            text = text.replace("<", "&lt;")
+        if ">" in text:
+            text = text.replace(">", "&gt;")
+        if "\"" in text:
+            text = text.replace("\"", "&quot;")
+        if "\n" in text:
+            text = text.replace("\n", "&#10;")
+        return text.encode(encoding, "xmlcharrefreplace")
+    except (TypeError, AttributeError):
+        _raise_serialization_error(text)
+
+def _escape_attrib_html(text, encoding):
+    # escape attribute value
+    try:
+        if "&" in text:
+            text = text.replace("&", "&amp;")
+        if ">" in text:
+            text = text.replace(">", "&gt;")
+        if "\"" in text:
+            text = text.replace("\"", "&quot;")
+        return text.encode(encoding, "xmlcharrefreplace")
+    except (TypeError, AttributeError):
+        _raise_serialization_error(text)
+
+
+def _serialize_html(write, elem, encoding, qnames, namespaces):
+    tag = elem.tag
+    text = elem.text
+    if tag is Comment:
+        write("<!--%s-->" % _escape_cdata(text, encoding))
+    elif tag is ProcessingInstruction:
+        write("<?%s?>" % _escape_cdata(text, encoding))
+    else:
+        tag = qnames[tag]
+        if tag is None:
+            if text:
+                write(_escape_cdata(text, encoding))
+            for e in elem:
+                _serialize_html(write, e, encoding, qnames, None)
+        else:
+            write("<" + tag)
+            items = elem.items()
+            if items or namespaces:
+                items.sort() # lexical order
+                for k, v in items:
+                    if isinstance(k, QName):
+                        k = k.text
+                    if isinstance(v, QName):
+                        v = qnames[v.text]
+                    else:
+                        v = _escape_attrib_html(v, encoding)
+                    # FIXME: handle boolean attributes
+                    write(" %s=\"%s\"" % (qnames[k], v))
+                if namespaces:
+                    items = namespaces.items()
+                    items.sort(key=lambda x: x[1]) # sort on prefix
+                    for v, k in items:
+                        if k:
+                            k = ":" + k
+                        write(" xmlns%s=\"%s\"" % (
+                            k.encode(encoding),
+                            _escape_attrib(v, encoding)
+                            ))
+            write(">")
+            tag = tag.lower()
+            if text:
+                if tag == "script" or tag == "style":
+                    write(_encode(text, encoding))
+                else:
+                    write(_escape_cdata(text, encoding))
+            for e in elem:
+                _serialize_html(write, e, encoding, qnames, None)
+            if tag not in HTML_EMPTY:
+                write("</" + tag + ">")
+    if elem.tail:
+        write(_escape_cdata(elem.tail, encoding))
+
+def write_html(root, f,
+          # keyword arguments
+          encoding="us-ascii",
+          default_namespace=None):
+    assert root is not None
+    if not hasattr(f, "write"):
+        f = open(f, "wb")
+    write = f.write
+    if not encoding:
+        encoding = "us-ascii"
+    qnames, namespaces = _namespaces(
+            root, encoding, default_namespace
+            )
+    _serialize_html(
+                write, root, encoding, qnames, namespaces
+                )
+
+# --------------------------------------------------------------------
+# serialization support
+
+def _namespaces(elem, encoding, default_namespace=None):
+    # identify namespaces used in this tree
+
+    # maps qnames to *encoded* prefix:local names
+    qnames = {None: None}
+
+    # maps uri:s to prefixes
+    namespaces = {}
+    if default_namespace:
+        namespaces[default_namespace] = ""
+
+    def encode(text):
+        return text.encode(encoding)
+
+    def add_qname(qname):
+        # calculate serialized qname representation
+        try:
+            if qname[:1] == "{":
+                uri, tag = qname[1:].split("}", 1)
+                prefix = namespaces.get(uri)
+                if prefix is None:
+                    prefix = _namespace_map.get(uri)
+                    if prefix is None:
+                        prefix = "ns%d" % len(namespaces)
+                    if prefix != "xml":
+                        namespaces[uri] = prefix
+                if prefix:
+                    qnames[qname] = encode("%s:%s" % (prefix, tag))
+                else:
+                    qnames[qname] = encode(tag) # default element
+            else:
+                if default_namespace:
+                    # FIXME: can this be handled in XML 1.0?
+                    raise ValueError(
+                        "cannot use non-qualified names with "
+                        "default_namespace option"
+                        )
+                qnames[qname] = encode(qname)
+        except TypeError:
+            _raise_serialization_error(qname)
+
+    # populate qname and namespaces table
+    try:
+        iterate = elem.iter
+    except AttributeError:
+        iterate = elem.getiterator # cET compatibility
+    for elem in iterate():
+        tag = elem.tag
+        if isinstance(tag, QName) and tag.text not in qnames:
+            add_qname(tag.text)
+        elif isinstance(tag, basestring):
+            if tag not in qnames:
+                add_qname(tag)
+        elif tag is not None and tag is not Comment and tag is not PI:
+            _raise_serialization_error(tag)
+        for key, value in elem.items():
+            if isinstance(key, QName):
+                key = key.text
+            if key not in qnames:
+                add_qname(key)
+            if isinstance(value, QName) and value.text not in qnames:
+                add_qname(value.text)
+        text = elem.text
+        if isinstance(text, QName) and text.text not in qnames:
+            add_qname(text.text)
+    return qnames, namespaces
+
+def to_html_string(element, encoding=None):
+    class dummy:
+        pass
+    data = []
+    file = dummy()
+    file.write = data.append
+    write_html(ElementTree(element).getroot(),file,encoding)
+    return "".join(data)
diff --git a/markdown/inlinepatterns.py b/markdown/inlinepatterns.py
new file mode 100644 (file)
index 0000000..917a9d3
--- /dev/null
@@ -0,0 +1,371 @@
+"""
+INLINE PATTERNS
+=============================================================================
+
+Inline patterns such as *emphasis* are handled by means of auxiliary
+objects, one per pattern.  Pattern objects must be instances of classes
+that extend markdown.Pattern.  Each pattern object uses a single regular
+expression and needs support the following methods:
+
+    pattern.getCompiledRegExp() # returns a regular expression
+
+    pattern.handleMatch(m) # takes a match object and returns
+                           # an ElementTree element or just plain text
+
+All of python markdown's built-in patterns subclass from Pattern,
+but you can add additional patterns that don't.
+
+Also note that all the regular expressions used by inline must
+capture the whole block.  For this reason, they all start with
+'^(.*)' and end with '(.*)!'.  In case with built-in expression
+Pattern takes care of adding the "^(.*)" and "(.*)!".
+
+Finally, the order in which regular expressions are applied is very
+important - e.g. if we first replace http://.../ links with <a> tags
+and _then_ try to replace inline html, we would end up with a mess.
+So, we apply the expressions in the following order:
+
+* escape and backticks have to go before everything else, so
+  that we can preempt any markdown patterns by escaping them.
+
+* then we handle auto-links (must be done before inline html)
+
+* then we handle inline HTML.  At this point we will simply
+  replace all inline HTML strings with a placeholder and add
+  the actual HTML to a hash.
+
+* then inline images (must be done before links)
+
+* then bracketed links, first regular then reference-style
+
+* finally we apply strong and emphasis
+"""
+
+import markdown
+import re
+from urlparse import urlparse, urlunparse
+import sys
+if sys.version >= "3.0":
+    from html import entities as htmlentitydefs
+else:
+    import htmlentitydefs
+
+"""
+The actual regular expressions for patterns
+-----------------------------------------------------------------------------
+"""
+
+NOBRACKET = r'[^\]\[]*'
+BRK = ( r'\[('
+        + (NOBRACKET + r'(\[')*6
+        + (NOBRACKET+ r'\])*')*6
+        + NOBRACKET + r')\]' )
+NOIMG = r'(?<!\!)'
+
+BACKTICK_RE = r'(?<!\\)(`+)(.+?)(?<!`)\2(?!`)' # `e=f()` or ``e=f("`")``
+ESCAPE_RE = r'\\(.)'                             # \<
+EMPHASIS_RE = r'(\*)([^\*]+)\2'                    # *emphasis*
+STRONG_RE = r'(\*{2}|_{2})(.+?)\2'                      # **strong**
+STRONG_EM_RE = r'(\*{3}|_{3})(.+?)\2'            # ***strong***
+
+if markdown.SMART_EMPHASIS:
+    EMPHASIS_2_RE = r'(?<!\w)(_)(\S.+?)\2(?!\w)'        # _emphasis_
+else:
+    EMPHASIS_2_RE = r'(_)(.+?)\2'                 # _emphasis_
+
+LINK_RE = NOIMG + BRK + \
+r'''\(\s*(<.*?>|((?:(?:\(.*?\))|[^\(\)]))*?)\s*((['"])(.*?)\12)?\)'''
+# [text](url) or [text](<url>)
+
+IMAGE_LINK_RE = r'\!' + BRK + r'\s*\((<.*?>|([^\)]*))\)'
+# ![alttxt](http://x.com/) or ![alttxt](<http://x.com/>)
+REFERENCE_RE = NOIMG + BRK+ r'\s*\[([^\]]*)\]'           # [Google][3]
+IMAGE_REFERENCE_RE = r'\!' + BRK + '\s*\[([^\]]*)\]' # ![alt text][2]
+NOT_STRONG_RE = r'((^| )(\*|_)( |$))'                        # stand-alone * or _
+AUTOLINK_RE = r'<((?:f|ht)tps?://[^>]*)>'        # <http://www.123.com>
+AUTOMAIL_RE = r'<([^> \!]*@[^> ]*)>'               # <me@example.com>
+
+HTML_RE = r'(\<([a-zA-Z/][^\>]*?|\!--.*?--)\>)'               # <...>
+ENTITY_RE = r'(&[\#a-zA-Z0-9]*;)'               # &amp;
+LINE_BREAK_RE = r'  \n'                     # two spaces at end of line
+LINE_BREAK_2_RE = r'  $'                    # two spaces at end of text
+
+
+def dequote(string):
+    """Remove quotes from around a string."""
+    if ( ( string.startswith('"') and string.endswith('"'))
+         or (string.startswith("'") and string.endswith("'")) ):
+        return string[1:-1]
+    else:
+        return string
+
+ATTR_RE = re.compile("\{@([^\}]*)=([^\}]*)}") # {@id=123}
+
+def handleAttributes(text, parent):
+    """Set values of an element based on attribute definitions ({@id=123})."""
+    def attributeCallback(match):
+        parent.set(match.group(1), match.group(2).replace('\n', ' '))
+    return ATTR_RE.sub(attributeCallback, text)
+
+
+"""
+The pattern classes
+-----------------------------------------------------------------------------
+"""
+
+class Pattern:
+    """Base class that inline patterns subclass. """
+
+    def __init__ (self, pattern, markdown_instance=None):
+        """
+        Create an instant of an inline pattern.
+
+        Keyword arguments:
+
+        * pattern: A regular expression that matches a pattern
+
+        """
+        self.pattern = pattern
+        self.compiled_re = re.compile("^(.*?)%s(.*?)$" % pattern, re.DOTALL)
+
+        # Api for Markdown to pass safe_mode into instance
+        self.safe_mode = False
+        if markdown_instance:
+            self.markdown = markdown_instance
+
+    def getCompiledRegExp (self):
+        """ Return a compiled regular expression. """
+        return self.compiled_re
+
+    def handleMatch(self, m):
+        """Return a ElementTree element from the given match.
+
+        Subclasses should override this method.
+
+        Keyword arguments:
+
+        * m: A re match object containing a match of the pattern.
+
+        """
+        pass
+
+    def type(self):
+        """ Return class name, to define pattern type """
+        return self.__class__.__name__
+
+BasePattern = Pattern # for backward compatibility
+
+class SimpleTextPattern (Pattern):
+    """ Return a simple text of group(2) of a Pattern. """
+    def handleMatch(self, m):
+        text = m.group(2)
+        if text == markdown.INLINE_PLACEHOLDER_PREFIX:
+            return None
+        return text
+
+class SimpleTagPattern (Pattern):
+    """
+    Return element of type `tag` with a text attribute of group(3)
+    of a Pattern.
+
+    """
+    def __init__ (self, pattern, tag):
+        Pattern.__init__(self, pattern)
+        self.tag = tag
+
+    def handleMatch(self, m):
+        el = markdown.etree.Element(self.tag)
+        el.text = m.group(3)
+        return el
+
+
+class SubstituteTagPattern (SimpleTagPattern):
+    """ Return a eLement of type `tag` with no children. """
+    def handleMatch (self, m):
+        return markdown.etree.Element(self.tag)
+
+
+class BacktickPattern (Pattern):
+    """ Return a `<code>` element containing the matching text. """
+    def __init__ (self, pattern):
+        Pattern.__init__(self, pattern)
+        self.tag = "code"
+
+    def handleMatch(self, m):
+        el = markdown.etree.Element(self.tag)
+        el.text = markdown.AtomicString(m.group(3).strip())
+        return el
+
+
+class DoubleTagPattern (SimpleTagPattern):
+    """Return a ElementTree element nested in tag2 nested in tag1.
+
+    Useful for strong emphasis etc.
+
+    """
+    def handleMatch(self, m):
+        tag1, tag2 = self.tag.split(",")
+        el1 = markdown.etree.Element(tag1)
+        el2 = markdown.etree.SubElement(el1, tag2)
+        el2.text = m.group(3)
+        return el1
+
+
+class HtmlPattern (Pattern):
+    """ Store raw inline html and return a placeholder. """
+    def handleMatch (self, m):
+        rawhtml = m.group(2)
+        inline = True
+        place_holder = self.markdown.htmlStash.store(rawhtml)
+        return place_holder
+
+
+class LinkPattern (Pattern):
+    """ Return a link element from the given match. """
+    def handleMatch(self, m):
+        el = markdown.etree.Element("a")
+        el.text = m.group(2)
+        title = m.group(11)
+        href = m.group(9)
+
+        if href:
+            if href[0] == "<":
+                href = href[1:-1]
+            el.set("href", self.sanitize_url(href.strip()))
+        else:
+            el.set("href", "")
+
+        if title:
+            title = dequote(title) #.replace('"', "&quot;")
+            el.set("title", title)
+        return el
+
+    def sanitize_url(self, url):
+        """
+        Sanitize a url against xss attacks in "safe_mode".
+
+        Rather than specifically blacklisting `javascript:alert("XSS")` and all
+        its aliases (see <http://ha.ckers.org/xss.html>), we whitelist known
+        safe url formats. Most urls contain a network location, however some
+        are known not to (i.e.: mailto links). Script urls do not contain a
+        location. Additionally, for `javascript:...`, the scheme would be
+        "javascript" but some aliases will appear to `urlparse()` to have no
+        scheme. On top of that relative links (i.e.: "foo/bar.html") have no
+        scheme. Therefore we must check "path", "parameters", "query" and
+        "fragment" for any literal colons. We don't check "scheme" for colons
+        because it *should* never have any and "netloc" must allow the form:
+        `username:password@host:port`.
+
+        """
+        locless_schemes = ['', 'mailto', 'news']
+        scheme, netloc, path, params, query, fragment = url = urlparse(url)
+        safe_url = False
+        if netloc != '' or scheme in locless_schemes:
+            safe_url = True
+
+        for part in url[2:]:
+            if ":" in part:
+                safe_url = False
+
+        if self.markdown.safeMode and not safe_url:
+            return ''
+        else:
+            return urlunparse(url)
+
+class ImagePattern(LinkPattern):
+    """ Return a img element from the given match. """
+    def handleMatch(self, m):
+        el = markdown.etree.Element("img")
+        src_parts = m.group(9).split()
+        if src_parts:
+            src = src_parts[0]
+            if src[0] == "<" and src[-1] == ">":
+                src = src[1:-1]
+            el.set('src', self.sanitize_url(src))
+        else:
+            el.set('src', "")
+        if len(src_parts) > 1:
+            el.set('title', dequote(" ".join(src_parts[1:])))
+
+        if markdown.ENABLE_ATTRIBUTES:
+            truealt = handleAttributes(m.group(2), el)
+        else:
+            truealt = m.group(2)
+
+        el.set('alt', truealt)
+        return el
+
+class ReferencePattern(LinkPattern):
+    """ Match to a stored reference and return link element. """
+    def handleMatch(self, m):
+        if m.group(9):
+            id = m.group(9).lower()
+        else:
+            # if we got something like "[Google][]"
+            # we'll use "google" as the id
+            id = m.group(2).lower()
+
+        if not id in self.markdown.references: # ignore undefined refs
+            return None
+        href, title = self.markdown.references[id]
+
+        text = m.group(2)
+        return self.makeTag(href, title, text)
+
+    def makeTag(self, href, title, text):
+        el = markdown.etree.Element('a')
+
+        el.set('href', self.sanitize_url(href))
+        if title:
+            el.set('title', title)
+
+        el.text = text
+        return el
+
+
+class ImageReferencePattern (ReferencePattern):
+    """ Match to a stored reference and return img element. """
+    def makeTag(self, href, title, text):
+        el = markdown.etree.Element("img")
+        el.set("src", self.sanitize_url(href))
+        if title:
+            el.set("title", title)
+        el.set("alt", text)
+        return el
+
+
+class AutolinkPattern (Pattern):
+    """ Return a link Element given an autolink (`<http://example/com>`). """
+    def handleMatch(self, m):
+        el = markdown.etree.Element("a")
+        el.set('href', m.group(2))
+        el.text = markdown.AtomicString(m.group(2))
+        return el
+
+class AutomailPattern (Pattern):
+    """
+    Return a mailto link Element given an automail link (`<foo@example.com>`).
+    """
+    def handleMatch(self, m):
+        el = markdown.etree.Element('a')
+        email = m.group(2)
+        if email.startswith("mailto:"):
+            email = email[len("mailto:"):]
+
+        def codepoint2name(code):
+            """Return entity definition by code, or the code if not defined."""
+            entity = htmlentitydefs.codepoint2name.get(code)
+            if entity:
+                return "%s%s;" % (markdown.AMP_SUBSTITUTE, entity)
+            else:
+                return "%s#%d;" % (markdown.AMP_SUBSTITUTE, code)
+
+        letters = [codepoint2name(ord(letter)) for letter in email]
+        el.text = markdown.AtomicString(''.join(letters))
+
+        mailto = "mailto:" + email
+        mailto = "".join([markdown.AMP_SUBSTITUTE + '#%d;' %
+                          ord(letter) for letter in mailto])
+        el.set('href', mailto)
+        return el
+
diff --git a/markdown/odict.py b/markdown/odict.py
new file mode 100644 (file)
index 0000000..bf3ef07
--- /dev/null
@@ -0,0 +1,162 @@
+class OrderedDict(dict):
+    """
+    A dictionary that keeps its keys in the order in which they're inserted.
+    
+    Copied from Django's SortedDict with some modifications.
+
+    """
+    def __new__(cls, *args, **kwargs):
+        instance = super(OrderedDict, cls).__new__(cls, *args, **kwargs)
+        instance.keyOrder = []
+        return instance
+
+    def __init__(self, data=None):
+        if data is None:
+            data = {}
+        super(OrderedDict, self).__init__(data)
+        if isinstance(data, dict):
+            self.keyOrder = data.keys()
+        else:
+            self.keyOrder = []
+            for key, value in data:
+                if key not in self.keyOrder:
+                    self.keyOrder.append(key)
+
+    def __deepcopy__(self, memo):
+        from copy import deepcopy
+        return self.__class__([(key, deepcopy(value, memo))
+                               for key, value in self.iteritems()])
+
+    def __setitem__(self, key, value):
+        super(OrderedDict, self).__setitem__(key, value)
+        if key not in self.keyOrder:
+            self.keyOrder.append(key)
+
+    def __delitem__(self, key):
+        super(OrderedDict, self).__delitem__(key)
+        self.keyOrder.remove(key)
+
+    def __iter__(self):
+        for k in self.keyOrder:
+            yield k
+
+    def pop(self, k, *args):
+        result = super(OrderedDict, self).pop(k, *args)
+        try:
+            self.keyOrder.remove(k)
+        except ValueError:
+            # Key wasn't in the dictionary in the first place. No problem.
+            pass
+        return result
+
+    def popitem(self):
+        result = super(OrderedDict, self).popitem()
+        self.keyOrder.remove(result[0])
+        return result
+
+    def items(self):
+        return zip(self.keyOrder, self.values())
+
+    def iteritems(self):
+        for key in self.keyOrder:
+            yield key, super(OrderedDict, self).__getitem__(key)
+
+    def keys(self):
+        return self.keyOrder[:]
+
+    def iterkeys(self):
+        return iter(self.keyOrder)
+
+    def values(self):
+        return [super(OrderedDict, self).__getitem__(k) for k in self.keyOrder]
+
+    def itervalues(self):
+        for key in self.keyOrder:
+            yield super(OrderedDict, self).__getitem__(key)
+
+    def update(self, dict_):
+        for k, v in dict_.items():
+            self.__setitem__(k, v)
+
+    def setdefault(self, key, default):
+        if key not in self.keyOrder:
+            self.keyOrder.append(key)
+        return super(OrderedDict, self).setdefault(key, default)
+
+    def value_for_index(self, index):
+        """Return the value of the item at the given zero-based index."""
+        return self[self.keyOrder[index]]
+
+    def insert(self, index, key, value):
+        """Insert the key, value pair before the item with the given index."""
+        if key in self.keyOrder:
+            n = self.keyOrder.index(key)
+            del self.keyOrder[n]
+            if n < index:
+                index -= 1
+        self.keyOrder.insert(index, key)
+        super(OrderedDict, self).__setitem__(key, value)
+
+    def copy(self):
+        """Return a copy of this object."""
+        # This way of initializing the copy means it works for subclasses, too.
+        obj = self.__class__(self)
+        obj.keyOrder = self.keyOrder[:]
+        return obj
+
+    def __repr__(self):
+        """
+        Replace the normal dict.__repr__ with a version that returns the keys
+        in their sorted order.
+        """
+        return '{%s}' % ', '.join(['%r: %r' % (k, v) for k, v in self.items()])
+
+    def clear(self):
+        super(OrderedDict, self).clear()
+        self.keyOrder = []
+
+    def index(self, key):
+        """ Return the index of a given key. """
+        return self.keyOrder.index(key)
+
+    def index_for_location(self, location):
+        """ Return index or None for a given location. """
+        if location == '_begin':
+            i = 0
+        elif location == '_end':
+            i = None
+        elif location.startswith('<') or location.startswith('>'):
+            i = self.index(location[1:])
+            if location.startswith('>'):
+                if i >= len(self):
+                    # last item
+                    i = None
+                else:
+                    i += 1
+        else:
+            raise ValueError('Not a valid location: "%s". Location key '
+                             'must start with a ">" or "<".' % location)
+        return i
+
+    def add(self, key, value, location):
+        """ Insert by key location. """
+        i = self.index_for_location(location)
+        if i is not None:
+            self.insert(i, key, value)
+        else:
+            self.__setitem__(key, value)
+
+    def link(self, key, location):
+        """ Change location of an existing item. """
+        n = self.keyOrder.index(key)
+        del self.keyOrder[n]
+        i = self.index_for_location(location)
+        try:
+            if i is not None:
+                self.keyOrder.insert(i, key)
+            else:
+                self.keyOrder.append(key)
+        except Error:
+            # restore to prevent data loss and reraise
+            self.keyOrder.insert(n, key)
+            raise Error
diff --git a/markdown/postprocessors.py b/markdown/postprocessors.py
new file mode 100644 (file)
index 0000000..80227bb
--- /dev/null
@@ -0,0 +1,77 @@
+"""
+POST-PROCESSORS
+=============================================================================
+
+Markdown also allows post-processors, which are similar to preprocessors in
+that they need to implement a "run" method. However, they are run after core
+processing.
+
+"""
+
+
+import markdown
+
+class Processor:
+    def __init__(self, markdown_instance=None):
+        if markdown_instance:
+            self.markdown = markdown_instance
+
+class Postprocessor(Processor):
+    """
+    Postprocessors are run after the ElementTree it converted back into text.
+
+    Each Postprocessor implements a "run" method that takes a pointer to a
+    text string, modifies it as necessary and returns a text string.
+
+    Postprocessors must extend markdown.Postprocessor.
+
+    """
+
+    def run(self, text):
+        """
+        Subclasses of Postprocessor should implement a `run` method, which
+        takes the html document as a single text string and returns a
+        (possibly modified) string.
+
+        """
+        pass
+
+
+class RawHtmlPostprocessor(Postprocessor):
+    """ Restore raw html to the document. """
+
+    def run(self, text):
+        """ Iterate over html stash and restore "safe" html. """
+        for i in range(self.markdown.htmlStash.html_counter):
+            html, safe  = self.markdown.htmlStash.rawHtmlBlocks[i]
+            if self.markdown.safeMode and not safe:
+                if str(self.markdown.safeMode).lower() == 'escape':
+                    html = self.escape(html)
+                elif str(self.markdown.safeMode).lower() == 'remove':
+                    html = ''
+                else:
+                    html = markdown.HTML_REMOVED_TEXT
+            if safe or not self.markdown.safeMode:
+                text = text.replace("<p>%s</p>" % 
+                            (markdown.preprocessors.HTML_PLACEHOLDER % i),
+                            html + "\n")
+            text =  text.replace(markdown.preprocessors.HTML_PLACEHOLDER % i, 
+                                 html)
+        return text
+
+    def escape(self, html):
+        """ Basic html escaping """
+        html = html.replace('&', '&amp;')
+        html = html.replace('<', '&lt;')
+        html = html.replace('>', '&gt;')
+        return html.replace('"', '&quot;')
+
+
+class AndSubstitutePostprocessor(Postprocessor):
+    """ Restore valid entities """
+    def __init__(self):
+        pass
+
+    def run(self, text):
+        text =  text.replace(markdown.AMP_SUBSTITUTE, "&")
+        return text
diff --git a/markdown/preprocessors.py b/markdown/preprocessors.py
new file mode 100644 (file)
index 0000000..ef04cab
--- /dev/null
@@ -0,0 +1,215 @@
+
+"""
+PRE-PROCESSORS
+=============================================================================
+
+Preprocessors work on source text before we start doing anything too
+complicated. 
+"""
+
+import re
+import markdown
+
+HTML_PLACEHOLDER_PREFIX = markdown.STX+"wzxhzdk:"
+HTML_PLACEHOLDER = HTML_PLACEHOLDER_PREFIX + "%d" + markdown.ETX
+
+class Processor:
+    def __init__(self, markdown_instance=None):
+        if markdown_instance:
+            self.markdown = markdown_instance
+
+class Preprocessor (Processor):
+    """
+    Preprocessors are run after the text is broken into lines.
+
+    Each preprocessor implements a "run" method that takes a pointer to a
+    list of lines of the document, modifies it as necessary and returns
+    either the same pointer or a pointer to a new list.
+
+    Preprocessors must extend markdown.Preprocessor.
+
+    """
+    def run(self, lines):
+        """
+        Each subclass of Preprocessor should override the `run` method, which
+        takes the document as a list of strings split by newlines and returns
+        the (possibly modified) list of lines.
+
+        """
+        pass
+
+class HtmlStash:
+    """
+    This class is used for stashing HTML objects that we extract
+    in the beginning and replace with place-holders.
+    """
+
+    def __init__ (self):
+        """ Create a HtmlStash. """
+        self.html_counter = 0 # for counting inline html segments
+        self.rawHtmlBlocks=[]
+
+    def store(self, html, safe=False):
+        """
+        Saves an HTML segment for later reinsertion.  Returns a
+        placeholder string that needs to be inserted into the
+        document.
+
+        Keyword arguments:
+
+        * html: an html segment
+        * safe: label an html segment as safe for safemode
+
+        Returns : a placeholder string
+
+        """
+        self.rawHtmlBlocks.append((html, safe))
+        placeholder = HTML_PLACEHOLDER % self.html_counter
+        self.html_counter += 1
+        return placeholder
+
+    def reset(self):
+        self.html_counter = 0
+        self.rawHtmlBlocks = []
+
+
+class HtmlBlockPreprocessor(Preprocessor):
+    """Remove html blocks from the text and store them for later retrieval."""
+
+    right_tag_patterns = ["</%s>", "%s>"]
+
+    def _get_left_tag(self, block):
+        return block[1:].replace(">", " ", 1).split()[0].lower()
+
+    def _get_right_tag(self, left_tag, block):
+        for p in self.right_tag_patterns:
+            tag = p % left_tag
+            i = block.rfind(tag)
+            if i > 2:
+                return tag.lstrip("<").rstrip(">"), i + len(p)-2 + len(left_tag)
+        return block.rstrip()[-len(left_tag)-2:-1].lower(), len(block)
+
+    def _equal_tags(self, left_tag, right_tag):
+        if left_tag == 'div' or left_tag[0] in ['?', '@', '%']: # handle PHP, etc.
+            return True
+        if ("/" + left_tag) == right_tag:
+            return True
+        if (right_tag == "--" and left_tag == "--"):
+            return True
+        elif left_tag == right_tag[1:] \
+            and right_tag[0] != "<":
+            return True
+        else:
+            return False
+
+    def _is_oneliner(self, tag):
+        return (tag in ['hr', 'hr/'])
+
+    def run(self, lines):
+        text = "\n".join(lines)
+        new_blocks = []
+        text = text.split("\n\n")
+        items = []
+        left_tag = ''
+        right_tag = ''
+        in_tag = False # flag
+
+        while text:
+            block = text[0]
+            if block.startswith("\n"):
+                block = block[1:]
+            text = text[1:]
+
+            if block.startswith("\n"):
+                block = block[1:]
+
+            if not in_tag:
+                if block.startswith("<"):
+                    left_tag = self._get_left_tag(block)
+                    right_tag, data_index = self._get_right_tag(left_tag, block)
+
+                    if block[1] == "!":
+                        # is a comment block
+                        left_tag = "--"
+                        right_tag, data_index = self._get_right_tag(left_tag, block)
+                        # keep checking conditions below and maybe just append
+                    
+                    if data_index < len(block) \
+                        and markdown.isBlockLevel(left_tag): 
+                        text.insert(0, block[data_index:])
+                        block = block[:data_index]
+
+                    if not (markdown.isBlockLevel(left_tag) \
+                        or block[1] in ["!", "?", "@", "%"]):
+                        new_blocks.append(block)
+                        continue
+
+                    if self._is_oneliner(left_tag):
+                        new_blocks.append(block.strip())
+                        continue
+
+                    if block.rstrip().endswith(">") \
+                        and self._equal_tags(left_tag, right_tag):
+                        new_blocks.append(
+                            self.markdown.htmlStash.store(block.strip()))
+                        continue
+                    else: #if not block[1] == "!":
+                        # if is block level tag and is not complete
+
+                        if markdown.isBlockLevel(left_tag) or left_tag == "--" \
+                            and not block.rstrip().endswith(">"):
+                            items.append(block.strip())
+                            in_tag = True
+                        else:
+                            new_blocks.append(
+                            self.markdown.htmlStash.store(block.strip()))
+
+                        continue
+
+                new_blocks.append(block)
+
+            else:
+                items.append(block.strip())
+
+                right_tag, data_index = self._get_right_tag(left_tag, block)
+
+                if self._equal_tags(left_tag, right_tag):
+                    # if find closing tag
+                    in_tag = False
+                    new_blocks.append(
+                        self.markdown.htmlStash.store('\n\n'.join(items)))
+                    items = []
+
+        if items:
+            new_blocks.append(self.markdown.htmlStash.store('\n\n'.join(items)))
+            new_blocks.append('\n')
+
+        new_text = "\n\n".join(new_blocks)
+        return new_text.split("\n")
+
+
+class ReferencePreprocessor(Preprocessor):
+    """ Remove reference definitions from text and store for later use. """
+
+    RE = re.compile(r'^(\ ?\ ?\ ?)\[([^\]]*)\]:\s*([^ ]*)(.*)$', re.DOTALL)
+
+    def run (self, lines):
+        new_text = [];
+        for line in lines:
+            m = self.RE.match(line)
+            if m:
+                id = m.group(2).strip().lower()
+                t = m.group(4).strip()  # potential title
+                if not t:
+                    self.markdown.references[id] = (m.group(3), t)
+                elif (len(t) >= 2
+                      and (t[0] == t[-1] == "\""
+                           or t[0] == t[-1] == "\'"
+                           or (t[0] == "(" and t[-1] == ")") ) ):
+                    self.markdown.references[id] = (m.group(3), t[1:-1])
+                else:
+                    new_text.append(line)
+            else:
+                new_text.append(line)
+
+        return new_text #+ "\n"
diff --git a/markdown/treeprocessors.py b/markdown/treeprocessors.py
new file mode 100644 (file)
index 0000000..1dc612a
--- /dev/null
@@ -0,0 +1,329 @@
+import markdown
+import re
+
+def isString(s):
+    """ Check if it's string """
+    return isinstance(s, unicode) or isinstance(s, str)
+
+class Processor:
+    def __init__(self, markdown_instance=None):
+        if markdown_instance:
+            self.markdown = markdown_instance
+
+class Treeprocessor(Processor):
+    """
+    Treeprocessors are run on the ElementTree object before serialization.
+
+    Each Treeprocessor implements a "run" method that takes a pointer to an
+    ElementTree, modifies it as necessary and returns an ElementTree
+    object.
+
+    Treeprocessors must extend markdown.Treeprocessor.
+
+    """
+    def run(self, root):
+        """
+        Subclasses of Treeprocessor should implement a `run` method, which
+        takes a root ElementTree. This method can return another ElementTree 
+        object, and the existing root ElementTree will be replaced, or it can 
+        modify the current tree and return None.
+        """
+        pass
+
+
+class InlineProcessor(Treeprocessor):
+    """
+    A Treeprocessor that traverses a tree, applying inline patterns.
+    """
+
+    def __init__ (self, md):
+        self.__placeholder_prefix = markdown.INLINE_PLACEHOLDER_PREFIX
+        self.__placeholder_suffix = markdown.ETX
+        self.__placeholder_length = 4 + len(self.__placeholder_prefix) \
+                                      + len(self.__placeholder_suffix)
+        self.__placeholder_re = re.compile(markdown.INLINE_PLACEHOLDER % r'([0-9]{4})')
+        self.markdown = md
+
+    def __makePlaceholder(self, type):
+        """ Generate a placeholder """
+        id = "%04d" % len(self.stashed_nodes)
+        hash = markdown.INLINE_PLACEHOLDER % id
+        return hash, id
+
+    def __findPlaceholder(self, data, index):
+        """
+        Extract id from data string, start from index
+
+        Keyword arguments:
+
+        * data: string
+        * index: index, from which we start search
+
+        Returns: placeholder id and string index, after the found placeholder.
+        """
+
+        m = self.__placeholder_re.search(data, index)
+        if m:
+            return m.group(1), m.end()
+        else:
+            return None, index + 1
+
+    def __stashNode(self, node, type):
+        """ Add node to stash """
+        placeholder, id = self.__makePlaceholder(type)
+        self.stashed_nodes[id] = node
+        return placeholder
+
+    def __handleInline(self, data, patternIndex=0):
+        """
+        Process string with inline patterns and replace it
+        with placeholders
+
+        Keyword arguments:
+
+        * data: A line of Markdown text
+        * patternIndex: The index of the inlinePattern to start with
+
+        Returns: String with placeholders.
+
+        """
+        if not isinstance(data, markdown.AtomicString):
+            startIndex = 0
+            while patternIndex < len(self.markdown.inlinePatterns):
+                data, matched, startIndex = self.__applyPattern(
+                    self.markdown.inlinePatterns.value_for_index(patternIndex),
+                    data, patternIndex, startIndex)
+                if not matched:
+                    patternIndex += 1
+        return data
+
+    def __processElementText(self, node, subnode, isText=True):
+        """
+        Process placeholders in Element.text or Element.tail
+        of Elements popped from self.stashed_nodes.
+
+        Keywords arguments:
+
+        * node: parent node
+        * subnode: processing node
+        * isText: bool variable, True - it's text, False - it's tail
+
+        Returns: None
+
+        """
+        if isText:
+            text = subnode.text
+            subnode.text = None
+        else:
+            text = subnode.tail
+            subnode.tail = None
+
+        childResult = self.__processPlaceholders(text, subnode)
+
+        if not isText and node is not subnode:
+            pos = node.getchildren().index(subnode)
+            node.remove(subnode)
+        else:
+            pos = 0
+
+        childResult.reverse()
+        for newChild in childResult:
+            node.insert(pos, newChild)
+
+    def __processPlaceholders(self, data, parent):
+        """
+        Process string with placeholders and generate ElementTree tree.
+
+        Keyword arguments:
+
+        * data: string with placeholders instead of ElementTree elements.
+        * parent: Element, which contains processing inline data
+
+        Returns: list with ElementTree elements with applied inline patterns.
+        """
+        def linkText(text):
+            if text:
+                if result:
+                    if result[-1].tail:
+                        result[-1].tail += text
+                    else:
+                        result[-1].tail = text
+                else:
+                    if parent.text:
+                        parent.text += text
+                    else:
+                        parent.text = text
+
+        result = []
+        strartIndex = 0
+        while data:
+            index = data.find(self.__placeholder_prefix, strartIndex)
+            if index != -1:
+                id, phEndIndex = self.__findPlaceholder(data, index)
+
+                if id in self.stashed_nodes:
+                    node = self.stashed_nodes.get(id)
+
+                    if index > 0:
+                        text = data[strartIndex:index]
+                        linkText(text)
+
+                    if not isString(node): # it's Element
+                        for child in [node] + node.getchildren():
+                            if child.tail:
+                                if child.tail.strip():
+                                    self.__processElementText(node, child, False)
+                            if child.text:
+                                if child.text.strip():
+                                    self.__processElementText(child, child)
+                    else: # it's just a string
+                        linkText(node)
+                        strartIndex = phEndIndex
+                        continue
+
+                    strartIndex = phEndIndex
+                    result.append(node)
+
+                else: # wrong placeholder
+                    end = index + len(prefix)
+                    linkText(data[strartIndex:end])
+                    strartIndex = end
+            else:
+                text = data[strartIndex:]
+                linkText(text)
+                data = ""
+
+        return result
+
+    def __applyPattern(self, pattern, data, patternIndex, startIndex=0):
+        """
+        Check if the line fits the pattern, create the necessary
+        elements, add it to stashed_nodes.
+
+        Keyword arguments:
+
+        * data: the text to be processed
+        * pattern: the pattern to be checked
+        * patternIndex: index of current pattern
+        * startIndex: string index, from which we starting search
+
+        Returns: String with placeholders instead of ElementTree elements.
+
+        """
+        match = pattern.getCompiledRegExp().match(data[startIndex:])
+        leftData = data[:startIndex]
+
+        if not match:
+            return data, False, 0
+
+        node = pattern.handleMatch(match)
+
+        if node is None:
+            return data, True, len(leftData) + match.span(len(match.groups()))[0]
+
+        if not isString(node):
+            if not isinstance(node.text, markdown.AtomicString):
+                # We need to process current node too
+                for child in [node] + node.getchildren():
+                    if not isString(node):
+                        if child.text:
+                            child.text = self.__handleInline(child.text,
+                                                            patternIndex + 1)
+                        if child.tail:
+                            child.tail = self.__handleInline(child.tail,
+                                                            patternIndex)
+
+        placeholder = self.__stashNode(node, pattern.type())
+
+        return "%s%s%s%s" % (leftData,
+                             match.group(1),
+                             placeholder, match.groups()[-1]), True, 0
+
+    def run(self, tree):
+        """Apply inline patterns to a parsed Markdown tree.
+
+        Iterate over ElementTree, find elements with inline tag, apply inline
+        patterns and append newly created Elements to tree.  If you don't
+        want process your data with inline paterns, instead of normal string,
+        use subclass AtomicString:
+
+            node.text = markdown.AtomicString("data won't be processed with inline patterns")
+
+        Arguments:
+
+        * markdownTree: ElementTree object, representing Markdown tree.
+
+        Returns: ElementTree object with applied inline patterns.
+
+        """
+        self.stashed_nodes = {}
+
+        stack = [tree]
+
+        while stack:
+            currElement = stack.pop()
+            insertQueue = []
+            for child in currElement.getchildren():
+                if child.text and not isinstance(child.text, markdown.AtomicString):
+                    text = child.text
+                    child.text = None
+                    lst = self.__processPlaceholders(self.__handleInline(
+                                                    text), child)
+                    stack += lst
+                    insertQueue.append((child, lst))
+
+                if child.getchildren():
+                    stack.append(child)
+
+            for element, lst in insertQueue:
+                if element.text:
+                    element.text = \
+                        markdown.inlinepatterns.handleAttributes(element.text, 
+                                                                 element)
+                i = 0
+                for newChild in lst:
+                    # Processing attributes
+                    if newChild.tail:
+                        newChild.tail = \
+                            markdown.inlinepatterns.handleAttributes(newChild.tail,
+                                                                     element)
+                    if newChild.text:
+                        newChild.text = \
+                            markdown.inlinepatterns.handleAttributes(newChild.text,
+                                                                     newChild)
+                    element.insert(i, newChild)
+                    i += 1
+        return tree
+
+
+class PrettifyTreeprocessor(Treeprocessor):
+    """ Add linebreaks to the html document. """
+
+    def _prettifyETree(self, elem):
+        """ Recursively add linebreaks to ElementTree children. """
+
+        i = "\n"
+        if markdown.isBlockLevel(elem.tag) and elem.tag not in ['code', 'pre']:
+            if (not elem.text or not elem.text.strip()) \
+                    and len(elem) and markdown.isBlockLevel(elem[0].tag):
+                elem.text = i
+            for e in elem:
+                if markdown.isBlockLevel(e.tag):
+                    self._prettifyETree(e)
+            if not elem.tail or not elem.tail.strip():
+                elem.tail = i
+        if not elem.tail or not elem.tail.strip():
+            elem.tail = i
+
+    def run(self, root):
+        """ Add linebreaks to ElementTree root object. """
+
+        self._prettifyETree(root)
+        # Do <br />'s seperately as they are often in the middle of
+        # inline content and missed by _prettifyETree.
+        brs = root.getiterator('br')
+        for br in brs:
+            if not br.tail or not br.tail.strip():
+                br.tail = '\n'
+            else:
+                br.tail = '\n%s' % br.tail
diff --git a/setup.py b/setup.py
new file mode 100755 (executable)
index 0000000..42939d3
--- /dev/null
+++ b/setup.py
@@ -0,0 +1,65 @@
+#!/usr/bin/env python
+
+import sys, os
+from distutils.core import setup
+from distutils.command.install_scripts import install_scripts
+
+version = '2.0.3'
+
+class md_install_scripts(install_scripts):
+    """ Customized install_scripts. Create markdown.bat for win32. """
+    def run(self):
+        install_scripts.run(self)
+
+        if sys.platform == 'win32':
+            try:
+                script_dir = os.path.join(sys.prefix, 'Scripts')
+                script_path = os.path.join(script_dir, 'markdown')
+                bat_str = '@"%s" "%s" %%*' % (sys.executable, script_path)
+                bat_path = os.path.join(self.install_dir, 'markdown.bat')
+                f = file(bat_path, 'w')
+                f.write(bat_str)
+                f.close()
+                print 'Created:', bat_path
+            except Exception, e:
+                print 'ERROR: Unable to create %s: %s' % (bat_path, e)
+
+data = dict(
+    name =          'Markdown',
+    version =       version,
+    url =           'http://www.freewisdom.org/projects/python-markdown',
+    download_url =  'http://pypi.python.org/packages/source/M/Markdown/Markdown-%s.tar.gz' % version,
+    description =   'Python implementation of Markdown.',
+    author =        'Manfred Stienstra and Yuri takhteyev',
+    author_email =  'yuri [at] freewisdom.org',
+    maintainer =    'Waylan Limberg',
+    maintainer_email = 'waylan [at] gmail.com',
+    license =       'BSD License',
+    packages =      ['markdown', 'markdown.extensions'],
+    scripts =       ['bin/markdown'],
+    cmdclass =      {'install_scripts': md_install_scripts},
+    classifiers =   ['Development Status :: 5 - Production/Stable',
+                     'License :: OSI Approved :: BSD License',
+                     'Operating System :: OS Independent',
+                     'Programming Language :: Python',
+                     'Programming Language :: Python :: 2',
+                     'Programming Language :: Python :: 2.3',
+                     'Programming Language :: Python :: 2.4',
+                     'Programming Language :: Python :: 2.5',
+                     'Programming Language :: Python :: 2.6',
+                     'Programming Language :: Python :: 3',
+                     'Programming Language :: Python :: 3.0',
+                     'Topic :: Communications :: Email :: Filters',
+                     'Topic :: Internet :: WWW/HTTP :: Dynamic Content :: CGI Tools/Libraries',
+                     'Topic :: Internet :: WWW/HTTP :: Site Management',
+                     'Topic :: Software Development :: Documentation',
+                     'Topic :: Software Development :: Libraries :: Python Modules',
+                     'Topic :: Text Processing :: Filters',
+                     'Topic :: Text Processing :: Markup :: HTML',
+                    ],
+    ) 
+
+if sys.version[:3] < '2.5':
+    data['install_requires'] = ['elementtree']
+
+setup(**data)