Subversion Repositories Applications.papyrus

Rev

Details | Last modification | View Log | RSS feed

Rev Author Line No. Line
831 florian 1
<?php
2
 
3
#
4
# Markdown  -  A text-to-HTML conversion tool for web writers
5
#
6
# Copyright (c) 2004-2005 John Gruber
7
# <http://daringfireball.net/projects/markdown/>
8
#
9
# Copyright (c) 2004-2005 Michel Fortin - PHP Port
10
# <http://www.michelf.com/projects/php-markdown/>
11
#
12
 
13
 
14
global	$MarkdownPHPVersion, $MarkdownSyntaxVersion,
15
		$md_empty_element_suffix, $md_tab_width,
16
		$md_nested_brackets_depth, $md_nested_brackets,
17
		$md_escape_table, $md_backslash_escape_table,
18
		$md_list_level;
19
 
20
$MarkdownPHPVersion    = '1.0.1c'; # Fri 9 Dec 2005
21
$MarkdownSyntaxVersion = '1.0.1';  # Sun 12 Dec 2004
22
 
23
 
24
#
25
# Global default settings:
26
#
27
$md_empty_element_suffix = " />";     # Change to ">" for HTML output
28
$md_tab_width = 4;
29
 
30
#
31
# WordPress settings:
32
#
33
$md_wp_posts    = true;  # Set to false to remove Markdown from posts.
34
$md_wp_comments = true;  # Set to false to remove Markdown from comments.
35
 
36
 
37
# -- WordPress Plugin Interface -----------------------------------------------
38
/*
39
Plugin Name: Markdown
40
Plugin URI: http://www.michelf.com/projects/php-markdown/
41
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>
42
Version: 1.0.1c
43
Author: Michel Fortin
44
Author URI: http://www.michelf.com/
45
*/
46
if (isset($wp_version)) {
47
	# More details about how it works here:
48
	# <http://www.michelf.com/weblog/2005/wordpress-text-flow-vs-markdown/>
49
 
50
	# Post content and excerpts
51
	if ($md_wp_posts) {
52
		remove_filter('the_content',  'wpautop');
53
		remove_filter('the_excerpt',  'wpautop');
54
		add_filter('the_content',     'Markdown', 6);
55
		add_filter('get_the_excerpt', 'Markdown', 6);
56
		add_filter('get_the_excerpt', 'trim', 7);
57
		add_filter('the_excerpt',     'md_add_p');
58
		add_filter('the_excerpt_rss', 'md_strip_p');
59
 
60
		remove_filter('content_save_pre',  'balanceTags', 50);
61
		remove_filter('excerpt_save_pre',  'balanceTags', 50);
62
		add_filter('the_content',  	  'balanceTags', 50);
63
		add_filter('get_the_excerpt', 'balanceTags', 9);
64
 
65
		function md_add_p($text) {
66
			if (strlen($text) == 0) return;
67
			if (strcasecmp(substr($text, -3), '<p>') == 0) return $text;
68
			return '<p>'.$text.'</p>';
69
		}
70
		function md_strip_p($t) { return preg_replace('{</?[pP]>}', '', $t); }
71
	}
72
 
73
	# Comments
74
	if ($md_wp_comments) {
75
		remove_filter('comment_text', 'wpautop');
76
		remove_filter('comment_text', 'make_clickable');
77
		add_filter('pre_comment_content', 'Markdown', 6);
78
		add_filter('pre_comment_content', 'md_hide_tags', 8);
79
		add_filter('pre_comment_content', 'md_show_tags', 12);
80
		add_filter('get_comment_text',    'Markdown', 6);
81
		add_filter('get_comment_excerpt', 'Markdown', 6);
82
		add_filter('get_comment_excerpt', 'md_strip_p', 7);
83
 
84
		global $md_hidden_tags;
85
		$md_hidden_tags = array(
86
			'<p>'	=> md5('<p>'),		'</p>'	=> md5('</p>'),
87
			'<pre>'	=> md5('<pre>'),	'</pre>'=> md5('</pre>'),
88
			'<ol>'	=> md5('<ol>'),		'</ol>'	=> md5('</ol>'),
89
			'<ul>'	=> md5('<ul>'),		'</ul>'	=> md5('</ul>'),
90
			'<li>'	=> md5('<li>'),		'</li>'	=> md5('</li>'),
91
			);
92
 
93
		function md_hide_tags($text) {
94
			global $md_hidden_tags;
95
			return str_replace(array_keys($md_hidden_tags),
96
								array_values($md_hidden_tags), $text);
97
		}
98
		function md_show_tags($text) {
99
			global $md_hidden_tags;
100
			return str_replace(array_values($md_hidden_tags),
101
								array_keys($md_hidden_tags), $text);
102
		}
103
	}
104
}
105
 
106
 
107
# -- bBlog Plugin Info --------------------------------------------------------
108
function identify_modifier_markdown() {
109
	global $MarkdownPHPVersion;
110
	return array(
111
		'name'			=> 'markdown',
112
		'type'			=> 'modifier',
113
		'nicename'		=> 'Markdown',
114
		'description'	=> 'A text-to-HTML conversion tool for web writers',
115
		'authors'		=> 'Michel Fortin and John Gruber',
116
		'licence'		=> 'GPL',
117
		'version'		=> $MarkdownPHPVersion,
118
		'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>'
119
	);
120
}
121
 
122
# -- Smarty Modifier Interface ------------------------------------------------
123
function smarty_modifier_markdown($text) {
124
	return Markdown($text);
125
}
126
 
127
# -- Textile Compatibility Mode -----------------------------------------------
128
# Rename this file to "classTextile.php" and it can replace Textile anywhere.
129
if (strcasecmp(substr(__FILE__, -16), "classTextile.php") == 0) {
130
	# Try to include PHP SmartyPants. Should be in the same directory.
131
	@include_once 'smartypants.php';
132
	# Fake Textile class. It calls Markdown instead.
133
	class Textile {
134
		function TextileThis($text, $lite='', $encode='', $noimage='', $strict='') {
135
			if ($lite == '' && $encode == '')   $text = Markdown($text);
136
			if (function_exists('SmartyPants')) $text = SmartyPants($text);
137
			return $text;
138
		}
139
	}
140
}
141
 
142
 
143
# -- Phorum Module Info --------------------------------------------------------
144
/* phorum module info
145
hook:  format|phorum_Markdown
146
title: Markdown
147
desc:  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.<br />http://daringfireball.net/projects/markdown/syntax<br />http://www.michelf.com/projects/php-markdown/
148
*/
149
function phorum_Markdown ($data)
150
{
151
    foreach($data as $key=>$message){
152
        if(!empty($message["body"])){
153
            $message["body"] = str_replace('<br phorum="true" />', '', $message["body"]);
154
            $data[$key]["body"] = Markdown($message["body"]);
155
        }
156
    }
157
    return ($data);
158
}
159
 
