Skip to content
This repository
Browse code

add PHP Markdown 1.0.1j

  • Loading branch information...
commit bffdadd5c30877780589a52712dd90fcdacbf22e 1 parent 3795c60
Trent Mick authored September 09, 2007

Showing 1 changed file with 1,541 additions and 0 deletions. Show diff stats Hide diff stats

  1. 1,541  test/markdown.php
1,541  test/markdown.php
... ...
@@ -0,0 +1,1541 @@
  1
+<?php
  2
+#
  3
+# Markdown  -  A text-to-HTML conversion tool for web writers
  4
+#
  5
+# PHP Markdown
  6
+# Copyright (c) 2004-2007 Michel Fortin  
  7
+# <http://www.michelf.com/projects/php-markdown/>
  8
+#
  9
+# Original Markdown
  10
+# Copyright (c) 2004-2006 John Gruber  
  11
+# <http://daringfireball.net/projects/markdown/>
  12
+#
  13
+
  14
+
  15
+define( 'MARKDOWN_VERSION',  "1.0.1j" ); # Tue 4 Sep 2007
  16
+
  17
+
  18
+#
  19
+# Global default settings:
  20
+#
  21
+
  22
+# Change to ">" for HTML output
  23
+@define( 'MARKDOWN_EMPTY_ELEMENT_SUFFIX',  " />");
  24
+
  25
+# Define the width of a tab for code blocks.
  26
+@define( 'MARKDOWN_TAB_WIDTH',     4 );
  27
+
  28
+
  29
+#
  30
+# WordPress settings:
  31
+#
  32
+
  33
+# Change to false to remove Markdown from posts and/or comments.
  34
+@define( 'MARKDOWN_WP_POSTS',      true );
  35
+@define( 'MARKDOWN_WP_COMMENTS',   true );
  36
+
  37
+
  38
+
  39
+### Standard Function Interface ###
  40
+
  41
+@define( 'MARKDOWN_PARSER_CLASS',  'Markdown_Parser' );
  42
+
  43
+function Markdown($text) {
  44
+#
  45
+# Initialize the parser and return the result of its transform method.
  46
+#
  47
+	# Setup static parser variable.
  48
+	static $parser;
  49
+	if (!isset($parser)) {
  50
+		$parser_class = MARKDOWN_PARSER_CLASS;
  51
+		$parser = new $parser_class;
  52
+	}
  53
+
  54
+	# Transform text using parser.
  55
+	return $parser->transform($text);
  56
+}
  57
+
  58
+
  59
+### WordPress Plugin Interface ###
  60
+
  61
+/*
  62
+Plugin Name: Markdown
  63
+Plugin URI: http://www.michelf.com/projects/php-markdown/
  64
+Description: <a href="http://daringfireball.net/projects/markdown/syntax">Markdown syntax</a> allows you to write using an easy-to-read, easy-to-write plain text format. Based on the original Perl version by <a href="http://daringfireball.net/">John Gruber</a>. <a href="http://www.michelf.com/projects/php-markdown/">More...</a>
  65
+Version: 1.0.1j
  66
+Author: Michel Fortin
  67
+Author URI: http://www.michelf.com/
  68
+*/
  69
+
  70
+if (isset($wp_version)) {
  71
+	# More details about how it works here:
  72
+	# <http://www.michelf.com/weblog/2005/wordpress-text-flow-vs-markdown/>
  73
+	
  74
+	# Post content and excerpts
  75
+	# - Remove WordPress paragraph generator.
  76
+	# - Run Markdown on excerpt, then remove all tags.
  77
+	# - Add paragraph tag around the excerpt, but remove it for the excerpt rss.
  78
+	if (MARKDOWN_WP_POSTS) {
  79
+		remove_filter('the_content',     'wpautop');
  80
+        remove_filter('the_content_rss', 'wpautop');
  81
+		remove_filter('the_excerpt',     'wpautop');
  82
+		add_filter('the_content',     'Markdown', 6);
  83
+        add_filter('the_content_rss', 'Markdown', 6);
  84
+		add_filter('get_the_excerpt', 'Markdown', 6);
  85
+		add_filter('get_the_excerpt', 'trim', 7);
  86
+		add_filter('the_excerpt',     'mdwp_add_p');
  87
+		add_filter('the_excerpt_rss', 'mdwp_strip_p');
  88
+		
  89
+		remove_filter('content_save_pre',  'balanceTags', 50);
  90
+		remove_filter('excerpt_save_pre',  'balanceTags', 50);
  91
+		add_filter('the_content',  	  'balanceTags', 50);
  92
+		add_filter('get_the_excerpt', 'balanceTags', 9);
  93
+	}
  94
+	
  95
+	# Comments
  96
+	# - Remove WordPress paragraph generator.
  97
+	# - Remove WordPress auto-link generator.
  98
+	# - Scramble important tags before passing them to the kses filter.
  99
+	# - Run Markdown on excerpt then remove paragraph tags.
  100
+	if (MARKDOWN_WP_COMMENTS) {
  101
+		remove_filter('comment_text', 'wpautop', 30);
  102
+		remove_filter('comment_text', 'make_clickable');
  103
+		add_filter('pre_comment_content', 'Markdown', 6);
  104
+		add_filter('pre_comment_content', 'mdwp_hide_tags', 8);
  105
+		add_filter('pre_comment_content', 'mdwp_show_tags', 12);
  106
+		add_filter('get_comment_text',    'Markdown', 6);
  107
+		add_filter('get_comment_excerpt', 'Markdown', 6);
  108
+		add_filter('get_comment_excerpt', 'mdwp_strip_p', 7);
  109
+	
  110
+		global $mdwp_hidden_tags, $mdwp_placeholders;
  111
+		$mdwp_hidden_tags = explode(' ',
  112
+			'<p> </p> <pre> </pre> <ol> </ol> <ul> </ul> <li> </li>');
  113
+		$mdwp_placeholders = explode(' ', str_rot13(
  114
+			'pEj07ZbbBZ U1kqgh4w4p pre2zmeN6K QTi31t9pre ol0MP1jzJR '.
  115
+			'ML5IjmbRol ulANi1NsGY J7zRLJqPul liA8ctl16T K9nhooUHli'));
  116
+	}
  117
+	
  118
+	function mdwp_add_p($text) {
  119
+		if (!preg_match('{^$|^<(p|ul|ol|dl|pre|blockquote)>}i', $text)) {
  120
+			$text = '<p>'.$text.'</p>';
  121
+			$text = preg_replace('{\n{2,}}', "</p>\n\n<p>", $text);
  122
+		}
  123
+		return $text;
  124
+	}
  125
+	
  126
+	function mdwp_strip_p($t) { return preg_replace('{</?p>}i', '', $t); }
  127
+
  128
+	function mdwp_hide_tags($text) {
  129
+		global $mdwp_hidden_tags, $mdwp_placeholders;
  130
+		return str_replace($mdwp_hidden_tags, $mdwp_placeholders, $text);
  131
+	}
  132
+	function mdwp_show_tags($text) {
  133
+		global $mdwp_hidden_tags, $mdwp_placeholders;
  134
+		return str_replace($mdwp_placeholders, $mdwp_hidden_tags, $text);
  135
+	}
  136
+}
  137
