[plain text] [download]
  1. <?php
  2. /*
  3. Plugin Name: Markdown Footnotes
  4. Plugin URI: http://rephrase.net/box/word/footnotes/
  5. Description: Adds footnote support to the object-oriented version of PHP-Markdown
  6. Version: 1.0
  7. Author: Sam Angove
  8. Author URI: http://rephrase.net/
  9. LICENSE
  10. =======
  11. Copyright (c) 2006 Sam Angove <sam@rephrase.net>
  12. This program is free software; you can redistribute it and/or modify
  13. it under the terms of the GNU General Public License as published by
  14. the Free Software Foundation; either version 2 of the License, or
  15. (at your option) any later version.
  16. This program is distributed in the hope that it will be useful,
  17. but WITHOUT ANY WARRANTY; without even the implied warranty of
  18. MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  19. GNU General Public License for more details.
  20. You should have received a copy of the GNU General Public License
  21. along with this program; if not, write to the Free Software
  22. Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
  23. INSTALLATION INSTRUCTIONS
  24. =========================
  25. 1. Install one of the new object-oriented versions of Markdown
  26.         <http://six.pairlist.net/pipermail/markdown-discuss/2006-May/000054.html>
  27. 2. If you get vanilla Markdown, find "MarkdownExtra" in this file
  28.    and change it to "Markdown".
  29.    
  30. 3. Install this plugin. The 'markdown.php' file **must** be installed
  31.    already.   
  32. 4. You may need to find the line in Markdown that says:
  33.         define( 'MARKDOWN_PARSER_CLASS',  'MarkdownExtra_Parser' );
  34.    or
  35.     define( 'MARKDOWN_PARSER_CLASS',  'Markdown_Parser' );
  36.    and change it to:
  37.     define( 'MARKDOWN_PARSER_CLASS',  'WP_Markdown_Footnotes' );
  38. 5. If you want to change the HTML output (and I imagine you will)
  39.    then you'll have to edit or extend the WP_Markdown_Footnotes class
  40.    -- if you don't know how to do that without my telling you, sorry.
  41.    Best I'm going to do is direct you to the bottom of this file.
  42. */
  43.  
  44. define('MARKDOWN_PARSER_CLASS', 'WP_Markdown_Footnotes');
  45. require_once(dirname(__FILE__) . DIRECTORY_SEPARATOR . 'markdown.php');
  46.  
  47. class Markdown_Footnotes extends MarkdownExtra_Parser {
  48.         var $footnotes = array();
  49.         var $_footnote_ids = array();
  50.         var $_count = 0;
  51.        
  52.         // Hook into parent class.
  53.         function transform($text) {
  54.                 $this->footnotes = array();
  55.                 $this->_footnote_ids = array();
  56.                 $this->_count = 0;
  57.  
  58.                 $text = $this->_stripFootnotes($text);
  59.  
  60.                 $text = parent::transform($text);
  61.  
  62.                 $text = $this->appendFootNotes($text)
  63.  
  64.                 return $text;
  65.         }
  66.         // Hack hack hack:
  67.         // Transform without resetting or appending footnotes.
  68.         function transformLite($text) {
  69.                 return parent::transform($text);       
  70.         }
  71.        
  72.         // Do footnote references at the same time as links.
  73.         function doAnchors($text) {
  74.                 $text = $this->_doFootnoteReferences($text);
  75.                 return parent::doAnchors($text);
  76.         }
  77.  
  78.         function _stripFootnotes($text) {
  79.  
  80.                 $less_than_tab = $this->tab_width - 1;
  81.                
  82.                 // Footnotes look like link references, except that they can contain
  83.                 // arbitrary HTML and be of any length. The content of a footnote
  84.                 // continues until another footnote/link definition, or the end of the
  85.                 // document.
  86.                 //
  87.                 $text = preg_replace_callback('{
  88.                                                         ^[ ]{0,'.$less_than_tab.'}\[\^(.+)\]:
  89.                                                           ([\s\S]+?)
  90.                                                         (?=^[ ]{0,'.$less_than_tab.'}\[|(\n|\r\n){2}^(?!\t|[ ]{'.$this->tab_width.'})|\Z)
  91.                         }xm',
  92.                         array(&$this, '_stripFootnotes_callback'),
  93.                         $text);
  94.                 return $text;
  95.        
  96.         }
  97.         function _stripFootnotes_callback($matches) {
  98.                 $fn_id = strtolower($matches[1]);
  99.                
  100.                 // strip leading spaces
  101.                 $note = $this->outdent($matches[2]);
  102.                 $this->footnotes[$fn_id] = $note;
  103.                 return '';
  104.         }
  105.        
  106.         function _doFootnoteReferences($text) {
  107.                 //
  108.                 // Inline footnotes like [^1](A brief aside.)
  109.                 //
  110.                 $text = preg_replace_callback('{
  111.                           [ ]?
  112.                           \[
  113.                                 \^(.*?)
  114.                           \]
  115.                           \(
  116.                                 (.*?)
  117.                           \)
  118.                         }xs',
  119.                         array(&$this, '_doInlineFootnoteReferences_callback'), $text);
  120.  
  121.                 //
  122.                 // Match note references like [^id].
  123.                 //
  124.                 $text = preg_replace_callback('{
  125.                           [ ]?
  126.                           \[
  127.                                 \^(.*?)
  128.                           \]
  129.                         }xs',
  130.                         array(&$this, '_doFootnoteReferences_callback'), $text);
  131.                
  132.                 return $text;
  133.                
  134.         }
  135.         function _doInlineFootnoteReferences_callback($matches) {
  136.                 $fn_id = strtolower($matches[1]);
  137.                
  138.                 // implicit inline footnotes will have no id
  139.                 if ( $fn_id == '' ) {
  140.                         $fn_id = uniqid();
  141.                 }
  142.                 $this->footnotes[$fn_id] = $matches[2];
  143.                
  144.                 return $this->footNoteReference($fn_id);
  145.         }
  146.         function _doFootnoteReferences_callback($matches) {
  147.                 $fn_id = strtolower($matches[1]);
  148.                
  149.                 if (! isset($this->footnotes[$fn_id]) )
  150.                         return $matches[0];
  151.                
  152.                 return $this->footNoteReference($fn_id);
  153.         }
  154.        
  155.         function appendFootNotes($text) {
  156.                 if ( sizeof($this->footnotes) == 0 ) {
  157.                         return $text;
  158.                 }
  159.                
  160.                 asort($this->_note_ids);
  161.                
  162.                 $text .= "<hr />\n\n";
  163.                 $text .= "<ol>\n";
  164.                 foreach ( $this->_note_ids as $fn_id => $c ) {
  165.                         $note = $this->footnotes[$fn_id];
  166.                         $text .= $this->footNote($fn_id, $note);
  167.                 }
  168.                 $text .= "</ol>\n";
  169.                 return $text;
  170.         }
  171.  
  172.         function footNoteAutonumber($fn_id) {
  173.                 if ( !isset($this->_note_ids[$fn_id]) ) {
  174.                         $this->_count++;
  175.                         $this->_note_ids[$fn_id] = $this->_count;
  176.                 }
  177.                 return $this->_note_ids[$fn_id];       
  178.         }
  179.        
  180.         function footNoteReference($fn_id) {
  181.                 $fn_id = $this->footNoteAutonumber($fn_id);
  182.                 $guid = $this->footNoteGUID($fn_id);
  183.                 return '[<a href="#fn-' . $guid . '" id="fnref-' . $guid . '">' . $fn_id . '</a>]';
  184.         }
  185.        
  186.         function footNote($fn_id, $note) {
  187.                 $fn_id = $this->footNoteAutonumber($fn_id);
  188.                 $guid = $this->footNoteGUID($fn_id);
  189.  
  190.                 $fnref = '[<a href="#fnref-'. $guid .'">return</a>]';
  191.  
  192.                 $note = $this->transformLite( $note );
  193.                 $note = $note . ' ' . $fnref;
  194.                 return '<li class="footnote" id="fn-' . $guid . '">' . $note . '</li>' . "\n";
  195.         }
  196.        
  197.         // If multiple Markdown documents can appear on a single page, use this
  198.         // to preserve ID uniqueness.
  199.         function footNoteGUID($fn_id) {
  200.                 return $fn_id;
  201.         }
  202. }
  203.  
  204. class WP_Markdown_Footnotes extends Markdown_Footnotes {
  205.         //
  206.         // Use the WordPress post id as part of every
  207.         // footnote HTML id attribute to preserve uniqueness.
  208.         function footNoteGUID($fn_id) {
  209.                 global $id;
  210.                 return $id . '-' . $fn_id;
  211.         }
  212. }
  213. ?>