160
 
161
#
162
# Globals:
163
#
164
 
165
# Regex to match balanced [brackets].
166
# Needed to insert a maximum bracked depth while converting to PHP.
167
$md_nested_brackets_depth = 6;
168
$md_nested_brackets =
169
	str_repeat('(?>[^\[\]]+|\[', $md_nested_brackets_depth).
170
	str_repeat('\])*', $md_nested_brackets_depth);
171
 
172
# Table of hash values for escaped characters:
173
$md_escape_table = array(
174
	"\\" => md5("\\"),
175
	"`" => md5("`"),
176
	"*" => md5("*"),
177
	"_" => md5("_"),
178
	"{" => md5("{"),
179
	"}" => md5("}"),
180
	"[" => md5("["),
181
	"]" => md5("]"),
182
	"(" => md5("("),
183
	")" => md5(")"),
184
	">" => md5(">"),
185
	"#" => md5("#"),
186
	"+" => md5("+"),
187
	"-" => md5("-"),
188
	"." => md5("."),
189
	"!" => md5("!")
190
);
191
# Create an identical table but for escaped characters.
192
$md_backslash_escape_table;
193
foreach ($md_escape_table as $key => $char)
194
	$md_backslash_escape_table["\\$key"] = $char;
195
 
196
 
197
function Markdown($text) {
198
#
199
# Main function. The order in which other subs are called here is
200
# essential. Link and image substitutions need to happen before
201
# _EscapeSpecialCharsWithinTagAttributes(), so that any *'s or _'s in the <a>
202
# and <img> tags get encoded.
203
#
204
	# Clear the global hashes. If we don't clear these, you get conflicts
205
	# from other articles when generating a page which contains more than
206
	# one article (e.g. an index page that shows the N most recent
207
	# articles):
208
	global $md_urls, $md_titles, $md_html_blocks;
209
	$md_urls = array();
210
	$md_titles = array();
211
	$md_html_blocks = array();
212
 
213
	# Standardize line endings:
214
	#   DOS to Unix and Mac to Unix
215
	$text = str_replace(array("\r\n", "\r"), "\n", $text);
216
 
217
	# Make sure $text ends with a couple of newlines:
218
	$text .= "\n\n";
219
 
220
	# Convert all tabs to spaces.
221
	$text = _Detab($text);
222
 
223
	# Strip any lines consisting only of spaces and tabs.
224
	# This makes subsequent regexen easier to write, because we can
225
	# match consecutive blank lines with /\n+/ instead of something
226
	# contorted like /[ \t]*\n+/ .
227
	$text = preg_replace('/^[ \t]+$/m', '', $text);
228
 
229
	# Turn block-level HTML blocks into hash entries
230
	$text = _HashHTMLBlocks($text);
231
 
232
	# Strip link definitions, store in hashes.
233
	$text = _StripLinkDefinitions($text);
234
 
235
	$text = _RunBlockGamut($text);
236
 
237
	$text = _UnescapeSpecialChars($text);
238
 
239
	return $text . "\n";
240
}
241
 
242
 
243
function _StripLinkDefinitions($text) {
244
#
245
# Strips link definitions from text, stores the URLs and titles in
246
# hash references.
247
#
248
	global $md_tab_width;
249
	$less_than_tab = $md_tab_width - 1;
250
 
251
	# Link defs are in the form: ^[id]: url "optional title"
252
	$text = preg_replace_callback('{
253
						^[ ]{0,'.$less_than_tab.'}\[(.+)\]:	# id = $1
254
						  [ \t]*
255
						  \n?				# maybe *one* newline
256
						  [ \t]*
257
						<?(\S+?)>?			# url = $2
258
						  [ \t]*
259
						  \n?				# maybe one newline
260
						  [ \t]*
261
						(?:
262
							(?<=\s)			# lookbehind for whitespace
263
							["(]
264
							(.+?)			# title = $3
265
							[")]
266
							[ \t]*
267
						)?	# title is optional
268
						(?:\n+|\Z)
269
		}xm',
270
		'_StripLinkDefinitions_callback',
271
		$text);
272
	return $text;
273
}
274
function _StripLinkDefinitions_callback($matches) {
275
	global $md_urls, $md_titles;
276
	$link_id = strtolower($matches[1]);
277
	$md_urls[$link_id] = _EncodeAmpsAndAngles($matches[2]);
278
	if (isset($matches[3]))
279
		$md_titles[$link_id] = str_replace('"', '&quot;', $matches[3]);
280
	return ''; # String that will replace the block
281
}
282
 
283
 
284
function _HashHTMLBlocks($text) {
285
	global $md_tab_width;
286
	$less_than_tab = $md_tab_width - 1;
287
 
288
	# Hashify HTML blocks:
289
	# We only want to do this for block-level HTML tags, such as headers,
290
	# lists, and tables. That's because we still want to wrap <p>s around
291
	# "paragraphs" that are wrapped in non-block-level tags, such as anchors,
292
	# phrase emphasis, and spans. The list of tags we're looking for is
293
	# hard-coded:
294
	$block_tags_a = 'p|div|h[1-6]|blockquote|pre|table|dl|ol|ul|'.
295
					'script|noscript|form|fieldset|iframe|math|ins|del';
296
	$block_tags_b = 'p|div|h[1-6]|blockquote|pre|table|dl|ol|ul|'.
297
					'script|noscript|form|fieldset|iframe|math';
298
 
299
	# First, look for nested blocks, e.g.:
300
	# 	<div>
301
	# 		<div>
302
	# 		tags for inner block must be indented.
303
	# 		</div>
304
	# 	</div>
305
	#
306
	# The outermost tags must start at the left margin for this to match, and
307
	# the inner nested divs must be indented.
308
	# We need to do this before the next, more liberal match, because the next
309
	# match will start at the first `<div>` and stop at the first `</div>`.
310
	$text = preg_replace_callback("{
311
				(						# save in $1
312
					^					# start of line  (with /m)
313
					<($block_tags_a)	# start tag = $2
314
					\\b					# word break
315
					(.*\\n)*?			# any number of lines, minimally matching
316
					</\\2>				# the matching end tag
317
					[ \\t]*				# trailing spaces/tabs
318
					(?=\\n+|\\Z)	# followed by a newline or end of document
319
				)
320
		}xm",
321
		'_HashHTMLBlocks_callback',
322
		$text);
323
 
324
	#
325
	# Now match more liberally, simply from `\n<tag>` to `</tag>\n`
326
	#
327
	$text = preg_replace_callback("{
328
				(						# save in $1
329
					^					# start of line  (with /m)
330
					<($block_tags_b)	# start tag = $2
331
					\\b					# word break
332
					(.*\\n)*?			# any number of lines, minimally matching
333
					.*</\\2>				# the matching end tag
334
					[ \\t]*				# trailing spaces/tabs
335
					(?=\\n+|\\Z)	# followed by a newline or end of document
336
				)
337
		}xm",
338
		'_HashHTMLBlocks_callback',
339
		$text);
