2 require_once('lib/HtmlElement.php');
5 require_once('lib/transform.php');
8 extends WikiTransform {
9 function InlineTransform() {
10 global $WikiNameRegexp, $AllowedProtocols, $InterWikiLinkRegexp;
12 $this->WikiTransform();
15 // functions are applied in order of registering
17 $this->register(WT_SIMPLE_MARKUP, 'wtm_plugin_link');
19 $this->register(WT_TOKENIZER, 'wtt_doublebrackets', '\[\[');
20 //$this->register(WT_TOKENIZER, 'wtt_footnotes', '^\[\d+\]');
21 //$this->register(WT_TOKENIZER, 'wtt_footnoterefs', '\[\d+\]');
22 $this->register(WT_TOKENIZER, 'wtt_bracketlinks', '\[.+?\]');
23 $this->register(WT_TOKENIZER, 'wtt_urls',
24 "!?\b($AllowedProtocols):[^\s<>\[\]\"'()]*[^\s<>\[\]\"'(),.?]");
26 if (function_exists('wtt_interwikilinks')) {
27 $this->register(WT_TOKENIZER, 'wtt_interwikilinks',
28 pcre_fix_posix_classes("!?(?<![[:alnum:]])") .
29 "$InterWikiLinkRegexp:[^\\s.,;?()]+");
31 $this->register(WT_TOKENIZER, 'wtt_bumpylinks', "!?$WikiNameRegexp");
33 $this->register(WT_SIMPLE_MARKUP, 'wtm_htmlchars');
34 $this->register(WT_SIMPLE_MARKUP, 'wtm_linebreak');
35 $this->register(WT_SIMPLE_MARKUP, 'wtm_bold_italics');
39 function TransformInline ($text) {
40 $lines = preg_split('/[ \t\r]*\n/', trim($text));
42 $trfm = new InlineTransform;
43 return $trfm->do_transform('', $lines);
52 * simple li's (vs compound li's) & (dd's).
55 function BlockParser ($block_types) {
56 $this->_blockTypes = array();
57 foreach ($block_types as $type)
58 $this->registerBlockType($type);
61 function registerBlockType ($class) {
62 // FIXME: this is hackish.
63 // It's because there seems to be no way to
64 // call static members of $class.
65 $prototype = new $class (false);
66 $this->_blockTypes[] = $prototype;
69 function parse ($text) {
72 // strip leading blank lines
73 $text = preg_replace("/\s*\n/A", "", $text);
76 $parsed = $this->_parseOne($text);
77 assert (!is_array($parsed));
81 $text = preg_replace("/\s*\n/A", "", $text);
86 function _parseOne (&$text) {
87 $block = $this->_grokBlockType($text); // determine block type from first line of text
89 $btext = $block->extractBlock($text);
91 while ($nextBlock = $this->_grokBlockType($text)) {
92 $next_text = $nextBlock->extractBlock($text);
94 if (! $nextBlock->matches($btext . $next_text)) {
95 // Can't combine blocks.
96 $text = $next_text . $text;
97 if ($nextBlock->getDepth() < $block->getDepth()) {
98 //if (!isa($block, "Block_blockquote")) {
99 if ($block->_tag != 'blockquote') {
101 $block = new Block_blockquote;
102 assert(preg_match('/(?:\s*\n)?( *)(?=\S)/', $btext, $m));
108 assert ($nextBlock->getDepth() <= $block->getDepth());
110 $btext .= $next_text;
114 echo "BLOCK $block->_tag:<pre>\n";
115 echo htmlspecialchars($btext);
116 echo "\n</pre><br />\n";
119 return $block->parse($btext);
122 function _grokBlockType ($text) {
123 foreach ($this->_blockTypes as $type) {
124 if (($block = $type->Match($text)))
142 function Block ($match = false) {
144 $this->_init($match);
148 function _init ($match) {
149 $qprefix = preg_quote($match[1], '/');
150 $this->_prefix_re = $qprefix;
151 $this->_block_re = "(?:(?:\s*\n)?${qprefix}.*\n?)+";
152 $this->_depth = strlen($match[1]);
156 * (This should be a static member function...)
158 function Match ($text) {
159 if (! preg_match($this->_match_re, $text, $match))
162 $block = $this; // Copy self.
163 $block->_init($match);
167 function extractBlock (&$text) {
168 $block_re = &$this->_block_re;
169 assert(preg_match("/$block_re/Ami", $text, $m));
170 $text = substr($text, strlen($m[0]));
174 function getDepth () {
175 return $this->_depth;
178 function matches ($text) {
179 $block_re = &$this->_block_re;
180 return preg_match("/$block_re\$/Ai", $text);
183 function parse ($text) {
184 assert ($this->matches($text));
186 // Strip block prefix from $text.
187 $prefix = &$this->_prefix_re;
188 $text = preg_replace("/^$prefix/m", "", $text);
192 return $this->wrap($BlockParser->parse($text));
196 function wrap($content) {
198 //if (is_array($content)) $content = join('', $content);
199 //$content = preg_replace("/(?<!\n)$/", "\n", $content);
200 //return "$this->_tag:\n" . preg_replace("/^(?!\$)/m", " ", $content);
202 return new HtmlElement($this->_tag, $this->_attr, $content);
207 class ListBlock extends Block
209 var $_match_re = "/(?:\s*\n)?( *[*#] *(?=\S))/A";
212 * Get a regexp which matches the line prefix for
213 * any <li> of the same type (ul/ol) and depth.
215 function makeLiPrefixRegexp ($match) {
216 return preg_quote($match[1], '/');
220 * Get a regexp which matches the line prefix for
221 * any <li> continuation lines.
223 function makeContPrefixRegexp ($match) {
224 return sprintf(" {%d}", strlen($match[1]));
228 class Block_list extends ListBlock
230 function _init ($match) {
231 $this->_tag = $this->grokListType($match);
233 $liprefix = $this->makeLiPrefixRegexp($match);
234 $cprefix = $this->makeContPrefixRegexp($match);
236 preg_match('/^ */', $match[1], $m);
237 $this->_depth = strlen($m[0]);
239 $this->_prefix_re = false; // don't strip any prefix
240 $this->_block_re = ( "(?:\s*\n)?" // leading blank lines.
242 . "${liprefix}.*\n?" // first line
243 . "(?:(?:\s*\n)?${cprefix}.*\n?)*" // continuation lines
248 function grokListType ($match) {
249 return preg_match("/#\s*\$/", $match[0]) ? 'ol' : 'ul';
252 function parse ($text) {
254 return $this->wrap($ListParser->parse($text));
259 class Block_li extends ListBlock
263 function _init ($match) {
264 $liprefix = $this->makeLiPrefixRegexp($match);
265 $cprefix = $this->makeContPrefixRegexp($match);
267 $this->_prefix_re = "(?:${cprefix}|${liprefix})";
268 $this->_block_re = ( "${liprefix}.*\n?" // first line
269 . "(?:(?:\s*\n)?${cprefix}.*\n?)*" ); // continuation lines
273 class Block_dl extends Block
276 var $_match_re = "/(?:\s*\n)?[^\s*#].*:\s*\n +\S/A";
277 var $_prefix_re = false; // no prefix to strip
278 var $_block_re = "(?:(?:\s*\n)?[^\s*#].*:(?:\s*\n +\S.*)+\n?)+";
283 function parse ($text) {
288 while ($block = $dt->Match($text)) {
289 $btext = $block->extractBlock($text);
290 $content[] = $block->parse($btext);
292 $block = $dd->Match($text);
294 $btext = $block->extractBlock($text);
295 $content[] = $block->parse($btext);
297 assert(preg_match("/\s*\$/A", $text));
298 return $this->wrap($content);
302 class Block_dt extends Block
305 var $_match_re = "/(?:\s*\n)?[^\s*#].*:\s*?\n/A";
306 var $_block_re = "(?:\s*\n)?[^\s*#].*:\s*\n";
311 function parse ($text) {
313 assert(preg_match("/(\S.*?)\s*:/A", $text, $m));
314 return $this->wrap($m[1]);
319 class Block_dd extends Block
322 var $_match_re = "/(?:(?:\s*\n)? +\S.*\n?)+/A";
323 var $_block_re = "(?:(?:\s*\n)? +\S.*\n?)+";
325 function _init ($match) {
326 $indent = $this->_getIndent($match[0]);
327 $this->_prefix_re = sprintf(" {%d}", $indent);
330 function _getIndent ($body) {
331 assert(preg_match_all("/^ +(?=\S)/m", $body, $m));
332 $indent = strlen($m[0][0]);
333 foreach ($m[0] as $pfx)
334 $indent = min($indent, strlen($pfx));
340 class Block_blockquote extends Block
342 var $_tag = 'blockquote';
343 var $_match_re = "/(?:\s*\n)?( +(?=\S))/A";
346 class BlockBlock extends Block
348 var $_prefix_re = false; // no prefix to strip
350 function BlockBlock ($begin_re, $end_re) {
351 $this->_begin_re = $begin_re;
352 $this->_end_re = $end_re;
354 $this->_block_re = ( "(?:\s*\n)?"
358 . "\s*?(?=\n|\S|$)" );
359 $this->_match_re = "/" . $this->_block_re . "/Ai";
365 function _strip ($text) {
366 $beg = $this->_begin_re;
367 $end = $this->_end_re;
369 $text = preg_replace("/.*?${beg}/Asi", "", $text);
370 $text = preg_replace("/${end}.*?$/si", "", $text);
374 function parse ($text) {
375 // FIXME: parse inline markup.
376 return $this->wrap(TransformInline($this->_strip($text)));
382 class Block_pre extends BlockBlock
386 function Block_pre () {
387 $this->BlockBlock("<pre>", "(?<!~)<\/pre>");
391 class Block_plugin extends BlockBlock
395 function Block_plugin () {
396 $this->BlockBlock("<\?plugin(?:-form)?\s", "\?>");
397 $this->_attr = array('class' => 'plugin');
400 function parse ($text) {
402 $loader = new WikiPluginLoader;
403 return $this->wrap($loader->expandPI($text, $request));
407 class Block_p extends Block
410 var $_match_re = "/(?=\S)/A";
411 var $_prefix_re = false; // no prefix to strip
412 var $_block_re = "\S.*\n?(?:^(?!\<\?)[^\s*#].*\n?)*";
414 function _init ($match) {
417 function parse ($text) {
418 // FIXME: parse inline markup.
419 return $this->wrap(TransformInline($text));
423 $GLOBALS['BlockParser'] = new BlockParser(array('Block_dl',
430 $GLOBALS['ListParser'] = new BlockParser(array('Block_li'));
433 // (c-file-style: "gnu")
438 // c-hanging-comment-ender-p: nil
439 // indent-tabs-mode: nil