+
  138
+
  139
+### bBlog Plugin Info ###
  140
+
  141
+function identify_modifier_markdown() {
  142
+	return array(
  143
+		'name'			=> 'markdown',
  144
+		'type'			=> 'modifier',
  145
+		'nicename'		=> 'Markdown',
  146
+		'description'	=> 'A text-to-HTML conversion tool for web writers',
  147
+		'authors'		=> 'Michel Fortin and John Gruber',
  148
+		'licence'		=> 'BSD-like',
  149
+		'version'		=> MARKDOWN_VERSION,
  150
+		'help'			=> '<a href="http://daringfireball.net/projects/markdown/syntax">Markdown syntax</a> allows you to write using an easy-to-read, easy-to-write plain text format. Based on the original Perl version by <a href="http://daringfireball.net/">John Gruber</a>. <a href="http://www.michelf.com/projects/php-markdown/">More...</a>'
  151
+	);
  152
+}
  153
+
  154
+
  155
+### Smarty Modifier Interface ###
  156
+
  157
+function smarty_modifier_markdown($text) {
  158
+	return Markdown($text);
  159
+}
  160
+
  161
+
  162
+### Textile Compatibility Mode ###
  163
+
  164
+# Rename this file to "classTextile.php" and it can replace Textile everywhere.
  165
+
  166
+if (strcasecmp(substr(__FILE__, -16), "classTextile.php") == 0) {
  167
+	# Try to include PHP SmartyPants. Should be in the same directory.
  168
+	@include_once 'smartypants.php';
  169
+	# Fake Textile class. It calls Markdown instead.
  170
+	class Textile {
  171
+		function TextileThis($text, $lite='', $encode='') {
  172
+			if ($lite == '' && $encode == '')    $text = Markdown($text);
  173
+			if (function_exists('SmartyPants'))  $text = SmartyPants($text);
  174
+			return $text;
  175
+		}
  176
+		# Fake restricted version: restrictions are not supported for now.
  177
+		function TextileRestricted($text, $lite='', $noimage='') {
  178
+			return $this->TextileThis($text, $lite);
  179
+		}
  180
+		# Workaround to ensure compatibility with TextPattern 4.0.3.
  181
+		function blockLite($text) { return $text; }
  182
+	}
  183
+}
  184
+
  185
+
  186
+
  187
+#
  188
+# Markdown Parser Class
  189
+#
  190
+
  191