340
 
341
	# Special case just for <hr />. It was easier to make a special case than
342
	# to make the other regex more complicated.
343
	$text = preg_replace_callback('{
344
				(?:
345
					(?<=\n\n)		# Starting after a blank line
346
					|				# or
347
					\A\n?			# the beginning of the doc
348
				)
349
				(						# save in $1
350
					[ ]{0,'.$less_than_tab.'}
351
					<(hr)				# start tag = $2
352
					\b					# word break
353
					([^<>])*?			#
354
					/?>					# the matching end tag
355
					[ \t]*
356
					(?=\n{2,}|\Z)		# followed by a blank line or end of document
357
				)
358
		}x',
359
		'_HashHTMLBlocks_callback',
360
		$text);
361
 
362
	# Special case for standalone HTML comments:
363
	$text = preg_replace_callback('{
364
				(?:
365
					(?<=\n\n)		# Starting after a blank line
366
					|				# or
367
					\A\n?			# the beginning of the doc
368
				)
369
				(						# save in $1
370
					[ ]{0,'.$less_than_tab.'}
371
					(?s:
372
						<!
373
						(--.*?--\s*)+
374
						>
375
					)
376
					[ \t]*
377
					(?=\n{2,}|\Z)		# followed by a blank line or end of document
378
				)
379
			}x',
380
			'_HashHTMLBlocks_callback',
381
			$text);
382
 
383
	return $text;
384
}
385
function _HashHTMLBlocks_callback($matches) {
386
	global $md_html_blocks;
387
	$text = $matches[1];
388
	$key = md5($text);
389
	$md_html_blocks[$key] = $text;
390
	return "\n\n$key\n\n"; # String that will replace the block
391
}
392
 
393
 
394
function _RunBlockGamut($text) {
395
#
396
# These are all the transformations that form block-level
397
# tags like paragraphs, headers, and list items.
398
#
399
	global $md_empty_element_suffix;
400
 
401
	$text = _DoHeaders($text);
402
 
403
	# Do Horizontal Rules:
404
	$text = preg_replace(
405
		array('{^[ ]{0,2}([ ]?\*[ ]?){3,}[ \t]*$}mx',
406
			  '{^[ ]{0,2}([ ]? -[ ]?){3,}[ \t]*$}mx',
407
			  '{^[ ]{0,2}([ ]? _[ ]?){3,}[ \t]*$}mx'),
408
		"\n<hr$md_empty_element_suffix\n",
409
		$text);
410
 
411
	$text = _DoLists($text);
412
	$text = _DoCodeBlocks($text);
413
	$text = _DoBlockQuotes($text);
414
 
415
	# We already ran _HashHTMLBlocks() before, in Markdown(), but that
416
	# was to escape raw HTML in the original Markdown source. This time,
417
	# we're escaping the markup we've just created, so that we don't wrap
418
	# <p> tags around block-level tags.
419
	$text = _HashHTMLBlocks($text);
420
	$text = _FormParagraphs($text);
421
 
422
	return $text;
423
}
424
 
425
 
426
function _RunSpanGamut($text) {
427
#
428
# These are all the transformations that occur *within* block-level
429
# tags like paragraphs, headers, and list items.
430
#
431
	global $md_empty_element_suffix;
432
 
433
	$text = _DoCodeSpans($text);
434
 
435
	$text = _EscapeSpecialChars($text);
436
 
437
	# Process anchor and image tags. Images must come first,
438
	# because ![foo][f] looks like an anchor.
439
	$text = _DoImages($text);
440
	$text = _DoAnchors($text);
441
 
442
	# Make links out of things like `<http://example.com/>`
443
	# Must come after _DoAnchors(), because you can use < and >
444
	# delimiters in inline links like [this](<url>).
445
	$text = _DoAutoLinks($text);
446
	$text = _EncodeAmpsAndAngles($text);
447
	$text = _DoItalicsAndBold($text);
448
 
449
	# Do hard breaks:
450
	$text = preg_replace('/ {2,}\n/', "<br$md_empty_element_suffix\n", $text);
451
 
452
	return $text;
453
}
454
 
455
 
456
function _EscapeSpecialChars($text) {
457
	global $md_escape_table;
458
	$tokens = _TokenizeHTML($text);
459
 
460
	$text = '';   # rebuild $text from the tokens
461
#	$in_pre = 0;  # Keep track of when we're inside <pre> or <code> tags.
462
#	$tags_to_skip = "!<(/?)(?:pre|code|kbd|script|math)[\s>]!";
463
 
464
	foreach ($tokens as $cur_token) {
465
		if ($cur_token[0] == 'tag') {
466
			# Within tags, encode * and _ so they don't conflict
467
			# with their use in Markdown for italics and strong.
468
			# We're replacing each such character with its
469
			# corresponding MD5 checksum value; this is likely
470
			# overkill, but it should prevent us from colliding
471
			# with the escape values by accident.
472
			$cur_token[1] = str_replace(array('*', '_'),
473
				array($md_escape_table['*'], $md_escape_table['_']),
474
				$cur_token[1]);
475
			$text .= $cur_token[1];
476
		} else {
477
			$t = $cur_token[1];
478
			$t = _EncodeBackslashEscapes($t);
479
			$text .= $t;
480
		}
481
	}
482
	return $text;
483
}
484
 
485
 
486
function _DoAnchors($text) {
487
#
488
# Turn Markdown link shortcuts into XHTML <a> tags.
489
#
490
	global $md_nested_brackets;
491
	#
492
	# First, handle reference-style links: [link text] [id]
493
	#
494
	$text = preg_replace_callback("{
495
		(					# wrap whole match in $1
496
		  \\[
497
			($md_nested_brackets)	# link text = $2
498
		  \\]
499
 
500
		  [ ]?				# one optional space
501
		  (?:\\n[ ]*)?		# one optional newline followed by spaces
502
 
503
		  \\[
504
			(.*?)		# id = $3
505
		  \\]
506
		)
507
		}xs",
508
		'_DoAnchors_reference_callback', $text);
509
 
510
	#
511
	# Next, inline-style links: [link text](url "optional title")
512
	#
