39 |
40 |
41 |
--------------------------------------------------------------------------------
/examples/test.json:
--------------------------------------------------------------------------------
1 | [{"tags":[{"type":"author","email":"mallluhuct@gmail.com","website":"http:\/\/avalanche123.com","name":"Bulat Shakirzyanov"},{"type":"param","types":["string"],"name":"world","description":"- the world to greet"},{"type":"return","types":["void"]}],"description":"Greets the world","isPrivate":false,"isProtected":false,"isPublic":true,"isAbstract":false,"isFinal":false,"isStatic":false,"code":"function hello($world)","type":"function","name":"hello()"}]
--------------------------------------------------------------------------------
/examples/test.php:
--------------------------------------------------------------------------------
1 | (http://avalanche123.com)
7 | *
8 | * @param string $world - the world to greet
9 | *
10 | * @return void
11 | */
12 | function hello($world) {
13 | echo "Hello ${world}";
14 | }
15 |
--------------------------------------------------------------------------------
/examples/test.rst:
--------------------------------------------------------------------------------
1 | .. php:function:: hello
2 |
3 | Greets the world
4 |
5 | :param string $world: - the world to greet
6 |
7 | :returns void:
--------------------------------------------------------------------------------
/lib/DoxPHP/Exception/Exception.php:
--------------------------------------------------------------------------------
1 | (http://avalanche123.com)
11 | *
12 | * asd
13 | */
14 | class Parser
15 | {
16 | /**
17 | * Parses given Tokens object and returns array of parsed blocks
18 | *
19 | * @param DoxPHP\Parser\Tokens $tokens - tokens to parse
20 | *
21 | * @return array
22 | */
23 | public function parse(Tokens $tokens)
24 | {
25 | $this->blocks = array();
26 | $tokens->rewind();
27 |
28 | while($tokens->valid()) {
29 | $block = (object) array(
30 | "tags" => array(),
31 | "description" => '',
32 | "isPrivate" => false,
33 | "isProtected" => false,
34 | "isPublic" => false,
35 | "isAbstract" => false,
36 | "isFinal" => false,
37 | "isStatic" => false,
38 | "code" => '',
39 | "type" => '',
40 | "name" => '',
41 | );
42 | $this->blocks[] = $block;
43 |
44 | $this->skipWhitespace($tokens);
45 | $this->parseBlock($tokens, $block);
46 | }
47 |
48 | return $this->blocks;
49 | }
50 |
51 | private function skipWhitespace(Tokens $tokens)
52 | {
53 | do {
54 | $tokens->next();
55 | $token = $tokens->current();
56 | } while ($tokens->valid() && in_array($token->name, array("T_WHITESPACE", "T_OPEN_TAG")));
57 | }
58 |
59 | private function parseBlock(Tokens $tokens, \stdClass $block)
60 | {
61 | $token = $tokens->current();
62 |
63 | if ("T_DOC_COMMENT" === $token->name) {
64 | // parse all comments, last one before code wins
65 | $this->parseComment($token, $block);
66 | $this->skipWhitespace($tokens);
67 | $token = $tokens->current();
68 | }
69 | if ("T_ABSTRACT" === $token->name) {
70 | $block->isAbstract = true;
71 | $block->code .= $token->value." ";
72 | $this->skipWhitespace($tokens);
73 | $token = $tokens->current();
74 | } else {
75 | $block->isAbstract = false;
76 | }
77 | if ("T_FINAL" === $token->name) {
78 | $block->isFinal = true;
79 | $block->code .= $token->value." ";
80 | $this->skipWhitespace($tokens);
81 | $token = $tokens->current();
82 | } else {
83 | $block->isFinal = false;
84 | }
85 | if ("T_PRIVATE" === $token->name) {
86 | $block->isPrivate = true;
87 | $block->code .= $token->value." ";
88 | $this->skipWhitespace($tokens);
89 | $token = $tokens->current();
90 | } else {
91 | $block->isPrivate = false;
92 | }
93 | if ("T_PROTECTED" === $token->name) {
94 | $block->isProtected = true;
95 | $block->code .= $token->value." ";
96 | $this->skipWhitespace($tokens);
97 | $token = $tokens->current();
98 | } else {
99 | $block->isProtected = false;
100 | }
101 | if ("T_PUBLIC" === $token->name) {
102 | $block->code .= $token->value." ";
103 | $this->skipWhitespace($tokens);
104 | $token = $tokens->current();
105 | }
106 | if ("T_STATIC" === $token->name) {
107 | $block->isStatic = true;
108 | $block->code .= $token->value." ";
109 | $this->skipWhitespace($tokens);
110 | $token = $tokens->current();
111 | } else {
112 | $block->isStatic = false;
113 | }
114 |
115 | // implicit public
116 | $block->isPublic = !($block->isPrivate || $block->isProtected);
117 |
118 | if ("T_CLASS" === $token->name) {
119 | $block->type = 'class';
120 |
121 | $this->parseClassOrInterface($tokens, $block);
122 | } elseif ("T_INTERFACE" === $token->name) {
123 | $block->type = 'interface';
124 |
125 | $this->parseClassOrInterface($tokens, $block);
126 | } elseif ("T_FUNCTION" === $token->name) {
127 | $block->type = 'function';
128 |
129 | $this->parseFunctionOrMethod($tokens, $block);
130 | } elseif ("T_NAMESPACE" === $token->name) {
131 | $block->type = 'namespace';
132 |
133 | $this->parseNamespace($tokens, $block);
134 | } elseif ("T_VARIABLE" === $token->name) {
135 | $block->type = 'variable';
136 |
137 | $this->parseVariable($tokens, $block);
138 | } elseif ("T_CONST" === $token->name) {
139 | $block->type = 'constant';
140 |
141 | $this->parseConstant($tokens, $block);
142 | } else {
143 | array_pop($this->blocks);
144 | }
145 | }
146 |
147 | private function parseComment($token, \stdClass $block)
148 | {
149 | $lines = array_map(function($line) {
150 | return substr(trim($line), 2);
151 | }, array_slice(explode("\n", $token->value), 1, -1));
152 |
153 | $tagLines = array();
154 |
155 | foreach ($lines as $i => $line) {
156 | if (strpos($line, "@") === 0) {
157 | $tagLines[] = $line;
158 | unset($lines[$i]);
159 | }
160 | }
161 |
162 | $block->tags = array_values(array_map(function($line) {
163 | $line = substr($line, 1);
164 | $words = explode(" ", preg_replace('!\s+!', ' ', $line), 4);
165 | $count = count($words);
166 |
167 | $tag = array('type' => $words[0]);
168 |
169 | if ($tag['type'] == 'author') {
170 | array_shift($words);
171 | $words = explode(" ", implode(" ", array_filter($words)));
172 | foreach ($words as $i => $word) {
173 | if (0 === strpos($word, "(")) {
174 | $tag['website'] = substr($word, 1, -1);
175 | unset($words[$i]);
176 | continue;
177 | }
178 | if (0 === strpos($word, "<")) {
179 | $tag['email'] = substr($word, 1, -1);
180 | unset($words[$i]);
181 | }
182 | }
183 | $tag['name'] = implode(' ', $words);
184 | } elseif ($count > 3) {
185 | $tag['types'] = explode("|", $words[1]);
186 | $tag['name'] = substr($words[2], 1);
187 | $tag['description'] = trim($words[3]);
188 | } elseif ($count > 2) {
189 | $tag['types'] = explode("|", $words[1]);
190 | $tag['name'] = substr($words[2], 1);
191 | } elseif ($count > 1) {
192 | $tag['types'] = explode("|", $words[1]);
193 | }
194 |
195 | return $tag;
196 | }, $tagLines));
197 |
198 | $block->description = trim(str_replace("\n\n\n", "\n\n", implode(PHP_EOL, $lines)), "\n");
199 | }
200 |
201 | private function parseClassOrInterface(Tokens $tokens, \stdClass $block)
202 | {
203 | $token = $tokens->current();
204 | $gotName = false;
205 |
206 | while ("{" !== $token->value) {
207 | $block->code .= $token->value;
208 | if (!$gotName && in_array($token->name, array("T_NS_SEPARATOR", "T_STRING"))) {
209 | $block->name .= $token->value;
210 | $block->line = $token->line;
211 | }
212 | if (!empty($block->name) && !$gotName && !in_array($token->name, array("T_NS_SEPARATOR", "T_STRING", "T_WHITESPACE"))) {
213 | $gotName = true;
214 | }
215 | $tokens->next();
216 | $token = $tokens->current();
217 | }
218 |
219 | $block->code = trim($block->code);
220 |
221 | while ("}" !== $token->value) {
222 | $subBlock = (object) array(
223 | "tags" => array(),
224 | "description" => '',
225 | "isPrivate" => false,
226 | "isProtected" => false,
227 | "isPublic" => false,
228 | "isAbstract" => false,
229 | "isFinal" => false,
230 | "isStatic" => false,
231 | "code" => '',
232 | "type" => '',
233 | "name" => $block->name.'::',
234 | );
235 |
236 | $this->blocks[] = $subBlock;
237 |
238 | $this->skipWhitespace($tokens);
239 |
240 | if ("}" === $token->value) {
241 | break;
242 | }
243 |
244 | $this->parseBlock($tokens, $subBlock);
245 |
246 | if ($subBlock->type === 'function') {
247 | $subBlock->type = 'method';
248 | }
249 | $token = $tokens->current();
250 | }
251 | }
252 |
253 | private function parseFunctionOrMethod(Tokens $tokens, \stdClass $block)
254 | {
255 | $gotName = false;
256 | $openCurlys = 0;
257 | $token = $tokens->current();
258 |
259 | while (!in_array($token->value, array("{", ";"))) {
260 | $block->code .= $token->value;
261 |
262 | // first string after keyword is the function name
263 | if (!$gotName && "T_STRING" === $token->name) {
264 | $gotName = true;
265 | $block->name .= $token->value."()";
266 | $block->line = $token->line;
267 | }
268 |
269 | $tokens->next();
270 | $token = $tokens->current();
271 | }
272 |
273 | $block->code = trim($block->code);
274 | $block->name = trim($block->name);
275 |
276 | if ("{" === $token->value) {
277 | $openCurlys = 1;
278 | $tokens->next();
279 | $token = $tokens->current();
280 |
281 | while ($openCurlys !== 0) {
282 | if ("}" === $token->value) {
283 | $openCurlys--;
284 | } elseif ("{" === $token->value) {
285 | $openCurlys++;
286 | }
287 | $tokens->next();
288 | $token = $tokens->current();
289 | }
290 | }
291 | }
292 |
293 | private function parseNamespace(Tokens $tokens, \stdClass $block)
294 | {
295 | $openCurlys = 0;
296 | $token = $tokens->current();
297 |
298 | while (!in_array($token->value, array("{", ";"))) {
299 | $block->code .= $token->value;
300 | if (in_array($token->name, array("T_NS_SEPARATOR", "T_STRING"))) {
301 | $block->name .= $token->value;
302 | }
303 | $tokens->next();
304 | $token = $tokens->current();
305 | }
306 |
307 | $block->code = trim($block->code);
308 | $block->name = trim($block->name);
309 |
310 | if ("{" === $token->value) {
311 | $openCurlys = 1;
312 | $tokens->next();
313 | $token = $tokens->current();
314 |
315 | // skip to the end of namespace declaration
316 | while ($openCurlys !== 0) {
317 | if ("}" === $token->value) {
318 | $openCurlys--;
319 | } elseif ("{" === $token->value) {
320 | $openCurlys++;
321 | }
322 | $tokens->next();
323 | $token = $tokens->current();
324 | }
325 | }
326 | }
327 |
328 | private function parseVariable(Tokens $tokens, \stdClass $block)
329 | {
330 | $token = $tokens->current();
331 |
332 | while (";" !== $token->value) {
333 | $block->code .= $token->value;
334 | if ("T_VARIABLE" === $token->name) {
335 | $block->name = $token->value;
336 | }
337 | $tokens->next();
338 | $token = $tokens->current();
339 | }
340 |
341 | $block->code = trim($block->code);
342 | $block->name = trim($block->name);
343 | }
344 |
345 | private function parseConstant(Tokens $tokens, \stdClass $block)
346 | {
347 | $token = $tokens->current();
348 |
349 | while (";" !== $token->value) {
350 | $block->code .= $token->value;
351 |
352 | $tokens->next();
353 | $token = $tokens->current();
354 |
355 | $block->name .= $token->value;
356 | }
357 |
358 | $block->code = trim($block->code);
359 | $block->name = trim($block->name);
360 | }
361 | }
362 |
--------------------------------------------------------------------------------
/lib/DoxPHP/Parser/Tokens.php:
--------------------------------------------------------------------------------
1 | token_name($token[0]),
20 | "value" => $token[1],
21 | "line" => $token[2],
22 | );
23 | } else {
24 | $tok = array(
25 | "name" => null,
26 | "value" => $token
27 | );
28 | }
29 |
30 | return (object) $tok;
31 | }
32 | }
33 |
--------------------------------------------------------------------------------
/lib/vendor/php-markdown/LICENSE:
--------------------------------------------------------------------------------
1 | PHP Markdown
2 | Copyright (c) 2004-2008 Michel Fortin
3 |
4 | All rights reserved.
5 |
6 | Additional features and fixes by Henrik Paul
7 |
8 |
9 | Based on Markdown
10 | Copyright (c) 2003-2006 John Gruber
11 |
12 | All rights reserved.
13 |
14 | Redistribution and use in source and binary forms, with or without
15 | modification, are permitted provided that the following conditions are
16 | met:
17 |
18 | * Redistributions of source code must retain the above copyright notice,
19 | this list of conditions and the following disclaimer.
20 |
21 | * Redistributions in binary form must reproduce the above copyright
22 | notice, this list of conditions and the following disclaimer in the
23 | documentation and/or other materials provided with the distribution.
24 |
25 | * Neither the name "Markdown" nor the names of its contributors may
26 | be used to endorse or promote products derived from this software
27 | without specific prior written permission.
28 |
29 | This software is provided by the copyright holders and contributors "as
30 | is" and any express or implied warranties, including, but not limited
31 | to, the implied warranties of merchantability and fitness for a
32 | particular purpose are disclaimed. In no event shall the copyright owner
33 | or contributors be liable for any direct, indirect, incidental, special,
34 | exemplary, or consequential damages (including, but not limited to,
35 | procurement of substitute goods or services; loss of use, data, or
36 | profits; or business interruption) however caused and on any theory of
37 | liability, whether in contract, strict liability, or tort (including
38 | negligence or otherwise) arising in any way out of the use of this
39 | software, even if advised of the possibility of such damage.
40 |
--------------------------------------------------------------------------------
/lib/vendor/php-markdown/README:
--------------------------------------------------------------------------------
1 | README.markdown
--------------------------------------------------------------------------------
/lib/vendor/php-markdown/README.markdown:
--------------------------------------------------------------------------------
1 | PHP Markdown
2 | ============
3 |
4 | Version 1.0.1m - Sat 21 Jun 2008
5 |
6 | by Michel Fortin
7 |
8 |
9 | Additional features and fixes by Henrik Paul
10 |
11 |
12 | based on Markdown by John Gruber
13 |
14 |
15 |
16 | Introduction
17 | ------------
18 |
19 | Markdown is a text-to-HTML conversion tool for web writers. Markdown
20 | allows you to write using an easy-to-read, easy-to-write plain text
21 | format, then convert it to structurally valid XHTML (or HTML).
22 |
23 | "Markdown" is two things: a plain text markup syntax, and a software
24 | tool, written in Perl, that converts the plain text markup to HTML.
25 | PHP Markdown is a port to PHP of the original Markdown program by
26 | John Gruber.
27 |
28 | PHP Markdown can work as a plug-in for WordPress and bBlog, as a
29 | modifier for the Smarty templating engine, or as a remplacement for
30 | textile formatting in any software that support textile.
31 |
32 | Full documentation of Markdown's syntax is available on John's
33 | Markdown page:
34 |
35 |
36 | Installation and Requirement
37 | ----------------------------
38 |
39 | PHP Markdown requires PHP version 4.0.5 or later.
40 |
41 |
42 | ### WordPress ###
43 |
44 | PHP Markdown works with [WordPress][wp], version 1.2 or later.
45 |
46 | [wp]: http://wordpress.org/
47 |
48 | 1. To use PHP Markdown with WordPress, place the "makrdown.php" file
49 | in the "plugins" folder. This folder is located inside
50 | "wp-content" at the root of your site:
51 |
52 | (site home)/wp-content/plugins/
53 |
54 | 2. Activate the plugin with the administrative interface of
55 | WordPress. In the "Plugins" section you will now find Markdown.
56 | To activate the plugin, click on the "Activate" button on the
57 | same line than Markdown. Your entries will now be formatted by
58 | PHP Markdown.
59 |
60 | 3. To post Markdown content, you'll first have to disable the
61 | "visual" editor in the User section of WordPress.
62 |
63 | You can configure PHP Markdown to not apply to the comments on your
64 | WordPress weblog. See the "Configuration" section below.
65 |
66 | It is not possible at this time to apply a different set of
67 | filters to different entries. All your entries will be formated by
68 | PHP Markdown. This is a limitation of WordPress. If your old entries
69 | are written in HTML (as opposed to another formatting syntax, like
70 | Textile), they'll probably stay fine after installing Markdown.
71 |
72 |
73 | ### bBlog ###
74 |
75 | PHP Markdown also works with [bBlog][bb].
76 |
77 | [bb]: http://www.bblog.com/
78 |
79 | To use PHP Markdown with bBlog, rename "markdown.php" to
80 | "modifier.markdown.php" and place the file in the "bBlog_plugins"
81 | folder. This folder is located inside the "bblog" directory of
82 | your site, like this:
83 |
84 | (site home)/bblog/bBlog_plugins/modifier.markdown.php
85 |
86 | Select "Markdown" as the "Entry Modifier" when you post a new
87 | entry. This setting will only apply to the entry you are editing.
88 |
89 |
90 | ### Replacing Textile in TextPattern ###
91 |
92 | [TextPattern][tp] use [Textile][tx] to format your text. You can
93 | replace Textile by Markdown in TextPattern without having to change
94 | any code by using the *Texitle Compatibility Mode*. This may work
95 | with other software that expect Textile too.
96 |
97 | [tx]: http://www.textism.com/tools/textile/
98 | [tp]: http://www.textpattern.com/
99 |
100 | 1. Rename the "markdown.php" file to "classTextile.php". This will
101 | make PHP Markdown behave as if it was the actual Textile parser.
102 |
103 | 2. Replace the "classTextile.php" file TextPattern installed in your
104 | web directory. It can be found in the "lib" directory:
105 |
106 | (site home)/textpattern/lib/
107 |
108 | Contrary to Textile, Markdown does not convert quotes to curly ones
109 | and does not convert multiple hyphens (`--` and `---`) into en- and
110 | em-dashes. If you use PHP Markdown in Textile Compatibility Mode, you
111 | can solve this problem by installing the "smartypants.php" file from
112 | [PHP SmartyPants][psp] beside the "classTextile.php" file. The Textile
113 | Compatibility Mode function will use SmartyPants automatically without
114 | further modification.
115 |
116 | [psp]: http://www.michelf.com/projects/php-smartypants/
117 |
118 |
119 | ### Updating Markdown in Other Programs ###
120 |
121 | Many web applications now ship with PHP Markdown, or have plugins to
122 | perform the conversion to HTML. You can update PHP Markdown in many of
123 | these programs by swapping the old "markdown.php" file for the new one.
124 |
125 | Here is a short non-exhaustive list of some programs and where they
126 | hide the "markdown.php" file.
127 |
128 | | Program | Path to Markdown
129 | | ------- | ----------------
130 | | [Pivot][] | `(site home)/pivot/includes/markdown/markdown.php`
131 |
132 | If you're unsure if you can do this with your application, ask the
133 | developer, or wait for the developer to update his application or
134 | plugin with the new version of PHP Markdown.
135 |
136 | [Pivot]: http://pivotlog.net/
137 |
138 |
139 | ### In Your Own Programs ###
140 |
141 | You can use PHP Markdown easily in your current PHP program. Simply
142 | include the file and then call the Markdown function on the text you
143 | want to convert:
144 |
145 | include_once "markdown.php";
146 | $my_html = Markdown($my_text);
147 |
148 | If you wish to use PHP Markdown with another text filter function
149 | built to parse HTML, you should filter the text *after* the Markdown
150 | function call. This is an example with [PHP SmartyPants][psp]:
151 |
152 | $my_html = SmartyPants(Markdown($my_text));
153 |
154 |
155 | ### With Smarty ###
156 |
157 | If your program use the [Smarty][sm] template engine, PHP Markdown
158 | can now be used as a modifier for your templates. Rename "markdown.php"
159 | to "modifier.markdown.php" and put it in your smarty plugins folder.
160 |
161 | [sm]: http://smarty.php.net/
162 |
163 | If you are using MovableType 3.1 or later, the Smarty plugin folder is
164 | located at `(MT CGI root)/php/extlib/smarty/plugins`. This will allow
165 | Markdown to work on dynamic pages.
166 |
167 |
168 | Configuration
169 | -------------
170 |
171 | By default, PHP Markdown produces XHTML output for tags with empty
172 | elements. E.g.:
173 |
174 |
175 |
176 | Markdown can be configured to produce HTML-style tags; e.g.:
177 |
178 |
179 |
180 | To do this, you must edit the "MARKDOWN_EMPTY_ELEMENT_SUFFIX"
181 | definition below the "Global default settings" header at the start of
182 | the "markdown.php" file.
183 |
184 |
185 | ### WordPress-Specific Settings ###
186 |
187 | By default, the Markdown plugin applies to both posts and comments on
188 | your WordPress weblog. To deactivate one or the other, edit the
189 | `MARKDOWN_WP_POSTS` or `MARKDOWN_WP_COMMENTS` definitions under the
190 | "WordPress settings" header at the start of the "markdown.php" file.
191 |
192 |
193 | Bugs
194 | ----
195 |
196 | To file bug reports please send email to:
197 |
198 |
199 | Please include with your report: (1) the example input; (2) the output you
200 | expected; (3) the output PHP Markdown actually produced.
201 |
202 |
203 | Version History
204 | ---------------
205 |
206 | 1.0.1m (21 Jun 2008):
207 |
208 | * Lists can now have empty items.
209 |
210 | * Rewrote the emphasis and strong emphasis parser to fix some issues
211 | with odly placed and overlong markers.
212 |
213 |
214 | 1.0.1l (11 May 2008):
215 |
216 | * Now removing the UTF-8 BOM at the start of a document, if present.
217 |
218 | * Now accepting capitalized URI schemes (such as HTTP:) in automatic
219 | links, such as ``.
220 |
221 | * Fixed a problem where `` was seen as a horizontal
222 | rule instead of an automatic link.
223 |
224 | * Fixed an issue where some characters in Markdown-generated HTML
225 | attributes weren't properly escaped with entities.
226 |
227 | * Fix for code blocks as first element of a list item. Previously,
228 | this didn't create any code block for item 2:
229 |
230 | * Item 1 (regular paragraph)
231 |
232 | * Item 2 (code block)
233 |
234 | * A code block starting on the second line of a document wasn't seen
235 | as a code block. This has been fixed.
236 |
237 | * Added programatically-settable parser properties `predef_urls` and
238 | `predef_titles` for predefined URLs and titles for reference-style
239 | links. To use this, your PHP code must call the parser this way:
240 |
241 | $parser = new Markdwon_Parser;
242 | $parser->predef_urls = array('linkref' => 'http://example.com');
243 | $html = $parser->transform($text);
244 |
245 | You can then use the URL as a normal link reference:
246 |
247 | [my link][linkref]
248 | [my link][linkRef]
249 |
250 | Reference names in the parser properties *must* be lowercase.
251 | Reference names in the Markdown source may have any case.
252 |
253 | * Added `setup` and `teardown` methods which can be used by subclassers
254 | as hook points to arrange the state of some parser variables before and
255 | after parsing.
256 |
257 |
258 | 1.0.1k (26 Sep 2007):
259 |
260 | * Fixed a problem introduced in 1.0.1i where three or more identical
261 | uppercase letters, as well as a few other symbols, would trigger
262 | a horizontal line.
263 |
264 |
265 | 1.0.1j (4 Sep 2007):
266 |
267 | * Fixed a problem introduced in 1.0.1i where the closing `code` and
268 | `pre` tags at the end of a code block were appearing in the wrong
269 | order.
270 |
271 | * Overriding configuration settings by defining constants from an
272 | external before markdown.php is included is now possible without
273 | producing a PHP warning.
274 |
275 |
276 | 1.0.1i (31 Aug 2007):
277 |
278 | * Fixed a problem where an escaped backslash before a code span
279 | would prevent the code span from being created. This should now
280 | work as expected:
281 |
282 | Litteral backslash: \\`code span`
283 |
284 | * Overall speed improvements, especially with long documents.
285 |
286 |
287 | 1.0.1h (3 Aug 2007):
288 |
289 | * Added two properties (`no_markup` and `no_entities`) to the parser
290 | allowing HTML tags and entities to be disabled.
291 |
292 | * Fix for a problem introduced in 1.0.1g where posting comments in
293 | WordPress would trigger PHP warnings and cause some markup to be
294 | incorrectly filtered by the kses filter in WordPress.
295 |
296 |
297 | 1.0.1g (3 Jul 2007):
298 |
299 | * Fix for PHP 5 compiled without the mbstring module. Previous fix to
300 | calculate the length of UTF-8 strings in `detab` when `mb_strlen` is
301 | not available was only working with PHP 4.
302 |
303 | * Fixed a problem with WordPress 2.x where full-content posts in RSS feeds
304 | were not processed correctly by Markdown.
305 |
306 | * Now supports URLs containing literal parentheses for inline links
307 | and images, such as:
308 |
309 | [WIMP](http://en.wikipedia.org/wiki/WIMP_(computing))
310 |
311 | Such parentheses may be arbitrarily nested, but must be
312 | balanced. Unbalenced parentheses are allowed however when the URL
313 | when escaped or when the URL is enclosed in angle brakets `<>`.
314 |
315 | * Fixed a performance problem where the regular expression for strong
316 | emphasis introduced in version 1.0.1d could sometime be long to process,
317 | give slightly wrong results, and in some circumstances could remove
318 | entirely the content for a whole paragraph.
319 |
320 | * Some change in version 1.0.1d made possible the incorrect nesting of
321 | anchors within each other. This is now fixed.
322 |
323 | * Fixed a rare issue where certain MD5 hashes in the content could
324 | be changed to their corresponding text. For instance, this:
325 |
326 | The MD5 value for "+" is "26b17225b626fb9238849fd60eabdf60".
327 |
328 | was incorrectly changed to this in previous versions of PHP Markdown:
329 |
330 |
The MD5 value for "+" is "+".
331 |
332 | * Now convert escaped characters to their numeric character
333 | references equivalent.
334 |
335 | This fix an integration issue with SmartyPants and backslash escapes.
336 | Since Markdown and SmartyPants have some escapable characters in common,
337 | it was sometime necessary to escape them twice. Previously, two
338 | backslashes were sometime required to prevent Markdown from "eating" the
339 | backslash before SmartyPants sees it:
340 |
341 | Here are two hyphens: \\--
342 |
343 | Now, only one backslash will do:
344 |
345 | Here are two hyphens: \--
346 |
347 |
348 | 1.0.1f (7 Feb 2007):
349 |
350 | * Fixed an issue with WordPress where manually-entered excerpts, but
351 | not the auto-generated ones, would contain nested paragraphs.
352 |
353 | * Fixed an issue introduced in 1.0.1d where headers and blockquotes
354 | preceded too closely by a paragraph (not separated by a blank line)
355 | where incorrectly put inside the paragraph.
356 |
357 | * Fixed an issue introduced in 1.0.1d in the tokenizeHTML method where
358 | two consecutive code spans would be merged into one when together they
359 | form a valid tag in a multiline paragraph.
360 |
361 | * Fixed an long-prevailing issue where blank lines in code blocks would
362 | be doubled when the code block is in a list item.
363 |
364 | This was due to the list processing functions relying on artificially
365 | doubled blank lines to correctly determine when list items should
366 | contain block-level content. The list item processing model was thus
367 | changed to avoid the need for double blank lines.
368 |
369 | * Fixed an issue with `<% asp-style %>` instructions used as inline
370 | content where the opening `<` was encoded as `<`.
371 |
372 | * Fixed a parse error occuring when PHP is configured to accept
373 | ASP-style delimiters as boundaries for PHP scripts.
374 |
375 | * Fixed a bug introduced in 1.0.1d where underscores in automatic links
376 | got swapped with emphasis tags.
377 |
378 |
379 | 1.0.1e (28 Dec 2006)
380 |
381 | * Added support for internationalized domain names for email addresses in
382 | automatic link. Improved the speed at which email addresses are converted
383 | to entities. Thanks to Milian Wolff for his optimisations.
384 |
385 | * Made deterministic the conversion to entities of email addresses in
386 | automatic links. This means that a given email address will always be
387 | encoded the same way.
388 |
389 | * PHP Markdown will now use its own function to calculate the length of an
390 | UTF-8 string in `detab` when `mb_strlen` is not available instead of
391 | giving a fatal error.
392 |
393 |
394 | 1.0.1d (1 Dec 2006)
395 |
396 | * Fixed a bug where inline images always had an empty title attribute. The
397 | title attribute is now present only when explicitly defined.
398 |
399 | * Link references definitions can now have an empty title, previously if the
400 | title was defined but left empty the link definition was ignored. This can
401 | be useful if you want an empty title attribute in images to hide the
402 | tooltip in Internet Explorer.
403 |
404 | * Made `detab` aware of UTF-8 characters. UTF-8 multi-byte sequences are now
405 | correctly mapped to one character instead of the number of bytes.
406 |
407 | * Fixed a small bug with WordPress where WordPress' default filter `wpautop`
408 | was not properly deactivated on comment text, resulting in hard line breaks
409 | where Markdown do not prescribes them.
410 |
411 | * Added a `TextileRestrited` method to the textile compatibility mode. There
412 | is no restriction however, as Markdown does not have a restricted mode at
413 | this point. This should make PHP Markdown work again in the latest
414 | versions of TextPattern.
415 |
416 | * Converted PHP Markdown to a object-oriented design.
417 |
418 | * Changed span and block gamut methods so that they loop over a
419 | customizable list of methods. This makes subclassing the parser a more
420 | interesting option for creating syntax extensions.
421 |
422 | * Also added a "document" gamut loop which can be used to hook document-level
423 | methods (like for striping link definitions).
424 |
425 | * Changed all methods which were inserting HTML code so that they now return
426 | a hashed representation of the code. New methods `hashSpan` and `hashBlock`
427 | are used to hash respectivly span- and block-level generated content. This
428 | has a couple of significant effects:
429 |
430 | 1. It prevents invalid nesting of Markdown-generated elements which
431 | could occur occuring with constructs like `*something [link*][1]`.
432 | 2. It prevents problems occuring with deeply nested lists on which
433 | paragraphs were ill-formed.
434 | 3. It removes the need to call `hashHTMLBlocks` twice during the the
435 | block gamut.
436 |
437 | Hashes are turned back to HTML prior output.
438 |
439 | * Made the block-level HTML parser smarter using a specially-crafted regular
440 | expression capable of handling nested tags.
441 |
442 | * Solved backtick issues in tag attributes by rewriting the HTML tokenizer to
443 | be aware of code spans. All these lines should work correctly now:
444 |
445 | bar
446 | bar
447 | ``
448 |
449 | * Changed the parsing of HTML comments to match simply from ``
450 | instead using of the more complicated SGML-style rule with paired `--`.
451 | This is how most browsers parse comments and how XML defines them too.
452 |
453 | * `` has been added to the list of block-level elements and is now
454 | treated as an HTML block instead of being wrapped within paragraph tags.
455 |
456 | * Now only trim trailing newlines from code blocks, instead of trimming
457 | all trailing whitespace characters.
458 |
459 | * Fixed bug where this:
460 |
461 | [text](http://m.com "title" )
462 |
463 | wasn't working as expected, because the parser wasn't allowing for spaces
464 | before the closing paren.
465 |
466 | * Filthy hack to support markdown='1' in div tags.
467 |
468 | * _DoAutoLinks() now supports the 'dict://' URL scheme.
469 |
470 | * PHP- and ASP-style processor instructions are now protected as
471 | raw HTML blocks.
472 |
473 | ... ?>
474 | <% ... %>
475 |
476 | * Fix for escaped backticks still triggering code spans:
477 |
478 | There are two raw backticks here: \` and here: \`, not a code span
479 |
480 |
481 | 1.0.1c (9 Dec 2005)
482 |
483 | * Fixed a problem occurring with PHP 5.1.1 due to a small
484 | change to strings variable replacement behaviour in
485 | this version.
486 |
487 |
488 | 1.0.1b (6 Jun 2005)
489 |
490 | * Fixed a bug where an inline image followed by a reference link would
491 | give a completely wrong result.
492 |
493 | * Fix for escaped backticks still triggering code spans:
494 |
495 | There are two raw backticks here: \` and here: \`, not a code span
496 |
497 | * Fix for an ordered list following an unordered list, and the
498 | reverse. There is now a loop in _DoList that does the two
499 | separately.
500 |
501 | * Fix for nested sub-lists in list-paragraph mode. Previously we got
502 | a spurious extra level of `
` tags for something like this:
503 |
504 | * this
505 |
506 | * sub
507 |
508 | that
509 |
510 | * Fixed some incorrect behaviour with emphasis. This will now work
511 | as it should:
512 |
513 | *test **thing***
514 | **test *thing***
515 | ***thing* test**
516 | ***thing** test*
517 |
518 | Name: __________
519 | Address: _______
520 |
521 | * Correct a small bug in `_TokenizeHTML` where a Doctype declaration
522 | was not seen as HTML.
523 |
524 | * Major rewrite of the WordPress integration code that should
525 | correct many problems by preventing default WordPress filters from
526 | tampering with Markdown-formatted text. More details here:
527 |
528 |
529 |
530 | 1.0.1a (15 Apr 2005)
531 |
532 | * Fixed an issue where PHP warnings were trigged when converting
533 | text with list items running on PHP 4.0.6. This was comming from
534 | the `rtrim` function which did not support the second argument
535 | prior version 4.1. Replaced by a regular expression.
536 |
537 | * Markdown now filter correctly post excerpts and comment
538 | excerpts in WordPress.
539 |
540 | * Automatic links and some code sample were "corrected" by
541 | the balenceTag filter in WordPress meant to ensure HTML
542 | is well formed. This new version of PHP Markdown postpone this
543 | filter so that it runs after Markdown.
544 |
545 | * Blockquote syntax and some code sample were stripped by
546 | a new WordPress 1.5 filter meant to remove unwanted HTML
547 | in comments. This new version of PHP Markdown postpone this
548 | filter so that it runs after Markdown.
549 |
550 |
551 | 1.0.1 (16 Dec 2004):
552 |
553 | * Changed the syntax rules for code blocks and spans. Previously,
554 | backslash escapes for special Markdown characters were processed
555 | everywhere other than within inline HTML tags. Now, the contents of
556 | code blocks and spans are no longer processed for backslash escapes.
557 | This means that code blocks and spans are now treated literally,
558 | with no special rules to worry about regarding backslashes.
559 |
560 | **IMPORTANT**: This breaks the syntax from all previous versions of
561 | Markdown. Code blocks and spans involving backslash characters will
562 | now generate different output than before.
563 |
564 | Implementation-wise, this change was made by moving the call to
565 | `_EscapeSpecialChars()` from the top-level `Markdown()` function to
566 | within `_RunSpanGamut()`.
567 |
568 | * Significants performance improvement in `_DoHeader`, `_Detab`
569 | and `_TokenizeHTML`.
570 |
571 | * Added `>`, `+`, and `-` to the list of backslash-escapable
572 | characters. These should have been done when these characters
573 | were added as unordered list item markers.
574 |
575 | * Inline links using `<` and `>` URL delimiters weren't working:
576 |
577 | like [this]()
578 |
579 | Fixed by moving `_DoAutoLinks()` after `_DoAnchors()` in
580 | `_RunSpanGamut()`.
581 |
582 | * Fixed bug where auto-links were being processed within code spans:
583 |
584 | like this: ``
585 |
586 | Fixed by moving `_DoAutoLinks()` from `_RunBlockGamut()` to
587 | `_RunSpanGamut()`.
588 |
589 | * Sort-of fixed a bug where lines in the middle of hard-wrapped
590 | paragraphs, which lines look like the start of a list item,
591 | would accidentally trigger the creation of a list. E.g. a
592 | paragraph that looked like this:
593 |
594 | I recommend upgrading to version
595 | 8. Oops, now this line is treated
596 | as a sub-list.
597 |
598 | This is fixed for top-level lists, but it can still happen for
599 | sub-lists. E.g., the following list item will not be parsed
600 | properly:
601 |
602 | * I recommend upgrading to version
603 | 8. Oops, now this line is treated
604 | as a sub-list.
605 |
606 | Given Markdown's list-creation rules, I'm not sure this can
607 | be fixed.
608 |
609 | * Fix for horizontal rules preceded by 2 or 3 spaces or followed by
610 | trailing spaces and tabs.
611 |
612 | * Standalone HTML comments are now handled; previously, they'd get
613 | wrapped in a spurious `
` tag.
614 |
615 | * `_HashHTMLBlocks()` now tolerates trailing spaces and tabs following
616 | HTML comments and `
` tags.
617 |
618 | * Changed special case pattern for hashing `` tags in
619 | `_HashHTMLBlocks()` so that they must occur within three spaces
620 | of left margin. (With 4 spaces or a tab, they should be
621 | code blocks, but weren't before this fix.)
622 |
623 | * Auto-linked email address can now optionally contain
624 | a 'mailto:' protocol. I.e. these are equivalent:
625 |
626 |
627 |
628 |
629 | * Fixed annoying bug where nested lists would wind up with
630 | spurious (and invalid) `
` tags.
631 |
632 | * Changed `_StripLinkDefinitions()` so that link definitions must
633 | occur within three spaces of the left margin. Thus if you indent
634 | a link definition by four spaces or a tab, it will now be a code
635 | block.
636 |
637 | * You can now write empty links:
638 |
639 | [like this]()
640 |
641 | and they'll be turned into anchor tags with empty href attributes.
642 | This should have worked before, but didn't.
643 |
644 | * `***this***` and `___this___` are now turned into
645 |
646 | this
647 |
648 | Instead of
649 |
650 | this
651 |
652 | which isn't valid.
653 |
654 | * Fixed problem for links defined with urls that include parens, e.g.:
655 |
656 | [1]: http://sources.wikipedia.org/wiki/Middle_East_Policy_(Chomsky)
657 |
658 | "Chomsky" was being erroneously treated as the URL's title.
659 |
660 | * Double quotes in the title of an inline link used to give strange
661 | results (incorrectly made entities). Fixed.
662 |
663 | * Tabs are now correctly changed into spaces. Previously, only
664 | the first tab was converted. In code blocks, the second one was too,
665 | but was not always correctly aligned.
666 |
667 | * Fixed a bug where a tab character inserted after a quote on the same
668 | line could add a slash before the quotes.
669 |
670 | This is "before" [tab] and "after" a tab.
671 |
672 | Previously gave this result:
673 |
674 |
This is \"before\" [tab] and "after" a tab.
675 |
676 | * Removed a call to `htmlentities`. This fixes a bug where multibyte
677 | characters present in the title of a link reference could lead to
678 | invalid utf-8 characters.
679 |
680 | * Changed a regular expression in `_TokenizeHTML` that could lead to
681 | a segmentation fault with PHP 4.3.8 on Linux.
682 |
683 | * Fixed some notices that could show up if PHP error reporting
684 | E_NOTICE flag was set.
685 |
686 |
687 | Copyright and License
688 | ---------------------
689 |
690 | PHP Markdown
691 | Copyright (c) 2004-2007 Michel Fortin
692 |
693 | All rights reserved.
694 |
695 | Additional features and fixes by Henrik Paul
696 |
697 |
698 | Based on Markdown
699 | Copyright (c) 2003-2006 John Gruber
700 |
701 | All rights reserved.
702 |
703 | Redistribution and use in source and binary forms, with or without
704 | modification, are permitted provided that the following conditions are
705 | met:
706 |
707 | * Redistributions of source code must retain the above copyright notice,
708 | this list of conditions and the following disclaimer.
709 |
710 | * Redistributions in binary form must reproduce the above copyright
711 | notice, this list of conditions and the following disclaimer in the
712 | documentation and/or other materials provided with the distribution.
713 |
714 | * Neither the name "Markdown" nor the names of its contributors may
715 | be used to endorse or promote products derived from this software
716 | without specific prior written permission.
717 |
718 | This software is provided by the copyright holders and contributors "as
719 | is" and any express or implied warranties, including, but not limited
720 | to, the implied warranties of merchantability and fitness for a
721 | particular purpose are disclaimed. In no event shall the copyright owner
722 | or contributors be liable for any direct, indirect, incidental, special,
723 | exemplary, or consequential damages (including, but not limited to,
724 | procurement of substitute goods or services; loss of use, data, or
725 | profits; or business interruption) however caused and on any theory of
726 | liability, whether in contract, strict liability, or tort (including
727 | negligence or otherwise) arising in any way out of the use of this
728 | software, even if advised of the possibility of such damage.
729 |
--------------------------------------------------------------------------------
/lib/vendor/php-markdown/markdown.php:
--------------------------------------------------------------------------------
1 |
8 | #
9 | # Original Markdown
10 | # Copyright (c) 2004-2006 John Gruber
11 | #
12 | #
13 |
14 |
15 | define( 'MARKDOWN_VERSION', "1.0.1m" ); # Sat 21 Jun 2008
16 |
17 |
18 | #
19 | # Global default settings:
20 | #
21 |
22 | # Change to ">" for HTML output
23 | @define( 'MARKDOWN_EMPTY_ELEMENT_SUFFIX', " />");
24 |
25 | # Define the width of a tab for code blocks.
26 | @define( 'MARKDOWN_TAB_WIDTH', 4 );
27 |
28 |
29 | #
30 | # WordPress settings:
31 | #
32 |
33 | # Change to false to remove Markdown from posts and/or comments.
34 | @define( 'MARKDOWN_WP_POSTS', true );
35 | @define( 'MARKDOWN_WP_COMMENTS', true );
36 |
37 |
38 |
39 | ### Standard Function Interface ###
40 |
41 | @define( 'MARKDOWN_PARSER_CLASS', 'Markdown_Parser' );
42 |
43 | function Markdown($text) {
44 | #
45 | # Initialize the parser and return the result of its transform method.
46 | #
47 | # Setup static parser variable.
48 | static $parser;
49 | if (!isset($parser)) {
50 | $parser_class = MARKDOWN_PARSER_CLASS;
51 | $parser = new $parser_class;
52 | }
53 |
54 | # Transform text using parser.
55 | return $parser->transform($text);
56 | }
57 |
58 |
59 | ### WordPress Plugin Interface ###
60 |
61 | /*
62 | Plugin Name: Markdown
63 | Plugin URI: http://www.michelf.com/projects/php-markdown/
64 | Description: 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...
65 | Version: 1.0.1m
66 | Author: Michel Fortin
67 | Author URI: http://www.michelf.com/
68 | */
69 |
70 | if (isset($wp_version)) {
71 | # More details about how it works here:
72 | #
73 |
74 | # Post content and excerpts
75 | # - Remove WordPress paragraph generator.
76 | # - Run Markdown on excerpt, then remove all tags.
77 | # - Add paragraph tag around the excerpt, but remove it for the excerpt rss.
78 | if (MARKDOWN_WP_POSTS) {
79 | remove_filter('the_content', 'wpautop');
80 | remove_filter('the_content_rss', 'wpautop');
81 | remove_filter('the_excerpt', 'wpautop');
82 | add_filter('the_content', 'Markdown', 6);
83 | add_filter('the_content_rss', 'Markdown', 6);
84 | add_filter('get_the_excerpt', 'Markdown', 6);
85 | add_filter('get_the_excerpt', 'trim', 7);
86 | add_filter('the_excerpt', 'mdwp_add_p');
87 | add_filter('the_excerpt_rss', 'mdwp_strip_p');
88 |
89 | remove_filter('content_save_pre', 'balanceTags', 50);
90 | remove_filter('excerpt_save_pre', 'balanceTags', 50);
91 | add_filter('the_content', 'balanceTags', 50);
92 | add_filter('get_the_excerpt', 'balanceTags', 9);
93 | }
94 |
95 | # Comments
96 | # - Remove WordPress paragraph generator.
97 | # - Remove WordPress auto-link generator.
98 | # - Scramble important tags before passing them to the kses filter.
99 | # - Run Markdown on excerpt then remove paragraph tags.
100 | if (MARKDOWN_WP_COMMENTS) {
101 | remove_filter('comment_text', 'wpautop', 30);
102 | remove_filter('comment_text', 'make_clickable');
103 | add_filter('pre_comment_content', 'Markdown', 6);
104 | add_filter('pre_comment_content', 'mdwp_hide_tags', 8);
105 | add_filter('pre_comment_content', 'mdwp_show_tags', 12);
106 | add_filter('get_comment_text', 'Markdown', 6);
107 | add_filter('get_comment_excerpt', 'Markdown', 6);
108 | add_filter('get_comment_excerpt', 'mdwp_strip_p', 7);
109 |
110 | global $mdwp_hidden_tags, $mdwp_placeholders;
111 | $mdwp_hidden_tags = explode(' ',
112 | '
", $text);
122 | }
123 | return $text;
124 | }
125 |
126 | function mdwp_strip_p($t) { return preg_replace('{?p>}i', '', $t); }
127 |
128 | function mdwp_hide_tags($text) {
129 | global $mdwp_hidden_tags, $mdwp_placeholders;
130 | return str_replace($mdwp_hidden_tags, $mdwp_placeholders, $text);
131 | }
132 | function mdwp_show_tags($text) {
133 | global $mdwp_hidden_tags, $mdwp_placeholders;
134 | return str_replace($mdwp_placeholders, $mdwp_hidden_tags, $text);
135 | }
136 | }
137 |
138 |
139 | ### bBlog Plugin Info ###
140 |
141 | function identify_modifier_markdown() {
142 | return array(
143 | 'name' => 'markdown',
144 | 'type' => 'modifier',
145 | 'nicename' => 'Markdown',
146 | 'description' => 'A text-to-HTML conversion tool for web writers',
147 | 'authors' => 'Michel Fortin and John Gruber',
148 | 'licence' => 'BSD-like',
149 | 'version' => MARKDOWN_VERSION,
150 | 'help' => '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...'
151 | );
152 | }
153 |
154 |
155 | ### Smarty Modifier Interface ###
156 |
157 | function smarty_modifier_markdown($text) {
158 | return Markdown($text);
159 | }
160 |
161 |
162 | ### Textile Compatibility Mode ###
163 |
164 | # Rename this file to "classTextile.php" and it can replace Textile everywhere.
165 |
166 | if (strcasecmp(substr(__FILE__, -16), "classTextile.php") == 0) {
167 | # Try to include PHP SmartyPants. Should be in the same directory.
168 | @include_once 'smartypants.php';
169 | # Fake Textile class. It calls Markdown instead.
170 | class Textile {
171 | function TextileThis($text, $lite='', $encode='') {
172 | if ($lite == '' && $encode == '') $text = Markdown($text);
173 | if (function_exists('SmartyPants')) $text = SmartyPants($text);
174 | return $text;
175 | }
176 | # Fake restricted version: restrictions are not supported for now.
177 | function TextileRestricted($text, $lite='', $noimage='') {
178 | return $this->TextileThis($text, $lite);
179 | }
180 | # Workaround to ensure compatibility with TextPattern 4.0.3.
181 | function blockLite($text) { return $text; }
182 | }
183 | }
184 |
185 |
186 |
187 | #
188 | # Markdown Parser Class
189 | #
190 |
191 | class Markdown_Parser {
192 |
193 | # Regex to match balanced [brackets].
194 | # Needed to insert a maximum bracked depth while converting to PHP.
195 | var $nested_brackets_depth = 6;
196 | var $nested_brackets_re;
197 |
198 | var $nested_url_parenthesis_depth = 4;
199 | var $nested_url_parenthesis_re;
200 |
201 | # Table of hash values for escaped characters:
202 | var $escape_chars = '\`*_{}[]()>#+-.!';
203 | var $escape_chars_re;
204 |
205 | # Change to ">" for HTML output.
206 | var $empty_element_suffix = MARKDOWN_EMPTY_ELEMENT_SUFFIX;
207 | var $tab_width = MARKDOWN_TAB_WIDTH;
208 |
209 | # Change to `true` to disallow markup or entities.
210 | var $no_markup = false;
211 | var $no_entities = false;
212 |
213 | # Predefined urls and titles for reference links and images.
214 | var $predef_urls = array();
215 | var $predef_titles = array();
216 |
217 |
218 | function Markdown_Parser() {
219 | #
220 | # Constructor function. Initialize appropriate member variables.
221 | #
222 | $this->_initDetab();
223 | $this->prepareItalicsAndBold();
224 |
225 | $this->nested_brackets_re =
226 | str_repeat('(?>[^\[\]]+|\[', $this->nested_brackets_depth).
227 | str_repeat('\])*', $this->nested_brackets_depth);
228 |
229 | $this->nested_url_parenthesis_re =
230 | str_repeat('(?>[^()\s]+|\(', $this->nested_url_parenthesis_depth).
231 | str_repeat('(?>\)))*', $this->nested_url_parenthesis_depth);
232 |
233 | $this->escape_chars_re = '['.preg_quote($this->escape_chars).']';
234 |
235 | # Sort document, block, and span gamut in ascendent priority order.
236 | asort($this->document_gamut);
237 | asort($this->block_gamut);
238 | asort($this->span_gamut);
239 | }
240 |
241 |
242 | # Internal hashes used during transformation.
243 | var $urls = array();
244 | var $titles = array();
245 | var $html_hashes = array();
246 |
247 | # Status flag to avoid invalid nesting.
248 | var $in_anchor = false;
249 |
250 |
251 | function setup() {
252 | #
253 | # Called before the transformation process starts to setup parser
254 | # states.
255 | #
256 | # Clear global hashes.
257 | $this->urls = $this->predef_urls;
258 | $this->titles = $this->predef_titles;
259 | $this->html_hashes = array();
260 |
261 | $in_anchor = false;
262 | }
263 |
264 | function teardown() {
265 | #
266 | # Called after the transformation process to clear any variable
267 | # which may be taking up memory unnecessarly.
268 | #
269 | $this->urls = array();
270 | $this->titles = array();
271 | $this->html_hashes = array();
272 | }
273 |
274 |
275 | function transform($text) {
276 | #
277 | # Main function. Performs some preprocessing on the input text
278 | # and pass it through the document gamut.
279 | #
280 | $this->setup();
281 |
282 | # Remove UTF-8 BOM and marker character in input, if present.
283 | $text = preg_replace('{^\xEF\xBB\xBF|\x1A}', '', $text);
284 |
285 | # Standardize line endings:
286 | # DOS to Unix and Mac to Unix
287 | $text = preg_replace('{\r\n?}', "\n", $text);
288 |
289 | # Make sure $text ends with a couple of newlines:
290 | $text .= "\n\n";
291 |
292 | # Convert all tabs to spaces.
293 | $text = $this->detab($text);
294 |
295 | # Turn block-level HTML blocks into hash entries
296 | $text = $this->hashHTMLBlocks($text);
297 |
298 | # Strip any lines consisting only of spaces and tabs.
299 | # This makes subsequent regexen easier to write, because we can
300 | # match consecutive blank lines with /\n+/ instead of something
301 | # contorted like /[ ]*\n+/ .
302 | $text = preg_replace('/^[ ]+$/m', '', $text);
303 |
304 | # Run document gamut methods.
305 | foreach ($this->document_gamut as $method => $priority) {
306 | $text = $this->$method($text);
307 | }
308 |
309 | $this->teardown();
310 |
311 | return $text . "\n";
312 | }
313 |
314 | var $document_gamut = array(
315 | # Strip link definitions, store in hashes.
316 | "stripLinkDefinitions" => 20,
317 |
318 | "runBasicBlockGamut" => 30,
319 | );
320 |
321 |
322 | function stripLinkDefinitions($text) {
323 | #
324 | # Strips link definitions from text, stores the URLs and titles in
325 | # hash references.
326 | #
327 | $less_than_tab = $this->tab_width - 1;
328 |
329 | # Link defs are in the form: ^[id]: url "optional title"
330 | $text = preg_replace_callback('{
331 | ^[ ]{0,'.$less_than_tab.'}\[(.+)\][ ]?: # id = $1
332 | [ ]*
333 | \n? # maybe *one* newline
334 | [ ]*
335 | (\S+?)>? # url = $2
336 | [ ]*
337 | \n? # maybe one newline
338 | [ ]*
339 | (?:
340 | (?<=\s) # lookbehind for whitespace
341 | ["(]
342 | (.*?) # title = $3
343 | [")]
344 | [ ]*
345 | )? # title is optional
346 | (?:\n+|\Z)
347 | }xm',
348 | array(&$this, '_stripLinkDefinitions_callback'),
349 | $text);
350 | return $text;
351 | }
352 | function _stripLinkDefinitions_callback($matches) {
353 | $link_id = strtolower($matches[1]);
354 | $this->urls[$link_id] = $matches[2];
355 | $this->titles[$link_id] =& $matches[3];
356 | return ''; # String that will replace the block
357 | }
358 |
359 |
360 | function hashHTMLBlocks($text) {
361 | if ($this->no_markup) return $text;
362 |
363 | $less_than_tab = $this->tab_width - 1;
364 |
365 | # Hashify HTML blocks:
366 | # We only want to do this for block-level HTML tags, such as headers,
367 | # lists, and tables. That's because we still want to wrap
s around
368 | # "paragraphs" that are wrapped in non-block-level tags, such as anchors,
369 | # phrase emphasis, and spans. The list of tags we're looking for is
370 | # hard-coded:
371 | #
372 | # * List "a" is made of tags which can be both inline or block-level.
373 | # These will be treated block-level when the start tag is alone on
374 | # its line, otherwise they're not matched here and will be taken as
375 | # inline later.
376 | # * List "b" is made of tags which are always block-level;
377 | #
378 | $block_tags_a_re = 'ins|del';
379 | $block_tags_b_re = 'p|div|h[1-6]|blockquote|pre|table|dl|ol|ul|address|'.
380 | 'script|noscript|form|fieldset|iframe|math|textarea';
381 |
382 | # Regular expression for the content of a block tag.
383 | $nested_tags_level = 4;
384 | $attr = '
385 | (?> # optional tag attributes
386 | \s # starts with whitespace
387 | (?>
388 | [^>"/]+ # text outside quotes
389 | |
390 | /+(?!>) # slash not followed by ">"
391 | |
392 | "[^"]*" # text inside double quotes (tolerate ">")
393 | |
394 | \'[^\']*\' # text inside single quotes (tolerate ">")
395 | )*
396 | )?
397 | ';
398 | $content =
399 | str_repeat('
400 | (?>
401 | [^<]+ # content without tag
402 | |
403 | <\2 # nested opening tag
404 | '.$attr.' # attributes
405 | (?>
406 | />
407 | |
408 | >', $nested_tags_level). # end of opening tag
409 | '.*?'. # last level nested tag content
410 | str_repeat('
411 | \2\s*> # closing nested tag
412 | )
413 | |
414 | <(?!/\2\s*> # other tags with a different name
415 | )
416 | )*',
417 | $nested_tags_level);
418 | $content2 = str_replace('\2', '\3', $content);
419 |
420 | # First, look for nested blocks, e.g.:
421 | #
422 | #
423 | # tags for inner block must be indented.
424 | #
425 | #
426 | #
427 | # The outermost tags must start at the left margin for this to match, and
428 | # the inner nested divs must be indented.
429 | # We need to do this before the next, more liberal match, because the next
430 | # match will start at the first `
` and stop at the first `
`.
431 | $text = preg_replace_callback('{(?>
432 | (?>
433 | (?<=\n\n) # Starting after a blank line
434 | | # or
435 | \A\n? # the beginning of the doc
436 | )
437 | ( # save in $1
438 |
439 | # Match from `\n` to `\n`, handling nested tags
440 | # in between.
441 |
442 | [ ]{0,'.$less_than_tab.'}
443 | <('.$block_tags_b_re.')# start tag = $2
444 | '.$attr.'> # attributes followed by > and \n
445 | '.$content.' # content, support nesting
446 | \2> # the matching end tag
447 | [ ]* # trailing spaces/tabs
448 | (?=\n+|\Z) # followed by a newline or end of document
449 |
450 | | # Special version for tags of group a.
451 |
452 | [ ]{0,'.$less_than_tab.'}
453 | <('.$block_tags_a_re.')# start tag = $3
454 | '.$attr.'>[ ]*\n # attributes followed by >
455 | '.$content2.' # content, support nesting
456 | \3> # the matching end tag
457 | [ ]* # trailing spaces/tabs
458 | (?=\n+|\Z) # followed by a newline or end of document
459 |
460 | | # Special case just for . It was easier to make a special
461 | # case than to make the other regex more complicated.
462 |
463 | [ ]{0,'.$less_than_tab.'}
464 | <(hr) # start tag = $2
465 | '.$attr.' # attributes
466 | /?> # the matching end tag
467 | [ ]*
468 | (?=\n{2,}|\Z) # followed by a blank line or end of document
469 |
470 | | # Special case for standalone HTML comments:
471 |
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 | | # PHP and ASP-style processor instructions ( and <%)
480 |
481 | [ ]{0,'.$less_than_tab.'}
482 | (?s:
483 | <([?%]) # $2
484 | .*?
485 | \2>
486 | )
487 | [ ]*
488 | (?=\n{2,}|\Z) # followed by a blank line or end of document
489 |
490 | )
491 | )}Sxmi',
492 | array(&$this, '_hashHTMLBlocks_callback'),
493 | $text);
494 |
495 | return $text;
496 | }
497 | function _hashHTMLBlocks_callback($matches) {
498 | $text = $matches[1];
499 | $key = $this->hashBlock($text);
500 | return "\n\n$key\n\n";
501 | }
502 |
503 |
504 | function hashPart($text, $boundary = 'X') {
505 | #
506 | # Called whenever a tag must be hashed when a function insert an atomic
507 | # element in the text stream. Passing $text to through this function gives
508 | # a unique text-token which will be reverted back when calling unhash.
509 | #
510 | # The $boundary argument specify what character should be used to surround
511 | # the token. By convension, "B" is used for block elements that needs not
512 | # to be wrapped into paragraph tags at the end, ":" is used for elements
513 | # that are word separators and "X" is used in the general case.
514 | #
515 | # Swap back any tag hash found in $text so we do not have to `unhash`
516 | # multiple times at the end.
517 | $text = $this->unhash($text);
518 |
519 | # Then hash the block.
520 | static $i = 0;
521 | $key = "$boundary\x1A" . ++$i . $boundary;
522 | $this->html_hashes[$key] = $text;
523 | return $key; # String that will replace the tag.
524 | }
525 |
526 |
527 | function hashBlock($text) {
528 | #
529 | # Shortcut function for hashPart with block-level boundaries.
530 | #
531 | return $this->hashPart($text, 'B');
532 | }
533 |
534 |
535 | var $block_gamut = array(
536 | #
537 | # These are all the transformations that form block-level
538 | # tags like paragraphs, headers, and list items.
539 | #
540 | "doHeaders" => 10,
541 | "doHorizontalRules" => 20,
542 |
543 | "doLists" => 40,
544 | "doCodeBlocks" => 50,
545 | "doBlockQuotes" => 60,
546 | );
547 |
548 | function runBlockGamut($text) {
549 | #
550 | # Run block gamut tranformations.
551 | #
552 | # We need to escape raw HTML in Markdown source before doing anything
553 | # else. This need to be done for each block, and not only at the
554 | # begining in the Markdown function since hashed blocks can be part of
555 | # list items and could have been indented. Indented blocks would have
556 | # been seen as a code block in a previous pass of hashHTMLBlocks.
557 | $text = $this->hashHTMLBlocks($text);
558 |
559 | return $this->runBasicBlockGamut($text);
560 | }
561 |
562 | function runBasicBlockGamut($text) {
563 | #
564 | # Run block gamut tranformations, without hashing HTML blocks. This is
565 | # useful when HTML blocks are known to be already hashed, like in the first
566 | # whole-document pass.
567 | #
568 | foreach ($this->block_gamut as $method => $priority) {
569 | $text = $this->$method($text);
570 | }
571 |
572 | # Finally form paragraph and restore hashed blocks.
573 | $text = $this->formParagraphs($text);
574 |
575 | return $text;
576 | }
577 |
578 |
579 | function doHorizontalRules($text) {
580 | # Do Horizontal Rules:
581 | return preg_replace(
582 | '{
583 | ^[ ]{0,3} # Leading space
584 | ([-*_]) # $1: First marker
585 | (?> # Repeated marker group
586 | [ ]{0,2} # Zero, one, or two spaces.
587 | \1 # Marker character
588 | ){2,} # Group repeated at least twice
589 | [ ]* # Tailing spaces
590 | $ # End of line.
591 | }mx',
592 | "\n".$this->hashBlock("empty_element_suffix")."\n",
593 | $text);
594 | }
595 |
596 |
597 | var $span_gamut = array(
598 | #
599 | # These are all the transformations that occur *within* block-level
600 | # tags like paragraphs, headers, and list items.
601 | #
602 | # Process character escapes, code spans, and inline HTML
603 | # in one shot.
604 | "parseSpan" => -30,
605 |
606 | # Process anchor and image tags. Images must come first,
607 | # because ![foo][f] looks like an anchor.
608 | "doImages" => 10,
609 | "doAnchors" => 20,
610 |
611 | # Make links out of things like ``
612 | # Must come after doAnchors, because you can use < and >
613 | # delimiters in inline links like [this]().
614 | "doAutoLinks" => 30,
615 | "encodeAmpsAndAngles" => 40,
616 |
617 | "doItalicsAndBold" => 50,
618 | "doHardBreaks" => 60,
619 | );
620 |
621 | function runSpanGamut($text) {
622 | #
623 | # Run span gamut tranformations.
624 | #
625 | foreach ($this->span_gamut as $method => $priority) {
626 | $text = $this->$method($text);
627 | }
628 |
629 | return $text;
630 | }
631 |
632 |
633 | function doHardBreaks($text) {
634 | # Do hard breaks:
635 | return preg_replace_callback('/ {2,}\n/',
636 | array(&$this, '_doHardBreaks_callback'), $text);
637 | }
638 | function _doHardBreaks_callback($matches) {
639 | return $this->hashPart(" empty_element_suffix\n");
640 | }
641 |
642 |
643 | function doAnchors($text) {
644 | #
645 | # Turn Markdown link shortcuts into XHTML tags.
646 | #
647 | if ($this->in_anchor) return $text;
648 | $this->in_anchor = true;
649 |
650 | #
651 | # First, handle reference-style links: [link text] [id]
652 | #
653 | $text = preg_replace_callback('{
654 | ( # wrap whole match in $1
655 | \[
656 | ('.$this->nested_brackets_re.') # link text = $2
657 | \]
658 |
659 | [ ]? # one optional space
660 | (?:\n[ ]*)? # one optional newline followed by spaces
661 |
662 | \[
663 | (.*?) # id = $3
664 | \]
665 | )
666 | }xs',
667 | array(&$this, '_doAnchors_reference_callback'), $text);
668 |
669 | #
670 | # Next, inline-style links: [link text](url "optional title")
671 | #
672 | $text = preg_replace_callback('{
673 | ( # wrap whole match in $1
674 | \[
675 | ('.$this->nested_brackets_re.') # link text = $2
676 | \]
677 | \( # literal paren
678 | [ ]*
679 | (?:
680 | <(\S*)> # href = $3
681 | |
682 | ('.$this->nested_url_parenthesis_re.') # href = $4
683 | )
684 | [ ]*
685 | ( # $5
686 | ([\'"]) # quote char = $6
687 | (.*?) # Title = $7
688 | \6 # matching quote
689 | [ ]* # ignore any spaces/tabs between closing quote and )
690 | )? # title is optional
691 | \)
692 | )
693 | }xs',
694 | array(&$this, '_doAnchors_inline_callback'), $text);
695 |
696 | #
697 | # Last, handle reference-style shortcuts: [link text]
698 | # These must come last in case you've also got [link text][1]
699 | # or [link text](/foo)
700 | #
701 | // $text = preg_replace_callback('{
702 | // ( # wrap whole match in $1
703 | // \[
704 | // ([^\[\]]+) # link text = $2; can\'t contain [ or ]
705 | // \]
706 | // )
707 | // }xs',
708 | // array(&$this, '_doAnchors_reference_callback'), $text);
709 |
710 | $this->in_anchor = false;
711 | return $text;
712 | }
713 | function _doAnchors_reference_callback($matches) {
714 | $whole_match = $matches[1];
715 | $link_text = $matches[2];
716 | $link_id =& $matches[3];
717 |
718 | if ($link_id == "") {
719 | # for shortcut links like [this][] or [this].
720 | $link_id = $link_text;
721 | }
722 |
723 | # lower-case and turn embedded newlines into spaces
724 | $link_id = strtolower($link_id);
725 | $link_id = preg_replace('{[ ]?\n}', ' ', $link_id);
726 |
727 | if (isset($this->urls[$link_id])) {
728 | $url = $this->urls[$link_id];
729 | $url = $this->encodeAttribute($url);
730 |
731 | $result = "titles[$link_id] ) ) {
733 | $title = $this->titles[$link_id];
734 | $title = $this->encodeAttribute($title);
735 | $result .= " title=\"$title\"";
736 | }
737 |
738 | $link_text = $this->runSpanGamut($link_text);
739 | $result .= ">$link_text";
740 | $result = $this->hashPart($result);
741 | }
742 | else {
743 | $result = $whole_match;
744 | }
745 | return $result;
746 | }
747 | function _doAnchors_inline_callback($matches) {
748 | $whole_match = $matches[1];
749 | $link_text = $this->runSpanGamut($matches[2]);
750 | $url = $matches[3] == '' ? $matches[4] : $matches[3];
751 | $title =& $matches[7];
752 |
753 | $url = $this->encodeAttribute($url);
754 |
755 | $result = "encodeAttribute($title);
758 | $result .= " title=\"$title\"";
759 | }
760 |
761 | $link_text = $this->runSpanGamut($link_text);
762 | $result .= ">$link_text";
763 |
764 | return $this->hashPart($result);
765 | }
766 |
767 |
768 | function doImages($text) {
769 | #
770 | # Turn Markdown image shortcuts into tags.
771 | #
772 | #
773 | # First, handle reference-style labeled images: ![alt text][id]
774 | #
775 | $text = preg_replace_callback('{
776 | ( # wrap whole match in $1
777 | !\[
778 | ('.$this->nested_brackets_re.') # alt text = $2
779 | \]
780 |
781 | [ ]? # one optional space
782 | (?:\n[ ]*)? # one optional newline followed by spaces
783 |
784 | \[
785 | (.*?) # id = $3
786 | \]
787 |
788 | )
789 | }xs',
790 | array(&$this, '_doImages_reference_callback'), $text);
791 |
792 | #
793 | # Next, handle inline images: 
794 | # Don't forget: encode * and _
795 | #
796 | $text = preg_replace_callback('{
797 | ( # wrap whole match in $1
798 | !\[
799 | ('.$this->nested_brackets_re.') # alt text = $2
800 | \]
801 | \s? # One optional whitespace character
802 | \( # literal paren
803 | [ ]*
804 | (?:
805 | <(\S*)> # src url = $3
806 | |
807 | ('.$this->nested_url_parenthesis_re.') # src url = $4
808 | )
809 | [ ]*
810 | ( # $5
811 | ([\'"]) # quote char = $6
812 | (.*?) # title = $7
813 | \6 # matching quote
814 | [ ]*
815 | )? # title is optional
816 | \)
817 | )
818 | }xs',
819 | array(&$this, '_doImages_inline_callback'), $text);
820 |
821 | return $text;
822 | }
823 | function _doImages_reference_callback($matches) {
824 | $whole_match = $matches[1];
825 | $alt_text = $matches[2];
826 | $link_id = strtolower($matches[3]);
827 |
828 | if ($link_id == "") {
829 | $link_id = strtolower($alt_text); # for shortcut links like ![this][].
830 | }
831 |
832 | $alt_text = $this->encodeAttribute($alt_text);
833 | if (isset($this->urls[$link_id])) {
834 | $url = $this->encodeAttribute($this->urls[$link_id]);
835 | $result = "titles[$link_id])) {
837 | $title = $this->titles[$link_id];
838 | $title = $this->encodeAttribute($title);
839 | $result .= " title=\"$title\"";
840 | }
841 | $result .= $this->empty_element_suffix;
842 | $result = $this->hashPart($result);
843 | }
844 | else {
845 | # If there's no such link ID, leave intact:
846 | $result = $whole_match;
847 | }
848 |
849 | return $result;
850 | }
851 | function _doImages_inline_callback($matches) {
852 | $whole_match = $matches[1];
853 | $alt_text = $matches[2];
854 | $url = $matches[3] == '' ? $matches[4] : $matches[3];
855 | $title =& $matches[7];
856 |
857 | $alt_text = $this->encodeAttribute($alt_text);
858 | $url = $this->encodeAttribute($url);
859 | $result = "encodeAttribute($title);
862 | $result .= " title=\"$title\""; # $title already quoted
863 | }
864 | $result .= $this->empty_element_suffix;
865 |
866 | return $this->hashPart($result);
867 | }
868 |
869 |
870 | function doHeaders($text) {
871 | # Setext-style headers:
872 | # Header 1
873 | # ========
874 | #
875 | # Header 2
876 | # --------
877 | #
878 | $text = preg_replace_callback('{ ^(.+?)[ ]*\n(=+|-+)[ ]*\n+ }mx',
879 | array(&$this, '_doHeaders_callback_setext'), $text);
880 |
881 | # atx-style headers:
882 | # # Header 1
883 | # ## Header 2
884 | # ## Header 2 with closing hashes ##
885 | # ...
886 | # ###### Header 6
887 | #
888 | $text = preg_replace_callback('{
889 | ^(\#{1,6}) # $1 = string of #\'s
890 | [ ]*
891 | (.+?) # $2 = Header text
892 | [ ]*
893 | \#* # optional closing #\'s (not counted)
894 | \n+
895 | }xm',
896 | array(&$this, '_doHeaders_callback_atx'), $text);
897 |
898 | return $text;
899 | }
900 | function _doHeaders_callback_setext($matches) {
901 | # Terrible hack to check we haven't found an empty list item.
902 | if ($matches[2] == '-' && preg_match('{^-(?: |$)}', $matches[1]))
903 | return $matches[0];
904 |
905 | $level = $matches[2]{0} == '=' ? 1 : 2;
906 | $block = "".$this->runSpanGamut($matches[1])."";
907 | return "\n" . $this->hashBlock($block) . "\n\n";
908 | }
909 | function _doHeaders_callback_atx($matches) {
910 | $level = strlen($matches[1]);
911 | $block = "".$this->runSpanGamut($matches[2])."";
912 | return "\n" . $this->hashBlock($block) . "\n\n";
913 | }
914 |
915 |
916 | function doLists($text) {
917 | #
918 | # Form HTML ordered (numbered) and unordered (bulleted) lists.
919 | #
920 | $less_than_tab = $this->tab_width - 1;
921 |
922 | # Re-usable patterns to match list item bullets and number markers:
923 | $marker_ul_re = '[*+-]';
924 | $marker_ol_re = '\d+[.]';
925 | $marker_any_re = "(?:$marker_ul_re|$marker_ol_re)";
926 |
927 | $markers_relist = array($marker_ul_re, $marker_ol_re);
928 |
929 | foreach ($markers_relist as $marker_re) {
930 | # Re-usable pattern to match any entirel ul or ol list:
931 | $whole_list_re = '
932 | ( # $1 = whole list
933 | ( # $2
934 | [ ]{0,'.$less_than_tab.'}
935 | ('.$marker_re.') # $3 = first list item marker
936 | [ ]+
937 | )
938 | (?s:.+?)
939 | ( # $4
940 | \z
941 | |
942 | \n{2,}
943 | (?=\S)
944 | (?! # Negative lookahead for another list item marker
945 | [ ]*
946 | '.$marker_re.'[ ]+
947 | )
948 | )
949 | )
950 | '; // mx
951 |
952 | # We use a different prefix before nested lists than top-level lists.
953 | # See extended comment in _ProcessListItems().
954 |
955 | if ($this->list_level) {
956 | $text = preg_replace_callback('{
957 | ^
958 | '.$whole_list_re.'
959 | }mx',
960 | array(&$this, '_doLists_callback'), $text);
961 | }
962 | else {
963 | $text = preg_replace_callback('{
964 | (?:(?<=\n)\n|\A\n?) # Must eat the newline
965 | '.$whole_list_re.'
966 | }mx',
967 | array(&$this, '_doLists_callback'), $text);
968 | }
969 | }
970 |
971 | return $text;
972 | }
973 | function _doLists_callback($matches) {
974 | # Re-usable patterns to match list item bullets and number markers:
975 | $marker_ul_re = '[*+-]';
976 | $marker_ol_re = '\d+[.]';
977 | $marker_any_re = "(?:$marker_ul_re|$marker_ol_re)";
978 |
979 | $list = $matches[1];
980 | $list_type = preg_match("/$marker_ul_re/", $matches[3]) ? "ul" : "ol";
981 |
982 | $marker_any_re = ( $list_type == "ul" ? $marker_ul_re : $marker_ol_re );
983 |
984 | $list .= "\n";
985 | $result = $this->processListItems($list, $marker_any_re);
986 |
987 | $result = $this->hashBlock("<$list_type>\n" . $result . "$list_type>");
988 | return "\n". $result ."\n\n";
989 | }
990 |
991 | var $list_level = 0;
992 |
993 | function processListItems($list_str, $marker_any_re) {
994 | #
995 | # Process the contents of a single ordered or unordered list, splitting it
996 | # into individual list items.
997 | #
998 | # The $this->list_level global keeps track of when we're inside a list.
999 | # Each time we enter a list, we increment it; when we leave a list,
1000 | # we decrement. If it's zero, we're not in a list anymore.
1001 | #
1002 | # We do this because when we're not inside a list, we want to treat
1003 | # something like this:
1004 | #
1005 | # I recommend upgrading to version
1006 | # 8. Oops, now this line is treated
1007 | # as a sub-list.
1008 | #
1009 | # As a single paragraph, despite the fact that the second line starts
1010 | # with a digit-period-space sequence.
1011 | #
1012 | # Whereas when we're inside a list (or sub-list), that line will be
1013 | # treated as the start of a sub-list. What a kludge, huh? This is
1014 | # an aspect of Markdown's syntax that's hard to parse perfectly
1015 | # without resorting to mind-reading. Perhaps the solution is to
1016 | # change the syntax rules such that sub-lists must start with a
1017 | # starting cardinal number; e.g. "1." or "a.".
1018 |
1019 | $this->list_level++;
1020 |
1021 | # trim trailing blank lines:
1022 | $list_str = preg_replace("/\n{2,}\\z/", "\n", $list_str);
1023 |
1024 | $list_str = preg_replace_callback('{
1025 | (\n)? # leading line = $1
1026 | (^[ ]*) # leading whitespace = $2
1027 | ('.$marker_any_re.' # list marker and space = $3
1028 | (?:[ ]+|(?=\n)) # space only required if item is not empty
1029 | )
1030 | ((?s:.*?)) # list item text = $4
1031 | (?:(\n+(?=\n))|\n) # tailing blank line = $5
1032 | (?= \n* (\z | \2 ('.$marker_any_re.') (?:[ ]+|(?=\n))))
1033 | }xm',
1034 | array(&$this, '_processListItems_callback'), $list_str);
1035 |
1036 | $this->list_level--;
1037 | return $list_str;
1038 | }
1039 | function _processListItems_callback($matches) {
1040 | $item = $matches[4];
1041 | $leading_line =& $matches[1];
1042 | $leading_space =& $matches[2];
1043 | $marker_space = $matches[3];
1044 | $tailing_blank_line =& $matches[5];
1045 |
1046 | if ($leading_line || $tailing_blank_line ||
1047 | preg_match('/\n{2,}/', $item))
1048 | {
1049 | # Replace marker with the appropriate whitespace indentation
1050 | $item = $leading_space . str_repeat(' ', strlen($marker_space)) . $item;
1051 | $item = $this->runBlockGamut($this->outdent($item)."\n");
1052 | }
1053 | else {
1054 | # Recursion for sub-lists:
1055 | $item = $this->doLists($this->outdent($item));
1056 | $item = preg_replace('/\n+$/', '', $item);
1057 | $item = $this->runSpanGamut($item);
1058 | }
1059 |
1060 | return "
1448 | #
1449 | # Based by a filter by Matthew Wickline, posted to BBEdit-Talk.
1450 | # With some optimizations by Milian Wolff.
1451 | #
1452 | $addr = "mailto:" . $addr;
1453 | $chars = preg_split('/(? $char) {
1457 | $ord = ord($char);
1458 | # Ignore non-ascii chars.
1459 | if ($ord < 128) {
1460 | $r = ($seed * (1 + $key)) % 100; # Pseudo-random function.
1461 | # roughly 10% raw, 45% hex, 45% dec
1462 | # '@' *must* be encoded. I insist.
1463 | if ($r > 90 && $char != '@') /* do nothing */;
1464 | else if ($r < 45) $chars[$key] = ''.dechex($ord).';';
1465 | else $chars[$key] = ''.$ord.';';
1466 | }
1467 | }
1468 |
1469 | $addr = implode('', $chars);
1470 | $text = implode('', array_slice($chars, 7)); # text without `mailto:`
1471 | $addr = "$text";
1472 |
1473 | return $addr;
1474 | }
1475 |
1476 |
1477 | function parseSpan($str) {
1478 | #
1479 | # Take the string $str and parse it into tokens, hashing embeded HTML,
1480 | # escaped characters and handling code spans.
1481 | #
1482 | $output = '';
1483 |
1484 | $span_re = '{
1485 | (
1486 | \\\\'.$this->escape_chars_re.'
1487 | |
1488 | (?no_markup ? '' : '
1491 | |
1492 | # comment
1493 | |
1494 | <\?.*?\?> | <%.*?%> # processing instruction
1495 | |
1496 | <[/!$]?[-a-zA-Z0-9:]+ # regular tags
1497 | (?>
1498 | \s
1499 | (?>[^"\'>]+|"[^"]*"|\'[^\']*\')*
1500 | )?
1501 | >
1502 | ').'
1503 | )
1504 | }xs';
1505 |
1506 | while (1) {
1507 | #
1508 | # Each loop iteration seach for either the next tag, the next
1509 | # openning code span marker, or the next escaped character.
1510 | # Each token is then passed to handleSpanToken.
1511 | #
1512 | $parts = preg_split($span_re, $str, 2, PREG_SPLIT_DELIM_CAPTURE);
1513 |
1514 | # Create token from text preceding tag.
1515 | if ($parts[0] != "") {
1516 | $output .= $parts[0];
1517 | }
1518 |
1519 | # Check if we reach the end.
1520 | if (isset($parts[1])) {
1521 | $output .= $this->handleSpanToken($parts[1], $parts[2]);
1522 | $str = $parts[2];
1523 | }
1524 | else {
1525 | break;
1526 | }
1527 | }
1528 |
1529 | return $output;
1530 | }
1531 |
1532 |
1533 | function handleSpanToken($token, &$str) {
1534 | #
1535 | # Handle $token provided by parseSpan by determining its nature and
1536 | # returning the corresponding value that should replace it.
1537 | #
1538 | switch ($token{0}) {
1539 | case "\\":
1540 | return $this->hashPart("". ord($token{1}). ";");
1541 | case "`":
1542 | # Search for end marker in remaining text.
1543 | if (preg_match('/^(.*?[^`])'.preg_quote($token).'(?!`)(.*)$/sm',
1544 | $str, $matches))
1545 | {
1546 | $str = $matches[2];
1547 | $codespan = $this->makeCodeSpan($matches[1]);
1548 | return $this->hashPart($codespan);
1549 | }
1550 | return $token; // return as text since no ending marker found.
1551 | default:
1552 | return $this->hashPart($token);
1553 | }
1554 | }
1555 |
1556 |
1557 | function outdent($text) {
1558 | #
1559 | # Remove one level of line-leading tabs or spaces
1560 | #
1561 | return preg_replace('/^(\t|[ ]{1,'.$this->tab_width.'})/m', '', $text);
1562 | }
1563 |
1564 |
1565 | # String length function for detab. `_initDetab` will create a function to
1566 | # hanlde UTF-8 if the default function does not exist.
1567 | var $utf8_strlen = 'mb_strlen';
1568 |
1569 | function detab($text) {
1570 | #
1571 | # Replace tabs with the appropriate amount of space.
1572 | #
1573 | # For each line we separate the line in blocks delemited by
1574 | # tab characters. Then we reconstruct every line by adding the
1575 | # appropriate number of space between each blocks.
1576 |
1577 | $text = preg_replace_callback('/^.*\t.*$/m',
1578 | array(&$this, '_detab_callback'), $text);
1579 |
1580 | return $text;
1581 | }
1582 | function _detab_callback($matches) {
1583 | $line = $matches[0];
1584 | $strlen = $this->utf8_strlen; # strlen function for UTF-8.
1585 |
1586 | # Split in blocks.
1587 | $blocks = explode("\t", $line);
1588 | # Add each blocks to the line.
1589 | $line = $blocks[0];
1590 | unset($blocks[0]); # Do not add first block twice.
1591 | foreach ($blocks as $block) {
1592 | # Calculate amount of space, insert spaces, insert block.
1593 | $amount = $this->tab_width -
1594 | $strlen($line, 'UTF-8') % $this->tab_width;
1595 | $line .= str_repeat(" ", $amount) . $block;
1596 | }
1597 | return $line;
1598 | }
1599 | function _initDetab() {
1600 | #
1601 | # Check for the availability of the function in the `utf8_strlen` property
1602 | # (initially `mb_strlen`). If the function is not available, create a
1603 | # function that will loosely count the number of UTF-8 characters with a
1604 | # regular expression.
1605 | #
1606 | if (function_exists($this->utf8_strlen)) return;
1607 | $this->utf8_strlen = create_function('$text', 'return preg_match_all(
1608 | "/[\\\\x00-\\\\xBF]|[\\\\xC0-\\\\xFF][\\\\x80-\\\\xBF]*/",
1609 | $text, $m);');
1610 | }
1611 |
1612 |
1613 | function unhash($text) {
1614 | #
1615 | # Swap back in all the tags hashed by _HashHTMLBlocks.
1616 | #
1617 | return preg_replace_callback('/(.)\x1A[0-9]+\1/',
1618 | array(&$this, '_unhash_callback'), $text);
1619 | }
1620 | function _unhash_callback($matches) {
1621 | return $this->html_hashes[$matches[0]];
1622 | }
1623 |
1624 | }
1625 |
1626 | /*
1627 |
1628 | PHP Markdown
1629 | ============
1630 |
1631 | Description
1632 | -----------
1633 |
1634 | This is a PHP translation of the original Markdown formatter written in
1635 | Perl by John Gruber.
1636 |
1637 | Markdown is a text-to-HTML filter; it translates an easy-to-read /
1638 | easy-to-write structured text format into HTML. Markdown's text format
1639 | is most similar to that of plain text email, and supports features such
1640 | as headers, *emphasis*, code blocks, blockquotes, and links.
1641 |
1642 | Markdown's syntax is designed not as a generic markup language, but
1643 | specifically to serve as a front-end to (X)HTML. You can use span-level
1644 | HTML tags anywhere in a Markdown document, and you can use block level
1645 | HTML tags (like
and
as well).
1646 |
1647 | For more information about Markdown's syntax, see:
1648 |
1649 |
1650 |
1651 |
1652 | Bugs
1653 | ----
1654 |
1655 | To file bug reports please send email to:
1656 |
1657 |
1658 |
1659 | Please include with your report: (1) the example input; (2) the output you
1660 | expected; (3) the output Markdown actually produced.
1661 |
1662 |
1663 | Version History
1664 | ---------------
1665 |
1666 | See the readme file for detailed release notes for this version.
1667 |
1668 |
1669 | Copyright and License
1670 | ---------------------
1671 |
1672 | PHP Markdown
1673 | Copyright (c) 2004-2008 Michel Fortin
1674 |
1675 | All rights reserved.
1676 |
1677 | Based on Markdown
1678 | Copyright (c) 2003-2006 John Gruber
1679 |
1680 | All rights reserved.
1681 |
1682 | Redistribution and use in source and binary forms, with or without
1683 | modification, are permitted provided that the following conditions are
1684 | met:
1685 |
1686 | * Redistributions of source code must retain the above copyright notice,
1687 | this list of conditions and the following disclaimer.
1688 |
1689 | * Redistributions in binary form must reproduce the above copyright
1690 | notice, this list of conditions and the following disclaimer in the
1691 | documentation and/or other materials provided with the distribution.
1692 |
1693 | * Neither the name "Markdown" nor the names of its contributors may
1694 | be used to endorse or promote products derived from this software
1695 | without specific prior written permission.
1696 |
1697 | This software is provided by the copyright holders and contributors "as
1698 | is" and any express or implied warranties, including, but not limited
1699 | to, the implied warranties of merchantability and fitness for a
1700 | particular purpose are disclaimed. In no event shall the copyright owner
1701 | or contributors be liable for any direct, indirect, incidental, special,
1702 | exemplary, or consequential damages (including, but not limited to,
1703 | procurement of substitute goods or services; loss of use, data, or
1704 | profits; or business interruption) however caused and on any theory of
1705 | liability, whether in contract, strict liability, or tort (including
1706 | negligence or otherwise) arising in any way out of the use of this
1707 | software, even if advised of the possibility of such damage.
1708 |
1709 | */
1710 | ?>
1711 |
--------------------------------------------------------------------------------