+class Markdown_Parser {
  192
+
  193
+	# Regex to match balanced [brackets].
  194
+	# Needed to insert a maximum bracked depth while converting to PHP.
  195
+	var $nested_brackets_depth = 6;
  196
+	var $nested_brackets;
  197
+	
  198
+	var $nested_url_parenthesis_depth = 4;
  199
+	var $nested_url_parenthesis;
  200
+
  201
+	# Table of hash values for escaped characters:
  202
+	var $escape_chars = '\`*_{}[]()>#+-.!';
  203
+
  204
+	# Change to ">" for HTML output.
  205
+	var $empty_element_suffix = MARKDOWN_EMPTY_ELEMENT_SUFFIX;
  206
+	var $tab_width = MARKDOWN_TAB_WIDTH;
  207
+	
  208
+	# Change to `true` to disallow markup or entities.
  209
+	var $no_markup = false;
  210
+	var $no_entities = false;
  211
+
  212
+
  213
+	function Markdown_Parser() {
  214
+	#
  215
+	# Constructor function. Initialize appropriate member variables.
  216
+	#
  217
+		$this->_initDetab();
  218
+	
  219
+		$this->nested_brackets = 
  220
+			str_repeat('(?>[^\[\]]+|\[', $this->nested_brackets_depth).
  221
+			str_repeat('\])*', $this->nested_brackets_depth);
  222
+	
  223
+		$this->nested_url_parenthesis = 
  224
+			str_repeat('(?>[^()\s]+|\(', $this->nested_url_parenthesis_depth).
  225
+			str_repeat('(?>\)))*', $this->nested_url_parenthesis_depth);
  226
+		
  227
+		# Sort document, block, and span gamut in ascendent priority order.
  228
+		asort($this->document_gamut);
  229
+		asort($this->block_gamut);
  230
+		asort($this->span_gamut);
  231
+	}
  232
+
  233
+
  234
+	# Internal hashes used during transformation.
  235
+	var $urls = array();
  236
+	var $titles = array();
  237
+	var $html_hashes = array();
  238
+	
  239
+	# Status flag to avoid invalid nesting.
  240
+	var $in_anchor = false;
  241
+
  242
+
  243
+	function transform($text) {
  244
+	#
  245
+	# Main function. The order in which other subs are called here is
  246
+	# essential. Link and image substitutions need to happen before
  247
+	# _EscapeSpecialCharsWithinTagAttributes(), so that any *'s or _'s in the <a>
  248
+	# and <img> tags get encoded.
  249
+	#
  250
+		# Clear the global hashes. If we don't clear these, you get conflicts
  251
+		# from other articles when generating a page which contains more than
  252
+		# one article (e.g. an index page that shows the N most recent
  253
+		# articles):
  254
+		$this->urls = array();
  255
+		$this->titles = array();
  256
+		$this->html_hashes = array();
  257
+
  258
+		# Standardize line endings:
  259
+		#   DOS to Unix and Mac to Unix
  260
+		$text = preg_replace('{\r\n?}', "\n", $text);
  261
+
  262
+		# Make sure $text ends with a couple of newlines:
  263
+		$text .= "\n\n";
  264
+
  265
+		# Convert all tabs to spaces.
  266
+		$text = $this->detab($text);
  267
+
  268
+		# Turn block-level HTML blocks into hash entries
  269
+		$text = $this->hashHTMLBlocks($text);
  270
+
  271
+		# Strip any lines consisting only of spaces and tabs.
  272
+		# This makes subsequent regexen easier to write, because we can
  273
+		# match consecutive blank lines with /\n+/ instead of something
  274
+		# contorted like /[ ]*\n+/ .
  275
+		$text = preg_replace('/^[ ]+$/m', '', $text);
  276
+
  277
+		# Run document gamut methods.
  278
+		foreach ($this->document_gamut as $method => $priority) {
  279
+			$text = $this->$method($text);
  280
+		}
  281
+
  282
+		return $text . "\n";
  283
+	}
  284
+	
  285
+	var $document_gamut = array(
  286
+		# Strip link definitions, store in hashes.
  287
+		"stripLinkDefinitions" => 20,
  288
+		
  289
+		"runBasicBlockGamut"   => 30,
  290
+		);
  291
+
  292
+
  293
+	function stripLinkDefinitions($text) {
  294
+	#
  295
+	# Strips link definitions from text, stores the URLs and titles in
  296
+	# hash references.
  297
+	#
  298
+		$less_than_tab = $this->tab_width - 1;
  299
+
  300
+		# Link defs are in the form: ^[id]: url "optional title"
  301
+		$text = preg_replace_callback('{
  302
+							^[ ]{0,'.$less_than_tab.'}\[(.+)\][ ]?:	# id = $1
  303
+							  [ ]*
  304
+							  \n?				# maybe *one* newline
  305
+							  [ ]*
  306
+							<?(\S+?)>?			# url = $2
  307
+							  [ ]*
  308
+							  \n?				# maybe one newline
  309
+							  [ ]*
  310
+							(?:
  311
+								(?<=\s)			# lookbehind for whitespace
  312
+								["(]
  313
+								(.*?)			# title = $3
  314
+								[")]
  315
+								[ ]*
  316
+							)?	# title is optional
  317
+							(?:\n+|\Z)
  318
+			}xm',
  319
+			array(&$this, '_stripLinkDefinitions_callback'),
  320
+			$text);
  321
+		return $text;
  322
+	}
  323
+	function _stripLinkDefinitions_callback($matches) {
  324
+		$link_id = strtolower($matches[1]);
  325
+		$this->urls[$link_id] = $this->encodeAmpsAndAngles($matches[2]);
  326
+		if (isset($matches[3]))
  327
+			$this->titles[$link_id] = str_replace('"', '&quot;', $matches[3]);
  328
+		return ''; # String that will replace the block
  329
+	}
  330
+
  331
+
  332
+	function hashHTMLBlocks($text) {
  333
+		if ($this->no_markup)  return $text;
  334
+
  335
+		$less_than_tab = $this->tab_width - 1;
  336
+
  337
+		# Hashify HTML blocks:
  338
+		# We only want to do this for block-level HTML tags, such as headers,
  339
+		# lists, and tables. That's because we still want to wrap <p>s around
  340
+		# "paragraphs" that are wrapped in non-block-level tags, such as anchors,
  341
+		# phrase emphasis, and spans. The list of tags we're looking for is
  342
+		# hard-coded:
  343
+		#
  344
+		# *  List "a" is made of tags which can be both inline or block-level.
  345
+		#    These will be treated block-level when the start tag is alone on 
  346
+		#    its line, otherwise they're not matched here and will be taken as 
  347
+		#    inline later.
  348
+		# *  List "b" is made of tags which are always block-level;
  349
+		#
  350
+		$block_tags_a = 'ins|del';
  351
+		$block_tags_b = 'p|div|h[1-6]|blockquote|pre|table|dl|ol|ul|address|'.
  352
+						'script|noscript|form|fieldset|iframe|math';
  353
+
  354
+		# Regular expression for the content of a block tag.
  355
+		$nested_tags_level = 4;
  356
+		$attr = '
  357
+			(?>				# optional tag attributes
  358
+			  \s			# starts with whitespace
  359
+			  (?>
  360
+				[^>"/]+		# text outside quotes
  361
+			  |
  362
+				/+(?!>)		# slash not followed by ">"
  363
+			  |
  364
+				"[^"]*"		# text inside double quotes (tolerate ">")
  365
+			  |
  366
+				\'[^\']*\'	# text inside single quotes (tolerate ">")
  367
+			  )*
  368
+			)?	
  369
+			';
  370
+		$content =
  371
+			str_repeat('
  372
+				(?>
  373
+				  [^<]+			# content without tag
  374
+				|
  375
+				  <\2			# nested opening tag
  376
+					'.$attr.'	# attributes
  377
+					(?>
  378
+					  />
  379
+					|
  380
+					  >', $nested_tags_level).	# end of opening tag
  381
+					  '.*?'.					# last level nested tag content
  382
+			str_repeat('
  383
+					  </\2\s*>	# closing nested tag
  384
+					)
  385
+				  |				
  386
+					<(?!/\2\s*>	# other tags with a different name
  387
+				  )
  388
+				)*',
  389
+				$nested_tags_level);
  390
+		$content2 = str_replace('\2', '\3', $content);
  391
+
  392
+		# First, look for nested blocks, e.g.:
  393
+		# 	<div>
  394
+		# 		<div>
  395
+		# 		tags for inner block must be indented.
  396
+		# 		</div>
  397
+		# 	</div>
  398
+		#
  399
+		# The outermost tags must start at the left margin for this to match, and
  400
+		# the inner nested divs must be indented.
  401
+		# We need to do this before the next, more liberal match, because the next
  402
+		# match will start at the first `<div>` and stop at the first `</div>`.
  403
+		$text = preg_replace_callback('{(?>
  404
+			(?>
  405
+				(?<=\n\n)		# Starting after a blank line
  406
+				|				# or
  407
+				\A\n?			# the beginning of the doc
  408
+			)
  409
+			(						# save in $1
  410
+
  411
+			  # Match from `\n<tag>` to `</tag>\n`, handling nested tags 
  412
+			  # in between.
  413
+					
  414
+						[ ]{0,'.$less_than_tab.'}
  415
+						<('.$block_tags_b.')# start tag = $2
  416
+						'.$attr.'>			# attributes followed by > and \n
  417
+						'.$content.'		# content, support nesting
  418
+						</\2>				# the matching end tag
  419
+						[ ]*				# trailing spaces/tabs
  420
+						(?=\n+|\Z)	# followed by a newline or end of document
  421
+
  422
+			| # Special version for tags of group a.
  423
+
  424
+						[ ]{0,'.$less_than_tab.'}
  425
+						<('.$block_tags_a.')# start tag = $3
  426
+						'.$attr.'>[ ]*\n	# attributes followed by >
  427
+						'.$content2.'		# content, support nesting
  428
+						</\3>				# the matching end tag
  429
+						[ ]*				# trailing spaces/tabs
  430
+						(?=\n+|\Z)	# followed by a newline or end of document
  431
+					
  432
+			| # Special case just for <hr />. It was easier to make a special 
  433
+			  # case than to make the other regex more complicated.
  434
+			
  435
+						[ ]{0,'.$less_than_tab.'}
  436
+						<(hr)				# start tag = $2
  437
+						\b					# word break
  438
+						([^<>])*?			# 
  439
+						/?>					# the matching end tag
  440
+						[ ]*
  441
+						(?=\n{2,}|\Z)		# followed by a blank line or end of document
  442
+			
  443
+			| # Special case for standalone HTML comments:
  444
+			
  445
+					[ ]{0,'.$less_than_tab.'}
  446
+					(?s:
  447
+						<!-- .*? -->
  448
+					)
  449
+					[ ]*
  450
+					(?=\n{2,}|\Z)		# followed by a blank line or end of document
  451
+			
  452
+			| # PHP and ASP-style processor instructions (<? and <%)
  453
+			
  454
+					[ ]{0,'.$less_than_tab.'}
  455
+					(?s:
  456
+						<([?%])			# $2
  457
+						.*?
  458
+						\2>
  459
+					)
  460
+					[ ]*
  461
+					(?=\n{2,}|\Z)		# followed by a blank line or end of document
  462
+					
  463
+			)
  464
+			)}Sxmi',
  465
+			array(&$this, '_hashHTMLBlocks_callback'),
  466
+			$text);
  467
+
  468
+		return $text;
  469
+	}
  470
+	function _hashHTMLBlocks_callback($matches) {
  471
+		$text = $matches[1];
  472
+		$key  = $this->hashBlock($text);
  473
+		return "\n\n$key\n\n";
  474
+	}
  475
+	
  476
+	
  477
+	function hashPart($text, $boundary = 'X') {
  478
+	#
  479
+	# Called whenever a tag must be hashed when a function insert an atomic 
  480
+	# element in the text stream. Passing $text to through this function gives
  481
+	# a unique text-token which will be reverted back when calling unhash.
  482
+	#
  483
+	# The $boundary argument specify what character should be used to surround
  484
+	# the token. By convension, "B" is used for block elements that needs not
  485
+	# to be wrapped into paragraph tags at the end, ":" is used for elements
  486
+	# that are word separators and "S" is used for general span-level elements.
  487
+	#
  488
+		# Swap back any tag hash found in $text so we do not have to `unhash`
  489
+		# multiple times at the end.
  490
+		$text = $this->unhash($text);
  491
+		
  492
+		# Then hash the block.
  493
+		static $i = 0;
  494
+		$key = "$boundary\x1A" . ++$i . $boundary;
  495
+		$this->html_hashes[$key] = $text;
  496
+		return $key; # String that will replace the tag.
  497
+	}
  498
+
  499
+
  500
+	function hashBlock($text) {
  501
+	#
  502
+	# Shortcut function for hashPart with block-level boundaries.
  503
+	#
  504
+		return $this->hashPart($text, 'B');
  505
+	}
  506
+
  507
+
  508
+	var $block_gamut = array(
  509
+	#
  510
+	# These are all the transformations that form block-level
  511
+	# tags like paragraphs, headers, and list items.
  512
+	#
  513
+		"doHeaders"         => 10,
  514
+		"doHorizontalRules" => 20,
  515
+		
  516
+		"doLists"           => 40,
  517
+		"doCodeBlocks"      => 50,
  518
+		"doBlockQuotes"     => 60,
  519
+		);
  520
+
  521
+	function runBlockGamut($text) {
  522
+	#
  523
+	# Run block gamut tranformations.
  524
+	#
  525
+		# We need to escape raw HTML in Markdown source before doing anything 
  526
+		# else. This need to be done for each block, and not only at the 
  527
+		# begining in the Markdown function since hashed blocks can be part of
  528
+		# list items and could have been indented. Indented blocks would have 
  529
+		# been seen as a code block in a previous pass of hashHTMLBlocks.
  530
+		$text = $this->hashHTMLBlocks($text);
  531
+		
  532
+		return $this->runBasicBlockGamut($text);
  533
+	}
  534
+	
  535
+	function runBasicBlockGamut($text) {
  536
+	#
  537
+	# Run block gamut tranformations, without hashing HTML blocks. This is 
  538
+	# useful when HTML blocks are known to be already hashed, like in the first
  539
+	# whole-document pass.
  540
+	#
  541
+		foreach ($this->block_gamut as $method => $priority) {
  542
+			$text = $this->$method($text);
  543
+		}
  544
+		
  545
+		# Finally form paragraph and restore hashed blocks.
  546
+		$text = $this->formParagraphs($text);
  547
+
  548
+		return $text;
  549
+	}
  550
+	
  551
+	
  552
+	function doHorizontalRules($text) {
  553
+		# Do Horizontal Rules:
  554
+		return preg_replace(
  555
+			'{
  556
+				^[ ]{0,3}	# Leading space
  557
+				([*-_])		# $1: First marker
  558
+				(?>			# Repeated marker group
  559
+					[ ]{0,2}	# Zero, one, or two spaces.
  560
+					\1			# Marker character
  561
+				){2,}		# Group repeated at least twice
  562
+				[ ]*		# Tailing spaces
  563
+				$			# End of line.
  564
+			}mx',
  565
+			"\n".$this->hashBlock("<hr$this->empty_element_suffix")."\n", 
  566
+			$text);
  567
+	}
  568