513
	$text = preg_replace_callback("{
514
		(				# wrap whole match in $1
515
		  \\[
516
			($md_nested_brackets)	# link text = $2
517
		  \\]
518
		  \\(			# literal paren
519
			[ \\t]*
520
			<?(.*?)>?	# href = $3
521
			[ \\t]*
522
			(			# $4
523
			  (['\"])	# quote char = $5
524
			  (.*?)		# Title = $6
525
			  \\5		# matching quote
526
			)?			# title is optional
527
		  \\)
528
		)
529
		}xs",
530
		'_DoAnchors_inline_callback', $text);
531
 
532
	return $text;
533
}
534
function _DoAnchors_reference_callback($matches) {
535
	global $md_urls, $md_titles, $md_escape_table;
536
	$whole_match = $matches[1];
537
	$link_text   = $matches[2];
538
	$link_id     = strtolower($matches[3]);
539
 
540
	if ($link_id == "") {
541
		$link_id = strtolower($link_text); # for shortcut links like [this][].
542
	}
543
 
544
	if (isset($md_urls[$link_id])) {
545
		$url = $md_urls[$link_id];
546
		# We've got to encode these to avoid conflicting with italics/bold.
547
		$url = str_replace(array('*', '_'),
548
						   array($md_escape_table['*'], $md_escape_table['_']),
549
						   $url);
550
		$result = "<a href=\"$url\"";
551
		if ( isset( $md_titles[$link_id] ) ) {
552
			$title = $md_titles[$link_id];
553
			$title = str_replace(array('*',     '_'),
554
								 array($md_escape_table['*'],
555
									   $md_escape_table['_']), $title);
556
			$result .=  " title=\"$title\"";
557
		}
558
		$result .= ">$link_text</a>";
559
	}
560
	else {
561
		$result = $whole_match;
562
	}
563
	return $result;
564
}
565
function _DoAnchors_inline_callback($matches) {
566
	global $md_escape_table;
567
	$whole_match	= $matches[1];
568
	$link_text		= $matches[2];
569
	$url			= $matches[3];
570
	$title			=& $matches[6];
571
 
572
	# We've got to encode these to avoid conflicting with italics/bold.
573
	$url = str_replace(array('*', '_'),
574
					   array($md_escape_table['*'], $md_escape_table['_']),
575
					   $url);
576
	$result = "<a href=\"$url\"";
577
	if (isset($title)) {
578
		$title = str_replace('"', '&quot;', $title);
579
		$title = str_replace(array('*', '_'),
580
							 array($md_escape_table['*'], $md_escape_table['_']),
581
							 $title);
582
		$result .=  " title=\"$title\"";
583
	}
584
 
585
	$result .= ">$link_text</a>";
586
 
587
	return $result;
588
}
589
 
590
 
591
function _DoImages($text) {
592
#
593
# Turn Markdown image shortcuts into <img> tags.
594
#
595
	global $md_nested_brackets;
596
 
597
	#
598
	# First, handle reference-style labeled images: ![alt text][id]
599
	#
600
	$text = preg_replace_callback('{
601
		(				# wrap whole match in $1
602
		  !\[
603
			('.$md_nested_brackets.')		# alt text = $2
604
		  \]
605
 
606
		  [ ]?				# one optional space
607
		  (?:\n[ ]*)?		# one optional newline followed by spaces
608
 
609
		  \[
610
			(.*?)		# id = $3
611
		  \]
612
 
613
		)
614
		}xs',
615
		'_DoImages_reference_callback', $text);
616
 
617
	#
618
	# Next, handle inline images:  ![alt text](url "optional title")
619
	# Don't forget: encode * and _
620
 
621
	$text = preg_replace_callback('{
622
		(				# wrap whole match in $1
623
		  !\[
624
			('.$md_nested_brackets.')		# alt text = $2
625
		  \]
626
		  \(			# literal paren
627
			[ \t]*
628
			<?(\S+?)>?	# src url = $3
629
			[ \t]*
630
			(			# $4
631
			  ([\'"])	# quote char = $5
632
			  (.*?)		# title = $6
633
			  \5		# matching quote
634
			  [ \t]*
635
			)?			# title is optional
636
		  \)
637
		)
638
		}xs',
639
		'_DoImages_inline_callback', $text);
640
 
641
	return $text;
642
}
643
function _DoImages_reference_callback($matches) {
644
	global $md_urls, $md_titles, $md_empty_element_suffix, $md_escape_table;
645
	$whole_match = $matches[1];
646
	$alt_text    = $matches[2];
647
	$link_id     = strtolower($matches[3]);
648
 
649
	if ($link_id == "") {
650
		$link_id = strtolower($alt_text); # for shortcut links like ![this][].
651
	}
652
 
653
	$alt_text = str_replace('"', '&quot;', $alt_text);
654
	if (isset($md_urls[$link_id])) {
655
		$url = $md_urls[$link_id];
656
		# We've got to encode these to avoid conflicting with italics/bold.
657
		$url = str_replace(array('*', '_'),
658
						   array($md_escape_table['*'], $md_escape_table['_']),
659
						   $url);
660
		$result = "<img src=\"$url\" alt=\"$alt_text\"";
661
		if (isset($md_titles[$link_id])) {
662
			$title = $md_titles[$link_id];
663
			$title = str_replace(array('*', '_'),
664
								 array($md_escape_table['*'],
665
									   $md_escape_table['_']), $title);
666
			$result .=  " title=\"$title\"";
667
		}
668
		$result .= $md_empty_element_suffix;
669
	}
670
	else {
671
		# If there's no such link ID, leave intact:
672
		$result = $whole_match;
673
	}
674
 
675
	return $result;
676
}
677
function _DoImages_inline_callback($matches) {
678
	global $md_empty_element_suffix, $md_escape_table;
679
	$whole_match	= $matches[1];
680
	$alt_text		= $matches[2];
681
	$url			= $matches[3];
682
	$title			= '';
683
	if (isset($matches[6])) {
684
		$title		= $matches[6];
685
	}
686
 
687
	$alt_text = str_replace('"', '&quot;', $alt_text);
688
	$title    = str_replace('"', '&quot;', $title);
689
	# We've got to encode these to avoid conflicting with italics/bold.
690
	$url = str_replace(array('*', '_'),
691
					   array($md_escape_table['*'], $md_escape_table['_']),
692
					   $url);
693
	$result = "<img src=\"$url\" alt=\"$alt_text\"";
694
	if (isset($title)) {
695
		$title = str_replace(array('*', '_'),
696
							 array($md_escape_table['*'], $md_escape_table['_']),
697
							 $title);
698
		$result .=  " title=\"$title\""; # $title already quoted
699
	}
700
	$result .= $md_empty_element_suffix;
701
 
702
	return $result;
703
}
704
 
705
 
