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('"', '"', $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('"', '"', $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('"', '"', $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('"', '"', $alt_text);
|
|
|
688 |
$title = str_replace('"', '"', $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('&', '&', $_);
|
|
|
1000 |
|
|
|
1001 |
# Do the angle bracket song and dance:
|
|
|
1002 |
$_ = str_replace(array('<', '>'),
|
|
|
1003 |
array('<', '>'), $_);
|
|
|
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 |
'&', $text);;
|
|
|
1123 |
|
|
|
1124 |
# Encode naked <'s
|
|
|
1125 |
$text = preg_replace('{<(?![a-z/?\$!])}i', '<', $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="mailto:foo@e
|
|
|
1175 |
# xample.com">foo
|
|
|
1176 |
# @example.com</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 |
?>
|