<?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;
}
}
?>