706
function _DoHeaders($text) {
707
	# Setext-style headers:
708
	#	  Header 1
709
	#	  ========
710
	#
711
	#	  Header 2
712
	#	  --------
713
	#
714
	$text = preg_replace(
715
		array('{ ^(.+)[ \t]*\n=+[ \t]*\n+ }emx',
716
			  '{ ^(.+)[ \t]*\n-+[ \t]*\n+ }emx'),
717
		array("'<h1>'._RunSpanGamut(_UnslashQuotes('\\1')).'</h1>\n\n'",
718
			  "'<h2>'._RunSpanGamut(_UnslashQuotes('\\1')).'</h2>\n\n'"),
719
		$text);
720
 
721
	# atx-style headers:
722
	#	# Header 1
723
	#	## Header 2
724
	#	## Header 2 with closing hashes ##
725
	#	...
726
	#	###### Header 6
727
	#
728
	$text = preg_replace("{
729
			^(\\#{1,6})	# $1 = string of #'s
730
			[ \\t]*
731
			(.+?)		# $2 = Header text
732
			[ \\t]*
733
			\\#*			# optional closing #'s (not counted)
734
			\\n+
735
		}xme",
736
		"'<h'.strlen('\\1').'>'._RunSpanGamut(_UnslashQuotes('\\2')).'</h'.strlen('\\1').'>\n\n'",
737
		$text);
738
 
739
	return $text;
740
}
741
 
742
 
743
function _DoLists($text) {
744
#
745
# Form HTML ordered (numbered) and unordered (bulleted) lists.
746
#
747
	global $md_tab_width, $md_list_level;
748
	$less_than_tab = $md_tab_width - 1;
749
 
750
	# Re-usable patterns to match list item bullets and number markers:
751
	$marker_ul  = '[*+-]';
752
	$marker_ol  = '\d+[.]';
753
	$marker_any = "(?:$marker_ul|$marker_ol)";
754
 
755
	$markers = array($marker_ul, $marker_ol);
756
 
757
	foreach ($markers as $marker) {
758
		# Re-usable pattern to match any entirel ul or ol list:
759
		$whole_list = '
760
			(								# $1 = whole list
761
			  (								# $2
762
				[ ]{0,'.$less_than_tab.'}
763
				('.$marker.')				# $3 = first list item marker
764
				[ \t]+
765
			  )
766
			  (?s:.+?)
767
			  (								# $4
768
				  \z
769
				|
770
				  \n{2,}
771
				  (?=\S)
772
				  (?!						# Negative lookahead for another list item marker
773
					[ \t]*
774
					'.$marker.'[ \t]+
775
				  )
776
			  )
777
			)
778
		'; // mx
779
 
780
		# We use a different prefix before nested lists than top-level lists.
781
		# See extended comment in _ProcessListItems().
782
 
783
		if ($md_list_level) {
784
			$text = preg_replace_callback('{
785
					^
786
					'.$whole_list.'
787
				}mx',
788
				'_DoLists_callback_top', $text);
789
		}
790
		else {
791
			$text = preg_replace_callback('{
792
					(?:(?<=\n\n)|\A\n?)
793
					'.$whole_list.'
794
				}mx',
795
				'_DoLists_callback_nested', $text);
796
		}
797
	}
798
 
799
	return $text;
800
}
801
function _DoLists_callback_top($matches) {
802
	# Re-usable patterns to match list item bullets and number markers:
803
	$marker_ul  = '[*+-]';
804
	$marker_ol  = '\d+[.]';
805
	$marker_any = "(?:$marker_ul|$marker_ol)";
806
 
807
	$list = $matches[1];
808
	$list_type = preg_match("/$marker_ul/", $matches[3]) ? "ul" : "ol";
809
 
810
	$marker_any = ( $list_type == "ul" ? $marker_ul : $marker_ol );
811
 
812
	# Turn double returns into triple returns, so that we can make a
813
	# paragraph for the last item in a list, if necessary:
814
	$list = preg_replace("/\n{2,}/", "\n\n\n", $list);
815
	$result = _ProcessListItems($list, $marker_any);
816
 
817
	# Trim any trailing whitespace, to put the closing `</$list_type>`
818
	# up on the preceding line, to get it past the current stupid
819
	# HTML block parser. This is a hack to work around the terrible
820
	# hack that is the HTML block parser.
821
	$result = rtrim($result);
822
	$result = "<$list_type>" . $result . "</$list_type>\n";
823
	return $result;
824
}
825
function _DoLists_callback_nested($matches) {
826
	# Re-usable patterns to match list item bullets and number markers:
827
	$marker_ul  = '[*+-]';
828
	$marker_ol  = '\d+[.]';
829
	$marker_any = "(?:$marker_ul|$marker_ol)";
830
 
831
	$list = $matches[1];
832
	$list_type = preg_match("/$marker_ul/", $matches[3]) ? "ul" : "ol";
833
 
834
	$marker_any = ( $list_type == "ul" ? $marker_ul : $marker_ol );
835
 
836
	# Turn double returns into triple returns, so that we can make a
837
	# paragraph for the last item in a list, if necessary:
838
	$list = preg_replace("/\n{2,}/", "\n\n\n", $list);
839
	$result = _ProcessListItems($list, $marker_any);
840
	$result = "<$list_type>\n" . $result . "</$list_type>\n";
841
	return $result;
842
}
843
 
844
 
845
function _ProcessListItems($list_str, $marker_any) {
846
#
847
#	Process the contents of a single ordered or unordered list, splitting it
848
#	into individual list items.
849
#
850
	global $md_list_level;
851
 
852
	# The $md_list_level global keeps track of when we're inside a list.
853
	# Each time we enter a list, we increment it; when we leave a list,
854
	# we decrement. If it's zero, we're not in a list anymore.
855
	#
856
	# We do this because when we're not inside a list, we want to treat
857
	# something like this:
858
	#
859
	#		I recommend upgrading to version
860
	#		8. Oops, now this line is treated
861
	#		as a sub-list.
862
	#
863
	# As a single paragraph, despite the fact that the second line starts
864
	# with a digit-period-space sequence.
865
	#
866
	# Whereas when we're inside a list (or sub-list), that line will be
867
	# treated as the start of a sub-list. What a kludge, huh? This is
868
	# an aspect of Markdown's syntax that's hard to parse perfectly
869
	# without resorting to mind-reading. Perhaps the solution is to
870
	# change the syntax rules such that sub-lists must start with a
871
	# starting cardinal number; e.g. "1." or "a.".
872
 
873
	$md_list_level++;
874
 
875
	# trim trailing blank lines:
876
	$list_str = preg_replace("/\n{2,}\\z/", "\n", $list_str);
877
 
878
	$list_str = preg_replace_callback('{
879
		(\n)?							# leading line = $1
880
		(^[ \t]*)						# leading whitespace = $2
881
		('.$marker_any.') [ \t]+		# list marker = $3
882
		((?s:.+?)						# list item text   = $4
883
		(\n{1,2}))
884
		(?= \n* (\z | \2 ('.$marker_any.') [ \t]+))
885
		}xm',
886
		'_ProcessListItems_callback', $list_str);