+
  569
+
  570
+	var $span_gamut = array(
  571
+	#
  572
+	# These are all the transformations that occur *within* block-level
  573
+	# tags like paragraphs, headers, and list items.
  574
+	#
  575
+		# Process character escapes, code spans, and inline HTML
  576
+		# in one shot.
  577
+		"parseSpan"           => -30,
  578
+
  579
+		# Process anchor and image tags. Images must come first,
  580
+		# because ![foo][f] looks like an anchor.
  581
+		"doImages"            =>  10,
  582
+		"doAnchors"           =>  20,
  583
+		
  584
+		# Make links out of things like `<http://example.com/>`
  585
+		# Must come after doAnchors, because you can use < and >
  586
+		# delimiters in inline links like [this](<url>).
  587
+		"doAutoLinks"         =>  30,
  588
+		"encodeAmpsAndAngles" =>  40,
  589
+
  590
+		"doItalicsAndBold"    =>  50,
  591
+		"doHardBreaks"        =>  60,
  592
+		);
  593
+
  594
+	function runSpanGamut($text) {
  595
+	#
  596
+	# Run span gamut tranformations.
  597
+	#
  598
+		foreach ($this->span_gamut as $method => $priority) {
  599
+			$text = $this->$method($text);
  600
+		}
  601
+
  602
+		return $text;
  603
+	}
  604
