<?php
/*
Plugin Name: Markdown Footnotes
Plugin URI: http://rephrase.net/box/word/footnotes/
Description: Adds footnote support to the object-oriented version of PHP-Markdown
Version: 1.0
Author: Sam Angove
Author URI: http://rephrase.net/

LICENSE
=======

Copyright (c) 2006 Sam Angove <sam@rephrase.net>


This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.

This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
GNU General Public License for more details.

You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA


INSTALLATION INSTRUCTIONS
=========================

1. Install one of the new object-oriented versions of Markdown 
	<http://six.pairlist.net/pipermail/markdown-discuss/2006-May/000054.html>

2. If you get vanilla Markdown, find "MarkdownExtra" in this file
   and change it to "Markdown". 
   
3. Install this plugin. The 'markdown.php' file **must** be installed
   already.   

4. You may need to find the line in Markdown that says:
	define( 'MARKDOWN_PARSER_CLASS',  'MarkdownExtra_Parser' );
   or
    define( 'MARKDOWN_PARSER_CLASS',  'Markdown_Parser' );
   and change it to:
    define( 'MARKDOWN_PARSER_CLASS',  'WP_Markdown_Footnotes' );

5. If you want to change the HTML output (and I imagine you will)
   then you'll have to edit or extend the WP_Markdown_Footnotes class
   -- if you don't know how to do that without my telling you, sorry.
   Best I'm going to do is direct you to the bottom of this file.

*/

define('MARKDOWN_PARSER_CLASS', 'WP_Markdown_Footnotes');
require_once(dirname(__FILE__) . DIRECTORY_SEPARATOR . 'markdown.php');

class Markdown_Footnotes extends MarkdownExtra_Parser {
	var $footnotes = array();
	var $_footnote_ids = array();
	var $_count = 0;
	
	// Hook into parent class.
	function transform($text) {
		$this->footnotes = array();
		$this->_footnote_ids = array();
		$this->_count = 0;

		$text = $this->_stripFootnotes($text);

		$text = parent::transform($text);

		$text = $this->appendFootNotes($text);	

		return $text;
	}
	// Hack hack hack:
	// Transform without resetting or appending footnotes.
	function transformLite($text) {
		return parent::transform($text);	
	}
	
	// Do footnote references at the same time as links.
	function doAnchors($text) {
		$text = $this->_doFootnoteReferences($text);
		return parent::doAnchors($text);
	}

	function _stripFootnotes($text) {

		$less_than_tab = $this->tab_width - 1;
		
		// Footnotes look like link references, except that they can contain
		// arbitrary HTML and be of any length. The content of a footnote
		// continues until another footnote/link definition, or the end of the 
		// document.
		//
		$text = preg_replace_callback('{
							^[ ]{0,'.$less_than_tab.'}\[\^(.+)\]:
							  ([\s\S]+?)
							(?=^[ ]{0,'.$less_than_tab.'}\[|(\n|\r\n){2}^(?!\t|[ ]{'.$this->tab_width.'})|\Z)
			}xm',
			array(&$this, '_stripFootnotes_callback'),
			$text);
		return $text;
	
	}
	function _stripFootnotes_callback($matches) {
		$fn_id = strtolower($matches[1]);
		
		// strip leading spaces
		$note = $this->outdent($matches[2]);
		$this->footnotes[$fn_id] = $note;
		return '';
	}
	
	function _doFootnoteReferences($text) {
		//
		// Inline footnotes like [^1](A brief aside.)
		//
		$text = preg_replace_callback('{
			  [ ]?
			  \[
				\^(.*?)
			  \]
			  \(
				(.*?)
			  \)
			}xs',
			array(&$this, '_doInlineFootnoteReferences_callback'), $text);

		//
		// Match note references like [^id].
		//
		$text = preg_replace_callback('{
			  [ ]?
			  \[
				\^(.*?)
			  \]
			}xs',
			array(&$this, '_doFootnoteReferences_callback'), $text);
		
		return $text;
		
	}
	function _doInlineFootnoteReferences_callback($matches) {
		$fn_id = strtolower($matches[1]);
		
		// implicit inline footnotes will have no id
		if ( $fn_id == '' ) {
			$fn_id = uniqid();
		}
		$this->footnotes[$fn_id] = $matches[2];
		
		return $this->footNoteReference($fn_id);
	}
	function _doFootnoteReferences_callback($matches) {
		$fn_id = strtolower($matches[1]);
		
		if (! isset($this->footnotes[$fn_id]) )
			return $matches[0];
		
		return $this->footNoteReference($fn_id);
	}
	
	function appendFootNotes($text) {
		if ( sizeof($this->footnotes) == 0 ) {
			return $text;
		}
		
		asort($this->_note_ids);
		
		$text .= "<hr />\n\n";
		$text .= "<ol>\n";
		foreach ( $this->_note_ids as $fn_id => $c ) {
			$note = $this->footnotes[$fn_id];
			$text .= $this->footNote($fn_id, $note);
		}
		$text .= "</ol>\n";
		return $text;
	}

	function footNoteAutonumber($fn_id) {
		if ( !isset($this->_note_ids[$fn_id]) ) {
			$this->_count++;
			$this->_note_ids[$fn_id] = $this->_count;
		}
		return $this->_note_ids[$fn_id];	
	}
	
	function footNoteReference($fn_id) {
		$fn_id = $this->footNoteAutonumber($fn_id);
		$guid = $this->footNoteGUID($fn_id);
		return '[<a href="#fn-' . $guid . '" id="fnref-' . $guid . '">' . $fn_id . '</a>]';
	}
	
	function footNote($fn_id, $note) {
		$fn_id = $this->footNoteAutonumber($fn_id);
		$guid = $this->footNoteGUID($fn_id);

		$fnref = '[<a href="#fnref-'. $guid .'">return</a>]';

		$note = $this->transformLite( $note );
		$note = $note . ' ' . $fnref;
		return '<li class="footnote" id="fn-' . $guid . '">' . $note . '</li>' . "\n";
	}
	
	// If multiple Markdown documents can appear on a single page, use this
	// to preserve ID uniqueness.
	function footNoteGUID($fn_id) {
		return $fn_id;
	}
}

class WP_Markdown_Footnotes extends Markdown_Footnotes {
	//
	// Use the WordPress post id as part of every
	// footnote HTML id attribute to preserve uniqueness.
	function footNoteGUID($fn_id) {
		global $id;
		return $id . '-' . $fn_id;
	}
}
?>