887
 
888
	$md_list_level--;
889
	return $list_str;
890
}
891
function _ProcessListItems_callback($matches) {
892
	$item = $matches[4];
893
	$leading_line =& $matches[1];
894
	$leading_space =& $matches[2];
895
 
896
	if ($leading_line || preg_match('/\n{2,}/', $item)) {
897
		$item = _RunBlockGamut(_Outdent($item));
898
	}
899
	else {
900
		# Recursion for sub-lists:
901
		$item = _DoLists(_Outdent($item));
902
		$item = preg_replace('/\n+$/', '', $item);
903
		$item = _RunSpanGamut($item);
904
	}
905
 
906
	return "<li>" . $item . "</li>\n";
907
}
908
 
909
 
910
function _DoCodeBlocks($text) {
911
#
912
#	Process Markdown `<pre><code>` blocks.
913
#
914
	global $md_tab_width;
915
	$text = preg_replace_callback('{
916
			(?:\n\n|\A)
917
			(	            # $1 = the code block -- one or more lines, starting with a space/tab
918
			  (?:
919
				(?:[ ]{'.$md_tab_width.'} | \t)  # Lines must start with a tab or a tab-width of spaces
920
				.*\n+
921
			  )+
922
			)
923
			((?=^[ ]{0,'.$md_tab_width.'}\S)|\Z)	# Lookahead for non-space at line-start, or end of doc
924
		}xm',
925
		'_DoCodeBlocks_callback', $text);
926
 
927
	return $text;
928
}
929
function _DoCodeBlocks_callback($matches) {
930
	$codeblock = $matches[1];
931
 
932
	$codeblock = _EncodeCode(_Outdent($codeblock));
933
//	$codeblock = _Detab($codeblock);
934
	# trim leading newlines and trailing whitespace
935
	$codeblock = preg_replace(array('/\A\n+/', '/\s+\z/'), '', $codeblock);
936
 
937
	$result = "\n\n<pre><code>" . $codeblock . "\n</code></pre>\n\n";
938
 
939
	return $result;
940
}
941
 
942
 
943
function _DoCodeSpans($text) {
944
#
945
# 	*	Backtick quotes are used for <code></code> spans.
946
#
947
# 	*	You can use multiple backticks as the delimiters if you want to
948
# 		include literal backticks in the code span. So, this input:
949
#
950
#		  Just type ``foo `bar` baz`` at the prompt.
951
#
952
#	  	Will translate to:
953
#
954
#		  <p>Just type <code>foo `bar` baz</code> at the prompt.</p>
955
#
956
#		There's no arbitrary limit to the number of backticks you
957
#		can use as delimters. If you need three consecutive backticks
958
#		in your code, use four for delimiters, etc.
959
#
960
#	*	You can use spaces to get literal backticks at the edges:
961
#
962
#		  ... type `` `bar` `` ...
963
#
964
#	  	Turns to:
965
#
966
#		  ... type <code>`bar`</code> ...
967
#
968
	$text = preg_replace_callback('@
969
			(?<!\\\)	# Character before opening ` can\'t be a backslash
970
			(`+)		# $1 = Opening run of `
971
			(.+?)		# $2 = The code block
972
			(?<!`)
973
			\1			# Matching closer
974
			(?!`)
975
		@xs',
976
		'_DoCodeSpans_callback', $text);
977
 
978
	return $text;
979
}
980
function _DoCodeSpans_callback($matches) {
981
	$c = $matches[2];
982
	$c = preg_replace('/^[ \t]*/', '', $c); # leading whitespace
983
	$c = preg_replace('/[ \t]*$/', '', $c); # trailing whitespace
984
	$c = _EncodeCode($c);
985
	return "<code>$c</code>";
986
}
987
 
988
 
989
function _EncodeCode($_) {
990
#
991
# Encode/escape certain characters inside Markdown code runs.
992
# The point is that in code, these characters are literals,
993
# and lose their special Markdown meanings.
994
#
995
	global $md_escape_table;
996
 
997
	# Encode all ampersands; HTML entities are not
998
	# entities within a Markdown code span.
999
	$_ = str_replace('&', '&amp;', $_);
1000
 
1001
	# Do the angle bracket song and dance:
1002
	$_ = str_replace(array('<',    '>'),
1003
					 array('&lt;', '&gt;'), $_);
1004
 
1005
	# Now, escape characters that are magic in Markdown:
1006
	$_ = str_replace(array_keys($md_escape_table),
1007
					 array_values($md_escape_table), $_);
1008
 
1009
	return $_;
1010
}
1011
 
1012
 
1013
function _DoItalicsAndBold($text) {
1014
	# <strong> must go first:
1015
	$text = preg_replace('{
1016
			(						# $1: Marker
1017
				(?<!\*\*) \*\* |	#     (not preceded by two chars of
1018
				(?<!__)   __		#      the same marker)
1019
			)
1020
			(?=\S) 					# Not followed by whitespace
1021
			(?!\1)					#   or two others marker chars.
1022
			(						# $2: Content
1023
				(?:
1024
					[^*_]+?			# Anthing not em markers.
1025
				|
1026
									# Balence any regular emphasis inside.
1027
					([*_]) (?=\S) .+? (?<=\S) \3	# $3: em char (* or _)
1028
				|
1029
					(?! \1 ) .		# Allow unbalenced * and _.
1030
				)+?
1031
			)
1032
			(?<=\S) \1				# End mark not preceded by whitespace.
1033
		}sx',
1034
		'<strong>\2</strong>', $text);
1035
	# Then <em>:
1036
	$text = preg_replace(
1037
		'{ ( (?<!\*)\* | (?<!_)_ ) (?=\S) (?! \1) (.+?) (?<=\S) \1 }sx',
1038
		'<em>\2</em>', $text);
1039
 
1040
	return $text;
1041
}
1042
 
1043
 