+	
  605
+	
  606
+	function doHardBreaks($text) {
  607
+		# Do hard breaks:
  608
+		return preg_replace_callback('/ {2,}\n/', 
  609
+			array(&$this, '_doHardBreaks_callback'), $text);
  610
+	}
  611
+	function _doHardBreaks_callback($matches) {
  612
+		return $this->hashPart("<br$this->empty_element_suffix\n");
  613
+	}
  614
+
  615
+
  616
+	function doAnchors($text) {
  617
+	#
  618
+	# Turn Markdown link shortcuts into XHTML <a> tags.
  619
+	#
  620
+		if ($this->in_anchor) return $text;
  621
+		$this->in_anchor = true;
  622
+		
  623
+		#
  624
+		# First, handle reference-style links: [link text] [id]
  625
+		#
  626
+		$text = preg_replace_callback('{
  627
+			(					# wrap whole match in $1
  628
+			  \[
  629
+				('.$this->nested_brackets.')	# link text = $2
  630
+			  \]
  631
+
  632
+			  [ ]?				# one optional space
  633
+			  (?:\n[ ]*)?		# one optional newline followed by spaces
  634
+
  635
+			  \[
  636
+				(.*?)		# id = $3
  637
+			  \]
  638
+			)
  639
+			}xs',
  640
+			array(&$this, '_doAnchors_reference_callback'), $text);
  641
+
  642
+		#
  643
+		# Next, inline-style links: [link text](url "optional title")
  644
+		#
  645
+		$text = preg_replace_callback('{
  646
+			(				# wrap whole match in $1
  647
+			  \[
  648
+				('.$this->nested_brackets.')	# link text = $2
  649
+			  \]
  650
+			  \(			# literal paren
  651
+				[ ]*
  652
+				(?:
  653
+					<(\S*)>	# href = $3
  654
+				|
  655
+					('.$this->nested_url_parenthesis.')	# href = $4
  656
+				)
  657
+				[ ]*
  658
+				(			# $5
  659
+				  ([\'"])	# quote char = $6
  660
+				  (.*?)		# Title = $7
  661
+				  \6		# matching quote
  662
+				  [ ]*	# ignore any spaces/tabs between closing quote and )
  663
+				)?			# title is optional
  664
+			  \)
  665
+			)
  666
+			}xs',
  667
+			array(&$this, '_DoAnchors_inline_callback'), $text);
  668
+
  669
+		#
  670
+		# Last, handle reference-style shortcuts: [link text]
  671
+		# These must come last in case you've also got [link test][1]
  672
+		# or [link test](/foo)
  673
+		#
  674
+//		$text = preg_replace_callback('{
  675
+//			(					# wrap whole match in $1
  676
+//			  \[
  677
+//				([^\[\]]+)		# link text = $2; can\'t contain [ or ]
  678
+//			  \]
  679
+//			)
  680
+//			}xs',
  681
+//			array(&$this, '_doAnchors_reference_callback'), $text);
  682
+
  683
+		$this->in_anchor = false;
  684
+		return $text;
  685
+	}
  686
+	function _doAnchors_reference_callback($matches) {
  687
+		$whole_match =  $matches[1];
  688
+		$link_text   =  $matches[2];
  689
+		$link_id     =& $matches[3];
  690
+
  691
+		if ($link_id == "") {
  692
+			# for shortcut links like [this][] or [this].
  693
+			$link_id = $link_text;
  694
+		}
  695
+		
  696
+		# lower-case and turn embedded newlines into spaces
  697
+		$link_id = strtolower($link_id);
  698
+		$link_id = preg_replace('{[ ]?\n}', ' ', $link_id);
  699
+
  700
+		if (isset($this->urls[$link_id])) {
  701
+			$url = $this->urls[$link_id];
  702
+			$url = $this->encodeAmpsAndAngles($url);
  703
+			
  704
+			$result = "<a href=\"$url\"";
  705
+			if ( isset( $this->titles[$link_id] ) ) {
  706
+				$title = $this->titles[$link_id];
  707
+				$title = $this->encodeAmpsAndAngles($title);
  708
+				$result .=  " title=\"$title\"";
  709
+			}
  710
+		
  711
+			$link_text = $this->runSpanGamut($link_text);
  712
+			$result .= ">$link_text</a>";
  713
+			$result = $this->hashPart($result);
  714
+		}
  715
+		else {
  716
+			$result = $whole_match;
  717
+		}
  718
+		return $result;
  719
+	}
  720
+	function _doAnchors_inline_callback($matches) {
  721
+		$whole_match	=  $matches[1];
  722
+		$link_text		=  $this->runSpanGamut($matches[2]);
  723
+		$url			=  $matches[3] == '' ? $matches[4] : $matches[3];
  724
+		$title			=& $matches[7];
  725
+
  726
+		$url = $this->encodeAmpsAndAngles($url);
  727
+
  728
+		$result = "<a href=\"$url\"";
  729
+		if (isset($title)) {
  730
+			$title = str_replace('"', '&quot;', $title);
  731
+			$title = $this->encodeAmpsAndAngles($title);
  732
+			$result .=  " title=\"$title\"";
  733
+		}
  734
+		
  735
+		$link_text = $this->runSpanGamut($link_text);
  736
+		$result .= ">$link_text</a>";
  737
+
  738
+		return $this->hashPart($result);
  739
+	}
  740
+
  741
+
  742
+	function doImages($text) {
  743
+	#
  744
+	# Turn Markdown image shortcuts into <img> tags.
  745
+	#
  746
+		#
  747
+		# First, handle reference-style labeled images: ![alt text][id]
  748
+		#
  749
+		$text = preg_replace_callback('{
  750
+			(				# wrap whole match in $1
  751
+			  !\[
  752
+				('.$this->nested_brackets.')		# alt text = $2
  753
+			  \]
  754
+
  755
+			  [ ]?				# one optional space
  756
+			  (?:\n[ ]*)?		# one optional newline followed by spaces
  757
+
  758
+			  \[
  759
+				(.*?)		# id = $3
  760
+			  \]
  761
+
  762
+			)
  763
+			}xs', 
  764
+			array(&$this, '_doImages_reference_callback'), $text);
  765
