#
# Original Markdown
# Copyright (c) 2004-2006 John Gruber  
# 
'.$text.'
'; $text = preg_replace('{\n{2,}}', "\n\n", $text); } return $text; } function mdwp_strip_p($t) { return preg_replace('{?p>}i', '', $t); } function mdwp_hide_tags($text) { global $mdwp_hidden_tags, $mdwp_placeholders; return str_replace($mdwp_hidden_tags, $mdwp_placeholders, $text); } function mdwp_show_tags($text) { global $mdwp_hidden_tags, $mdwp_placeholders; return str_replace($mdwp_placeholders, $mdwp_hidden_tags, $text); } } ### bBlog Plugin Info ### function identify_modifier_markdown() { return array( 'name' => 'markdown', 'type' => 'modifier', 'nicename' => 'Markdown', 'description' => 'A text-to-HTML conversion tool for web writers', 'authors' => 'Michel Fortin and John Gruber', 'licence' => 'BSD-like', 'version' => MARKDOWN_VERSION, 'help' => 'Markdown syntax allows you to write using an easy-to-read, easy-to-write plain text format. Based on the original Perl version by John Gruber. More...' ); } ### Smarty Modifier Interface ### function smarty_modifier_markdown($text) { return Markdown($text); } ### Textile Compatibility Mode ### # Rename this file to "classTextile.php" and it can replace Textile everywhere. if (strcasecmp(substr(__FILE__, -16), "classTextile.php") == 0) { # Try to include PHP SmartyPants. Should be in the same directory. @include_once 'smartypants.php'; # Fake Textile class. It calls Markdown instead. class Textile { function TextileThis($text, $lite='', $encode='') { if ($lite == '' && $encode == '') $text = Markdown($text); if (function_exists('SmartyPants')) $text = SmartyPants($text); return $text; } # Fake restricted version: restrictions are not supported for now. function TextileRestricted($text, $lite='', $noimage='') { return $this->TextileThis($text, $lite); } # Workaround to ensure compatibility with TextPattern 4.0.3. function blockLite($text) { return $text; } } } # # Markdown Parser Class # class Markdown_Parser { # Regex to match balanced [brackets]. # Needed to insert a maximum bracked depth while converting to PHP. var $nested_brackets_depth = 6; var $nested_brackets_re; var $nested_url_parenthesis_depth = 4; var $nested_url_parenthesis_re; # Table of hash values for escaped characters: var $escape_chars = '\`*_{}[]()>#+-.!'; var $escape_chars_re; # Change to ">" for HTML output. var $empty_element_suffix = MARKDOWN_EMPTY_ELEMENT_SUFFIX; var $tab_width = MARKDOWN_TAB_WIDTH; # Change to `true` to disallow markup or entities. var $no_markup = false; var $no_entities = false; # Predefined urls and titles for reference links and images. var $predef_urls = array(); var $predef_titles = array(); function Markdown_Parser() { # # Constructor function. Initialize appropriate member variables. # $this->_initDetab(); $this->prepareItalicsAndBold(); $this->nested_brackets_re = str_repeat('(?>[^\[\]]+|\[', $this->nested_brackets_depth). str_repeat('\])*', $this->nested_brackets_depth); $this->nested_url_parenthesis_re = str_repeat('(?>[^()\s]+|\(', $this->nested_url_parenthesis_depth). str_repeat('(?>\)))*', $this->nested_url_parenthesis_depth); $this->escape_chars_re = '['.preg_quote($this->escape_chars).']'; # Sort document, block, and span gamut in ascendent priority order. asort($this->document_gamut); asort($this->block_gamut); asort($this->span_gamut); } # Internal hashes used during transformation. var $urls = array(); var $titles = array(); var $html_hashes = array(); # Status flag to avoid invalid nesting. var $in_anchor = false; function setup() { # # Called before the transformation process starts to setup parser # states. # # Clear global hashes. $this->urls = $this->predef_urls; $this->titles = $this->predef_titles; $this->html_hashes = array(); $in_anchor = false; } function teardown() { # # Called after the transformation process to clear any variable # which may be taking up memory unnecessarly. # $this->urls = array(); $this->titles = array(); $this->html_hashes = array(); } function transform($text) { # # Main function. Performs some preprocessing on the input text # and pass it through the document gamut. # $this->setup(); # Remove UTF-8 BOM and marker character in input, if present. $text = preg_replace('{^\xEF\xBB\xBF|\x1A}', '', $text); # Standardize line endings: # DOS to Unix and Mac to Unix $text = preg_replace('{\r\n?}', "\n", $text); # Make sure $text ends with a couple of newlines: $text .= "\n\n"; # Convert all tabs to spaces. $text = $this->detab($text); # Turn block-level HTML blocks into hash entries $text = $this->hashHTMLBlocks($text); # Strip any lines consisting only of spaces and tabs. # This makes subsequent regexen easier to write, because we can # match consecutive blank lines with /\n+/ instead of something # contorted like /[ ]*\n+/ . $text = preg_replace('/^[ ]+$/m', '', $text); # Run document gamut methods. foreach ($this->document_gamut as $method => $priority) { $text = $this->$method($text); } $this->teardown(); return $text . "\n"; } var $document_gamut = array( # Strip link definitions, store in hashes. "stripLinkDefinitions" => 20, "runBasicBlockGamut" => 30, ); function stripLinkDefinitions($text) { # # Strips link definitions from text, stores the URLs and titles in # hash references. # $less_than_tab = $this->tab_width - 1; # Link defs are in the form: ^[id]: url "optional title" $text = preg_replace_callback('{ ^[ ]{0,'.$less_than_tab.'}\[(.+)\][ ]?: # id = $1 [ ]* \n? # maybe *one* newline [ ]* (\S+?)>? # url = $2 [ ]* \n? # maybe one newline [ ]* (?: (?<=\s) # lookbehind for whitespace ["(] (.*?) # title = $3 [")] [ ]* )? # title is optional (?:\n+|\Z) }xm', array(&$this, '_stripLinkDefinitions_callback'), $text); return $text; } function _stripLinkDefinitions_callback($matches) { $link_id = strtolower($matches[1]); $this->urls[$link_id] = $matches[2]; $this->titles[$link_id] =& $matches[3]; return ''; # String that will replace the block } function hashHTMLBlocks($text) { if ($this->no_markup) return $text; $less_than_tab = $this->tab_width - 1; # Hashify HTML blocks: # We only want to do this for block-level HTML tags, such as headers, # lists, and tables. That's because we still want to wrap
s around # "paragraphs" that are wrapped in non-block-level tags, such as anchors, # phrase emphasis, and spans. The list of tags we're looking for is # hard-coded: # # * List "a" is made of tags which can be both inline or block-level. # These will be treated block-level when the start tag is alone on # its line, otherwise they're not matched here and will be taken as # inline later. # * List "b" is made of tags which are always block-level; # $block_tags_a_re = 'ins|del'; $block_tags_b_re = 'p|div|h[1-6]|blockquote|pre|table|dl|ol|ul|address|'. 'script|noscript|form|fieldset|iframe|math'; # Regular expression for the content of a block tag. $nested_tags_level = 4; $attr = ' (?> # optional tag attributes \s # starts with whitespace (?> [^>"/]+ # text outside quotes | /+(?!>) # slash not followed by ">" | "[^"]*" # text inside double quotes (tolerate ">") | \'[^\']*\' # text inside single quotes (tolerate ">") )* )? '; $content = str_repeat(' (?> [^<]+ # content without tag | <\2 # nested opening tag '.$attr.' # attributes (?> /> | >', $nested_tags_level). # end of opening tag '.*?'. # last level nested tag content str_repeat(' \2\s*> # closing nested tag ) | <(?!/\2\s*> # other tags with a different name ) )*', $nested_tags_level); $content2 = str_replace('\2', '\3', $content); # First, look for nested blocks, e.g.: #
` blocks.
	#
		$text = preg_replace_callback('{
				(?:\n\n|\A\n?)
				(	            # $1 = the code block -- one or more lines, starting with a space/tab
				  (?>
					[ ]{'.$this->tab_width.'}  # Lines must start with a tab or a tab-width of spaces
					.*\n+
				  )+
				)
				((?=^[ ]{0,'.$this->tab_width.'}\S)|\Z)	# Lookahead for non-space at line-start, or end of doc
			}xm',
			array(&$this, '_doCodeBlocks_callback'), $text);
		return $text;
	}
	function _doCodeBlocks_callback($matches) {
		$codeblock = $matches[1];
		$codeblock = $this->outdent($codeblock);
		$codeblock = htmlspecialchars($codeblock, ENT_NOQUOTES);
		# trim leading newlines and trailing newlines
		$codeblock = preg_replace('/\A\n+|\n+\z/', '', $codeblock);
		$codeblock = "$codeblock\n
";
		return "\n\n".$this->hashBlock($codeblock)."\n\n";
	}
	function makeCodeSpan($code) {
	#
	# Create a code span markup for $code. Called from handleSpanToken.
	#
		$code = htmlspecialchars(trim($code), ENT_NOQUOTES);
		return $this->hashPart("$code");
	}
	var $em_relist = array(
		''  => '(?:(? '(?<=\S)(? '(?<=\S)(? '(?:(? '(?<=\S)(? '(?<=\S)(? '(?:(? '(?<=\S)(? '(?<=\S)(?em_relist as $em => $em_re) {
			foreach ($this->strong_relist as $strong => $strong_re) {
				# Construct list of allowed token expressions.
				$token_relist = array();
				if (isset($this->em_strong_relist["$em$strong"])) {
					$token_relist[] = $this->em_strong_relist["$em$strong"];
				}
				$token_relist[] = $em_re;
				$token_relist[] = $strong_re;
				
				# Construct master expression from list.
				$token_re = '{('. implode('|', $token_relist) .')}';
				$this->em_strong_prepared_relist["$em$strong"] = $token_re;
			}
		}
	}
	
	function doItalicsAndBold($text) {
		$token_stack = array('');
		$text_stack = array('');
		$em = '';
		$strong = '';
		$tree_char_em = false;
		
		while (1) {
			#
			# Get prepared regular expression for seraching emphasis tokens
			# in current context.
			#
			$token_re = $this->em_strong_prepared_relist["$em$strong"];
			
			#
			# Each loop iteration seach for the next emphasis token. 
			# Each token is then passed to handleSpanToken.
			#
			$parts = preg_split($token_re, $text, 2, PREG_SPLIT_DELIM_CAPTURE);
			$text_stack[0] .= $parts[0];
			$token =& $parts[1];
			$text =& $parts[2];
			
			if (empty($token)) {
				# Reached end of text span: empty stack without emitting.
				# any more emphasis.
				while ($token_stack[0]) {
					$text_stack[1] .= array_shift($token_stack);
					$text_stack[0] .= array_shift($text_stack);
				}
				break;
			}
			
			$token_len = strlen($token);
			if ($tree_char_em) {
				# Reached closing marker while inside a three-char emphasis.
				if ($token_len == 3) {
					# Three-char closing marker, close em and strong.
					array_shift($token_stack);
					$span = array_shift($text_stack);
					$span = $this->runSpanGamut($span);
					$span = "$span";
					$text_stack[0] .= $this->hashPart($span);
					$em = '';
					$strong = '';
				} else {
					# Other closing marker: close one em or strong and
					# change current token state to match the other
					$token_stack[0] = str_repeat($token{0}, 3-$token_len);
					$tag = $token_len == 2 ? "strong" : "em";
					$span = $text_stack[0];
					$span = $this->runSpanGamut($span);
					$span = "<$tag>$span$tag>";
					$text_stack[0] = $this->hashPart($span);
					$$tag = ''; # $$tag stands for $em or $strong
				}
				$tree_char_em = false;
			} else if ($token_len == 3) {
				if ($em) {
					# Reached closing marker for both em and strong.
					# Closing strong marker:
					for ($i = 0; $i < 2; ++$i) {
						$shifted_token = array_shift($token_stack);
						$tag = strlen($shifted_token) == 2 ? "strong" : "em";
						$span = array_shift($text_stack);
						$span = $this->runSpanGamut($span);
						$span = "<$tag>$span$tag>";
						$text_stack[0] .= $this->hashPart($span);
						$$tag = ''; # $$tag stands for $em or $strong
					}
				} else {
					# Reached opening three-char emphasis marker. Push on token 
					# stack; will be handled by the special condition above.
					$em = $token{0};
					$strong = "$em$em";
					array_unshift($token_stack, $token);
					array_unshift($text_stack, '');
					$tree_char_em = true;
				}
			} else if ($token_len == 2) {
				if ($strong) {
					# Unwind any dangling emphasis marker:
					if (strlen($token_stack[0]) == 1) {
						$text_stack[1] .= array_shift($token_stack);
						$text_stack[0] .= array_shift($text_stack);
					}
					# Closing strong marker:
					array_shift($token_stack);
					$span = array_shift($text_stack);
					$span = $this->runSpanGamut($span);
					$span = "$span";
					$text_stack[0] .= $this->hashPart($span);
					$strong = '';
				} else {
					array_unshift($token_stack, $token);
					array_unshift($text_stack, '');
					$strong = $token;
				}
			} else {
				# Here $token_len == 1
				if ($em) {
					if (strlen($token_stack[0]) == 1) {
						# Closing emphasis marker:
						array_shift($token_stack);
						$span = array_shift($text_stack);
						$span = $this->runSpanGamut($span);
						$span = "$span";
						$text_stack[0] .= $this->hashPart($span);
						$em = '';
					} else {
						$text_stack[0] .= $token;
					}
				} else {
					array_unshift($token_stack, $token);
					array_unshift($text_stack, '');
					$em = $token;
				}
			}
		}
		return $text_stack[0];
	}
	function doBlockQuotes($text) {
		$text = preg_replace_callback('/
			  (								# Wrap whole match in $1
				(?>
				  ^[ ]*>[ ]?			# ">" at the start of a line
					.+\n					# rest of the first line
				  (.+\n)*					# subsequent consecutive lines
				  \n*						# blanks
				)+
			  )
			/xm',
			array(&$this, '_doBlockQuotes_callback'), $text);
		return $text;
	}
	function _doBlockQuotes_callback($matches) {
		$bq = $matches[1];
		# trim one level of quoting - trim whitespace-only lines
		$bq = preg_replace('/^[ ]*>[ ]?|^[ ]+$/m', '', $bq);
		$bq = $this->runBlockGamut($bq);		# recurse
		$bq = preg_replace('/^/m', "  ", $bq);
		# These leading spaces cause problem with  content, 
		# so we need to fix that:
		$bq = preg_replace_callback('{(\s*.+?
)}sx', 
			array(&$this, '_DoBlockQuotes_callback2'), $bq);
		return "\n". $this->hashBlock("\n$bq\n
")."\n\n";
	}
	function _doBlockQuotes_callback2($matches) {
		$pre = $matches[1];
		$pre = preg_replace('/^  /m', '', $pre);
		return $pre;
	}
	function formParagraphs($text) {
	#
	#	Params:
	#		$text - string to process with html  tags
	#
		# Strip leading and trailing lines:
		$text = preg_replace('/\A\n+|\n+\z/', '', $text);
		$grafs = preg_split('/\n{2,}/', $text, -1, PREG_SPLIT_NO_EMPTY);
		#
		# Wrap 
 tags and unhashify HTML blocks
		#
		foreach ($grafs as $key => $value) {
			if (!preg_match('/^B\x1A[0-9]+B$/', $value)) {
				# Is a paragraph.
				$value = $this->runSpanGamut($value);
				$value = preg_replace('/^([ ]*)/', "
", $value);
				$value .= "
";
				$grafs[$key] = $this->unhash($value);
			}
			else {
				# Is a block.
				# Modify elements of @grafs in-place...
				$graf = $value;
				$block = $this->html_hashes[$graf];
				$graf = $block;
//				if (preg_match('{
//					\A
//					(							# $1 =  tag
//					  ]*
//					  \b
//					  markdown\s*=\s*  ([\'"])	#	$2 = attr quote char
//					  1
//					  \2
//					  [^>]*
//					  >
//					)
//					(							# $3 = contents
//					.*
//					)
//					()					# $4 = closing tag
//					\z
//					}xs', $block, $matches))
//				{
//					list(, $div_open, , $div_content, $div_close) = $matches;
//
//					# We cannot call Markdown(), because that resets the hash;
//					# that initialization code should be pulled into its own sub, though.
//					$div_content = $this->hashHTMLBlocks($div_content);
//					
//					# Run document gamut methods on the content.
//					foreach ($this->document_gamut as $method => $priority) {
//						$div_content = $this->$method($div_content);
//					}
//
//					$div_open = preg_replace(
//						'{\smarkdown\s*=\s*([\'"]).+?\1}', '', $div_open);
//
//					$graf = $div_open . "\n" . $div_content . "\n" . $div_close;
//				}
				$grafs[$key] = $graf;
			}
		}
		return implode("\n\n", $grafs);
	}
	function encodeAttribute($text) {
	#
	# Encode text for a double-quoted HTML attribute. This function
	# is *not* suitable for attributes enclosed in single quotes.
	#
		$text = $this->encodeAmpsAndAngles($text);
		$text = str_replace('"', '"', $text);
		return $text;
	}
	
	
	function encodeAmpsAndAngles($text) {
	#
	# Smart processing for ampersands and angle brackets that need to 
	# be encoded. Valid character entities are left alone unless the
	# no-entities mode is set.
	#
		if ($this->no_entities) {
			$text = str_replace('&', '&', $text);
		} else {
			# Ampersand-encoding based entirely on Nat Irons's Amputator
			# MT plugin:  as well).
For more information about Markdown's syntax, see:
Please include with your report: (1) the example input; (2) the output you
expected; (3) the output Markdown actually produced.
Version History
--------------- 
See the readme file for detailed release notes for this version.
Copyright and License
---------------------
PHP Markdown
Copyright (c) 2004-2008 Michel Fortin