├── blog ├── 20070808.markdown ├── 20070809.markdown └── 20070810.markdown ├── favicon.ico ├── templates ├── textblock.html ├── blogpost.html ├── textblock_edit.html ├── blogpost_edit.html ├── blogpost_create.html └── template.html ├── texts ├── resources.markdown ├── about.markdown ├── index.markdown ├── contact_sent.markdown ├── contact_error.markdown ├── about_friends.markdown ├── 404.markdown ├── login.markdown └── contact.markdown ├── 404.php ├── index.php ├── style ├── leman_swiss_flag.jpg └── style.css ├── resources.php ├── sidebar ├── external_profiles.markdown └── blog_feed.markdown ├── about_friends.php ├── about.php ├── blog.php ├── blog_new_post.php ├── include ├── config.php ├── email.php ├── logic.php └── markdown.php ├── contact.php ├── logout.php ├── login.php ├── rss.php └── README.md /blog/20070808.markdown: -------------------------------------------------------------------------------- 1 | xxx 2 | August 8, 2007 3 | asd -------------------------------------------------------------------------------- /blog/20070809.markdown: -------------------------------------------------------------------------------- 1 | yyy 2 | August 9, 2007 3 | sdf -------------------------------------------------------------------------------- /blog/20070810.markdown: -------------------------------------------------------------------------------- 1 | zzz 2 | August 10, 2007 3 | dfg -------------------------------------------------------------------------------- /favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nst/phpMyFlatSite/master/favicon.ico -------------------------------------------------------------------------------- /templates/textblock.html: -------------------------------------------------------------------------------- 1 | {{ text }} 2 | 3 |
One of my most productive days was
throwing away 1000 lines of code.
-Ken Thompson
{{ site_name }}
28 |
'.$text.'
'; 130 | $text = preg_replace('{\n{2,}}', "\n\n", $text);
131 | }
132 | return $text;
133 | }
134 |
135 | function mdwp_strip_p($t) { return preg_replace('{?p>}i', '', $t); }
136 |
137 | function mdwp_hide_tags($text) {
138 | global $wp_markdown_hidden;
139 | return str_replace(explode($wp_markdown_hidden),
140 | explode($wp_markdown_hidden), $text);
141 | }
142 | function mdwp_show_tags($text) {
143 | global $markdown_hidden_tags;
144 | return str_replace(array_values($markdown_hidden_tags),
145 | array_keys($markdown_hidden_tags), $text);
146 | }
147 | }
148 |
149 |
150 | ### bBlog Plugin Info ###
151 |
152 | function identify_modifier_markdown() {
153 | return array(
154 | 'name' => 'markdown',
155 | 'type' => 'modifier',
156 | 'nicename' => 'PHP Markdown Extra',
157 | 'description' => 'A text-to-HTML conversion tool for web writers',
158 | 'authors' => 'Michel Fortin and John Gruber',
159 | 'licence' => 'GPL',
160 | 'version' => MARKDOWNEXTRA_VERSION,
161 | 'help' => 'Markdown syntax allows you to write using an easy-to-read, easy-to-write plain text format. Based on the original Perl version by John Gruber. More...',
162 | );
163 | }
164 |
165 |
166 | ### Smarty Modifier Interface ###
167 |
168 | function smarty_modifier_markdown($text) {
169 | return Markdown($text);
170 | }
171 |
172 |
173 | ### Textile Compatibility Mode ###
174 |
175 | # Rename this file to "classTextile.php" and it can replace Textile everywhere.
176 |
177 | if (strcasecmp(substr(__FILE__, -16), "classTextile.php") == 0) {
178 | # Try to include PHP SmartyPants. Should be in the same directory.
179 | @include_once 'smartypants.php';
180 | # Fake Textile class. It calls Markdown instead.
181 | class Textile {
182 | function TextileThis($text, $lite='', $encode='') {
183 | if ($lite == '' && $encode == '') $text = Markdown($text);
184 | if (function_exists('SmartyPants')) $text = SmartyPants($text);
185 | return $text;
186 | }
187 | # Fake restricted version: restrictions are not supported for now.
188 | function TextileRestricted($text, $lite='', $noimage='') {
189 | return $this->TextileThis($text, $lite);
190 | }
191 | # Workaround to ensure compatibility with TextPattern 4.0.3.
192 | function blockLite($text) { return $text; }
193 | }
194 | }
195 |
196 |
197 |
198 | #
199 | # Markdown Parser Class
200 | #
201 |
202 | class Markdown_Parser {
203 |
204 | # Regex to match balanced [brackets].
205 | # Needed to insert a maximum bracked depth while converting to PHP.
206 | var $nested_brackets_depth = 6;
207 | var $nested_brackets;
208 |
209 | var $nested_url_parenthesis_depth = 4;
210 | var $nested_url_parenthesis;
211 |
212 | # Table of hash values for escaped characters:
213 | var $escape_chars = '\`*_{}[]()>#+-.!';
214 | var $escape_table = array();
215 | var $backslash_escape_table = array();
216 |
217 | # Change to ">" for HTML output.
218 | var $empty_element_suffix = MARKDOWN_EMPTY_ELEMENT_SUFFIX;
219 | var $tab_width = MARKDOWN_TAB_WIDTH;
220 |
221 |
222 | function Markdown_Parser() {
223 | #
224 | # Constructor function. Initialize appropriate member variables.
225 | #
226 | $this->_initDetab();
227 |
228 | $this->nested_brackets =
229 | str_repeat('(?>[^\[\]]+|\[', $this->nested_brackets_depth).
230 | str_repeat('\])*', $this->nested_brackets_depth);
231 |
232 | $this->nested_url_parenthesis =
233 | str_repeat('(?>[^()\s]+|\(', $this->nested_url_parenthesis_depth).
234 | str_repeat('(?>\)))*', $this->nested_url_parenthesis_depth);
235 |
236 | # Create an identical table but for escaped characters.
237 | foreach (preg_split('/(?!^|$)/', $this->escape_chars) as $char) {
238 | $entity = "". ord($char). ";";
239 | $this->escape_table[$char] = $entity;
240 | $this->backslash_escape_table["\\$char"] = $entity;
241 | }
242 |
243 | # Sort document, block, and span gamut in ascendent priority order.
244 | asort($this->document_gamut);
245 | asort($this->block_gamut);
246 | asort($this->span_gamut);
247 | }
248 |
249 |
250 | # Internal hashes used during transformation.
251 | var $urls = array();
252 | var $titles = array();
253 | var $html_blocks = array();
254 | var $html_hashes = array(); # Contains both blocks and span hashes.
255 |
256 | # Status flag to avoid invalid nesting.
257 | var $in_anchor = false;
258 |
259 |
260 | function transform($text) {
261 | #
262 | # Main function. The order in which other subs are called here is
263 | # essential. Link and image substitutions need to happen before
264 | # _EscapeSpecialCharsWithinTagAttributes(), so that any *'s or _'s in the
265 | # and s around
356 | # "paragraphs" that are wrapped in non-block-level tags, such as anchors,
357 | # phrase emphasis, and spans. The list of tags we're looking for is
358 | # hard-coded:
359 | $block_tags_a = 'p|div|h[1-6]|blockquote|pre|table|dl|ol|ul|address|'.
360 | 'script|noscript|form|fieldset|iframe|math|ins|del';
361 | $block_tags_b = 'p|div|h[1-6]|blockquote|pre|table|dl|ol|ul|address|'.
362 | 'script|noscript|form|fieldset|iframe|math';
363 |
364 | # Regular expression for the content of a block tag.
365 | $nested_tags_level = 4;
366 | $attr = '
367 | (?> # optional tag attributes
368 | \s # starts with whitespace
369 | (?>
370 | [^>"/]+ # text outside quotes
371 | |
372 | /+(?!>) # slash not followed by ">"
373 | |
374 | "[^"]*" # text inside double quotes (tolerate ">")
375 | |
376 | \'[^\']*\' # text inside single quotes (tolerate ">")
377 | )*
378 | )?
379 | ';
380 | $content =
381 | str_repeat('
382 | (?>
383 | [^<]+ # content without tag
384 | |
385 | <\2 # nested opening tag
386 | '.$attr.' # attributes
387 | (?:
388 | />
389 | |
390 | >', $nested_tags_level). # end of opening tag
391 | '.*?'. # last level nested tag content
392 | str_repeat('
393 | \2\s*> # closing nested tag
394 | )
395 | |
396 | <(?!/\2\s*> # other tags with a different name
397 | )
398 | )*',
399 | $nested_tags_level);
400 |
401 | # First, look for nested blocks, e.g.:
402 | # Just type tags
1271 | #
1272 | # Strip leading and trailing lines:
1273 | $text = preg_replace(array('/\A\n+/', '/\n+\z/'), '', $text);
1274 |
1275 | $grafs = preg_split('/\n{2,}/', $text, -1, PREG_SPLIT_NO_EMPTY);
1276 |
1277 | #
1278 | # Wrap tags.
1279 | #
1280 | foreach ($grafs as $key => $value) {
1281 | if (!isset( $this->html_blocks[$value] )) {
1282 | $value = $this->runSpanGamut($value);
1283 | $value = preg_replace('/^([ ]*)/', " ", $value);
1284 | $value .= " s around
1675 | # "paragraphs" that are wrapped in non-block-level tags, such as anchors,
1676 | # phrase emphasis, and spans. The list of tags we're looking for is
1677 | # hard-coded.
1678 | #
1679 | # This works by calling _HashHTMLBlocks_InMarkdown, which then calls
1680 | # _HashHTMLBlocks_InHTML when it encounter block tags. When the markdown="1"
1681 | # attribute is found whitin a tag, _HashHTMLBlocks_InHTML calls back
1682 | # _HashHTMLBlocks_InMarkdown to handle the Markdown syntax within the tag.
1683 | # These two functions are calling each other. It's recursive!
1684 | #
1685 | #
1686 | # Call the HTML-in-Markdown hasher.
1687 | #
1688 | list($text, ) = $this->_hashHTMLBlocks_inMarkdown($text);
1689 |
1690 | return $text;
1691 | }
1692 | function _hashHTMLBlocks_inMarkdown($text, $indent = 0,
1693 | $enclosing_tag = '', $span = false)
1694 | {
1695 | #
1696 | # Parse markdown text, calling _HashHTMLBlocks_InHTML for block tags.
1697 | #
1698 | # * $indent is the number of space to be ignored when checking for code
1699 | # blocks. This is important because if we don't take the indent into
1700 | # account, something like this (which looks right) won't work as expected:
1701 | #
1702 | # tags and unhashify HTML blocks
2445 | #
2446 | foreach ($grafs as $key => $value) {
2447 | $value = trim($this->runSpanGamut($value));
2448 |
2449 | # Check if this should be enclosed in a paragraph.
2450 | # Clean tag hashes & block tag hashes are left alone.
2451 | $clean_key = $value;
2452 | $block_key = substr($value, 0, 34);
2453 |
2454 | $is_p = (!isset($this->html_blocks[$block_key]) &&
2455 | !isset($this->html_cleans[$clean_key]));
2456 |
2457 | if ($is_p) {
2458 | $value = " $value $backlink tags get encoded.
266 | #
267 | # Clear the global hashes. If we don't clear these, you get conflicts
268 | # from other articles when generating a page which contains more than
269 | # one article (e.g. an index page that shows the N most recent
270 | # articles):
271 | $this->urls = array();
272 | $this->titles = array();
273 | $this->html_blocks = array();
274 | $this->html_hashes = array();
275 |
276 | # Standardize line endings:
277 | # DOS to Unix and Mac to Unix
278 | $text = str_replace(array("\r\n", "\r"), "\n", $text);
279 |
280 | # Make sure $text ends with a couple of newlines:
281 | $text .= "\n\n";
282 |
283 | # Convert all tabs to spaces.
284 | $text = $this->detab($text);
285 |
286 | # Turn block-level HTML blocks into hash entries
287 | $text = $this->hashHTMLBlocks($text);
288 |
289 | # Strip any lines consisting only of spaces and tabs.
290 | # This makes subsequent regexen easier to write, because we can
291 | # match consecutive blank lines with /\n+/ instead of something
292 | # contorted like /[ ]*\n+/ .
293 | $text = preg_replace('/^[ ]+$/m', '', $text);
294 |
295 | # Run document gamut methods.
296 | foreach ($this->document_gamut as $method => $priority) {
297 | $text = $this->$method($text);
298 | }
299 |
300 | return $text . "\n";
301 | }
302 |
303 | var $document_gamut = array(
304 | # Strip link definitions, store in hashes.
305 | "stripLinkDefinitions" => 20,
306 |
307 | "runBasicBlockGamut" => 30,
308 | );
309 |
310 |
311 | function stripLinkDefinitions($text) {
312 | #
313 | # Strips link definitions from text, stores the URLs and titles in
314 | # hash references.
315 | #
316 | $less_than_tab = $this->tab_width - 1;
317 |
318 | # Link defs are in the form: ^[id]: url "optional title"
319 | $text = preg_replace_callback('{
320 | ^[ ]{0,'.$less_than_tab.'}\[(.+)\][ ]?: # id = $1
321 | [ ]*
322 | \n? # maybe *one* newline
323 | [ ]*
324 | (\S+?)>? # url = $2
325 | [ ]*
326 | \n? # maybe one newline
327 | [ ]*
328 | (?:
329 | (?<=\s) # lookbehind for whitespace
330 | ["(]
331 | (.*?) # title = $3
332 | [")]
333 | [ ]*
334 | )? # title is optional
335 | (?:\n+|\Z)
336 | }xm',
337 | array(&$this, '_stripLinkDefinitions_callback'),
338 | $text);
339 | return $text;
340 | }
341 | function _stripLinkDefinitions_callback($matches) {
342 | $link_id = strtolower($matches[1]);
343 | $this->urls[$link_id] = $this->encodeAmpsAndAngles($matches[2]);
344 | if (isset($matches[3]))
345 | $this->titles[$link_id] = str_replace('"', '"', $matches[3]);
346 | return ''; # String that will replace the block
347 | }
348 |
349 |
350 | function hashHTMLBlocks($text) {
351 | $less_than_tab = $this->tab_width - 1;
352 |
353 | # Hashify HTML blocks:
354 | # We only want to do this for block-level HTML tags, such as headers,
355 | # lists, and tables. That's because we still want to wrap
. It was easier to make a special case than
444 | # to make the other regex more complicated.
445 | $text = preg_replace_callback('{
446 | (?:
447 | (?<=\n\n) # Starting after a blank line
448 | | # or
449 | \A\n? # the beginning of the doc
450 | )
451 | ( # save in $1
452 | [ ]{0,'.$less_than_tab.'}
453 | <(hr) # start tag = $2
454 | \b # word break
455 | ([^<>])*? #
456 | /?> # the matching end tag
457 | [ ]*
458 | (?=\n{2,}|\Z) # followed by a blank line or end of document
459 | )
460 | }xi',
461 | array(&$this, '_hashHTMLBlocks_callback'),
462 | $text);
463 |
464 | # Special case for standalone HTML comments:
465 | $text = preg_replace_callback('{
466 | (?:
467 | (?<=\n\n) # Starting after a blank line
468 | | # or
469 | \A\n? # the beginning of the doc
470 | )
471 | ( # save in $1
472 | [ ]{0,'.$less_than_tab.'}
473 | (?s:
474 |
475 | )
476 | [ ]*
477 | (?=\n{2,}|\Z) # followed by a blank line or end of document
478 | )
479 | }x',
480 | array(&$this, '_hashHTMLBlocks_callback'),
481 | $text);
482 |
483 | # PHP and ASP-style processor instructions ( and <%)
484 | $text = preg_replace_callback('{
485 | (?:
486 | (?<=\n\n) # Starting after a blank line
487 | | # or
488 | \A\n? # the beginning of the doc
489 | )
490 | ( # save in $1
491 | [ ]{0,'.$less_than_tab.'}
492 | (?s:
493 | <([?%]) # $2
494 | .*?
495 | \2>
496 | )
497 | [ ]*
498 | (?=\n{2,}|\Z) # followed by a blank line or end of document
499 | )
500 | }x',
501 | array(&$this, '_hashHTMLBlocks_callback'),
502 | $text);
503 |
504 | return $text;
505 | }
506 | function _hashHTMLBlocks_callback($matches) {
507 | $text = $matches[1];
508 | $key = $this->hashBlock($text);
509 | return "\n\n$key\n\n";
510 | }
511 |
512 |
513 | function hashBlock($text) {
514 | #
515 | # Called whenever a tag must be hashed when a function insert a block-level
516 | # tag in $text, it pass through this function and is automaticaly escaped,
517 | # which remove the need to call _HashHTMLBlocks at every step.
518 | #
519 | # Swap back any tag hash found in $text so we do not have to `unhash`
520 | # multiple times at the end.
521 | $text = $this->unhash($text);
522 |
523 | # Then hash the block.
524 | $key = "B\x1A". md5($text);
525 | $this->html_hashes[$key] = $text;
526 | $this->html_blocks[$key] = $text;
527 | return $key; # String that will replace the tag.
528 | }
529 |
530 |
531 | function hashSpan($text, $word_separator = false) {
532 | #
533 | # Called whenever a tag must be hashed when a function insert a span-level
534 | # element in $text, it pass through this function and is automaticaly
535 | # escaped, blocking invalid nested overlap. If optional argument
536 | # $word_separator is true, surround the hash value by spaces.
537 | #
538 | # Swap back any tag hash found in $text so we do not have to `unhash`
539 | # multiple times at the end.
540 | $text = $this->unhash($text);
541 |
542 | # Then hash the span.
543 | $key = "S\x1A". md5($text);
544 | if ($word_separator) $key = ":$key:";
545 |
546 | $this->html_hashes[$key] = $text;
547 | return $key; # String that will replace the span tag.
548 | }
549 |
550 |
551 | var $block_gamut = array(
552 | #
553 | # These are all the transformations that form block-level
554 | # tags like paragraphs, headers, and list items.
555 | #
556 | "doHeaders" => 10,
557 | "doHorizontalRules" => 20,
558 |
559 | "doLists" => 40,
560 | "doCodeBlocks" => 50,
561 | "doBlockQuotes" => 60,
562 | );
563 |
564 | function runBlockGamut($text) {
565 | #
566 | # Run block gamut tranformations.
567 | #
568 | # We need to escape raw HTML in Markdown source before doing anything
569 | # else. This need to be done for each block, and not only at the
570 | # begining in the Markdown function since hashed blocks can be part of
571 | # list items and could have been indented. Indented blocks would have
572 | # been seen as a code block in a previous pass of hashHTMLBlocks.
573 | $text = $this->hashHTMLBlocks($text);
574 |
575 | return $this->runBasicBlockGamut($text);
576 | }
577 |
578 | function runBasicBlockGamut($text) {
579 | #
580 | # Run block gamut tranformations, without hashing HTML blocks. This is
581 | # useful when HTML blocks are known to be already hashed, like in the first
582 | # whole-document pass.
583 | #
584 | foreach ($this->block_gamut as $method => $priority) {
585 | $text = $this->$method($text);
586 | }
587 |
588 | # Finally form paragraph and restore hashed blocks.
589 | $text = $this->formParagraphs($text);
590 |
591 | return $text;
592 | }
593 |
594 |
595 | function doHorizontalRules($text) {
596 | # Do Horizontal Rules:
597 | return preg_replace(
598 | array('{^[ ]{0,2}([ ]?\*[ ]?){3,}[ ]*$}mx',
599 | '{^[ ]{0,2}([ ]? -[ ]?){3,}[ ]*$}mx',
600 | '{^[ ]{0,2}([ ]? _[ ]?){3,}[ ]*$}mx'),
601 | "\n".$this->hashBlock("
empty_element_suffix")."\n",
602 | $text);
603 | }
604 |
605 |
606 | var $span_gamut = array(
607 | #
608 | # These are all the transformations that occur *within* block-level
609 | # tags like paragraphs, headers, and list items.
610 | #
611 | "escapeSpecialCharsWithinTagAttributes" => -20,
612 | "doCodeSpans" => -10,
613 | "encodeBackslashEscapes" => -5,
614 |
615 | # Process anchor and image tags. Images must come first,
616 | # because ![foo][f] looks like an anchor.
617 | "doImages" => 10,
618 | "doAnchors" => 20,
619 |
620 | # Make links out of things like `
empty_element_suffix\n");
645 | return preg_replace('/ {2,}\n/', $br_tag, $text);
646 | }
647 |
648 |
649 | function escapeSpecialCharsWithinTagAttributes($text) {
650 | #
651 | # Within tags -- meaning between < and > -- encode [\ ` * _] so they
652 | # don't conflict with their use in Markdown for code, italics and strong.
653 | # We're replacing each such character with its corresponding MD5 checksum
654 | # value; this is likely overkill, but it should prevent us from colliding
655 | # with the escape values by accident.
656 | #
657 | $tokens = $this->tokenizeHTML($text);
658 | $text = ''; # rebuild $text from the tokens
659 |
660 | foreach ($tokens as $cur_token) {
661 | if ($cur_token[0] == 'tag') {
662 | $cur_token[1] = str_replace('\\', $this->escape_table['\\'], $cur_token[1]);
663 | $cur_token[1] = str_replace('`', $this->escape_table['`'], $cur_token[1]);
664 | $cur_token[1] = str_replace('*', $this->escape_table['*'], $cur_token[1]);
665 | $cur_token[1] = str_replace('_', $this->escape_table['_'], $cur_token[1]);
666 | }
667 | $text .= $cur_token[1];
668 | }
669 | return $text;
670 | }
671 |
672 |
673 | function doAnchors($text) {
674 | #
675 | # Turn Markdown link shortcuts into XHTML tags.
676 | #
677 | if ($this->in_anchor) return $text;
678 | $this->in_anchor = true;
679 |
680 | #
681 | # First, handle reference-style links: [link text] [id]
682 | #
683 | $text = preg_replace_callback('{
684 | ( # wrap whole match in $1
685 | \[
686 | ('.$this->nested_brackets.') # link text = $2
687 | \]
688 |
689 | [ ]? # one optional space
690 | (?:\n[ ]*)? # one optional newline followed by spaces
691 |
692 | \[
693 | (.*?) # id = $3
694 | \]
695 | )
696 | }xs',
697 | array(&$this, '_doAnchors_reference_callback'), $text);
698 |
699 | #
700 | # Next, inline-style links: [link text](url "optional title")
701 | #
702 | $text = preg_replace_callback('{
703 | ( # wrap whole match in $1
704 | \[
705 | ('.$this->nested_brackets.') # link text = $2
706 | \]
707 | \( # literal paren
708 | [ ]*
709 | (?:
710 | <(\S*)> # href = $3
711 | |
712 | ('.$this->nested_url_parenthesis.') # href = $4
713 | )
714 | [ ]*
715 | ( # $5
716 | ([\'"]) # quote char = $6
717 | (.*?) # Title = $7
718 | \6 # matching quote
719 | [ ]* # ignore any spaces/tabs between closing quote and )
720 | )? # title is optional
721 | \)
722 | )
723 | }xs',
724 | array(&$this, '_DoAnchors_inline_callback'), $text);
725 |
726 | #
727 | # Last, handle reference-style shortcuts: [link text]
728 | # These must come last in case you've also got [link test][1]
729 | # or [link test](/foo)
730 | #
731 | // $text = preg_replace_callback('{
732 | // ( # wrap whole match in $1
733 | // \[
734 | // ([^\[\]]+) # link text = $2; can\'t contain [ or ]
735 | // \]
736 | // )
737 | // }xs',
738 | // array(&$this, '_doAnchors_reference_callback'), $text);
739 |
740 | $this->in_anchor = false;
741 | return $text;
742 | }
743 | function _doAnchors_reference_callback($matches) {
744 | $whole_match = $matches[1];
745 | $link_text = $matches[2];
746 | $link_id =& $matches[3];
747 |
748 | if ($link_id == "") {
749 | # for shortcut links like [this][] or [this].
750 | $link_id = $link_text;
751 | }
752 |
753 | # lower-case and turn embedded newlines into spaces
754 | $link_id = strtolower($link_id);
755 | $link_id = preg_replace('{[ ]?\n}', ' ', $link_id);
756 |
757 | if (isset($this->urls[$link_id])) {
758 | $url = $this->urls[$link_id];
759 | $url = $this->encodeAmpsAndAngles($url);
760 |
761 | $result = "titles[$link_id] ) ) {
763 | $title = $this->titles[$link_id];
764 | $title = $this->encodeAmpsAndAngles($title);
765 | $result .= " title=\"$title\"";
766 | }
767 |
768 | $link_text = $this->runSpanGamut($link_text);
769 | $result .= ">$link_text";
770 | $result = $this->hashSpan($result);
771 | }
772 | else {
773 | $result = $whole_match;
774 | }
775 | return $result;
776 | }
777 | function _doAnchors_inline_callback($matches) {
778 | $whole_match = $matches[1];
779 | $link_text = $this->runSpanGamut($matches[2]);
780 | $url = $matches[3] == '' ? $matches[4] : $matches[3];
781 | $title =& $matches[7];
782 |
783 | $url = $this->encodeAmpsAndAngles($url);
784 |
785 | $result = "encodeAmpsAndAngles($title);
789 | $result .= " title=\"$title\"";
790 | }
791 |
792 | $link_text = $this->runSpanGamut($link_text);
793 | $result .= ">$link_text";
794 |
795 | return $this->hashSpan($result);
796 | }
797 |
798 |
799 | function doImages($text) {
800 | #
801 | # Turn Markdown image shortcuts into tags.
802 | #
803 | #
804 | # First, handle reference-style labeled images: ![alt text][id]
805 | #
806 | $text = preg_replace_callback('{
807 | ( # wrap whole match in $1
808 | !\[
809 | ('.$this->nested_brackets.') # alt text = $2
810 | \]
811 |
812 | [ ]? # one optional space
813 | (?:\n[ ]*)? # one optional newline followed by spaces
814 |
815 | \[
816 | (.*?) # id = $3
817 | \]
818 |
819 | )
820 | }xs',
821 | array(&$this, '_doImages_reference_callback'), $text);
822 |
823 | #
824 | # Next, handle inline images: 
825 | # Don't forget: encode * and _
826 | #
827 | $text = preg_replace_callback('{
828 | ( # wrap whole match in $1
829 | !\[
830 | ('.$this->nested_brackets.') # alt text = $2
831 | \]
832 | \s? # One optional whitespace character
833 | \( # literal paren
834 | [ ]*
835 | (?:
836 | <(\S*)> # src url = $3
837 | |
838 | ('.$this->nested_url_parenthesis.') # src url = $4
839 | )
840 | [ ]*
841 | ( # $5
842 | ([\'"]) # quote char = $6
843 | (.*?) # title = $7
844 | \6 # matching quote
845 | [ ]*
846 | )? # title is optional
847 | \)
848 | )
849 | }xs',
850 | array(&$this, '_doImages_inline_callback'), $text);
851 |
852 | return $text;
853 | }
854 | function _doImages_reference_callback($matches) {
855 | $whole_match = $matches[1];
856 | $alt_text = $matches[2];
857 | $link_id = strtolower($matches[3]);
858 |
859 | if ($link_id == "") {
860 | $link_id = strtolower($alt_text); # for shortcut links like ![this][].
861 | }
862 |
863 | $alt_text = str_replace('"', '"', $alt_text);
864 | if (isset($this->urls[$link_id])) {
865 | $url = $this->urls[$link_id];
866 | $result = "
titles[$link_id])) {
868 | $title = $this->titles[$link_id];
869 | $result .= " title=\"$title\"";
870 | }
871 | $result .= $this->empty_element_suffix;
872 | $result = $this->hashSpan($result);
873 | }
874 | else {
875 | # If there's no such link ID, leave intact:
876 | $result = $whole_match;
877 | }
878 |
879 | return $result;
880 | }
881 | function _doImages_inline_callback($matches) {
882 | $whole_match = $matches[1];
883 | $alt_text = $matches[2];
884 | $url = $matches[3] == '' ? $matches[4] : $matches[3];
885 | $title =& $matches[7];
886 |
887 | $alt_text = str_replace('"', '"', $alt_text);
888 | $result = "
empty_element_suffix;
894 |
895 | return $this->hashSpan($result);
896 | }
897 |
898 |
899 | function doHeaders($text) {
900 | # Setext-style headers:
901 | # Header 1
902 | # ========
903 | #
904 | # Header 2
905 | # --------
906 | #
907 | $text = preg_replace_callback('{ ^(.+?)[ ]*\n=+[ ]*\n+ }mx',
908 | array(&$this, '_doHeaders_callback_setext_h1'), $text);
909 | $text = preg_replace_callback('{ ^(.+?)[ ]*\n-+[ ]*\n+ }mx',
910 | array(&$this, '_doHeaders_callback_setext_h2'), $text);
911 |
912 | # atx-style headers:
913 | # # Header 1
914 | # ## Header 2
915 | # ## Header 2 with closing hashes ##
916 | # ...
917 | # ###### Header 6
918 | #
919 | $text = preg_replace_callback('{
920 | ^(\#{1,6}) # $1 = string of #\'s
921 | [ ]*
922 | (.+?) # $2 = Header text
923 | [ ]*
924 | \#* # optional closing #\'s (not counted)
925 | \n+
926 | }xm',
927 | array(&$this, '_doHeaders_callback_atx'), $text);
928 |
929 | return $text;
930 | }
931 | function _doHeaders_callback_setext_h1($matches) {
932 | $block = "
".$this->runSpanGamut($matches[1])."
";
933 | return "\n" . $this->hashBlock($block) . "\n\n";
934 | }
935 | function _doHeaders_callback_setext_h2($matches) {
936 | $block = "".$this->runSpanGamut($matches[1])."
";
937 | return "\n" . $this->hashBlock($block) . "\n\n";
938 | }
939 | function _doHeaders_callback_atx($matches) {
940 | $level = strlen($matches[1]);
941 | $block = "` blocks.
1092 | #
1093 | $text = preg_replace_callback('{
1094 | (?:\n\n|\A)
1095 | ( # $1 = the code block -- one or more lines, starting with a space/tab
1096 | (?:
1097 | (?:[ ]{'.$this->tab_width.'} | \t) # Lines must start with a tab or a tab-width of spaces
1098 | .*\n+
1099 | )+
1100 | )
1101 | ((?=^[ ]{0,'.$this->tab_width.'}\S)|\Z) # Lookahead for non-space at line-start, or end of doc
1102 | }xm',
1103 | array(&$this, '_doCodeBlocks_callback'), $text);
1104 |
1105 | return $text;
1106 | }
1107 | function _doCodeBlocks_callback($matches) {
1108 | $codeblock = $matches[1];
1109 |
1110 | $codeblock = $this->encodeCode($this->outdent($codeblock));
1111 | // $codeblock = $this->detab($codeblock);
1112 | # trim leading newlines and trailing whitespace
1113 | $codeblock = preg_replace(array('/\A\n+/', '/\n+\z/'), '', $codeblock);
1114 |
1115 | $result = "\n\n".$this->hashBlock("
")."\n\n";
1116 |
1117 | return $result;
1118 | }
1119 |
1120 |
1121 | function doCodeSpans($text) {
1122 | #
1123 | # * Backtick quotes are used for " . $codeblock . "\n spans.
1124 | #
1125 | # * You can use multiple backticks as the delimiters if you want to
1126 | # include literal backticks in the code span. So, this input:
1127 | #
1128 | # Just type ``foo `bar` baz`` at the prompt.
1129 | #
1130 | # Will translate to:
1131 | #
1132 | # foo `bar` baz at the prompt.`bar` ...
1145 | #
1146 | $text = preg_replace_callback('@
1147 | (?encodeCode($c);
1163 | return $this->hashSpan("$c");
1164 | }
1165 |
1166 |
1167 | function encodeCode($_) {
1168 | #
1169 | # Encode/escape certain characters inside Markdown code runs.
1170 | # The point is that in code, these characters are literals,
1171 | # and lose their special Markdown meanings.
1172 | #
1173 | # Encode all ampersands; HTML entities are not
1174 | # entities within a Markdown code span.
1175 | $_ = str_replace('&', '&', $_);
1176 |
1177 | # Do the angle bracket song and dance:
1178 | $_ = str_replace(array('<', '>'),
1179 | array('<', '>'), $_);
1180 |
1181 | # Now, escape characters that are magic in Markdown:
1182 | // $_ = str_replace(array_keys($this->escape_table),
1183 | // array_values($this->escape_table), $_);
1184 |
1185 | return $_;
1186 | }
1187 |
1188 |
1189 | function doItalicsAndBold($text) {
1190 | # must go first:
1191 | $text = preg_replace_callback('{
1192 | ( # $1: Marker
1193 | (?
1201 | [^*_]+? # Anthing not em markers.
1202 | |
1203 | # Balence any regular emphasis inside.
1204 | \1 (?=\S) .+? (?<=\S) \1
1205 | |
1206 | . # Allow unbalenced * and _.
1207 | )+?
1208 | )
1209 | (?<=\S) \1\1 # End mark not preceded by whitespace.
1210 | }sx',
1211 | array(&$this, '_doItalicAndBold_strong_callback'), $text);
1212 | # Then :
1213 | $text = preg_replace_callback(
1214 | '{ ( (?runSpanGamut($text);
1222 | return $this->hashSpan("$text");
1223 | }
1224 | function _doItalicAndBold_strong_callback($matches) {
1225 | $text = $matches[2];
1226 | $text = $this->runSpanGamut($text);
1227 | return $this->hashSpan("$text");
1228 | }
1229 |
1230 |
1231 | function doBlockQuotes($text) {
1232 | $text = preg_replace_callback('/
1233 | ( # Wrap whole match in $1
1234 | (
1235 | ^[ ]*>[ ]? # ">" at the start of a line
1236 | .+\n # rest of the first line
1237 | (.+\n)* # subsequent consecutive lines
1238 | \n* # blanks
1239 | )+
1240 | )
1241 | /xm',
1242 | array(&$this, '_doBlockQuotes_callback'), $text);
1243 |
1244 | return $text;
1245 | }
1246 | function _doBlockQuotes_callback($matches) {
1247 | $bq = $matches[1];
1248 | # trim one level of quoting - trim whitespace-only lines
1249 | $bq = preg_replace(array('/^[ ]*>[ ]?/m', '/^[ ]+$/m'), '', $bq);
1250 | $bq = $this->runBlockGamut($bq); # recurse
1251 |
1252 | $bq = preg_replace('/^/m', " ", $bq);
1253 | # These leading spaces cause problem with content,
1254 | # so we need to fix that:
1255 | $bq = preg_replace_callback('{(\s*.+?
)}sx',
1256 | array(&$this, '_DoBlockQuotes_callback2'), $bq);
1257 |
1258 | return "\n". $this->hashBlock("\n$bq\n
")."\n\n";
1259 | }
1260 | function _doBlockQuotes_callback2($matches) {
1261 | $pre = $matches[1];
1262 | $pre = preg_replace('/^ /m', '', $pre);
1263 | return $pre;
1264 | }
1265 |
1266 |
1267 | function formParagraphs($text) {
1268 | #
1269 | # Params:
1270 | # $text - string to process with html
)
1967 | # Comments and Processing Instructions.
1968 | #
1969 | if (preg_match("{^?(?:$this->auto_close_tags)\b}", $tag) ||
1970 | $tag{1} == '!' || $tag{1} == '?')
1971 | {
1972 | # Just add the tag to the block as if it was text.
1973 | $block_text .= $tag;
1974 | }
1975 | else {
1976 | #
1977 | # Increase/decrease nested tag count. Only do so if
1978 | # the tag's name match base tag's.
1979 | #
1980 | if (preg_match("{^?$base_tag_name\b}", $tag)) {
1981 | if ($tag{1} == '/') $depth--;
1982 | else if ($tag{strlen($tag)-2} != '/') $depth++;
1983 | }
1984 |
1985 | #
1986 | # Check for `markdown="1"` attribute and handle it.
1987 | #
1988 | if ($md_attr &&
1989 | preg_match($markdown_attr_match, $tag, $attr_m) &&
1990 | preg_match('/^1|block|span$/', $attr_m[2] . $attr_m[3]))
1991 | {
1992 | # Remove `markdown` attribute from opening tag.
1993 | $tag = preg_replace($markdown_attr_match, '', $tag);
1994 |
1995 | # Check if text inside this tag must be parsed in span mode.
1996 | $this->mode = $attr_m[2] . $attr_m[3];
1997 | $span_mode = $this->mode == 'span' || $this->mode != 'block' &&
1998 | preg_match("{^<(?:$this->contain_span_tags)\b}", $tag);
1999 |
2000 | # Calculate indent before tag.
2001 | preg_match('/(?:^|\n)( *?)(?! ).*?$/', $block_text, $matches);
2002 | $indent = strlen($matches[1]);
2003 |
2004 | # End preceding block with this tag.
2005 | $block_text .= $tag;
2006 | $parsed .= $this->$hash_method($block_text);
2007 |
2008 | # Get enclosing tag name for the ParseMarkdown function.
2009 | preg_match('/^<([\w:$]*)\b/', $tag, $matches);
2010 | $tag_name = $matches[1];
2011 |
2012 | # Parse the content using the HTML-in-Markdown parser.
2013 | list ($block_text, $text)
2014 | = $this->_hashHTMLBlocks_inMarkdown($text, $indent,
2015 | $tag_name, $span_mode);
2016 |
2017 | # Outdent markdown text.
2018 | if ($indent > 0) {
2019 | $block_text = preg_replace("/^[ ]{1,$indent}/m", "",
2020 | $block_text);
2021 | }
2022 |
2023 | # Append tag content to parsed text.
2024 | if (!$span_mode) $parsed .= "\n\n$block_text\n\n";
2025 | else $parsed .= "$block_text";
2026 |
2027 | # Start over a new block.
2028 | $block_text = "";
2029 | }
2030 | else $block_text .= $tag;
2031 | }
2032 |
2033 | } while ($depth > 0);
2034 |
2035 | #
2036 | # Hash last block text that wasn't processed inside the loop.
2037 | #
2038 | $parsed .= $this->$hash_method($block_text);
2039 |
2040 | return array($parsed, $text);
2041 | }
2042 |
2043 |
2044 | function hashClean($text) {
2045 | #
2046 | # Called whenever a tag must be hashed when a function insert a "clean" tag
2047 | # in $text, it pass through this function and is automaticaly escaped,
2048 | # blocking invalid nested overlap.
2049 | #
2050 | # Swap back any tag hash found in $text so we do not have to `unhash`
2051 | # multiple times at the end.
2052 | $text = $this->unhash($text);
2053 |
2054 | # Then hash the tag.
2055 | $key = "C\x1A". md5($text);
2056 | $this->html_cleans[$key] = $text;
2057 | $this->html_hashes[$key] = $text;
2058 | return $key; # String that will replace the clean tag.
2059 | }
2060 |
2061 |
2062 | function doHeaders($text) {
2063 | #
2064 | # Redefined to add id attribute support.
2065 | #
2066 | # Setext-style headers:
2067 | # Header 1 {#header1}
2068 | # ========
2069 | #
2070 | # Header 2 {#header2}
2071 | # --------
2072 | #
2073 | $text = preg_replace_callback(
2074 | '{ (^.+?) (?:[ ]+\{\#([-_:a-zA-Z0-9]+)\})? [ ]*\n=+[ ]*\n+ }mx',
2075 | array(&$this, '_doHeaders_callback_setext_h1'), $text);
2076 | $text = preg_replace_callback(
2077 | '{ (^.+?) (?:[ ]+\{\#([-_:a-zA-Z0-9]+)\})? [ ]*\n-+[ ]*\n+ }mx',
2078 | array(&$this, '_doHeaders_callback_setext_h2'), $text);
2079 |
2080 | # atx-style headers:
2081 | # # Header 1 {#header1}
2082 | # ## Header 2 {#header2}
2083 | # ## Header 2 with closing hashes ## {#header3}
2084 | # ...
2085 | # ###### Header 6 {#header2}
2086 | #
2087 | $text = preg_replace_callback('{
2088 | ^(\#{1,6}) # $1 = string of #\'s
2089 | [ ]*
2090 | (.+?) # $2 = Header text
2091 | [ ]*
2092 | \#* # optional closing #\'s (not counted)
2093 | (?:[ ]+\{\#([-_:a-zA-Z0-9]+)\})? # id attribute
2094 | [ ]*
2095 | \n+
2096 | }xm',
2097 | array(&$this, '_doHeaders_callback_atx'), $text);
2098 |
2099 | return $text;
2100 | }
2101 | function _doHeaders_attr($attr) {
2102 | if (empty($attr)) return "";
2103 | return " id=\"$attr\"";
2104 | }
2105 | function _doHeaders_callback_setext_h1($matches) {
2106 | $attr = $this->_doHeaders_attr($id =& $matches[2]);
2107 | $block = "".$this->runSpanGamut($matches[1])."
";
2108 | return "\n" . $this->hashBlock($block) . "\n\n";
2109 | }
2110 | function _doHeaders_callback_setext_h2($matches) {
2111 | $attr = $this->_doHeaders_attr($id =& $matches[2]);
2112 | $block = "".$this->runSpanGamut($matches[1])."
";
2113 | return "\n" . $this->hashBlock($block) . "\n\n";
2114 | }
2115 | function _doHeaders_callback_atx($matches) {
2116 | $level = strlen($matches[1]);
2117 | $attr = $this->_doHeaders_attr($id =& $matches[3]);
2118 | $block = "\n";
2221 | $text .= "\n";
2222 | $text .= "
";
2248 |
2249 | return $this->hashBlock($text) . "\n";
2250 | }
2251 |
2252 |
2253 | function doDefLists($text) {
2254 | #
2255 | # Form HTML definition lists.
2256 | #
2257 | $less_than_tab = $this->tab_width - 1;
2258 |
2259 | # Re-usable pattern to match any entire dl list:
2260 | $whole_list = '
2261 | ( # $1 = whole list
2262 | ( # $2
2263 | [ ]{0,'.$less_than_tab.'}
2264 | ((?>.*\S.*\n)+) # $3 = defined term
2265 | \n?
2266 | [ ]{0,'.$less_than_tab.'}:[ ]+ # colon starting definition
2267 | )
2268 | (?s:.+?)
2269 | ( # $4
2270 | \z
2271 | |
2272 | \n{2,}
2273 | (?=\S)
2274 | (?! # Negative lookahead for another term
2275 | [ ]{0,'.$less_than_tab.'}
2276 | (?: \S.*\n )+? # defined term
2277 | \n?
2278 | [ ]{0,'.$less_than_tab.'}:[ ]+ # colon starting definition
2279 | )
2280 | (?! # Negative lookahead for another definition
2281 | [ ]{0,'.$less_than_tab.'}:[ ]+ # colon starting definition
2282 | )
2283 | )
2284 | )
2285 | '; // mx
2286 |
2287 | $text = preg_replace_callback('{
2288 | (?:(?<=\n\n)|\A\n?)
2289 | '.$whole_list.'
2290 | }mx',
2291 | array(&$this, '_doDefLists_callback'), $text);
2292 |
2293 | return $text;
2294 | }
2295 | function _doDefLists_callback($matches) {
2296 | # Re-usable patterns to match list item bullets and number markers:
2297 | $list = $matches[1];
2298 |
2299 | # Turn double returns into triple returns, so that we can make a
2300 | # paragraph for the last item in a list, if necessary:
2301 | $result = trim($this->processDefListItems($list));
2302 | $result = "\n";
2223 | foreach ($headers as $n => $header)
2224 | $text .= " \n";
2226 | $text .= "\n";
2227 |
2228 | # Split content by row.
2229 | $rows = explode("\n", trim($content, "\n"));
2230 |
2231 | $text .= "\n";
2232 | foreach ($rows as $row) {
2233 | # Creating code spans before splitting the row is an easy way to
2234 | # handle a code span containg pipes.
2235 | $row = $this->doCodeSpans($row);
2236 |
2237 | # Split row by cell.
2238 | $row_cells = preg_split('/ *[|] */', $row, $col_count);
2239 | $row_cells = array_pad($row_cells, $col_count, '');
2240 |
2241 | $text .= "".$this->runSpanGamut(trim($header))." \n";
2225 | $text .= "\n";
2242 | foreach ($row_cells as $n => $cell)
2243 | $text .= " \n";
2245 | }
2246 | $text .= "\n";
2247 | $text .= "".$this->runSpanGamut(trim($cell))." \n";
2244 | $text .= "\n" . $result . "\n
";
2303 | return $this->hashBlock($result) . "\n\n";
2304 | }
2305 |
2306 |
2307 | function processDefListItems($list_str) {
2308 | #
2309 | # Process the contents of a single definition list, splitting it
2310 | # into individual term and definition list items.
2311 | #
2312 | $less_than_tab = $this->tab_width - 1;
2313 |
2314 | # trim trailing blank lines:
2315 | $list_str = preg_replace("/\n{2,}\\z/", "\n", $list_str);
2316 |
2317 | # Process definition terms.
2318 | $list_str = preg_replace_callback('{
2319 | (?:\n\n+|\A\n?) # leading line
2320 | ( # definition terms = $1
2321 | [ ]{0,'.$less_than_tab.'} # leading whitespace
2322 | (?![:][ ]|[ ]) # negative lookahead for a definition
2323 | # mark (colon) or more whitespace.
2324 | (?: \S.* \n)+? # actual term (not whitespace).
2325 | )
2326 | (?=\n?[ ]{0,3}:[ ]) # lookahead for following line feed
2327 | # with a definition mark.
2328 | }xm',
2329 | array(&$this, '_processDefListItems_callback_dt'), $list_str);
2330 |
2331 | # Process actual definitions.
2332 | $list_str = preg_replace_callback('{
2333 | \n(\n+)? # leading line = $1
2334 | [ ]{0,'.$less_than_tab.'} # whitespace before colon
2335 | [:][ ]+ # definition mark (colon)
2336 | ((?s:.+?)) # definition text = $2
2337 | (?= \n+ # stop at next definition mark,
2338 | (?: # next term or end of text
2339 | [ ]{0,'.$less_than_tab.'} [:][ ] |
2340 |
fn_backlink_class != "") {
2537 | $class = $this->fn_backlink_class;
2538 | $class = $this->encodeAmpsAndAngles($class);
2539 | $class = str_replace('"', '"', $class);
2540 | $attr .= " class=\"$class\"";
2541 | }
2542 | if ($this->fn_backlink_title != "") {
2543 | $title = $this->fn_backlink_title;
2544 | $title = $this->encodeAmpsAndAngles($title);
2545 | $title = str_replace('"', '"', $title);
2546 | $attr .= " title=\"$title\"";
2547 | }
2548 | $num = 0;
2549 |
2550 | $this->in_footnote = true;
2551 |
2552 | foreach ($this->footnotes_ordered as $note_id => $footnote) {
2553 | $footnote .= "\n"; # Need to append newline before parsing.
2554 | $footnote = $this->runBlockGamut("$footnote\n");
2555 |
2556 | $attr2 = str_replace("%%", ++$num, $attr);
2557 |
2558 | # Add backlink to last paragraph; create new paragraph if needed.
2559 | $backlink = "↩";
2560 | if (preg_match('{$}', $footnote)) {
2561 | $footnote = substr($footnote, 0, -4) . " $backlink";
2562 | } else {
2563 | $footnote .= "\n\n as well).
2696 |
2697 | For more information about Markdown's syntax, see:
2698 |
2699 |