+
  766
+		#
  767
+		# Next, handle inline images:  ![alt text](url "optional title")
  768
+		# Don't forget: encode * and _
  769
+		#
  770
+		$text = preg_replace_callback('{
  771
+			(				# wrap whole match in $1
  772
+			  !\[
  773
+				('.$this->nested_brackets.')		# alt text = $2
  774
+			  \]
  775
+			  \s?			# One optional whitespace character
  776
+			  \(			# literal paren
  777
+				[ ]*
  778
+				(?:
  779
+					<(\S*)>	# src url = $3
  780
+				|
  781
+					('.$this->nested_url_parenthesis.')	# src url = $4
  782
+				)
  783
+				[ ]*
  784
+				(			# $5
  785
+				  ([\'"])	# quote char = $6
  786
+				  (.*?)		# title = $7
  787
+				  \6		# matching quote
  788
+				  [ ]*
  789
+				)?			# title is optional
  790
+			  \)
  791
+			)
  792
+			}xs',
  793
+			array(&$this, '_doImages_inline_callback'), $text);
  794
+
  795
+		return $text;
  796
+	}
  797
+	function _doImages_reference_callback($matches) {
  798
+		$whole_match = $matches[1];
  799
+		$alt_text    = $matches[2];
  800
+		$link_id     = strtolower($matches[3]);
  801
+
  802
+		if ($link_id == "") {
  803
+			$link_id = strtolower($alt_text); # for shortcut links like ![this][].
  804
+		}
  805
+
  806
+		$alt_text = str_replace('"', '&quot;', $alt_text);
  807
+		if (isset($this->urls[$link_id])) {
  808
+			$url = $this->urls[$link_id];
  809
+			$result = "<img src=\"$url\" alt=\"$alt_text\"";
  810
+			if (isset($this->titles[$link_id])) {
  811
+				$title = $this->titles[$link_id];
  812
+				$result .=  " title=\"$title\"";
  813
+			}
  814
+			$result .= $this->empty_element_suffix;
  815
+			$result = $this->hashPart($result);
  816
+		}
  817
+		else {
  818
+			# If there's no such link ID, leave intact:
  819
+			$result = $whole_match;
  820
+		}
  821
+
  822
+		return $result;
  823
+	}
  824
+	function _doImages_inline_callback($matches) {
  825
+		$whole_match	= $matches[1];
  826
+		$alt_text		= $matches[2];
  827
+		$url			= $matches[3] == '' ? $matches[4] : $matches[3];
  828
+		$title			=& $matches[7];
  829
+
  830
+		$alt_text = str_replace('"', '&quot;', $alt_text);
  831
+		$result = "<img src=\"$url\" alt=\"$alt_text\"";
  832
+		if (isset($title)) {
  833
+			$title = str_replace('"', '&quot;', $title);
  834
+			$result .=  " title=\"$title\""; # $title already quoted
  835
+		}
  836
+		$result .= $this->empty_element_suffix;
  837
+
  838
+		return $this->hashPart($result);
  839
+	}
  840
+
  841
+
  842
+	function doHeaders($text) {
  843
+		# Setext-style headers:
  844
+		#	  Header 1
  845
+		#	  ========
  846
+		#  
  847
+		#	  Header 2
  848
+		#	  --------
  849
+		#
  850
+		$text = preg_replace_callback('{ ^(.+?)[ ]*\n(=+|-+)[ ]*\n+ }mx',
  851
+			array(&$this, '_doHeaders_callback_setext'), $text);
  852
+
  853
+		# atx-style headers:
  854
+		#	# Header 1
  855
+		#	## Header 2
  856
+		#	## Header 2 with closing hashes ##
  857
+		#	...
  858
+		#	###### Header 6
  859
+		#
  860
+		$text = preg_replace_callback('{
  861
+				^(\#{1,6})	# $1 = string of #\'s
  862
+				[ ]*
  863
+				(.+?)		# $2 = Header text
  864
+				[ ]*
  865
+				\#*			# optional closing #\'s (not counted)
  866
+				\n+
  867
+			}xm',
  868
+			array(&$this, '_doHeaders_callback_atx'), $text);
  869
+
  870
+		return $text;
  871
+	}
  872
+	function _doHeaders_callback_setext($matches) {
  873
+		$level = $matches[2]{0} == '=' ? 1 : 2;
  874
+		$block = "<h$level>".$this->runSpanGamut($matches[1])."</h$level>";
  875
+		return "\n" . $this->hashBlock($block) . "\n\n";
  876
+	}
  877
+	function _doHeaders_callback_atx($matches) {
  878
+		$level = strlen($matches[1]);
  879
+		$block = "<h$level>".$this->runSpanGamut($matches[2])."</h$level>";
  880
+		return "\n" . $this->hashBlock($block) . "\n\n";
  881
+	}
  882
+
  883
+
  884
+	function doLists($text) {
  885
+	#
  886
+	# Form HTML ordered (numbered) and unordered (bulleted) lists.
  887
+	#
  888
+		$less_than_tab = $this->tab_width - 1;
  889
+
  890
+		# Re-usable patterns to match list item bullets and number markers:
  891
+		$marker_ul  = '[*+-]';
  892
+		$marker_ol  = '\d+[.]';
  893
+		$marker_any = "(?:$marker_ul|$marker_ol)";
  894
+
  895
+		$markers = array($marker_ul, $marker_ol);
  896
+
  897
+		foreach ($markers as $marker) {
  898
+			# Re-usable pattern to match any entirel ul or ol list:
  899
+			$whole_list = '
  900
+				(								# $1 = whole list
  901
+				  (								# $2
  902
+					[ ]{0,'.$less_than_tab.'}
  903
+					('.$marker.')				# $3 = first list item marker
  904
+					[ ]+
  905
+				  )
  906
+				  (?s:.+?)
  907
+				  (								# $4
  908
+					  \z
  909
+					|
  910
+					  \n{2,}
  911
+					  (?=\S)
  912
+					  (?!						# Negative lookahead for another list item marker
  913
+						[ ]*
  914
+						'.$marker.'[ ]+
  915
+					  )
  916
+				  )
  917
+				)
  918
+			'; // mx
  919
+			
  920
+			# We use a different prefix before nested lists than top-level lists.
  921
+			# See extended comment in _ProcessListItems().
  922
+		
  923
+			if ($this->list_level) {
  924
+				$text = preg_replace_callback('{
  925
+						^
  926
+						'.$whole_list.'
  927
+					}mx',
  928
+					array(&$this, '_doLists_callback'), $text);
  929
+			}
  930
+			else {
  931
+				$text = preg_replace_callback('{
  932
+						(?:(?<=\n)\n|\A\n?) # Must eat the newline
  933
+						'.$whole_list.'
  934
+					}mx',
  935
+					array(&$this, '_doLists_callback'), $text);
  936
+			}
  937