1044
function _DoBlockQuotes($text) {
1045
	$text = preg_replace_callback('/
1046
		  (								# Wrap whole match in $1
1047
			(
1048
			  ^[ \t]*>[ \t]?			# ">" at the start of a line
1049
				.+\n					# rest of the first line
1050
			  (.+\n)*					# subsequent consecutive lines
1051
			  \n*						# blanks
1052
			)+
1053
		  )
1054
		/xm',
1055
		'_DoBlockQuotes_callback', $text);
1056
 
1057
	return $text;
1058
}
1059
function _DoBlockQuotes_callback($matches) {
1060
	$bq = $matches[1];
1061
	# trim one level of quoting - trim whitespace-only lines
1062
	$bq = preg_replace(array('/^[ \t]*>[ \t]?/m', '/^[ \t]+$/m'), '', $bq);
1063
	$bq = _RunBlockGamut($bq);		# recurse
1064
 
1065
	$bq = preg_replace('/^/m', "  ", $bq);
1066
	# These leading spaces screw with <pre> content, so we need to fix that:
1067
	$bq = preg_replace_callback('{(\s*<pre>.+?</pre>)}sx',
1068
								'_DoBlockQuotes_callback2', $bq);
1069
 
1070
	return "<blockquote>\n$bq\n</blockquote>\n\n";
1071
}
1072
function _DoBlockQuotes_callback2($matches) {
1073
	$pre = $matches[1];
1074
	$pre = preg_replace('/^  /m', '', $pre);
1075
	return $pre;
1076
}
1077
 
1078
 
1079
function _FormParagraphs($text) {
1080
#
1081
#	Params:
1082
#		$text - string to process with html <p> tags
1083
#
1084
	global $md_html_blocks;
1085
 
1086
	# Strip leading and trailing lines:
1087
	$text = preg_replace(array('/\A\n+/', '/\n+\z/'), '', $text);
1088
 
1089
	$grafs = preg_split('/\n{2,}/', $text, -1, PREG_SPLIT_NO_EMPTY);
1090
 
1091
	#
1092
	# Wrap <p> tags.
1093
	#
1094
	foreach ($grafs as $key => $value) {
1095
		if (!isset( $md_html_blocks[$value] )) {
1096
			$value = _RunSpanGamut($value);
1097
			$value = preg_replace('/^([ \t]*)/', '<p>', $value);
1098
			$value .= "</p>";
1099
			$grafs[$key] = $value;
1100
		}
1101
	}
1102
 
1103
	#
1104
	# Unhashify HTML blocks
1105
	#
1106
	foreach ($grafs as $key => $value) {
1107
		if (isset( $md_html_blocks[$value] )) {
1108
			$grafs[$key] = $md_html_blocks[$value];
1109
		}
1110
	}
1111
 
1112
	return implode("\n\n", $grafs);
1113
}
1114
 
1115
 
1116
function _EncodeAmpsAndAngles($text) {
1117
# Smart processing for ampersands and angle brackets that need to be encoded.
1118
 
1119
	# Ampersand-encoding based entirely on Nat Irons's Amputator MT plugin:
1120
	#   http://bumppo.net/projects/amputator/
1121
	$text = preg_replace('/&(?!#?[xX]?(?:[0-9a-fA-F]+|\w+);)/',
1122
						 '&amp;', $text);;
1123
 
1124
	# Encode naked <'s
1125
	$text = preg_replace('{<(?![a-z/?\$!])}i', '&lt;', $text);
1126
 
1127
	return $text;
1128
}
1129
 
1130
 
1131
function _EncodeBackslashEscapes($text) {
1132
#
1133
#	Parameter:  String.
1134
#	Returns:    The string, with after processing the following backslash
1135
#				escape sequences.
1136
#
1137
	global $md_escape_table, $md_backslash_escape_table;
1138
	# Must process escaped backslashes first.
1139
	return str_replace(array_keys($md_backslash_escape_table),
1140
					   array_values($md_backslash_escape_table), $text);
1141
}
1142
 
1143
 
1144
function _DoAutoLinks($text) {
1145
	$text = preg_replace("!<((https?|ftp):[^'\">\\s]+)>!",
1146
						 '<a href="\1">\1</a>', $text);
1147
 
1148
	# Email addresses: <address@domain.foo>
1149
	$text = preg_replace('{
1150
		<
1151
        (?:mailto:)?
1152
		(
1153
			[-.\w]+
1154
			\@
1155
			[-a-z0-9]+(\.[-a-z0-9]+)*\.[a-z]+
1156
		)
1157
		>
1158
		}exi',
1159
		"_EncodeEmailAddress(_UnescapeSpecialChars(_UnslashQuotes('\\1')))",
1160
		$text);
1161
 
1162
	return $text;
1163
}
1164
 
1165
 
1166
function _EncodeEmailAddress($addr) {
1167
#
1168
#	Input: an email address, e.g. "foo@example.com"
1169
#
1170
#	Output: the email address as a mailto link, with each character
1171
#		of the address encoded as either a decimal or hex entity, in
1172
#		the hopes of foiling most address harvesting spam bots. E.g.:
1173
#
1174
#	  <a href="&#x6D;&#97;&#105;&#108;&#x74;&#111;:&#102;&#111;&#111;&#64;&#101;
1175
#		x&#x61;&#109;&#x70;&#108;&#x65;&#x2E;&#99;&#111;&#109;">&#102;&#111;&#111;
1176
#		&#64;&#101;x&#x61;&#109;&#x70;&#108;&#x65;&#x2E;&#99;&#111;&#109;</a>
1177
#
1178
#	Based by a filter by Matthew Wickline, posted to the BBEdit-Talk
1179
#	mailing list: <http://tinyurl.com/yu7ue>
1180
#
1181
	$addr = "mailto:" . $addr;
1182
	$length = strlen($addr);
1183
 
1184
	# leave ':' alone (to spot mailto: later)
1185
	$addr = preg_replace_callback('/([^\:])/',
1186
								  '_EncodeEmailAddress_callback', $addr);
1187
 
1188
	$addr = "<a href=\"$addr\">$addr</a>";
1189
	# strip the mailto: from the visible part
1190
	$addr = preg_replace('/">.+?:/', '">', $addr);
1191
 
1192
	return $addr;
1193
}
1194
function _EncodeEmailAddress_callback($matches) {
1195
	$char = $matches[1];
1196
	$r = rand(0, 100);
1197
	# roughly 10% raw, 45% hex, 45% dec
1198
	# '@' *must* be encoded. I insist.
1199
	if ($r > 90 && $char != '@') return $char;
1200
	if ($r < 45) return '&#x'.dechex(ord($char)).';';
1201
	return '&#'.ord($char).';';
1202
}
1203
 
1204
 
1205
function _UnescapeSpecialChars($text) {
1206
#
1207
# Swap back in all the special characters we've hidden.
1208
#
1209
	global $md_escape_table;
1210
	return str_replace(array_values($md_escape_table),
1211
					   array_keys($md_escape_table), $text);
1212
}
1213
 
1214
 
