Anonymous avatar Anonymous committed d301d69 Merge

Merge git@gitorious.org:python-markdown/mainline

Comments (0)

Files changed (37)

 *.bak
 *.tmp
 tmp/*
-__init__.py
-markdown_old.py
+build/*
+dist/*

CHANGE_LOG.txt

-PYTHON MARKDOWN CHANGELOG
-=========================
-
-August 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/
-
-README.txt
-README.html
-CHANGE_LOG.txt
 markdown.py
-mdx_codehilite.py
-mdx_fenced_code.py
-mdx_footnotes.py
-mdx_headerid.py
-mdx_imagelinks.py
-mdx_meta.py
-mdx_rss.py
-mdx_tables.py
-mdx_wikilink.py
+markdown_extnesions/__init__.py
+markdown_extensions/codehilite.py
+markdown_extensions/fenced_code.py
+markdown_extensions/footnotes.py
+markdown_extensions/headerid.py
+markdown_extensions/imagelinks.py
+markdown_extensions/meta.py
+markdown_extensions/rss.py
+markdown_extensions/tables.py
+markdown_extensions/wikilink.py
 setup.py
-scripts/pymarkdown.py
+scripts/pymarkdown.py
+docs/README
+docs/README.html
+docs/CHANGE_LOG
+docs/INSTALL
+docs/AUTHORS
+docs/writing_extensions.txt
+

README.html

-<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>Installation</h2>
-<p>To install Python Markdown <a href="http://sourceforge.net/project/showfiles.php?group_id=153041">download</a> the zip file and extract the 
-   files.  If you want to install markdown as a module into your python 
-   tree, run <code>sudo python setup.py install</code> from a directory where you 
-   unzip the files.
-</p>
-
-<h2>Command Line Usage</h2>
-<p>To use markdown.py from the command line, run it as 
-</p>
-<pre><code>python markdown.py &lt;input_file&gt;
-</code></pre><p>or 
-</p>
-<pre><code>python markdown.py &lt;input_file&gt; &gt; &lt;output_file&gt;
-</code></pre><p>For more details, use the <code>-h</code> or <code>--help</code> options from the command line 
-   or read the <a href="http://www.freewisdom.org/projects/python-markdown/Command_Line">Command Line Docs</a> available online.
-</p>
-
-<h2>Using as a Python Module</h2>
-<p>To use markdown as a module:
-</p>
-<pre><code>import markdown
-html = markdown.markdown(your_text_string)
-</code></pre><p>For more details see the <a href="http://www.freewisdom.org/projects/python-markdown/Using_as_a_Module">Module Docs</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://sourceforge.net/tracker/?func=add&amp;group_id=153041&amp;atid=790198">bug tracker</a>.
-</p>
-
-<h2>Credits</h2>
-<ul>
- <li>
-     Most of the code currently in the module was written by <a href="http://www.freewisdom.org">Yuri Takhteyev</a>
-  while procrastinating from his Ph.D.
- </li>
-
- <li>
-     The original version of this script was written by <a href="http://www.dwerg.net/">Manfred Stienstra</a>,
-  who is responsible for about a quarter of the code.
- </li>
-
- <li>
-     Many recent bugs are being fixed by <a href="http://achinghead.com/">Waylan Limberg</a>.
- </li>
-</ul>
-<p>Other contributions:
-</p>
-<ul>
- <li>
-     Daniel Krech provided the setup.py script.
- </li>
-
- <li>
-     G. Clark Haynes submitted a patch for indented lists.
- </li>
-
- <li>
-     Tiago Cogumbreiro submitted an email autolink fix.
- </li>
-
- <li>
-     Sergej Chodarev submitted a patch for treatment of <code>&lt;hr/&gt;</code> tags.
- </li>
-
- <li>
-     Chris Clark submitted a patch to handle <code>&lt;mailto:...&gt;</code> syntax and a reg ex 
-  for &quot;smart&quot; emphasis (ignoring underscores within a word).
- </li>
-
- <li>
-     Steward Midwinter wrote command-line parser and cleaned up comments.
- </li>
-
- <li>
-     Many other people helped by reporting bugs.
- </li>
-</ul>
-
-<h2>License</h2>
-<p>The code is dual-licensed under <a href="http://www.gnu.org/copyleft/gpl.html)">GPL</a> and <a href="http://www.opensource.org/licenses/bsd-license.php">BSD License</a>.  Other
-   licensing arrangements can be discussed.
-</p>

README.txt

-[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
-
-
-Installation
-------------
-
-To install Python Markdown [download][] the zip file and extract the 
-files.  If you want to install markdown as a module into your python 
-tree, run `sudo python setup.py install` from a directory where you 
-unzip the files.
-
-[download]: http://sourceforge.net/project/showfiles.php?group_id=153041
-
-
-Command Line Usage
-------------------
-
-To use markdown.py from the command line, run it as 
-
-    python markdown.py <input_file>
-
-or 
-
-    python markdown.py <input_file> > <output_file>
-
-For more details, use the `-h` or `--help` options from the command line 
-or read the [Command Line Docs][] available online.
-
-[Command Line Docs]: http://www.freewisdom.org/projects/python-markdown/Command_Line
-
-
-
-Using as a Python Module
-------------------------
-
-To use markdown as a module:
-
-    import markdown
-    html = markdown.markdown(your_text_string)
-
-For more details see the [Module Docs][].
-
-[Module Docs]: http://www.freewisdom.org/projects/python-markdown/Using_as_a_Module
-
-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://sourceforge.net/tracker/?func=add&group_id=153041&atid=790198
-
-
-Credits
--------
-
-* Most of the code currently in the module was written by [Yuri Takhteyev][]
-  while procrastinating from his Ph.D.
-* The original version of this script was written by [Manfred Stienstra][],
-  who is responsible for about a quarter of the code.
-* Many recent bugs are being fixed by [Waylan Limberg][].
-
-Other contributions:
-
-* Daniel Krech provided the setup.py script.
-* G. Clark Haynes submitted a patch for indented lists.
-* Tiago Cogumbreiro submitted an email autolink fix.
-* Sergej Chodarev submitted a patch for treatment of `<hr/>` tags.
-* Chris Clark submitted a patch to handle `<mailto:...>` syntax and a reg ex 
-  for "smart" emphasis (ignoring underscores within a word).
-* Steward Midwinter wrote command-line parser and cleaned up comments.
-* Many other people helped by reporting bugs.
-
-[Yuri Takhteyev]: http://www.freewisdom.org
-[Manfred Stienstra]: http://www.dwerg.net/
-[Waylan Limberg]: http://achinghead.com/
-
-
-License
--------
-
-The code is dual-licensed under [GPL][] and [BSD License][].  Other
-licensing arrangements can be discussed.
-
-[GPL]: http://www.gnu.org/copyleft/gpl.html) 
-[BSD License]: http://www.opensource.org/licenses/bsd-license.php
+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.
+
+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.
+
+Jeff Balogh
+Sergej Chodarev
+Chris Clark
+Tiago Cogumbreiro
+G. Clark Haynes
+Daniel Krech
+Steward Midwinter
+Malcolm Tredinnick
+and many others to helped by reporting bugs
+PYTHON MARKDOWN CHANGELOG
+=========================
+
+August 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/
+
+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 install that if you don't already have it and it's available for your
+system.
+
+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>
+
+Doubleclick the file and follow the instructions.
+
+If you preffer 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 commandline script, you need to make sure your
+script directory is on your system path. On a typical Python install on Windows
+the Scripts directory is `C:\Python25\Scripts\`. Adjust according to your 
+system and add that to your system path.
+
+Installing on *nix Sytems
+-------------------------
+
+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
+[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
+
+
+Installation
+------------
+
+To install Python Markdown [download][] the zip file and extract the 
+files.  If you want to install markdown as a module into your python 
+tree, run `sudo python setup.py install` from a directory where you 
+unzip the files.
+
+[download]: http://sourceforge.net/project/showfiles.php?group_id=153041
+
+
+Command Line Usage
+------------------
+
+To use markdown.py from the command line, run it as 
+
+    python markdown.py <input_file>
+
+or 
+
+    python markdown.py <input_file> > <output_file>
+
+For more details, use the `-h` or `--help` options from the command line 
+or read the [Command Line Docs][] available online.
+
+[Command Line Docs]: http://www.freewisdom.org/projects/python-markdown/Command_Line
+
+
+
+Using as a Python Module
+------------------------
+
+To use markdown as a module:
+
+    import markdown
+    html = markdown.markdown(your_text_string)
+
+For more details see the [Module Docs][].
+
+[Module Docs]: http://www.freewisdom.org/projects/python-markdown/Using_as_a_Module
+
+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://sourceforge.net/tracker/?func=add&group_id=153041&atid=790198
+
+
+Credits
+-------
+
+* Most of the code currently in the module was written by [Yuri Takhteyev][]
+  while procrastinating from his Ph.D.
+* The original version of this script was written by [Manfred Stienstra][],
+  who is responsible for about a quarter of the code.
+* Many recent bugs are being fixed by [Waylan Limberg][].
+
+Other contributions:
+
+* Daniel Krech provided the setup.py script.
+* G. Clark Haynes submitted a patch for indented lists.
+* Tiago Cogumbreiro submitted an email autolink fix.
+* Sergej Chodarev submitted a patch for treatment of `<hr/>` tags.
+* Chris Clark submitted a patch to handle `<mailto:...>` syntax and a reg ex 
+  for "smart" emphasis (ignoring underscores within a word).
+* Steward Midwinter wrote command-line parser and cleaned up comments.
+* Many other people helped by reporting bugs.
+
+[Yuri Takhteyev]: http://www.freewisdom.org
+[Manfred Stienstra]: http://www.dwerg.net/
+[Waylan Limberg]: http://achinghead.com/
+
+
+License
+-------
+
+The code is dual-licensed under [GPL][] and [BSD License][].  Other
+licensing arrangements can be discussed.
+
+[GPL]: http://www.gnu.org/copyleft/gpl.html) 
+[BSD License]: http://www.opensource.org/licenses/bsd-license.php
+<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>Installation</h2>
+<p>To install Python Markdown <a href="http://sourceforge.net/project/showfiles.php?group_id=153041">download</a> the zip file and extract the 
+   files.  If you want to install markdown as a module into your python 
+   tree, run <code>sudo python setup.py install</code> from a directory where you 
+   unzip the files.
+</p>
+
+<h2>Command Line Usage</h2>
+<p>To use markdown.py from the command line, run it as 
+</p>
+<pre><code>python markdown.py &lt;input_file&gt;
+</code></pre><p>or 
+</p>
+<pre><code>python markdown.py &lt;input_file&gt; &gt; &lt;output_file&gt;
+</code></pre><p>For more details, use the <code>-h</code> or <code>--help</code> options from the command line 
+   or read the <a href="http://www.freewisdom.org/projects/python-markdown/Command_Line">Command Line Docs</a> available online.
+</p>
+
+<h2>Using as a Python Module</h2>
+<p>To use markdown as a module:
+</p>
+<pre><code>import markdown
+html = markdown.markdown(your_text_string)
+</code></pre><p>For more details see the <a href="http://www.freewisdom.org/projects/python-markdown/Using_as_a_Module">Module Docs</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://sourceforge.net/tracker/?func=add&amp;group_id=153041&amp;atid=790198">bug tracker</a>.
+</p>
+
+<h2>Credits</h2>
+<ul>
+ <li>
+     Most of the code currently in the module was written by <a href="http://www.freewisdom.org">Yuri Takhteyev</a>
+  while procrastinating from his Ph.D.
+ </li>
+
+ <li>
+     The original version of this script was written by <a href="http://www.dwerg.net/">Manfred Stienstra</a>,
+  who is responsible for about a quarter of the code.
+ </li>
+
+ <li>
+     Many recent bugs are being fixed by <a href="http://achinghead.com/">Waylan Limberg</a>.
+ </li>
+</ul>
+<p>Other contributions:
+</p>
+<ul>
+ <li>
+     Daniel Krech provided the setup.py script.
+ </li>
+
+ <li>
+     G. Clark Haynes submitted a patch for indented lists.
+ </li>
+
+ <li>
+     Tiago Cogumbreiro submitted an email autolink fix.
+ </li>
+
+ <li>
+     Sergej Chodarev submitted a patch for treatment of <code>&lt;hr/&gt;</code> tags.
+ </li>
+
+ <li>
+     Chris Clark submitted a patch to handle <code>&lt;mailto:...&gt;</code> syntax and a reg ex 
+  for &quot;smart&quot; emphasis (ignoring underscores within a word).
+ </li>
+
+ <li>
+     Steward Midwinter wrote command-line parser and cleaned up comments.
+ </li>
+
+ <li>
+     Many other people helped by reporting bugs.
+ </li>
+</ul>
+
+<h2>License</h2>
+<p>The code is dual-licensed under <a href="http://www.gnu.org/copyleft/gpl.html)">GPL</a> and <a href="http://www.opensource.org/licenses/bsd-license.php">BSD License</a>.  Other
+   licensing arrangements can be discussed.
+</p>

docs/writing_extensions.txt

+### 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.
+
+As the parser builds an [ElementTree][] object which is later rendered 
+as Unicode text, there are also some helpers provided to make manipulation of 
+the tree easier. Each part of the API is discussed in its respective 
+section below. You may find reading the source of some [[existing extensions]] 
+helpful as well. For example, the [[footnote]] extension uses most of the 
+features documented here.
+
+* [Preprocessors][]
+    * [TextPreprocessors][]
+    * [Line Preprocessors][]
+* [InlinePatterns][]
+* [Postprocessors][]
+    * [ElementTree Postprocessors][]
+    * [TextProstprocessors][]
+* [Working with the ElementTree][]
+* [Integrating your code into Markdown][]
+    * [extendMarkdown][]
+    * [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.
+
+There are two types of preprocessors: [TextPreprocessors][] and 
+[Line Preprocessors][].
+
+<h4 id="textpreprocessors">TextPreprocessors</h4>
+
+TextPreprocessors should inherit from `markdown.TextPreprocessor` and implement
+a `run` method with one argument `text`. The `run` method of each 
+TextPreprocessor will be passed the entire source text as a single Unicode
+string and should either return that single Unicode string, or an altered
+version of it.
+
+For example, a simple TextPreprocessor that normalizes newlines [^1] might look
+like this:
+
+    class NormalizePreprocessor(markdown.TextPreprocessor):
+        def run(self, text):
+            return text.replace("\r\n", "\n").replace("\r", "\n")
+
+[^1]: It should be noted that Markdown already normalizes newlines. This 
+example is for illustrative purposes only.
+
+<h4 id="linepreprocessors">Line Preprocessors</h4>
+
+Line Preprocessors should inherit from `markdown.Preprocessor` and implement 
+a `run` method with one argument `lines`. The `run` method of each Line
+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.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.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.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="postprocessors">Postprocessors</h3>
+
+Postprocessors manipulate a document after it has passed through the Markdown 
+core. This is were stored text gets added back in such as a list of footnotes, 
+a table of contents or raw html.
+
+There are two types of postprocessors: [ElementTree Postprocessors][] and 
+[TextPostprocessors][].
+
+<h4 id="etpostprocessors">ElementTree Postprocessors</h4>
+
+A ElementTree Postprocessor should inherit from `markdown.Postprocessor` and over-ride
+the `run` method which takes one argument `root` and should return either
+that root element or a modified root element.
+
+A pseudo example:
+
+    class MyPostprocessor(markdown.Postprocessor):
+    def run(self, root):
+        #do stufff
+        return my_modified_root
+
+For specifics on manipulating the ElementTree, see [Working with the ElementTree][] below.
+
+<h4 id="textpostprocessors">TextPostprocessors</h4>
+
+A TextPostprocessor should inherit from `markdown.TextPostprocessor` and
+over-ride the `run` method which takes one argument `text` and returns a
+Unicode string.
+
+TextPostprocessors 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 TocTextPostprocessor(markdown.TextPostprocessor):
+    def run(self, text):
+        return MYMARKERRE.sub(MyToc, text)
+
+<h3 id="working_with_et">Working with the ElementTree</h3>
+
+As mentioned, the Markdown parser converts a source document to an 
+[ElementTree][] 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 of all, to get access to the ElementTree module object you should use: 
+
+    import markdown
+    etree = markdown.etree
+    
+It's try to import ElementTree from any known places, first as standard Python 
+cElementTree/ElementTree module, then as separately installed cElementTree/ElementTree.
+Another thing you need to know is that all text data, included in <inline> tag will be
+processed later with [InlinePatterns][].
+
+Example below show basic ElementTree functionality:
+
+    table = etree.Element("table") 
+    table.set("cellpadding", "2") # Set cellpadding to 2
+    tr = etree.SubElement(table, "tr") # Added child tr to table
+    td = etree.SubElement(tr, "td") # Added child td to tr
+    td.text = "Cell content" # Added text content to td element
+    table.tail = "Text after table" # Added text after table Element
+    print etree.tostring(table) # Serialized our table    
+
+Now let's write a simple ElementTree Postprocessor, that will add "class" attribute
+to all "a" elements:
+
+	class AttrPostprocessor(markdown.Postprocessor):
+	    
+	    def _findElement(self, element, name):
+	    """ 
+		find elements with @name and return list 
+		
+		Keyword arguments:
+		
+		* element: ElementTree Element
+		* name: tag name to search
+		
+		""" 
+		result = []
+		for child in element: 
+		    if child.tag == name:
+			result.append(child)
+		    result += self._findElement(child, name)
+		return result
+	    
+	    def run(self, root):
+
+		for element in self._findElement(root, "a"):
+		     element.set("class", "MyClass") # Set "class" atribute
+		     
+		retrun root
+
+For more information about working with ElementTree visit 
+ElementTree [official site](http://effbot.org/zone/element-index.htm).
+
+<h3 id="integrating_into_markdown">Integrating Your Code Into Markdown
+
+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 within lists. Your `Extension` class will need to manipulate
+those lists appropriately. You may insert instances of your processors and
+patterns into the appropriate location in a list, remove a built-in instances,
+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 lists of processors and patterns. They are found under the 
+    following attributes:
+
+    * `md.textPreprocessors`
+    * `md.preprocessors`
+    * `md.inlinePatterns`
+    * `md.postpreprocessors`
+    * `md.textPostprocessors`
+
+    Some other things you may want to access in the markdown instance are:
+
+    * `md.inlineStash`
+    * `md.htmlStash`
+    * `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 monkeypatching techniques. However, you should
+be aware that the various undocumented or private parts of markdown may change
+without notice and your monkeypatches may no longer work. Therefore, what you
+really should be doing is inserting processors and patterns into the markdown
+pipeline.
+
+<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.
+
+However, Markdown will also accept an already existing instance of an extension.For example:
+
+    import markdown, mdx_myextension
+    configs = {...}
+    myext = mdx_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
+[TextPreprocessors]: #textpreprocessors
+[Line Preprocessors]: #linepreprocessors
+[InlinePatterns]: #inlinepatterns
+[Postprocessors]: #postprocessors
+[ElementTree Postprocessors]: #etpostprocessors
+[TextProstprocessors]: #textpostprocessors
+[Working with the ElementTree]: #working_with_et
+[Integrating your code into Markdown]: #integrating_into_markdown
+[extendMarkdown]: #extendmarkdown
+[Config Settings]: #configsettings
+[makeExtension]: #makeextension
+
         pairs = [x.split("=") for x in ext_args.split(",")]
         configs.update([(x.strip(), y.strip()) for (x, y) in pairs])
 
-    extension_module_name = "mdx_" + ext_name
+    ext_module = 'markdown_extensions'
+    module_name = '.'.join([ext_module, ext_name])
+    extension_module_name = '_'.join(['mdx', ext_name])
 
     try:
-        module = __import__(extension_module_name)
-
+            module = __import__(module_name, {}, {}, [ext_module])
     except ImportError:
-        message(WARN,
-                "Couldn't load extension '%s' from \"%s\" - continuing without."
-                % (ext_name, extension_module_name) )
-        # Return a dummy (do nothing) Extension as silent failure
-        return Extension(configs={})
+        try:
+            module = __import__(extension_module_name)
+        except:
+            message(WARN,
+                "Failed loading extension '%s' from '%s' or '%s' "
+                "- continuing without."
+                % (ext_name, module_name, extension_module_name) )
+            # Return a dummy (do nothing) Extension as silent failure
+            return Extension(configs={})
 
     return module.makeExtension(configs.items())    
 
Add a comment to this file

markdown_extensions/__init__.py

Empty file added.

markdown_extensions/codehilite.py

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

markdown_extensions/extra.py

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

markdown_extensions/fenced_code.py

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

markdown_extensions/footnotes.py

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

markdown_extensions/headerid.py

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

markdown_extensions/imagelinks.py

+"""
+========================= IMAGE LINKS =================================
+
+
+Turns paragraphs like
+
+<~~~~~~~~~~~~~~~~~~~~~~~~
+dir/subdir
+dir/subdir
+dir/subdir
+~~~~~~~~~~~~~~
+dir/subdir
+dir/subdir
+dir/subdir
+~~~~~~~~~~~~~~~~~~~>
+
+Into mini-photo galleries.
+
+"""
+
+import re, markdown
+import url_manager
+
+
+IMAGE_LINK = """<a href="%s"><img src="%s" title="%s"/></a>"""
+SLIDESHOW_LINK = """<a href="%s" target="_blank">[slideshow]</a>"""
+ALBUM_LINK = """&nbsp;<a href="%s">[%s]</a>"""
+
+
+class ImageLinksExtension (markdown.Extension):
+
+    def __init__ (self) :
+        self.reset()
+
+    def extendMarkdown(self, md, md_globals) :
+
+        self.md = md
+
+        # Stateless extensions do not need to be registered
+        md.registerExtension(self)
+
+        # Insert a preprocessor before all preprocessors
+
+        preprocessor = ImageLinkPreprocessor()
+        preprocessor.md = md
+        md.preprocessors.insert(0, preprocessor)
+
+    def reset(self) :
+        # May be called by Markdown is state reset is desired
+        pass
+
+
+class ImageLinkPreprocessor (markdown.Preprocessor):
+
+    def run(self, lines) :
+
+        url = url_manager.BlogEntryUrl(url_manager.BlogUrl("all"),
+                                       "2006/08/29/the_rest_of_our")
+
+
+        all_images = []
+        blocks = []
+        in_image_block = False
+
+        new_lines = []
+        
+        for line in lines :
+
+            if line.startswith("<~~~~~~~") :
+                albums = []
+                rows = []
+                in_image_block = True
+
+            if not in_image_block :
+
+                new_lines.append(line)
+
+            else :
+
+                line = line.strip()
+                
+                if line.endswith("~~~~~~>") or not line :
+                    in_image_block = False
+                    new_block = "<div><br/><center><span class='image-links'>\n"
+
+                    album_url_hash = {}
+
+                    for row in rows :
+                        for photo_url, title in row :
+                            new_block += "&nbsp;"
+                            new_block += IMAGE_LINK % (photo_url,
+                                                       photo_url.get_thumbnail(),
+                                                       title)
+                            
+                            album_url_hash[str(photo_url.get_album())] = 1
+                        
+                    new_block += "<br/>"
+                            
+                    new_block += "</span>"
+                    new_block += SLIDESHOW_LINK % url.get_slideshow()
+
+                    album_urls = album_url_hash.keys()
+                    album_urls.sort()
+
+                    if len(album_urls) == 1 :
+                        new_block += ALBUM_LINK % (album_urls[0], "complete album")
+                    else :
+                        for i in range(len(album_urls)) :
+                            new_block += ALBUM_LINK % (album_urls[i],
+                                                       "album %d" % (i + 1) )
+                    
+                    new_lines.append(new_block + "</center><br/></div>")
+
+                elif line[1:6] == "~~~~~" :
+                    rows.append([])  # start a new row
+                else :
+                    parts = line.split()
+                    line = parts[0]
+                    title = " ".join(parts[1:])
+
+                    album, photo = line.split("/")
+                    photo_url = url.get_photo(album, photo,
+                                              len(all_images)+1)
+                    all_images.append(photo_url)                        
+                    rows[-1].append((photo_url, title))
+