+		}
  938
+
  939
+		return $text;
  940
+	}
  941
+	function _doLists_callback($matches) {
  942
+		# Re-usable patterns to match list item bullets and number markers:
  943
+		$marker_ul  = '[*+-]';
  944
+		$marker_ol  = '\d+[.]';
  945
+		$marker_any = "(?:$marker_ul|$marker_ol)";
  946
+		
  947
+		$list = $matches[1];
  948
+		$list_type = preg_match("/$marker_ul/", $matches[3]) ? "ul" : "ol";
  949
+		
  950
+		$marker_any = ( $list_type == "ul" ? $marker_ul : $marker_ol );
  951
+		
  952
+		$list .= "\n";
  953
+		$result = $this->processListItems($list, $marker_any);
  954
+		
  955
+		$result = $this->hashBlock("<$list_type>\n" . $result . "</$list_type>");
  956
+		return "\n". $result ."\n\n";
  957
+	}
  958
+
  959
+	var $list_level = 0;
  960
+
  961
+	function processListItems($list_str, $marker_any) {
  962
+	#
  963
+	#	Process the contents of a single ordered or unordered list, splitting it
  964
+	#	into individual list items.
  965
+	#
  966
+		# The $this->list_level global keeps track of when we're inside a list.
  967
+		# Each time we enter a list, we increment it; when we leave a list,
  968
+		# we decrement. If it's zero, we're not in a list anymore.
  969
+		#
  970
+		# We do this because when we're not inside a list, we want to treat
  971
+		# something like this:
  972
+		#
  973
+		#		I recommend upgrading to version
  974
+		#		8. Oops, now this line is treated
  975
+		#		as a sub-list.
  976
+		#
  977
+		# As a single paragraph, despite the fact that the second line starts
  978
+		# with a digit-period-space sequence.
  979
+		#
  980
+		# Whereas when we're inside a list (or sub-list), that line will be
  981
+		# treated as the start of a sub-list. What a kludge, huh? This is
  982
+		# an aspect of Markdown's syntax that's hard to parse perfectly
  983
+		# without resorting to mind-reading. Perhaps the solution is to
  984
+		# change the syntax rules such that sub-lists must start with a
  985
+		# starting cardinal number; e.g. "1." or "a.".
  986
+		
  987
+		$this->list_level++;
  988
+
  989
+		# trim trailing blank lines:
  990
+		$list_str = preg_replace("/\n{2,}\\z/", "\n", $list_str);
  991
+
  992
+		$list_str = preg_replace_callback('{
  993
+			(\n)?							# leading line = $1
  994
+			(^[ ]*)						# leading whitespace = $2
  995
+			('.$marker_any.') [ ]+		# list marker = $3
  996
+			((?s:.+?))						# list item text   = $4
  997
+			(?:(\n+(?=\n))|\n)				# tailing blank line = $5
  998
+			(?= \n* (\z | \2 ('.$marker_any.') [ ]+))
  999
+			}xm',
  1000
+			array(&$this, '_processListItems_callback'), $list_str);
  1001
+
  1002
+		$this->list_level--;
  1003
+		return $list_str;
  1004
+	}
  1005
+	function _processListItems_callback($matches) {
  1006
+		$item = $matches[4];
  1007
+		$leading_line =& $matches[1];
  1008
+		$leading_space =& $matches[2];
  1009
+		$tailing_blank_line =& $matches[5];
  1010
+
  1011
+		if ($leading_line || $tailing_blank_line || 
  1012
+			preg_match('/\n{2,}/', $item))
  1013
+		{
  1014
+			$item = $this->runBlockGamut($this->outdent($item)."\n");
  1015
+		}
  1016
+		else {
  1017
+			# Recursion for sub-lists:
  1018
+			$item = $this->doLists($this->outdent($item));
  1019
+			$item = preg_replace('/\n+$/', '', $item);
  1020
+			$item = $this->runSpanGamut($item);
  1021
+		}
  1022
+
  1023
+		return "<li>" . $item . "</li>\n";
  1024
+	}
  1025
+
  1026
+
  1027
+	function doCodeBlocks($text) {
  1028
+	#
  1029
+	#	Process Markdown `<pre><code>` blocks.
  1030
+	#
  1031
+		$text = preg_replace_callback('{
  1032
+				(?:\n\n|\A)
  1033
+				(	            # $1 = the code block -- one or more lines, starting with a space/tab
  1034
+				  (?>
  1035
+					[ ]{'.$this->tab_width.'}  # Lines must start with a tab or a tab-width of spaces
  1036
+					.*\n+
  1037
+				  )+
  1038
+				)
  1039
+				((?=^[ ]{0,'.$this->tab_width.'}\S)|\Z)	# Lookahead for non-space at line-start, or end of doc
  1040
+			}xm',
  1041
+			array(&$this, '_doCodeBlocks_callback'), $text);
  1042
+
  1043
+		return $text;
  1044
+	}
  1045
+	function _doCodeBlocks_callback($matches) {
  1046
+		$codeblock = $matches[1];
  1047
+
  1048
+		$codeblock = $this->outdent($codeblock);
  1049
+		$codeblock = htmlspecialchars($codeblock, ENT_NOQUOTES);
  1050
+
  1051
+		# trim leading newlines and trailing newlines
  1052
+		$codeblock = preg_replace('/\A\n+|\n+\z/', '', $codeblock);
  1053
+
  1054
+		$codeblock = "<pre><code>$codeblock\n</code></pre>";
  1055
+		return "\n\n".$this->hashBlock($codeblock)."\n\n";
  1056
+	}
  1057
+
  1058
+
  1059