1215
# _TokenizeHTML is shared between PHP Markdown and PHP SmartyPants.
1216
# We only define it if it is not already defined.
1217
if (!function_exists('_TokenizeHTML')) :
1218
function _TokenizeHTML($str) {
1219
#
1220
#   Parameter:  String containing HTML markup.
1221
#   Returns:    An array of the tokens comprising the input
1222
#               string. Each token is either a tag (possibly with nested,
1223
#               tags contained therein, such as <a href="<MTFoo>">, or a
1224
#               run of text between tags. Each element of the array is a
1225
#               two-element array; the first is either 'tag' or 'text';
1226
#               the second is the actual value.
1227
#
1228
#
1229
#   Regular expression derived from the _tokenize() subroutine in
1230
#   Brad Choate's MTRegex plugin.
1231
#   <http://www.bradchoate.com/past/mtregex.php>
1232
#
1233
	$index = 0;
1234
	$tokens = array();
1235
 
1236
	$match = '(?s:<!(?:--.*?--\s*)+>)|'.	# comment
1237
			 '(?s:<\?.*?\?>)|'.				# processing instruction
1238
			 								# regular tags
1239
			 '(?:<[/!$]?[-a-zA-Z0-9:]+\b(?>[^"\'>]+|"[^"]*"|\'[^\']*\')*>)';
1240
 
1241
	$parts = preg_split("{($match)}", $str, -1, PREG_SPLIT_DELIM_CAPTURE);
1242
 
1243
	foreach ($parts as $part) {
1244
		if (++$index % 2 && $part != '')
1245
			$tokens[] = array('text', $part);
1246
		else
1247
			$tokens[] = array('tag', $part);
1248
	}
1249
 
1250
	return $tokens;
1251
}
1252
endif;
1253
 
1254
 
1255
function _Outdent($text) {
1256
#
1257
# Remove one level of line-leading tabs or spaces
1258
#
1259
	global $md_tab_width;
1260
	return preg_replace("/^(\\t|[ ]{1,$md_tab_width})/m", "", $text);
1261
}
1262
 
1263
 
1264
function _Detab($text) {
1265
#
1266
# Replace tabs with the appropriate amount of space.
1267
#
1268
	global $md_tab_width;
1269
 
1270
	# For each line we separate the line in blocks delemited by
1271
	# tab characters. Then we reconstruct every line by adding the
1272
	# appropriate number of space between each blocks.
1273
 
1274
	$lines = explode("\n", $text);
1275
	$text = "";
1276
 
1277
	foreach ($lines as $line) {
1278
		# Split in blocks.
1279
		$blocks = explode("\t", $line);
1280
		# Add each blocks to the line.
1281
		$line = $blocks[0];
1282
		unset($blocks[0]); # Do not add first block twice.
1283
		foreach ($blocks as $block) {
1284
			# Calculate amount of space, insert spaces, insert block.
1285
			$amount = $md_tab_width - strlen($line) % $md_tab_width;
1286
			$line .= str_repeat(" ", $amount) . $block;
1287
		}
1288
		$text .= "$line\n";
1289
	}
1290
	return $text;
1291
}
1292
 
1293
 
1294
function _UnslashQuotes($text) {
1295
#
1296
#	This function is useful to remove automaticaly slashed double quotes
1297
#	when using preg_replace and evaluating an expression.
1298
#	Parameter:  String.
1299
#	Returns:    The string with any slash-double-quote (\") sequence replaced
1300
#				by a single double quote.
1301
#
1302
	return str_replace('\"', '"', $text);
1303
}
1304
 
1305
 
1306
/*
1307
 
1308
PHP Markdown
1309
============
1310
 
1311
Description
1312
-----------
1313
 
1314
This is a PHP translation of the original Markdown formatter written in
1315
Perl by John Gruber.
1316
 
1317
Markdown is a text-to-HTML filter; it translates an easy-to-read /
1318
easy-to-write structured text format into HTML. Markdown's text format
1319
is most similar to that of plain text email, and supports features such
1320
as headers, *emphasis*, code blocks, blockquotes, and links.
1321
 
1322
Markdown's syntax is designed not as a generic markup language, but
1323
specifically to serve as a front-end to (X)HTML. You can use span-level
1324
HTML tags anywhere in a Markdown document, and you can use block level
1325
HTML tags (like <div> and <table> as well).
1326
 
1327
For more information about Markdown's syntax, see:
1328
 
1329
<http://daringfireball.net/projects/markdown/>
1330
 
1331
 
1332
Bugs
1333
----
1334
 
1335
To file bug reports please send email to:
1336
 
1337
<michel.fortin@michelf.com>
1338
 
1339
Please include with your report: (1) the example input; (2) the output you
1340
expected; (3) the output Markdown actually produced.
1341
 
1342
 
1343
Version History
1344
---------------
1345
 
1346
See the readme file for detailed release notes for this version.
1347
 
1348
1.0.1c - 9 Dec 2005
1349
 
1350
1.0.1b - 6 Jun 2005
1351
 
1352
1.0.1a - 15 Apr 2005
1353
 
1354
1.0.1 - 16 Dec 2004
1355
 
1356
1.0 - 21 Aug 2004
1357
 
1358
 
1359
Author & Contributors
1360
---------------------
1361
 
1362
Original Perl version by John Gruber
1363
<http://daringfireball.net/>
1364
 
1365
PHP port and other contributions by Michel Fortin
1366
<http://www.michelf.com/>
1367
 
1368
 
1369
Copyright and License
1370
---------------------
1371
 
1372
Copyright (c) 2004-2005 Michel Fortin
1373
<http://www.michelf.com/>
1374
All rights reserved.
1375
 
1376
Copyright (c) 2003-2004 John Gruber
1377
<http://daringfireball.net/>
1378
All rights reserved.
1379
 
1380
Redistribution and use in source and binary forms, with or without
1381
modification, are permitted provided that the following conditions are
1382
met:
1383
 
1384
*	Redistributions of source code must retain the above copyright notice,
1385
	this list of conditions and the following disclaimer.
1386
 
1387
*	Redistributions in binary form must reproduce the above copyright
1388
	notice, this list of conditions and the following disclaimer in the
1389
	documentation and/or other materials provided with the distribution.
1390
 
1391
*	Neither the name "Markdown" nor the names of its contributors may
1392
	be used to endorse or promote products derived from this software
1393
	without specific prior written permission.
1394
 
1395
This software is provided by the copyright holders and contributors "as
1396
is" and any express or implied warranties, including, but not limited
1397
to, the implied warranties of merchantability and fitness for a
1398
particular purpose are disclaimed. In no event shall the copyright owner
1399
or contributors be liable for any direct, indirect, incidental, special,
1400
exemplary, or consequential damages (including, but not limited to,
1401
procurement of substitute goods or services; loss of use, data, or
1402
profits; or business interruption) however caused and on any theory of
1403
liability, whether in contract, strict liability, or tort (including
1404
negligence or otherwise) arising in any way out of the use of this
1405
software, even if advised of the possibility of such damage.
1406
 
1407
*/
1408
?>