+	function makeCodeSpan($code) {
  1060
+	#
  1061
+	# Create a code span markup for $code. Called from handleSpanToken.
  1062
+	#
  1063
+		$code = htmlspecialchars(trim($code), ENT_NOQUOTES);
  1064
+		return $this->hashPart("<code>$code</code>");
  1065
+	}
  1066
+
  1067
+
  1068
+	function doItalicsAndBold($text) {
  1069
+		# <strong> must go first:
  1070
+		$text = preg_replace_callback('{
  1071
+				(						# $1: Marker
  1072
+					(?<!\*\*) \* |		#     (not preceded by two chars of
  1073
+					(?<!__)   _			#      the same marker)
  1074
+				)
  1075
+				\1
  1076
+				(?=\S) 					# Not followed by whitespace 
  1077
+				(?!\1\1)				#   or two others marker chars.
  1078
+				(						# $2: Content
  1079
+					(?>
  1080
+						[^*_]+?			# Anthing not em markers.
  1081
+					|
  1082
+										# Balence any regular emphasis inside.
  1083
+						\1 (?=\S) .+? (?<=\S) \1
  1084
+					|
  1085
+						.				# Allow unbalenced * and _.
  1086
+					)+?
  1087
+				)
  1088
+				(?<=\S) \1\1			# End mark not preceded by whitespace.
  1089
+			}sx',
  1090
+			array(&$this, '_doItalicAndBold_strong_callback'), $text);
  1091
+		# Then <em>:
  1092
+		$text = preg_replace_callback(
  1093
+			'{ ( (?<!\*)\* | (?<!_)_ ) (?=\S) (?! \1) (.+?) (?<=\S)(?<!\s(?=\1).) \1 }sx',
  1094
+			array(&$this, '_doItalicAndBold_em_callback'), $text);
  1095
+
  1096
+		return $text;
  1097
+	}
  1098
+	function _doItalicAndBold_em_callback($matches) {
  1099
+		$text = $matches[2];
  1100
+		$text = $this->runSpanGamut($text);
  1101
+		return $this->hashPart("<em>$text</em>");
  1102
+	}
  1103
+	function _doItalicAndBold_strong_callback($matches) {
  1104
+		$text = $matches[2];
  1105
+		$text = $this->runSpanGamut($text);
  1106
+		return $this->hashPart("<strong>$text</strong>");
  1107
+	}
  1108
+
  1109
+
  1110
+	function doBlockQuotes($text) {
  1111
+		$text = preg_replace_callback('/
  1112
+			  (								# Wrap whole match in $1
  1113
+				(?>
  1114
+				  ^[ ]*>[ ]?			# ">" at the start of a line
  1115
+					.+\n					# rest of the first line
  1116
+				  (.+\n)*					# subsequent consecutive lines
  1117
+				  \n*						# blanks
  1118
+				)+
  1119
+			  )
  1120
+			/xm',
  1121
+			array(&$this, '_doBlockQuotes_callback'), $text);
  1122
+
  1123
+		return $text;
  1124
+	}
  1125
+	function _doBlockQuotes_callback($matches) {
  1126
+		$bq = $matches[1];
  1127
+		# trim one level of quoting - trim whitespace-only lines
  1128
+		$bq = preg_replace('/^[ ]*>[ ]?|^[ ]+$/m', '', $bq);
  1129
+		$bq = $this->runBlockGamut($bq);		# recurse
  1130
+
  1131
+		$bq = preg_replace('/^/m', "  ", $bq);
  1132
+		# These leading spaces cause problem with <pre> content, 
  1133
+		# so we need to fix that:
  1134
+		$bq = preg_replace_callback('{(\s*<pre>.+?</pre>)}sx', 
  1135
+			array(&$this, '_DoBlockQuotes_callback2'), $bq);
  1136
+
  1137
+		return "\n". $this->hashBlock("<blockquote>\n$bq\n</blockquote>")."\n\n";
  1138
+	}
  1139
+	function _doBlockQuotes_callback2($matches) {
  1140
+		$pre = $matches[1];
  1141
+		$pre = preg_replace('/^  /m', '', $pre);
  1142
+		return $pre;
  1143
+	}
  1144
+
  1145
+
  1146
+	function formParagraphs($text) {
  1147
+	#
  1148
+	#	Params:
  1149
+	#		$text - string to process with html <p> tags
  1150
+	#
  1151
+		# Strip leading and trailing lines:
  1152
+		$text = preg_replace('/\A\n+|\n+\z/', '', $text);
  1153
+
  1154
+		$grafs = preg_split('/\n{2,}/', $text, -1, PREG_SPLIT_NO_EMPTY);
  1155
+
  1156
+		#
  1157
+		# Wrap <p> tags and unhashify HTML blocks
  1158
+		#
  1159
+		foreach ($grafs as $key => $value) {
  1160
+			if (!preg_match('/^B\x1A[0-9]+B$/', $value)) {
  1161
+				# Is a paragraph.
  1162
+				$value = $this->runSpanGamut($value);
  1163
+				$value = preg_replace('/^([ ]*)/', "<p>", $value);
  1164
+				$value .= "</p>";
  1165
+				$grafs[$key] = $this->unhash($value);
  1166
+			}
  1167
+			else {
  1168
+				# Is a block.
  1169
+				# Modify elements of @grafs in-place...
  1170
+				$graf = $value;
  1171
+				$block = $this->html_hashes[$graf];
  1172
+				$graf = $block;
  1173
+//				if (preg_match('{
  1174
+//					\A
  1175
+//					(							# $1 = <div> tag
  1176
+//					  <div  \s+
  1177
+//					  [^>]*
  1178
+//					  \b
  1179
+//					  markdown\s*=\s*  ([\'"])	#	$2 = attr quote char
  1180
+//					  1
  1181
+//					  \2
  1182
+//					  [^>]*
  1183
+//					  >
  1184
+//					)
  1185
+//					(							# $3 = contents
  1186
+//					.*
  1187
+//					)
  1188
+//					(</div>)					# $4 = closing tag
  1189
+//					\z
  1190
+//					}xs', $block, $matches))
  1191
+//				{
  1192
+//					list(, $div_open, , $div_content, $div_close) = $matches;
  1193
+//
  1194
+//					# We can't call Markdown(), because that resets the hash;
  1195
+//					# that initialization code should be pulled into its own sub, though.
  1196
+//					$div_content = $this->hashHTMLBlocks($div_content);
  1197
+//					
  1198
+//					# Run document gamut methods on the content.
  1199
+//					foreach ($this->document_gamut as $method => $priority) {
  1200
+//						$div_content = $this->$method($div_content);